diff --git a/CHANGELOG.md b/CHANGELOG.md index 1aa7d25..5371604 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,12 +4,30 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +#### [1.4.0](https://github.com/rdkcentral/subtec-app/compare/1.3.1...1.4.0) + +- RDKEMW-7101 : Sarnoff issue [`#39`](https://github.com/rdkcentral/subtec-app/pull/39) +- RDKEMW-4412: TTML attribute tests update to subtec L2 framework [`#38`](https://github.com/rdkcentral/subtec-app/pull/38) +- RDKEMW-4206 : Upstream LGI subtec changes [`#36`](https://github.com/rdkcentral/subtec-app/pull/36) +- RDKEMW-4206: Subtec-app code rebase [`37b33c0`](https://github.com/rdkcentral/subtec-app/commit/37b33c0054d0aa7abb542754ecff40f15e6d4669) +- RDKEMW-4491: Merge tag '1.3.0' into develop [`4b45661`](https://github.com/rdkcentral/subtec-app/commit/4b45661117ac5a6c026f24bf78b2d3caf8bedc76) + +#### [1.3.1](https://github.com/rdkcentral/subtec-app/compare/1.3.0...1.3.1) + +> 3 September 2025 + +- RDKEMW-7101 : Sarnoff issue [`#40`](https://github.com/rdkcentral/subtec-app/pull/40) +- 1.3.1 release change log update [`045f0dc`](https://github.com/rdkcentral/subtec-app/commit/045f0dc4435dcf9212ee631a88e019e38bb35fdb) + #### [1.3.0](https://github.com/rdkcentral/subtec-app/compare/1.2.3...1.3.0) +> 20 May 2025 + - RDKEMW-4075: Enable TTML override on runtime instead of buildtime [`#26`](https://github.com/rdkcentral/subtec-app/pull/26) - RDKEMW-3517 : subttxrend-app uses subttxrend-ctrl [`#25`](https://github.com/rdkcentral/subtec-app/pull/25) +- RDKEMW-4491:Merge branch 'release/1.3.0' into main [`9cdf8b1`](https://github.com/rdkcentral/subtec-app/commit/9cdf8b112cb535e3d0b23e2a5dba53fefd0e8e61) - RDKEMW-3517: subttxrend-app uses subttxrend-ctrl [`aca405c`](https://github.com/rdkcentral/subtec-app/commit/aca405c9c38ade415de8f890530d9b86bc586a39) -- Merge tag '1.2.3' into develop [`5bd1171`](https://github.com/rdkcentral/subtec-app/commit/5bd1171397c556b43d570ca09d8895184dfb9477) +- 1.3.0 release changelog update [`a5b0ebe`](https://github.com/rdkcentral/subtec-app/commit/a5b0ebe0df8c6dd24f9952905ca6f63236d1fa75) #### [1.2.3](https://github.com/rdkcentral/subtec-app/compare/1.2.2...1.2.3) diff --git a/dvbsubdecoder/CMakeLists.txt b/dvbsubdecoder/CMakeLists.txt index 8b47876..5d55a06 100644 --- a/dvbsubdecoder/CMakeLists.txt +++ b/dvbsubdecoder/CMakeLists.txt @@ -120,7 +120,7 @@ set(DVBSUBDECODER_SOURCES # set(LIBRARY_NAME "dvbsubdecoder") add_library(${LIBRARY_NAME} SHARED ${DVBSUBDECODER_SOURCES}) -set_property(TARGET ${LIBRARY_NAME} PROPERTY CXX_STANDARD 11) +set_property(TARGET ${LIBRARY_NAME} PROPERTY CXX_STANDARD 14) set_property(TARGET ${LIBRARY_NAME} PROPERTY VERSION 0.0.0) set_property(TARGET ${LIBRARY_NAME} PROPERTY SOVERSION 0) set_property(TARGET ${LIBRARY_NAME} PROPERTY PUBLIC_HEADER ${DVBSUBDECODER_PUBLIC_HEADERS}) diff --git a/dvbsubdecoder/test/CMakeLists.txt b/dvbsubdecoder/test/CMakeLists.txt index 38b2bb9..f0e5ece 100644 --- a/dvbsubdecoder/test/CMakeLists.txt +++ b/dvbsubdecoder/test/CMakeLists.txt @@ -60,7 +60,7 @@ macro (add_cppunit_test _name) # invoke built-in add_executable add_executable(${ARGV}) - set_property(TARGET ${_name} PROPERTY CXX_STANDARD 11) + set_property(TARGET ${_name} PROPERTY CXX_STANDARD 14) target_link_libraries(${_name} ${LIBCPPUNIT_LIBRARIES}) if(CMAKE_COMPILER_IS_GNUCXX) diff --git a/dvbsubdecoder/test/PixelWriter/PixelWriter_test.cpp b/dvbsubdecoder/test/PixelWriter/PixelWriter_test.cpp index 44b0f49..128dc70 100644 --- a/dvbsubdecoder/test/PixelWriter/PixelWriter_test.cpp +++ b/dvbsubdecoder/test/PixelWriter/PixelWriter_test.cpp @@ -22,6 +22,8 @@ #include "PixelWriter.hpp" +#include + using dvbsubdecoder::PixelWriter; using dvbsubdecoder::Pixmap; diff --git a/dvbsubdecoder/test/RenderingState/RenderingState_test.cpp b/dvbsubdecoder/test/RenderingState/RenderingState_test.cpp index eaf7065..6259d43 100644 --- a/dvbsubdecoder/test/RenderingState/RenderingState_test.cpp +++ b/dvbsubdecoder/test/RenderingState/RenderingState_test.cpp @@ -149,7 +149,7 @@ CPPUNIT_TEST_SUITE( RenderingStateTest ); if (state.addRegion(i & 0xFF, i & 0x0F, { 0, 0, 10, 10 })) { - CPPUNIT_ASSERT(state.getRegionByIndex(i).m_id == i & 0xFF); + CPPUNIT_ASSERT(state.getRegionByIndex(i).m_id == (i & 0xFF)); ++regionCount; CPPUNIT_ASSERT(state.getRegionCount() == regionCount); diff --git a/dvbsubdecoder/test/common/DecoderClientMock.hpp b/dvbsubdecoder/test/common/DecoderClientMock.hpp index bf6615d..a76b66a 100644 --- a/dvbsubdecoder/test/common/DecoderClientMock.hpp +++ b/dvbsubdecoder/test/common/DecoderClientMock.hpp @@ -89,7 +89,7 @@ class DecoderClientMock : public dvbsubdecoder::DecoderClient, return block; } - virtual void gfxFree(void* block) + virtual void gfxFree(void* block) override { if (block) { diff --git a/subttxrend-app/CMakeLists.txt b/subttxrend-app/CMakeLists.txt index 1ef42fb..96207e1 100644 --- a/subttxrend-app/CMakeLists.txt +++ b/subttxrend-app/CMakeLists.txt @@ -32,7 +32,9 @@ option(INSTALL_CONFIG_FILE "Install the configuration file" ON) # # Extra compiler / linker options # +if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) SET( CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--no-as-needed" ) +endif() IF(CMAKE_COMPILER_IS_GNUCXX) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra -Werror -Wformat=2") ENDIF(CMAKE_COMPILER_IS_GNUCXX) @@ -132,8 +134,6 @@ target_link_libraries(${APP_NAME} ${LIBSUBTTXRENDCC_LIBRARIES}) target_link_libraries(${APP_NAME} ${LIBSUBTTXRENDTTXT_LIBRARIES}) target_link_libraries(${APP_NAME} ${LIBSUBTTXRENDTTML_LIBRARIES}) target_link_libraries(${APP_NAME} ${LIBSUBTTXRENDWEBVTT_LIBRARIES}) -target_link_libraries(${APP_NAME} pthread) -target_link_libraries(${APP_NAME} boost_system) # # Install rules diff --git a/subttxrend-app/conf/subttxrend-app.conf b/subttxrend-app/conf/subttxrend-app.conf deleted file mode 100644 index c56a9ec..0000000 --- a/subttxrend-app/conf/subttxrend-app.conf +++ /dev/null @@ -1,4 +0,0 @@ -#Create path for rmfstreamer in volatile storage - -#Type Path Mode UID GID Age Argument -d /var/run/subttx 0750 root users diff --git a/subttxrend-app/src/Application.cpp b/subttxrend-app/src/Application.cpp index 0d0ff85..f1766e7 100644 --- a/subttxrend-app/src/Application.cpp +++ b/subttxrend-app/src/Application.cpp @@ -98,6 +98,13 @@ void Application::quit() common::LoggerManager::getInstance()->deinit(); } + +#ifdef __APPLE__ +void Application::startBlockingApplicationWindow() +{ + m_gfxEngine->startBlockingApplicationWindow(); +} +#endif } // namespace app } // namespace subttxrend diff --git a/subttxrend-app/src/Application.hpp b/subttxrend-app/src/Application.hpp index e0adc04..d7c85cd 100644 --- a/subttxrend-app/src/Application.hpp +++ b/subttxrend-app/src/Application.hpp @@ -50,6 +50,10 @@ class Application : private common::NonCopyable void runAsync(); void quit(); +#ifdef __APPLE__ + void startBlockingApplicationWindow(); +#endif + private: /** Application configuration. */ diff --git a/subttxrend-app/src/Controller.cpp b/subttxrend-app/src/Controller.cpp index 94dc888..e5595ad 100644 --- a/subttxrend-app/src/Controller.cpp +++ b/subttxrend-app/src/Controller.cpp @@ -69,7 +69,6 @@ void forAllControllers(Ctrls const& ctrls, Packet const& packet, void (ctrl::Con } constexpr const std::chrono::milliseconds connection_status_check_timeout{1000}; -constexpr const std::chrono::milliseconds as_data_acq_timeout{2000}; } /* namespace */ @@ -239,6 +238,10 @@ void Controller::doOnPacketReceived(UniqueLock& lock, const protocol::Packet& pa processWebvttTimestamp(static_cast(packet)); break; } + case protocol::Packet::Type::FLUSH: { + processFlushPacket(static_cast(packet)); + break; + } case protocol::Packet::Type::PAUSE: { processPausePacket(static_cast(packet)); break; @@ -373,7 +376,7 @@ void Controller::processTtmlSelection(const protocol::PacketTtmlSelection& packe common::Properties properties; try { - properties = m_asLstnr->getData(as_data_acq_timeout); + properties = m_asLstnr->getData(); } catch (std::exception const& e) { m_logger.oserror(__LOGGER_FUNC__, " exception: ", e.what()); } @@ -458,6 +461,14 @@ void Controller::processWebvttTimestamp(const protocol::PacketWebvttTimestamp& p forAllControllers(m_activeControllers, packet, &ctrl::ControllerInterface::processTimestamp); } +void Controller::processFlushPacket(const protocol::PacketChannelSpecific& packet) +{ + auto timing = m_logger.timing(__LOGGER_FUNC__); + forAllControllers(m_activeControllers, packet, &ctrl::ControllerInterface::flush); + m_dataqueue.clear(); + m_renderCond.notify_one(); +} + void Controller::processPausePacket(const protocol::PacketChannelSpecific& packet) { auto timing = m_logger.timing(__LOGGER_FUNC__); diff --git a/subttxrend-app/src/Controller.hpp b/subttxrend-app/src/Controller.hpp index 272b671..70fe8ef 100644 --- a/subttxrend-app/src/Controller.hpp +++ b/subttxrend-app/src/Controller.hpp @@ -50,6 +50,7 @@ #include #include #include +#include #include #include #include @@ -266,6 +267,13 @@ class Controller : private common::NonCopyable, */ void processWebvttTimestamp(const protocol::PacketWebvttTimestamp& timestampPacket); + /** + * Processes flush packet. + * + * @param packet + * flush packet. + */ + void processFlushPacket(const protocol::PacketChannelSpecific& packet); /** * Processes pause packet. * diff --git a/subttxrend-app/src/main.cpp b/subttxrend-app/src/main.cpp index edfb72e..0b5447a 100644 --- a/subttxrend-app/src/main.cpp +++ b/subttxrend-app/src/main.cpp @@ -52,8 +52,9 @@ auto registerSignalListener() int main(int argc, char* argv[]) { +#ifndef __APPLE__ auto exitListener = registerSignalListener(); - +#endif int rv = EXIT_FAILURE; try { @@ -78,10 +79,13 @@ int main(int argc, app.runAsync(); std::cerr << "subttxrend-app started" << std::endl; - auto exitRequested = exitListener.get(); - std::cerr << "subttxrend-app signaled to exit" << std::endl; +#ifndef __APPLE__ + auto signalNum = exitListener.get(); + std::cerr << "subttxrend-app signaled (" << signalNum << ") to exit" << std::endl; +#else // __APPLE__ + app.startBlockingApplicationWindow(); +#endif app.quit(); - rv = EXIT_SUCCESS; } catch (std::exception const& e) diff --git a/subttxrend-app/x86_builder/build.sh b/subttxrend-app/x86_builder/build.sh index 3c5ab8d..05cc7ac 100755 --- a/subttxrend-app/x86_builder/build.sh +++ b/subttxrend-app/x86_builder/build.sh @@ -41,8 +41,10 @@ function build_project_cmake { cmake \ -DCMAKE_INSTALL_PREFIX=$3/usr/local \ + -DCMAKE_MODULE_PATH=$3/usr/local/share/cmake/Modules \ -DCMAKE_BUILD_TYPE=Debug \ -DCMAKE_FIND_ROOT_PATH=$3 \ + -DCMAKE_INSTALL_NAME_DIR=$3/usr/local/lib \ -DCMAKE_CXX_FLAGS="-DPC_BUILD" \ $4 \ $1 @@ -146,7 +148,12 @@ function build_tests { mkdir -p $2 pushd $2 - cmake -DCMAKE_INSTALL_PREFIX=$3/usr/local -DCMAKE_BUILD_TYPE=Debug -DCMAKE_FIND_ROOT_PATH=$3 $4 $1 + cmake -DCMAKE_INSTALL_PREFIX=$3/usr/local \ + -DCMAKE_MODULE_PATH=$3/usr/local/share/cmake/Modules \ + -DCMAKE_BUILD_TYPE=Debug \ + -DCMAKE_FIND_ROOT_PATH=$3 \ + $4 \ + $1 make $PARALLEL_JOBS_OPT if cat Makefile | egrep "\binstall\b" 2> /dev/null @@ -177,6 +184,28 @@ function run_tests_coverage { popd } +function build_components { + build_project_cmake $BASE_DIR/src/rdklogger build/rdklogger $INSTALL_DIR "" + build_project_cmake $AV_PROJECTS_DIR/subttxrend-common build/subttxrend-common $INSTALL_DIR "${SUBTTXREND_COMMON_DFLAGS}" + build_project_cmake $AV_PROJECTS_DIR/ttxdecoder build/ttxdecoder $INSTALL_DIR "" + build_project_cmake $AV_PROJECTS_DIR/dvbsubdecoder build/dvbsubdecoder $INSTALL_DIR "" + build_project_cmake $AV_PROJECTS_DIR/subttxrend-protocol build/subttxrend-protocol $INSTALL_DIR "" + build_project_cmake $AV_PROJECTS_DIR/subttxrend-socksrc build/subttxrend-socksrc $INSTALL_DIR "" + build_project_cmake $AV_PROJECTS_DIR/subttxrend-dbus build/subttxrend-dbus $INSTALL_DIR "" + build_project_cmake $AV_PROJECTS_DIR/subttxrend-gfx build/subttxrend-gfx $INSTALL_DIR -DWITH_OPENGL=1 + build_project_cmake $AV_PROJECTS_DIR/subttxrend-dvbsub build/subttxrend-dvbsub $INSTALL_DIR "" + build_project_cmake $AV_PROJECTS_DIR/subttxrend-ttxt build/subttxrend-ttxt $INSTALL_DIR "" + build_project_cmake $AV_PROJECTS_DIR/subttxrend-ttml build/subttxrend-ttml $INSTALL_DIR "${SUBTTXREND_TTML_DFLAGS}" + build_project_cmake $AV_PROJECTS_DIR/subttxrend-webvtt build/subttxrend-webvtt $INSTALL_DIR "" + build_project_cmake $AV_PROJECTS_DIR/subttxrend-scte build/subttxrend-scte $INSTALL_DIR "" + build_project_cmake $AV_PROJECTS_DIR/subttxrend-cc build/subttxrend-cc $INSTALL_DIR "" + build_project_cmake $AV_PROJECTS_DIR/subttxrend-ctrl build/subttxrend-ctrl $INSTALL_DIR "" + build_project_cmake $AV_PROJECTS_DIR/subttxrend-app build/subttxrend-app $INSTALL_DIR -DINSTALL_CONFIG_FILE=OFF + build_project_cmake $AV_PROJECTS_DIR/subttxrend-testapps build/subttxrend-testapps $INSTALL_DIR "" + + ls -Rl $INSTALL_DIR +} + #============================================================================ #============================================================================ @@ -190,15 +219,16 @@ if [ "$#" -lt 1 ] ; then echo "Example: $0 build" echo "---------------------------------------------" echo "Available commands:" - echo "full: clean + build + dotest + run" - echo "clean: clean everything" - echo "build: build everything" - echo "run: run the application" - echo "fast: build with -j8 (jobs) option" - echo "dotest: build, build tests, run tests" + echo "full: clean + build + dotest + run" + echo "clean: clean everything" + echo "build: build everything" + echo "buildwithas: build everything with as clients and lgi transformer enabled" + echo "run: run the application" + echo "fast: build with -j8 (jobs) option" + echo "dotest: build, build tests, run tests" echo "buildunits $1: build specified unit" - echo "testunits $1: build, build tests, run tests for unit" - echo "doc: build, build docs, show docs warnings" + echo "testunits $1: build, build tests, run tests for unit" + echo "doc: build, build docs, show docs warnings" echo "---------------------------------------------" echo "" exit @@ -236,10 +266,18 @@ INSTALL_DIR=$OE_BUILD_DIR/subttxrend-app/x86_builder/build/install RDK_COMPONENTS_DIR=$ONEMW_ROOT_DIR/components/generic/dvb/ RDK_PATCHES_DIR=$ONEMW_ROOT_DIR/meta-lgi-om-common/meta-rdk/recipes-extended/ +UTILS_DFLAGS="-DBUILD_PC=1" +SUBTTXREND_COMMON_DFLAGS="-DBUILD_RDK_REFERENCE=1" +SUBTTXREND_TTML_DFLAGS="-DBUILD_RDK_REFERENCE=1" + # # export path for .pc for pkg-config # +if [[ "$OSTYPE" == "darwin"* ]]; then +export PKG_CONFIG_PATH+=$INSTALL_DIR/usr/lib/pkgconfig/:$INSTALL_DIR/usr/local/lib/pkgconfig/:/opt/homebrew/lib/pkgconfig +else export PKG_CONFIG_PATH+=$INSTALL_DIR/usr/lib/pkgconfig/:$INSTALL_DIR/usr/local/lib/pkgconfig/ +fi if [ "$COMMAND" == "full" ] ; then @@ -254,24 +292,15 @@ rm -rf build elif [ "$COMMAND" == "build" ] ; then -build_project_cmake $BASE_DIR/src/rdklogger build/rdklogger $INSTALL_DIR "" -build_project_cmake $AV_PROJECTS_DIR/subttxrend-common build/subttxrend-common $INSTALL_DIR -DBUILD_RDK_REFERENCE=1 -build_project_cmake $AV_PROJECTS_DIR/ttxdecoder build/ttxdecoder $INSTALL_DIR "" -build_project_cmake $AV_PROJECTS_DIR/dvbsubdecoder build/dvbsubdecoder $INSTALL_DIR "" -build_project_cmake $AV_PROJECTS_DIR/subttxrend-protocol build/subttxrend-protocol $INSTALL_DIR "" -build_project_cmake $AV_PROJECTS_DIR/subttxrend-socksrc build/subttxrend-socksrc $INSTALL_DIR "" -build_project_cmake $AV_PROJECTS_DIR/subttxrend-dbus build/subttxrend-dbus $INSTALL_DIR "" -build_project_cmake $AV_PROJECTS_DIR/subttxrend-gfx build/subttxrend-gfx $INSTALL_DIR -DWITH_OPENGL=1 -build_project_cmake $AV_PROJECTS_DIR/subttxrend-dvbsub build/subttxrend-dvbsub $INSTALL_DIR "" -build_project_cmake $AV_PROJECTS_DIR/subttxrend-ttxt build/subttxrend-ttxt $INSTALL_DIR "" -build_project_cmake $AV_PROJECTS_DIR/subttxrend-ttml build/subttxrend-ttml $INSTALL_DIR -DBUILD_RDK_REFERENCE=1 -build_project_cmake $AV_PROJECTS_DIR/subttxrend-webvtt build/subttxrend-webvtt $INSTALL_DIR "" -build_project_cmake $AV_PROJECTS_DIR/subttxrend-scte build/subttxrend-scte $INSTALL_DIR "" -build_project_cmake $AV_PROJECTS_DIR/subttxrend-cc build/subttxrend-cc $INSTALL_DIR "" -build_project_cmake $AV_PROJECTS_DIR/subttxrend-app build/subttxrend-app $INSTALL_DIR -DINSTALL_CONFIG_FILE=OFF -build_project_cmake $AV_PROJECTS_DIR/subttxrend-testapps build/subttxrend-testapps $INSTALL_DIR "" - -ls -Rl $INSTALL_DIR +build_components + +elif [ "$COMMAND" == "buildwithas" ] ; then + +UTILS_DFLAGS="-DBUILD_PC=1 -DENABLE_AS=ON" +SUBTTXREND_COMMON_DFLAGS="-DBUILD_RDK_REFERENCE=0" +SUBTTXREND_TTML_DFLAGS="-DBUILD_RDK_REFERENCE=0" + +build_components elif [ "$COMMAND" == "buildunits" ] ; then @@ -352,6 +381,7 @@ elif [ "$COMMAND" == "doc" ] ; then $0 fast build_doc_cmake build/subttxrend-app +build_doc_cmake build/subttxrend-ctrl build_doc_cmake build/subttxrend-common build_doc_cmake build/subttxrend-dbus build_doc_cmake build/subttxrend-protocol @@ -363,6 +393,7 @@ build_doc_cmake build/subttxrend-ttml build_doc_cmake build/ttxdecoder build_doc_cmake build/dvbsubdecoder build_doc_aggregate build/documentation $BASE_DIR/src/doc \ + $BASE_DIR/build/subttxrend-ctrl \ $BASE_DIR/build/subttxrend-app \ $BASE_DIR/build/subttxrend-common \ $BASE_DIR/build/subttxrend-dbus \ diff --git a/subttxrend-app/x86_builder/src/rdklogger/include/rdk_debug.h b/subttxrend-app/x86_builder/src/rdklogger/include/rdk_debug.h index 01e42db..0dad804 100644 --- a/subttxrend-app/x86_builder/src/rdklogger/include/rdk_debug.h +++ b/subttxrend-app/x86_builder/src/rdklogger/include/rdk_debug.h @@ -63,13 +63,11 @@ typedef enum #define RDK_LOG_TRACE8 RDK_LOG_TRACE #define RDK_LOG_TRACE9 RDK_LOG_TRACE -#define rdk_dbg_enabled rdk_logger_is_logLevel_enabled - rdk_Error rdk_logger_init(const char* debugConfigFile); rdk_Error rdk_logger_deinit(); -rdk_logger_Bool rdk_dbg_enabled(const char *module, +rdk_logger_Bool rdk_logger_is_logLevel_enabled(const char *module, rdk_LogLevel level); void RDK_LOG(rdk_LogLevel level, diff --git a/subttxrend-app/x86_builder/src/rdklogger/src/logger.cpp b/subttxrend-app/x86_builder/src/rdklogger/src/logger.cpp index 35dd4bc..2a2840b 100644 --- a/subttxrend-app/x86_builder/src/rdklogger/src/logger.cpp +++ b/subttxrend-app/x86_builder/src/rdklogger/src/logger.cpp @@ -73,7 +73,7 @@ rdk_Error rdk_logger_deinit() return 0; } -rdk_logger_Bool rdk_dbg_enabled(const char *module, +rdk_logger_Bool rdk_logger_is_logLevel_enabled(const char *module, rdk_LogLevel level) { return true; diff --git a/subttxrend-cc/CMakeLists.txt b/subttxrend-cc/CMakeLists.txt index f8bd4e5..c06733e 100644 --- a/subttxrend-cc/CMakeLists.txt +++ b/subttxrend-cc/CMakeLists.txt @@ -41,6 +41,7 @@ ENDIF(CMAKE_COMPILER_IS_GNUCXX) # find_package(LibSubTtxRendCommon REQUIRED) find_package(LibSubTtxRendGfx REQUIRED) +find_package(LibSubTtxRendProtocol REQUIRED) # # Include directories @@ -49,6 +50,7 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) include_directories(${LIBSUBTTXRENDCOMMON_INCLUDE_DIRS}) include_directories(${LIBSUBTTXRENDGFX_INCLUDE_DIRS}) include_directories(${LIBCOMMON_INCLUDE_DIRS}) +include_directories(${LIBSUBTTXRENDPROTOCOL_INCLUDE_DIRS}) # # Public headers @@ -98,7 +100,7 @@ set_property(TARGET ${LIBRARY_NAME} PROPERTY SOVERSION 0) set_property(TARGET ${LIBRARY_NAME} PROPERTY PUBLIC_HEADER ${SUBTTXREND_CC_PUBLIC_HEADERS}) target_link_libraries(${LIBRARY_NAME} ${LIBSUBTTXRENDCOMMON_LIBRARIES}) target_link_libraries(${LIBRARY_NAME} ${LIBSUBTTXRENDGFX_LIBRARIES}) -target_link_libraries(${LIBRARY_NAME} ${LIBDVBSUBDECODER_LIBRARIES}) +target_link_libraries(${LIBRARY_NAME} ${LIBSUBTTXRENDPROTOCOL_LIBRARIES}) # # Install rules @@ -119,7 +121,7 @@ find_package(PkgConfig REQUIRED) set(PACKAGE_NAME "${PROJECT_NAME}") set(PACKAGE_DESCRIPTION "Subtitles Renderer - Closed Captions Renderer") set(PACKAGE_VERSION "0.0.0") -set(PACKAGE_REQUIRES "") +set(PACKAGE_REQUIRES "${LIBSUBTTXRENDGFX_PACKAGE_NAME} ${LIBSUBTTXRENDPROTOCOL_PACKAGE_NAME}") set(PACKAGE_LIB_NAME "${LIBRARY_NAME}") if(PKG_CONFIG_FOUND) diff --git a/subttxrend-dvbsub/cmake/modules/FindLibDvbGfxEngine.cmake b/subttxrend-cc/cmake/modules/FindLibSubTtxRendProtocol.cmake similarity index 87% rename from subttxrend-dvbsub/cmake/modules/FindLibDvbGfxEngine.cmake rename to subttxrend-cc/cmake/modules/FindLibSubTtxRendProtocol.cmake index 94680a5..4e341be 100644 --- a/subttxrend-dvbsub/cmake/modules/FindLibDvbGfxEngine.cmake +++ b/subttxrend-cc/cmake/modules/FindLibSubTtxRendProtocol.cmake @@ -17,8 +17,8 @@ include(PkgConfigHelper) -pkgconfig_resolve(LibDvbGfxEngine - dvb-graphics-engine - vlGraphics.h - dvbgraphicsengine +pkgconfig_resolve(LibSubTtxRendProtocol + subttxrend-protocol + subttxrend/protocol/Packet.hpp + subttxrend-protocol ) diff --git a/subttxrend-cc/include/CcRenderer.hpp b/subttxrend-cc/include/CcRenderer.hpp index 4744572..42f28c6 100644 --- a/subttxrend-cc/include/CcRenderer.hpp +++ b/subttxrend-cc/include/CcRenderer.hpp @@ -44,7 +44,7 @@ class Renderer: public Gfx void drawBackground(const Point& p, const Dimensions& dimensions, Color color) override; virtual void drawBorder(const Point& p, const Dimensions& dimensions, Color color, - Color br_color, WindowBorder border_type); + Color br_color, WindowBorder border_type) override; void update() override; void clear() override; void show() override; diff --git a/subttxrend-cc/include/CcTextGfxDrawer.hpp b/subttxrend-cc/include/CcTextGfxDrawer.hpp index 3623651..af29fac 100644 --- a/subttxrend-cc/include/CcTextGfxDrawer.hpp +++ b/subttxrend-cc/include/CcTextGfxDrawer.hpp @@ -57,7 +57,7 @@ class TextGfxDrawer: public TextDrawer bool drawable() override; bool backspace() override; void setColumn(int column) override; - void transparentSpace(bool nonbreaking); + void transparentSpace(bool nonbreaking) override; void setPenAttributes(PenAttributes penattrs) override; void getPenAttributes(PenAttributes &penattrs) override; diff --git a/subttxrend-cc/src/CcWindow.cpp b/subttxrend-cc/src/CcWindow.cpp index 105fa6e..8b69211 100644 --- a/subttxrend-cc/src/CcWindow.cpp +++ b/subttxrend-cc/src/CcWindow.cpp @@ -501,6 +501,17 @@ Dimensions Window::calculateRects4Text(std::vector& tdRects) // If we enable the ability to change text size per row then we need to make sure // that this row does not overwrite the row above (ie cant do row * this-row-font-height) + if ((previousTextDrawer != m_textDrawers.end()) && + ((*textDrawer)->row > (*previousTextDrawer)->row)) + { + if ((tdRects.back().y + tdRects.back().h) > textRelativeY) + { + logger.debug("%s calculated - %d expected %d",__LOGGER_FUNC__,textRelativeY, tdRects.back().y + tdRects.back().h); + textRelativeY = tdRects.back().y + tdRects.back().h; + } + } + + (*textDrawer)->padding = 0; // If this text drawer row is assigned to the same row as the last one, diff --git a/subttxrend-common/CMakeLists.txt b/subttxrend-common/CMakeLists.txt index 6e1edb0..f08b700 100644 --- a/subttxrend-common/CMakeLists.txt +++ b/subttxrend-common/CMakeLists.txt @@ -137,8 +137,6 @@ set_property(TARGET ${LIBRARY_NAME} PROPERTY VERSION 0.0.0) set_property(TARGET ${LIBRARY_NAME} PROPERTY SOVERSION 0) set_property(TARGET ${LIBRARY_NAME} PROPERTY PUBLIC_HEADER ${SUBTTXREND_COMMON_PUBLIC_HEADERS}) target_link_libraries(${LIBRARY_NAME} ${LIBRDKLOGGER_LIBRARIES}) -target_link_libraries(${LIBRARY_NAME} boost_system) -target_link_libraries(${LIBRARY_NAME} pthread) # # Install rules diff --git a/subttxrend-common/include/AsListener.hpp b/subttxrend-common/include/AsListener.hpp index 1ca556e..66cee76 100644 --- a/subttxrend-common/include/AsListener.hpp +++ b/subttxrend-common/include/AsListener.hpp @@ -62,7 +62,7 @@ class AsListener public: virtual ~AsListener() = default; virtual AsHelpers getHelpers() = 0; - virtual Properties getData(std::chrono::milliseconds timeout) = 0; + virtual Properties getData() = 0; }; } // namespace common diff --git a/subttxrend-common/include/TtmlAsClient.hpp b/subttxrend-common/include/TtmlAsClient.hpp index 00d2d5d..d4fcb6e 100644 --- a/subttxrend-common/include/TtmlAsClient.hpp +++ b/subttxrend-common/include/TtmlAsClient.hpp @@ -45,7 +45,7 @@ class TtmlAsClient final : public AsListener ~TtmlAsClient(); AsHelpers getHelpers() override; - Properties getData(std::chrono::milliseconds timeout) override; + Properties getData() override; private: class Impl; diff --git a/subttxrend-common/src/clients-generic/TtmlAsClient.cpp b/subttxrend-common/src/clients-generic/TtmlAsClient.cpp index e826f3d..2ef6a4b 100644 --- a/subttxrend-common/src/clients-generic/TtmlAsClient.cpp +++ b/subttxrend-common/src/clients-generic/TtmlAsClient.cpp @@ -28,20 +28,12 @@ namespace common class TtmlAsClient::Impl final { public: - Impl() - { - } - - ~Impl() - { - } - AsHelpers getHelpers() { return AsHelpers{}; } - Properties getData(std::chrono::milliseconds timeout) + Properties getData() { return Properties{}; } @@ -54,9 +46,9 @@ TtmlAsClient::TtmlAsClient() } TtmlAsClient::~TtmlAsClient() = default; -Properties TtmlAsClient::getData(std::chrono::milliseconds timeout) +Properties TtmlAsClient::getData() { - return impl->getData(timeout); + return impl->getData(); } AsHelpers TtmlAsClient::getHelpers() { diff --git a/subttxrend-common/test/rdk_debug.cpp b/subttxrend-common/test/rdk_debug.cpp index 646808c..6ceb630 100644 --- a/subttxrend-common/test/rdk_debug.cpp +++ b/subttxrend-common/test/rdk_debug.cpp @@ -73,7 +73,7 @@ rdk_Error rdk_logger_deinit() return 0; } -rdk_logger_Bool rdk_dbg_enabled(const char *module, +rdk_logger_Bool rdk_logger_is_logLevel_enabled(const char *module, rdk_LogLevel level) { return true; diff --git a/subttxrend-common/test/rdk_debug.h b/subttxrend-common/test/rdk_debug.h index 5d1d6e5..23657a0 100644 --- a/subttxrend-common/test/rdk_debug.h +++ b/subttxrend-common/test/rdk_debug.h @@ -45,26 +45,30 @@ typedef enum RDK_LOG_INFO, RDK_LOG_DEBUG, - RDK_LOG_TRACE1, - RDK_LOG_TRACE2, - RDK_LOG_TRACE3, - RDK_LOG_TRACE4, - RDK_LOG_TRACE5, - RDK_LOG_TRACE6, - RDK_LOG_TRACE7, - RDK_LOG_TRACE8, - RDK_LOG_TRACE9, + RDK_LOG_TRACE, ENUM_RDK_LOG_COUNT } rdk_LogLevel; +/** + * Updated as per rdk_logger to allow compatibility of subttxrend-common/test written using loglevels RDK_LOG_TRACE1..RDK_LOG_TRACE9 and function rdk_dbg_enabled + */ +#define RDK_LOG_TRACE1 RDK_LOG_TRACE +#define RDK_LOG_TRACE2 RDK_LOG_TRACE +#define RDK_LOG_TRACE3 RDK_LOG_TRACE +#define RDK_LOG_TRACE4 RDK_LOG_TRACE +#define RDK_LOG_TRACE5 RDK_LOG_TRACE +#define RDK_LOG_TRACE6 RDK_LOG_TRACE +#define RDK_LOG_TRACE7 RDK_LOG_TRACE +#define RDK_LOG_TRACE8 RDK_LOG_TRACE +#define RDK_LOG_TRACE9 RDK_LOG_TRACE + rdk_Error rdk_logger_init(const char* debugConfigFile); rdk_Error rdk_logger_deinit(); -rdk_logger_Bool rdk_dbg_enabled(const char *module, +rdk_logger_Bool rdk_logger_is_logLevel_enabled(const char *module, rdk_LogLevel level); - void RDK_LOG(rdk_LogLevel level, const char *module, const char *format, diff --git a/subttxrend-ctrl/src/Configuration.hpp b/subttxrend-ctrl/src/Configuration.hpp index 47a8c3f..762bd83 100644 --- a/subttxrend-ctrl/src/Configuration.hpp +++ b/subttxrend-ctrl/src/Configuration.hpp @@ -142,10 +142,10 @@ class Configuration : private common::ConfigProvider /** Logger config provider. */ common::PrefixConfigProvider m_loggerConfigProvider; - /** Logger config provider. */ + /** TTML config provider. */ common::PrefixConfigProvider m_ttmlConfigProvider; - /** Logger config provider. */ + /** WebVTT config provider. */ common::PrefixConfigProvider m_webvttConfigProvider; }; diff --git a/subttxrend-ctrl/src/ControllerInterface.hpp b/subttxrend-ctrl/src/ControllerInterface.hpp index a4eadd5..0273984 100644 --- a/subttxrend-ctrl/src/ControllerInterface.hpp +++ b/subttxrend-ctrl/src/ControllerInterface.hpp @@ -61,6 +61,7 @@ class ControllerInterface : private common::NonCopyable virtual void processTimestamp(const protocol::Packet& ){} virtual void processInfo(const protocol::Packet& ){} + virtual void flush() {} virtual void pause() {} virtual void resume() {} virtual void processSetCCAttributesPacket(const protocol::PacketSetCCAttributes&){} @@ -94,7 +95,7 @@ class MutexedController final : public ControllerInterface std::lock_guard lock{mutex}; controller.activate(); } - virtual void deactivate() override + virtual void deactivate() override { std::lock_guard lock{mutex}; controller.deactivate(); @@ -118,6 +119,12 @@ class MutexedController final : public ControllerInterface controller.processTimestamp(packet); } + void flush() override + { + std::lock_guard lock{mutex}; + controller.flush(); + } + void pause() override { std::lock_guard lock{mutex}; diff --git a/subttxrend-ctrl/src/PesFinder.hpp b/subttxrend-ctrl/src/PesFinder.hpp index 38f1932..960df54 100644 --- a/subttxrend-ctrl/src/PesFinder.hpp +++ b/subttxrend-ctrl/src/PesFinder.hpp @@ -22,6 +22,7 @@ #define ONEMW_SRC_AV_SUBTTXREND_APP_SRC_PESFINDER_HPP_ #include +#include #include diff --git a/subttxrend-ctrl/src/TtmlController.cpp b/subttxrend-ctrl/src/TtmlController.cpp index eb9ecf1..1b285e3 100644 --- a/subttxrend-ctrl/src/TtmlController.cpp +++ b/subttxrend-ctrl/src/TtmlController.cpp @@ -129,6 +129,11 @@ void TtmlController::processInfo(const std::string& contentType, const std::stri m_ttmlEngine->setSubtitleInfo(contentType, subsInfo); } +void TtmlController::flush() +{ + m_ttmlEngine->flush(); +} + void TtmlController::pause() { m_ttmlEngine->pause(); diff --git a/subttxrend-ctrl/src/TtmlController.hpp b/subttxrend-ctrl/src/TtmlController.hpp index ef10bf4..fd52d9c 100644 --- a/subttxrend-ctrl/src/TtmlController.hpp +++ b/subttxrend-ctrl/src/TtmlController.hpp @@ -67,6 +67,7 @@ class TtmlController final : public ControllerInterface void mute(bool muted) override; bool wantsData(protocol::PacketChannelSpecific const& packet) const override; + void flush() override; void pause() override; void resume() override; diff --git a/subttxrend-dbus/CMakeLists.txt b/subttxrend-dbus/CMakeLists.txt index 0f2802e..7dd62de 100644 --- a/subttxrend-dbus/CMakeLists.txt +++ b/subttxrend-dbus/CMakeLists.txt @@ -121,11 +121,10 @@ set(SUBTTXREND_DBUS_SOURCES # set(LIBRARY_NAME "subttxrend-dbus") add_library(${LIBRARY_NAME} SHARED ${SUBTTXREND_DBUS_SOURCES}) -set_property(TARGET ${LIBRARY_NAME} PROPERTY CXX_STANDARD 11) +set_property(TARGET ${LIBRARY_NAME} PROPERTY CXX_STANDARD 14) set_property(TARGET ${LIBRARY_NAME} PROPERTY VERSION 0.0.0) set_property(TARGET ${LIBRARY_NAME} PROPERTY SOVERSION 0) set_property(TARGET ${LIBRARY_NAME} PROPERTY PUBLIC_HEADER ${SUBTTXREND_DBUS_PUBLIC_HEADERS}) -target_link_libraries(${LIBRARY_NAME} ${LIBSUBTTXRENDCOMMON_LIBRARIES}) target_link_libraries(${LIBRARY_NAME} ${LIBJANSSON_LIBRARIES}) if(${LIBGIOUNIX_FOUND}) target_link_libraries(${LIBRARY_NAME} ${LIBGIOUNIX_LIBRARIES}) @@ -153,7 +152,7 @@ find_package(PkgConfig REQUIRED) set(PACKAGE_NAME "${PROJECT_NAME}") set(PACKAGE_DESCRIPTION "Subtitles/Teletext Renderer - DBus API") set(PACKAGE_VERSION "0.0.0") -set(PACKAGE_REQUIRES "") +set(PACKAGE_REQUIRES "${LIBJANSSON_PACKAGE_NAME} glib-2.0") set(PACKAGE_LIB_NAME "${LIBRARY_NAME}") if(PKG_CONFIG_FOUND) diff --git a/subttxrend-dbus/cmake/modules/FindLibGlib.cmake b/subttxrend-dbus/cmake/modules/FindLibGlib.cmake index 6c03265..8cf81be 100644 --- a/subttxrend-dbus/cmake/modules/FindLibGlib.cmake +++ b/subttxrend-dbus/cmake/modules/FindLibGlib.cmake @@ -54,20 +54,28 @@ message(STATUS "PC_LIBGLIB_VERSION = ${PC_LIBGLIB_VERSION}") message(STATUS "PC_LIBGLIB_PREFIX = ${PC_LIBGLIB_PREFIX}") message(STATUS "PC_LIBGLIB_INCLUDEDIR = ${PC_LIBGLIB_INCLUDEDIR}") message(STATUS "PC_LIBGLIB_LIBDIR = ${PC_LIBGLIB_LIBDIR}") +message(STATUS "PC_LIBGLIB_LINK_LIBRARIES = ${PC_LIBGLIB_LINK_LIBRARIES}") -find_path(LIBGLIB_INCLUDE_DIR - NAMES glib.h - HINTS ${PC_LIBGLIB_INCLUDEDIR} ${PC_LIBGLIB_INCLUDE_DIRS} - PATH_SUFFIXES glib-2.0 ) +if(CMAKE_SYSTEM_NAME STREQUAL Darwin) + # Trust the find_package tool + set(LIBGLIB_INCLUDE_DIR ${PC_LIBGLIB_INCLUDEDIR}) + set(LIBGLIB_CONFIG_INCLUDE_DIR ${PC_LIBGLIB_INCLUDEDIR}) + set(LIBGLIB_LIBRARY ${PC_LIBGLIB_LINK_LIBRARIES}) +else() + find_path(LIBGLIB_INCLUDE_DIR + NAMES glib.h + HINTS ${PC_LIBGLIB_INCLUDEDIR} ${PC_LIBGLIB_INCLUDE_DIRS} + PATH_SUFFIXES glib-2.0 ) -find_path(LIBGLIB_CONFIG_INCLUDE_DIR - NAMES glibconfig.h - HINTS ${PC_LIBGLIB_INCLUDEDIR} ${PC_LIBGLIB_INCLUDE_DIRS} - PATH_SUFFIXES lib/glib-2.0/include ../lib/glib-2.0/include ) + find_path(LIBGLIB_CONFIG_INCLUDE_DIR + NAMES glibconfig.h + HINTS ${PC_LIBGLIB_INCLUDEDIR} ${PC_LIBGLIB_INCLUDE_DIRS} + PATH_SUFFIXES lib/glib-2.0/include ../lib/glib-2.0/include ) -find_library(LIBGLIB_LIBRARY - NAMES glib-2.0 - HINTS ${PC_LIBGLIB_LIBDIR} ${PC_LIBGLIB_LIBRARY_DIRS} ) + find_library(LIBGLIB_LIBRARY + NAMES glib-2.0 + HINTS ${PC_LIBGLIB_LIBDIR} ${PC_LIBGLIB_LIBRARY_DIRS} ) +endif() message(STATUS "LIBGLIB_INCLUDE_DIR = ${LIBGLIB_INCLUDE_DIR}") message(STATUS "LIBGLIB_CONFIG_INCLUDE_DIR = ${LIBGLIB_CONFIG_INCLUDE_DIR}") @@ -81,5 +89,10 @@ find_package_handle_standard_args(LibGlib DEFAULT_MSG mark_as_advanced(LIBGLIB_INCLUDE_DIR LIBGLIB_CONFIG_INCLUDE_DIR LIBGLIB_LIBRARY) -set(LIBGLIB_INCLUDE_DIRS ${LIBGLIB_INCLUDE_DIR} ${LIBGLIB_CONFIG_INCLUDE_DIR}) +if(CMAKE_SYSTEM_NAME STREQUAL Darwin) + set(LIBGLIB_INCLUDE_DIRS ${LIBGLIB_INCLUDE_DIR} ${PC_LIBGLIB_INCLUDE_DIRS}) +else() + set(LIBGLIB_INCLUDE_DIRS ${LIBGLIB_INCLUDE_DIR} ${LIBGLIB_CONFIG_INCLUDE_DIR}) +endif() set(LIBGLIB_LIBRARIES ${LIBGLIB_LIBRARY}) + diff --git a/subttxrend-dvbsub/CMakeLists.txt b/subttxrend-dvbsub/CMakeLists.txt index c6e9dd4..8d6f2dc 100644 --- a/subttxrend-dvbsub/CMakeLists.txt +++ b/subttxrend-dvbsub/CMakeLists.txt @@ -103,7 +103,7 @@ set(SUBTTXREND_DVBSUB_SOURCES # set(LIBRARY_NAME "subttxrend-dvbsub") add_library(${LIBRARY_NAME} SHARED ${SUBTTXREND_DVBSUB_SOURCES}) -set_property(TARGET ${LIBRARY_NAME} PROPERTY CXX_STANDARD 11) +set_property(TARGET ${LIBRARY_NAME} PROPERTY CXX_STANDARD 14) set_property(TARGET ${LIBRARY_NAME} PROPERTY VERSION 0.0.0) set_property(TARGET ${LIBRARY_NAME} PROPERTY SOVERSION 0) set_property(TARGET ${LIBRARY_NAME} PROPERTY PUBLIC_HEADER ${SUBTTXREND_DVBSUB_PUBLIC_HEADERS}) @@ -130,7 +130,7 @@ find_package(PkgConfig REQUIRED) set(PACKAGE_NAME "${PROJECT_NAME}") set(PACKAGE_DESCRIPTION "Subtitles/Teletext Renderer - DVB Subtitles Renderer") set(PACKAGE_VERSION "0.0.0") -set(PACKAGE_REQUIRES "") +set(PACKAGE_REQUIRES "${LIBSUBTTXRENDGFX_PACKAGE_NAME} ${LIBDVBSUBDECODER_PACKAGE_NAME}") set(PACKAGE_LIB_NAME "${LIBRARY_NAME}") if(PKG_CONFIG_FOUND) diff --git a/subttxrend-gfx/CMakeLists.txt b/subttxrend-gfx/CMakeLists.txt index 7051ba5..7c8abb5 100644 --- a/subttxrend-gfx/CMakeLists.txt +++ b/subttxrend-gfx/CMakeLists.txt @@ -18,9 +18,11 @@ project(subttxrend-gfx) cmake_minimum_required (VERSION 3.2) - +if(CMAKE_SYSTEM_NAME STREQUAL Darwin) +add_subdirectory(quartzcpp) +else() add_subdirectory(waylandcpp) - +endif() # # Directory with modules # @@ -30,7 +32,6 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/mo # Configuration variables # option(WITH_OPENGL "Include the OpenGL support" OFF) -option(WITH_WESTEROS "Include the Westeros support" OFF) # # Extra compiler / linker options @@ -45,17 +46,19 @@ ENDIF(CMAKE_COMPILER_IS_GNUCXX) find_package(LibGlib REQUIRED) find_package(LibGio REQUIRED) find_package(LibSubTtxRendCommon REQUIRED) -find_package(LibWaylandClient REQUIRED) find_package(LibXkbCommon REQUIRED) find_package(LibFontConfig REQUIRED) find_package(Freetype REQUIRED) find_package(HarfBuzz REQUIRED) find_package(LibPng REQUIRED) +if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) +find_package(LibWaylandClient REQUIRED) if(WITH_OPENGL) find_package(LibWaylandEgl REQUIRED) find_package(LibEgl REQUIRED) find_package(LibGlesV2 REQUIRED) endif(WITH_OPENGL) +endif() if(WITH_WESTEROS) find_package(LibWesterosSimpleShellClient REQUIRED) endif(WITH_WESTEROS) @@ -64,7 +67,9 @@ endif(WITH_WESTEROS) # Include directories # include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/waylandcpp/include) +if(CMAKE_SYSTEM_NAME STREQUAL Darwin) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/quartzcpp/include) +endif() include_directories(${CMAKE_CURRENT_SOURCE_DIR}/ftcpp/include) include_directories(${LIBGLIB_INCLUDE_DIRS}) include_directories(${LIBGIO_INCLUDE_DIRS}) @@ -75,11 +80,14 @@ include_directories(${LIBFONTCONFIG_INCLUDE_DIRS}) include_directories(${FREETYPE_INCLUDE_DIRS}) include_directories(${LIBHARFBUZZ_INCLUDE_DIRS}) include_directories(${LIBPNG_INCLUDE_DIRS}) +if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/waylandcpp/include) if(WITH_OPENGL) include_directories(${LIBWAYLANDEGL_INCLUDE_DIRS}) include_directories(${LIBEGL_INCLUDE_DIRS}) include_directories(${LIBGLESV2_INCLUDE_DIRS}) endif(WITH_OPENGL) +endif() if(WITH_WESTEROS) include_directories(${LIBWESTEROSSIMPLESHELLCLIENT_INCLUDE_DIRS}) endif(WITH_WESTEROS) @@ -143,13 +151,17 @@ set(SUBTTXREND_GFX_SOURCES src/Factory.cpp src/FontStripImpl.cpp src/WindowImpl.cpp - src/WaylandBackend.cpp - src/WaylandBackendLoopGlib.cpp src/PrerenderedFontImpl.cpp src/Base64ToPixmap.cpp src/PrerenderedFontCache.cpp ) +if(CMAKE_SYSTEM_NAME STREQUAL Darwin) +add_definitions(-DBACKEND_TYPE=BACKEND_TYPE_QUARTZ) +list(APPEND SUBTTXREND_GFX_SOURCES src/QuartzBackend.cpp) +else() +list(APPEND SUBTTXREND_GFX_SOURCES src/WaylandBackend.cpp) +list(APPEND SUBTTXREND_GFX_SOURCES src/WaylandBackendLoopGlib.cpp) if(WITH_OPENGL) list(APPEND SUBTTXREND_GFX_SOURCES src/WaylandBackendEgl.cpp) list(APPEND SUBTTXREND_GFX_SOURCES src/GLcpp.cpp) @@ -160,6 +172,7 @@ list(APPEND SUBTTXREND_GFX_SOURCES src/WaylandBuffer.cpp) list(APPEND SUBTTXREND_GFX_SOURCES src/WaylandBufferManager.cpp) add_definitions(-DBACKEND_TYPE=BACKEND_TYPE_SHM) endif(WITH_OPENGL) +endif() if(WITH_WESTEROS) add_definitions(-DWESTEROS) endif(WITH_WESTEROS) @@ -173,14 +186,18 @@ set_property(TARGET ${LIBRARY_NAME} PROPERTY CXX_STANDARD 14) set_property(TARGET ${LIBRARY_NAME} PROPERTY VERSION 0.0.0) set_property(TARGET ${LIBRARY_NAME} PROPERTY SOVERSION 0) set_property(TARGET ${LIBRARY_NAME} PROPERTY PUBLIC_HEADER ${SUBTTXREND_GFX_PUBLIC_HEADERS}) -target_link_libraries(${LIBRARY_NAME} waylandcpp) +if(CMAKE_SYSTEM_NAME STREQUAL Darwin) + target_link_libraries(${LIBRARY_NAME} quartzcpp) +endif() target_link_libraries(${LIBRARY_NAME} ${LIBSUBTTXRENDCOMMON_LIBRARIES}) -target_link_libraries(${LIBRARY_NAME} ${LIBWAYLANDCLIENT_LIBRARIES}) target_link_libraries(${LIBRARY_NAME} ${LIBXKBCOMMON_LIBRARIES}) target_link_libraries(${LIBRARY_NAME} ${LIBFONTCONFIG_LIBRARIES}) target_link_libraries(${LIBRARY_NAME} ${FREETYPE_LIBRARIES}) target_link_libraries(${LIBRARY_NAME} ${LIBHARFBUZZ_LIBRARIES}) target_link_libraries(${LIBRARY_NAME} ${LIBPNG_LIBRARIES}) +if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) +target_link_libraries(${LIBRARY_NAME} waylandcpp) +target_link_libraries(${LIBRARY_NAME} ${LIBWAYLANDCLIENT_LIBRARIES}) target_compile_definitions(${LIBRARY_NAME} PRIVATE BACKEND_TYPE_EGL=2 BACKEND_TYPE_SHM=1) if(WITH_OPENGL) @@ -188,7 +205,8 @@ target_link_libraries(${LIBRARY_NAME} ${LIBWAYLANDEGL_LIBRARIES}) target_link_libraries(${LIBRARY_NAME} ${LIBEGL_LIBRARIES}) target_link_libraries(${LIBRARY_NAME} ${LIBGLESV2_LIBRARIES}) endif(WITH_OPENGL) -target_link_libraries(${LIBRARY_NAME} ${LIBGIO_LIBRARIES}) +endif() +target_link_libraries(${LIBRARY_NAME} ${LIBGIO_LINK_LIBRARIES}) target_link_libraries(${LIBRARY_NAME} ${LIBGLIB_LIBRARIES}) if(WITH_WESTEROS) target_link_libraries(${LIBRARY_NAME} ${LIBWESTEROSSIMPLESHELLCLIENT_LIBRARIES}) @@ -213,7 +231,10 @@ find_package(PkgConfig REQUIRED) set(PACKAGE_NAME "${PROJECT_NAME}") set(PACKAGE_DESCRIPTION "Subtitles/Teletext Renderer - Gfx Engine") set(PACKAGE_VERSION "0.0.0") -set(PACKAGE_REQUIRES "") +set(PACKAGE_REQUIRES "${LIBSUBTTXRENDCOMMON_PACKAGE_NAME} ${LIBWAYLANDCLIENT_PACKAGE_NAME} ${LIBXKBCOMMON_PACKAGE_NAME} ${LIBFONTCONFIG_PACKAGE_NAME} ${LIBHARFBUZZ_PACKAGE_NAME} ${LIBPNG_PACKAGE_NAME} glib-2.0") +if(WITH_OPENGL) +set(PACKAGE_REQUIRES "${PACKAGE_REQUIRES} ${LIBWAYLANDEGL_PACKAGE_NAME} ${LIBEGL_PACKAGE_NAME} ${LIBGLESV2_PACKAGE_NAME}") +endif(WITH_OPENGL) set(PACKAGE_LIB_NAME "${LIBRARY_NAME}") if(PKG_CONFIG_FOUND) diff --git a/subttxrend-gfx/include/Engine.hpp b/subttxrend-gfx/include/Engine.hpp index 50defd3..db4e06d 100644 --- a/subttxrend-gfx/include/Engine.hpp +++ b/subttxrend-gfx/include/Engine.hpp @@ -109,6 +109,12 @@ class Engine */ virtual void detach(WindowPtr window) = 0; +#ifdef __APPLE__ + /** + * @brief Start blocking application window (block this thread) + */ + virtual void startBlockingApplicationWindow() = 0; +#endif }; /** diff --git a/subttxrend-gfx/include/PrerenderedFont.hpp b/subttxrend-gfx/include/PrerenderedFont.hpp index 07c1054..9d161c4 100644 --- a/subttxrend-gfx/include/PrerenderedFont.hpp +++ b/subttxrend-gfx/include/PrerenderedFont.hpp @@ -22,7 +22,7 @@ #define _SUBTTXREND_GFX_PRERENDEREDFONT_HPP_ #include -#include +#include #include #include #include diff --git a/subttxrend-gfx/quartzcpp/CMakeLists.txt b/subttxrend-gfx/quartzcpp/CMakeLists.txt new file mode 100644 index 0000000..73ea4ac --- /dev/null +++ b/subttxrend-gfx/quartzcpp/CMakeLists.txt @@ -0,0 +1,59 @@ +############################################################################## +# +# If not stated otherwise in this file or this component's license file the +# following copyright and licenses apply: +# +# Copyright 2022 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +############################################################################## + +project(quartzcpp) + +cmake_minimum_required (VERSION 3.2) + +# +# Directory with modules +# +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/modules/") + +# +# Packages to use +# +find_package(LibGlib REQUIRED) +find_library(APPKIT_LIB AppKit) +find_library(GLUT_LIB GLUT) +find_library(OPENGL_LIB OpenGl) +# +# Include directories +# +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) +include_directories(${LIBGLIB_INCLUDE_DIRS}) + +# +# Sources to compile +# +set(DVBGRAPHICSENGINE_SOURCES + src/cocoa_window.mm + ) + +# +# Targets +# +add_library(quartzcpp STATIC ${DVBGRAPHICSENGINE_SOURCES}) +set_property(TARGET quartzcpp PROPERTY CXX_STANDARD 14) +target_link_libraries(quartzcpp PRIVATE ${APPKIT_LIB}) +target_link_libraries(quartzcpp PRIVATE ${GLUT_LIB}) +target_link_libraries(quartzcpp PRIVATE ${OPENGL_LIB}) +install(FILES "assets/RDKLogo.png" DESTINATION assets) diff --git a/subttxrend-gfx/quartzcpp/assets/RDKLogo.png b/subttxrend-gfx/quartzcpp/assets/RDKLogo.png new file mode 100644 index 0000000..e69de29 diff --git a/subttxrend-gfx/quartzcpp/include/cocoa_window.h b/subttxrend-gfx/quartzcpp/include/cocoa_window.h new file mode 100644 index 0000000..fdd9df5 --- /dev/null +++ b/subttxrend-gfx/quartzcpp/include/cocoa_window.h @@ -0,0 +1,73 @@ +/** + * If not stated otherwise in this file or this component's license file the + * following copyright and licenses apply: + * + * Copyright 2022 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file cocoa_window.h + * Subtec Simulator Window + */ + +#ifndef __SUBTEC_COCOA_WINDOW_H +#define __SUBTEC_COCOA_WINDOW_H + +#include +#include + +namespace subttxrend +{ +namespace gfx +{ + +/** + * @brief Create and Run Cocoa Window object. + * + * Note: This is a blocking call + * + * @param width Width of the video that will be displayed in the window. + * @param height Height of the video that will be displayed in the window. + * + * @return int 0 on success when the window closes. + */ +int createAndRunCocoaWindow(int width, int height); + +/** + * @brief Set the Simulator Window Title. + * + * @param title Title to be set + */ +void setSimulatorWindowTitle( const char *title ); + +/** + * @brief Set the Subtitle Data object + * + * @param data Pointer to the subtitle bitmap data. + * @param size Size of the subtitle bitmap data (typically width * height * bytes per pixel). + * @param width Width of the subtitle bitmap image. + * @param height Height of the subtitle bitmap image. + */ +void setSubtitleData(uint8_t * data, unsigned int size, int width, int height); + +/** + * @brief Mute the subtitles (i.e. stop them being displayed) + */ +void muteSubtitles(); + +} // namespace gfx +} // namespace subttxrend + + +#endif //__SUBTEC_COCOA_WINDOW_H diff --git a/subttxrend-gfx/quartzcpp/src/cocoa_window.mm b/subttxrend-gfx/quartzcpp/src/cocoa_window.mm new file mode 100644 index 0000000..0ae2eb1 --- /dev/null +++ b/subttxrend-gfx/quartzcpp/src/cocoa_window.mm @@ -0,0 +1,430 @@ +/** + * If not stated otherwise in this file or this component's license file the + * following copyright and licenses apply: + * + * Copyright 2022 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file cocoa_window.mm + * Subtec Simulator Player + */ +#define GL_SILENCE_DEPRECATION +#import +#import "cocoa_window.h" +#include +#include + +#define ADD_BACKGROUND_IMAGE +//#define USE_TEST_CHECK + +@interface SubtitleView : NSOpenGLView +{ + BOOL _updateTexture; + uint8_t* _data; + unsigned int _size; + GLuint _texture; + NSSize _dimensions; +} +- (void) drawRect: (NSRect) bounds; +- (BOOL) setSubtitleData: (uint8_t*) data withSize: (unsigned int) size andDimensions: (NSSize) dimensions; +- (void) clearSubtitleData; +- (void) releaseBitmapAndTexture; + +@property (readonly) BOOL mute; + +@end + +@implementation SubtitleView + +-(void) drawRect: (NSRect) bounds +{ + NSSize viewSize = self.bounds.size; + + //NSLog(@"SubtitleView::drawRect data=%p, width=%f, height=%f, view width=%f, height=%f, ratio=%f, mute=%s", _data, _dimensions.width, _dimensions.height, viewSize.width, viewSize.height, viewSize.width / viewSize.height, _mute ? "true" : "false"); + + if (_data != NULL) + { +#ifdef USE_TEST_CHECK + _dimensions.width = 1920; + _dimensions.height = 1080; + int checkImageWidth = _dimensions.width; + int checkImageHeight = _dimensions.height; + GLubyte *checkImage = (GLubyte*) malloc(checkImageHeight * checkImageWidth * 4 * sizeof(GLubyte)); + + for (int i = 0; i < checkImageHeight; i++) { + for (int j = 0; j < checkImageWidth; j++) { + int c = ((((i&0x80)==0)^((j&0x80))==0))*255; + unsigned int offset = (i * 4 * checkImageWidth) + (j * 4); + *(checkImage + offset) = (GLubyte) 0; + *(checkImage + offset + 1) = (GLubyte) c; + *(checkImage + offset + 2) = (GLubyte) 0; + *(checkImage + offset + 3) = (GLubyte) c; //0xFF + } + } +#endif + glEnable(GL_DOUBLE); + glEnable(GL_DEPTH); + glEnable(GL_RGB); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glClearColor(0.0f, .0f, 0.0f, 0.0f ); // clear background + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glPixelStorei(GL_PACK_ALIGNMENT, 1); + + if (_texture == 0) + { + NSLog(@"SubtitleView::drawRect generate texture"); + glGenTextures(1, &_texture); + } + + if (_updateTexture == TRUE) + { + NSLog(@"SubtitleView::drawRect update texture"); + glBindTexture(GL_TEXTURE_2D, _texture); +#ifdef USE_TEST_CHECK + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _dimensions.width, _dimensions.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, checkImage); +#else + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _dimensions.width, _dimensions.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, _data); +#endif + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); + _updateTexture = FALSE; + } + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); // Clear the window with current clearing color + glShadeModel(GL_SMOOTH); + glEnable(GL_NORMALIZE); + glPushMatrix(); + { + glDisable(GL_LIGHTING); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, _texture); + glBegin(GL_QUADS); // draw something with the texture on + { + /* Flip the co-ordinate system because MacOS origin is in the (vertically) opposite corner to linux */ + glTexCoord2f(0.0, 1.0); + glVertex2f(-1.0, -1.0); + + glTexCoord2f(1.0, 1.0); + glVertex2f(1.0, -1.0); + + glTexCoord2f(1.0, 0.0); + glVertex2f(1.0, 1.0); + + glTexCoord2f(0.0, 0.0); + glVertex2f(-1.0, 1.0); + } + glEnd(); + } + glPopMatrix(); + glFlush(); + +#ifdef USE_TEST_CHECK + free(checkImage); +#endif + } + +} + +- (BOOL) setSubtitleData: (uint8_t*) data withSize: (unsigned int) size andDimensions: (NSSize) dimensions +{ + NSLog(@"SubtitleView::setSubtitleData data=%p, dataSize=%u, width=%f, height=%f", data, size, dimensions.width, dimensions.height); + + BOOL ret = TRUE; + + if (_data != NULL) + { + free(_data); + _data = NULL; + } + if (data != NULL) + { + _data = (uint8_t*)malloc(size); + if (_data != NULL) + { + _size = size; + memcpy(_data, data, size); + _updateTexture = TRUE; + _mute = FALSE; + } + else + { + ret = FALSE; + } + _dimensions = dimensions; + } + return ret; +} + +- (void) clearSubtitleData +{ + NSLog(@"SubtitleView::clearSubtitleData"); + + if (_data != NULL) + { + memset(_data, 0x00, _size); + _updateTexture = TRUE; + _mute = TRUE; + } +} + +- (void) releaseBitmapAndTexture +{ + NSLog(@"SubtitleView::releaseBitmapAndTexture"); + + if (_data != NULL) + { + free(_data); + _data = NULL; + } + if (_texture != 0) + { + glDeleteTextures(1, &_texture); + } +} + +@end + +@interface BackgroundImageView: NSView +{ +} +- (void) drawRect: (NSRect) rect; + +@property NSImage * backgroundImage; + +@end + + +@implementation BackgroundImageView + +- (void) drawRect: (NSRect) rect +{ + if (_backgroundImage != nil) + { + [_backgroundImage drawInRect:rect]; + } +} + +@end + +@interface VideoWindow: NSWindow +{ + BOOL _windowActive; + BackgroundImageView * _imageView; + SubtitleView * _subtitleView; + NSOpenGLContext* _glContext; + NSSize _aspectRatio; +} + +- (BOOL) isActive; +- (id) initializeWindow: (NSRect) bounds; +- (BOOL) setSubtitleData: (uint8_t*) data withSize: (unsigned int) size andDimensions: (NSSize) dimensions; +- (void) muteSubtitles; + +@end + +static VideoWindow *gCocoaWindow=nil; +static NSApplication* application; + +@implementation VideoWindow + +- (id) initializeWindow: (NSRect) bounds +{ + self = [super initWithContentRect: bounds + styleMask: (NSWindowStyleMaskClosable | NSWindowStyleMaskTitled | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable) + backing: NSBackingStoreBuffered defer: NO screen: nil]; + + [application setDelegate:self]; + [self setReleasedWhenClosed:NO]; + _windowActive = TRUE; + [self setTitle:@"Subtec Test Player"]; + + _aspectRatio = bounds.size; + +#ifdef ADD_BACKGROUND_IMAGE + NSImage *image = [[NSImage alloc]initWithContentsOfFile: @"./build/install/usr/local/assets/RDKLogo.png"]; + if (image == nil) + { + NSLog(@"image nil"); + } + + _imageView = [[BackgroundImageView alloc] initWithFrame:CGRectMake(0, 0, bounds.size.width, bounds.size.height)]; + _imageView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; + _imageView.backgroundImage = image; + [self.contentView addSubview:_imageView]; +#endif + + // Specify the pixel-format attributes. + NSOpenGLPixelFormatAttribute attrs[] = + { + NSOpenGLPFAWindow, + // NSOpenGLPFADoubleBuffer, + NSOpenGLPFADepthSize, 32, + 0 + }; + NSOpenGLPixelFormat* pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs]; + + _subtitleView = [[SubtitleView alloc] initWithFrame:CGRectMake(0, 0, bounds.size.width, bounds.size.height) + pixelFormat:pixFmt]; + [pixFmt release]; + _subtitleView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; + [self.contentView addSubview:_subtitleView]; + + _glContext = [_subtitleView openGLContext]; + GLint opacity = 0; + + [_glContext setValues: &opacity forParameter: NSOpenGLCPSurfaceOpacity]; + [_glContext makeCurrentContext]; + + return self; +} + +- (BOOL) windowShouldClose: (id) sender +{ + _windowActive = FALSE; + return YES; +} + +- (void) applicationDidFinishLaunching: (NSNotification *) notification +{ + [self makeMainWindow]; + [self center]; + [self orderFront:self]; +} + +- (void) applicationWillTerminate: (NSNotification *) notification +{ + if (_subtitleView) + { + [_subtitleView releaseBitmapAndTexture]; + } +} + +- (BOOL) applicationShouldTerminateAfterLastWindowClosed: (NSApplication *) application +{ + return YES; +} + +- (BOOL) isActive +{ + return _windowActive; +} + +- (NSSize) windowWillResize: (NSWindow *) sender toSize: (NSSize) frameSize +{ + sender.contentAspectRatio = NSMakeSize(_aspectRatio.width, _aspectRatio.height); + return frameSize; +} + +- (BOOL) setSubtitleData: (uint8_t*) data withSize: (unsigned int) size andDimensions: (NSSize) dimensions +{ + BOOL ret = TRUE; + + NSLog(@"VideoWindow::setSubtitleData _subtitleView=%p", _subtitleView); + + if (_subtitleView != nil) + { + ret = [_subtitleView setSubtitleData:data withSize:size andDimensions:dimensions]; + if (ret) + { + _subtitleView.needsDisplay = TRUE; + [_subtitleView displayIfNeeded]; + } + } + + return ret; +} + +- (void) muteSubtitles +{ + if (_subtitleView != nil) + { + if (_subtitleView.mute == false) + { + NSLog(@"VideoWindow::muteSubtitles muting subtitles"); + [_subtitleView clearSubtitleData]; + _subtitleView.needsDisplay = TRUE; + [_subtitleView displayIfNeeded]; + } + } +} + +@end + +namespace subttxrend +{ +namespace gfx +{ + +int createAndRunCocoaWindow(int width, int height) +{ + NSRect windowCordinates; + windowCordinates.size.width = width; + windowCordinates.size.height = height; + windowCordinates.origin.x = 0; + windowCordinates.origin.y = 0; + + application = [NSApplication sharedApplication]; + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; + gCocoaWindow = [[VideoWindow alloc] initializeWindow:windowCordinates]; + + [gCocoaWindow orderFront:nil]; + [gCocoaWindow setLevel: NSNormalWindowLevel]; + + [application run]; + return 0; +} + +void setSimulatorWindowTitle(const char *title) +{ + NSString *nsTitle = [NSString stringWithUTF8String:title]; + if( gCocoaWindow ) + { + dispatch_async(dispatch_get_main_queue(), ^{ + [gCocoaWindow setTitle:nsTitle]; + }); + } + else + { + dispatch_async(dispatch_get_main_queue(), ^{ + glutSetWindowTitle( [nsTitle cStringUsingEncoding:NSASCIIStringEncoding] ); + }); + } +} + +void setSubtitleData(uint8_t * data, unsigned int size, int width, int height) +{ + NSLog(@"Cocoa setBuffer data=%p, size=%u, width=%d, height=%d, bytes per row=%u", data, size, width, height, size / height); + + NSSize dimensions; + dimensions.width = width; + dimensions.height = height; + + if (size == 0) + { + NSLog(@"Data not allocated!"); + } + else + { + [gCocoaWindow setSubtitleData:data withSize:size andDimensions:dimensions]; + } +} + +void muteSubtitles() +{ + [gCocoaWindow muteSubtitles]; +} + +} // namespace gfx +} // namespace subttxrend \ No newline at end of file diff --git a/subttxrend-gfx/src/Backend.hpp b/subttxrend-gfx/src/Backend.hpp index b4b7acc..fd0b474 100644 --- a/subttxrend-gfx/src/Backend.hpp +++ b/subttxrend-gfx/src/Backend.hpp @@ -96,6 +96,13 @@ class Backend */ virtual void forceRender() = 0; +#ifdef __APPLE__ + /** + * @brief start blocking application window (this will block the calling thread) + */ + virtual void startBlockingApplicationWindow() = 0; +#endif + protected: /** * Returns backend events listener. diff --git a/subttxrend-gfx/src/BackendFactory.cpp b/subttxrend-gfx/src/BackendFactory.cpp index 5fcd034..0c9b894 100644 --- a/subttxrend-gfx/src/BackendFactory.cpp +++ b/subttxrend-gfx/src/BackendFactory.cpp @@ -30,6 +30,11 @@ #include "WaylandBackendEgl.hpp" #define BACKEND_CLASS_NAME WaylandBackendEgl +#elif BACKEND_TYPE == BACKEND_TYPE_QUARTZ + +#include "QuartzBackend.hpp" +#define BACKEND_CLASS_NAME QuartzBackend + #else #error Unknown backend type diff --git a/subttxrend-gfx/src/ColorArgb.cpp b/subttxrend-gfx/src/ColorArgb.cpp index 6f181de..eaa9cc9 100644 --- a/subttxrend-gfx/src/ColorArgb.cpp +++ b/subttxrend-gfx/src/ColorArgb.cpp @@ -21,6 +21,7 @@ #include "ColorArgb.hpp" #include #include +#include namespace subttxrend { diff --git a/subttxrend-gfx/src/EngineImpl.cpp b/subttxrend-gfx/src/EngineImpl.cpp index 645f43e..1e03dee 100644 --- a/subttxrend-gfx/src/EngineImpl.cpp +++ b/subttxrend-gfx/src/EngineImpl.cpp @@ -282,6 +282,16 @@ void EngineImpl::onPreferredSize(const Size& size) unlock(); } +#ifdef __APPLE__ +void EngineImpl::startBlockingApplicationWindow() +{ + if (m_backend) + { + m_backend->startBlockingApplicationWindow(); + } +} +#endif + } // namespace gfx }// namespace subttxrend diff --git a/subttxrend-gfx/src/EngineImpl.hpp b/subttxrend-gfx/src/EngineImpl.hpp index f654db0..48a5930 100644 --- a/subttxrend-gfx/src/EngineImpl.hpp +++ b/subttxrend-gfx/src/EngineImpl.hpp @@ -59,7 +59,7 @@ class EngineImpl : public Engine, virtual void shutdown() override; - virtual void execute(); + virtual void execute() override; virtual WindowPtr createWindow() override; @@ -70,6 +70,10 @@ class EngineImpl : public Engine, virtual void detach(WindowPtr window) override; +#ifdef __APPLE__ + virtual void startBlockingApplicationWindow() override; +#endif + private: /** Mutex type. */ using Mutex = std::recursive_mutex; diff --git a/subttxrend-gfx/src/FontStripImpl.cpp b/subttxrend-gfx/src/FontStripImpl.cpp index a6d9182..f26d8c4 100644 --- a/subttxrend-gfx/src/FontStripImpl.cpp +++ b/subttxrend-gfx/src/FontStripImpl.cpp @@ -121,7 +121,7 @@ class GlyphRenderer : public ftcpp::MonospaceRenderer /** @copydoc ftcpp::MonospaceRenderer::setPixel */ virtual void setPixel(FT_Long x, FT_Long y, - uint8_t value) + uint8_t value) override { if ((x < 0) || (y < 0) || (x >= m_rect.m_w) || (y >= m_rect.m_h)) { diff --git a/subttxrend-gfx/src/QuartzBackend.cpp b/subttxrend-gfx/src/QuartzBackend.cpp new file mode 100644 index 0000000..f9e4564 --- /dev/null +++ b/subttxrend-gfx/src/QuartzBackend.cpp @@ -0,0 +1,234 @@ +/** + * If not stated otherwise in this file or this component's license file the + * following copyright and licenses apply: + * + * Copyright 2022 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "QuartzBackend.hpp" + +#ifdef __APPLE__ +#import +#endif + +#include + +#include "Pixmap.hpp" + +namespace subttxrend +{ +namespace gfx +{ + +namespace +{ +subttxrend::common::Logger g_logger("Gfx", "QuartzBackend"); +} // namespace + +//------------------------------------------ + +const Size QuartzBackend::DEFAULT_SIZE(854, 480); + +QuartzBackend::QuartzBackend(BackendListener* listener) : + Backend(listener) +{ +} + +QuartzBackend::~QuartzBackend() +{ +} + +bool QuartzBackend::isSyncNeeded() const +{ + return true; +} + +bool QuartzBackend::init() +{ + + if ((m_initialSize.m_w == 0) && (m_initialSize.m_h == 0)) + { + m_initialSize = DEFAULT_SIZE; + } + + return true; +} + +bool QuartzBackend::start() +{ + return true; +} + +void QuartzBackend::startBlockingApplicationWindow() +{ + createAndRunCocoaWindow(DEFAULT_SIZE.m_w, DEFAULT_SIZE.m_h); +} + + +void QuartzBackend::stop() +{ +} + +void QuartzBackend::requestRender() +{ + g_logger.trace("%s", __func__); + + redraw(); +} + +void QuartzBackend::forceRender() +{ + g_logger.trace("%s", __func__); + + redraw(); +} + +void QuartzBackend::redraw() +{ + g_logger.trace("%s", __func__); + + auto contentSize = calculateContentSize(); + + const bool anythingToDraw = (contentSize.m_w > 0) || (contentSize.m_h > 0); + + /* When rendering ensure background is Clear - with 0% opacity so that any underlying content + is still visible. */ + + // render windows + if (anythingToDraw) + { + if (!prepareBuffer(contentSize)) + { + g_logger.fatal("%s - cannot prepare buffer of size %dx%d", __func__, contentSize.m_w, contentSize.m_h); + return; + } + + if (!copyToBuffer(contentSize)) + { + g_logger.fatal("%s - cannot copy contents", __func__); + return; + } + + g_logger.trace("%s - width=%d height=%d size=%d ptr=%X", __func__, contentSize.m_w, contentSize.m_h, m_buffer.size(), m_buffer.data()); + // Send the buffer to Quartz + setSubtitleData(m_buffer.data(), m_buffer.size(), contentSize.m_w, contentSize.m_h); + } + else + { + g_logger.trace("%s nothing to draw", __func__); + // Tell Quartz to mute the subtitles + muteSubtitles(); + } +} + +Size QuartzBackend::calculateContentSize() +{ + g_logger.trace("%s", __func__); + + class SizeCalculator : public BackendWindowEnumerator + { + public: + virtual void processWindow(const Pixmap& pixmap) override + { + m_size.m_w = std::max(m_size.m_w, pixmap.getWidth()); + m_size.m_h = std::max(m_size.m_h, pixmap.getHeight()); + } + + Size getSize() const + { + return m_size; + } + + private: + Size m_size; + }; + + SizeCalculator calculator; + + getListener()->enumerateVisibleWindows(calculator); + + return calculator.getSize(); +} + +bool QuartzBackend::prepareBuffer(const Size& contentSize) +{ + size_t cap = contentSize.m_w * contentSize.m_h * 4; + + if (cap != m_buffer.size()) + { + m_buffer.resize(cap, 0x00); + } + + return true; +} + +bool QuartzBackend::copyToBuffer(const Size& contentSize) +{ + g_logger.trace("%s", __func__); + + class BitmapCopier : public BackendWindowEnumerator + { + public: + BitmapCopier(const Size& imageSize, std::vector& buffer) : + m_imageSize(imageSize), + m_buffer(buffer) + { + } + + virtual void processWindow(const Pixmap& pixmap) override + { + const auto pw = pixmap.getWidth(); + const auto ph = pixmap.getHeight(); + + if ((pw > m_imageSize.m_w) || (ph > m_imageSize.m_h)) + { + g_logger.info("%s - pixmap larger than image, skipping", __func__); + return; + } + + // copy the bitmap data + std::vector::iterator it = m_buffer.begin(); + for (int y = 0; y < ph; y++) + { + auto linePtr = pixmap.getLine(y).ptr(); + + /* The in memory ordering of the lines is correct for Linux but MacOs' origin is in the vertically opposite + corner. We don't have to worry about it here because we can use the texture rendering to flip the origin. */ + std::copy((uint8_t*)linePtr, (uint8_t*)linePtr + pw * 4, it); + it += (pw * 4); + } + } + + private: + const Size m_imageSize; + std::vector& m_buffer; + }; + + if (m_clearArrayBuffer.size() != (contentSize.m_w * contentSize.m_h * 4)) + { + m_clearArrayBuffer.resize(contentSize.m_w * contentSize.m_h * 4, 0x00); + } + + // clear the buffer + m_buffer = m_clearArrayBuffer; + + BitmapCopier copier(contentSize, m_buffer); + + getListener()->enumerateVisibleWindows(copier); + + return true; +} + +} // namespace gfx +} // namespace subttxrend diff --git a/subttxrend-gfx/src/QuartzBackend.hpp b/subttxrend-gfx/src/QuartzBackend.hpp new file mode 100644 index 0000000..981767a --- /dev/null +++ b/subttxrend-gfx/src/QuartzBackend.hpp @@ -0,0 +1,136 @@ +/** + * If not stated otherwise in this file or this component's license file the + * following copyright and licenses apply: + * + * Copyright 2022 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SUBTTXREND_GFX_QUARTZ_BACKEND_HPP_ +#define SUBTTXREND_GFX_QUARTZ_BACKEND_HPP_ + +#include "Backend.hpp" +#include +#include +#include +#include "Types.hpp" + +namespace subttxrend +{ +namespace gfx +{ + +/** + * Rendering backend using Quartz. + */ +class QuartzBackend : public Backend +{ + public: + /** + * Constructor. + * + * @param listener + * Listener for backend events. + */ + QuartzBackend(BackendListener* listener); + + /** + * Destructor. + */ + virtual ~QuartzBackend(); + + /** @copydoc Backend::isSyncNeeded() */ + bool isSyncNeeded() const override final; + + /** @copydoc Backend::init() */ + bool init() override final; + + /** @copydoc Backend::start() */ + bool start() override final; + + /** @copydoc Backend::stop() */ + void stop() override final; + + /** @copydoc Backend::startBlockingApplicationWindow() */ + void startBlockingApplicationWindow() override final; + + /** + * Requests render. + * + * This method shall be used to request the frame to be redrawn. + */ + void requestRender() override final; + + /** + * Force render. + * + * This method shall be used to request the frame to be redrawn without + * waiting for external conditions (like frame done callback). + */ + void forceRender() override final; + + private: + /** Initial window size. */ + Size m_initialSize; + + /** Buffer for clearing the image */ + std::vector m_clearArrayBuffer; + + /** Default size. */ + static const Size DEFAULT_SIZE; + + /** Buffer for the data to be passed to Cocoa. */ + std::vector m_buffer; + + /** + * @brief Causes the redraw of the subtitles + */ + void redraw(); + + /** + * @brief Calculates the content size. + * + * @return Calculated size. + */ + Size calculateContentSize(); + + /** + * @brief Copies the subtitle pixmap (as bitmap) to the buffer + * used to send the subtitle data to the Quartz engine. + * + * @param contentSize Size of the content to copy. + * + * @return true on success. + * @return false on error. + */ + bool copyToBuffer(const Size& contentSize); + + /** + * @brief Prepares the buffer, used for copying the subtitle pixmap + * to the Quartz engine, by resizing it to the given content + * size. + * + * @param contentSize The size of the content that will be copied. + * + * @return true on success. + * @return false on error. + */ + bool prepareBuffer(const Size& contentSize); + +}; + +} // namespace gfx +} // namespace subttxrend + +#endif // SUBTTXREND_GFX_QUARTZ_BACKEND_HPP_ diff --git a/subttxrend-gfx/src/WaylandBackend.hpp b/subttxrend-gfx/src/WaylandBackend.hpp index f344753..53ea40d 100644 --- a/subttxrend-gfx/src/WaylandBackend.hpp +++ b/subttxrend-gfx/src/WaylandBackend.hpp @@ -93,6 +93,11 @@ class WaylandBackend : public Backend, /** @copydoc Backend::stop() */ virtual void stop() override final; +#ifdef __APPLE__ + /** @copydoc Backend::startBlockingApplicationWindow() */ + void startBlockingApplicationWindow() override final {}; +#endif + /** * Requests render. * @@ -223,13 +228,13 @@ class WaylandBackend : public Backend, // TODO: shall not be protected /** Frame ready flag. */ - std::atomic m_frameReady; + std::atomic m_frameReady{false}; /** Render requested flag. */ - std::atomic m_renderRequested; + std::atomic m_renderRequested{false}; /** Force render flag. */ - std::atomic m_forceRender; + std::atomic m_forceRender{false}; /** Current surface id. */ uint32_t m_currentSurfaceId; diff --git a/subttxrend-gfx/src/WaylandBackendLoopEpoll.hpp b/subttxrend-gfx/src/WaylandBackendLoopEpoll.hpp index 66b199a..94d2e88 100644 --- a/subttxrend-gfx/src/WaylandBackendLoopEpoll.hpp +++ b/subttxrend-gfx/src/WaylandBackendLoopEpoll.hpp @@ -49,35 +49,40 @@ class WaylandBackendLoopEpoll : public WaylandBackendLoop, /** * Destructor. */ - virtual ~WaylandBackendLoopEpoll() + ~WaylandBackendLoopEpoll() { - stop(); + internalStop(); } - virtual bool isSyncNeeded() const override + bool isSyncNeeded() const override { return true; } - virtual bool start(waylandcpp::DisplayPtr display, + bool start(waylandcpp::DisplayPtr display, WaylandBackendLoopListener* listener) override { m_listener = listener; return m_displayLoop.start(display, this); } - virtual void stop() override + void stop() override { - m_displayLoop.stop(); + internalStop(); } - virtual void requestWakeup() override + void requestWakeup() override { m_displayLoop.wakeup(); } +private: + void internalStop() + { + m_displayLoop.stop(); + } protected: - virtual void started(waylandcpp::EpollDisplayLoop* /*loop*/) override + void started(waylandcpp::EpollDisplayLoop* /*loop*/) override { if (m_listener) { @@ -85,7 +90,7 @@ class WaylandBackendLoopEpoll : public WaylandBackendLoop, } } - virtual void finished(waylandcpp::EpollDisplayLoop* /*loop*/) override + void finished(waylandcpp::EpollDisplayLoop* /*loop*/) override { if (m_listener) { @@ -93,7 +98,7 @@ class WaylandBackendLoopEpoll : public WaylandBackendLoop, } } - virtual void wakeupReceived(waylandcpp::EpollDisplayLoop* /*loop*/) override + void wakeupReceived(waylandcpp::EpollDisplayLoop* /*loop*/) override { if (m_listener) { diff --git a/subttxrend-gfx/src/WaylandBackendLoopGlib.cpp b/subttxrend-gfx/src/WaylandBackendLoopGlib.cpp index 4c0cefe..4d7a1e6 100644 --- a/subttxrend-gfx/src/WaylandBackendLoopGlib.cpp +++ b/subttxrend-gfx/src/WaylandBackendLoopGlib.cpp @@ -195,7 +195,9 @@ WaylandBackendLoopGlib::WaylandBackendLoopGlib() : WaylandBackendLoopGlib::~WaylandBackendLoopGlib() { - stop(); + // Explicit call to specific stop() method from WaylandBackendLoopGlib is a result of static code analyser warning + // as described here: https://pvs-studio.com/en/blog/posts/cpp/0891/ + WaylandBackendLoopGlib::stop(); } bool WaylandBackendLoopGlib::start(waylandcpp::DisplayPtr display, diff --git a/subttxrend-gfx/src/WaylandBufferManager.cpp b/subttxrend-gfx/src/WaylandBufferManager.cpp index c806141..a0f1c51 100644 --- a/subttxrend-gfx/src/WaylandBufferManager.cpp +++ b/subttxrend-gfx/src/WaylandBufferManager.cpp @@ -222,7 +222,7 @@ WaylandBuffer::Ptr WaylandBufferManager::findBuffer(const waylandcpp::BufferPara WaylandBuffer::Ptr WaylandBufferManager::createEmptyBuffer() { - waylandcpp::BufferParams params(0, 1, 1, waylandcpp::PixelFormat::ARGB8888); + waylandcpp::BufferParams params(0, 64, 64, waylandcpp::PixelFormat::ARGB8888); auto shmPool = createPool(params.getMemSize()); if (!shmPool) diff --git a/subttxrend-gfx/src/WindowImpl.cpp b/subttxrend-gfx/src/WindowImpl.cpp index 27236b0..f8aa118 100644 --- a/subttxrend-gfx/src/WindowImpl.cpp +++ b/subttxrend-gfx/src/WindowImpl.cpp @@ -26,7 +26,7 @@ #include "FontStripImpl.hpp" #include "ColorizedPixmap.hpp" -#include // newer compilers would have +#include // newer compilers would have #include #include diff --git a/subttxrend-protocol/CMakeLists.txt b/subttxrend-protocol/CMakeLists.txt index 20a1571..e76e3ab 100644 --- a/subttxrend-protocol/CMakeLists.txt +++ b/subttxrend-protocol/CMakeLists.txt @@ -78,6 +78,7 @@ set(SUBTTXREND_PROTOCOL_PUBLIC_HEADERS include/PacketData.hpp include/PacketTimestamp.hpp + include/PacketFlush.hpp include/PacketPause.hpp include/PacketResume.hpp include/PacketMute.hpp @@ -131,8 +132,6 @@ set_property(TARGET ${LIBRARY_NAME} PROPERTY VERSION 0.0.0) set_property(TARGET ${LIBRARY_NAME} PROPERTY SOVERSION 0) set_property(TARGET ${LIBRARY_NAME} PROPERTY PUBLIC_HEADER ${SUBTTXREND_PROTOCOL_PUBLIC_HEADERS}) target_link_libraries(${LIBRARY_NAME} ${LIBSUBTTXRENDCOMMON_LIBRARIES}) -target_link_libraries(${LIBRARY_NAME} ${LIBGIO_LIBRARIES}) -target_link_libraries(${LIBRARY_NAME} ${LIBGLIB_LIBRARIES}) # # Install rules @@ -153,7 +152,7 @@ find_package(PkgConfig REQUIRED) set(PACKAGE_NAME "${PROJECT_NAME}") set(PACKAGE_DESCRIPTION "Subtitles/Teletext Renderer - Protocol") set(PACKAGE_VERSION "0.0.0") -set(PACKAGE_REQUIRES "") +set(PACKAGE_REQUIRES "${LIBSUBTTXRENDCOMMON_PACKAGE_NAME}") set(PACKAGE_LIB_NAME "${LIBRARY_NAME}") if(PKG_CONFIG_FOUND) diff --git a/subttxrend-protocol/include/Packet.hpp b/subttxrend-protocol/include/Packet.hpp index e1eb6be..2f3fd42 100644 --- a/subttxrend-protocol/include/Packet.hpp +++ b/subttxrend-protocol/include/Packet.hpp @@ -25,6 +25,7 @@ #include #include +#include #include namespace subttxrend @@ -66,7 +67,9 @@ class Packet : private common::NonCopyable WEBVTT_TIMESTAMP = 17, SET_CC_ATTRIBUTES = 18, TTML_INFO = 19, + FLUSH = 20, + MAX, INVALID = 0xFFFFFFFF, }; @@ -233,7 +236,6 @@ class Packet : private common::NonCopyable /** Logger. */ static common::Logger m_logger; }; - static const std::unordered_map> packetTypeStr = { {Packet::Type::PES_DATA, "PES_DATA"}, {Packet::Type::TIMESTAMP, "TIMESTAMP"}, @@ -253,7 +255,8 @@ static const std::unordered_map()); addParsablePacket(std::make_unique()); addParsablePacket(std::make_unique()); + addParsablePacket(std::make_unique()); addParsablePacket(std::make_unique()); addParsablePacket(std::make_unique()); addParsablePacket(std::make_unique()); diff --git a/subttxrend-protocol/test/PacketTtmlInfo_test.cpp b/subttxrend-protocol/test/PacketTtmlInfo_test.cpp index d84bac5..1751bbd 100644 --- a/subttxrend-protocol/test/PacketTtmlInfo_test.cpp +++ b/subttxrend-protocol/test/PacketTtmlInfo_test.cpp @@ -52,7 +52,7 @@ CPPUNIT_TEST_SUITE_END(); { std::uint8_t packetData[] = { - 0x0F, 0x00, 0x00, 0x00, // type + 0x13, 0x00, 0x00, 0x00, // type 0x01, 0x23, 0x45, 0x67, // counter 0x0F, 0x00, 0x00, 0x00, // data size 0x76, 0x54, 0x32, 0x10, // channel id @@ -73,7 +73,7 @@ CPPUNIT_TEST_SUITE_END(); std::uint8_t anotherPacketData[] = { - 0x0F, 0x00, 0x00, 0x00, // type + 0x13, 0x00, 0x00, 0x00, // type 0x01, 0x23, 0x45, 0x67, // counter 0x0F, 0x00, 0x00, 0x00, // data size 0x76, 0x54, 0x32, 0x10, // channel id @@ -114,7 +114,7 @@ CPPUNIT_TEST_SUITE_END(); { std::uint8_t packetData[] = { - 0x0F, 0x00, 0x00, 0x00, // type + 0x13, 0x00, 0x00, 0x00, // type 0x01, 0x23, 0x45, 0x67, // counter 0x01, 0x00, 0x00, 0x00, // data size 0x76, 0x54, 0x32, 0x10, // channel id diff --git a/subttxrend-protocol/test/StreamValidator_test.cpp b/subttxrend-protocol/test/StreamValidator_test.cpp index 09d4a3a..6a2843d 100644 --- a/subttxrend-protocol/test/StreamValidator_test.cpp +++ b/subttxrend-protocol/test/StreamValidator_test.cpp @@ -154,12 +154,12 @@ CPPUNIT_TEST_SUITE_END(); CPPUNIT_ASSERT(validator.validate(generatePacketTimestamp(3))); CPPUNIT_ASSERT(validator.validate(generatePacketTimestamp(4))); - // invalid counter - CPPUNIT_ASSERT(!validator.validate(generatePacketTimestamp(10))); + // invalid counter - still will be considered valid + CPPUNIT_ASSERT(validator.validate(generatePacketTimestamp(10))); // try to get back to valid - shall not switch stream validity - CPPUNIT_ASSERT(!validator.validate(generatePacketTimestamp(5))); - CPPUNIT_ASSERT(!validator.validate(generatePacketTimestamp(11))); + CPPUNIT_ASSERT(validator.validate(generatePacketTimestamp(5))); + CPPUNIT_ASSERT(validator.validate(generatePacketTimestamp(11))); // reset all CPPUNIT_ASSERT(validator.validate(generatePacketResetAll())); diff --git a/subttxrend-scte/CMakeLists.txt b/subttxrend-scte/CMakeLists.txt index 275dba1..74ac55b 100644 --- a/subttxrend-scte/CMakeLists.txt +++ b/subttxrend-scte/CMakeLists.txt @@ -41,7 +41,7 @@ ENDIF(CMAKE_COMPILER_IS_GNUCXX) # find_package(LibSubTtxRendCommon REQUIRED) find_package(LibSubTtxRendGfx REQUIRED) -#find_package(LibDvbSubDecoder REQUIRED) +find_package(LibSubTtxRendProtocol REQUIRED) find_package(ZLIB) # @@ -51,6 +51,7 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) include_directories(${LIBSUBTTXRENDCOMMON_INCLUDE_DIRS}) include_directories(${LIBSUBTTXRENDGFX_INCLUDE_DIRS}) #include_directories(${LIBDVBSUBDECODER_INCLUDE_DIRS}) +include_directories(${LIBSUBTTXRENDPROTOCOL_INCLUDE_DIRS}) include_directories(${ZLIB_INCLUDE_DIRS}) # @@ -92,9 +93,8 @@ set_property(TARGET ${LIBRARY_NAME} PROPERTY CXX_STANDARD 14) set_property(TARGET ${LIBRARY_NAME} PROPERTY VERSION 0.0.0) set_property(TARGET ${LIBRARY_NAME} PROPERTY SOVERSION 0) set_property(TARGET ${LIBRARY_NAME} PROPERTY PUBLIC_HEADER ${SUBTTXREND_SCTE_PUBLIC_HEADERS}) -target_link_libraries(${LIBRARY_NAME} ${LIBSUBTTXRENDCOMMON_LIBRARIES}) target_link_libraries(${LIBRARY_NAME} ${LIBSUBTTXRENDGFX_LIBRARIES}) -target_link_libraries(${LIBRARY_NAME} ${LIBDVBSUBDECODER_LIBRARIES}) +target_link_libraries(${LIBRARY_NAME} ${LIBSUBTTXRENDPROTOCOL_LIBRARIES}) target_link_libraries(${LIBRARY_NAME} ${ZLIB_LIBRARIES}) # @@ -116,7 +116,7 @@ find_package(PkgConfig REQUIRED) set(PACKAGE_NAME "${PROJECT_NAME}") set(PACKAGE_DESCRIPTION "Subtitles Renderer - SCTE Subtitles Renderer") set(PACKAGE_VERSION "0.0.0") -set(PACKAGE_REQUIRES "") +set(PACKAGE_REQUIRES "${LIBSUBTTXRENDGFX_PACKAGE_NAME} ${LIBSUBTTXRENDPROTOCOL_PACKAGE_NAME}") set(PACKAGE_LIB_NAME "${LIBRARY_NAME}") if(PKG_CONFIG_FOUND) diff --git a/subttxrend-dvbsub/cmake/modules/FindLibXkbCommon.cmake b/subttxrend-scte/cmake/modules/FindLibSubTtxRendProtocol.cmake similarity index 87% rename from subttxrend-dvbsub/cmake/modules/FindLibXkbCommon.cmake rename to subttxrend-scte/cmake/modules/FindLibSubTtxRendProtocol.cmake index 64a8d51..4e341be 100644 --- a/subttxrend-dvbsub/cmake/modules/FindLibXkbCommon.cmake +++ b/subttxrend-scte/cmake/modules/FindLibSubTtxRendProtocol.cmake @@ -17,8 +17,8 @@ include(PkgConfigHelper) -pkgconfig_resolve(LibXkbCommon - xkbcommon - xkbcommon/xkbcommon.h - xkbcommon +pkgconfig_resolve(LibSubTtxRendProtocol + subttxrend-protocol + subttxrend/protocol/Packet.hpp + subttxrend-protocol ) diff --git a/subttxrend-socksrc/CMakeLists.txt b/subttxrend-socksrc/CMakeLists.txt index c1c1d8d..62daaae 100644 --- a/subttxrend-socksrc/CMakeLists.txt +++ b/subttxrend-socksrc/CMakeLists.txt @@ -38,8 +38,6 @@ ENDIF(SUBTTXACCESS_GROUP) # # Packages to use # -find_package(LibGlib REQUIRED) -find_package(LibGio REQUIRED) find_package(LibSubTtxRendCommon REQUIRED) find_package(LibSubTtxRendProtocol REQUIRED) @@ -47,8 +45,6 @@ find_package(LibSubTtxRendProtocol REQUIRED) # Include directories # include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) -include_directories(${LIBGLIB_INCLUDE_DIRS}) -include_directories(${LIBGIO_INCLUDE_DIRS}) include_directories(${LIBSUBTTXRENDCOMMON_INCLUDE_DIRS}) include_directories(${LIBSUBTTXRENDPROTOCOL_INCLUDE_DIRS}) @@ -107,10 +103,7 @@ set_property(TARGET ${LIBRARY_NAME} PROPERTY CXX_STANDARD 14) set_property(TARGET ${LIBRARY_NAME} PROPERTY VERSION 0.0.0) set_property(TARGET ${LIBRARY_NAME} PROPERTY SOVERSION 0) set_property(TARGET ${LIBRARY_NAME} PROPERTY PUBLIC_HEADER ${SUBTTXREND_SOCKSRC_PUBLIC_HEADERS}) -target_link_libraries(${LIBRARY_NAME} ${LIBSUBTTXRENDCOMMON_LIBRARIES}) target_link_libraries(${LIBRARY_NAME} ${LIBSUBTTXRENDPROTOCOL_LIBRARIES}) -target_link_libraries(${LIBRARY_NAME} ${LIBGIO_LIBRARIES}) -target_link_libraries(${LIBRARY_NAME} ${LIBGLIB_LIBRARIES}) # # Install rules @@ -131,7 +124,7 @@ find_package(PkgConfig REQUIRED) set(PACKAGE_NAME "${PROJECT_NAME}") set(PACKAGE_DESCRIPTION "Subtitles/Teletext Renderer - Socket Source") set(PACKAGE_VERSION "0.0.0") -set(PACKAGE_REQUIRES "") +set(PACKAGE_REQUIRES "${LIBSUBTTXRENDPROTOCOL_PACKAGE_NAME}") set(PACKAGE_LIB_NAME "${LIBRARY_NAME}") if(PKG_CONFIG_FOUND) diff --git a/subttxrend-socksrc/cmake/modules/FindLibGio.cmake b/subttxrend-socksrc/cmake/modules/FindLibGio.cmake deleted file mode 100644 index 6635d63..0000000 --- a/subttxrend-socksrc/cmake/modules/FindLibGio.cmake +++ /dev/null @@ -1,79 +0,0 @@ -############################################################################## -# If not stated otherwise in this file or this component's LICENSE file the -# following copyright and licenses apply: -# -# Copyright 2021 Liberty Global Service B.V.# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License.# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -############################################################################## - -# - Try to find GIO -# -# Once done this will define -# LIBGIO_FOUND - System has the component -# LIBGIO_INCLUDE_DIRS - Component include directories -# LIBGIO_LIBRARIES - Libraries needed to use the component - -# Use the pkgconfig -find_package(PkgConfig REQUIRED) - -# Find the component information -pkg_check_modules(PC_LIBGIO QUIET gio-2.0) - -# _FOUND - set to 1 if module(s) exist -# _LIBRARIES - only the libraries (w/o the '-l') -# _LIBRARY_DIRS - the paths of the libraries (w/o the '-L') -# _LDFLAGS - all required linker flags -# _LDFLAGS_OTHER - all other linker flags -# _INCLUDE_DIRS - the '-I' preprocessor flags (w/o the '-I') -# _CFLAGS - all required cflags -# _CFLAGS_OTHER - the other compiler flags - -# _VERSION - version of the module -# _PREFIX - prefix-directory of the module -# _INCLUDEDIR - include-dir of the module -# _LIBDIR - lib-dir of the module - -message(STATUS "PC_LIBGIO_FOUND = ${PC_LIBGIO_FOUND}") -message(STATUS "PC_LIBGIO_LIBRARIES = ${PC_LIBGIO_LIBRARIES}") -message(STATUS "PC_LIBGIO_LIBRARY_DIRS = ${PC_LIBGIO_LIBRARY_DIRS}") -message(STATUS "PC_LIBGIO_LDFLAGS = ${PC_LIBGIO_LDFLAGS}") -message(STATUS "PC_LIBGIO_LDFLAGS_OTHER = ${PC_LIBGIO_LDFLAGS_OTHER}") -message(STATUS "PC_LIBGIO_INCLUDE_DIRS = ${PC_LIBGIO_INCLUDE_DIRS}") -message(STATUS "PC_LIBGIO_CFLAGS = ${PC_LIBGIO_CFLAGS}") -message(STATUS "PC_LIBGIO_CFLAGS_OTHER = ${PC_LIBGIO_CFLAGS_OTHER}") -message(STATUS "PC_LIBGIO_VERSION = ${PC_LIBGIO_VERSION}") -message(STATUS "PC_LIBGIO_PREFIX = ${PC_LIBGIO_PREFIX}") -message(STATUS "PC_LIBGIO_INCLUDEDIR = ${PC_LIBGIO_INCLUDEDIR}") -message(STATUS "PC_LIBGIO_LIBDIR = ${PC_LIBGIO_LIBDIR}") - -find_path(LIBGIO_INCLUDE_DIR - NAMES gio/gio.h - HINTS ${PC_LIBGIO_INCLUDEDIR} ${PC_LIBGIO_INCLUDE_DIRS} - PATH_SUFFIXES glib-2.0 ) - -find_library(LIBGIO_LIBRARY - NAMES gio-2.0 - HINTS ${PC_LIBGIO_LIBDIR} ${PC_LIBGIO_LIBRARY_DIRS} ) - -message(STATUS "LIBGIO_INCLUDE_DIR = ${LIBGIO_INCLUDE_DIR}") -message(STATUS "LIBGIO_LIBRARY = ${LIBGIO_LIBRARY}") - -# handle the QUIETLY and REQUIRED arguments and set component to TRUE -# if all listed variables are TRUE -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(LibGio DEFAULT_MSG - LIBGIO_LIBRARY LIBGIO_INCLUDE_DIR) - -mark_as_advanced(LIBGIO_INCLUDE_DIR LIBGIO_LIBRARY) - -set(LIBGIO_INCLUDE_DIRS ${LIBGIO_INCLUDE_DIR}) -set(LIBGIO_LIBRARIES ${LIBGIO_LIBRARY}) diff --git a/subttxrend-socksrc/cmake/modules/FindLibGlib.cmake b/subttxrend-socksrc/cmake/modules/FindLibGlib.cmake deleted file mode 100644 index 6c03265..0000000 --- a/subttxrend-socksrc/cmake/modules/FindLibGlib.cmake +++ /dev/null @@ -1,85 +0,0 @@ -############################################################################## -# If not stated otherwise in this file or this component's LICENSE file the -# following copyright and licenses apply: -# -# Copyright 2021 Liberty Global Service B.V.# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License.# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -############################################################################## - -# - Try to find Glib -# -# Once done this will define -# LIBGLIB_FOUND - System has the component -# LIBGLIB_INCLUDE_DIRS - Component include directories -# LIBGLIB_LIBRARIES - Libraries needed to use the component - -# Use the pkgconfig -find_package(PkgConfig REQUIRED) - -# Find the component information -pkg_check_modules(PC_LIBGLIB QUIET glib-2.0) - -# _FOUND - set to 1 if module(s) exist -# _LIBRARIES - only the libraries (w/o the '-l') -# _LIBRARY_DIRS - the paths of the libraries (w/o the '-L') -# _LDFLAGS - all required linker flags -# _LDFLAGS_OTHER - all other linker flags -# _INCLUDE_DIRS - the '-I' preprocessor flags (w/o the '-I') -# _CFLAGS - all required cflags -# _CFLAGS_OTHER - the other compiler flags - -# _VERSION - version of the module -# _PREFIX - prefix-directory of the module -# _INCLUDEDIR - include-dir of the module -# _LIBDIR - lib-dir of the module - -message(STATUS "PC_LIBGLIB_FOUND = ${PC_LIBGLIB_FOUND}") -message(STATUS "PC_LIBGLIB_LIBRARIES = ${PC_LIBGLIB_LIBRARIES}") -message(STATUS "PC_LIBGLIB_LIBRARY_DIRS = ${PC_LIBGLIB_LIBRARY_DIRS}") -message(STATUS "PC_LIBGLIB_LDFLAGS = ${PC_LIBGLIB_LDFLAGS}") -message(STATUS "PC_LIBGLIB_LDFLAGS_OTHER = ${PC_LIBGLIB_LDFLAGS_OTHER}") -message(STATUS "PC_LIBGLIB_INCLUDE_DIRS = ${PC_LIBGLIB_INCLUDE_DIRS}") -message(STATUS "PC_LIBGLIB_CFLAGS = ${PC_LIBGLIB_CFLAGS}") -message(STATUS "PC_LIBGLIB_CFLAGS_OTHER = ${PC_LIBGLIB_CFLAGS_OTHER}") -message(STATUS "PC_LIBGLIB_VERSION = ${PC_LIBGLIB_VERSION}") -message(STATUS "PC_LIBGLIB_PREFIX = ${PC_LIBGLIB_PREFIX}") -message(STATUS "PC_LIBGLIB_INCLUDEDIR = ${PC_LIBGLIB_INCLUDEDIR}") -message(STATUS "PC_LIBGLIB_LIBDIR = ${PC_LIBGLIB_LIBDIR}") - -find_path(LIBGLIB_INCLUDE_DIR - NAMES glib.h - HINTS ${PC_LIBGLIB_INCLUDEDIR} ${PC_LIBGLIB_INCLUDE_DIRS} - PATH_SUFFIXES glib-2.0 ) - -find_path(LIBGLIB_CONFIG_INCLUDE_DIR - NAMES glibconfig.h - HINTS ${PC_LIBGLIB_INCLUDEDIR} ${PC_LIBGLIB_INCLUDE_DIRS} - PATH_SUFFIXES lib/glib-2.0/include ../lib/glib-2.0/include ) - -find_library(LIBGLIB_LIBRARY - NAMES glib-2.0 - HINTS ${PC_LIBGLIB_LIBDIR} ${PC_LIBGLIB_LIBRARY_DIRS} ) - -message(STATUS "LIBGLIB_INCLUDE_DIR = ${LIBGLIB_INCLUDE_DIR}") -message(STATUS "LIBGLIB_CONFIG_INCLUDE_DIR = ${LIBGLIB_CONFIG_INCLUDE_DIR}") -message(STATUS "LIBGLIB_LIBRARY = ${LIBGLIB_LIBRARY}") - -# handle the QUIETLY and REQUIRED arguments and set component to TRUE -# if all listed variables are TRUE -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(LibGlib DEFAULT_MSG - LIBGLIB_LIBRARY LIBGLIB_INCLUDE_DIR LIBGLIB_CONFIG_INCLUDE_DIR) - -mark_as_advanced(LIBGLIB_INCLUDE_DIR LIBGLIB_CONFIG_INCLUDE_DIR LIBGLIB_LIBRARY) - -set(LIBGLIB_INCLUDE_DIRS ${LIBGLIB_INCLUDE_DIR} ${LIBGLIB_CONFIG_INCLUDE_DIR}) -set(LIBGLIB_LIBRARIES ${LIBGLIB_LIBRARY}) diff --git a/subttxrend-testapps/src/common/DataTargetFactory.cpp b/subttxrend-testapps/src/common/DataTargetFactory.cpp index ecdfb76..1042351 100644 --- a/subttxrend-testapps/src/common/DataTargetFactory.cpp +++ b/subttxrend-testapps/src/common/DataTargetFactory.cpp @@ -17,7 +17,7 @@ * limitations under the License. *****************************************************************************/ - +#include #include "DataTargetFactory.hpp" #include "Ipv4SocketTarget.hpp" diff --git a/subttxrend-ttml/CMakeLists.txt b/subttxrend-ttml/CMakeLists.txt index 1924827..5e2ab7f 100644 --- a/subttxrend-ttml/CMakeLists.txt +++ b/subttxrend-ttml/CMakeLists.txt @@ -40,6 +40,7 @@ ENDIF(CMAKE_COMPILER_IS_GNUCXX) # Packages to use # find_package(LibSubTtxRendCommon REQUIRED) +find_package(LibSubTtxRendGfx REQUIRED) find_package(LibXml2 REQUIRED) # @@ -47,6 +48,7 @@ find_package(LibXml2 REQUIRED) # include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) include_directories(${LIBSUBTTXRENDCOMMON_INCLUDE_DIRS}) +include_directories(${LIBSUBTTXRENDGFX_INCLUDE_DIRS}) include_directories(${LIBXML2_INCLUDE_DIRS}) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src) @@ -92,10 +94,15 @@ set(SUBTTXREND_TTML_PUBLIC_HEADERS set(SUBTTXREND_TTML_SOURCES src/Factory.cpp src/Parser/AttributeHandlers.cpp + src/Parser/DocumentInstance.cpp + src/Parser/XmlLibSaxParserWrapper.cpp src/Parser/Outline.cpp src/Parser/Parser.cpp src/Parser/StyleSet.cpp src/Parser/Utils.cpp + src/Parser/XmlLibSaxParserWrapper.cpp + src/Parser/DocumentInstance.cpp + src/DataDumper.cpp src/TtmlEngineImpl.cpp src/TtmlRenderer.cpp src/IntermediateDocDrawer.cpp @@ -105,7 +112,10 @@ set(SUBTTXREND_TTML_SOURCES if(BUILD_RDK_REFERENCE) set(SUBTTXREND_TTML_SOURCES ${SUBTTXREND_TTML_SOURCES} src/transform/TtmlTransformer.cpp) else(BUILD_RDK_REFERENCE) - set(SUBTTXREND_TTML_SOURCES ${SUBTTXREND_TTML_SOURCES} src/transform-lgi/TtmlTransformer.cpp) + set(SUBTTXREND_TTML_SOURCES ${SUBTTXREND_TTML_SOURCES} + src/transform-lgi/TtmlTransformer.cpp + src/transform-lgi/StyleRulesHelper.cpp + ) endif(BUILD_RDK_REFERENCE) # @@ -118,6 +128,7 @@ set_property(TARGET ${LIBRARY_NAME} PROPERTY VERSION 0.0.0) set_property(TARGET ${LIBRARY_NAME} PROPERTY SOVERSION 0) set_property(TARGET ${LIBRARY_NAME} PROPERTY PUBLIC_HEADER ${SUBTTXREND_TTML_PUBLIC_HEADERS}) target_link_libraries(${LIBRARY_NAME} ${LIBSUBTTXRENDCOMMON_LIBRARIES}) +target_link_libraries(${LIBRARY_NAME} ${LIBSUBTTXRENDGFX_LIBRARIES}) target_link_libraries(${LIBRARY_NAME} ${LIBXML2_LIBRARIES}) # @@ -139,7 +150,7 @@ find_package(PkgConfig REQUIRED) set(PACKAGE_NAME "${PROJECT_NAME}") set(PACKAGE_DESCRIPTION "Ttml Subtitles Engine") set(PACKAGE_VERSION "0.0.0") -set(PACKAGE_REQUIRES "${LIBSUBTTMLRENDCOMMON_PACKAGE_NAME}") +set(PACKAGE_REQUIRES "${LIBSUBTTXRENDCOMMON_PACKAGE_NAME} ${LIBSUBTTXRENDGFX_PACKAGE_NAME}") set(PACKAGE_LIB_NAME "${LIBRARY_NAME}") if(PKG_CONFIG_FOUND) diff --git a/subttxrend-ttml/include/TtmlEngine.hpp b/subttxrend-ttml/include/TtmlEngine.hpp index 277099e..7308bbf 100644 --- a/subttxrend-ttml/include/TtmlEngine.hpp +++ b/subttxrend-ttml/include/TtmlEngine.hpp @@ -77,6 +77,11 @@ class TtmlEngine */ virtual void stop() = 0; + /** + * Flushes subtitle state. + */ + virtual void flush() = 0; + /** * Pauses subtitle processing. */ diff --git a/subttxrend-ttml/src/DataDumper.cpp b/subttxrend-ttml/src/DataDumper.cpp new file mode 100644 index 0000000..e5cc2af --- /dev/null +++ b/subttxrend-ttml/src/DataDumper.cpp @@ -0,0 +1,171 @@ +/***************************************************************************** +* If not stated otherwise in this file or this component's LICENSE file the +* following copyright and licenses apply: +* +* Copyright 2021 Liberty Global Service B.V. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*****************************************************************************/ + +#include +#include + +#include "DataDumper.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace subttxrend +{ +namespace ttmlengine +{ + +namespace +{ +common::Logger g_logger("TtmlEngine", "DataDumper"); + +} + +void DataDumper::setup(const common::ConfigProvider* provider) +{ + assert(provider); + + dumpTolog = (provider->getInt("DUMP_TO_LOG", 0) != 0); + + std::string dumpDir = provider->get("DUMP_DIR"); + if (dumpDir != "") + { + mkdir(dumpDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + dirPath.assign(dumpDir); + dumpToFile = true; + g_logger.info("ttml dump directory created: %s", dirPath.c_str()); + } +} + +void DataDumper::dump(const std::uint8_t* buffer, std::size_t bufferSize) const +{ + if (dumpTolog) + { + toLog(buffer, bufferSize); + } + + if (dumpToFile) + { + dumpXml(buffer, bufferSize); + } +} + +void DataDumper::toFile(const std::string filename, const std::uint8_t* buffer, std::size_t bufferLen) const +{ + if (! dirPath.empty()) + { + auto bufferStr = std::string{(char const*)buffer, bufferLen}; + g_logger.info("dumping: %s (size %zu) to %s", bufferStr.c_str(), bufferLen, filename.c_str()); + + std::string filePath = dirPath + filename; + FILE* dumpFile = fopen(filePath.c_str(), "aw+"); + if (dumpFile) + { + auto written = fwrite(buffer, 1, bufferLen, dumpFile); + g_logger.debug("written: %zu out of %zu to %s", written, bufferLen, filePath.c_str()); + fclose(dumpFile); + } + else + { + g_logger.error("could not open dump file: %s errno: %d", filePath.c_str(), errno); + } + } +} + +bool DataDumper::imageDumpEnabled() const +{ + return dumpToFile; +} + +std::vector DataDumper::readTtmlFromFile(const std::string& path) +{ + std::vector data; + + FILE* dataFile = fopen(path.c_str(), "r"); + if (dataFile) + { + fseek(dataFile,0,SEEK_END); + auto fileSize = ftell(dataFile); + fseek(dataFile,0,SEEK_SET); + + data.resize(fileSize); + auto dataRead = fread(data.data(), 1, data.size(), dataFile); + + g_logger.info("read: %zu out of %zu from %s", dataRead, data.size(), path.c_str()); + + fclose(dataFile); + } + else + { + g_logger.error("could not open dump file: %s errno: %d", path.c_str(), errno); + } + return data; +} + +void DataDumper::dumpXml(const std::uint8_t* buffer, std::size_t bufferSize) const +{ + using namespace std::chrono; + + // get current time and get the number of milliseconds for the current second + // = remainder after division into seconds + auto now = system_clock::now(); + auto ms = duration_cast(now.time_since_epoch()) % 1000; + + // convert current time to std::time_t in order to convert to std::tm + auto converttime = system_clock::to_time_t(now); + std::tm tmtime = *std::localtime(&converttime); + + char hoursMinutesSeconds[24]{}; + + // convert to HH:MM:SS string, + (void)std::strftime(hoursMinutesSeconds, sizeof(hoursMinutesSeconds), "%T", &tmtime); + + std::ostringstream oss; + + oss << hoursMinutesSeconds; + oss << '.' << std::setfill('0') << std::setw(3) << ms.count(); + + auto filename = oss.str(); + + std::replace(filename.begin(), filename.end(), ':', '_'); + std::replace(filename.begin(), filename.end(), '.', '_'); + + filename += ".xml"; + + toFile(filename, buffer, bufferSize); +} + +void DataDumper::toLog(const std::uint8_t* buffer, std::size_t bufferSize) const +{ + g_logger.info("TTML DATA DUMP >>>>>>>>>>>>>>>>>>>>>>>>>"); + auto bufferStr = std::string{(char const*)buffer, bufferSize}; + g_logger.info("%s", bufferStr.c_str()); + g_logger.info("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"); +} + +} // namespace ttmlengine +} // namespace subttxrend + diff --git a/subttxrend-ttml/src/DataDumper.h b/subttxrend-ttml/src/DataDumper.h index fa138de..cee7847 100644 --- a/subttxrend-ttml/src/DataDumper.h +++ b/subttxrend-ttml/src/DataDumper.h @@ -17,183 +17,36 @@ #pragma once -#include -#include -#include -#include -#include -#include -#include #include - -#include - -#include -#include +#include namespace subttxrend { -namespace ttmlengine -{ - -namespace -{ -common::Logger g_logger("TtmlEngine", "DataDumper"); - +namespace common { + class ConfigProvider; } +namespace ttmlengine +{ /** * Data dumper - used for debugging purposes. */ class DataDumper { public: - DataDumper() = default; - void setup(const common::ConfigProvider* provider) - { - assert(provider); - - dumpTolog = (provider->getInt("DUMP_TO_LOG", 0) != 0); - - std::string dumpDir = provider->get("DUMP_DIR"); - if (dumpDir != "") - { - mkdir(dumpDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); - dirPath.assign(dumpDir); - dumpToFile = true; - g_logger.info("ttml dump directory created: %s", dirPath.c_str()); - } - } - - void dump(const std::uint8_t* buffer, - std::size_t bufferSize) const - { - if (dumpTolog) - { - toLog(buffer, bufferSize); - } - - if (dumpToFile) - { - dumpXml(buffer, bufferSize); - } - } - - void toFile(const std::string filename, - const std::uint8_t* buffer, - std::size_t bufferLen) const - { - if (! dirPath.empty()) - { - g_logger.info("dumping: %p (size %zu) to %s", buffer, bufferLen, filename.c_str()); - - std::string filePath = dirPath + filename; - FILE* dumpFile = fopen(filePath.c_str(), "aw+"); - if (dumpFile) - { - auto written = fwrite(buffer, 1, bufferLen, dumpFile); - g_logger.debug("written: %zu out of %zu to %s", written, bufferLen, filePath.c_str()); - fclose(dumpFile); - } - else - { - g_logger.error("could not open dump file: %s errno: %d", filePath.c_str(), errno); - } - } - } - - bool imageDumpEnabled() const - { - return dumpToFile; - } - - std::vector readTtmlFromFile(const std::string& path) - { - std::vector data; - - FILE* dataFile = fopen(path.c_str(), "r"); - if (dataFile) - { - fseek(dataFile,0,SEEK_END); - auto fileSize = ftell(dataFile); - fseek(dataFile,0,SEEK_SET); - - data.resize(fileSize); - auto dataRead = fread(data.data(), 1, data.size(), dataFile); - - g_logger.info("read: %zu out of %zu from %s", dataRead, data.size(), path.c_str()); - - fclose(dataFile); - } - else - { - g_logger.error("could not open dump file: %s errno: %d", path.c_str(), errno); - } - return data; - } + void setup(const common::ConfigProvider* provider); + void dump(const std::uint8_t* buffer, std::size_t bufferSize) const; + void toFile(const std::string filename, const std::uint8_t* buffer, std::size_t bufferLen) const; + bool imageDumpEnabled() const; + std::vector readTtmlFromFile(const std::string& path); private: + void dumpXml(const std::uint8_t* buffer, std::size_t bufferSize) const; + void toLog(const std::uint8_t* buffer, std::size_t bufferSize) const; - void dumpXml(const std::uint8_t* buffer, - std::size_t bufferSize) const - { - using namespace std::chrono; - - // get current time and get the number of milliseconds for the current second - // = remainder after division into seconds - auto now = system_clock::now(); - auto ms = duration_cast(now.time_since_epoch()) % 1000; - - // convert current time to std::time_t in order to convert to std::tm - auto converttime = system_clock::to_time_t(now); - std::tm tmtime = *std::localtime(&converttime); - - char hoursMinutesSeconds[24]{}; - - // convert to HH:MM:SS string, - (void)std::strftime(hoursMinutesSeconds, sizeof(hoursMinutesSeconds), "%T", &tmtime); - - std::ostringstream oss; - - oss << hoursMinutesSeconds; - oss << '.' << std::setfill('0') << std::setw(3) << ms.count(); - - auto filename = oss.str(); - - std::replace(filename.begin(), filename.end(), ':', '_'); - std::replace(filename.begin(), filename.end(), '.', '_'); - - filename += ".xml"; - - toFile(filename, buffer, bufferSize); - } - - void toLog(const std::uint8_t* buffer, std::size_t bufferSize) const - { - g_logger.info("TTML DATA DUMP >>>>>>>>>>>>>>>>>>>>>>>>>"); - std::string str; - str.resize(bufferSize); - for (std::size_t i = 0; i < bufferSize; ++i ) - { - if (buffer[i] == '\n') - { - g_logger.info("%s", str.c_str()); - str.clear(); - } - else - { - str += (reinterpret_cast(buffer)[i]); - } - } - if (!str.empty()) - { - g_logger.info("%s", str.c_str()); - } - g_logger.info("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"); - } - +private: bool dumpTolog{false}; bool dumpToFile{false}; std::string dirPath; @@ -203,4 +56,3 @@ class DataDumper } // namespace ttmlengine } // namespace subttxrend - diff --git a/subttxrend-ttml/src/IntermediateDocDrawer.cpp b/subttxrend-ttml/src/IntermediateDocDrawer.cpp index 1b6cbcd..f6f7392 100644 --- a/subttxrend-ttml/src/IntermediateDocDrawer.cpp +++ b/subttxrend-ttml/src/IntermediateDocDrawer.cpp @@ -32,7 +32,16 @@ namespace subttxrend namespace ttmlengine { -const std::string IntermediateDocDrawer::FALLBACK_FONT_NAME = "Bitstream Vera Sans Mono Bold"; +namespace +{ +/** Text rectangle horizontal margin. + Calculated as percent of font size. + The value 32,1% is used to give 26px for font 81px */ +const auto BACKGROUND_RECT_MARGIN = DomainValue(DomainValue::Type::PERCENTAGE_HUNDREDTHS, 3210); + +/** Default font to use if loading requested font failed. */ +constexpr auto FALLBACK_FONT_NAME = "Bitstream Vera Sans Mono Bold"; +}//anonymous namespace IntermediateDocDrawer::IntermediateDocDrawer(const common::ConfigProvider *configProvider, const ValueConverter &valueConverter) @@ -171,6 +180,7 @@ bool IntermediateDocDrawer::drawLine(const IntermediateDocument::TextLine& textL const auto cellHeightPx = m_valueConverter.getCellHeight(); drawingState.m_fontSize = m_valueConverter.sizeToPixels(style.getFontSize(), cellHeightPx); drawingState.m_font = getFont(style.getFontFamily(), drawingState.m_fontSize); + drawingState.m_margin = m_valueConverter.sizeToPixels(BACKGROUND_RECT_MARGIN, drawingState.m_fontSize); if (drawingState.m_font) { @@ -214,13 +224,14 @@ void IntermediateDocDrawer::alignPenX(DrawingState& drawingState, const std::int32_t textLength = drawingState.m_lineSize.m_w; m_logger.ostrace(__LOGGER_FUNC__, " textAlign=", textAlign, " textLength=", textLength); + if (textAlign == StyleSet::TextAlign::LEFT) { - drawingState.m_penX = BACKGROUND_RECT_MARGIN; + drawingState.m_penX = drawingState.m_margin; } else if (textAlign == StyleSet::TextAlign::RIGHT) { - drawingState.m_penX = drawingState.m_regionRect.m_w - textLength - BACKGROUND_RECT_MARGIN; + drawingState.m_penX = drawingState.m_regionRect.m_w - textLength - drawingState.m_margin; } else // defaults - "center" { @@ -283,10 +294,10 @@ void IntermediateDocDrawer::getLineSize(const IntermediateDocument::TextLine& li DrawingState& drawingState) { assert(drawingState.m_font); - float textWidth = 0.0; - float textTrimmed = 0.0; + std::int32_t textWidth = 0; + std::int32_t textTrimmed = 0; bool textStarted = false; - float xOffset = 0.0; + std::int32_t xOffset = 0; for (const IntermediateDocument::TextChunk& chunk : line) { @@ -298,21 +309,21 @@ void IntermediateDocDrawer::getLineSize(const IntermediateDocument::TextLine& li if (!textStarted) { if (token.isWhite) - xOffset += token.totalAdvanceX; + xOffset += std::round(token.totalAdvanceX); else textStarted = true; } if (textStarted) - textWidth += token.totalAdvanceX; + textWidth += std::round(token.totalAdvanceX); if (!token.isWhite) textTrimmed = textWidth; } } - drawingState.m_lineSize.m_w = static_cast(std::ceil(textTrimmed)); + drawingState.m_lineSize.m_w = textTrimmed; drawingState.m_lineSize.m_h = m_valueConverter.sizeToPixels(lineHeight, drawingState.m_fontSize); - drawingState.m_lineXOffset = static_cast(std::ceil(xOffset)); + drawingState.m_lineXOffset = xOffset; } void IntermediateDocDrawer::drawLineBackground(const gfx::ColorArgb& backgroundColor, @@ -323,9 +334,9 @@ void IntermediateDocDrawer::drawLineBackground(const gfx::ColorArgb& backgroundC { dc.fillRectangle(backgroundColor, gfx::Rectangle { - drawingState.m_regionRect.m_x + drawingState.m_lineXOffset + drawingState.m_penX - BACKGROUND_RECT_MARGIN, + drawingState.m_regionRect.m_x + drawingState.m_lineXOffset + drawingState.m_penX - drawingState.m_margin, drawingState.m_regionRect.m_y + drawingState.m_penY, - drawingState.m_lineSize.m_w + (2 * BACKGROUND_RECT_MARGIN), + drawingState.m_lineSize.m_w + (2 * drawingState.m_margin), drawingState.m_lineSize.m_h }); } } @@ -361,7 +372,7 @@ bool IntermediateDocDrawer::drawChunk(const IntermediateDocument::TextChunk& chu if (token.forceNewline) { // TODO: FEATURE tts:wrapOption support - if ((drawingState.m_penX + token.totalAdvanceX) > drawingState.m_regionRect.m_w) + if ((drawingState.m_penX + std::round(token.totalAdvanceX)) > drawingState.m_regionRect.m_w) { newline(drawingState); didNewline = true; @@ -386,7 +397,7 @@ bool IntermediateDocDrawer::drawChunk(const IntermediateDocument::TextChunk& chu // TODO: would need more whitespace handling in textToTokens otherwise if (!didNewline) { - drawingState.m_penX += token.totalAdvanceX; + drawingState.m_penX += std::round(token.totalAdvanceX); } } else @@ -427,7 +438,7 @@ bool IntermediateDocDrawer::drawChunk(const IntermediateDocument::TextChunk& chu //then draw the text on top of the outline dc.drawString(*(drawingState.m_font.get()), rectangle, token.glyphs, fgColor, gfx::ColorArgb::TRANSPARENT, 0, verticalOffset); - drawingState.m_penX += token.totalAdvanceX; + drawingState.m_penX += std::round(token.totalAdvanceX); } } diff --git a/subttxrend-ttml/src/IntermediateDocDrawer.hpp b/subttxrend-ttml/src/IntermediateDocDrawer.hpp index da799d4..d64578d 100644 --- a/subttxrend-ttml/src/IntermediateDocDrawer.hpp +++ b/subttxrend-ttml/src/IntermediateDocDrawer.hpp @@ -97,6 +97,9 @@ class IntermediateDocDrawer //current font size int m_fontSize{0}; + //current magin [px] + int m_margin{0}; + //total height of all text lines per entity unsigned int m_totalTextHeight = 0; }; @@ -183,12 +186,6 @@ class IntermediateDocDrawer std::int32_t getMargin(IntermediateDocument::TextLine const& line); private: - /** Text rectangle vertical margin. */ - static constexpr int BACKGROUND_RECT_MARGIN = 10; - - /** Default font to use if loading requested font failed. */ - static const std::string FALLBACK_FONT_NAME; - /** Logger object. */ subttxrend::common::Logger m_logger; diff --git a/subttxrend-ttml/src/Parser/DocumentInstance.cpp b/subttxrend-ttml/src/Parser/DocumentInstance.cpp new file mode 100644 index 0000000..cd7d741 --- /dev/null +++ b/subttxrend-ttml/src/Parser/DocumentInstance.cpp @@ -0,0 +1,529 @@ +/***************************************************************************** +* If not stated otherwise in this file or this component's LICENSE file the +* following copyright and licenses apply: +* +* Copyright 2021 Liberty Global Service B.V. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*****************************************************************************/ + +#include "DocumentInstance.hpp" + +namespace subttxrend +{ +namespace ttmlengine +{ +DocumentInstance::DocumentInstance() : + m_styles(), + m_regions(), + m_parsedBodyElementsStack(), + m_content(), + m_logger("TtmlEngine", "DocumentInstance") +{ + // noop +} + +void DocumentInstance::reset() +{ + m_root.reset(); + m_regions.clear(); + m_styles.clear(); + m_content.clear(); + m_images.clear(); + + while (!m_parsedBodyElementsStack.empty()) + { + m_parsedBodyElementsStack.pop(); + } + // We do not clear the override style attributes + + m_currentImageElement = std::shared_ptr(); +} + +std::shared_ptr DocumentInstance::startElement(const std::string& name) +{ + m_logger.ostrace(__LOGGER_FUNC__, " name ", name); + + if (name == "image") + { + std::shared_ptr img = std::make_shared(); + m_images.insert(img); + m_currentImageElement = img; + return img; + } + else if (name == "region") + { + auto regionElement = std::make_shared(); + m_regions.insert(regionElement); + return regionElement; + } + else if (name == "style") + { + auto styleElement = std::make_shared(); + m_styles.insert(styleElement); + return styleElement; + } + else if (name == "body" || name == "div" || name == "p" || name == "span") + { + std::shared_ptr bodyElement; + if (m_parsedBodyElementsStack.empty()) + { + bodyElement = std::make_shared(); + } + else + { + bodyElement = std::make_shared(m_parsedBodyElementsStack.top()); + } + m_parsedBodyElementsStack.push(bodyElement); + m_content.push_back(bodyElement); + return bodyElement; + } + else if (name == "tt") + { + if (m_root == nullptr) + { + m_root = std::make_shared(); + } + else + { + m_logger.oswarning(__LOGGER_FUNC__, " more than one tt element in document"); + } + return m_root; + } + else if (name == "br") + { + if (m_parsedBodyElementsStack.empty() == false) + { + auto& currentBodyElement = m_parsedBodyElementsStack.top(); + currentBodyElement->appendNewline(); + } + m_parsedBodyElementsStack.push(nullptr); + return nullptr; + } + else + { + return nullptr; + } +} + +void DocumentInstance::endElement(bool copyTopElement) +{ + if (m_parsedBodyElementsStack.empty() == false) + { + auto currentElement = m_parsedBodyElementsStack.top(); + m_parsedBodyElementsStack.pop(); + + if (currentElement and currentElement->hasValidContent()) + { + currentElement->finalize(); + } + } + // + // Below creates a copy of the parent and adds to m_content collection on "end" of child element. + // + // This is to cover cases like: + //

2
4

+ // "p" element is parent, "span" elements are children of "p". Whenever end of "span" element is detected - + // this function is called for "span" - a copy of "p" is added to m_content collection and endElement() is called + // for previously added "p" - so there is single "p" element on BodyElement-s stack. + // + //
- new line - needs to be added in the correct place - after 2 + // To achieve this

element is added to m_content collection each time its child element ends. + // Once it's added as top element - the text and new lines are added to it (by the Parser). + // + // With adding

element on child end rendering is: + // 24 + // + // Without adding

element on child end rendering is: + // 24 + // + // As a side effect following

is now correctly supported as well: + //

12345

+ // + if (copyTopElement && m_parsedBodyElementsStack.empty() == false) + { + //Note: copyElement is created with its original element as parent + auto copyElement = std::make_shared(m_parsedBodyElementsStack.top()); + endElement(false); + m_parsedBodyElementsStack.push(copyElement); + m_content.push_back(copyElement); + } + + m_currentImageElement = std::shared_ptr(); +} + +std::shared_ptr DocumentInstance::getCurrentElement() const +{ + return m_parsedBodyElementsStack.empty() ? nullptr : m_parsedBodyElementsStack.top(); +} + +std::shared_ptr DocumentInstance::getCurrentImageElement() const +{ + return m_currentImageElement; +} + +void DocumentInstance::newEntity(std::vector& entities, + std::string reason) const +{ + if (!entities.empty() && !entities.back().m_textLines.empty()) + { + applyWhitespaceHandling(entities.back().m_textLines.back()); + } + entities.emplace_back(); + entities.back().m_region = std::make_shared(); + m_logger.ostrace(__LOGGER_FUNC__, " - ", reason); +} + +void DocumentInstance::applyWhitespaceHandling(IntermediateDocument::TextLine& textLine) const +{ + //Text chunks with DEFAULT space handling are already treated by + //BodyElement::applyDefaultWhitespaceHandling() + //now spaces: + // - leading + // - trailing + // - on the border between text chunks + // need to be handled + //(they might come from different elements + have different space handling) + if (!textLine.empty()) + { + //Remove leading space + auto& firstChunk = textLine.front(); + if (firstChunk.m_whitespaceHandling == XmlSpace::DEFAULT && !firstChunk.m_text.empty()) + { + if (isSpace(firstChunk.m_text.front())) + { + firstChunk.m_text.erase(0, 1); + } + } + + //Remove trailing space + auto& lastChunk = textLine.back(); + if (lastChunk.m_whitespaceHandling == XmlSpace::DEFAULT && !lastChunk.m_text.empty()) + { + if (isSpace(lastChunk.m_text.back())) + { + lastChunk.m_text.pop_back(); + } + } + + //Remove spaces on the border between text chunks + //in case there are mixed (DEFAULT/PRESERVE) whitespace handling + //check leading and trailing spaces + for (int i=0; i < ((int)textLine.size() - 1); i++) + { + if (textLine[i].m_whitespaceHandling == XmlSpace::DEFAULT && !textLine[i].m_text.empty()) + { + if (isSpace(textLine[i].m_text.back()) && isSpace(textLine[i+1].m_text.front())) + { + textLine[i].m_text.pop_back(); + } + } + if (i > 0 && textLine[i].m_whitespaceHandling == XmlSpace::DEFAULT && !textLine[i].m_text.empty()) + { + if (isSpace(textLine[i].m_text.front()) && isSpace(textLine[i-1].m_text.back())) + { + textLine[i].m_text.erase(0, 1); + } + } + } + } +} + +void DocumentInstance::newLine(IntermediateDocument::Entity& entity) const +{ + if (!entity.m_textLines.empty()) + { + applyWhitespaceHandling(entity.m_textLines.back()); + } + entity.m_textLines.emplace_back(); +} + +std::list DocumentInstance::generateTimeline() const +{ + std::list timeline; + + if (m_root) + { + auto timings = generateTimings(); + updateStyleAttributes(); + updateRegionAttributes(); + + for (auto &timing : timings) + { + m_logger.ostrace(__LOGGER_FUNC__, ' ', timing.toStr()); + + std::vector entities; + + for (auto &content : m_content) + { + assert(content.get() != nullptr); + + if (content->getTiming().isOverlapping(timing)) + { + if (entities.empty()) + { + //Add new Entity on timing change + newEntity(entities, "document start"); + } + auto region = findRegion(content->getRegionId()); + if (region != nullptr) + { + //Create a new entity if the current one is not empty and is not in the same + //region. + //Otherwise just update the region + if ((!entities.back().empty()) && + ((entities.back().m_region == nullptr) || + (entities.back().m_region->getId() != content->getRegionId()))) + { + newEntity(entities, "region"); + } + entities.back().m_region = std::move(region); + } + + auto image = findImage(content->getBackgroundImageId()); + if (image != nullptr) + { + //image based subtitles - IntermediateDocument should hold single image + entities.back().m_imageChunk.m_image = std::move(image); + if (entities.size() > 1) + { + m_logger.oswarning("Subtitles with multiple images or mixed image and text not supported"); + } + } + else + { + //text based subtitles - IntermediateDocument::Entity represents single region to render + // it may consists of more than one BodyElement-s + const auto& contextLines = content->getTextLines(); + + for (const auto& textLine : contextLines) + { + if (!textLine.text.empty()) + { + if (entities.back().m_textLines.empty()) + { + newLine(entities.back()); + } + + auto& currentLine = entities.back().m_textLines.back(); + currentLine.emplace_back(); + auto& textChunk = currentLine.back(); + textChunk.m_text = textLine.text; + textChunk.m_whitespaceHandling = content->getWhiteSpaceHandling(); + + auto styleId = content->getStyleId(); + textChunk.m_style.setStyleId(styleId); + textChunk.m_style.merge(content->getStyleAttributes()); + + m_logger.ostrace(__LOGGER_FUNC__, " chunk: \'", textChunk.m_text, "\'", ", style: ", textChunk.m_style.toStr()); + } + //If TTML contained new line mark - add new line to render + if (textLine.isForcedLine == true && !entities.back().empty()) + { + newLine(entities.back()); + } + } + } + } + } + if (!entities.empty()) + { + if (!entities.back().m_textLines.empty()) + { + applyWhitespaceHandling(entities.back().m_textLines.back()); + } + + IntermediateDocument timespan; + + timespan.m_entites = std::move(entities); + timespan.m_timing = timing; + timespan.setCellResolution(m_root->getCellResolution()); + + timeline.emplace_back(std::move(timespan)); + } + } + } + else + { + m_logger.oswarning("Root element not found - ignoring content"); + } + + return timeline; +} + +void DocumentInstance::dump() const +{ + m_logger.ostrace("--------- STYLES -----------"); + for (auto &style : m_styles) + { + m_logger.ostrace("id: ", style->getId()); + } + + m_logger.ostrace("--------- REGIONS -----------"); + for (auto ®ion : m_regions) + { + m_logger.ostrace(region->toStr()); + } + + m_logger.ostrace("--------- CONTENTS -----------"); + for (auto &content : m_content) + { + auto timing = content->getTiming(); + m_logger.ostrace("id:", + content->getId(), + " ", + timing.toStr(), + " reg:", + content->getRegionId(), + " style:", + content->getStyleId(), + " lines of text: ", + content->getTextLines().size()); + } +} + +std::shared_ptr DocumentInstance::findStyle(const std::string& styleId) const +{ + auto retVal = find(styleId, m_styles); + if (retVal == nullptr) { + retVal = std::make_shared(); + } + return retVal; +} + +std::shared_ptr DocumentInstance::findRegion(const std::string& regionId) const +{ + return find(regionId, m_regions); +} + +std::shared_ptr DocumentInstance::findImage(const std::string& imageId) const +{ + return find(imageId, m_images); +} + +std::vector DocumentInstance::generateTimings() const +{ + std::set timepoints; + + // first create sorted list of unique points in time... + for (auto &content : m_content) + { + std::string contentStr; + + for (auto& line : content->getTextLines()) { + contentStr.append("["); + contentStr.append(line.text); + contentStr.append("]"); + } + m_logger.ostrace( + __LOGGER_FUNC__, " some content, with texts[", content->getTextLines().size(), "]: ", contentStr); + + if (!(content->getTextLines().empty() && content->getBackgroundImageId().empty())) + { + timepoints.insert(content->getTiming().getStartTimeRef()); + timepoints.insert(content->getTiming().getEndTimeRef()); + } + } + + // than generate time periods for every neighboring timepoints + std::vector result; + if (!timepoints.empty()) + { + m_logger.ostrace(__LOGGER_FUNC__, " size: ", timepoints.size()); + + auto it = timepoints.begin(); + auto it2 = timepoints.begin(); + it2++; + + while (it2 != timepoints.end()) + { + result.emplace_back(*it, *it2); + it = it2; + it2++; + } + } + + for (auto x : result) + { + m_logger.ostrace(__LOGGER_FUNC__, " result: ", x.toStr()); + } + + return result; +} + +void DocumentInstance::updateStyleAttributes() const +{ + for (auto& content : m_content) + { + Attributes styleAttrs; + + assert(content.get() != nullptr); + const auto& parent = content->getParent(); + + if (parent != nullptr) + { + //merge style attributes from parent + mergeAttributes(styleAttrs, parent->getStyleAttributes()); + } + + const auto& style = findStyle(content->getStyleId()); + //merge style attributes from element style + mergeAttributes(styleAttrs, style->getStyleAttributes()); + + const auto& region = findRegion(content->getRegionId()); + if (region != nullptr) + { + const auto& regionStyle = findStyle(region->getStyleId()); + //merge style attributes from region style + mergeAttributes(styleAttrs, regionStyle->getStyleAttributes()); + + //merge style attributes from region + mergeAttributes(styleAttrs, region->getStyleAttributes()); + } + + //finally merge style attributes from element + mergeAttributes(styleAttrs, content->getStyleAttributes()); + + // and at last merge style attributes from override-styling + mergeAttributes(styleAttrs, m_overrideStyleAttributes); + + content->set(std::move(styleAttrs)); + + std::ostringstream stream; + stream << content->getStyleAttributes(); + m_logger.ostrace(__LOGGER_FUNC__, " content style attributes: ", stream.str()); + } +} + +void DocumentInstance::setStyleOverrideAttributes(const Attributes& styleAttributes) +{ + m_overrideStyleAttributes = styleAttributes; +} + +void DocumentInstance::updateRegionAttributes() const +{ + for (auto& region : m_regions) + { + const auto& regionStyle = findStyle(region->getStyleId()); + //merge style attributes from region style + const auto& regionStyleAttrs = regionStyle->getStyleAttributes(); + for (auto& styleAttr : regionStyleAttrs) { + region->addAttribute(styleAttr.first, styleAttr.second); + } + + m_logger.ostrace(__LOGGER_FUNC__, " region: ", region->toStr()); + } +} + +} // namespace subttxrend +} // namespace ttmlengine diff --git a/subttxrend-ttml/src/Parser/DocumentInstance.hpp b/subttxrend-ttml/src/Parser/DocumentInstance.hpp index 9c0ae1d..8d5faf7 100644 --- a/subttxrend-ttml/src/Parser/DocumentInstance.hpp +++ b/subttxrend-ttml/src/Parser/DocumentInstance.hpp @@ -45,35 +45,12 @@ class DocumentInstance /** * Constructor. */ - DocumentInstance() : - m_styles(), - m_regions(), - m_parsedBodyElementsStack(), - m_content(), - m_logger("TtmlEngine", "DocumentInstance") - { - // noop - } + DocumentInstance(); /** * Clears document. */ - void reset() - { - m_root.reset(); - m_regions.clear(); - m_styles.clear(); - m_content.clear(); - m_images.clear(); - - while (!m_parsedBodyElementsStack.empty()) - { - m_parsedBodyElementsStack.pop(); - } - // We do not clear the override style attributes - - m_currentImageElement = std::shared_ptr(); - } + void reset(); /** * Adds element to the document. @@ -83,71 +60,7 @@ class DocumentInstance * @return * Shared pointer to the element or nullptr if element type is not supported. */ - std::shared_ptr startElement(const std::string& name) - { - m_logger.ostrace(__LOGGER_FUNC__, " name ", name); - - if (name == "image") - { - std::shared_ptr img = std::make_shared(); - m_images.insert(img); - m_currentImageElement = img; - return img; - } - else if (name == "region") - { - auto regionElement = std::make_shared(); - m_regions.insert(regionElement); - return regionElement; - } - else if (name == "style") - { - auto styleElement = std::make_shared(); - m_styles.insert(styleElement); - return styleElement; - } - else if (name == "body" || name == "div" || name == "p" || name == "span") - { - std::shared_ptr bodyElement; - if (m_parsedBodyElementsStack.empty()) - { - bodyElement = std::make_shared(); - } - else - { - bodyElement = std::make_shared(m_parsedBodyElementsStack.top()); - } - m_parsedBodyElementsStack.push(bodyElement); - m_content.push_back(bodyElement); - return bodyElement; - } - else if (name == "tt") - { - if (m_root == nullptr) - { - m_root = std::make_shared(); - } - else - { - m_logger.oswarning(__LOGGER_FUNC__, " more than one tt element in document"); - } - return m_root; - } - else if (name == "br") - { - if (m_parsedBodyElementsStack.empty() == false) - { - auto& currentBodyElement = m_parsedBodyElementsStack.top(); - currentBodyElement->appendNewline(); - } - m_parsedBodyElementsStack.push(nullptr); - return nullptr; - } - else - { - return nullptr; - } - } + std::shared_ptr startElement(const std::string& name); /** * Sets styling override attributes. @@ -155,59 +68,12 @@ class DocumentInstance * @param styleAttributes * The styling override attributes. */ - void setStyleOverrideAttributes(const Attributes& styleAttributes) - { - m_overrideStyleAttributes = styleAttributes; - } + void setStyleOverrideAttributes(const Attributes& styleAttributes); /** * Signals end of element. */ - void endElement(bool copyTopElement = true) - { - if (m_parsedBodyElementsStack.empty() == false) - { - auto currentElement = m_parsedBodyElementsStack.top(); - m_parsedBodyElementsStack.pop(); - - if (currentElement and currentElement->hasValidContent()) - { - currentElement->finalize(); - } - } - // - // Below creates a copy of the parent and adds to m_content collection on "end" of child element. - // - // This is to cover cases like: - //

2
4

- // "p" element is parent, "span" elements are children of "p". Whenever end of "span" element is detected - - // this function is called for "span" - a copy of "p" is added to m_content collection and endElement() is called - // for previously added "p" - so there is single "p" element on BodyElement-s stack. - // - //
- new line - needs to be added in the correct place - after 2 - // To achieve this

element is added to m_content collection each time its child element ends. - // Once it's added as top element - the text and new lines are added to it (by the Parser). - // - // With adding

element on child end rendering is: - // 24 - // - // Without adding

element on child end rendering is: - // 24 - // - // As a side effect following

is now correctly supported as well: - //

12345

- // - if (copyTopElement && m_parsedBodyElementsStack.empty() == false) - { - //Note: copyElement is created with its original element as parent - auto copyElement = std::make_shared(m_parsedBodyElementsStack.top()); - endElement(false); - m_parsedBodyElementsStack.push(copyElement); - m_content.push_back(copyElement); - } - - m_currentImageElement = std::shared_ptr(); - } + void endElement(bool copyTopElement = true); /** * Currently parsed element getter. @@ -215,23 +81,16 @@ class DocumentInstance * @return * Currently parsed element. */ - std::shared_ptr getCurrentElement() const - { - return m_parsedBodyElementsStack.empty() ? nullptr : m_parsedBodyElementsStack.top(); - } + std::shared_ptr getCurrentElement() const; - std::shared_ptr getCurrentImageElement() const - { - return m_currentImageElement; - } + std::shared_ptr getCurrentImageElement() const; void newEntity(std::vector& entities, - std::string reason) const - { - entities.emplace_back(); - entities.back().m_region = std::make_shared(); - m_logger.ostrace(__LOGGER_FUNC__, " - ", reason); - } + std::string reason) const; + + void applyWhitespaceHandling(IntermediateDocument::TextLine& textLine) const; + + void newLine(IntermediateDocument::Entity& entity) const; /** * Generates list of time periods during which ttml content is constant. @@ -249,143 +108,12 @@ class DocumentInstance * @return * Timelined list of text content and it's properties. */ - std::list generateTimeline() const - { - std::list timeline; - - if (m_root) - { - auto timings = generateTimings(); - updateStyleAttributes(); - updateRegionAttributes(); - - for (auto &timing : timings) - { - m_logger.ostrace(__LOGGER_FUNC__, ' ', timing.toStr()); - - std::vector entities; - - for (auto &content : m_content) - { - assert(content.get() != nullptr); - - if (content->getTiming().isOverlapping(timing)) - { - if (entities.empty()) - { - //Add new Entity on timing change - newEntity(entities, "document start"); - } - auto region = findRegion(content->getRegionId()); - if (region != nullptr) - { - //New region - create new entity if current is not empty - //Otherwise just update the region - if (!entities.back().empty()) - { - newEntity(entities, "region"); - } - entities.back().m_region = std::move(region); - } - - auto image = findImage(content->getBackgroundImageId()); - if (image != nullptr) - { - //image based subtitles - IntermediateDocument should hold single image - entities.back().m_imageChunk.m_image = std::move(image); - if (entities.size() > 1) - { - m_logger.oswarning("Subtitles with multiple images or mixed image and text not supported"); - } - } - else - { - //text based subtitles - IntermediateDocument::Entity represents single region to render - // it may consists of more than one BodyElement-s - const auto& contextLines = content->getTextLines(); - - for (const auto& textLine : contextLines) - { - if (!textLine.text.empty()) - { - if (entities.back().m_textLines.empty()) - { - entities.back().m_textLines.emplace_back(); - } - - auto& currentLine = entities.back().m_textLines.back(); - currentLine.emplace_back(); - auto &textChunk = currentLine.back(); - textChunk.m_text = textLine.text; - - auto styleId = content->getStyleId(); - textChunk.m_style.setStyleId(styleId); - textChunk.m_style.merge(content->getStyleAttributes()); - - m_logger.ostrace(__LOGGER_FUNC__, " chunk: \'", textChunk.m_text, "\'", ", style: ", textChunk.m_style.toStr()); - } - //If TTML contained new line mark - add new line to render - if (textLine.isForcedLine == true && !entities.back().empty()) - { - entities.back().m_textLines.emplace_back(); - } - } - } - } - } - if (!entities.empty()) - { - IntermediateDocument timespan; - - timespan.m_entites = std::move(entities); - timespan.m_timing = timing; - timespan.setCellResolution(m_root->getCellResolution()); - - timeline.emplace_back(std::move(timespan)); - } - } - } - else - { - m_logger.oswarning("Root element not found - ignoring content"); - } - - return timeline; - } + std::list generateTimeline() const; /** * Dumps contents to log. Used for debugging purposes. */ - void dump() const - { - m_logger.ostrace("--------- STYLES -----------"); - for (auto &style : m_styles) - { - m_logger.ostrace("id: ", style->getId()); - } - - m_logger.ostrace("--------- REGIONS -----------"); - for (auto ®ion : m_regions) - { - m_logger.ostrace(region->toStr()); - } - - m_logger.ostrace("--------- CONTENTS -----------"); - for (auto &content : m_content) - { - auto timing = content->getTiming(); - m_logger.ostrace("id:", - content->getId(), - " ", - timing.toStr(), - " reg:", - content->getRegionId(), - " style:", - content->getStyleId(), - " lines of text: ", - content->getTextLines().size()); - } - } + void dump() const; private: @@ -421,14 +149,7 @@ class DocumentInstance * @return * StyleElement instance with given id, default style if not found. */ - std::shared_ptr findStyle(const std::string& styleId) const - { - auto retVal = find(styleId, m_styles); - if (retVal == nullptr) { - retVal = std::make_shared(); - } - return retVal; - } + std::shared_ptr findStyle(const std::string& styleId) const; /** * Finds region with given region id. @@ -438,10 +159,7 @@ class DocumentInstance * @return * RegionElement instance with given id, nullptr if not found. */ - std::shared_ptr findRegion(const std::string& regionId) const - { - return find(regionId, m_regions); - } + std::shared_ptr findRegion(const std::string& regionId) const; /** * Finds image with given id. @@ -451,10 +169,7 @@ class DocumentInstance * @return * ImageElement instance with given id, nullptr if not found. */ - std::shared_ptr findImage(const std::string& imageId) const - { - return find(imageId, m_images); - } + std::shared_ptr findImage(const std::string& imageId) const; /** * Generates list of time periods within which subtitle content does not change. @@ -462,55 +177,7 @@ class DocumentInstance * @return * Sorted list of time periods. */ - std::vector generateTimings() const - { - std::set timepoints; - - // first create sorted list of unique points in time... - for (auto &content : m_content) - { - std::string contentStr; - - for (auto& line : content->getTextLines()) { - contentStr.append("["); - contentStr.append(line.text); - contentStr.append("]"); - } - m_logger.ostrace( - __LOGGER_FUNC__, " some content, with texts[", content->getTextLines().size(), "]: ", contentStr); - - if (!(content->getTextLines().empty() && content->getBackgroundImageId().empty())) - { - timepoints.insert(content->getTiming().getStartTimeRef()); - timepoints.insert(content->getTiming().getEndTimeRef()); - } - } - - // than generate time periods for every neighboring timepoints - std::vector result; - if (!timepoints.empty()) - { - m_logger.ostrace(__LOGGER_FUNC__, " size: ", timepoints.size()); - - auto it = timepoints.begin(); - auto it2 = timepoints.begin(); - it2++; - - while (it2 != timepoints.end()) - { - result.emplace_back(*it, *it2); - it = it2; - it2++; - } - } - - for (auto x : result) - { - m_logger.ostrace(__LOGGER_FUNC__, " result: ", x.toStr()); - } - - return result; - } + std::vector generateTimings() const; /** * Update style attributes for all BodyElement-s @@ -521,69 +188,14 @@ class DocumentInstance * - region * - "self" */ - void updateStyleAttributes() const - { - for (auto& content : m_content) - { - Attributes styleAttrs; - - assert(content.get() != nullptr); - const auto& parent = content->getParent(); - - if (parent != nullptr) - { - //merge style attributes from parent - mergeAttributes(styleAttrs, parent->getStyleAttributes()); - } - - const auto& style = findStyle(content->getStyleId()); - //merge style attributes from element style - mergeAttributes(styleAttrs, style->getStyleAttributes()); - - const auto& region = findRegion(content->getRegionId()); - if (region != nullptr) - { - const auto& regionStyle = findStyle(region->getStyleId()); - //merge style attributes from region style - mergeAttributes(styleAttrs, regionStyle->getStyleAttributes()); - - //merge style attributes from region - mergeAttributes(styleAttrs, region->getStyleAttributes()); - } - - //finally merge style attributes from element - mergeAttributes(styleAttrs, content->getStyleAttributes()); - - // and at last merge style attributes from override-styling - mergeAttributes(styleAttrs, m_overrideStyleAttributes); - - content->set(std::move(styleAttrs)); - - std::ostringstream stream; - stream << content->getStyleAttributes(); - m_logger.ostrace(__LOGGER_FUNC__, " content style attributes: ", stream.str()); - } - } + void updateStyleAttributes() const; /** * Add style attributes referenced by the region into region * As a result, all attributes referenced by style become attributes of region * among others "tts:extent" and "tts:origin" */ - void updateRegionAttributes() const - { - for (auto& region : m_regions) - { - const auto& regionStyle = findStyle(region->getStyleId()); - //merge style attributes from region style - const auto& regionStyleAttrs = regionStyle->getStyleAttributes(); - for (auto& styleAttr : regionStyleAttrs) { - region->addAttribute(styleAttr.first, styleAttr.second); - } - - m_logger.ostrace(__LOGGER_FUNC__, " region: ", region->toStr()); - } - } + void updateRegionAttributes() const; private: /** TT root element */ diff --git a/subttxrend-ttml/src/Parser/Elements.hpp b/subttxrend-ttml/src/Parser/Elements.hpp index a6120aa..61e7ebd 100644 --- a/subttxrend-ttml/src/Parser/Elements.hpp +++ b/subttxrend-ttml/src/Parser/Elements.hpp @@ -422,6 +422,15 @@ class ImageElement : public Element return m_base64ImageData; } + bool isSameImage(std::shared_ptr const& other) const + { + auto isSame{false}; + if (other && (getId() == other->getId()) && (m_base64ImageData->size() == other->m_base64ImageData->size())) { + isSame = !m_base64ImageData->compare(*(other->m_base64ImageData)); + } + return isSame; + } + /** * Equality operator. * @@ -482,7 +491,7 @@ class BodyElement : public Element, m_parent(parent), m_whitespaceHandling(parent->m_whitespaceHandling), m_styleRef(parent->m_styleRef), - m_regionRef(), + m_regionRef(parent->m_regionRef), m_timing(parent->m_timing), m_textLines({{}}) { @@ -514,7 +523,7 @@ class BodyElement : public Element, } } - virtual void appendNewline() + virtual void appendNewline() override { m_textLines.push_back({true, {}}); } @@ -585,6 +594,17 @@ class BodyElement : public Element, return m_parent; } + /** + * White space handling getter. + * + * @return + * white space handling. + */ + XmlSpace getWhiteSpaceHandling() + { + return m_whitespaceHandling; + } + bool hasValidContent() { return (not m_textLines.empty() or not m_backgroundImageId.empty()); @@ -597,7 +617,7 @@ class BodyElement : public Element, auto & linePair = *it; if (m_whitespaceHandling == XmlSpace::DEFAULT) { - linePair.text = trimWhitespace(linePair.text); + applyDefaultWhitespaceHandling(linePair.text); } if (linePair.isForcedLine == false && linePair.text.empty()) { @@ -612,15 +632,6 @@ class BodyElement : public Element, protected: - /** - * Defines how to process whitespace. - */ - enum class XmlSpace - { - DEFAULT = 0, //!< DEFAULT - PRESERVE //!< PRESERVE - }; - /** @copydoc Element::parseAttributeDerived */ virtual void parseAttributeDerived(const std::string& name, const std::string& value) override @@ -706,6 +717,28 @@ class BodyElement : public Element, } } + void applyDefaultWhitespaceHandling(std::string& input) + { + //for default white space handling: + // - "linefeed-treatment" = "treat-as-space" + // replace linefeed with space + // - "white-space-collapse" = "true" + // replace multiple spaces with just single one + + auto dstIt = input.begin(); + for (auto it = input.begin(); it < input.end(); it++) { + if ( ((it == input.begin()) && !isSpace(*it)) || // Retain first char only if not space + ((it != input.begin()) && (!isSpace(*(it-1)) || !isSpace(*it))) ) // handle consecutive spaces + { + *dstIt = (*it != '\n') ? *it : ' '; + dstIt++; + } + } + if (dstIt != input.end()) { + input.erase(dstIt, input.end()); + } + } + /** Parent element */ std::shared_ptr m_parent; diff --git a/subttxrend-ttml/src/Parser/IntermediateDocument.hpp b/subttxrend-ttml/src/Parser/IntermediateDocument.hpp index 7746448..941ebb4 100644 --- a/subttxrend-ttml/src/Parser/IntermediateDocument.hpp +++ b/subttxrend-ttml/src/Parser/IntermediateDocument.hpp @@ -97,6 +97,8 @@ class IntermediateDocument /** Force newline flag. */ bool m_forceNewline = false; + + XmlSpace m_whitespaceHandling = XmlSpace::DEFAULT; }; struct ImageChunk @@ -162,7 +164,7 @@ class IntermediateDocument { if (m_textLines.empty() && other.m_textLines.empty()) { return m_imageChunk.m_image && other.m_imageChunk.m_image && - m_imageChunk.m_image->getId() == other.m_imageChunk.m_image->getId(); + m_imageChunk.m_image->isSameImage(other.m_imageChunk.m_image); } // always false for text ttml return false; @@ -233,6 +235,8 @@ class IntermediateDocument * @return * True if element are the same. False otherwise. */ + //TODO: does not seem to be used - potentially to be removed + // together with used inside equality operators friend bool operator==(const IntermediateDocument& lhs, const IntermediateDocument& rhs) { diff --git a/subttxrend-ttml/src/Parser/StyleSet.cpp b/subttxrend-ttml/src/Parser/StyleSet.cpp index e2d6cd3..83e6306 100644 --- a/subttxrend-ttml/src/Parser/StyleSet.cpp +++ b/subttxrend-ttml/src/Parser/StyleSet.cpp @@ -333,8 +333,9 @@ std::string StyleSet::toStr() std::ostringstream str; str << "[" << m_styleId << "]: " << "font: " << m_color << ", " << m_fontFamily << ", " << m_fontSize - << ", bg: " << m_backgroundColor << ", text: " << m_textAlign << ", display: " << m_displayAlign - << ", outline: " << m_textOutline.getColor() << ", " << m_textOutline.getThickness(); + << ", bg: " << m_backgroundColor << ", lineH: " << m_lineHeight << ", text: " << m_textAlign + << ", display: " << m_displayAlign << ", outline: " << m_textOutline.getColor() << ", " + << m_textOutline.getThickness(); return str.str(); } diff --git a/subttxrend-ttml/src/Parser/StyleSet.hpp b/subttxrend-ttml/src/Parser/StyleSet.hpp index d12f66d..d53785a 100644 --- a/subttxrend-ttml/src/Parser/StyleSet.hpp +++ b/subttxrend-ttml/src/Parser/StyleSet.hpp @@ -31,6 +31,14 @@ namespace subttxrend { namespace ttmlengine { +/** + * Defines how to process whitespace. + */ +enum class XmlSpace +{ + DEFAULT = 0, //!< DEFAULT + PRESERVE //!< PRESERVE +}; class StyleSet { diff --git a/subttxrend-ttml/src/Parser/XmlLibSaxParserWrapper.cpp b/subttxrend-ttml/src/Parser/XmlLibSaxParserWrapper.cpp new file mode 100644 index 0000000..50489f0 --- /dev/null +++ b/subttxrend-ttml/src/Parser/XmlLibSaxParserWrapper.cpp @@ -0,0 +1,186 @@ +/***************************************************************************** +* If not stated otherwise in this file or this component's LICENSE file the +* following copyright and licenses apply: +* +* Copyright 2021 Liberty Global Service B.V. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*****************************************************************************/ + +#include "XmlLibSaxParserWrapper.hpp" +#include + +namespace subttxrend +{ +namespace ttmlengine +{ + +SaxParser::SaxParser(SaxCallbacks& callbacks) : + m_saxHandler(), m_parserCtxPtr(nullptr), m_saxCallbacks(callbacks), m_logger("TtmlEngine", "SaxParser") +{ + m_logger.ostrace(__LOGGER_FUNC__); + init(); +} + +SaxParser::~SaxParser() +{ + cleanup(); +} + +void SaxParser::parse(const std::uint8_t* buffer, + std::size_t size) +{ + static constexpr int LIBXML_PARSE_TERMINATION_MARKER = 1; + auto parseResult = xmlParseChunk(m_parserCtxPtr, + reinterpret_cast(buffer), + static_cast(size), + LIBXML_PARSE_TERMINATION_MARKER); + if (parseResult != 0) + { + m_logger.osinfo(__LOGGER_FUNC__, " error parsing chunk: ", parseResult); + } +} + +void SaxParser::reset() +{ + cleanup(); + init(); +} + +void SaxParser::init() +{ + assert(m_parserCtxPtr == nullptr); + + m_saxHandler.initialized = XML_SAX2_MAGIC; + + m_saxHandler.startDocument = onStartDocument; + m_saxHandler.endDocument = onEndDocument; + + m_saxHandler.startElementNs = onStartElementNs; + m_saxHandler.endElementNs = onEndElementNs; + + m_saxHandler.characters = onCharacters; + + m_saxHandler.warning = onWarning; + m_saxHandler.error = onError; + + m_parserCtxPtr = xmlCreatePushParserCtxt(&m_saxHandler, this, nullptr, 0, nullptr); +} + +void SaxParser::cleanup() +{ + if (m_parserCtxPtr) + { + xmlFreeParserCtxt(m_parserCtxPtr); + m_parserCtxPtr = nullptr; + } + m_saxHandler = xmlSAXHandler(); +} + +SaxParser* SaxParser::cast(void* ptr) +{ + return reinterpret_cast(ptr); +} + +void SaxParser::onStartDocument(void *ctx) +{ + auto thiz = cast(ctx); + thiz->m_saxCallbacks.onStartDocument(); +} + +void SaxParser::onEndDocument(void *ctx) +{ + auto thiz = cast(ctx); + thiz->m_saxCallbacks.onEndDocument(); +} + +void SaxParser::onStartElementNs(void * ctx, + const xmlChar * localname, + const xmlChar * prefix, + const xmlChar * URI, + int nb_namespaces, + const xmlChar ** namespaces, + int nb_attributes, + int nb_defaulted, + const xmlChar ** attributes) +{ + auto thiz = cast(ctx); + + std::vector attributesVector{static_cast(nb_attributes)}; + unsigned int index = 0; + for (int indexAttribute = 0; indexAttribute < nb_attributes; ++indexAttribute, index += 5) + { + // name should always be present + assert(attributes[index]); + std::string attrName = std::string(reinterpret_cast(attributes[index])); + + // prefix is optional + auto attrPrefixPtr = reinterpret_cast(attributes[index + 1]); + std::string attrPrefix = std::string(attrPrefixPtr ? attrPrefixPtr : ""); + + // value should be present + assert(attributes[index + 3]); + assert(attributes[index + 4]); + auto valueBegin = reinterpret_cast(attributes[index + 3]); + auto valueEnd = reinterpret_cast(attributes[index + 4]); + std::string value(valueBegin, valueEnd); + + attributesVector.emplace_back(SaxCallbacks::Attribute{attrPrefix, attrName, value}); + } + + assert(localname != nullptr); + thiz->m_saxCallbacks.onStartElementNs(reinterpret_cast(localname), + prefix != nullptr ? reinterpret_cast(prefix) : "", + URI != nullptr ? reinterpret_cast(URI) : "", + attributesVector); +} + +void SaxParser::onEndElementNs(void * ctx, + const xmlChar * localname, + const xmlChar * prefix, + const xmlChar * URI) +{ + auto thiz = cast(ctx); + assert(localname != nullptr); + thiz->m_saxCallbacks.onEndElementNs(reinterpret_cast(localname), + prefix != nullptr ? reinterpret_cast(prefix) : "", + URI != nullptr ? reinterpret_cast(URI) : ""); +} + +void SaxParser::onError(void * ctx, + const char * msg, + ...) +{ + auto thiz = cast(ctx); + thiz->m_saxCallbacks.onError(msg); +} + +void SaxParser::onWarning(void * ctx, + const char * msg, + ...) +{ + auto thiz = cast(ctx); + thiz->m_saxCallbacks.onWarning(msg); +} + +void SaxParser::onCharacters(void *ctx, + const xmlChar *ch, + int len) +{ + auto thiz = cast(ctx); + thiz->m_saxCallbacks.onCharacters(std::string{ch, ch + len}); +} + + +} //namespace subttxrend +} // namespace ttmlengine diff --git a/subttxrend-ttml/src/Parser/XmlLibSaxParserWrapper.hpp b/subttxrend-ttml/src/Parser/XmlLibSaxParserWrapper.hpp index e3bb0e1..dcc6482 100644 --- a/subttxrend-ttml/src/Parser/XmlLibSaxParserWrapper.hpp +++ b/subttxrend-ttml/src/Parser/XmlLibSaxParserWrapper.hpp @@ -25,7 +25,7 @@ #include #include - +#include #include #include @@ -145,20 +145,12 @@ class SaxParser * @param callbacks * Sax callback receiver. */ - SaxParser(SaxCallbacks& callbacks) : - m_saxHandler(), m_parserCtxPtr(nullptr), m_saxCallbacks(callbacks), m_logger("TtmlEngine", "SaxParser") - { - m_logger.ostrace(__LOGGER_FUNC__); - init(); - } + SaxParser(SaxCallbacks& callbacks); /** * Destructor. */ - ~SaxParser() - { - cleanup(); - } + ~SaxParser(); /** * Parses chunk of data. Assumes buffer contains full xml document. @@ -169,66 +161,25 @@ class SaxParser * Buffer size. */ void parse(const std::uint8_t* buffer, - std::size_t size) - { - static constexpr int LIBXML_PARSE_TERMINATION_MARKER = 1; - auto parseResult = xmlParseChunk(m_parserCtxPtr, - reinterpret_cast(buffer), - static_cast(size), - LIBXML_PARSE_TERMINATION_MARKER); - if (parseResult != 0) - { - m_logger.osinfo(__LOGGER_FUNC__, " error parsing chunk: ", parseResult); - } - } + std::size_t size); /** * Resets parser. */ - void reset() - { - cleanup(); - init(); - } + void reset(); private: /** * Initialization function. */ - void init() - { - assert(m_parserCtxPtr == nullptr); - - m_saxHandler.initialized = XML_SAX2_MAGIC; - - m_saxHandler.startDocument = onStartDocument; - m_saxHandler.endDocument = onEndDocument; - - m_saxHandler.startElementNs = onStartElementNs; - m_saxHandler.endElementNs = onEndElementNs; - - m_saxHandler.characters = onCharacters; - - m_saxHandler.warning = onWarning; - m_saxHandler.error = onError; - - m_parserCtxPtr = xmlCreatePushParserCtxt(&m_saxHandler, this, nullptr, 0, nullptr); - } + void init(); /** * Cleanup function. * */ - void cleanup() - { - if (m_parserCtxPtr) - { - xmlFreeParserCtxt(m_parserCtxPtr); - m_parserCtxPtr = nullptr; - } - m_saxHandler = xmlSAXHandler(); - } + void cleanup(); /** * Casts pointer to class instance. @@ -238,10 +189,7 @@ class SaxParser * @return * Pointer to class instance. */ - static SaxParser* cast(void* ptr) - { - return reinterpret_cast(ptr); - } + static SaxParser* cast(void* ptr); /** * Start document callback. @@ -249,11 +197,7 @@ class SaxParser * @param ctx * Context passed - expected to be pointer to class instance. */ - static void onStartDocument(void *ctx) - { - auto thiz = cast(ctx); - thiz->m_saxCallbacks.onStartDocument(); - } + static void onStartDocument(void *ctx); /** * End document callback. @@ -261,11 +205,7 @@ class SaxParser * @param ctx * Context passed - expected to be pointer to class instance. */ - static void onEndDocument(void *ctx) - { - auto thiz = cast(ctx); - thiz->m_saxCallbacks.onEndDocument(); - } + static void onEndDocument(void *ctx); /** * Start element callback. @@ -297,38 +237,7 @@ class SaxParser const xmlChar ** namespaces, int nb_attributes, int nb_defaulted, - const xmlChar ** attributes) - { - auto thiz = cast(ctx); - - std::vector attributesVector{static_cast(nb_attributes)}; - unsigned int index = 0; - for (int indexAttribute = 0; indexAttribute < nb_attributes; ++indexAttribute, index += 5) - { - // name should always be present - assert(attributes[index]); - std::string attrName = std::string(reinterpret_cast(attributes[index])); - - // prefix is optional - auto attrPrefixPtr = reinterpret_cast(attributes[index + 1]); - std::string attrPrefix = std::string(attrPrefixPtr ? attrPrefixPtr : ""); - - // value should be present - assert(attributes[index + 3]); - assert(attributes[index + 4]); - auto valueBegin = reinterpret_cast(attributes[index + 3]); - auto valueEnd = reinterpret_cast(attributes[index + 4]); - std::string value(valueBegin, valueEnd); - - attributesVector.emplace_back(SaxCallbacks::Attribute{attrPrefix, attrName, value}); - } - - assert(localname != nullptr); - thiz->m_saxCallbacks.onStartElementNs(reinterpret_cast(localname), - prefix != nullptr ? reinterpret_cast(prefix) : "", - URI != nullptr ? reinterpret_cast(URI) : "", - attributesVector); - } + const xmlChar ** attributes); /** * SAX2 callback when an element end has been detected by the parser. It provides the namespace informations for the element. @@ -345,14 +254,7 @@ class SaxParser static void onEndElementNs(void * ctx, const xmlChar * localname, const xmlChar * prefix, - const xmlChar * URI) - { - auto thiz = cast(ctx); - assert(localname != nullptr); - thiz->m_saxCallbacks.onEndElementNs(reinterpret_cast(localname), - prefix != nullptr ? reinterpret_cast(prefix) : "", - URI != nullptr ? reinterpret_cast(URI) : ""); - } + const xmlChar * URI); /** * Display and format error messages callback. @@ -364,11 +266,7 @@ class SaxParser */ static void onError(void * ctx, const char * msg, - ...) - { - auto thiz = cast(ctx); - thiz->m_saxCallbacks.onError(msg); - } + ...); /** * Display and format warning messages callback. @@ -380,11 +278,7 @@ class SaxParser */ static void onWarning(void * ctx, const char * msg, - ...) - { - auto thiz = cast(ctx); - thiz->m_saxCallbacks.onWarning(msg); - } + ...); /** * Receiving some chars from the parser. @@ -398,11 +292,7 @@ class SaxParser */ static void onCharacters(void *ctx, const xmlChar *ch, - int len) - { - auto thiz = cast(ctx); - thiz->m_saxCallbacks.onCharacters(std::string{ch, ch + len}); - } + int len); /** xmlLib sax handler. */ xmlSAXHandler m_saxHandler; diff --git a/subttxrend-ttml/src/TtmlEngineImpl.cpp b/subttxrend-ttml/src/TtmlEngineImpl.cpp index 31a168c..f99fcac 100644 --- a/subttxrend-ttml/src/TtmlEngineImpl.cpp +++ b/subttxrend-ttml/src/TtmlEngineImpl.cpp @@ -86,21 +86,15 @@ void TtmlEngineImpl::init(const common::ConfigProvider* configProvider, m_renderer = std::make_unique(configProvider, gfxWindow, m_dataDumper); m_docTransformer.setProperties(properties); + m_pathTtmlFromFile = configProvider->get("READ_FROM_FILE"); - auto ttmlFromFile = configProvider->get("READ_FROM_FILE"); - m_logger.osinfo(__LOGGER_FUNC__, " ttmlFromFile=", ttmlFromFile); - if (not ttmlFromFile.empty()) - { - auto data = m_dataDumper.readTtmlFromFile(ttmlFromFile); - addData(data.data(), data.size()); - m_useTtmlFromFile = true; - } clear(); } void TtmlEngineImpl::setRelatedVideoSize(gfx::Size relatedVideoSize) { m_renderer->setRelatedVideoSize(relatedVideoSize); + m_docTransformer.setRelatedVideoSize(relatedVideoSize); } void TtmlEngineImpl::start() @@ -122,6 +116,15 @@ void TtmlEngineImpl::stop() m_renderer->hide(); } +void TtmlEngineImpl::flush() +{ + m_logger.osinfo("flush received"); + std::lock_guard lock{m_mutex}; + m_timeline.clear(); + m_shownDocuments.clear(); + clear(); +} + void TtmlEngineImpl::pause() { m_logger.osdebug("pause received"); @@ -176,7 +179,7 @@ void TtmlEngineImpl::addData(const std::uint8_t* buffer, " mediatime=", getCurrentMediatime().toStr()); - if (m_useTtmlFromFile) + if (!m_pathTtmlFromFile.empty()) { m_logger.osinfo("using ttml from file, skipping data"); return; @@ -250,6 +253,15 @@ void TtmlEngineImpl::setSubtitleInfo(const std::string& contentType, const std:: { m_logger.osinfo(__LOGGER_FUNC__, " contentType = ", contentType, " subtitleInfo = ", subsInfo); m_docTransformer.setSubtitleInfo(contentType, subsInfo); + if (!m_pathTtmlFromFile.empty()) + { + m_logger.osinfo(__LOGGER_FUNC__, " path TTML from file: ", m_pathTtmlFromFile); + auto data = m_dataDumper.readTtmlFromFile(m_pathTtmlFromFile); + std::string tmp; + tmp.swap(m_pathTtmlFromFile); + addData(data.data(), data.size()); + m_pathTtmlFromFile.swap(tmp); + } } void TtmlEngineImpl::setCustomTtmlStyling(const std::string& styling) @@ -294,10 +306,10 @@ void TtmlEngineImpl::process() // intentionally empty }; - // remove no-loger visible ones + // remove no-longer visible ones m_shownDocuments.remove_if([&needUpdate, currentMediaTimeMs](IntermediateDocument& doc) { auto end = doc.m_timing.getEndTimeRef().toMilliseconds(); - bool ret = end < currentMediaTimeMs; + bool ret = end <= currentMediaTimeMs; if (!ret) { needUpdate = false; } @@ -390,32 +402,34 @@ std::chrono::milliseconds TtmlEngineImpl::getWaitTime() const auto anythingToDraw = !m_timeline.empty(); auto anythingToHide = !m_shownDocuments.empty() || m_startTimer; - if ((anythingToDraw || anythingToHide)) { - // TimePoint const currentMediaTime = getCurrentMediatime(); - - // if (anythingToDraw) { - // TimePoint const& start = m_timeline.front().m_timing.getStartTimeRef(); - - // if (currentMediaTime < start) { - // waitTime = start.toMilliseconds() - currentMediaTime.toMilliseconds(); - // } - // } - - // for_each(m_shownDocuments.begin(), m_shownDocuments.end(), [&waitTime, - // currentMediaTime](const IntermediateDocument &doc) { - // TimePoint const& end = doc.m_timing.getEndTimeRef(); - // auto hideTime = end.toMilliseconds() - currentMediaTime.toMilliseconds(); - // if (hideTime < waitTime) { - // waitTime = hideTime; - // } - // }); - - auto static constexpr MIN_WAIT_TIME = 25ms; - // if (waitTime < MIN_WAIT_TIME) { - waitTime = MIN_WAIT_TIME; - // } + { + std::lock_guard lock{m_mutex}; + if ((m_lastMediatimeMs != -1) && (anythingToDraw || anythingToHide)) { + // TimePoint const currentMediaTime = getCurrentMediatime(); + + // if (anythingToDraw) { + // TimePoint const& start = m_timeline.front().m_timing.getStartTimeRef(); + + // if (currentMediaTime < start) { + // waitTime = start.toMilliseconds() - currentMediaTime.toMilliseconds(); + // } + // } + + // for_each(m_shownDocuments.begin(), m_shownDocuments.end(), [&waitTime, + // currentMediaTime](const IntermediateDocument &doc) { + // TimePoint const& end = doc.m_timing.getEndTimeRef(); + // auto hideTime = end.toMilliseconds() - currentMediaTime.toMilliseconds(); + // if (hideTime < waitTime) { + // waitTime = hideTime; + // } + // }); + + auto static constexpr MIN_WAIT_TIME = 25ms; + // if (waitTime < MIN_WAIT_TIME) { + waitTime = MIN_WAIT_TIME; + // } + } } - m_logger.osdebug(__LOGGER_FUNC__, " waitTime: ", waitTime.count()); return waitTime; } diff --git a/subttxrend-ttml/src/TtmlEngineImpl.hpp b/subttxrend-ttml/src/TtmlEngineImpl.hpp index 35b0bdc..3220497 100644 --- a/subttxrend-ttml/src/TtmlEngineImpl.hpp +++ b/subttxrend-ttml/src/TtmlEngineImpl.hpp @@ -70,6 +70,9 @@ class TtmlEngineImpl : public TtmlEngine /** @copydoc TtmlEngine::stop */ virtual void stop() override; + /** @copydoc TtmlEngine::flush */ + virtual void flush() override; + /** @copydoc TtmlEngine::pause */ virtual void pause() override; @@ -162,11 +165,11 @@ class TtmlEngineImpl : public TtmlEngine /** Debug feature - show current media time on screen. */ bool m_showMediatime{false}; - /** Use ttml data from file. */ - bool m_useTtmlFromFile{false}; + /** Path file ttml data to be read from,*/ + std::string m_pathTtmlFromFile; /** Ordered list of subtitles. */ - std::mutex m_mutex; + mutable std::mutex m_mutex; std::list m_timeline; std::list m_shownDocuments; diff --git a/subttxrend-ttml/src/TtmlRenderer.cpp b/subttxrend-ttml/src/TtmlRenderer.cpp index a87e0d7..3f1d7c6 100644 --- a/subttxrend-ttml/src/TtmlRenderer.cpp +++ b/subttxrend-ttml/src/TtmlRenderer.cpp @@ -32,9 +32,8 @@ namespace ttmlengine namespace { -const gfx::Size MAX_SURFACE_SIZE{1920, 1080}; // 1x1 is special case for UI -const gfx::Size MIN_SURFACE_SIZE{2, 2}; +const gfx::Size MIN_SURFACE_SIZE{64, 64}; bool isFullScreen(gfx::Rectangle const& rect, gfx::Size const& windowSize) { @@ -59,18 +58,7 @@ TtmlRenderer::TtmlRenderer(const common::ConfigProvider *configProvider, void TtmlRenderer::setRelatedVideoSize(gfx::Size relatedVideoSize) { // size from IPPlayer -> size of max video representation - m_valueConverter.setSourceSize(relatedVideoSize); - - if ((relatedVideoSize.m_h > MAX_SURFACE_SIZE.m_h) - or (relatedVideoSize.m_w > MAX_SURFACE_SIZE.m_w)) - { - m_surfaceSize = MAX_SURFACE_SIZE; - } - else - { - m_surfaceSize = relatedVideoSize; - } - m_valueConverter.setDrawingSize(m_surfaceSize); + m_surfaceSize = m_valueConverter.setSourceSize(relatedVideoSize); } void TtmlRenderer::update() @@ -118,10 +106,7 @@ void TtmlRenderer::resizeWindow(IntermediateDocument& doc) { if (m_surfaceSize != bmpSize) { m_logger.osinfo("surface size different than bmp size:", m_surfaceSize, " vs:", bmpSize); - - m_surfaceSize = bmpSize; - m_valueConverter.setSourceSize(m_surfaceSize); - m_valueConverter.setDrawingSize(m_surfaceSize); + m_surfaceSize = m_valueConverter.setSourceSize(bmpSize); } } else diff --git a/subttxrend-ttml/src/TtmlRenderer.hpp b/subttxrend-ttml/src/TtmlRenderer.hpp index 2dfd57c..bb0fc8f 100644 --- a/subttxrend-ttml/src/TtmlRenderer.hpp +++ b/subttxrend-ttml/src/TtmlRenderer.hpp @@ -111,8 +111,7 @@ class TtmlRenderer /** Window pointer. */ gfx::Window* const m_gfxWindow; - const gfx::Size DEFAULT_SURFACE_SIZE{1920, 1080}; - gfx::Size m_surfaceSize{DEFAULT_SURFACE_SIZE}; + gfx::Size m_surfaceSize{ValueConverter::MAX_SURFACE_SIZE}; /** Value converter. */ ValueConverter m_valueConverter{}; diff --git a/subttxrend-ttml/src/TtmlTransformer.hpp b/subttxrend-ttml/src/TtmlTransformer.hpp index 77d3947..78f688b 100644 --- a/subttxrend-ttml/src/TtmlTransformer.hpp +++ b/subttxrend-ttml/src/TtmlTransformer.hpp @@ -55,6 +55,7 @@ class TtmlTransformer final public: void setProperties(common::Properties const& properties); void setSubtitleInfo(const std::string& contentType, const std::string& subsInfo); + void setRelatedVideoSize(gfx::Size relatedVideoSize); void transform(IntermediateDocument& doc); private: diff --git a/subttxrend-ttml/src/ValueConverter.cpp b/subttxrend-ttml/src/ValueConverter.cpp index de19ffd..82f455d 100644 --- a/subttxrend-ttml/src/ValueConverter.cpp +++ b/subttxrend-ttml/src/ValueConverter.cpp @@ -34,30 +34,30 @@ namespace common::Logger logger("TtmlEngine", "ValueConverter"); } -void ValueConverter::setDrawingSize(gfx::Size drawingSize_) -{ - drawingSize = drawingSize_; - updateScalingFactors(); - - logger.info("%s size %ux%u scaling[hor: %f ver: %f])", - __LOGGER_FUNC__, - drawingSize.m_w, - drawingSize.m_h, - horizontalScalingFactor, - verticalScalingFactor); -} +gfx::Size const ValueConverter::MAX_SURFACE_SIZE{1920, 1080}; -void ValueConverter::setSourceSize(gfx::Size sourceSize) +gfx::Size ValueConverter::setSourceSize(gfx::Size const& sourceSize) { srcSize = sourceSize; + if ((sourceSize.m_h > MAX_SURFACE_SIZE.m_h) || (sourceSize.m_w > MAX_SURFACE_SIZE.m_w)) + { + drawingSize = MAX_SURFACE_SIZE; + } + else + { + drawingSize = sourceSize; + } updateScalingFactors(); - logger.info("%s size %ux%u scaling[hor: %f ver: %f])", + logger.info("%s source: %ux%u drawing: %ux%u scaling[hor: %f ver: %f])", __LOGGER_FUNC__, srcSize.m_w, srcSize.m_h, + drawingSize.m_w, + drawingSize.m_h, horizontalScalingFactor, verticalScalingFactor); + return drawingSize; } void ValueConverter::setCellResolution(gfx::Size cellResolution) @@ -137,10 +137,7 @@ int ValueConverter::sizeToPixels(DomainValue value, int relativeSizeInPixels) co pixelSize = PixelDefault; } - std::stringstream ss; - ss << value; - auto valueStr = ss.str(); - logger.debug("%s size %d (from %s)", __LOGGER_FUNC__, pixelSize, valueStr.c_str()); + logger.debug("%s size %d (from %d)", __LOGGER_FUNC__, pixelSize, static_cast(value.getValue())); return pixelSize; } @@ -179,27 +176,23 @@ int ValueConverter::convert(DomainValue value, { case DomainValue::Type::PERCENTAGE_HUNDREDTHS: { - convertedValue = static_cast(value.getValue() * dimensionValue / 10000.0); + convertedValue = std::lround(value.getValue() * dimensionValue / 10000.0); break; } case DomainValue::Type::PIXEL: { - convertedValue = static_cast(value.getValue() * scalingFactor); + convertedValue = std::lround(value.getValue() * scalingFactor); break; } case DomainValue::Type::CELL_HUNDREDTHS: default: { - logger.warning("%s value type (%d) not supported for dimension", - __LOGGER_FUNC__, static_cast(value.getType())); + logger.warning("%s value (%d) not supported for dimension", + __LOGGER_FUNC__, static_cast(value.getValue())); break; } } - - std::stringstream ss; - ss << value; - auto valueStr = ss.str(); - logger.debug("%s to %d from %s)", __LOGGER_FUNC__, convertedValue, valueStr.c_str()); + logger.debug("%s to %d from %d)", __LOGGER_FUNC__, convertedValue, static_cast(value.getValue())); return convertedValue; } diff --git a/subttxrend-ttml/src/ValueConverter.hpp b/subttxrend-ttml/src/ValueConverter.hpp index 30dd2b9..2c6e9b0 100644 --- a/subttxrend-ttml/src/ValueConverter.hpp +++ b/subttxrend-ttml/src/ValueConverter.hpp @@ -32,8 +32,7 @@ class ValueConverter { public: - void setDrawingSize(gfx::Size drawingSize); - void setSourceSize(gfx::Size relatedVideoSize); + gfx::Size setSourceSize(gfx::Size const& sourceSize); void setCellResolution(gfx::Size sourceSize); int getCellHeight() const; @@ -42,6 +41,9 @@ class ValueConverter int sizeToPixels(DomainValue value, int relativeSizeInPixels) const; +public: + static gfx::Size const MAX_SURFACE_SIZE; + private: void updateScalingFactors(); diff --git a/subttxrend-ttml/src/transform/TtmlTransformer.cpp b/subttxrend-ttml/src/transform/TtmlTransformer.cpp index 1c00e05..ed8d5b5 100644 --- a/subttxrend-ttml/src/transform/TtmlTransformer.cpp +++ b/subttxrend-ttml/src/transform/TtmlTransformer.cpp @@ -41,6 +41,10 @@ void TtmlTransformer::setRules(std::vector const& rules) { } +void TtmlTransformer::setRelatedVideoSize(gfx::Size relatedVideoSize) +{ +} + Style TtmlTransformer::getPreferredStyle() { return Style::NORMAL; diff --git a/subttxrend-ttml/test/CMakeLists.txt b/subttxrend-ttml/test/CMakeLists.txt index 7c37f54..72ddcba 100644 --- a/subttxrend-ttml/test/CMakeLists.txt +++ b/subttxrend-ttml/test/CMakeLists.txt @@ -41,6 +41,7 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../include) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../src) include_directories(${LIBCPPUNIT_INCLUDE_DIRS}) include_directories(${LIBSUBTTXRENDGFX_INCLUDE_DIRS}) +include_directories(${LIBSUBTTXRENDCOMMON_INCLUDE_DIRS}) # # Macros @@ -52,6 +53,7 @@ macro (add_cppunit_test _name) set_property(TARGET ${_name} PROPERTY CXX_STANDARD 14) target_link_libraries(${_name} ${LIBSUBTTXRENDGFX_LIBRARIES}) + target_link_libraries(${_name} ${LIBSUBTTXRENDCOMMON_LIBRARIES}) target_link_libraries(${_name} ${LIBCPPUNIT_LIBRARIES}) add_test(NAME ${_name} COMMAND ${_name} ) @@ -79,9 +81,11 @@ add_cppunit_test(StyleSet_Test add_cppunit_test(DocumentInstance_Test DocumentInstance_test.cpp ../src/Parser/AttributeHandlers.cpp + ../src/Parser/DocumentInstance.cpp ../src/Parser/Outline.cpp ../src/Parser/StyleSet.cpp ../src/Parser/Utils.cpp + ../src/Parser/DocumentInstance.cpp TestRunner.cpp ) diff --git a/subttxrend-ttml/test/DocumentInstance_test.cpp b/subttxrend-ttml/test/DocumentInstance_test.cpp index 8d88dab..b3da561 100644 --- a/subttxrend-ttml/test/DocumentInstance_test.cpp +++ b/subttxrend-ttml/test/DocumentInstance_test.cpp @@ -19,12 +19,15 @@ #include #include "Parser/DocumentInstance.hpp" +#include using namespace subttxrend::ttmlengine; class DocumentInstanceTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE( DocumentInstanceTest ); + CPPUNIT_TEST(ignoreDocumentWithoutRootElement), + CPPUNIT_TEST(ignoreDocumentWithoutTTRootElement), CPPUNIT_TEST(basicDocumentInstance); CPPUNIT_TEST(referredStyle); CPPUNIT_TEST(styleInheritanceFromParent); @@ -33,6 +36,11 @@ CPPUNIT_TEST_SUITE( DocumentInstanceTest ); CPPUNIT_TEST(styleInheritanceRegionOverRegionStyle); CPPUNIT_TEST(styleInheritanceElementOverRegion); CPPUNIT_TEST(styleInheritanceOverrideOverElement); + CPPUNIT_TEST(regionGeometry); + CPPUNIT_TEST(regionGeometryInheritanceFromBody); + CPPUNIT_TEST(multiSpanRegionCase1); + CPPUNIT_TEST(multiSpanRegionCase2); + CPPUNIT_TEST(defaultWhitespaceHandling); CPPUNIT_TEST_SUITE_END(); public: @@ -405,6 +413,353 @@ CPPUNIT_TEST_SUITE_END(); CPPUNIT_ASSERT(textChunk.m_style.getTextAlign() == StyleSet::TextAlign::CENTER); } + /** + * @brief Test region geometry + * + * Parse a TTML document: + * + * + * ... + * + * ... + *

+ * p_text + *

+ * ... + *
+ * + * Verify the entity region origin and extent. + */ + void regionGeometry() + { + DocumentInstance doc{}; + + CPPUNIT_ASSERT(doc.generateTimeline().empty()); + + doc.startElement("tt"); + auto region = doc.startElement("region"); + region->parseAttribute("", "id", "region1"); + region->parseAttribute("tts", "origin", "10% 20%"); + region->parseAttribute("tts", "extent", "30% 40%"); + region->parseAttribute("tts", "textAlign", "center"); + doc.endElement(); // region + + auto pElem = doc.startElement("p"); + pElem->parseAttribute("", "begin", "00:00:00"); + pElem->parseAttribute("", "end", "00:00:10"); + pElem->parseAttribute("", "region", "region1"); + pElem->appendText("p_text"); + + doc.endElement(); // p + doc.endElement(); // tt + + auto timeline = doc.generateTimeline(); + CPPUNIT_ASSERT(timeline.size() == 1); + + auto firstDoc = timeline.front(); + CPPUNIT_ASSERT(firstDoc.m_entites.size() == 1); + + auto firstEntity = firstDoc.m_entites[0]; + CPPUNIT_ASSERT(firstEntity.m_textLines.size() == 1); + CPPUNIT_ASSERT(firstEntity.m_region); + CPPUNIT_ASSERT(firstEntity.m_region->getX().getValue() == 1000); + CPPUNIT_ASSERT(firstEntity.m_region->getY().getValue() == 2000); + CPPUNIT_ASSERT(firstEntity.m_region->getWidth().getValue() == 3000); + CPPUNIT_ASSERT(firstEntity.m_region->getHeight().getValue() == 4000); + + auto firstLine = firstEntity.m_textLines[0]; + CPPUNIT_ASSERT(firstLine.size() == 1); + + auto firstChunk = firstLine[0]; + CPPUNIT_ASSERT(firstChunk.m_text == "p_text"); + CPPUNIT_ASSERT(firstChunk.m_style.getTextAlign() == StyleSet::TextAlign::CENTER); + } + + /** + * @brief Test region geometry inherited from a body element + * + * Parse a TTML document: + * + * + * ... + * + * ... + * + * ... + *

+ * p_text + *

+ * ... + * + *
+ * + * Verify the entity region origin and extent. + */ + void regionGeometryInheritanceFromBody() + { + DocumentInstance doc{}; + + CPPUNIT_ASSERT(doc.generateTimeline().empty()); + + doc.startElement("tt"); + auto region = doc.startElement("region"); + region->parseAttribute("", "id", "region1"); + region->parseAttribute("tts", "origin", "10% 20%"); + region->parseAttribute("tts", "extent", "30% 40%"); + region->parseAttribute("tts", "textAlign", "center"); + doc.endElement(); // region + + auto body = doc.startElement("body"); + body->parseAttribute("", "region", "region1"); + + auto pElem = doc.startElement("p"); + pElem->parseAttribute("", "begin", "00:00:00"); + pElem->parseAttribute("", "end", "00:00:10"); + pElem->appendText("p_text"); + + doc.endElement(); // p + doc.endElement(); // body + doc.endElement(); // tt + + auto timeline = doc.generateTimeline(); + CPPUNIT_ASSERT(timeline.size() == 1); + + auto firstDoc = timeline.front(); + CPPUNIT_ASSERT(firstDoc.m_entites.size() == 1); + + auto firstEntity = firstDoc.m_entites[0]; + CPPUNIT_ASSERT(firstEntity.m_textLines.size() == 1); + CPPUNIT_ASSERT(firstEntity.m_region); + CPPUNIT_ASSERT(firstEntity.m_region->getX().getValue() == 1000); + CPPUNIT_ASSERT(firstEntity.m_region->getY().getValue() == 2000); + CPPUNIT_ASSERT(firstEntity.m_region->getWidth().getValue() == 3000); + CPPUNIT_ASSERT(firstEntity.m_region->getHeight().getValue() == 4000); + + auto firstLine = firstEntity.m_textLines[0]; + CPPUNIT_ASSERT(firstLine.size() == 1); + + auto firstChunk = firstLine[0]; + CPPUNIT_ASSERT(firstChunk.m_text == "p_text"); + CPPUNIT_ASSERT(firstChunk.m_style.getTextAlign() == StyleSet::TextAlign::CENTER); + } + + /** + * @brief Test region geometry applied to multiple elements - case 1 + * + * Parse a TTML document: + * + * + * ... + * + * + * ... + * + * ... + *

+ * Span 1 + *
+ * -Span 2- + *
+ * --Span 3-- + *

+ * ... + * + * ... + *
+ */ + void multiSpanRegionCase1() + { + DocumentInstance doc{}; + + CPPUNIT_ASSERT(doc.generateTimeline().empty()); + + doc.startElement("tt"); + + auto region1 = doc.startElement("region"); + region1->parseAttribute("xml", "id", "speaker_1"); + region1->parseAttribute("tts", "origin", "14% 60%"); + region1->parseAttribute("tts", "extent", "68% 16%"); + region1->parseAttribute("tts", "textAlign", "center"); + doc.endElement(); // region + + auto region2 = doc.startElement("region"); + region2->parseAttribute("xml", "id", "speaker_2"); + region2->parseAttribute("tts", "origin", "14% 70%"); + region2->parseAttribute("tts", "extent", "68% 16%"); + region2->parseAttribute("tts", "textAlign", "center"); + doc.endElement(); // region + + auto body = doc.startElement("body"); + + auto pElem = doc.startElement("p"); + pElem->parseAttribute("", "begin", "00:00:00.000"); + pElem->parseAttribute("", "end", "999999999:26:21.587"); + pElem->parseAttribute("", "region", "speaker_2"); + + auto span1 = doc.startElement("span"); + span1->appendText("Span 1"); + doc.endElement(); // span + + auto br1 = doc.startElement("br"); + doc.endElement(); // br + + auto span2 = doc.startElement("span"); + span2->appendText("-Span 2-"); + doc.endElement(); // span + + auto br2 = doc.startElement("br"); + doc.endElement(); // br + + auto span3 = doc.startElement("span"); + span3->appendText("--Span 3--"); + doc.endElement(); // span + + doc.endElement(); // p + doc.endElement(); // body + doc.endElement(); // tt + + auto timeline = doc.generateTimeline(); + CPPUNIT_ASSERT(timeline.size() == 1); + + auto firstDoc = timeline.front(); + CPPUNIT_ASSERT(firstDoc.m_entites.size() == 1); + + auto firstEntity = firstDoc.m_entites[0]; + CPPUNIT_ASSERT(firstEntity.m_textLines.size() == 3); + CPPUNIT_ASSERT(firstEntity.m_region); + CPPUNIT_ASSERT(firstEntity.m_region->getX().getValue() == 1400); + CPPUNIT_ASSERT(firstEntity.m_region->getY().getValue() == 7000); + CPPUNIT_ASSERT(firstEntity.m_region->getWidth().getValue() == 6800); + CPPUNIT_ASSERT(firstEntity.m_region->getHeight().getValue() == 1600); + + auto firstLine = firstEntity.m_textLines[0]; + CPPUNIT_ASSERT(firstLine.size() == 1); + + auto firstChunk = firstLine[0]; + CPPUNIT_ASSERT(firstChunk.m_text == "Span 1"); + CPPUNIT_ASSERT(firstChunk.m_style.getTextAlign() == StyleSet::TextAlign::CENTER); + + auto secondLine = firstEntity.m_textLines[1]; + CPPUNIT_ASSERT(secondLine.size() == 1); + + auto secondChunk = secondLine[0]; + CPPUNIT_ASSERT(secondChunk.m_text == "-Span 2-"); + CPPUNIT_ASSERT(secondChunk.m_style.getTextAlign() == StyleSet::TextAlign::CENTER); + + auto thirdLine = firstEntity.m_textLines[2]; + CPPUNIT_ASSERT(thirdLine.size() == 1); + + auto thirdChunk = thirdLine[0]; + CPPUNIT_ASSERT(thirdChunk.m_text == "--Span 3--"); + CPPUNIT_ASSERT(thirdChunk.m_style.getTextAlign() == StyleSet::TextAlign::CENTER); + } + + /** + * @brief Test region geometry applied to multiple elements - case 2 + * + * Parse a TTML document: + * + * + * ... + * + * + * ... + * + * ... + *

+ * Span 1 + * -Span 2- + * --Span 3--* + *

+ * ... + * + * ... + *
+ */ + void multiSpanRegionCase2() + { + DocumentInstance doc{}; + + CPPUNIT_ASSERT(doc.generateTimeline().empty()); + + doc.startElement("tt"); + + auto region1 = doc.startElement("region"); + region1->parseAttribute("xml", "id", "speaker_1"); + region1->parseAttribute("tts", "origin", "14% 60%"); + region1->parseAttribute("tts", "extent", "68% 16%"); + region1->parseAttribute("tts", "textAlign", "center"); + doc.endElement(); // region + + auto region2 = doc.startElement("region"); + region2->parseAttribute("xml", "id", "speaker_2"); + region2->parseAttribute("tts", "origin", "14% 70%"); + region2->parseAttribute("tts", "extent", "68% 16%"); + region2->parseAttribute("tts", "textAlign", "center"); + doc.endElement(); // region + + auto body = doc.startElement("body"); + + auto pElem = doc.startElement("p"); + pElem->parseAttribute("", "begin", "00:00:00.000"); + pElem->parseAttribute("", "end", "999999999:26:21.587"); + pElem->parseAttribute("", "region", "speaker_1"); + + auto span1 = doc.startElement("span"); + span1->appendText("Span 1"); + doc.endElement(); // span + + auto span2 = doc.startElement("span"); + span2->appendText("-Span 2-"); + doc.endElement(); // span + + auto span3 = doc.startElement("span"); + span3->appendText("--Span 3--"); + doc.endElement(); // span + + doc.endElement(); // p + doc.endElement(); // body + doc.endElement(); // tt + + auto timeline = doc.generateTimeline(); + CPPUNIT_ASSERT(timeline.size() == 1); + + auto firstDoc = timeline.front(); + CPPUNIT_ASSERT(firstDoc.m_entites.size() == 1); + + auto firstEntity = firstDoc.m_entites[0]; + CPPUNIT_ASSERT(firstEntity.m_textLines.size() == 1); + CPPUNIT_ASSERT(firstEntity.m_region); + CPPUNIT_ASSERT(firstEntity.m_region->getX().getValue() == 1400); + CPPUNIT_ASSERT(firstEntity.m_region->getY().getValue() == 6000); + CPPUNIT_ASSERT(firstEntity.m_region->getWidth().getValue() == 6800); + CPPUNIT_ASSERT(firstEntity.m_region->getHeight().getValue() == 1600); + + auto firstLine = firstEntity.m_textLines[0]; + CPPUNIT_ASSERT(firstLine.size() == 3); + + auto firstChunk = firstLine[0]; + CPPUNIT_ASSERT(firstChunk.m_text == "Span 1"); + CPPUNIT_ASSERT(firstChunk.m_style.getTextAlign() == StyleSet::TextAlign::CENTER); + + auto secondChunk = firstLine[1]; + CPPUNIT_ASSERT(secondChunk.m_text == "-Span 2-"); + CPPUNIT_ASSERT(secondChunk.m_style.getTextAlign() == StyleSet::TextAlign::CENTER); + + auto thirdChunk = firstLine[2]; + CPPUNIT_ASSERT(thirdChunk.m_text == "--Span 3--"); + CPPUNIT_ASSERT(thirdChunk.m_style.getTextAlign() == StyleSet::TextAlign::CENTER); + } + void styleInheritanceOverrideOverElement() { DocumentInstance doc{}; @@ -445,23 +800,91 @@ CPPUNIT_TEST_SUITE_END(); doc.endElement(); //tt auto timeline = doc.generateTimeline(); - CPPUNIT_ASSERT_EQUAL(1, timeline.size()); + CPPUNIT_ASSERT(timeline.size() == 1); auto firstDoc = timeline.front(); - CPPUNIT_ASSERT_EQUAL(1, firstDoc.m_entites.size()); + CPPUNIT_ASSERT(firstDoc.m_entites.size() == 1); auto firstEntity = firstDoc.m_entites[0]; - CPPUNIT_ASSERT_EQUAL(1, firstEntity.m_textLines.size()); + CPPUNIT_ASSERT(firstEntity.m_textLines.size() == 1); auto firstLine = firstEntity.m_textLines[0]; - CPPUNIT_ASSERT_EQUAL(1, firstLine.size()); + CPPUNIT_ASSERT(firstLine.size() == 1); auto textChunk = firstLine[0]; - CPPUNIT_ASSERT_EQUAL("text", textChunk.m_text); + CPPUNIT_ASSERT(textChunk.m_text == "text"); - CPPUNIT_ASSERT_EQUAL(StyleSet::TextAlign::LEFT, textChunk.m_style.getTextAlign()); - CPPUNIT_ASSERT_EQUAL(gfx::ColorArgb::BLUE, textChunk.m_style.getColor()); + CPPUNIT_ASSERT(textChunk.m_style.getTextAlign() == StyleSet::TextAlign::LEFT); + CPPUNIT_ASSERT(subttxrend::gfx::ColorArgb::BLUE == textChunk.m_style.getColor()); } + + void defaultWhitespaceHandling() + { + DocumentInstance doc{}; + + doc.startElement("tt"); + auto div = doc.startElement("div"); + div->parseAttribute("", "begin", "00:00:00"); + div->parseAttribute("", "end", "00:00:10"); + div->appendText(" \n \n div text \n "); + + auto pElem1 = doc.startElement("p"); + pElem1->appendText(" p \n\n\n text \n\n "); + doc.endElement(); //p + + auto pElem2 = doc.startElement("p"); + pElem2->appendText("p_text"); + doc.endElement(); //p + + auto pElem3 = doc.startElement("p"); + + auto spanElem1 = doc.startElement("span"); + spanElem1->appendText(" 123"); + doc.endElement(); //span + + //mimic element in a new line + auto pElem3Copy = doc.getCurrentElement(); + if (pElem3Copy) { + pElem3Copy->appendText("\n \n "); + } + + auto spanElem2 = doc.startElement("span"); + spanElem2->appendText("456 "); + doc.endElement(); //span + + doc.endElement(); //p + + doc.endElement(); //div + doc.endElement(); //tt + + auto timeline = doc.generateTimeline(); + CPPUNIT_ASSERT(timeline.size() == 1); + + auto firstDoc = timeline.front(); + CPPUNIT_ASSERT(firstDoc.m_entites.size() == 1); + + auto firstEntity = firstDoc.m_entites[0]; + CPPUNIT_ASSERT(firstEntity.m_textLines.size() == 1); + + auto firstLine = firstEntity.m_textLines[0]; + CPPUNIT_ASSERT(firstLine.size() == 5); + + auto firstChunk = firstLine[0]; + CPPUNIT_ASSERT(firstChunk.m_text == "div text "); + + auto secondChunk = firstLine[1]; + CPPUNIT_ASSERT(secondChunk.m_text == "p text "); + + auto thirdChunk = firstLine[2]; + CPPUNIT_ASSERT(thirdChunk.m_text == "p_text"); + + auto fourthChunk = firstLine[3]; + CPPUNIT_ASSERT(fourthChunk.m_text == "123"); + + auto fifthChunk = firstLine[4]; + CPPUNIT_ASSERT(fifthChunk.m_text == "456"); + } + }; // Registers the fixture into the 'registry' diff --git a/subttxrend-ttml/test/StyleSet_test.cpp b/subttxrend-ttml/test/StyleSet_test.cpp index fa958a1..22361af 100644 --- a/subttxrend-ttml/test/StyleSet_test.cpp +++ b/subttxrend-ttml/test/StyleSet_test.cpp @@ -51,7 +51,7 @@ CPPUNIT_TEST_SUITE_END(); CPPUNIT_ASSERT(styleSet.getFontFamily() == "default"); CPPUNIT_ASSERT((styleSet.getFontSize() == DomainValue{DomainValue::Type::CELL_HUNDREDTHS, 100})); CPPUNIT_ASSERT((styleSet.getLineHeight() == DomainValue{DomainValue::Type::PERCENTAGE_HUNDREDTHS, 100*100})); - CPPUNIT_ASSERT(styleSet.getTextAlign() == StyleSet::TextAlign::LEFT); + CPPUNIT_ASSERT(styleSet.getTextAlign() == StyleSet::TextAlign::CENTER); CPPUNIT_ASSERT(styleSet.getDisplayAlign() == StyleSet::DisplayAlign::BEFORE); auto outline = Outline{DomainValue{DomainValue::Type::CELL_HUNDREDTHS, 0}, subttxrend::gfx::ColorArgb{subttxrend::gfx::ColorArgb::BLACK}}; CPPUNIT_ASSERT((styleSet.getOutline() == outline)); @@ -74,7 +74,7 @@ CPPUNIT_TEST_SUITE_END(); CPPUNIT_ASSERT(styleSet.getFontFamily() == std::string{someFontName}); CPPUNIT_ASSERT((styleSet.getFontSize() == DomainValue{DomainValue::Type::CELL_HUNDREDTHS, 100})); CPPUNIT_ASSERT((styleSet.getLineHeight() == DomainValue{DomainValue::Type::PERCENTAGE_HUNDREDTHS, 100*100})); - CPPUNIT_ASSERT(styleSet.getTextAlign() == StyleSet::TextAlign::LEFT); + CPPUNIT_ASSERT(styleSet.getTextAlign() == StyleSet::TextAlign::CENTER); CPPUNIT_ASSERT(styleSet.getDisplayAlign() == StyleSet::DisplayAlign::BEFORE); auto outline = Outline{DomainValue{DomainValue::Type::CELL_HUNDREDTHS, 0}, subttxrend::gfx::ColorArgb{subttxrend::gfx::ColorArgb::BLACK}}; CPPUNIT_ASSERT((styleSet.getOutline() == outline)); diff --git a/subttxrend-ttxt/CMakeLists.txt b/subttxrend-ttxt/CMakeLists.txt index e4a6513..f16f0b4 100644 --- a/subttxrend-ttxt/CMakeLists.txt +++ b/subttxrend-ttxt/CMakeLists.txt @@ -42,7 +42,6 @@ ENDIF(CMAKE_COMPILER_IS_GNUCXX) find_package(LibSubTtxRendCommon REQUIRED) find_package(LibSubTtxRendGfx REQUIRED) find_package(LibTtxDecoder REQUIRED) -find_package(LibXkbCommon REQUIRED) # # Include directories @@ -51,7 +50,6 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) include_directories(${LIBSUBTTXRENDCOMMON_INCLUDE_DIRS}) include_directories(${LIBSUBTTXRENDGFX_INCLUDE_DIRS}) include_directories(${LIBTTXDECODER_INCLUDE_DIRS}) -include_directories(${LIBXKBCOMMON_INCLUDE_DIRS}) # # Documentation @@ -115,14 +113,13 @@ set(SUBTTXREND_TTXT_SOURCES # set(LIBRARY_NAME "subttxrend-ttxt") add_library(${LIBRARY_NAME} SHARED ${SUBTTXREND_TTXT_SOURCES}) -set_property(TARGET ${LIBRARY_NAME} PROPERTY CXX_STANDARD 11) +set_property(TARGET ${LIBRARY_NAME} PROPERTY CXX_STANDARD 14) set_property(TARGET ${LIBRARY_NAME} PROPERTY VERSION 0.0.0) set_property(TARGET ${LIBRARY_NAME} PROPERTY SOVERSION 0) set_property(TARGET ${LIBRARY_NAME} PROPERTY PUBLIC_HEADER ${SUBTTXREND_TTXT_PUBLIC_HEADERS}) target_link_libraries(${LIBRARY_NAME} ${LIBSUBTTXRENDCOMMON_LIBRARIES}) target_link_libraries(${LIBRARY_NAME} ${LIBSUBTTXRENDGFX_LIBRARIES}) target_link_libraries(${LIBRARY_NAME} ${LIBTTXDECODER_LIBRARIES}) -target_link_libraries(${LIBRARY_NAME} ${LIBXKBCOMMON_LIBRARIES}) # # Install rules @@ -143,7 +140,7 @@ find_package(PkgConfig REQUIRED) set(PACKAGE_NAME "${PROJECT_NAME}") set(PACKAGE_DESCRIPTION "Subtitles/Teletext Renderer - Teletext Renderer") set(PACKAGE_VERSION "0.0.0") -set(PACKAGE_REQUIRES "") +set(PACKAGE_REQUIRES "${LIBSUBTTXRENDCOMMON_PACKAGE_NAME} ${LIBSUBTTXRENDGFX_PACKAGE_NAME} ${LIBTTXDECODER_PACKAGE_NAME}") set(PACKAGE_LIB_NAME "${LIBRARY_NAME}") if(PKG_CONFIG_FOUND) diff --git a/subttxrend-ttxt/cmake/modules/FindLibXkbCommon.cmake b/subttxrend-ttxt/cmake/modules/FindLibXkbCommon.cmake deleted file mode 100644 index 64a8d51..0000000 --- a/subttxrend-ttxt/cmake/modules/FindLibXkbCommon.cmake +++ /dev/null @@ -1,24 +0,0 @@ -############################################################################## -# If not stated otherwise in this file or this component's LICENSE file the -# following copyright and licenses apply: -# -# Copyright 2021 Liberty Global Service B.V.# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License.# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -############################################################################## - -include(PkgConfigHelper) - -pkgconfig_resolve(LibXkbCommon - xkbcommon - xkbcommon/xkbcommon.h - xkbcommon -) diff --git a/subttxrend-ttxt/src/GfxRenderer.cpp b/subttxrend-ttxt/src/GfxRenderer.cpp index e6ccee0..cafa29a 100644 --- a/subttxrend-ttxt/src/GfxRenderer.cpp +++ b/subttxrend-ttxt/src/GfxRenderer.cpp @@ -59,14 +59,6 @@ GfxRenderer& GfxRenderer::getSingleton() } GfxRenderer::GfxRenderer() : - m_initCount(0), - m_currentClient(nullptr), - m_bgAlpha(0), - m_newPageId(0), - m_zoomMode(ZoomMode::NONE), - m_paused(false), - m_transparentMode(false), - m_clut(), m_gridModel(GRID_SIZE_IN_CELLS), m_grid(m_gridModel, m_clut) { diff --git a/subttxrend-ttxt/src/GfxRenderer.hpp b/subttxrend-ttxt/src/GfxRenderer.hpp index 2c2a87f..17fe430 100644 --- a/subttxrend-ttxt/src/GfxRenderer.hpp +++ b/subttxrend-ttxt/src/GfxRenderer.hpp @@ -255,19 +255,19 @@ class GfxRenderer : private gfx::KeyEventListener virtual void onKeyEvent(const gfx::KeyEvent& event) override; /** Number of initializations. */ - int m_initCount; + int m_initCount{}; /** Current client. */ - GfxRendererClient* m_currentClient; + GfxRendererClient* m_currentClient{}; /** GFX elements - window. */ - gfx::Window* m_gfxWindow; + gfx::Window* m_gfxWindow{}; /** Current background alpha. */ - std::uint8_t m_bgAlpha; + std::uint8_t m_bgAlpha{}; /** Current new page id (selected using DIGIT keys). */ - std::uint16_t m_newPageId; + std::uint16_t m_newPageId{}; /** Last renderer start (show) time. */ TimePoint m_startTime; @@ -276,13 +276,13 @@ class GfxRenderer : private gfx::KeyEventListener TimePoint m_lastDigitTime; /** Rendering paused. */ - bool m_paused; + bool m_paused{}; /** Transparent background mode (subtitles/news). */ - bool m_transparentMode; + bool m_transparentMode{}; /** Current zoom mode. */ - ZoomMode m_zoomMode; + ZoomMode m_zoomMode{ZoomMode::NONE}; /** Teletext CLUT. */ GfxTtxClut m_clut; diff --git a/subttxrend-webvtt/CMakeLists.txt b/subttxrend-webvtt/CMakeLists.txt index c62316c..d3a3a2a 100644 --- a/subttxrend-webvtt/CMakeLists.txt +++ b/subttxrend-webvtt/CMakeLists.txt @@ -35,6 +35,7 @@ ENDIF(CMAKE_COMPILER_IS_GNUCXX) # Packages to use # find_package(LibSubTtxRendCommon REQUIRED) +find_package(LibSubTtxRendGfx REQUIRED) # # Include directories @@ -42,6 +43,7 @@ find_package(LibSubTtxRendCommon REQUIRED) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/Parser/include) include_directories(${LIBSUBTTXRENDCOMMON_INCLUDE_DIRS}) +include_directories(${LIBSUBTTXRENDGFX_INCLUDE_DIRS}) # # Documentation @@ -86,6 +88,7 @@ set(SUBTTXREND_WEBVTT_PUBLIC_HEADERS set(SUBTTXREND_WEBVTT_SOURCES src/Factory.cpp src/WebvttEngineImpl.cpp + src/WebVTTConfig.cpp src/WebVTTRenderer.cpp src/LineBuilder.cpp src/Parser/WebVTTCue.cpp @@ -111,6 +114,7 @@ set_property(TARGET ${LIBRARY_NAME} PROPERTY SOVERSION 0) set_property(TARGET ${LIBRARY_NAME} PROPERTY PUBLIC_HEADER ${SUBTTXREND_WEBVTT_PUBLIC_HEADERS}) target_include_directories(${LIBRARY_NAME} PRIVATE ${WEBVTT_PRIVATE_HEADERS}) target_link_libraries(${LIBRARY_NAME} ${LIBSUBTTXRENDCOMMON_LIBRARIES}) +target_link_libraries(${LIBRARY_NAME} ${LIBSUBTTXRENDGFX_LIBRARIES}) set_target_properties(${LIBRARY_NAME} PROPERTIES COMPILE_FLAGS "${WEBVTT_DEFINES}") # @@ -137,7 +141,7 @@ find_package(PkgConfig REQUIRED) set(PACKAGE_NAME "${PROJECT_NAME}") set(PACKAGE_DESCRIPTION "WebVTT Subtitles Engine") set(PACKAGE_VERSION "0.0.0") -set(PACKAGE_REQUIRES "${LIBSUBWEBVTTRENDCOMMON_PACKAGE_NAME}") +set(PACKAGE_REQUIRES "${LIBSUBTTXRENDCOMMON_PACKAGE_NAME} ${LIBSUBTTXRENDGFX_PACKAGE_NAME}") set(PACKAGE_LIB_NAME "${LIBRARY_NAME}") if(PKG_CONFIG_FOUND) diff --git a/subttxrend-ttxt/cmake/modules/FindLibDvbGfxEngine.cmake b/subttxrend-webvtt/cmake/modules/FindLibSubTtxRendGfx.cmake similarity index 89% rename from subttxrend-ttxt/cmake/modules/FindLibDvbGfxEngine.cmake rename to subttxrend-webvtt/cmake/modules/FindLibSubTtxRendGfx.cmake index 94680a5..1243c58 100644 --- a/subttxrend-ttxt/cmake/modules/FindLibDvbGfxEngine.cmake +++ b/subttxrend-webvtt/cmake/modules/FindLibSubTtxRendGfx.cmake @@ -17,8 +17,8 @@ include(PkgConfigHelper) -pkgconfig_resolve(LibDvbGfxEngine - dvb-graphics-engine - vlGraphics.h - dvbgraphicsengine +pkgconfig_resolve(LibSubTtxRendGfx + subttxrend-gfx + subttxrend/gfx/ColorArgb.hpp + subttxrend-gfx ) diff --git a/subttxrend-webvtt/src/LineBuilder.cpp b/subttxrend-webvtt/src/LineBuilder.cpp index 6fa0013..dbaa060 100644 --- a/subttxrend-webvtt/src/LineBuilder.cpp +++ b/subttxrend-webvtt/src/LineBuilder.cpp @@ -262,7 +262,7 @@ static void replaceEscapedCharacters(std::string &editString) { * @param style_list * @return std::string */ -std::string searchTag(const std::string& search, std::vector& results, std::list styleList) { +std::string searchTag(const std::string& search, std::vector& results, std::list& styleList) { std::string prefix, suffix, tag; std::list localList; Result result; @@ -576,7 +576,6 @@ std::list LineBuilder::getRegionLines(const CueSharedList &cueList, const try { const auto regionWidthVwH = region.width_vw_h; - const auto regionWidthPx = m_converter.vwToWidthPixels(regionWidthVwH); const auto linesVhH = m_converter.lineHeightVh() * region.lines; const auto linesPx = m_converter.vhToHeightPixels(linesVhH); const auto left = region.viewport_anchor.x - (region.region_anchor.x * (regionWidthVwH / 10000.0)); @@ -669,10 +668,8 @@ std::list LineBuilder::getOutputLines(const CueSharedList &cueList) { const auto snapToLines = cueBox.snapToLines; const auto align = cueBox.cueTextAlign; const auto lineAlign = cueBox.cueLineAlign; - const auto lineHeight = m_converter.lineHeightPixels(); const auto viewportHeight = m_converter.height(); const auto region_width_px = m_converter.vwToWidthPixels(cueBox.computedSizeVwH); - const auto horizPadding = m_converter.horizontalPadding(); auto boxes = buildLines(cue->lines(), region_width_px); @@ -737,6 +734,8 @@ std::list LineBuilder::getOutputLines(const CueSharedList &cueList) { linePositionsBeforeAdjustment(boxes, align, ComputedLine_px, ComputedPosition_px, true); switch(lineAlign) { + case WebVTTCue::LineAlignType::kStart: + break; case WebVTTCue::LineAlignType::kCenter: for (auto &cueLine : boxes) { cueLine.lineRectangle.m_y -= total_boxes_height / 2; diff --git a/subttxrend-webvtt/src/Parser/WebVTTDocument.cpp b/subttxrend-webvtt/src/Parser/WebVTTDocument.cpp index 4f8273b..f02eb04 100644 --- a/subttxrend-webvtt/src/Parser/WebVTTDocument.cpp +++ b/subttxrend-webvtt/src/Parser/WebVTTDocument.cpp @@ -278,7 +278,7 @@ void WebVTTDocument::checkValidWebVTTHeader(std::istream& ifile) { } if (line.substr(0, 6) != "WEBVTT" || - line.length() > 6 && !std::isspace(line[6])) { + (line.length() > 6 && !std::isspace(line[6])) ) { throw InvalidCueException("Bad WEBVTT header"); } } diff --git a/subttxrend-webvtt/src/Parser/include/WebVTTStyle.hpp b/subttxrend-webvtt/src/Parser/include/WebVTTStyle.hpp index 9031673..56006b6 100644 --- a/subttxrend-webvtt/src/Parser/include/WebVTTStyle.hpp +++ b/subttxrend-webvtt/src/Parser/include/WebVTTStyle.hpp @@ -20,6 +20,7 @@ #include #include +#include #include diff --git a/subttxrend-webvtt/src/WebVTTConfig.cpp b/subttxrend-webvtt/src/WebVTTConfig.cpp new file mode 100644 index 0000000..1468543 --- /dev/null +++ b/subttxrend-webvtt/src/WebVTTConfig.cpp @@ -0,0 +1,45 @@ +/***************************************************************************** +* If not stated otherwise in this file or this component's LICENSE file the +* following copyright and licenses apply: +* +* Copyright 2021 Liberty Global Service B.V. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*****************************************************************************/ +#include +//#include + +namespace subttxrend { +namespace webvttengine { +common::Logger g_logger_config("WebvttEngine", "WebVTTConfig"); + +double getConfigFractional(const common::ConfigProvider* configProvider, const std::string &key, int defaultValue, int fraction) +{ + const std::string defString = std::to_string((double)defaultValue / (double)fraction); + const std::string confString = configProvider->get(key, defString); + + try { + double confDouble = std::stod(confString); + int converted = std::round(fraction * confDouble); + g_logger_config.osdebug(__LOGGER_FUNC__, " ", key, "=", confString, " :", converted); + + return converted; + } catch (const std::exception &e) { + g_logger_config.oswarning(__LOGGER_FUNC__, " - Can't convert ", confString, " to double"); + } + + return defaultValue; +} + +} +} diff --git a/subttxrend-webvtt/src/WebvttEngineImpl.cpp b/subttxrend-webvtt/src/WebvttEngineImpl.cpp index 2215700..24d27a6 100644 --- a/subttxrend-webvtt/src/WebvttEngineImpl.cpp +++ b/subttxrend-webvtt/src/WebvttEngineImpl.cpp @@ -361,7 +361,7 @@ std::chrono::milliseconds WebvttEngineImpl::getWaitTime() const auto anythingToDraw = !m_timeline.empty(); auto anythingToHide = !m_shownDocuments.empty(); - if ((anythingToDraw || anythingToHide) && !m_paused) { + if ((anythingToDraw || anythingToHide) && (m_lastMediatimeMs != -1) && (!m_paused)) { // TimePoint const currentMediaTime = getCurrentMediatime(); // if (anythingToDraw) { diff --git a/subttxrend-webvtt/src/include/WebVTTConfig.hpp b/subttxrend-webvtt/src/include/WebVTTConfig.hpp index 59a5f97..46f00bb 100644 --- a/subttxrend-webvtt/src/include/WebVTTConfig.hpp +++ b/subttxrend-webvtt/src/include/WebVTTConfig.hpp @@ -29,26 +29,9 @@ namespace subttxrend { namespace webvttengine { -namespace { -common::Logger g_logger_config("WebvttEngine", "WebVTTConfig"); +extern common::Logger g_logger_config; -double getConfigFractional(const common::ConfigProvider* configProvider, const std::string &key, int defaultValue, int fraction) { - const std::string defString = std::to_string((double)defaultValue / (double)fraction); - const std::string confString = configProvider->get(key, defString); - - try { - double confDouble = std::stod(confString); - int converted = std::round(fraction * confDouble); - g_logger_config.osdebug(__LOGGER_FUNC__, " ", key, "=", confString, " :", converted); - - return converted; - } catch (const std::exception &e) { - g_logger_config.oswarning(__LOGGER_FUNC__, " - Can't convert ", confString, " to double"); - } - - return defaultValue; -} -} +double getConfigFractional(const common::ConfigProvider* configProvider, const std::string &key, int defaultValue, int fraction); struct WebVTTConfig { void init(const common::ConfigProvider* configProvider) { diff --git a/subttxrend-webvtt/src/include/WebVTTExceptions.hpp b/subttxrend-webvtt/src/include/WebVTTExceptions.hpp index d3a5093..ccb3133 100644 --- a/subttxrend-webvtt/src/include/WebVTTExceptions.hpp +++ b/subttxrend-webvtt/src/include/WebVTTExceptions.hpp @@ -21,6 +21,12 @@ #include #include +#ifdef __APPLE__ +#undef _GLIBCXX_USE_NOEXCEPT +#define _GLIBCXX_USE_NOEXCEPT _NOEXCEPT +#endif + + class WebVTTException : public std::exception { diff --git a/subttxrend-webvtt/test/WebVTTDocument_test.cpp b/subttxrend-webvtt/test/WebVTTDocument_test.cpp index bb15df3..36ef920 100644 --- a/subttxrend-webvtt/test/WebVTTDocument_test.cpp +++ b/subttxrend-webvtt/test/WebVTTDocument_test.cpp @@ -129,9 +129,9 @@ line:75% CPPUNIT_ASSERT_NO_THROW(std::tie(list, std::ignore) = doc.parseCueList(time_offset)); CPPUNIT_ASSERT_EQUAL((std::size_t)1, list.size()); auto list_iter = list.begin(); - //89s + 10s PTS + 5s LOCAL offset - CPPUNIT_ASSERT(TimePoint(104000) == (*list_iter)->startTime()); - CPPUNIT_ASSERT(TimePoint(106000) == (*list_iter)->endTime()); + //89s + 10s PTS - 5s LOCAL offset + CPPUNIT_ASSERT(TimePoint(94000) == (*list_iter)->startTime()); + CPPUNIT_ASSERT(TimePoint(96000) == (*list_iter)->endTime()); } void testRegion() @@ -475,10 +475,10 @@ Special Characters { WebVTTDocumentTestFixture parser; std::map goodlines = { - {"X-TIMESTAMP-MAP=MPEGTS:900000,LOCAL:02:00:05", (std::uint64_t)7215000}, + {"X-TIMESTAMP-MAP=MPEGTS:900000,LOCAL:02:00:05", (std::uint64_t)10000}, {"X-TIMESTAMP-MAP=MPEGTS:900000", (std::uint64_t)10000}, - {"X-TIMESTAMP-MAP=LOCAL:02:00:05", (std::uint64_t)7205000}, - {"X-TIMESTAMP-MAP=LOCAL:02:00:05.123", (std::uint64_t)7205123} + {"X-TIMESTAMP-MAP=LOCAL:02:00:05", (std::uint64_t)0}, + {"X-TIMESTAMP-MAP=LOCAL:02:00:05.123", (std::uint64_t)0} }; std::list badlines = { "X-TIMESTAMP-MAPMPEGTS:900000LOCAL:02:00:05", @@ -490,7 +490,8 @@ Special Characters std::uint64_t ptsOffset = 0; auto& str = good.first; auto& expectedPts = good.second; - CPPUNIT_ASSERT_MESSAGE(str, ptsOffset = parser.parseXTimestampMap(str)); + + ptsOffset = parser.parseXTimestampMap(str); CPPUNIT_ASSERT_EQUAL_MESSAGE(str, (std::uint64_t)expectedPts, ptsOffset); } diff --git a/test/Readme.md b/test/Readme.md index f4d2af7..b5dc25a 100644 --- a/test/Readme.md +++ b/test/Readme.md @@ -2,7 +2,7 @@ Readme.md # How to run subtec on ubuntu -To install required packages for subtec to build successsfully on ubuntu, run below script from ***subtec-app/test/*** folder for first time. +To install required packages for subtec to build successfully on ubuntu, run below script from ***subtec-app/test/*** folder for first time. bash install_subtec_dependencies.sh diff --git a/test/TEST_1018_webvtt_Attrib_WinOpacity/TEST_1018_webvtt_Attrib_WinOpacity.cpp b/test/TEST_1018_webvtt_Attrib_WinOpacity/TEST_1018_webvtt_Attrib_WinOpacity.cpp index fb590bc..04ac659 100644 --- a/test/TEST_1018_webvtt_Attrib_WinOpacity/TEST_1018_webvtt_Attrib_WinOpacity.cpp +++ b/test/TEST_1018_webvtt_Attrib_WinOpacity/TEST_1018_webvtt_Attrib_WinOpacity.cpp @@ -48,7 +48,7 @@ void sendCCAttributeWebvtt(Opacity color, std::string filename, AttribPosInArray int main(int argc, char *argv[]) { - debug_log("Test webvtt attributes background opacity started"); + debug_log("Test webvtt attributes window opacity started"); bool ret = false; ret = channel->InitComms(); diff --git a/test/TEST_1019_webvtt_Attrib_fontSize/TEST_1019_webvtt_Attrib_fontSize.py b/test/TEST_1019_webvtt_Attrib_fontSize/TEST_1019_webvtt_Attrib_fontSize.py index 8083d1f..c611f1b 100644 --- a/test/TEST_1019_webvtt_Attrib_fontSize/TEST_1019_webvtt_Attrib_fontSize.py +++ b/test/TEST_1019_webvtt_Attrib_fontSize/TEST_1019_webvtt_Attrib_fontSize.py @@ -51,28 +51,37 @@ def test_1019(): 'displaying: [00:00:00.000-00:00:05.000]: Sample text to test FONT_SIZE_EMBEDDED'] assert monitor_log_for_sequence(subtec_copy_path, sequence), "Logs for test_1019 missing" + calculated_font_size = calculate_webvtt_font_size(subtec_copy_path, 315) + log_string = f"getFont - get fontFamily:Cinecav Mono size:{calculated_font_size}" sequence = [ 'new value for attibId[0x20] = 0', 'displaying: [00:00:00.000-00:00:05.000]: Sample text to test FONT_SIZE_SMALL', - 'getFont - get fontFamily:Cinecav Mono size:19'] + log_string] assert monitor_log_for_sequence(subtec_copy_path, sequence), "Logs for test_1019 missing" - + + calculated_font_size = calculate_webvtt_font_size(subtec_copy_path, 463) + log_string = f"getFont - get fontFamily:Cinecav Mono size:{calculated_font_size}" sequence = [ 'new value for attibId[0x20] = 1', 'displaying: [00:00:00.000-00:00:05.000]: Sample text to test FONT_SIZE_STANDARD', - 'getFont - get fontFamily:Cinecav Mono size:28'] + log_string] assert monitor_log_for_sequence(subtec_copy_path, sequence), "Logs for test_1019 missing" - + + calculated_font_size = calculate_webvtt_font_size(subtec_copy_path, 741) + log_string = f"getFont - get fontFamily:Cinecav Mono size:{calculated_font_size}" sequence = [ 'new value for attibId[0x20] = 2', 'displaying: [00:00:00.000-00:00:05.000]: Sample text to test FONT_SIZE_LARGE', - 'getFont - get fontFamily:Cinecav Mono size:45'] + log_string] assert monitor_log_for_sequence(subtec_copy_path, sequence), "Logs for test_1019 missing" - + + calculated_font_size = calculate_webvtt_font_size(subtec_copy_path, 926) + log_string = f"getFont - get fontFamily:Cinecav Mono size:{calculated_font_size}" sequence = [ 'new value for attibId[0x20] = 3', 'displaying: [00:00:00.000-00:00:05.000]: Sample text to test FONT_SIZE_EXTRALARGE', - 'getFont - get fontFamily:Cinecav Mono size:56'] + log_string] assert monitor_log_for_sequence(subtec_copy_path, sequence), "Logs for test_1019 missing" - print("test_1019".ljust(50), "Test Passed") \ No newline at end of file + print("test_1019".ljust(50), "Test Passed") + diff --git a/test/TEST_1020_webvtt_Attrib_fontOpacity/TEST_1020_webvtt_Attrib_fontOpacity.cpp b/test/TEST_1020_webvtt_Attrib_fontOpacity/TEST_1020_webvtt_Attrib_fontOpacity.cpp index eed630d..e25b227 100644 --- a/test/TEST_1020_webvtt_Attrib_fontOpacity/TEST_1020_webvtt_Attrib_fontOpacity.cpp +++ b/test/TEST_1020_webvtt_Attrib_fontOpacity/TEST_1020_webvtt_Attrib_fontOpacity.cpp @@ -43,6 +43,9 @@ void sendccattribute(Opacity value, std::string filename, AttribPosInArray attri attributesMask |= (1 << FONT_SIZE_ARR_POSITION); attributesValues[FONT_SIZE_ARR_POSITION] = FONT_SIZE_EXTRALARGE; + attributesMask |= (1 << BACKGROUND_OPACITY_ARR_POSITION); + attributesValues[BACKGROUND_OPACITY_ARR_POSITION] = OPACITY_TRANSLUCENT; + if (attributesMask) { channel->SendCCSetAttributePacket(ccType, attributesMask, attributesValues); @@ -63,6 +66,7 @@ int main(int argc, char *argv[]) channel->SendSelectionPacket(1920, 1080); channel->SendUnmutePacket(); + resetAllWebVttAttributes(channel); /* TODO : Resetting attributes here causes test case failure as font opacity seems to be dependent on background opacity when running on ubuntu, so setting background opacity to diff --git a/test/TEST_1039_CC_608_attrib_win_bgColor/TEST_1039_CC_608_attrib_win_bgColor.py b/test/TEST_1039_CC_608_attrib_win_bgColor/TEST_1039_CC_608_attrib_win_bgColor.py index 2f8c39b..dfb2a8b 100644 --- a/test/TEST_1039_CC_608_attrib_win_bgColor/TEST_1039_CC_608_attrib_win_bgColor.py +++ b/test/TEST_1039_CC_608_attrib_win_bgColor/TEST_1039_CC_608_attrib_win_bgColor.py @@ -39,7 +39,7 @@ def test_1039(): run_test_prereq(script_dir) # wait for end of test - wait_for_log_string(subtec_log_path, "[ClosedCaptions::TextGfxDrawer] draw text:[swe: 00:00:45:00]") + wait_for_log_string(subtec_log_path, "[ClosedCaptions::TextGfxDrawer] draw text:[swe: 00:00:38:00]") copy_log_file(subtec_log_path, subtec_copy_path) sequence = [ diff --git a/test/TEST_1044_CC_708_ResetPacket/TEST_1044_CC_708_ResetPacket.py b/test/TEST_1044_CC_708_ResetPacket/TEST_1044_CC_708_ResetPacket.py index e2b7acc..2d09859 100644 --- a/test/TEST_1044_CC_708_ResetPacket/TEST_1044_CC_708_ResetPacket.py +++ b/test/TEST_1044_CC_708_ResetPacket/TEST_1044_CC_708_ResetPacket.py @@ -38,7 +38,7 @@ def test_1044(): run_test_prereq(script_dir) # wait for end of test - wait_for_log_string(subtec_log_path, 'processLoop no active controller, clearing the data queue') + wait_for_log_string(subtec_log_path, 'addBuffer no active controller, skipping the packet') copy_log_file(subtec_log_path, subtec_copy_path) sequence = [ @@ -59,9 +59,8 @@ def test_1044(): 'validate - RESET ALL received - resetting stream state', 'onPacketReceived packet=type:RESET_ALL', 'reset All', - 'addBuffer no active controller, skipping the packet', - 'processLoop no active controller, clearing the data queue' + 'addBuffer no active controller, skipping the packet' ] assert monitor_log_for_sequence(subtec_copy_path, sequence), "Logs for test_1044 missing" - print("test_1044".ljust(50), "Test Passed") \ No newline at end of file + print("test_1044".ljust(50), "Test Passed") diff --git a/test/TEST_1045_CC_708_Unmute_woActive/TEST_1045_CC_708_Unmute_woActive.cpp b/test/TEST_1045_CC_708_Unmute_woActive/TEST_1045_CC_708_Unmute_woActive.cpp index 87360ea..cb3b1cd 100644 --- a/test/TEST_1045_CC_708_Unmute_woActive/TEST_1045_CC_708_Unmute_woActive.cpp +++ b/test/TEST_1045_CC_708_Unmute_woActive/TEST_1045_CC_708_Unmute_woActive.cpp @@ -24,7 +24,7 @@ int main(int argc, char *argv[]) { - debug_log("Test CC 708 unmute packet wiithout active type packett started"); + debug_log("Test CC 708 unmute packet wiithout active type packet started"); bool ret = false; ClosedCaptionsChannel *channel = new ClosedCaptionsChannel(); diff --git a/test/TEST_1055_CC_708_attrib_edgeColor/TEST_1055_CC_708_attrib_edgeColor.cpp b/test/TEST_1055_CC_708_attrib_edgeColor/TEST_1055_CC_708_attrib_edgeColor.cpp index 6749772..a002aff 100644 --- a/test/TEST_1055_CC_708_attrib_edgeColor/TEST_1055_CC_708_attrib_edgeColor.cpp +++ b/test/TEST_1055_CC_708_attrib_edgeColor/TEST_1055_CC_708_attrib_edgeColor.cpp @@ -66,7 +66,7 @@ int main(int argc, char *argv[]) unsigned dataLength; long long localPts; while (offset + 24 <= hexData.length()) { - if (counter == 180) { + if (counter == 190) { break; } if (counter == 0) { diff --git a/test/TEST_1061_ttml_Pause_resumePacket/TEST_1061_ttml_Pause_resumePacket.py b/test/TEST_1061_ttml_Pause_resumePacket/TEST_1061_ttml_Pause_resumePacket.py index f19813f..49b7d53 100644 --- a/test/TEST_1061_ttml_Pause_resumePacket/TEST_1061_ttml_Pause_resumePacket.py +++ b/test/TEST_1061_ttml_Pause_resumePacket/TEST_1061_ttml_Pause_resumePacket.py @@ -17,7 +17,7 @@ # limitations under the License. # import os - +import time from utils import * import pytest @@ -38,7 +38,8 @@ def test_1061(): run_test_prereq(script_dir) # wait for end of test - wait_for_log_string(subtec_log_path, "processLoop no active controller, clearing the data queue") + wait_for_log_string(subtec_log_path, "resume received") + time.sleep(2) copy_log_file(subtec_log_path, subtec_copy_path) log_sequence = [ diff --git a/test/TEST_1061_ttml_Pause_resumePacket/input_files/sample.ttml b/test/TEST_1061_ttml_Pause_resumePacket/input_files/sample.ttml index 4f72627..9b756da 100644 --- a/test/TEST_1061_ttml_Pause_resumePacket/input_files/sample.ttml +++ b/test/TEST_1061_ttml_Pause_resumePacket/input_files/sample.ttml @@ -12,8 +12,8 @@
-

This text is added to test reset
packet - 0-5 seconds

-

This text is added to test reset
packet - 5-10 seconds

+

This text is added to test pause-resume
packet - 0-5 seconds

+

This text is added to test pause-resume
packet - 5-10 seconds

diff --git a/test/TEST_1063_ttml_DataPacket/ReadMe.md b/test/TEST_1063_ttml_DataPacket/ReadMe.md index e9e84eb..78945de 100644 --- a/test/TEST_1063_ttml_DataPacket/ReadMe.md +++ b/test/TEST_1063_ttml_DataPacket/ReadMe.md @@ -1,7 +1,7 @@ # Subtec ttml data L2 test This python3 L2 test verifies ttml data packet functionality, verifies the -data in webvtt file is correctly received. +data in ttml file is correctly received. ## Pre-requisites to L2 tests: @@ -14,4 +14,4 @@ sample.ttml - sample input ttml file to be placed in input_files folder. From the *subtec-app/test/* folder run: -./run_l2_tests.py -i 1063 \ No newline at end of file +./run_l2_tests.py -i 1063 diff --git a/test/TEST_1070_ttml_Attrib_textAlign/TEST_1070_ttml_Attrib_textAlign.py b/test/TEST_1070_ttml_Attrib_textAlign/TEST_1070_ttml_Attrib_textAlign.py index 61c0987..7f3c5f8 100644 --- a/test/TEST_1070_ttml_Attrib_textAlign/TEST_1070_ttml_Attrib_textAlign.py +++ b/test/TEST_1070_ttml_Attrib_textAlign/TEST_1070_ttml_Attrib_textAlign.py @@ -61,7 +61,7 @@ def test_1070(): 'parseAttributes adding tts:fontFamily=proportionalSansSerif', 'displaying: [00:00:00.000-00:00:05.000]: [ ] [Test text align] [ ] [ ] [left] [ ] ', 'alignPenX textAlign=left', - 'drawChunk starting pen position: 10 0 ('] + 'drawChunk starting pen position: 23 0 ('] assert monitor_log_for_sequence(subtec_copy_path, sequence), "Logs for test_1070 missing" sequence = [ @@ -79,7 +79,7 @@ def test_1070(): 'parseAttributes adding tts:fontFamily=proportionalSansSerif', 'displaying: [00:00:00.000-00:00:05.000]: [ ] [Test text align] [ ] [ ] [right] [ ] ', 'alignPenX textAlign=right', - 'drawChunk starting pen position: 1139 0 ('] + 'drawChunk starting pen position: 1126 0 ('] assert monitor_log_for_sequence(subtec_copy_path, sequence), "Logs for test_1070 missing" sequence = [ @@ -100,4 +100,4 @@ def test_1070(): 'drawChunk starting pen position: 574 0 ('] assert monitor_log_for_sequence(subtec_copy_path, sequence), "Logs for test_1070 missing" - print("test_1070".ljust(50), "Test Passed") \ No newline at end of file + print("test_1070".ljust(50), "Test Passed") diff --git a/test/TEST_1072_ttml_Attrib_displayAlign/TEST_1072_ttml_Attrib_displayAlign.py b/test/TEST_1072_ttml_Attrib_displayAlign/TEST_1072_ttml_Attrib_displayAlign.py index 4477edf..44392eb 100644 --- a/test/TEST_1072_ttml_Attrib_displayAlign/TEST_1072_ttml_Attrib_displayAlign.py +++ b/test/TEST_1072_ttml_Attrib_displayAlign/TEST_1072_ttml_Attrib_displayAlign.py @@ -63,7 +63,7 @@ def test_1072(): 'parseAttributes adding tts:fontFamily=proportionalSansSerif', 'displaying: [00:00:00.000-00:00:05.000]: [ ] [Test display align] [ ] [ ] [before] [ ]', 'alignPenY displayAlign=before textHeight=', - 'drawChunk starting pen position: 10 0 ('] + 'drawChunk starting pen position: 23 0 ('] assert monitor_log_for_sequence(subtec_copy_path, sequence), "Logs for test_1072 missing" sequence = [ @@ -83,7 +83,7 @@ def test_1072(): 'parseAttributes adding tts:fontFamily=proportionalSansSerif', 'displaying: [00:00:00.000-00:00:05.000]: [ ] [Test display align] [ ] [ ] [after] [ ] ', 'alignPenY displayAlign=after textHeight=', - 'drawChunk starting pen position: 10 39 ('] + 'drawChunk starting pen position: 23 40 ('] assert monitor_log_for_sequence(subtec_copy_path, sequence), "Logs for test_1072 missing" sequence = [ @@ -103,7 +103,7 @@ def test_1072(): 'parseAttributes adding tts:fontFamily=proportionalSansSerif', 'displaying: [00:00:00.000-00:00:05.000]: [ ] [Test display align] [ ] [ ] [center] [ ] ', 'alignPenY displayAlign=center textHeight=', - 'drawChunk starting pen position: 10 19 ('] + 'drawChunk starting pen position: 23 20 ('] assert monitor_log_for_sequence(subtec_copy_path, sequence), "Logs for test_1072 missing" - print("test_1072".ljust(50), "Test Passed") \ No newline at end of file + print("test_1072".ljust(50), "Test Passed") diff --git a/test/TEST_1074_ttml_Attrib_fontSize/TEST_1074_ttml_Attrib_fontSize.cpp b/test/TEST_1074_ttml_Attrib_fontSize/TEST_1074_ttml_Attrib_fontSize.cpp index e60dcc9..90a2bd1 100644 --- a/test/TEST_1074_ttml_Attrib_fontSize/TEST_1074_ttml_Attrib_fontSize.cpp +++ b/test/TEST_1074_ttml_Attrib_fontSize/TEST_1074_ttml_Attrib_fontSize.cpp @@ -72,6 +72,9 @@ int main(int argc, char *argv[]) read_file_send_data_packet_ttml("input_files/test_sample_fontSize_99percent.ttml", channel); sleep (6); + + read_file_send_data_packet_ttml("input_files/test_sample_fontSize_115percent.ttml", channel); + sleep (6); } else { diff --git a/test/TEST_1074_ttml_Attrib_fontSize/TEST_1074_ttml_Attrib_fontSize.py b/test/TEST_1074_ttml_Attrib_fontSize/TEST_1074_ttml_Attrib_fontSize.py index c9b7244..07c05f4 100644 --- a/test/TEST_1074_ttml_Attrib_fontSize/TEST_1074_ttml_Attrib_fontSize.py +++ b/test/TEST_1074_ttml_Attrib_fontSize/TEST_1074_ttml_Attrib_fontSize.py @@ -39,7 +39,7 @@ def test_1074(): run_test_prereq(script_dir) # wait for end of test - wait_for_log_string(subtec_log_path, "displaying: [00:00:00.000-00:00:05.000]: [ ] [Test font size] [ ] [ ] [99%] [ ] ", 20) + wait_for_log_string(subtec_log_path, "displaying: [00:00:00.000-00:00:05.000]: [ ] [Test font size] [ ] [ ] [115%] [ ] ", 20) copy_log_file(subtec_log_path, subtec_copy_path) sequence = [ @@ -52,7 +52,7 @@ def test_1074(): 'parseAttributes adding xml:id=transparentspace', 'parseAttributes adding tts:fontSize=0.25c', 'displaying: [00:00:00.000-00:00:05.000]: [ ] [Test font size] [ ] [ ] [0.25c] [ ]', - 'sizeToPixels size 18 (from 0.25c)', + 'sizeToPixels size 18 (from 25)', 'drawLine font size: 18'] assert monitor_log_for_sequence(subtec_copy_path, sequence), "Logs for test_1074 missing" @@ -62,7 +62,7 @@ def test_1074(): 'parseAttributes adding xml:id=transparentspace', 'parseAttributes adding tts:fontSize=0.5c', 'displaying: [00:00:00.000-00:00:05.000]: [ ] [Test font size] [ ] [ ] [0.5c] [ ] ', - 'sizeToPixels size 36 (from 0.5c)', + 'sizeToPixels size 36 (from 50)', 'drawLine font size: 36'] assert monitor_log_for_sequence(subtec_copy_path, sequence), "Logs for test_1074 missing" @@ -72,7 +72,7 @@ def test_1074(): 'parseAttributes adding xml:id=transparentspace', 'parseAttributes adding tts:fontSize=0.75c', 'displaying: [00:00:00.000-00:00:05.000]: [ ] [Test font size] [ ] [ ] [0.75c] [ ] ', - 'sizeToPixels size 54 (from 0.75c)', + 'sizeToPixels size 54 (from 75)', 'drawLine font size: 54'] assert monitor_log_for_sequence(subtec_copy_path, sequence), "Logs for test_1074 missing" @@ -82,7 +82,7 @@ def test_1074(): 'parseAttributes adding xml:id=transparentspace', 'parseAttributes adding tts:fontSize=1.2c', 'displaying: [00:00:00.000-00:00:05.000]: [ ] [Test font size] [ ] [ ] [1.2c] [ ] ', - 'sizeToPixels size 86 (from 1.2c)', + 'sizeToPixels size 86 (from 120)', 'drawLine font size: 86'] assert monitor_log_for_sequence(subtec_copy_path, sequence), "Logs for test_1074 missing" @@ -92,7 +92,7 @@ def test_1074(): 'parseAttributes adding xml:id=transparentspace', 'parseAttributes adding tts:fontSize=20px', 'displaying: [00:00:00.000-00:00:05.000]: [ ] [Test font size] [ ] [ ] [20px] [ ] ', - 'sizeToPixels size 20 (from 20px)', + 'sizeToPixels size 20 (from 20)', 'drawLine font size: 20'] assert monitor_log_for_sequence(subtec_copy_path, sequence), "Logs for test_1074 missing" @@ -102,7 +102,7 @@ def test_1074(): 'parseAttributes adding xml:id=transparentspace', 'parseAttributes adding tts:fontSize=36px', 'displaying: [00:00:00.000-00:00:05.000]: [ ] [Test font size] [ ] [ ] [36px] [ ] ', - 'sizeToPixels size 36 (from 36px)', + 'sizeToPixels size 36 (from 36)', 'drawLine font size: 36'] assert monitor_log_for_sequence(subtec_copy_path, sequence), "Logs for test_1074 missing" @@ -112,7 +112,7 @@ def test_1074(): 'parseAttributes adding xml:id=transparentspace', 'parseAttributes adding tts:fontSize=54px', 'displaying: [00:00:00.000-00:00:05.000]: [ ] [Test font size] [ ] [ ] [54px] [ ] ', - 'sizeToPixels size 54 (from 54px)', + 'sizeToPixels size 54 (from 54)', 'drawLine font size: 54'] assert monitor_log_for_sequence(subtec_copy_path, sequence), "Logs for test_1074 missing" @@ -122,7 +122,7 @@ def test_1074(): 'parseAttributes adding xml:id=transparentspace', 'parseAttributes adding tts:fontSize=75px', 'displaying: [00:00:00.000-00:00:05.000]: [ ] [Test font size] [ ] [ ] [75px] [ ] ', - 'sizeToPixels size 75 (from 75px)', + 'sizeToPixels size 75 (from 75)', 'drawLine font size: 75'] assert monitor_log_for_sequence(subtec_copy_path, sequence), "Logs for test_1074 missing" @@ -132,7 +132,7 @@ def test_1074(): 'parseAttributes adding xml:id=transparentspace', 'parseAttributes adding tts:fontSize=25%', 'displaying: [00:00:00.000-00:00:05.000]: [ ] [Test font size] [ ] [ ] [25%] [ ] ', - 'sizeToPixels size 18 (from 25%)', + 'sizeToPixels size 18 (from 2500)', 'drawLine font size: 18'] assert monitor_log_for_sequence(subtec_copy_path, sequence), "Logs for test_1074 missing" @@ -142,7 +142,7 @@ def test_1074(): 'parseAttributes adding xml:id=transparentspace', 'parseAttributes adding tts:fontSize=50%', 'displaying: [00:00:00.000-00:00:05.000]: [ ] [Test font size] [ ] [ ] [50%] [ ] ', - 'sizeToPixels size 36 (from 50%)', + 'sizeToPixels size 36 (from 5000)', 'drawLine font size: 36'] assert monitor_log_for_sequence(subtec_copy_path, sequence), "Logs for test_1074 missing" @@ -152,18 +152,28 @@ def test_1074(): 'parseAttributes adding xml:id=transparentspace', 'parseAttributes adding tts:fontSize=75%', 'displaying: [00:00:00.000-00:00:05.000]: [ ] [Test font size] [ ] [ ] [75%] [ ] ', - 'sizeToPixels size 54 (from 75%)', + 'sizeToPixels size 54 (from 7500)', 'drawLine font size: 54'] assert monitor_log_for_sequence(subtec_copy_path, sequence), "Logs for test_1074 missing" - sequence = [ + sequence = [ 'parseAttributes adding xml:id=s0', 'parseAttributes adding tts:fontSize=99%', 'parseAttributes adding xml:id=transparentspace', 'parseAttributes adding tts:fontSize=99%', 'displaying: [00:00:00.000-00:00:05.000]: [ ] [Test font size] [ ] [ ] [99%] [ ] ', - 'sizeToPixels size 71 (from 99%)', + 'sizeToPixels size 71 (from 9900)', 'drawLine font size: 71'] assert monitor_log_for_sequence(subtec_copy_path, sequence), "Logs for test_1074 missing" - print("test_1074".ljust(50), "Test Passed") \ No newline at end of file + sequence = [ + 'parseAttributes adding xml:id=s0', + 'parseAttributes adding tts:fontSize=115%', + 'parseAttributes adding xml:id=transparentspace', + 'parseAttributes adding tts:fontSize=115%', + 'displaying: [00:00:00.000-00:00:05.000]: [ ] [Test font size] [ ] [ ] [115%] [ ] ', + 'sizeToPixels size 83 (from 11500)', + 'drawLine font size: 83'] + assert monitor_log_for_sequence(subtec_copy_path, sequence), "Logs for test_1074 missing" + + print("test_1074".ljust(50), "Test Passed") diff --git a/test/TEST_1074_ttml_Attrib_fontSize/input_files/test_sample_fontSize_115percent.ttml b/test/TEST_1074_ttml_Attrib_fontSize/input_files/test_sample_fontSize_115percent.ttml new file mode 100644 index 0000000..116e1ff --- /dev/null +++ b/test/TEST_1074_ttml_Attrib_fontSize/input_files/test_sample_fontSize_115percent.ttml @@ -0,0 +1,18 @@ + + + + +