diff --git a/.github/workflows/build-docker.yaml b/.github/workflows/build-docker.yaml index f70cccb113..99f831cde2 100644 --- a/.github/workflows/build-docker.yaml +++ b/.github/workflows/build-docker.yaml @@ -27,7 +27,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{ inputs.ref }} submodules: recursive diff --git a/.github/workflows/build-docs.yaml b/.github/workflows/build-docs.yaml index 33d4d02406..24a8a747c5 100644 --- a/.github/workflows/build-docs.yaml +++ b/.github/workflows/build-docs.yaml @@ -40,7 +40,7 @@ jobs: breathe - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{ inputs.ref }} @@ -66,12 +66,12 @@ jobs: chmod -R +rX gh-pages/ - name: Upload docs artifacts - uses: actions/upload-pages-artifact@v2 + uses: actions/upload-pages-artifact@v3 with: path: gh-pages - name: Debug - uses: mxschmitt/action-tmate@v3.6 + uses: mxschmitt/action-tmate@v3.17 with: limit-access-to-actor: true if: failure() && inputs.debug diff --git a/.github/workflows/build-matrix.json b/.github/workflows/build-matrix.json index 71c87f1833..506e59b0ac 100644 --- a/.github/workflows/build-matrix.json +++ b/.github/workflows/build-matrix.json @@ -9,12 +9,21 @@ "generator": "Ninja" }, { - "os": "macos-latest", + "comment": "Explicit macOS version 13 is required for explicit x64 CPU.", + "os": "macos-13", "os_name": "osx", "target_arch": "x64", "exe_ext": "", "generator": "Ninja" }, + { + "comment": "Explicit macOS version 14 is required for explicit arm64 CPU.", + "os": "macos-14", + "os_name": "osx", + "target_arch": "arm64", + "exe_ext": "", + "generator": "Ninja" + }, { "os": "windows-latest", "os_name": "win", diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 5650fc27f2..91e1ba0991 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -44,7 +44,7 @@ jobs: INCLUDE: ${{ steps.configure.outputs.INCLUDE }} OS: ${{ steps.configure.outputs.OS }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: ${{ inputs.ref }} @@ -92,10 +92,11 @@ jobs: run: git config --global core.autocrlf false - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{ inputs.ref }} submodules: recursive + fetch-tags: true - name: Install Linux deps if: runner.os == 'Linux' @@ -108,7 +109,7 @@ jobs: kitware_key_path="/usr/share/keyrings/kitware-archive-keyring.gpg" kitware_sources_path="/etc/apt/sources.list.d/kitware.list" - wget -O - "$kitware_key_url" 2>/dev/null | gpg --dearmor - \ + curl -sL "$kitware_key_url" | gpg --dearmor - \ | sudo tee "$kitware_key_path" >/dev/null . /etc/lsb-release # Defines $DISTRIB_CODENAME (jammy, focal, etc) @@ -127,6 +128,28 @@ jobs: run: | brew install ninja + - name: Check Mac CPU architecture + if: runner.os == 'macOS' + # In case we get confused about GitHub's mac VM image labels, + # explicitly check that the CPU type matches our expectations. + run: | + if [[ "${{matrix.target_arch}}" == "arm64" ]]; then + CORRECT_LABEL="arm64" + else + CORRECT_LABEL="x86_64" + fi + + LABEL=$(uname -m) + echo "Hardware label: \"$LABEL\"" + + if [[ "$LABEL" != "$CORRECT_LABEL" ]]; then + echo "Wrong hardware label \"$LABEL\", expecting \"$CORRECT_LABEL\"." + echo "Full uname string: $(uname -a)" + echo "Full sysctl CPU info:" + sysctl machdep.cpu + exit 1 + fi + - name: Generate build files run: | mkdir -p build/ @@ -149,9 +172,32 @@ jobs: export PACKAGER_LOW_MEMORY_BUILD=yes fi + # Do fully static release builds on Linux. + BUILD_CONFIG="${{ matrix.build_type }}-${{ matrix.lib_type }}" + if [[ "${{ runner.os }}" == "Linux" && \ + "$BUILD_CONFIG" == "Release-static" ]]; then + # Enable build settings for fully-static. + FULLY_STATIC="ON" + + # Use a musl toolchain, since glibc static executables are not + # portable. + if [[ "${{matrix.target_arch}}" == "arm64" ]]; then + MUSL_ARCH="aarch64" + else + MUSL_ARCH="x86_64" + fi + curl -LO https://musl.cc/"$MUSL_ARCH"-linux-musl-native.tgz + tar xf "$MUSL_ARCH"-linux-musl-native.tgz + export CC=`pwd`/"$MUSL_ARCH"-linux-musl-native/bin/"$MUSL_ARCH"-linux-musl-gcc + export CXX=`pwd`/"$MUSL_ARCH"-linux-musl-native/bin/"$MUSL_ARCH"-linux-musl-g++ + else + FULLY_STATIC="OFF" + fi + cmake \ -DCMAKE_BUILD_TYPE="${{ matrix.build_type }}" \ -DBUILD_SHARED_LIBS="$BUILD_SHARED_LIBS" \ + -DFULLY_STATIC="$FULLY_STATIC" \ -S . \ -B build/ @@ -166,7 +212,7 @@ jobs: run: ctest -C "${{ matrix.build_type }}" -V --test-dir build/ - name: Publish Test Report - uses: mikepenz/action-junit-report@150e2f992e4fad1379da2056d1d1c279f520e058 + uses: mikepenz/action-junit-report@v4 if: ${{ always() }} with: report_paths: 'junit-reports/TEST-*.xml' @@ -179,7 +225,21 @@ jobs: exit 0 fi - # TODO: Check static executables? + # Check static executables + if [[ "${{ runner.os }}" == "Linux" ]]; then + echo "::group::Check static executables" + for exe in build/packager/{packager,mpd_generator}; do + # Capture information about the executables, but also let it be + # logged to stdout. + ldd "$exe" | tee static.log + # The phrase "statically linked" means we got it right. Fail if + # we don't find it. + if ! cat static.log | grep -q statically; then + exit 1 + fi + done + echo "::endgroup::" + fi echo "::group::Prepare artifacts folder" mkdir artifacts @@ -217,7 +277,7 @@ jobs: fi - name: Upload static release build artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: matrix.build_type == 'Release' && matrix.lib_type == 'static' with: name: artifacts-${{ matrix.os_name }}-${{ matrix.target_arch }} @@ -226,7 +286,7 @@ jobs: retention-days: 5 - name: Debug - uses: mxschmitt/action-tmate@v3.6 + uses: mxschmitt/action-tmate@v3.17 with: limit-access-to-actor: true if: failure() && inputs.debug diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index e8d1fe9712..1d43098ee2 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -27,7 +27,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{ inputs.ref }} # We must use 'fetch-depth: 2', or else the linter won't have another diff --git a/.github/workflows/publish-docker.yaml b/.github/workflows/publish-docker.yaml index 8d9c816f39..c7b01b9141 100644 --- a/.github/workflows/publish-docker.yaml +++ b/.github/workflows/publish-docker.yaml @@ -43,10 +43,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{ inputs.tag }} submodules: recursive + fetch-tags: true - name: Log in to Docker Hub uses: docker/login-action@v3 @@ -57,6 +58,11 @@ jobs: - name: Push to Docker Hub uses: docker/build-push-action@v5 with: + # Important: use actions/checkout source, which has the right tags! + # Without context: ., this action will try to fetch git source + # itself, and it will be unable to determine the correct version + # number. + context: . push: true tags: ${{ secrets.DOCKERHUB_PACKAGE_NAME }}:${{ inputs.tag }} @@ -64,5 +70,10 @@ jobs: if: ${{ inputs.latest }} uses: docker/build-push-action@v5 with: + # Important: use actions/checkout source, which has the right tags! + # Without context: ., this action will try to fetch git source + # itself, and it will be unable to determine the correct version + # number. + context: . push: true tags: ${{ secrets.DOCKERHUB_PACKAGE_NAME }}:latest diff --git a/.github/workflows/publish-docs.yaml b/.github/workflows/publish-docs.yaml index 7f1b7d8768..33c63d4b11 100644 --- a/.github/workflows/publish-docs.yaml +++ b/.github/workflows/publish-docs.yaml @@ -48,4 +48,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v2 + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/publish-npm.yaml b/.github/workflows/publish-npm.yaml index 97c7c54677..a8172c1797 100644 --- a/.github/workflows/publish-npm.yaml +++ b/.github/workflows/publish-npm.yaml @@ -41,10 +41,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{ inputs.tag }} - submodules: recursive - uses: actions/setup-node@v4 with: diff --git a/.github/workflows/release-please.yaml b/.github/workflows/release-please.yaml index ff7338031f..9c55857de7 100644 --- a/.github/workflows/release-please.yaml +++ b/.github/workflows/release-please.yaml @@ -13,27 +13,28 @@ on: - v[0-9]* jobs: + settings: + name: Settings + uses: ./.github/workflows/settings.yaml + release: runs-on: ubuntu-latest + needs: settings outputs: release_created: ${{ steps.release.outputs.release_created }} tag_name: ${{ steps.release.outputs.tag_name }} steps: # Create/update release PR - - uses: google-github-actions/release-please-action@v3 + - uses: google-github-actions/release-please-action@v4 id: release with: - # Required input to specify the release type. This is not really a - # go project, but go projects in release-please only update - # CHANGELOG.md and nothing else. This is what we want. - release-type: go # Make sure we create the PR against the correct branch. - default-branch: ${{ github.ref_name }} + target-branch: ${{ github.ref_name }} # Use a special shaka-bot access token for releases. token: ${{ secrets.RELEASE_PLEASE_TOKEN || secrets.GITHUB_TOKEN }} - # Temporary settings to bootstrap v3.0.0. - last-release-sha: 634af6591ce8c701587a78042ae7f81761725710 - bootstrap-sha: 634af6591ce8c701587a78042ae7f81761725710 + # See also settings in these files: + manifest-file: .release-please-manifest.json + config-file: .release-please-config.json # The jobs below are all conditional on a release having been created by # someone merging the release PR. @@ -51,10 +52,9 @@ jobs: outputs: latest: ${{ steps.compute.outputs.latest }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-tags: true - persist-credentials: false - name: Compute latest id: compute @@ -101,11 +101,13 @@ jobs: # Do a complete build build: name: Build - needs: release + needs: [settings, release] if: needs.release.outputs.release_created uses: ./.github/workflows/build.yaml with: ref: ${{ github.ref }} + self_hosted: ${{ needs.settings.outputs.self_hosted != '' }} + debug: ${{ needs.settings.outputs.debug != '' }} # Attach build artifacts to the release artifacts: @@ -114,22 +116,21 @@ jobs: needs: [release, build] if: needs.release.outputs.release_created steps: - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: path: artifacts + merge-multiple: true - name: Debug run: find -ls - name: Attach packager to release - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - tag: ${{ needs.release.outputs.tag_name }} - make_latest: false # Already set for the release - file_glob: true - file: artifacts/artifacts*/* - overwrite: true + env: + GITHUB_TOKEN: ${{ secrets.RELEASE_PLEASE_TOKEN || secrets.GITHUB_TOKEN }} + run: | + gh -R ${{ github.repository }} release upload \ + ${{ needs.release.outputs.tag_name }} artifacts/* \ + --clobber # Surprisingly, Shaka Packager binaries can be installed via npm. # Publish NPM package updates. diff --git a/.github/workflows/sync-labels.yaml b/.github/workflows/sync-labels.yaml index 47e73a9a44..0b679eab38 100644 --- a/.github/workflows/sync-labels.yaml +++ b/.github/workflows/sync-labels.yaml @@ -18,7 +18,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: shaka-project/shaka-github-tools diff --git a/.github/workflows/test-linux-distros.yaml b/.github/workflows/test-linux-distros.yaml index 8391675e39..992900b174 100644 --- a/.github/workflows/test-linux-distros.yaml +++ b/.github/workflows/test-linux-distros.yaml @@ -29,7 +29,7 @@ jobs: outputs: MATRIX: ${{ steps.configure.outputs.MATRIX }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: ${{ inputs.ref }} @@ -65,7 +65,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: ${{ inputs.ref }} submodules: recursive diff --git a/.github/workflows/update-issues.yaml b/.github/workflows/update-issues.yaml index e69eba5e81..6424ce19ae 100644 --- a/.github/workflows/update-issues.yaml +++ b/.github/workflows/update-issues.yaml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: shaka-project/shaka-github-tools diff --git a/.gitmodules b/.gitmodules index dfd4de8b34..b986b41076 100644 --- a/.gitmodules +++ b/.gitmodules @@ -34,3 +34,6 @@ [submodule "packager/third_party/c-ares/source"] path = packager/third_party/c-ares/source url = https://github.com/c-ares/c-ares +[submodule "packager/third_party/mimalloc/source"] + path = packager/third_party/mimalloc/source + url = https://github.com/microsoft/mimalloc diff --git a/.release-please-config.json b/.release-please-config.json new file mode 100644 index 0000000000..3b2ecb248d --- /dev/null +++ b/.release-please-config.json @@ -0,0 +1,11 @@ +{ + "packages": { + ".": { + "include-component-in-tag": false, + "include-v-in-tag": true, + "component": "", + "release-type-comment": "This is not really a go project, but go projects in release-please only update CHANGELOG.md and nothing else. This is what we want.", + "release-type": "go" + } + } +} diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 0000000000..857973b955 --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "3.0.4" +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a630bf5f2..59cefd4ef4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,117 @@ # Changelog +## [3.0.4](https://github.com/shaka-project/shaka-packager/compare/v3.0.3...v3.0.4) (2024-03-27) + + +### Bug Fixes + +* BaseURL missing when MPD base path is empty ([#1380](https://github.com/shaka-project/shaka-packager/issues/1380)) ([90c3c3f](https://github.com/shaka-project/shaka-packager/commit/90c3c3f9b3e8e36706b9769e574aa316e8bbb351)), closes [#1378](https://github.com/shaka-project/shaka-packager/issues/1378) +* Fix NPM binary selection on ARM Macs ([#1376](https://github.com/shaka-project/shaka-packager/issues/1376)) ([733af91](https://github.com/shaka-project/shaka-packager/commit/733af9128dfa7c46dd7a9fe3f8361ab50a829afe)), closes [#1375](https://github.com/shaka-project/shaka-packager/issues/1375) + +## [3.0.3](https://github.com/shaka-project/shaka-packager/compare/v3.0.2...v3.0.3) (2024-03-12) + + +### Bug Fixes + +* Fix NPM binary publication ([#1371](https://github.com/shaka-project/shaka-packager/issues/1371)) ([4cb6536](https://github.com/shaka-project/shaka-packager/commit/4cb653606047b086affc111321187c7889fa238a)), closes [#1369](https://github.com/shaka-project/shaka-packager/issues/1369) +* Fix tags in official Docker images and binaries ([#1370](https://github.com/shaka-project/shaka-packager/issues/1370)) ([d83c7b1](https://github.com/shaka-project/shaka-packager/commit/d83c7b1d4505a08ef578390115cf28bea77404c2)), closes [#1366](https://github.com/shaka-project/shaka-packager/issues/1366) + +## [3.0.2](https://github.com/shaka-project/shaka-packager/compare/v3.0.1...v3.0.2) (2024-03-07) + + +### Bug Fixes + +* duplicate representation id for TTML when forced ordering is on ([#1364](https://github.com/shaka-project/shaka-packager/issues/1364)) ([0fd815a](https://github.com/shaka-project/shaka-packager/commit/0fd815a160cc4546ea0c13ac916727777f5cdd41)), closes [#1362](https://github.com/shaka-project/shaka-packager/issues/1362) + +## [3.0.1](https://github.com/shaka-project/shaka-packager/compare/v3.0.0...v3.0.1) (2024-03-05) + + +### Bug Fixes + +* **CI:** Add Mac-arm64 to build matrix ([#1359](https://github.com/shaka-project/shaka-packager/issues/1359)) ([c456ad6](https://github.com/shaka-project/shaka-packager/commit/c456ad64d1291bcf057c22a5c34479fcb4bbda55)) +* **CI:** Add missing Linux arm64 builds to release ([9c033b9](https://github.com/shaka-project/shaka-packager/commit/9c033b9d40087a9a4eef6a013d17fd696c44459c)) + +## [3.0.0](https://github.com/shaka-project/shaka-packager/compare/v2.6.1...v3.0.0) (2024-02-28) + + +### ⚠ BREAKING CHANGES + +* Update all dependencies +* Drop Python 2 support in all scripts +* Replace glog with absl::log, tweak log output and flags +* Replace gyp build system with CMake + +### Features + +* Add input support for EBU Teletext in MPEG-TS ([#1344](https://github.com/shaka-project/shaka-packager/issues/1344)) ([71c175d](https://github.com/shaka-project/shaka-packager/commit/71c175d4b8fd7dd1ebb2df8ce06c575f54666738)) +* Add install target to build system ([3e71302](https://github.com/shaka-project/shaka-packager/commit/3e71302ba46e5164db91495c5da5ba07fc88cfca)) +* Add PlayReady support in HLS. ([#1011](https://github.com/shaka-project/shaka-packager/issues/1011)) ([96efc5a](https://github.com/shaka-project/shaka-packager/commit/96efc5aa70c2152a242f36644100161bc562d3f7)) +* add startwithSAP/subsegmentstartswithSAP for audio tracks ([#1346](https://github.com/shaka-project/shaka-packager/issues/1346)) ([d23cce8](https://github.com/shaka-project/shaka-packager/commit/d23cce85b93263a4c7541d9761145be54dd1348d)) +* Add support for ALAC codec ([#1299](https://github.com/shaka-project/shaka-packager/issues/1299)) ([b68ec87](https://github.com/shaka-project/shaka-packager/commit/b68ec87f6a552c27882746a14415bb334041d86b)) +* Add support for single file TS for HLS ([#934](https://github.com/shaka-project/shaka-packager/issues/934)) ([4aa4b4b](https://github.com/shaka-project/shaka-packager/commit/4aa4b4b9aac08fdcf10954de37b9982761fa42c1)) +* Add support for the EXT-X-START tag ([#973](https://github.com/shaka-project/shaka-packager/issues/973)) ([76eb2c1](https://github.com/shaka-project/shaka-packager/commit/76eb2c1575d4c8b32782292dc5216c444a6f2b27)) +* Add xHE-AAC support ([#1092](https://github.com/shaka-project/shaka-packager/issues/1092)) ([5d998fc](https://github.com/shaka-project/shaka-packager/commit/5d998fca7fb1d3d9c98f5b26561f842edcb2a925)) +* Allow LIVE UDP WebVTT input ([#1349](https://github.com/shaka-project/shaka-packager/issues/1349)) ([89376d3](https://github.com/shaka-project/shaka-packager/commit/89376d3c4d3f3005de64dd4bc1668297024dfe46)) +* **DASH:** Add Label element. ([#1175](https://github.com/shaka-project/shaka-packager/issues/1175)) ([b1c5a74](https://github.com/shaka-project/shaka-packager/commit/b1c5a7433e1c148fb6648aadd36c14840e1a4b50)) +* **DASH:** Add video transfer characteristics. ([#1210](https://github.com/shaka-project/shaka-packager/issues/1210)) ([8465f5f](https://github.com/shaka-project/shaka-packager/commit/8465f5f020b5c6152d24107a6d164301e05c3176)) +* default text zero bias ([#1330](https://github.com/shaka-project/shaka-packager/issues/1330)) ([2ba67bc](https://github.com/shaka-project/shaka-packager/commit/2ba67bc24cf6116349ad16d3bfa1a121c95a3173)) +* Drop Python 2 support in all scripts ([3e71302](https://github.com/shaka-project/shaka-packager/commit/3e71302ba46e5164db91495c5da5ba07fc88cfca)) +* Generate the entire AV1 codec string when the colr atom is present ([#1205](https://github.com/shaka-project/shaka-packager/issues/1205)) ([cc9a691](https://github.com/shaka-project/shaka-packager/commit/cc9a691aef946dfb4d68077d3a741ef1b88d2f21)), closes [#1007](https://github.com/shaka-project/shaka-packager/issues/1007) +* HLS / DASH support forced subtitle ([#1020](https://github.com/shaka-project/shaka-packager/issues/1020)) ([f73ad0d](https://github.com/shaka-project/shaka-packager/commit/f73ad0d9614ba5b1ba552385c0157b7017dba204)) +* Move all third-party deps into git submodules ([#1083](https://github.com/shaka-project/shaka-packager/issues/1083)) ([3e71302](https://github.com/shaka-project/shaka-packager/commit/3e71302ba46e5164db91495c5da5ba07fc88cfca)) +* order streams in manifest based on command-line order ([#1329](https://github.com/shaka-project/shaka-packager/issues/1329)) ([aad2a12](https://github.com/shaka-project/shaka-packager/commit/aad2a12a9d6b79abe511d66e9e8d936e0ba46cb4)) +* Parse MPEG-TS PMT ES language and maximum bitrate descriptors ([#369](https://github.com/shaka-project/shaka-packager/issues/369)) ([#1311](https://github.com/shaka-project/shaka-packager/issues/1311)) ([c09eb83](https://github.com/shaka-project/shaka-packager/commit/c09eb831b85c93eb122373ce82b64c354d68c3c1)) +* Portable, fully-static release executables on Linux ([#1351](https://github.com/shaka-project/shaka-packager/issues/1351)) ([9be7c2b](https://github.com/shaka-project/shaka-packager/commit/9be7c2b1ac63d2299ed8293c381ef5877003b5c4)) +* Replace glog with absl::log, tweak log output and flags ([3e71302](https://github.com/shaka-project/shaka-packager/commit/3e71302ba46e5164db91495c5da5ba07fc88cfca)) +* Replace gyp build system with CMake ([3e71302](https://github.com/shaka-project/shaka-packager/commit/3e71302ba46e5164db91495c5da5ba07fc88cfca)), closes [#1047](https://github.com/shaka-project/shaka-packager/issues/1047) +* Respect the file mode for HttpFiles ([#1081](https://github.com/shaka-project/shaka-packager/issues/1081)) ([3e71302](https://github.com/shaka-project/shaka-packager/commit/3e71302ba46e5164db91495c5da5ba07fc88cfca)) +* This patch adds support for DTS:X Profile 2 audio in MP4 files. ([#1303](https://github.com/shaka-project/shaka-packager/issues/1303)) ([07f780d](https://github.com/shaka-project/shaka-packager/commit/07f780dae19d7953bd27b646d9410ed68b4b580e)) +* Update all dependencies ([3e71302](https://github.com/shaka-project/shaka-packager/commit/3e71302ba46e5164db91495c5da5ba07fc88cfca)) +* Write colr atom to muxed mp4 ([#1261](https://github.com/shaka-project/shaka-packager/issues/1261)) ([f264bef](https://github.com/shaka-project/shaka-packager/commit/f264befe868a9fbf054697a6d06218cc25de055d)), closes [#1202](https://github.com/shaka-project/shaka-packager/issues/1202) + + +### Bug Fixes + +* Accept 100% when parsing WEBVTT regions ([#1006](https://github.com/shaka-project/shaka-packager/issues/1006)) ([e1b0c7c](https://github.com/shaka-project/shaka-packager/commit/e1b0c7c45431327fd3ce193514a5407d07b39b22)), closes [#1004](https://github.com/shaka-project/shaka-packager/issues/1004) +* Add missing <cstdint> includes ([#1306](https://github.com/shaka-project/shaka-packager/issues/1306)) ([ba5c771](https://github.com/shaka-project/shaka-packager/commit/ba5c77155a6b0263f48f24f93033c7a386bc83b6)), closes [#1305](https://github.com/shaka-project/shaka-packager/issues/1305) +* Always log to stderr by default ([#1350](https://github.com/shaka-project/shaka-packager/issues/1350)) ([35c2f46](https://github.com/shaka-project/shaka-packager/commit/35c2f4642830e1446701abe17ae6d3b96d6043ae)), closes [#1325](https://github.com/shaka-project/shaka-packager/issues/1325) +* AudioSampleEntry size caluations due to bad merge ([#1354](https://github.com/shaka-project/shaka-packager/issues/1354)) ([615720e](https://github.com/shaka-project/shaka-packager/commit/615720e7dd9699f2d90c63d4bb6ac45c0e804d32)) +* dash_roles add role=description for DVS audio per DASH-IF-IOP-v4.3 ([#1054](https://github.com/shaka-project/shaka-packager/issues/1054)) ([dc03952](https://github.com/shaka-project/shaka-packager/commit/dc0395291a090b342beaab2e89c627dc33ee89b0)) +* Don't close upstream on HttpFile::Flush ([#1201](https://github.com/shaka-project/shaka-packager/issues/1201)) ([53d91cd](https://github.com/shaka-project/shaka-packager/commit/53d91cd0f1295a0c3456cb1a34e5235a0316c523)), closes [#1196](https://github.com/shaka-project/shaka-packager/issues/1196) +* duration formatting and update mpd testdata to reflect new format ([#1320](https://github.com/shaka-project/shaka-packager/issues/1320)) ([56bd823](https://github.com/shaka-project/shaka-packager/commit/56bd823339bbb9ba94ed60a84c554864b42cf94a)) +* Fix build errors related to std::numeric_limits ([#972](https://github.com/shaka-project/shaka-packager/issues/972)) ([9996c73](https://github.com/shaka-project/shaka-packager/commit/9996c736aea79e0cce22bee18dc7dcabfffff47b)) +* Fix build on FreeBSD ([#1287](https://github.com/shaka-project/shaka-packager/issues/1287)) ([3e71302](https://github.com/shaka-project/shaka-packager/commit/3e71302ba46e5164db91495c5da5ba07fc88cfca)) +* Fix clang build ([#1288](https://github.com/shaka-project/shaka-packager/issues/1288)) ([3e71302](https://github.com/shaka-project/shaka-packager/commit/3e71302ba46e5164db91495c5da5ba07fc88cfca)) +* Fix failure on very short WebVTT files ([#1216](https://github.com/shaka-project/shaka-packager/issues/1216)) ([dab165d](https://github.com/shaka-project/shaka-packager/commit/dab165d3e5d979e2e5ff783d91d948357b932078)), closes [#1217](https://github.com/shaka-project/shaka-packager/issues/1217) +* Fix handling of non-interleaved multi track FMP4 files ([#1214](https://github.com/shaka-project/shaka-packager/issues/1214)) ([dcf3225](https://github.com/shaka-project/shaka-packager/commit/dcf32258ffd725bc3de06c9bceb86fc8a403ecba)), closes [#1213](https://github.com/shaka-project/shaka-packager/issues/1213) +* Fix issues with `collections.abc` in Python 3.10+ ([#1188](https://github.com/shaka-project/shaka-packager/issues/1188)) ([80e0240](https://github.com/shaka-project/shaka-packager/commit/80e024013df87a4bfeb265c8ea83cfa2a0c5db0f)), closes [#1192](https://github.com/shaka-project/shaka-packager/issues/1192) +* Fix local files with UTF8 names ([#1246](https://github.com/shaka-project/shaka-packager/issues/1246)) ([3e71302](https://github.com/shaka-project/shaka-packager/commit/3e71302ba46e5164db91495c5da5ba07fc88cfca)) +* Fix missing newline at the end of usage ([#1352](https://github.com/shaka-project/shaka-packager/issues/1352)) ([6276584](https://github.com/shaka-project/shaka-packager/commit/6276584de784025380f46dca379795ed6ee42b8f)) +* Fix Python 3.10+ compatibility in scripts ([3e71302](https://github.com/shaka-project/shaka-packager/commit/3e71302ba46e5164db91495c5da5ba07fc88cfca)) +* Fix uninitialized value found by Valgrind ([#1336](https://github.com/shaka-project/shaka-packager/issues/1336)) ([7ef5167](https://github.com/shaka-project/shaka-packager/commit/7ef51671f1a221443bcd000ccb13189ee6ccf749)) +* Fix various build issues on macOS ([3e71302](https://github.com/shaka-project/shaka-packager/commit/3e71302ba46e5164db91495c5da5ba07fc88cfca)) +* Fix various build issues on Windows ([3e71302](https://github.com/shaka-project/shaka-packager/commit/3e71302ba46e5164db91495c5da5ba07fc88cfca)) +* hls, set the DEFAULT explicitly to NO. Supports native HLS players. ([#1170](https://github.com/shaka-project/shaka-packager/issues/1170)) ([1ab6818](https://github.com/shaka-project/shaka-packager/commit/1ab68188326685ab4e427e7a6eab0694e0b0b60a)), closes [#1169](https://github.com/shaka-project/shaka-packager/issues/1169) +* http_file: Close upload cache on task exit ([#1348](https://github.com/shaka-project/shaka-packager/issues/1348)) ([6acdcc3](https://github.com/shaka-project/shaka-packager/commit/6acdcc394a1583b22c3c16cee3e419eea6c6e4f9)), closes [#1347](https://github.com/shaka-project/shaka-packager/issues/1347) +* Indexing `bytes` produces `int` on python3 for `pssh-box.py` ([#1228](https://github.com/shaka-project/shaka-packager/issues/1228)) ([d9d3c7f](https://github.com/shaka-project/shaka-packager/commit/d9d3c7f8be13e493a99b2ff4b72a402c441c1666)), closes [#1227](https://github.com/shaka-project/shaka-packager/issues/1227) +* Low Latency DASH: include the "availabilityTimeComplete=false" attribute ([#1198](https://github.com/shaka-project/shaka-packager/issues/1198)) ([d687ad1](https://github.com/shaka-project/shaka-packager/commit/d687ad1ed00da260c4f4e5169b042ef4291052a0)) +* misleading log output when HLS target duration updates (fixes [#969](https://github.com/shaka-project/shaka-packager/issues/969)) ([#971](https://github.com/shaka-project/shaka-packager/issues/971)) ([f7b3986](https://github.com/shaka-project/shaka-packager/commit/f7b3986818915ab3d7d3dc31b8316fcef0384294)) +* **MP4:** Add compatible brand dby1 for Dolby content. ([#1211](https://github.com/shaka-project/shaka-packager/issues/1211)) ([520926c](https://github.com/shaka-project/shaka-packager/commit/520926c27ad0d183127e4548c4564af33a2ad2f3)) +* Parse one frame mpeg-ts video ([#1015](https://github.com/shaka-project/shaka-packager/issues/1015)) ([b221aa9](https://github.com/shaka-project/shaka-packager/commit/b221aa9caf4f8357a696f3265d1e2a5bf504dbd9)), closes [#1013](https://github.com/shaka-project/shaka-packager/issues/1013) +* preserve case for stream descriptors ([#1321](https://github.com/shaka-project/shaka-packager/issues/1321)) ([5d44368](https://github.com/shaka-project/shaka-packager/commit/5d44368478bbd0cd8bd06f0dfa739f8cfa032ddc)) +* Prevent crash in GetEarliestTimestamp() if periods are empty ([#1173](https://github.com/shaka-project/shaka-packager/issues/1173)) ([d6f28d4](https://github.com/shaka-project/shaka-packager/commit/d6f28d456c6ec5ecf39c868447d85294a698166d)), closes [#1172](https://github.com/shaka-project/shaka-packager/issues/1172) +* PTS diverge DTS when DTS close to 2pow33 and PTS more than 0 ([#1050](https://github.com/shaka-project/shaka-packager/issues/1050)) ([ab8ab12](https://github.com/shaka-project/shaka-packager/commit/ab8ab12d098c352372014180bd2cb5407e018739)), closes [#1049](https://github.com/shaka-project/shaka-packager/issues/1049) +* remove extra block assumptions in mbedtls integration ([#1323](https://github.com/shaka-project/shaka-packager/issues/1323)) ([db59ad5](https://github.com/shaka-project/shaka-packager/commit/db59ad582a353fa1311563bdde93c49449159859)), closes [#1316](https://github.com/shaka-project/shaka-packager/issues/1316) +* Restore support for legacy FairPlay system ID ([#1357](https://github.com/shaka-project/shaka-packager/issues/1357)) ([4d22e99](https://github.com/shaka-project/shaka-packager/commit/4d22e99f8e7777557f5de3d5439f7e7b397e4323)) +* Roll back depot_tools, bypass vpython ([#1045](https://github.com/shaka-project/shaka-packager/issues/1045)) ([3fd538a](https://github.com/shaka-project/shaka-packager/commit/3fd538a587184a87e2b41a526e089247007aa526)), closes [#1023](https://github.com/shaka-project/shaka-packager/issues/1023) +* set array_completeness in HEVCDecoderConfigurationRecord correctly ([#975](https://github.com/shaka-project/shaka-packager/issues/975)) ([270888a](https://github.com/shaka-project/shaka-packager/commit/270888abb12b2181ff84071f2c2685bd196de6fe)) +* TTML generator timestamp millisecond formatting ([#1179](https://github.com/shaka-project/shaka-packager/issues/1179)) ([494769c](https://github.com/shaka-project/shaka-packager/commit/494769ca864e04d582f707934a6573cad78d2e8c)), closes [#1180](https://github.com/shaka-project/shaka-packager/issues/1180) +* Update golden files for ttml tests and failing hls unit tests. ([#1226](https://github.com/shaka-project/shaka-packager/issues/1226)) ([ac47e52](https://github.com/shaka-project/shaka-packager/commit/ac47e529ad7b69cc232f7f96e2a042990505776f)) +* Update to use official FairPlay UUID. ([#1281](https://github.com/shaka-project/shaka-packager/issues/1281)) ([ac59b9e](https://github.com/shaka-project/shaka-packager/commit/ac59b9ebc94018b216c657854ca5163c9d2e7f31)) +* use a better estimate of frame rate for cases with very short first sample durations ([#838](https://github.com/shaka-project/shaka-packager/issues/838)) ([5644041](https://github.com/shaka-project/shaka-packager/commit/56440413aa32a13cbfbb41a6d5ee611bae02ab2e)) +* webvtt single cue do not fail on EOS ([#1061](https://github.com/shaka-project/shaka-packager/issues/1061)) ([b9d477b](https://github.com/shaka-project/shaka-packager/commit/b9d477b969f34124dfe184b4ac1d00ea8faf0a7d)), closes [#1018](https://github.com/shaka-project/shaka-packager/issues/1018) + ## [2.6.1] - 2021-10-14 ### Fixed - Fix crash in static-linked linux builds (#996) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2c90d093fc..5b96a2429e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,14 +15,18 @@ include("packager/policies.cmake") # Project name. May not contain spaces. Versioning is managed elsewhere. project(shaka-packager VERSION "") -# The only build option for Shaka Packager is whether to build a shared -# libpackager library. By default, don't. +# Whether to build a shared libpackager library. By default, don't. option(BUILD_SHARED_LIBS "Build libpackager as a shared library" OFF) +# Whether to attempt a static linking of the command line front-end. +# Only supported on Linux and with musl. This will also cause us to link +# against mimalloc to replace the standard allocator in musl, which is slow. +option(FULLY_STATIC "Attempt fully static linking of all CLI apps" OFF) + # Enable CMake's test infrastructure. enable_testing() -option(SKIP_INTEGRATION_TESTS "Skip the packager integration tests" ON) +option(SKIP_INTEGRATION_TESTS "Skip the packager integration tests" OFF) # Subdirectories with their own CMakeLists.txt add_subdirectory(packager) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index d7757949bb..9ba03ca1b7 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -54,6 +54,7 @@ Sanil Raut Sergio Ammirata Thomas Inskip Tim Lansen +Torbjörn Einarsson Vincent Nguyen Weiguo Shao diff --git a/docs/source/build_instructions.md b/docs/source/build_instructions.md index 84423f3db5..3f830ac5b8 100644 --- a/docs/source/build_instructions.md +++ b/docs/source/build_instructions.md @@ -106,6 +106,18 @@ After configuring CMake you can run the build with cmake --build build --parallel ``` +To build portable, fully-static executables on Linux, you will need either musl +as your system libc, or a musl toolchain. (See [musl.cc](https://musl.cc). +To create a portable, fully-static build for Linux, configure CMake with: + +```shell + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_SHARED_LIBS="OFF" \ + -DFULLY_STATIC="ON" \ + -DCMAKE_C_COMPILER=/path/to/x86_64-linux-musl-gcc \ + -DCMAKE_CXX_COMPILER=/path/to/x86_64-linux-musl-g++ +``` + #### Windows Windows build instructions are similar. Using Tools > Command Line > diff --git a/docs/source/options/dash_stream_descriptors.rst b/docs/source/options/dash_stream_descriptors.rst index c5c646899b..a50f7fadaa 100644 --- a/docs/source/options/dash_stream_descriptors.rst +++ b/docs/source/options/dash_stream_descriptors.rst @@ -10,7 +10,10 @@ DASH specific stream descriptor fields :dash_roles (roles): - Optional semicolon separated list of values for DASH Role element. The + optional semicolon separated list of values for DASH Role element. The value should be one of: **caption**, **subtitle**, **main**, **alternate**, - **supplementary**, **commentary**, **description**, **dub** and **forced-subtitle** . + **supplementary**, **commentary**, **dub**, **description**, **sign**, + **metadata**, **enhanced-audio- intelligibility**, **emergency**, + **forced-subtitle**, **easyreader**, and **karaoke**. + See DASH (ISO/IEC 23009-1) specification for details. diff --git a/docs/source/options/stream_descriptors.rst b/docs/source/options/stream_descriptors.rst index 82e8037735..b7d041dfb5 100644 --- a/docs/source/options/stream_descriptors.rst +++ b/docs/source/options/stream_descriptors.rst @@ -60,6 +60,20 @@ These are the available fields: For subtitles in MP4, you can specify 'vtt+mp4' or 'ttml+mp4' to control which text format is used. +:input_format (format): + + Optional value which specifies the format of the input files or + streams. If not specified, it will be autodetected, which in some + cases may fail. + + For example, a live UDP WebVTT input stream may be up and streaming + long before a shaka packager instance consumes it, and therefore + shaka packager never gets the initial "WEBVTT" header string. In + such a case, shaka packager can't properly autodetect the stream + format as WebVTT, and thus doesn't process it. But stating + 'input_format=webvtt' as selector parameter will tell shaka packager + to omit autodetection and consider WebVTT format for that stream. + :trick_play_factor (tpf): Optional value which specifies the trick play, a.k.a. trick mode, stream diff --git a/docs/source/tutorials/text.rst b/docs/source/tutorials/text.rst index 502c309336..8291a29794 100644 --- a/docs/source/tutorials/text.rst +++ b/docs/source/tutorials/text.rst @@ -45,3 +45,9 @@ Examples in=in_en.vtt,stream=text,language=en,output=out_en.mp4 \ in=in_sp.vtt,stream=text,language=sp,output=out_sp.mp4 \ in=in_fr.vtt,stream=text,language=fr,output=out_fr.mp4 + +* Get a single 3-digit page from DVB-teletext and set language for output formats stpp (TTML in mp4), wvtt (WebVTT in mp4) and HLS WebVTT:: + + $ packager in=input.ts,stream=text,cc_index=888,lang=en,format=ttml+mp4,output=output.mp4 + $ packager in=input.ts,stream=text,cc_index=888,lang=en,output=output.mp4 + $ packager in=input.ts,stream=text,cc_index=888,segment_template=text/$Number$.vtt,playlist_name=text/main.m3u8,hls_group_id=text,hls_name=ENGLISH diff --git a/include/packager/packager.h b/include/packager/packager.h index 76917a4120..6b835fe24f 100644 --- a/include/packager/packager.h +++ b/include/packager/packager.h @@ -152,6 +152,11 @@ struct StreamDescriptor { /// Set to true to indicate that the stream is for hls only. bool hls_only = false; + /// Optional value which specifies input container format. + /// Useful for live streaming situations, like auto-detecting webvtt without + /// its initial header. + std::string input_format; + /// Optional, indicates if this is a Forced Narrative subtitle stream. bool forced_subtitle = false; diff --git a/link-test/CMakeLists.txt b/link-test/CMakeLists.txt index 3fc0d097d3..4fce0f1b92 100644 --- a/link-test/CMakeLists.txt +++ b/link-test/CMakeLists.txt @@ -13,7 +13,7 @@ if(BUILD_SHARED_LIBS) # Custom commands aren't targets, but have outputs. add_custom_command( - DEPENDS mpd_generator packager libpackager + DEPENDS mpd_generator packager libpackager pssh_box_py WORKING_DIRECTORY ${CMAKE_BINARY_DIR} OUTPUT ${TEST_INSTALL_DIR} COMMAND diff --git a/npm/.npmignore b/npm/.npmignore new file mode 100644 index 0000000000..9d17c9c4d9 --- /dev/null +++ b/npm/.npmignore @@ -0,0 +1,8 @@ +# This file is required for us, even if empty. +# +# The default .npmignore would disallow bin/, which contains binaries for our +# NPM release. Every release before v3.0.3 was missing .npmignore, and +# therefore had no binaries. These were unusable in NPM, and have been marked +# as deprecated because of it. +# +# See also https://github.com/shaka-project/shaka-packager/issues/1369 diff --git a/npm/index.js b/npm/index.js index 549a8c24a0..215dc573e4 100755 --- a/npm/index.js +++ b/npm/index.js @@ -13,6 +13,7 @@ var commandNames = { }, darwin: { 'x64': 'packager-osx-x64', + 'arm64': 'packager-osx-arm64', }, win32: { 'x64': 'packager-win-x64.exe', diff --git a/npm/prepublish.js b/npm/prepublish.js index 57b1304984..b6a2c49eb1 100755 --- a/npm/prepublish.js +++ b/npm/prepublish.js @@ -14,6 +14,7 @@ var commandNames = { }, darwin: { 'x64': 'packager-osx-x64', + 'arm64': 'packager-osx-arm64', }, win32: { 'x64': 'packager-win-x64.exe', diff --git a/packager/CMakeLists.txt b/packager/CMakeLists.txt index 243a1efdab..7238d243bb 100644 --- a/packager/CMakeLists.txt +++ b/packager/CMakeLists.txt @@ -57,12 +57,20 @@ include_directories(..) # Public include folder, to reference public headers as packager/foo.h include_directories(../include) +# Include settings for optional fully-static binaries. +include("fully-static.cmake") + # Include our module for gtest-based testing. include("gtest.cmake") # Include our module for building protos. include("protobuf.cmake") +# Find Python3 used by integration tests, license notice and version string +if(NOT Python3_EXECUTABLE) + find_package(Python3 COMPONENTS Interpreter REQUIRED) +endif() + # Subdirectories with their own CMakeLists.txt, all of whose targets are built. add_subdirectory(file) add_subdirectory(kv_pairs) @@ -171,6 +179,7 @@ target_link_libraries(packager libpackager license_notice string_utils + ${EXTRA_EXE_LIBRARIES} ) add_executable(mpd_generator @@ -187,6 +196,7 @@ target_link_libraries(mpd_generator license_notice mpd_builder mpd_util + ${EXTRA_EXE_LIBRARIES} ) add_executable(packager_test @@ -223,9 +233,22 @@ add_custom_target(packager_test_py_copy ALL if(NOT SKIP_INTEGRATION_TESTS) add_test (NAME packager_test_py - COMMAND ${PYTHON_EXECUTABLE} packager_test.py + COMMAND "${Python3_EXECUTABLE}" packager_test.py WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} ) + + set(test_environment_vars "PACKAGER_SRC_DIR=${CMAKE_SOURCE_DIR}") + list(APPEND test_environment_vars "PACKAGER_BIN=$") + list(APPEND test_environment_vars "MPD_GENERATOR_BIN=$") + if(BUILD_SHARED_LIBS) + list(APPEND test_environment_vars "BUILD_TYPE=shared") + else() + list(APPEND test_environment_vars "BUILD_TYPE=static") + endif() + + set_tests_properties(packager_test_py PROPERTIES + ENVIRONMENT "${test_environment_vars}" + ) endif() configure_file(packager.pc.in packager.pc @ONLY) @@ -233,12 +256,6 @@ configure_file(packager.pc.in packager.pc @ONLY) # Always install the binaries. install(TARGETS mpd_generator packager) -# Always install the python tools. -install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/pssh-box.py - DESTINATION ${CMAKE_INSTALL_BINDIR}) -install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/pssh-box-protos - DESTINATION ${CMAKE_INSTALL_BINDIR}) - # With shared libraries, also install the library, headers, and pkgconfig. # The static library isn't usable as a standalone because it doesn't include # its static dependencies (zlib, absl, etc). diff --git a/packager/app/mpd_generator.cc b/packager/app/mpd_generator.cc index ee9de0f810..e4cec7ed48 100644 --- a/packager/app/mpd_generator.cc +++ b/packager/app/mpd_generator.cc @@ -31,6 +31,9 @@ ABSL_FLAG(std::string, "", "Packager version for testing. Should be used for testing only."); +// From absl/log: +ABSL_DECLARE_FLAG(int, stderrthreshold); + namespace shaka { namespace { const char kUsage[] = @@ -109,6 +112,13 @@ int MpdMain(int argc, char** argv) { auto usage = absl::StrFormat(kUsage, argv[0]); absl::SetProgramUsageMessage(usage); + + // Before parsing the command line, change the default value of some flags + // provided by libraries. + + // Always log to stderr. Log levels are still controlled by --minloglevel. + absl::SetFlag(&FLAGS_stderrthreshold, 0); + absl::ParseCommandLine(argc, argv); if (absl::GetFlag(FLAGS_licenses)) { diff --git a/packager/app/packager_main.cc b/packager/app/packager_main.cc index 9ff6baa0a3..4f3d8338d5 100644 --- a/packager/app/packager_main.cc +++ b/packager/app/packager_main.cc @@ -58,6 +58,9 @@ ABSL_FLAG(bool, false, "If enabled, only use one thread when generating content."); +// From absl/log: +ABSL_DECLARE_FLAG(int, stderrthreshold); + namespace shaka { namespace { @@ -89,6 +92,10 @@ const char kUsage[] = " - output_format (format): Optional value which specifies the format\n" " of the output files (MP4 or WebM). If not specified, it will be\n" " derived from the file extension of the output file.\n" + " - input_format (format): Optional value which specifies the format\n" + " of the input files or streams. If not specified, it will be\n" + " autodetected, which in some cases (such as live UDP webvtt) may\n" + " fail.\n" " - skip_encryption=0|1: Optional. Defaults to 0 if not specified. If\n" " it is set to 1, no encryption of the stream will be made.\n" " - drm_label: Optional value for custom DRM label, which defines the\n" @@ -119,9 +126,10 @@ const char kUsage[] = " list of values for DASH Accessibility elements. The value should be\n" " in the format: scheme_id_uri=value.\n" " - dash_roles (roles): Optional semicolon separated list of values for\n" - " DASH Role elements. The value should be one of: caption, subtitle,\n" - " forced-subtitle, main, alternate, supplementary, commentary, \n" - " description and dub. See DASH\n" + " DASH Role elements. The value should be one of: caption, subtitle, \n" + " main, alternate, supplementary, commentary, dub, description, sign, \n" + " metadata, enhanced-audio- intelligibility, emergency, \n" + " forced-subtitle, easyreader, and karaoke. See DASH\n" " (ISO/IEC 23009-1) specification for details.\n" " - forced_subtitle: Optional boolean value (0|1). If set to 1 \n" " indicates that this stream is a Forced Narrative subtitle that \n" @@ -129,7 +137,7 @@ const char kUsage[] = " used to caption short portions of the audio that might be in a \n" " foreign language. For DASH this will set role to forced_subtitle, \n" " for HLS it will set FORCED=YES and AUTOSELECT=YES. \n" - " Only valid for subtitles."; + " Only valid for subtitles.\n"; // Labels for parameters in RawKey key info. const char kDrmLabelLabel[] = "label"; @@ -619,6 +627,12 @@ int PackagerMain(int argc, char** argv) { auto usage = absl::StrFormat(kUsage, argv[0]); absl::SetProgramUsageMessage(usage); + // Before parsing the command line, change the default value of some flags + // provided by libraries. + + // Always log to stderr. Log levels are still controlled by --minloglevel. + absl::SetFlag(&FLAGS_stderrthreshold, 0); + auto remaining_args = absl::ParseCommandLine(argc, argv); if (absl::GetFlag(FLAGS_licenses)) { for (const char* line : kLicenseNotice) diff --git a/packager/app/stream_descriptor.cc b/packager/app/stream_descriptor.cc index b49d2dfc8f..afcc4fcb3d 100644 --- a/packager/app/stream_descriptor.cc +++ b/packager/app/stream_descriptor.cc @@ -41,6 +41,7 @@ enum FieldType { kHlsOnlyField, kDashLabelField, kForcedSubtitleField, + kInputFormatField, }; struct FieldNameToTypeMapping { @@ -90,6 +91,7 @@ const FieldNameToTypeMapping kFieldNameTypeMappings[] = { {"hls_only", kHlsOnlyField}, {"dash_label", kDashLabelField}, {"forced_subtitle", kForcedSubtitleField}, + {"input_format", kInputFormatField}, }; FieldType GetFieldType(const std::string& field_name) { @@ -271,6 +273,10 @@ std::optional ParseStreamDescriptor( } descriptor.forced_subtitle = forced_subtitle_value > 0; break; + case kInputFormatField: { + descriptor.input_format = pair.second; + break; + } default: LOG(ERROR) << "Unknown field in stream descriptor (\"" << pair.first << "\")."; diff --git a/packager/app/test/packager_app.py b/packager/app/test/packager_app.py index a8c8752159..c736749c3d 100644 --- a/packager/app/test/packager_app.py +++ b/packager/app/test/packager_app.py @@ -17,10 +17,8 @@ class PackagerApp(object): """Main integration class for testing the packager binaries.""" def __init__(self): - self.packager_binary = os.path.join(test_env.SCRIPT_DIR, - self._GetBinaryName('packager')) - self.mpd_generator_binary = os.path.join( - test_env.SCRIPT_DIR, self._GetBinaryName('mpd_generator')) + self.packager_binary = test_env.PACKAGER_BIN + self.mpd_generator_binary = test_env.MPD_GENERATOR_BIN # Set this to empty for now in case GetCommandLine() is called before # Package(). self.packaging_command_line = '' @@ -28,15 +26,10 @@ class PackagerApp(object): 'Please run from output directory, e.g. out/Debug/packager_test.py\n' ' Missing: ' + self.packager_binary) - def _GetBinaryName(self, name): - if platform.system() == 'Windows': - name += '.exe' - return name - def GetEnv(self): env = os.environ.copy() if (platform.system() == 'Darwin' and - test_env.options.libpackager_type == 'shared_library'): + test_env.BUILD_TYPE == 'shared'): env['DYLD_FALLBACK_LIBRARY_PATH'] = test_env.SCRIPT_DIR return env diff --git a/packager/app/test/packager_test.py b/packager/app/test/packager_test.py index 3d44474365..1c6eefab53 100755 --- a/packager/app/test/packager_test.py +++ b/packager/app/test/packager_test.py @@ -498,6 +498,7 @@ class PackagerAppTest(unittest.TestCase): sbd_url_text=None, sbd_template_text=None, sbd_key_text=None): + flags = ['--single_threaded'] if not strip_parameter_set_nalus: @@ -609,8 +610,10 @@ class PackagerAppTest(unittest.TestCase): if allow_codec_switching: flags += ['--allow_codec_switching'] - if force_cl_index: + if force_cl_index is True: flags += ['--force_cl_index'] + elif force_cl_index is False: + flags += ['--noforce_cl_index'] if ad_cues: flags += ['--ad_cues', ad_cues] @@ -793,7 +796,8 @@ class PackagerFunctionalTest(PackagerAppTest): self._GetStream('video', trick_play_factor=2), ] - self.assertPackageSuccess(streams, self._GetFlags(output_dash=True)) + self.assertPackageSuccess(streams, self._GetFlags(output_dash=True, + force_cl_index=False)) self._CheckTestResults('audio-video-with-two-trick-play') def testAudioVideoWithTwoTrickPlayDecreasingRate(self): @@ -804,7 +808,8 @@ class PackagerFunctionalTest(PackagerAppTest): self._GetStream('video', trick_play_factor=1), ] - self.assertPackageSuccess(streams, self._GetFlags(output_dash=True)) + self.assertPackageSuccess(streams, self._GetFlags(output_dash=True, + force_cl_index=False)) # Since the stream descriptors are sorted in packager app, a different # order of trick play factors gets the same mpd. self._CheckTestResults('audio-video-with-two-trick-play') @@ -1535,6 +1540,15 @@ class PackagerFunctionalTest(PackagerAppTest): self.assertPackageSuccess(streams, flags) self._CheckTestResults('flac-with-encryption', verify_decryption=True) + def testAlac(self): + streams = [ + self._GetStream('audio', test_file='bear-alac.mp4'), + ] + flags = self._GetFlags(output_dash=True) + + self.assertPackageSuccess(streams, flags) + self._CheckTestResults('audio-alac') + def testAv1Mp4WithEncryption(self): self.assertPackageSuccess( self._GetStreams(['video'], test_files=['bear-av1.mp4']), @@ -1559,8 +1573,8 @@ class PackagerFunctionalTest(PackagerAppTest): # TODO(kqyang): Fix shared_library not supporting strip_parameter_set_nalus # problem. - @unittest.skipUnless( - test_env.options.libpackager_type == 'static_library', + @unittest.skipIf( + test_env.BUILD_TYPE == 'shared', 'libpackager shared_library does not support ' '--strip_parameter_set_nalus flag.' ) @@ -1655,6 +1669,13 @@ class PackagerFunctionalTest(PackagerAppTest): self._GetFlags(encryption=True, output_hls=True)) self._CheckTestResults('ec3-and-hls-single-segment-mp4-encrypted') + def testHlsSingleSegmentTs(self): + self.assertPackageSuccess( + self._GetStreams( + ['audio', 'video'], hls=True, test_files=['bear-640x360.ts']), + self._GetFlags(output_hls=True)) + self._CheckTestResults('hls-single-segment-ts') + def testEc3PackedAudioEncrypted(self): streams = [ self._GetStream( @@ -1774,6 +1795,18 @@ class PackagerFunctionalTest(PackagerAppTest): force_cl_index=True)) self._CheckTestResults('forced-commandline-ordering') + def testForcedCommandlineOrderingWithTTML(self): + streams = [ + self._GetStream('video', test_file='bear-640x360.mp4'), + self._GetStream('audio', test_file='bear-640x360.mp4'), + self._GetStream('text', test_file='bear-english.ttml'), + ] + + self.assertPackageSuccess(streams, + self._GetFlags(output_dash=True, output_hls=False, + force_cl_index=True)) + self._CheckTestResults('forced-commandline-ordering-ttml') + def testAllowCodecSwitchingWithCommandlineOrdering(self): streams = [ self._GetStream('audio', test_file='bear-640x360.mp4'), diff --git a/packager/app/test/test_env.py b/packager/app/test/test_env.py index 602d9b9bb3..f26a46523f 100644 --- a/packager/app/test/test_env.py +++ b/packager/app/test/test_env.py @@ -14,21 +14,38 @@ flags through the command line interface. import argparse import os +import platform import sys +def GetBinaryName(name): + if platform.system() == 'Windows': + name += '.exe' + return name + # Define static global objects and attributes. SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) -SRC_DIR = os.path.join(SCRIPT_DIR, os.pardir, os.pardir) +SRC_DIR = os.environ.get('PACKAGER_SRC_DIR') +if not SRC_DIR: + # fallback to computing src dir from script dir + SRC_DIR = os.path.join(SCRIPT_DIR, os.pardir, os.pardir) + +PACKAGER_BIN = os.environ.get('PACKAGER_BIN') +if not PACKAGER_BIN: + PACKAGER_BIN = os.path.join(SCRIPT_DIR, + GetBinaryName('packager')) + +MPD_GENERATOR_BIN = os.environ.get('MPD_GENERATOR_BIN') +if not MPD_GENERATOR_BIN: + MPD_GENERATOR_BIN = os.path.join(SCRIPT_DIR, + GetBinaryName('mpd_generator')) + +BUILD_TYPE = os.environ.get('BUILD_TYPE', 'static') # Parse arguments and calculate dynamic global objects and attributes. parser = argparse.ArgumentParser() - parser.add_argument('--test_update_golden_files', action='store_true') -parser.add_argument('--libpackager_type', default='static_library', - choices=['static_library', 'shared_library']) - parser.add_argument('--v') parser.add_argument('--vmodule') # Overwrite the test to encryption key/iv specified in the command line. diff --git a/packager/app/test/testdata/acc-he/output.mpd b/packager/app/test/testdata/acc-he/output.mpd index 229ef2b15f..e0376baa3d 100644 --- a/packager/app/test/testdata/acc-he/output.mpd +++ b/packager/app/test/testdata/acc-he/output.mpd @@ -2,7 +2,7 @@ - + bear-640x360-aac_he-silent_right-audio.mp4 diff --git a/packager/app/test/testdata/audio-alac/bear-alac-audio.mp4 b/packager/app/test/testdata/audio-alac/bear-alac-audio.mp4 new file mode 100644 index 0000000000..2239643239 Binary files /dev/null and b/packager/app/test/testdata/audio-alac/bear-alac-audio.mp4 differ diff --git a/packager/app/test/testdata/audio-alac/output.mpd b/packager/app/test/testdata/audio-alac/output.mpd new file mode 100644 index 0000000000..7678c0fed6 --- /dev/null +++ b/packager/app/test/testdata/audio-alac/output.mpd @@ -0,0 +1,15 @@ + + + + + + + + bear-alac-audio.mp4 + + + + + + + diff --git a/packager/app/test/testdata/audio-video-with-accessibilities-and-roles/output.mpd b/packager/app/test/testdata/audio-video-with-accessibilities-and-roles/output.mpd index 32c93b2fe7..3c01ae5ee4 100644 --- a/packager/app/test/testdata/audio-video-with-accessibilities-and-roles/output.mpd +++ b/packager/app/test/testdata/audio-video-with-accessibilities-and-roles/output.mpd @@ -2,7 +2,7 @@ - + diff --git a/packager/app/test/testdata/audio-video-with-codec-switching-and-forced-commandline_order/output.mpd b/packager/app/test/testdata/audio-video-with-codec-switching-and-forced-commandline_order/output.mpd index 4dbb0e2019..3ff2faa158 100644 --- a/packager/app/test/testdata/audio-video-with-codec-switching-and-forced-commandline_order/output.mpd +++ b/packager/app/test/testdata/audio-video-with-codec-switching-and-forced-commandline_order/output.mpd @@ -2,7 +2,7 @@ - + bear-640x360-audio.mp4 diff --git a/packager/app/test/testdata/audio-video-with-codec-switching-encryption-trick-play/output.mpd b/packager/app/test/testdata/audio-video-with-codec-switching-encryption-trick-play/output.mpd index d33134418d..b5b8344745 100644 --- a/packager/app/test/testdata/audio-video-with-codec-switching-encryption-trick-play/output.mpd +++ b/packager/app/test/testdata/audio-video-with-codec-switching-encryption-trick-play/output.mpd @@ -49,7 +49,7 @@ - + AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA== diff --git a/packager/app/test/testdata/audio-video-with-codec-switching/output.mpd b/packager/app/test/testdata/audio-video-with-codec-switching/output.mpd index 63c949e4d6..5504466c18 100644 --- a/packager/app/test/testdata/audio-video-with-codec-switching/output.mpd +++ b/packager/app/test/testdata/audio-video-with-codec-switching/output.mpd @@ -28,7 +28,7 @@ - + bear-640x360-audio.mp4 diff --git a/packager/app/test/testdata/audio-video-with-language-override-with-subtag/output.mpd b/packager/app/test/testdata/audio-video-with-language-override-with-subtag/output.mpd index e3bd7b5a80..00bec7e215 100644 --- a/packager/app/test/testdata/audio-video-with-language-override-with-subtag/output.mpd +++ b/packager/app/test/testdata/audio-video-with-language-override-with-subtag/output.mpd @@ -2,7 +2,7 @@ - + bear-640x360-audio.mp4 diff --git a/packager/app/test/testdata/audio-video-with-language-override/output.mpd b/packager/app/test/testdata/audio-video-with-language-override/output.mpd index 3a04fe7567..c75de98f00 100644 --- a/packager/app/test/testdata/audio-video-with-language-override/output.mpd +++ b/packager/app/test/testdata/audio-video-with-language-override/output.mpd @@ -2,7 +2,7 @@ - + diff --git a/packager/app/test/testdata/audio-video-with-trick-play-and-forced-commandline-order/output.mpd b/packager/app/test/testdata/audio-video-with-trick-play-and-forced-commandline-order/output.mpd index 3e6bb8c966..95cb7de42c 100644 --- a/packager/app/test/testdata/audio-video-with-trick-play-and-forced-commandline-order/output.mpd +++ b/packager/app/test/testdata/audio-video-with-trick-play-and-forced-commandline-order/output.mpd @@ -2,7 +2,7 @@ - + bear-640x360-audio.mp4 diff --git a/packager/app/test/testdata/audio-video-with-trick-play/output.mpd b/packager/app/test/testdata/audio-video-with-trick-play/output.mpd index f2ed8f3c10..05e5387dac 100644 --- a/packager/app/test/testdata/audio-video-with-trick-play/output.mpd +++ b/packager/app/test/testdata/audio-video-with-trick-play/output.mpd @@ -2,7 +2,7 @@ - + bear-640x360-audio.mp4 diff --git a/packager/app/test/testdata/audio-video-with-two-trick-play/output.mpd b/packager/app/test/testdata/audio-video-with-two-trick-play/output.mpd index 560d57adaf..bee30c7943 100644 --- a/packager/app/test/testdata/audio-video-with-two-trick-play/output.mpd +++ b/packager/app/test/testdata/audio-video-with-two-trick-play/output.mpd @@ -2,8 +2,31 @@ - - + + + bear-640x360-video.mp4 + + + + + + + + + bear-640x360-video-trick_play_factor_1.mp4 + + + + + + bear-640x360-video-trick_play_factor_2.mp4 + + + + + + + bear-640x360-audio.mp4 @@ -11,28 +34,5 @@ - - - bear-640x360-video.mp4 - - - - - - - - - bear-640x360-video-trick_play_factor_2.mp4 - - - - - - bear-640x360-video-trick_play_factor_1.mp4 - - - - - diff --git a/packager/app/test/testdata/audio-video/output.mpd b/packager/app/test/testdata/audio-video/output.mpd index ba7a0c5612..fe3119bb05 100644 --- a/packager/app/test/testdata/audio-video/output.mpd +++ b/packager/app/test/testdata/audio-video/output.mpd @@ -2,7 +2,7 @@ - + bear-640x360-audio.mp4 diff --git a/packager/app/test/testdata/avc-aac-ts/output.mpd b/packager/app/test/testdata/avc-aac-ts/output.mpd index 65a525535e..a238464544 100644 --- a/packager/app/test/testdata/avc-aac-ts/output.mpd +++ b/packager/app/test/testdata/avc-aac-ts/output.mpd @@ -2,7 +2,7 @@ - + diff --git a/packager/app/test/testdata/avc-ts-live-playlist-dash-dynamic-with-segment-deletion/output.mpd b/packager/app/test/testdata/avc-ts-live-playlist-dash-dynamic-with-segment-deletion/output.mpd index 3cbc98eccc..762fd7c69b 100644 --- a/packager/app/test/testdata/avc-ts-live-playlist-dash-dynamic-with-segment-deletion/output.mpd +++ b/packager/app/test/testdata/avc-ts-live-playlist-dash-dynamic-with-segment-deletion/output.mpd @@ -2,7 +2,7 @@ - + diff --git a/packager/app/test/testdata/bandwidth-override/output.mpd b/packager/app/test/testdata/bandwidth-override/output.mpd index dd59d81a02..21e779f058 100644 --- a/packager/app/test/testdata/bandwidth-override/output.mpd +++ b/packager/app/test/testdata/bandwidth-override/output.mpd @@ -2,7 +2,7 @@ - + bear-640x360-audio.mp4 diff --git a/packager/app/test/testdata/dash-label/output.mpd b/packager/app/test/testdata/dash-label/output.mpd index 537849bb38..7bcd23108f 100644 --- a/packager/app/test/testdata/dash-label/output.mpd +++ b/packager/app/test/testdata/dash-label/output.mpd @@ -11,7 +11,7 @@ - + diff --git a/packager/app/test/testdata/ec3-and-hls-single-segment-mp4-encrypted/output.m3u8 b/packager/app/test/testdata/ec3-and-hls-single-segment-mp4-encrypted/output.m3u8 index 7ec849768f..5db89137c5 100644 --- a/packager/app/test/testdata/ec3-and-hls-single-segment-mp4-encrypted/output.m3u8 +++ b/packager/app/test/testdata/ec3-and-hls-single-segment-mp4-encrypted/output.m3u8 @@ -5,7 +5,7 @@ #EXT-X-MEDIA:TYPE=AUDIO,URI="bear-640x360-ec3-audio.m3u8",GROUP-ID="default-audio-group",NAME="stream_0",DEFAULT=NO,AUTOSELECT=YES,CHANNELS="2" -#EXT-X-STREAM-INF:BANDWIDTH=1174214,AVERAGE-BANDWIDTH=1061802,CODECS="avc1.64001e,ec-3",RESOLUTION=640x360,FRAME-RATE=9.990,AUDIO="default-audio-group",CLOSED-CAPTIONS=NONE +#EXT-X-STREAM-INF:BANDWIDTH=1174214,AVERAGE-BANDWIDTH=1061802,CODECS="avc1.64001e,ec-3",RESOLUTION=640x360,FRAME-RATE=29.970,AUDIO="default-audio-group",CLOSED-CAPTIONS=NONE bear-640x360-ec3-video.m3u8 #EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=218705,AVERAGE-BANDWIDTH=159315,CODECS="avc1.64001e",RESOLUTION=640x360,CLOSED-CAPTIONS=NONE,URI="bear-640x360-ec3-video-iframe.m3u8" diff --git a/packager/app/test/testdata/encryption-and-ad-cues-and-dash-trick-play/output.mpd b/packager/app/test/testdata/encryption-and-ad-cues-and-dash-trick-play/output.mpd index 7cbfc7ec85..0ec3c23e7c 100644 --- a/packager/app/test/testdata/encryption-and-ad-cues-and-dash-trick-play/output.mpd +++ b/packager/app/test/testdata/encryption-and-ad-cues-and-dash-trick-play/output.mpd @@ -2,7 +2,7 @@ - + AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA== @@ -42,7 +42,7 @@ - + AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA== diff --git a/packager/app/test/testdata/encryption-and-ad-cues-split-content/output.mpd b/packager/app/test/testdata/encryption-and-ad-cues-split-content/output.mpd index d3f3269f48..74006ed7b4 100644 --- a/packager/app/test/testdata/encryption-and-ad-cues-split-content/output.mpd +++ b/packager/app/test/testdata/encryption-and-ad-cues-split-content/output.mpd @@ -2,7 +2,7 @@ - + AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA== @@ -29,7 +29,7 @@ - + AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA== diff --git a/packager/app/test/testdata/encryption-and-ad-cues/output.mpd b/packager/app/test/testdata/encryption-and-ad-cues/output.mpd index 92231ba2f5..50050924c6 100644 --- a/packager/app/test/testdata/encryption-and-ad-cues/output.mpd +++ b/packager/app/test/testdata/encryption-and-ad-cues/output.mpd @@ -2,7 +2,7 @@ - + AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA== @@ -29,7 +29,7 @@ - + AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA== diff --git a/packager/app/test/testdata/encryption-and-no-clear-lead/output.mpd b/packager/app/test/testdata/encryption-and-no-clear-lead/output.mpd index 494ba28b60..99574347e4 100644 --- a/packager/app/test/testdata/encryption-and-no-clear-lead/output.mpd +++ b/packager/app/test/testdata/encryption-and-no-clear-lead/output.mpd @@ -2,7 +2,7 @@ - + AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA== diff --git a/packager/app/test/testdata/encryption-and-no-pssh-in-stream/output.mpd b/packager/app/test/testdata/encryption-and-no-pssh-in-stream/output.mpd index 0dc8b94c8d..377e3198f8 100644 --- a/packager/app/test/testdata/encryption-and-no-pssh-in-stream/output.mpd +++ b/packager/app/test/testdata/encryption-and-no-pssh-in-stream/output.mpd @@ -2,7 +2,7 @@ - + AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA== diff --git a/packager/app/test/testdata/encryption-and-non-dash-if-iop/output.mpd b/packager/app/test/testdata/encryption-and-non-dash-if-iop/output.mpd index cf597e8577..9b0a75df76 100644 --- a/packager/app/test/testdata/encryption-and-non-dash-if-iop/output.mpd +++ b/packager/app/test/testdata/encryption-and-non-dash-if-iop/output.mpd @@ -2,7 +2,7 @@ - + diff --git a/packager/app/test/testdata/encryption-and-output-media-info-and-mpd-from-media-info-segmentlist/output.mpd b/packager/app/test/testdata/encryption-and-output-media-info-and-mpd-from-media-info-segmentlist/output.mpd index 4c04f2a6e0..26636f0451 100644 --- a/packager/app/test/testdata/encryption-and-output-media-info-and-mpd-from-media-info-segmentlist/output.mpd +++ b/packager/app/test/testdata/encryption-and-output-media-info-and-mpd-from-media-info-segmentlist/output.mpd @@ -2,7 +2,7 @@ - + AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA== diff --git a/packager/app/test/testdata/encryption-and-trick-play/output.mpd b/packager/app/test/testdata/encryption-and-trick-play/output.mpd index 1553fb6375..4d289f0442 100644 --- a/packager/app/test/testdata/encryption-and-trick-play/output.mpd +++ b/packager/app/test/testdata/encryption-and-trick-play/output.mpd @@ -2,7 +2,7 @@ - + AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA== diff --git a/packager/app/test/testdata/encryption-and-two-trick-plays/output.mpd b/packager/app/test/testdata/encryption-and-two-trick-plays/output.mpd index d0023219bb..88e1a8ef0c 100644 --- a/packager/app/test/testdata/encryption-and-two-trick-plays/output.mpd +++ b/packager/app/test/testdata/encryption-and-two-trick-plays/output.mpd @@ -2,7 +2,7 @@ - + AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA== @@ -27,7 +27,7 @@ - + AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA== @@ -39,7 +39,7 @@ - + bear-640x360-video-trick_play_factor_2.mp4 diff --git a/packager/app/test/testdata/encryption-cbc-1/output.mpd b/packager/app/test/testdata/encryption-cbc-1/output.mpd index c54248a533..1f73d8372f 100644 --- a/packager/app/test/testdata/encryption-cbc-1/output.mpd +++ b/packager/app/test/testdata/encryption-cbc-1/output.mpd @@ -2,7 +2,7 @@ - + AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA== diff --git a/packager/app/test/testdata/encryption-cbcs-with-full-protection/output.mpd b/packager/app/test/testdata/encryption-cbcs-with-full-protection/output.mpd index 2686f8be43..bd2d40a560 100644 --- a/packager/app/test/testdata/encryption-cbcs-with-full-protection/output.mpd +++ b/packager/app/test/testdata/encryption-cbcs-with-full-protection/output.mpd @@ -2,7 +2,7 @@ - + AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA== diff --git a/packager/app/test/testdata/encryption-cbcs/output.mpd b/packager/app/test/testdata/encryption-cbcs/output.mpd index 2686f8be43..bd2d40a560 100644 --- a/packager/app/test/testdata/encryption-cbcs/output.mpd +++ b/packager/app/test/testdata/encryption-cbcs/output.mpd @@ -2,7 +2,7 @@ - + AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA== diff --git a/packager/app/test/testdata/encryption-cens/output.mpd b/packager/app/test/testdata/encryption-cens/output.mpd index 7637e41a5d..d73a8c7636 100644 --- a/packager/app/test/testdata/encryption-cens/output.mpd +++ b/packager/app/test/testdata/encryption-cens/output.mpd @@ -2,7 +2,7 @@ - + AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA== diff --git a/packager/app/test/testdata/encryption-multi-keys-with-stream-label/output.mpd b/packager/app/test/testdata/encryption-multi-keys-with-stream-label/output.mpd index dd35a56c54..eb7f66b390 100644 --- a/packager/app/test/testdata/encryption-multi-keys-with-stream-label/output.mpd +++ b/packager/app/test/testdata/encryption-multi-keys-with-stream-label/output.mpd @@ -2,7 +2,7 @@ - + AAAARHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAIQERITFBUWFxgZICEiIyQlICEiIyQlJicoKTAxMjM0NQAAAAA= diff --git a/packager/app/test/testdata/encryption-multi-keys/output.mpd b/packager/app/test/testdata/encryption-multi-keys/output.mpd index 96cde0c744..a11a0e0d49 100644 --- a/packager/app/test/testdata/encryption-multi-keys/output.mpd +++ b/packager/app/test/testdata/encryption-multi-keys/output.mpd @@ -2,7 +2,7 @@ - + AAAARHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAIQERITFBUWFxgZICEiIyQlICEiIyQlJicoKTAxMjM0NQAAAAA= diff --git a/packager/app/test/testdata/encryption-of-only-video-stream/output.mpd b/packager/app/test/testdata/encryption-of-only-video-stream/output.mpd index cc004536d5..ad04e1a117 100644 --- a/packager/app/test/testdata/encryption-of-only-video-stream/output.mpd +++ b/packager/app/test/testdata/encryption-of-only-video-stream/output.mpd @@ -2,7 +2,7 @@ - + bear-640x360-audio-skip_encryption.mp4 diff --git a/packager/app/test/testdata/encryption-using-explicit-pssh/output.mpd b/packager/app/test/testdata/encryption-using-explicit-pssh/output.mpd index 1b40fefc95..4e0cddb507 100644 --- a/packager/app/test/testdata/encryption-using-explicit-pssh/output.mpd +++ b/packager/app/test/testdata/encryption-using-explicit-pssh/output.mpd @@ -2,7 +2,7 @@ - + AAAAIHBzc2gAAAAAEHfv7MCyTQKs4zweUuL7SwAAAAA= diff --git a/packager/app/test/testdata/encryption-using-fixed-key/output.mpd b/packager/app/test/testdata/encryption-using-fixed-key/output.mpd index 3d496a56ce..53a9ffb759 100644 --- a/packager/app/test/testdata/encryption-using-fixed-key/output.mpd +++ b/packager/app/test/testdata/encryption-using-fixed-key/output.mpd @@ -2,7 +2,7 @@ - + AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA== diff --git a/packager/app/test/testdata/encryption-with-multi-drms/output.mpd b/packager/app/test/testdata/encryption-with-multi-drms/output.mpd index aed997af74..9ec1468930 100644 --- a/packager/app/test/testdata/encryption-with-multi-drms/output.mpd +++ b/packager/app/test/testdata/encryption-with-multi-drms/output.mpd @@ -2,7 +2,7 @@ - + AAACJnBzc2gAAAAAmgTweZhAQoarkuZb4IhflQAAAgYGAgAAAQABAPwBPABXAFIATQBIAEUAQQBEAEUAUgAgAHgAbQBsAG4AcwA9ACIAaAB0AHQAcAA6AC8ALwBzAGMAaABlAG0AYQBzAC4AbQBpAGMAcgBvAHMAbwBmAHQALgBjAG8AbQAvAEQAUgBNAC8AMgAwADAANwAvADAAMwAvAFAAbABhAHkAUgBlAGEAZAB5AEgAZQBhAGQAZQByACIAIAB2AGUAcgBzAGkAbwBuAD0AIgA0AC4AMAAuADAALgAwACIAPgA8AEQAQQBUAEEAPgA8AFAAUgBPAFQARQBDAFQASQBOAEYATwA+ADwASwBFAFkATABFAE4APgAxADYAPAAvAEsARQBZAEwARQBOAD4APABBAEwARwBJAEQAPgBBAEUAUwBDAFQAUgA8AC8AQQBMAEcASQBEAD4APAAvAFAAUgBPAFQARQBDAFQASQBOAEYATwA+ADwASwBJAEQAPgBOAEQATQB5AE0AVABZADEATwBEAGMANQBNAEQARQB5AE0AegBRADEATgBnAD0APQA8AC8ASwBJAEQAPgA8AEMASABFAEMASwBTAFUATQA+AGwANQBMAG8AVQBnAEsAOQBLAEMAZwA9ADwALwBDAEgARQBDAEsAUwBVAE0APgA8AC8ARABBAFQAQQA+ADwALwBXAFIATQBIAEUAQQBEAEUAUgA+AA== diff --git a/packager/app/test/testdata/encryption-with-multi-drms/stream_0.m3u8 b/packager/app/test/testdata/encryption-with-multi-drms/stream_0.m3u8 index 1eecba5c11..5f5caf3af6 100644 --- a/packager/app/test/testdata/encryption-with-multi-drms/stream_0.m3u8 +++ b/packager/app/test/testdata/encryption-with-multi-drms/stream_0.m3u8 @@ -4,6 +4,7 @@ #EXT-X-TARGETDURATION:2 #EXT-X-PLAYLIST-TYPE:VOD #EXT-X-MAP:URI="bear-640x360-audio.mp4",BYTERANGE="1568@0" +#EXT-X-KEY:METHOD=SAMPLE-AES-CTR,URI="data:text/plain;charset=UTF-16;base64,BgIAAAEAAQD8ATwAVwBSAE0ASABFAEEARABFAFIAIAB4AG0AbABuAHMAPQAiAGgAdAB0AHAAOgAvAC8AcwBjAGgAZQBtAGEAcwAuAG0AaQBjAHIAbwBzAG8AZgB0AC4AYwBvAG0ALwBEAFIATQAvADIAMAAwADcALwAwADMALwBQAGwAYQB5AFIAZQBhAGQAeQBIAGUAYQBkAGUAcgAiACAAdgBlAHIAcwBpAG8AbgA9ACIANAAuADAALgAwAC4AMAAiAD4APABEAEEAVABBAD4APABQAFIATwBUAEUAQwBUAEkATgBGAE8APgA8AEsARQBZAEwARQBOAD4AMQA2ADwALwBLAEUAWQBMAEUATgA+ADwAQQBMAEcASQBEAD4AQQBFAFMAQwBUAFIAPAAvAEEATABHAEkARAA+ADwALwBQAFIATwBUAEUAQwBUAEkATgBGAE8APgA8AEsASQBEAD4ATgBEAE0AeQBNAFQAWQAxAE8ARABjADUATQBEAEUAeQBNAHoAUQAxAE4AZwA9AD0APAAvAEsASQBEAD4APABDAEgARQBDAEsAUwBVAE0APgBsADUATABvAFUAZwBLADkASwBDAGcAPQA8AC8AQwBIAEUAQwBLAFMAVQBNAD4APAAvAEQAQQBUAEEAPgA8AC8AVwBSAE0ASABFAEEARABFAFIAPgA=",KEYFORMATVERSIONS="1",KEYFORMAT="com.microsoft.playready" #EXT-X-KEY:METHOD=SAMPLE-AES-CTR,URI="data:text/plain;base64,AAAAOHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABgSEDEyMzQ1Njc4OTAxMjM0NTZI49yVmwY=",KEYID=0x31323334353637383930313233343536,KEYFORMATVERSIONS="1",KEYFORMAT="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed" #EXT-X-KEY:METHOD=SAMPLE-AES-CTR,URI="skd://www.license.com/getkey?KeyId=31323334-3536-3738-3930-313233343536",KEYFORMATVERSIONS="1",KEYFORMAT="com.apple.streamingkeydelivery" #EXTINF:1.022, diff --git a/packager/app/test/testdata/encryption-with-multi-drms/stream_1.m3u8 b/packager/app/test/testdata/encryption-with-multi-drms/stream_1.m3u8 index 6b261d7f39..443fdfa39a 100644 --- a/packager/app/test/testdata/encryption-with-multi-drms/stream_1.m3u8 +++ b/packager/app/test/testdata/encryption-with-multi-drms/stream_1.m3u8 @@ -4,6 +4,7 @@ #EXT-X-TARGETDURATION:2 #EXT-X-PLAYLIST-TYPE:VOD #EXT-X-MAP:URI="bear-640x360-video.mp4",BYTERANGE="1692@0" +#EXT-X-KEY:METHOD=SAMPLE-AES-CTR,URI="data:text/plain;charset=UTF-16;base64,BgIAAAEAAQD8ATwAVwBSAE0ASABFAEEARABFAFIAIAB4AG0AbABuAHMAPQAiAGgAdAB0AHAAOgAvAC8AcwBjAGgAZQBtAGEAcwAuAG0AaQBjAHIAbwBzAG8AZgB0AC4AYwBvAG0ALwBEAFIATQAvADIAMAAwADcALwAwADMALwBQAGwAYQB5AFIAZQBhAGQAeQBIAGUAYQBkAGUAcgAiACAAdgBlAHIAcwBpAG8AbgA9ACIANAAuADAALgAwAC4AMAAiAD4APABEAEEAVABBAD4APABQAFIATwBUAEUAQwBUAEkATgBGAE8APgA8AEsARQBZAEwARQBOAD4AMQA2ADwALwBLAEUAWQBMAEUATgA+ADwAQQBMAEcASQBEAD4AQQBFAFMAQwBUAFIAPAAvAEEATABHAEkARAA+ADwALwBQAFIATwBUAEUAQwBUAEkATgBGAE8APgA8AEsASQBEAD4ATgBEAE0AeQBNAFQAWQAxAE8ARABjADUATQBEAEUAeQBNAHoAUQAxAE4AZwA9AD0APAAvAEsASQBEAD4APABDAEgARQBDAEsAUwBVAE0APgBsADUATABvAFUAZwBLADkASwBDAGcAPQA8AC8AQwBIAEUAQwBLAFMAVQBNAD4APAAvAEQAQQBUAEEAPgA8AC8AVwBSAE0ASABFAEEARABFAFIAPgA=",KEYFORMATVERSIONS="1",KEYFORMAT="com.microsoft.playready" #EXT-X-KEY:METHOD=SAMPLE-AES-CTR,URI="data:text/plain;base64,AAAAOHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABgSEDEyMzQ1Njc4OTAxMjM0NTZI49yVmwY=",KEYID=0x31323334353637383930313233343536,KEYFORMATVERSIONS="1",KEYFORMAT="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed" #EXT-X-KEY:METHOD=SAMPLE-AES-CTR,URI="skd://www.license.com/getkey?KeyId=31323334-3536-3738-3930-313233343536",KEYFORMATVERSIONS="1",KEYFORMAT="com.apple.streamingkeydelivery" #EXTINF:1.001, diff --git a/packager/app/test/testdata/encryption/output.mpd b/packager/app/test/testdata/encryption/output.mpd index 3d496a56ce..53a9ffb759 100644 --- a/packager/app/test/testdata/encryption/output.mpd +++ b/packager/app/test/testdata/encryption/output.mpd @@ -2,7 +2,7 @@ - + AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA== diff --git a/packager/app/test/testdata/forced-commandline-ordering-ttml/bear-640x360-audio.mp4 b/packager/app/test/testdata/forced-commandline-ordering-ttml/bear-640x360-audio.mp4 new file mode 100644 index 0000000000..10077f9af8 Binary files /dev/null and b/packager/app/test/testdata/forced-commandline-ordering-ttml/bear-640x360-audio.mp4 differ diff --git a/packager/app/test/testdata/forced-commandline-ordering-ttml/bear-640x360-video.mp4 b/packager/app/test/testdata/forced-commandline-ordering-ttml/bear-640x360-video.mp4 new file mode 100644 index 0000000000..de83807979 Binary files /dev/null and b/packager/app/test/testdata/forced-commandline-ordering-ttml/bear-640x360-video.mp4 differ diff --git a/packager/app/test/testdata/forced-commandline-ordering-ttml/bear-english-text.ttml b/packager/app/test/testdata/forced-commandline-ordering-ttml/bear-english-text.ttml new file mode 100644 index 0000000000..978056a2ab --- /dev/null +++ b/packager/app/test/testdata/forced-commandline-ordering-ttml/bear-english-text.ttml @@ -0,0 +1,9 @@ + + + +
+

Yup, that's a bear, eh.

+

He 's... um... doing bear-like stuff.

+
+ +
diff --git a/packager/app/test/testdata/forced-commandline-ordering-ttml/output.mpd b/packager/app/test/testdata/forced-commandline-ordering-ttml/output.mpd new file mode 100644 index 0000000000..b53225c868 --- /dev/null +++ b/packager/app/test/testdata/forced-commandline-ordering-ttml/output.mpd @@ -0,0 +1,28 @@ + + + + + + + bear-640x360-video.mp4 + + + + + + + + + bear-640x360-audio.mp4 + + + + + + + + bear-english-text.ttml + + + + diff --git a/packager/app/test/testdata/forced-commandline-ordering/output.mpd b/packager/app/test/testdata/forced-commandline-ordering/output.mpd index c6f9030b45..8e1683a3b7 100644 --- a/packager/app/test/testdata/forced-commandline-ordering/output.mpd +++ b/packager/app/test/testdata/forced-commandline-ordering/output.mpd @@ -8,7 +8,7 @@ bear-english-text.vtt
- + bear-640x360-audio.mp4 diff --git a/packager/app/test/testdata/forced-subtitle/output.mpd b/packager/app/test/testdata/forced-subtitle/output.mpd index 28ccba2d4e..e557fb28ea 100644 --- a/packager/app/test/testdata/forced-subtitle/output.mpd +++ b/packager/app/test/testdata/forced-subtitle/output.mpd @@ -2,7 +2,7 @@ - + bear-640x360-audio.mp4 diff --git a/packager/app/test/testdata/hls-only-dash-only-captions/output.mpd b/packager/app/test/testdata/hls-only-dash-only-captions/output.mpd index 6b5413197d..b760e778ee 100644 --- a/packager/app/test/testdata/hls-only-dash-only-captions/output.mpd +++ b/packager/app/test/testdata/hls-only-dash-only-captions/output.mpd @@ -2,7 +2,7 @@ - + diff --git a/packager/app/test/testdata/hls-only-dash-only/output.mpd b/packager/app/test/testdata/hls-only-dash-only/output.mpd index 038d88a0cf..55e6c71d2b 100644 --- a/packager/app/test/testdata/hls-only-dash-only/output.mpd +++ b/packager/app/test/testdata/hls-only-dash-only/output.mpd @@ -2,7 +2,7 @@ - + bear-640x360-audio.mp4 diff --git a/packager/app/test/testdata/hls-single-segment-ts/bear-640x360-audio.m3u8 b/packager/app/test/testdata/hls-single-segment-ts/bear-640x360-audio.m3u8 new file mode 100644 index 0000000000..d7c015a03f --- /dev/null +++ b/packager/app/test/testdata/hls-single-segment-ts/bear-640x360-audio.m3u8 @@ -0,0 +1,15 @@ +#EXTM3U +#EXT-X-VERSION:6 +## Generated with https://github.com/shaka-project/shaka-packager version -- +#EXT-X-TARGETDURATION:2 +#EXT-X-PLAYLIST-TYPE:VOD +#EXTINF:0.975, +#EXT-X-BYTERANGE:23312@0 +bear-640x360-audio.ts +#EXTINF:0.998, +#EXT-X-BYTERANGE:24252 +bear-640x360-audio.ts +#EXTINF:0.789, +#EXT-X-BYTERANGE:17296 +bear-640x360-audio.ts +#EXT-X-ENDLIST diff --git a/packager/app/test/testdata/hls-single-segment-ts/bear-640x360-audio.ts b/packager/app/test/testdata/hls-single-segment-ts/bear-640x360-audio.ts new file mode 100644 index 0000000000..eaed17eef1 Binary files /dev/null and b/packager/app/test/testdata/hls-single-segment-ts/bear-640x360-audio.ts differ diff --git a/packager/app/test/testdata/hls-single-segment-ts/bear-640x360-video-iframe.m3u8 b/packager/app/test/testdata/hls-single-segment-ts/bear-640x360-video-iframe.m3u8 new file mode 100644 index 0000000000..adfc872a9d --- /dev/null +++ b/packager/app/test/testdata/hls-single-segment-ts/bear-640x360-video-iframe.m3u8 @@ -0,0 +1,16 @@ +#EXTM3U +#EXT-X-VERSION:6 +## Generated with https://github.com/shaka-project/shaka-packager version -- +#EXT-X-TARGETDURATION:2 +#EXT-X-PLAYLIST-TYPE:VOD +#EXT-X-I-FRAMES-ONLY +#EXTINF:1.001, +#EXT-X-BYTERANGE:15604@376 +bear-640x360-video.ts +#EXTINF:1.001, +#EXT-X-BYTERANGE:18236@105656 +bear-640x360-video.ts +#EXTINF:0.734, +#EXT-X-BYTERANGE:19928@233684 +bear-640x360-video.ts +#EXT-X-ENDLIST diff --git a/packager/app/test/testdata/hls-single-segment-ts/bear-640x360-video.m3u8 b/packager/app/test/testdata/hls-single-segment-ts/bear-640x360-video.m3u8 new file mode 100644 index 0000000000..a1cd8ce3d8 --- /dev/null +++ b/packager/app/test/testdata/hls-single-segment-ts/bear-640x360-video.m3u8 @@ -0,0 +1,15 @@ +#EXTM3U +#EXT-X-VERSION:6 +## Generated with https://github.com/shaka-project/shaka-packager version -- +#EXT-X-TARGETDURATION:2 +#EXT-X-PLAYLIST-TYPE:VOD +#EXTINF:1.001, +#EXT-X-BYTERANGE:105280@0 +bear-640x360-video.ts +#EXTINF:1.001, +#EXT-X-BYTERANGE:128028 +bear-640x360-video.ts +#EXTINF:0.734, +#EXT-X-BYTERANGE:84600 +bear-640x360-video.ts +#EXT-X-ENDLIST diff --git a/packager/app/test/testdata/hls-single-segment-ts/bear-640x360-video.ts b/packager/app/test/testdata/hls-single-segment-ts/bear-640x360-video.ts new file mode 100644 index 0000000000..3e52b07d4e Binary files /dev/null and b/packager/app/test/testdata/hls-single-segment-ts/bear-640x360-video.ts differ diff --git a/packager/app/test/testdata/hls-single-segment-ts/output.m3u8 b/packager/app/test/testdata/hls-single-segment-ts/output.m3u8 new file mode 100644 index 0000000000..b827ca1dc8 --- /dev/null +++ b/packager/app/test/testdata/hls-single-segment-ts/output.m3u8 @@ -0,0 +1,11 @@ +#EXTM3U +## Generated with https://github.com/shaka-project/shaka-packager version -- + +#EXT-X-INDEPENDENT-SEGMENTS + +#EXT-X-MEDIA:TYPE=AUDIO,URI="bear-640x360-audio.m3u8",GROUP-ID="default-audio-group",NAME="stream_0",DEFAULT=NO,AUTOSELECT=YES,CHANNELS="2" + +#EXT-X-STREAM-INF:BANDWIDTH=1217520,AVERAGE-BANDWIDTH=1117320,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,FRAME-RATE=29.970,AUDIO="default-audio-group",CLOSED-CAPTIONS=NONE +bear-640x360-video.m3u8 + +#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=217180,AVERAGE-BANDWIDTH=157213,CODECS="avc1.64001e",RESOLUTION=640x360,CLOSED-CAPTIONS=NONE,URI="bear-640x360-video-iframe.m3u8" diff --git a/packager/app/test/testdata/live-profile-and-encryption-and-mult-files/output.mpd b/packager/app/test/testdata/live-profile-and-encryption-and-mult-files/output.mpd index c5f284b6df..9437851ec1 100644 --- a/packager/app/test/testdata/live-profile-and-encryption-and-mult-files/output.mpd +++ b/packager/app/test/testdata/live-profile-and-encryption-and-mult-files/output.mpd @@ -2,7 +2,7 @@ - + AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA== diff --git a/packager/app/test/testdata/live-profile-and-encryption-and-non-dash-if-iop/output.mpd b/packager/app/test/testdata/live-profile-and-encryption-and-non-dash-if-iop/output.mpd index d62981b56a..cf50fffd4e 100644 --- a/packager/app/test/testdata/live-profile-and-encryption-and-non-dash-if-iop/output.mpd +++ b/packager/app/test/testdata/live-profile-and-encryption-and-non-dash-if-iop/output.mpd @@ -2,7 +2,7 @@ - + diff --git a/packager/app/test/testdata/live-profile-and-encryption/output.mpd b/packager/app/test/testdata/live-profile-and-encryption/output.mpd index 81b06c5f32..fba35c610f 100644 --- a/packager/app/test/testdata/live-profile-and-encryption/output.mpd +++ b/packager/app/test/testdata/live-profile-and-encryption/output.mpd @@ -2,7 +2,7 @@ - + AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA== diff --git a/packager/app/test/testdata/live-profile-and-key-rotation-and-no-pssh-in-stream/output.mpd b/packager/app/test/testdata/live-profile-and-key-rotation-and-no-pssh-in-stream/output.mpd index 4befe541d3..2ed6d6621e 100644 --- a/packager/app/test/testdata/live-profile-and-key-rotation-and-no-pssh-in-stream/output.mpd +++ b/packager/app/test/testdata/live-profile-and-key-rotation-and-no-pssh-in-stream/output.mpd @@ -2,7 +2,7 @@ - + diff --git a/packager/app/test/testdata/live-profile-and-key-rotation-and-non-dash-if-iop/output.mpd b/packager/app/test/testdata/live-profile-and-key-rotation-and-non-dash-if-iop/output.mpd index 4a27746c50..efcb25c193 100644 --- a/packager/app/test/testdata/live-profile-and-key-rotation-and-non-dash-if-iop/output.mpd +++ b/packager/app/test/testdata/live-profile-and-key-rotation-and-non-dash-if-iop/output.mpd @@ -2,7 +2,7 @@ - + diff --git a/packager/app/test/testdata/live-profile-and-key-rotation-cbcs/output.mpd b/packager/app/test/testdata/live-profile-and-key-rotation-cbcs/output.mpd index 5f7ae9bdfd..eeec8f59c8 100644 --- a/packager/app/test/testdata/live-profile-and-key-rotation-cbcs/output.mpd +++ b/packager/app/test/testdata/live-profile-and-key-rotation-cbcs/output.mpd @@ -2,7 +2,7 @@ - + diff --git a/packager/app/test/testdata/live-profile-and-key-rotation/output.mpd b/packager/app/test/testdata/live-profile-and-key-rotation/output.mpd index 02fb8c83f5..78be429746 100644 --- a/packager/app/test/testdata/live-profile-and-key-rotation/output.mpd +++ b/packager/app/test/testdata/live-profile-and-key-rotation/output.mpd @@ -2,7 +2,7 @@ - + diff --git a/packager/app/test/testdata/live-profile-with-webm/output.m3u8 b/packager/app/test/testdata/live-profile-with-webm/output.m3u8 index 57ad801cfc..cfcfc457ea 100644 --- a/packager/app/test/testdata/live-profile-with-webm/output.m3u8 +++ b/packager/app/test/testdata/live-profile-with-webm/output.m3u8 @@ -5,5 +5,5 @@ #EXT-X-MEDIA:TYPE=AUDIO,URI="stream_0.m3u8",GROUP-ID="default-audio-group",NAME="stream_0",DEFAULT=NO,AUTOSELECT=YES,CHANNELS="2" -#EXT-X-STREAM-INF:BANDWIDTH=556353,AVERAGE-BANDWIDTH=412719,CODECS="vp08.00.10.08.01.02.02.02.00,vorbis",RESOLUTION=640x360,FRAME-RATE=30.303,AUDIO="default-audio-group",CLOSED-CAPTIONS=NONE +#EXT-X-STREAM-INF:BANDWIDTH=556353,AVERAGE-BANDWIDTH=412719,CODECS="vp08.00.10.08.01.02.02.02.00,vorbis",RESOLUTION=640x360,FRAME-RATE=29.412,AUDIO="default-audio-group",CLOSED-CAPTIONS=NONE stream_1.m3u8 diff --git a/packager/app/test/testdata/live-profile-with-webm/output.mpd b/packager/app/test/testdata/live-profile-with-webm/output.mpd index c41814ca15..ea922c3284 100644 --- a/packager/app/test/testdata/live-profile-with-webm/output.mpd +++ b/packager/app/test/testdata/live-profile-with-webm/output.mpd @@ -14,7 +14,7 @@ - + diff --git a/packager/app/test/testdata/live-profile/output.mpd b/packager/app/test/testdata/live-profile/output.mpd index 03bf36387f..5a2ac4c9f4 100644 --- a/packager/app/test/testdata/live-profile/output.mpd +++ b/packager/app/test/testdata/live-profile/output.mpd @@ -2,7 +2,7 @@ - + diff --git a/packager/app/test/testdata/live-static-profile-with-time-in-segment-name/output.mpd b/packager/app/test/testdata/live-static-profile-with-time-in-segment-name/output.mpd index 8d0ce66cf8..d584f61eb7 100644 --- a/packager/app/test/testdata/live-static-profile-with-time-in-segment-name/output.mpd +++ b/packager/app/test/testdata/live-static-profile-with-time-in-segment-name/output.mpd @@ -2,7 +2,7 @@ - + diff --git a/packager/app/test/testdata/live-static-profile/output.mpd b/packager/app/test/testdata/live-static-profile/output.mpd index 2224ac20fe..1673295b61 100644 --- a/packager/app/test/testdata/live-static-profile/output.mpd +++ b/packager/app/test/testdata/live-static-profile/output.mpd @@ -2,7 +2,7 @@ - + diff --git a/packager/app/test/testdata/mp4-trailing-moov/output.mpd b/packager/app/test/testdata/mp4-trailing-moov/output.mpd index 83b418247f..c1139bd564 100644 --- a/packager/app/test/testdata/mp4-trailing-moov/output.mpd +++ b/packager/app/test/testdata/mp4-trailing-moov/output.mpd @@ -2,7 +2,7 @@ - + bear-640x360-trailing-moov-audio.mp4 diff --git a/packager/app/test/testdata/opus-vp9-mp4-with-encryption/output.mpd b/packager/app/test/testdata/opus-vp9-mp4-with-encryption/output.mpd index a9e318ea99..42125e65fd 100644 --- a/packager/app/test/testdata/opus-vp9-mp4-with-encryption/output.mpd +++ b/packager/app/test/testdata/opus-vp9-mp4-with-encryption/output.mpd @@ -15,7 +15,7 @@
- + AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA== diff --git a/packager/app/test/testdata/segmented-ttml-mp4/bear-english-text-1.m4s b/packager/app/test/testdata/segmented-ttml-mp4/bear-english-text-1.m4s index 1de1cd08d3..319e9a7cdd 100644 Binary files a/packager/app/test/testdata/segmented-ttml-mp4/bear-english-text-1.m4s and b/packager/app/test/testdata/segmented-ttml-mp4/bear-english-text-1.m4s differ diff --git a/packager/app/test/testdata/segmented-ttml-mp4/bear-english-text-2.m4s b/packager/app/test/testdata/segmented-ttml-mp4/bear-english-text-2.m4s index 29351536a0..ac847dfc25 100644 Binary files a/packager/app/test/testdata/segmented-ttml-mp4/bear-english-text-2.m4s and b/packager/app/test/testdata/segmented-ttml-mp4/bear-english-text-2.m4s differ diff --git a/packager/app/test/testdata/segmented-ttml-mp4/bear-english-text-3.m4s b/packager/app/test/testdata/segmented-ttml-mp4/bear-english-text-3.m4s index 8b81e6d983..db5c1dd6f6 100644 Binary files a/packager/app/test/testdata/segmented-ttml-mp4/bear-english-text-3.m4s and b/packager/app/test/testdata/segmented-ttml-mp4/bear-english-text-3.m4s differ diff --git a/packager/app/test/testdata/segmented-ttml-mp4/bear-english-text-4.m4s b/packager/app/test/testdata/segmented-ttml-mp4/bear-english-text-4.m4s index 9c86b0e5e2..32792d0cfc 100644 Binary files a/packager/app/test/testdata/segmented-ttml-mp4/bear-english-text-4.m4s and b/packager/app/test/testdata/segmented-ttml-mp4/bear-english-text-4.m4s differ diff --git a/packager/app/test/testdata/segmented-ttml-mp4/bear-english-text-5.m4s b/packager/app/test/testdata/segmented-ttml-mp4/bear-english-text-5.m4s index e0dc6e3b8d..224ccd2c68 100644 Binary files a/packager/app/test/testdata/segmented-ttml-mp4/bear-english-text-5.m4s and b/packager/app/test/testdata/segmented-ttml-mp4/bear-english-text-5.m4s differ diff --git a/packager/app/test/testdata/segmented-ttml-mp4/output.mpd b/packager/app/test/testdata/segmented-ttml-mp4/output.mpd index 35ddebf4a1..bc8c0c7591 100644 --- a/packager/app/test/testdata/segmented-ttml-mp4/output.mpd +++ b/packager/app/test/testdata/segmented-ttml-mp4/output.mpd @@ -4,7 +4,7 @@ - + diff --git a/packager/app/test/testdata/segmented-ttml-text/bear-english-text-1.ttml b/packager/app/test/testdata/segmented-ttml-text/bear-english-text-1.ttml index 94a5092c9d..2bd77c0bed 100644 --- a/packager/app/test/testdata/segmented-ttml-text/bear-english-text-1.ttml +++ b/packager/app/test/testdata/segmented-ttml-text/bear-english-text-1.ttml @@ -1,6 +1,10 @@ - + + + + +

Yup, that's a bear, eh.

diff --git a/packager/app/test/testdata/segmented-ttml-text/bear-english-text-2.ttml b/packager/app/test/testdata/segmented-ttml-text/bear-english-text-2.ttml index 8048787ec7..10a7664c86 100644 --- a/packager/app/test/testdata/segmented-ttml-text/bear-english-text-2.ttml +++ b/packager/app/test/testdata/segmented-ttml-text/bear-english-text-2.ttml @@ -1,6 +1,10 @@ - + + + + +

He 's... um... doing bear-like stuff.

diff --git a/packager/app/test/testdata/segmented-ttml-text/bear-english-text-3.ttml b/packager/app/test/testdata/segmented-ttml-text/bear-english-text-3.ttml index 8048787ec7..10a7664c86 100644 --- a/packager/app/test/testdata/segmented-ttml-text/bear-english-text-3.ttml +++ b/packager/app/test/testdata/segmented-ttml-text/bear-english-text-3.ttml @@ -1,6 +1,10 @@ - + + + + +

He 's... um... doing bear-like stuff.

diff --git a/packager/app/test/testdata/segmented-ttml-text/bear-english-text-4.ttml b/packager/app/test/testdata/segmented-ttml-text/bear-english-text-4.ttml index 8048787ec7..10a7664c86 100644 --- a/packager/app/test/testdata/segmented-ttml-text/bear-english-text-4.ttml +++ b/packager/app/test/testdata/segmented-ttml-text/bear-english-text-4.ttml @@ -1,6 +1,10 @@ - + + + + +

He 's... um... doing bear-like stuff.

diff --git a/packager/app/test/testdata/segmented-ttml-text/bear-english-text-5.ttml b/packager/app/test/testdata/segmented-ttml-text/bear-english-text-5.ttml index 8048787ec7..10a7664c86 100644 --- a/packager/app/test/testdata/segmented-ttml-text/bear-english-text-5.ttml +++ b/packager/app/test/testdata/segmented-ttml-text/bear-english-text-5.ttml @@ -1,6 +1,10 @@ - + + + + +

He 's... um... doing bear-like stuff.

diff --git a/packager/app/test/testdata/segmented-ttml-text/output.mpd b/packager/app/test/testdata/segmented-ttml-text/output.mpd index 9b9e87e2ed..398aeeca60 100644 --- a/packager/app/test/testdata/segmented-ttml-text/output.mpd +++ b/packager/app/test/testdata/segmented-ttml-text/output.mpd @@ -4,7 +4,7 @@ - + diff --git a/packager/app/test/testdata/video-audio-ttml/output.mpd b/packager/app/test/testdata/video-audio-ttml/output.mpd index e1ef315e3e..9c87c4c893 100644 --- a/packager/app/test/testdata/video-audio-ttml/output.mpd +++ b/packager/app/test/testdata/video-audio-ttml/output.mpd @@ -2,12 +2,7 @@ - - - bear-english-text.ttml - - - + bear-640x360-audio.mp4 @@ -16,7 +11,7 @@ - + bear-640x360-video.mp4 @@ -24,5 +19,10 @@ + + + bear-english-text.ttml + + diff --git a/packager/app/test/testdata/video-audio-webvtt/output.mpd b/packager/app/test/testdata/video-audio-webvtt/output.mpd index b128b524e1..f681078a7e 100644 --- a/packager/app/test/testdata/video-audio-webvtt/output.mpd +++ b/packager/app/test/testdata/video-audio-webvtt/output.mpd @@ -2,7 +2,7 @@ - + bear-640x360-audio.mp4 diff --git a/packager/app/test/testdata/vp8-mp4-with-encryption/output.mpd b/packager/app/test/testdata/vp8-mp4-with-encryption/output.mpd index d5d1caa5ee..80dbde48de 100644 --- a/packager/app/test/testdata/vp8-mp4-with-encryption/output.mpd +++ b/packager/app/test/testdata/vp8-mp4-with-encryption/output.mpd @@ -2,7 +2,7 @@ - + AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA== diff --git a/packager/app/test/testdata/vp8-webm/output.mpd b/packager/app/test/testdata/vp8-webm/output.mpd index 08d7c36986..5eecef53b4 100644 --- a/packager/app/test/testdata/vp8-webm/output.mpd +++ b/packager/app/test/testdata/vp8-webm/output.mpd @@ -2,7 +2,7 @@ - + bear-640x360-video.webm diff --git a/packager/app/test/testdata/vp9-webm-with-blockgroup/output.mpd b/packager/app/test/testdata/vp9-webm-with-blockgroup/output.mpd index 4878206a73..c78caf168d 100644 --- a/packager/app/test/testdata/vp9-webm-with-blockgroup/output.mpd +++ b/packager/app/test/testdata/vp9-webm-with-blockgroup/output.mpd @@ -2,7 +2,7 @@ - + bear-vp9-blockgroup-video.webm diff --git a/packager/app/test/testdata/vp9-webm/output.mpd b/packager/app/test/testdata/vp9-webm/output.mpd index e87196a732..5e109a04ef 100644 --- a/packager/app/test/testdata/vp9-webm/output.mpd +++ b/packager/app/test/testdata/vp9-webm/output.mpd @@ -11,7 +11,7 @@ - + bear-320x240-vp9-opus-video.webm diff --git a/packager/app/test/testdata/vtt-text-to-mp4-with-ad-cues/output.mpd b/packager/app/test/testdata/vtt-text-to-mp4-with-ad-cues/output.mpd index 31b4ab7a99..75bf1c3bfe 100644 --- a/packager/app/test/testdata/vtt-text-to-mp4-with-ad-cues/output.mpd +++ b/packager/app/test/testdata/vtt-text-to-mp4-with-ad-cues/output.mpd @@ -2,7 +2,7 @@ - + @@ -35,7 +35,7 @@ - + diff --git a/packager/app/test/testdata/webm-subsample-encryption/output.mpd b/packager/app/test/testdata/webm-subsample-encryption/output.mpd index e700f5835a..a68d23d32c 100644 --- a/packager/app/test/testdata/webm-subsample-encryption/output.mpd +++ b/packager/app/test/testdata/webm-subsample-encryption/output.mpd @@ -2,7 +2,7 @@ - + AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA== diff --git a/packager/app/test/testdata/webm-vp9-full-sample-encryption/output.mpd b/packager/app/test/testdata/webm-vp9-full-sample-encryption/output.mpd index e700f5835a..a68d23d32c 100644 --- a/packager/app/test/testdata/webm-vp9-full-sample-encryption/output.mpd +++ b/packager/app/test/testdata/webm-vp9-full-sample-encryption/output.mpd @@ -2,7 +2,7 @@ - + AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA== diff --git a/packager/app/test/testdata/webm-with-encryption/output.mpd b/packager/app/test/testdata/webm-with-encryption/output.mpd index eebc46aba2..ee3390f948 100644 --- a/packager/app/test/testdata/webm-with-encryption/output.mpd +++ b/packager/app/test/testdata/webm-with-encryption/output.mpd @@ -2,7 +2,7 @@ - + AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA== diff --git a/packager/app/test/testdata/wvm-input-without-stripping-parameters-set-nalus/output.mpd b/packager/app/test/testdata/wvm-input-without-stripping-parameters-set-nalus/output.mpd index 55bcbce912..0107e5f763 100644 --- a/packager/app/test/testdata/wvm-input-without-stripping-parameters-set-nalus/output.mpd +++ b/packager/app/test/testdata/wvm-input-without-stripping-parameters-set-nalus/output.mpd @@ -16,7 +16,7 @@ - + bear-multi-configs-1.mp4 diff --git a/packager/app/test/testdata/wvm-input/output.mpd b/packager/app/test/testdata/wvm-input/output.mpd index b157fe5dd6..240810b322 100644 --- a/packager/app/test/testdata/wvm-input/output.mpd +++ b/packager/app/test/testdata/wvm-input/output.mpd @@ -16,7 +16,7 @@ - + bear-multi-configs-1.mp4 diff --git a/packager/file/http_file.cc b/packager/file/http_file.cc index e2a27e2d0d..b50fef6f07 100644 --- a/packager/file/http_file.cc +++ b/packager/file/http_file.cc @@ -372,6 +372,12 @@ void HttpFile::ThreadMain() { error_message); } + // In some cases it is possible that the server has already closed the + // connection without reading the request body. This can for example happen + // when the server responds with a non-successful status code. In this case we + // need to make sure to close the upload cache here, otherwise some other + // thread may block forever on Flush(). + upload_cache_.Close(); download_cache_.Close(); task_exit_event_.Notify(); } diff --git a/packager/fully-static.cmake b/packager/fully-static.cmake new file mode 100644 index 0000000000..3190b99ad6 --- /dev/null +++ b/packager/fully-static.cmake @@ -0,0 +1,27 @@ +# Copyright 2024 Google LLC. All rights reserved. +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd + +# Fully-static build settings. +if(FULLY_STATIC) + # This is the "object" version of mimalloc, as opposed to the library + # version. This is important for a static override of malloc and friends. + set(EXTRA_EXE_LIBRARIES $) + + # Keep the linker from searching for dynamic libraries. + set(CMAKE_LINK_SEARCH_START_STATIC OFF) + set(CMAKE_LINK_SEARCH_END_STATIC OFF) + + # Tell CMake not to plan to relink the executables, which wouldn't make sense + # in this context and causes CMake to fail at configure time when using a + # musl toolchain for static builds. + set(CMAKE_SKIP_BUILD_RPATH ON) + + # Set extra linker options necessary for fully static linking. These apply + # to all executables, which is critical when using a musl toolchain. Without + # applying these to all executables, we could create dynamic musl executables + # as intermediate outputs, which then could not run on a glibc host system. + add_link_options(-static-libgcc -static-libstdc++ -static) +endif() diff --git a/packager/hls/base/simple_hls_notifier.cc b/packager/hls/base/simple_hls_notifier.cc index 9b85f83bd1..2ab7087aa5 100644 --- a/packager/hls/base/simple_hls_notifier.cc +++ b/packager/hls/base/simple_hls_notifier.cc @@ -36,6 +36,7 @@ namespace hls { namespace { const char kUriBase64Prefix[] = "data:text/plain;base64,"; +const char kUriBase64Utf16Prefix[] = "data:text/plain;charset=UTF-16;base64,"; const char kUriFairPlayPrefix[] = "skd://"; const char kWidevineDashIfIopUUID[] = "urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"; @@ -57,6 +58,18 @@ bool IsFairPlaySystemId(const std::vector& system_id) { media::kFairPlaySystemId); } +bool IsLegacyFairPlaySystemId(const std::vector& system_id) { + return system_id.size() == std::size(media::kLegacyFairPlaySystemId) && + std::equal(system_id.begin(), system_id.end(), + media::kLegacyFairPlaySystemId); +} + +bool IsPlayReadySystemId(const std::vector& system_id) { + return system_id.size() == std::size(media::kPlayReadySystemId) && + std::equal(system_id.begin(), system_id.end(), + media::kPlayReadySystemId); +} + std::string Base64EncodeData(const std::string& prefix, const std::string& data) { std::string data_base64; @@ -65,7 +78,7 @@ std::string Base64EncodeData(const std::string& prefix, } std::string VectorToString(const std::vector& v) { - return std::string(v.begin(), v.end()); + return std::string(v.begin(), v.end()); } // Segment URL is relative to either output directory or the directory @@ -455,7 +468,7 @@ bool SimpleHlsNotifier::NotifyEncryptionUpdate( iv, "identity", "", media_playlist.get()); return true; } - if (IsFairPlaySystemId(system_id)) { + if (IsFairPlaySystemId(system_id) || IsLegacyFairPlaySystemId(system_id)) { std::string key_uri = hls_params().key_uri; if (key_uri.empty()) { // Use key_id as the key_uri. The player needs to have custom logic to @@ -471,6 +484,20 @@ bool SimpleHlsNotifier::NotifyEncryptionUpdate( "1", media_playlist.get()); return true; } + if (IsPlayReadySystemId(system_id)) { + std::unique_ptr b = + media::PsshBoxBuilder::ParseFromBox( + protection_system_specific_data.data(), + protection_system_specific_data.size()); + std::string pssh_data(reinterpret_cast(b->pssh_data().data()), + b->pssh_data().size()); + std::string key_uri_data_base64 = + Base64EncodeData(kUriBase64Utf16Prefix, pssh_data); + NotifyEncryptionToMediaPlaylist(encryption_method, key_uri_data_base64, + empty_key_id, iv, "com.microsoft.playready", + "1", media_playlist.get()); + return true; + } LOG(WARNING) << "HLS: Ignore unknown or unsupported system ID: " << absl::BytesToHexString(absl::string_view( diff --git a/packager/media/base/audio_stream_info.cc b/packager/media/base/audio_stream_info.cc index df38386950..bb1a6b1975 100644 --- a/packager/media/base/audio_stream_info.cc +++ b/packager/media/base/audio_stream_info.cc @@ -25,6 +25,8 @@ std::string AudioCodecToString(Codec codec) { return "AAC"; case kCodecAC3: return "AC3"; + case kCodecALAC: + return "ALAC"; case kCodecDTSC: return "DTSC"; case kCodecDTSE: @@ -138,6 +140,8 @@ std::string AudioStreamInfo::GetCodecString(Codec codec, return "mp4a.40." + absl::StrFormat("%hhu", audio_object_type); case kCodecAC3: return "ac-3"; + case kCodecALAC: + return "alac"; case kCodecDTSC: return "dtsc"; case kCodecDTSE: diff --git a/packager/media/base/fourccs.h b/packager/media/base/fourccs.h index b075d5a306..4c85c02c10 100644 --- a/packager/media/base/fourccs.h +++ b/packager/media/base/fourccs.h @@ -23,6 +23,7 @@ enum FourCC : uint32_t { FOURCC_ac_3 = 0x61632d33, // "ac-3" FOURCC_ac_4 = 0x61632d34, // "ac-4" FOURCC_ac3d = 0x61633364, + FOURCC_alac = 0x616c6163, FOURCC_apad = 0x61706164, FOURCC_av01 = 0x61763031, FOURCC_av1C = 0x61763143, diff --git a/packager/media/base/protection_system_ids.h b/packager/media/base/protection_system_ids.h index 92dd923565..5ddaf8cb07 100644 --- a/packager/media/base/protection_system_ids.h +++ b/packager/media/base/protection_system_ids.h @@ -23,6 +23,12 @@ const uint8_t kFairPlaySystemId[] = {0x94, 0xCE, 0x86, 0xFB, 0x07, 0xFF, 0x4F, 0x43, 0xAD, 0xB8, 0x93, 0xD2, 0xFA, 0x96, 0x8C, 0xA2}; +// this is a legacy system ID used for FairPlay in old packager versions, kept +// for backwards compatibility only +const uint8_t kLegacyFairPlaySystemId[] = {0x29, 0x70, 0x1F, 0xE4, 0x3C, 0xC7, + 0x4A, 0x34, 0x8C, 0x5B, 0xAE, 0x90, + 0xC7, 0x43, 0x9A, 0x47}; + // Marlin Adaptive Streaming Specification – Simple Profile, V1.0. const uint8_t kMarlinSystemId[] = {0x5E, 0x62, 0x9A, 0xF5, 0x38, 0xDA, 0x40, 0x63, 0x89, 0x77, 0x97, 0xFF, diff --git a/packager/media/base/stream_info.h b/packager/media/base/stream_info.h index 355b032a54..1a40816673 100644 --- a/packager/media/base/stream_info.h +++ b/packager/media/base/stream_info.h @@ -41,6 +41,7 @@ enum Codec { kCodecAAC = kCodecAudio, kCodecAC3, kCodecAC4, + kCodecALAC, // TODO(kqyang): Use kCodecDTS and a kDtsStreamFormat for the various DTS // streams. kCodecDTSC, diff --git a/packager/media/base/text_sample.h b/packager/media/base/text_sample.h index 81f78eac67..22b4647984 100644 --- a/packager/media/base/text_sample.h +++ b/packager/media/base/text_sample.h @@ -80,6 +80,11 @@ struct TextFragmentStyle { std::optional underline; std::optional bold; std::optional italic; + // The colors could be any string that can be interpreted as + // a color in TTML (or WebVTT). As a start, the 8 teletext colors are used, + // i.e. black, red, green, yellow, blue, magenta, cyan, and white + std::string color; + std::string backgroundColor; }; /// Represents a recursive structure of styled blocks of text. Only one of diff --git a/packager/media/demuxer/demuxer.cc b/packager/media/demuxer/demuxer.cc index f3dd066b0d..beb2623b8a 100644 --- a/packager/media/demuxer/demuxer.cc +++ b/packager/media/demuxer/demuxer.cc @@ -165,21 +165,25 @@ Status Demuxer::InitializeParser() { "Cannot open file for reading " + file_name_); } - // Read enough bytes before detecting the container. int64_t bytes_read = 0; bool eof = false; - while (static_cast(bytes_read) < kInitBufSize) { - int64_t read_result = - media_file_->Read(buffer_.get() + bytes_read, kInitBufSize); - if (read_result < 0) - return Status(error::FILE_FAILURE, "Cannot read file " + file_name_); - if (read_result == 0) { - eof = true; - break; + if (input_format_.empty()) { + // Read enough bytes before detecting the container. + while (static_cast(bytes_read) < kInitBufSize) { + int64_t read_result = + media_file_->Read(buffer_.get() + bytes_read, kInitBufSize); + if (read_result < 0) + return Status(error::FILE_FAILURE, "Cannot read file " + file_name_); + if (read_result == 0) { + eof = true; + break; + } + bytes_read += read_result; } - bytes_read += read_result; + container_name_ = DetermineContainer(buffer_.get(), bytes_read); + } else { + container_name_ = DetermineContainerFromFormatName(input_format_); } - container_name_ = DetermineContainer(buffer_.get(), bytes_read); // Initialize media parser. switch (container_name_) { diff --git a/packager/media/demuxer/demuxer.h b/packager/media/demuxer/demuxer.h index e2a402f930..78617aae40 100644 --- a/packager/media/demuxer/demuxer.h +++ b/packager/media/demuxer/demuxer.h @@ -75,6 +75,10 @@ class Demuxer : public OriginHandler { dump_stream_info_ = dump_stream_info; } + void set_input_format(std::string input_format) { + input_format_ = input_format; + } + protected: /// @name MediaHandler implementation overrides. /// @{ @@ -148,6 +152,8 @@ class Demuxer : public OriginHandler { // Whether to dump stream info when it is received. bool dump_stream_info_ = false; Status init_event_status_; + // Explicitly defined input format, for avoiding autodetection. + std::string input_format_; }; } // namespace media diff --git a/packager/media/event/muxer_listener_factory.h b/packager/media/event/muxer_listener_factory.h index 9901373c48..375e7c12b7 100644 --- a/packager/media/event/muxer_listener_factory.h +++ b/packager/media/event/muxer_listener_factory.h @@ -40,6 +40,10 @@ class MuxerListenerFactory { // told to output media info. std::string media_info_output; + // Explicit input format, for avoiding autodetection when needed. + // This is useful for cases such as live WebVTT through UDP. + std::string input_format; + // HLS specific values needed to write to HLS manifests. Will only be used // if an HlsNotifier is given to the factory. std::string hls_group_id; diff --git a/packager/media/formats/mp2t/CMakeLists.txt b/packager/media/formats/mp2t/CMakeLists.txt index 8aa9fb95cc..a86fa017b2 100644 --- a/packager/media/formats/mp2t/CMakeLists.txt +++ b/packager/media/formats/mp2t/CMakeLists.txt @@ -22,6 +22,9 @@ add_library(mp2t STATIC es_parser_h265.h es_parser_h26x.cc es_parser_h26x.h + es_parser_teletext.cc + es_parser_teletext.h + es_parser_teletext_tables.h es_parser.h mp2t_media_parser.cc mp2t_media_parser.h @@ -69,6 +72,7 @@ ac3_header_unittest.cc adts_header_unittest.cc es_parser_h264_unittest.cc es_parser_h26x_unittest.cc +es_parser_teletext_unittest.cc mp2t_media_parser_unittest.cc mpeg1_header_unittest.cc pes_packet_generator_unittest.cc diff --git a/packager/media/formats/mp2t/es_parser.h b/packager/media/formats/mp2t/es_parser.h index 2cb6f472ea..1225260c3a 100644 --- a/packager/media/formats/mp2t/es_parser.h +++ b/packager/media/formats/mp2t/es_parser.h @@ -7,6 +7,7 @@ #include #include +#include namespace shaka { namespace media { diff --git a/packager/media/formats/mp2t/es_parser_teletext.cc b/packager/media/formats/mp2t/es_parser_teletext.cc new file mode 100644 index 0000000000..ac42ce2f23 --- /dev/null +++ b/packager/media/formats/mp2t/es_parser_teletext.cc @@ -0,0 +1,619 @@ +// Copyright 2020 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +#include + +#include +#include +#include +#include +#include + +namespace shaka { +namespace media { +namespace mp2t { + +namespace { + +constexpr const char* kRegionTeletextPrefix = "ttx_"; + +const uint8_t EBU_TELETEXT_WITH_SUBTITLING = 0x03; +const int kPayloadSize = 40; +const int kNumTriplets = 13; + +template +constexpr T bit(T value, const size_t bit_pos) { + return (value >> bit_pos) & 0x1; +} + +uint8_t ReadHamming(BitReader& reader) { + uint8_t bits; + RCHECK(reader.ReadBits(8, &bits)); + return TELETEXT_HAMMING_8_4[bits]; +} + +bool Hamming_24_18(const uint32_t value, uint32_t& out_result) { + uint32_t result = value; + + uint8_t test = 0; + for (uint8_t i = 0; i < 23; i++) { + test ^= ((result >> i) & 0x01) * (i + 0x21); + } + test ^= ((result >> 0x17) & 0x01) * 0x20; + + if ((test & 0x1f) != 0x1f) { + if ((test & 0x20) == 0x20) { + return false; + } + result ^= 1 << (0x1e - test); + } + + out_result = (result & 0x000004) >> 2 | (result & 0x000070) >> 3 | + (result & 0x007f00) >> 4 | (result & 0x7f0000) >> 5; + return true; +} + +bool ParseSubtitlingDescriptor( + const uint8_t* descriptor, + const size_t size, + std::unordered_map& result) { + BitReader reader(descriptor, size); + RCHECK(reader.SkipBits(8)); + + size_t data_size; + RCHECK(reader.ReadBits(8, &data_size)); + RCHECK(data_size + 2 <= size); + + for (size_t i = 0; i < data_size; i += 8) { + uint32_t lang_code; + RCHECK(reader.ReadBits(24, &lang_code)); + uint8_t ignored_teletext_type; + RCHECK(reader.ReadBits(5, &ignored_teletext_type)); + uint8_t magazine_number; + RCHECK(reader.ReadBits(3, &magazine_number)); + if (magazine_number == 0) { + magazine_number = 8; + } + + uint8_t page_number_tens; + RCHECK(reader.ReadBits(4, &page_number_tens)); + uint8_t page_number_units; + RCHECK(reader.ReadBits(4, &page_number_units)); + const uint8_t page_number = page_number_tens * 10 + page_number_units; + + std::string lang(3, '\0'); + lang[0] = static_cast((lang_code >> 16) & 0xff); + lang[1] = static_cast((lang_code >> 8) & 0xff); + lang[2] = static_cast((lang_code >> 0) & 0xff); + + const uint16_t index = magazine_number * 100 + page_number; + result.emplace(index, std::move(lang)); + } + + return true; +} + +} // namespace + +EsParserTeletext::EsParserTeletext(const uint32_t pid, + const NewStreamInfoCB& new_stream_info_cb, + const EmitTextSampleCB& emit_sample_cb, + const uint8_t* descriptor, + const size_t descriptor_length) + : EsParser(pid), + new_stream_info_cb_(new_stream_info_cb), + emit_sample_cb_(emit_sample_cb), + magazine_(0), + page_number_(0), + charset_code_(0), + current_charset_{}, + last_pts_(0) { + if (!ParseSubtitlingDescriptor(descriptor, descriptor_length, languages_)) { + LOG(ERROR) << "Unable to parse teletext_descriptor"; + } + UpdateCharset(); +} + +bool EsParserTeletext::Parse(const uint8_t* buf, + int size, + int64_t pts, + int64_t dts) { + if (!sent_info_) { + sent_info_ = true; + auto info = std::make_shared(pid(), kMpeg2Timescale, + kInfiniteDuration, kCodecText, + "", "", 0, 0, ""); + for (const auto& pair : languages_) { + info->AddSubStream(pair.first, {pair.second}); + } + + new_stream_info_cb_(info); + } + + return ParseInternal(buf, size, pts); +} + +bool EsParserTeletext::Flush() { + std::vector keys; + for (const auto& entry : page_state_) { + keys.push_back(entry.first); + } + + for (const auto key : keys) { + SendPending(key, last_pts_); + } + + return true; +} + +void EsParserTeletext::Reset() { + page_state_.clear(); + magazine_ = 0; + page_number_ = 0; + sent_info_ = false; + charset_code_ = 0; + UpdateCharset(); +} + +bool EsParserTeletext::ParseInternal(const uint8_t* data, + const size_t size, + const int64_t pts) { + BitReader reader(data, size); + RCHECK(reader.SkipBits(8)); + std::vector rows; + + while (reader.bits_available()) { + uint8_t data_unit_id; + RCHECK(reader.ReadBits(8, &data_unit_id)); + + uint8_t data_unit_length; + RCHECK(reader.ReadBits(8, &data_unit_length)); + + if (data_unit_id != EBU_TELETEXT_WITH_SUBTITLING) { + RCHECK(reader.SkipBytes(data_unit_length)); + continue; + } + + if (data_unit_length != 44) { + // Teletext data unit length is always 44 bytes + LOG(ERROR) << "Bad Teletext data length"; + break; + } + + + RCHECK(reader.SkipBits(16)); + + uint16_t address_bits; + RCHECK(reader.ReadBits(16, &address_bits)); + + uint8_t magazine = bit(address_bits, 14) + 2 * bit(address_bits, 12) + + 4 * bit(address_bits, 10); + + if (magazine == 0) { + magazine = 8; + } + + const uint8_t packet_nr = + (bit(address_bits, 8) + 2 * bit(address_bits, 6) + + 4 * bit(address_bits, 4) + 8 * bit(address_bits, 2) + + 16 * bit(address_bits, 0)); + const uint8_t* data_block = reader.current_byte_ptr(); + RCHECK(reader.SkipBytes(40)); + + TextRow row; + if (ParseDataBlock(pts, data_block, packet_nr, magazine, row)) { + rows.emplace_back(std::move(row)); + } + } + + if (rows.empty()) { + return true; + } + const uint16_t index = magazine_ * 100 + page_number_; + auto page_state_itr = page_state_.find(index); + if (page_state_itr == page_state_.end()) { + page_state_.emplace(index, TextBlock{std::move(rows), {}, last_pts_}); + + } else { + for (auto& row : rows) { + auto& page_state_lines = page_state_itr->second.rows; + page_state_lines.emplace_back(std::move(row)); + } + rows.clear(); + } + + return true; +} + +bool EsParserTeletext::ParseDataBlock(const int64_t pts, + const uint8_t* data_block, + const uint8_t packet_nr, + const uint8_t magazine, + TextRow& row) { + if (packet_nr == 0) { + last_pts_ = pts; + BitReader reader(data_block, 32); + + const uint8_t page_number_units = ReadHamming(reader); + const uint8_t page_number_tens = ReadHamming(reader); + if (page_number_units == 0xf || page_number_tens == 0xf) { + RCHECK(reader.SkipBits(40)); + return false; + } + const uint8_t page_number = 10 * page_number_tens + page_number_units; + + const uint16_t index = magazine * 100 + page_number; + SendPending(index, pts); + + page_number_ = page_number; + magazine_ = magazine; + + RCHECK(reader.SkipBits(40)); + const uint8_t subcode_c11_c14 = ReadHamming(reader); + const uint8_t charset_code = subcode_c11_c14 >> 1; + if (charset_code != charset_code_) { + charset_code_ = charset_code; + UpdateCharset(); + } + + return false; + + } else if (packet_nr == 26) { + ParsePacket26(data_block); + return false; + + } else if (packet_nr > 26) { + return false; + } + + row = BuildRow(data_block, packet_nr); + return true; +} + +void EsParserTeletext::UpdateCharset() { + memcpy(current_charset_, TELETEXT_CHARSET_G0_LATIN, sizeof(TELETEXT_CHARSET_G0_LATIN)); + if (charset_code_ > 7) { + return; + } + const auto teletext_national_subset = + static_cast(charset_code_); + + switch (teletext_national_subset) { + case TELETEXT_NATIONAL_SUBSET::ENGLISH: + UpdateNationalSubset(TELETEXT_NATIONAL_SUBSET_ENGLISH); + break; + case TELETEXT_NATIONAL_SUBSET::FRENCH: + UpdateNationalSubset(TELETEXT_NATIONAL_SUBSET_FRENCH); + break; + case TELETEXT_NATIONAL_SUBSET::SWEDISH_FINNISH_HUNGARIAN: + UpdateNationalSubset(TELETEXT_NATIONAL_SUBSET_SWEDISH_FINNISH_HUNGARIAN); + break; + case TELETEXT_NATIONAL_SUBSET::CZECH_SLOVAK: + UpdateNationalSubset(TELETEXT_NATIONAL_SUBSET_CZECH_SLOVAK); + break; + case TELETEXT_NATIONAL_SUBSET::GERMAN: + UpdateNationalSubset(TELETEXT_NATIONAL_SUBSET_GERMAN); + break; + case TELETEXT_NATIONAL_SUBSET::PORTUGUESE_SPANISH: + UpdateNationalSubset(TELETEXT_NATIONAL_SUBSET_PORTUGUESE_SPANISH); + break; + case TELETEXT_NATIONAL_SUBSET::ITALIAN: + UpdateNationalSubset(TELETEXT_NATIONAL_SUBSET_ITALIAN); + break; + case TELETEXT_NATIONAL_SUBSET::NONE: + default: + break; + } +} + +void EsParserTeletext::SendPending(const uint16_t index, const int64_t pts) { + auto page_state_itr = page_state_.find(index); + + if (page_state_itr == page_state_.end() || + page_state_itr->second.rows.empty()) { + return; + } + + const auto& pending_rows = page_state_itr->second.rows; + const auto pending_pts = page_state_itr->second.pts; + + TextSettings text_settings; + std::shared_ptr text_sample; + std::vector sub_fragments; + + if (pending_rows.size() == 1) { + // This is a single line of formatted text. + // Propagate row number/2 and alignment + const float line_nr = float(pending_rows[0].row_number) / 2.0; + text_settings.line = TextNumber(line_nr, TextUnitType::kLines); + text_settings.region = kRegionTeletextPrefix + std::to_string(int(line_nr)); + text_settings.text_alignment = pending_rows[0].alignment; + text_sample = std::make_shared( + "", pending_pts, pts, text_settings, pending_rows[0].fragment); + text_sample->set_sub_stream_index(index); + emit_sample_cb_(text_sample); + page_state_.erase(index); + return; + } else { + int32_t latest_row_nr = -1; + bool last_double_height = false; + bool new_sample = true; + for (const auto& row : pending_rows) { + int row_nr = row.row_number; + bool double_height = row.double_height; + int row_step = last_double_height ? 2 : 1; + if (latest_row_nr != -1) { // Not the first row + if (row_nr != latest_row_nr + row_step) { + // Send what has been collected since not adjacent + text_sample = + std::make_shared("", pending_pts, pts, text_settings, + TextFragment({}, sub_fragments)); + text_sample->set_sub_stream_index(index); + emit_sample_cb_(text_sample); + new_sample = true; + } else { + // Add a newline and the next row to the current sample + sub_fragments.push_back(TextFragment({}, true)); + sub_fragments.push_back(row.fragment); + new_sample = false; + } + } + if (new_sample) { + const float line_nr = float(row.row_number) / 2.0; + text_settings.line = TextNumber(line_nr, TextUnitType::kLines); + text_settings.region = + kRegionTeletextPrefix + std::to_string(int(line_nr)); + text_settings.text_alignment = row.alignment; + sub_fragments.clear(); + sub_fragments.push_back(row.fragment); + } + last_double_height = double_height; + latest_row_nr = row_nr; + } + } + + text_sample = std::make_shared( + "", pending_pts, pts, text_settings, TextFragment({}, sub_fragments)); + text_sample->set_sub_stream_index(index); + emit_sample_cb_(text_sample); + + page_state_.erase(index); +} + +// BuildRow builds a row with alignment information. +EsParserTeletext::TextRow EsParserTeletext::BuildRow(const uint8_t* data_block, + const uint8_t row) const { + std::string next_string; + next_string.reserve(kPayloadSize * 2); + + const uint16_t index = magazine_ * 100 + page_number_; + const auto page_state_itr = page_state_.find(index); + + const std::unordered_map* column_replacement_map = + nullptr; + if (page_state_itr != page_state_.cend()) { + const auto row_itr = + page_state_itr->second.packet_26_replacements.find(row); + if (row_itr != page_state_itr->second.packet_26_replacements.cend()) { + column_replacement_map = &(row_itr->second); + } + } + + int32_t start_pos = 0; + int32_t end_pos = 0; + bool double_height = false; + TextFragmentStyle text_style = TextFragmentStyle(); + text_style.color = "white"; + text_style.backgroundColor = "black"; + // A typical 40 character line looks like: + // doubleHeight, [color] spaces, Start, Start, text, End End, spaces + for (size_t i = 0; i < kPayloadSize; ++i) { + if (column_replacement_map) { + const auto column_itr = column_replacement_map->find(i); + if (column_itr != column_replacement_map->cend()) { + next_string.append(column_itr->second); + continue; + } + } + + char next_char = + static_cast(TELETEXT_BITREVERSE_8[data_block[i]] & 0x7f); + + if (next_char < 0x20) { + // Here are control characters, which are not printable. + // These include colors, double-height, flashing, etc. + // We only handle one-foreground color and double-height. + switch (next_char) { + case 0x0: // Alpha Black (not included in Level 1.5) + // color = ColorBlack + break; + case 0x1: + text_style.color = "red"; + break; + case 0x2: + text_style.color = "green"; + break; + case 0x3: + text_style.color = "yellow"; + break; + case 0x4: + text_style.color = "blue"; + break; + case 0x5: + text_style.color = "magenta"; + break; + case 0x6: + text_style.color = "cyan"; + break; + case 0x7: + text_style.color = "white"; + break; + case 0x08: // Flash (not handled) + break; + case 0x09: // Steady (not handled) + break; + case 0xa: // End Box + end_pos = i - 1; + break; + case 0xb: // Start Box, typically twice due to double height + start_pos = i + 1; + continue; // Do not propagate as a space + break; + case 0xc: // Normal size + break; + case 0xd: // Double height, typically always used + double_height = true; + break; + case 0x1c: // Black background (not handled) + break; + case 0x1d: // Set background color from text color. + text_style.backgroundColor = text_style.color; + text_style.color = "black"; // Avoid having same as background + break; + default: + // Rest of codes below 0x20 are not part of Level 1.5 or related to + // mosaic graphics (non-text) + break; + } + next_char = + 0x20; // These characters result in a space if between start and end + } + if (start_pos == 0 || end_pos != 0) { // Not between start and end + continue; + } + switch (next_char) { + case '&': + next_string.append("&"); + break; + case '<': + next_string.append("<"); + break; + default: { + const std::string replacement(current_charset_[next_char - 0x20]); + next_string.append(replacement); + } break; + } + } + if (end_pos == 0) { + end_pos = kPayloadSize - 1; + } + + // Using start_pos and end_pos we approximated alignment of text + // depending on the number of spaces to the left and right of the text. + auto left_right_diff = start_pos - (kPayloadSize - 1 - end_pos); + TextAlignment alignment; + if (left_right_diff > 4) { + alignment = TextAlignment::kRight; + } else if (left_right_diff < -4) { + alignment = TextAlignment::kLeft; + } else { + alignment = TextAlignment::kCenter; + } + const auto text_row = TextRow( + {alignment, row, double_height, {TextFragment(text_style, next_string)}}); + + return text_row; +} + +void EsParserTeletext::ParsePacket26(const uint8_t* data_block) { + const uint16_t index = magazine_ * 100 + page_number_; + auto page_state_itr = page_state_.find(index); + if (page_state_itr == page_state_.end()) { + page_state_.emplace(index, TextBlock{{}, {}, last_pts_}); + } + auto& replacement_map = page_state_[index].packet_26_replacements; + + uint8_t row = 0; + + std::vector x26_triplets; + x26_triplets.reserve(kNumTriplets); + for (uint8_t i = 1; i < kPayloadSize; i += 3) { + const uint32_t bytes = (TELETEXT_BITREVERSE_8[data_block[i + 2]] << 16) | + (TELETEXT_BITREVERSE_8[data_block[i + 1]] << 8) | + TELETEXT_BITREVERSE_8[data_block[i]]; + uint32_t triplet; + if (Hamming_24_18(bytes, triplet)) { + x26_triplets.emplace_back(triplet); + } + } + + for (const auto triplet : x26_triplets) { + const uint8_t mode = (triplet & 0x7c0) >> 6; + const uint8_t address = triplet & 0x3f; + const uint8_t row_address_group = (address >= 0x28) && (address <= 0x3f); + + if ((mode == 0x4) && (row_address_group == 0x1)) { + row = address - 0x28; + if (row == 0x0) { + row = 0x18; + } + } + + if (mode >= 0x11 && mode <= 0x1f && row_address_group == 0x1) { + break; + } + + const uint8_t data = (triplet & 0x3f800) >> 11; + + if (mode == 0x0f && row_address_group == 0x0 && data > 0x1f) { + SetPacket26ReplacementString(replacement_map, row, address, + reinterpret_cast( + TELETEXT_CHARSET_G2_LATIN[data - 0x20])); + } + + if (mode == 0x10 && row_address_group == 0x0 && data == 0x40) { + SetPacket26ReplacementString(replacement_map, row, address, "@"); + } + + if (mode < 0x11 || mode > 0x1f || row_address_group != 0x0) { + continue; + } + + if (data >= 0x41 && data <= 0x5a) { + SetPacket26ReplacementString( + replacement_map, row, address, + reinterpret_cast( + TELETEXT_G2_LATIN_ACCENTS[mode - 0x11][data - 0x41])); + + } else if (data >= 0x61 && data <= 0x7a) { + SetPacket26ReplacementString( + replacement_map, row, address, + reinterpret_cast( + TELETEXT_G2_LATIN_ACCENTS[mode - 0x11][data - 0x47])); + + } else if ((data & 0x7f) >= 0x20) { + SetPacket26ReplacementString( + replacement_map, row, address, + reinterpret_cast( + TELETEXT_CHARSET_G0_LATIN[(data & 0x7f) - 0x20])); + } + } +} + +void EsParserTeletext::UpdateNationalSubset( + const uint8_t national_subset[13][3]) { + for (size_t i = 0; i < 13; ++i) { + const size_t position = TELETEXT_NATIONAL_CHAR_INDEX_G0[i]; + memcpy(current_charset_[position], national_subset[i], 3); + } +} + +void EsParserTeletext::SetPacket26ReplacementString( + RowColReplacementMap& replacement_map, + const uint8_t row, + const uint8_t column, + std::string&& replacement_string) { + auto replacement_map_itr = replacement_map.find(row); + if (replacement_map_itr == replacement_map.cend()) { + replacement_map.emplace(row, std::unordered_map{}); + } + auto& column_map = replacement_map[row]; + column_map.emplace(column, std::move(replacement_string)); +} + +} // namespace mp2t +} // namespace media +} // namespace shaka diff --git a/packager/media/formats/mp2t/es_parser_teletext.h b/packager/media/formats/mp2t/es_parser_teletext.h new file mode 100644 index 0000000000..0efde38f3e --- /dev/null +++ b/packager/media/formats/mp2t/es_parser_teletext.h @@ -0,0 +1,89 @@ +// Copyright 2020 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +#ifndef PACKAGER_MEDIA_FORMATS_MP2T_ES_PARSER_TELETEXT_H_ +#define PACKAGER_MEDIA_FORMATS_MP2T_ES_PARSER_TELETEXT_H_ + +#include +#include +#include +#include + +#include +#include + +namespace shaka { +namespace media { +namespace mp2t { + +class EsParserTeletext : public EsParser { + public: + EsParserTeletext(const uint32_t pid, + const NewStreamInfoCB& new_stream_info_cb, + const EmitTextSampleCB& emit_sample_cb, + const uint8_t* descriptor, + const size_t descriptor_length); + + EsParserTeletext(const EsParserTeletext&) = delete; + EsParserTeletext& operator=(const EsParserTeletext&) = delete; + + bool Parse(const uint8_t* buf, int size, int64_t pts, int64_t dts) override; + bool Flush() override; + void Reset() override; + + private: + using RowColReplacementMap = + std::unordered_map>; + + struct TextRow { + TextAlignment alignment; + int row_number; + bool double_height; + TextFragment fragment; + }; + + struct TextBlock { + std::vector rows; + RowColReplacementMap packet_26_replacements; + int64_t pts; + }; + + bool ParseInternal(const uint8_t* data, const size_t size, const int64_t pts); + bool ParseDataBlock(const int64_t pts, + const uint8_t* data_block, + const uint8_t packet_nr, + const uint8_t magazine, + TextRow& display_text); + void UpdateCharset(); + void SendPending(const uint16_t index, const int64_t pts); + TextRow BuildRow(const uint8_t* data_block, const uint8_t row) const; + void ParsePacket26(const uint8_t* data_block); + void UpdateNationalSubset(const uint8_t national_subset[13][3]); + + static void SetPacket26ReplacementString( + RowColReplacementMap& replacement_map, + const uint8_t row, + const uint8_t column, + std::string&& replacement_string); + + NewStreamInfoCB new_stream_info_cb_; + EmitTextSampleCB emit_sample_cb_; + + std::unordered_map languages_; + bool sent_info_ = false; + uint8_t magazine_; + uint8_t page_number_; + std::unordered_map page_state_; + uint8_t charset_code_; + char current_charset_[96][3]; + int64_t last_pts_; +}; + +} // namespace mp2t +} // namespace media +} // namespace shaka + +#endif // PACKAGER_MEDIA_FORMATS_MP2T_ES_PARSER_TELETEXT_H_ diff --git a/packager/media/formats/mp2t/es_parser_teletext_tables.h b/packager/media/formats/mp2t/es_parser_teletext_tables.h new file mode 100644 index 0000000000..25e7560a6b --- /dev/null +++ b/packager/media/formats/mp2t/es_parser_teletext_tables.h @@ -0,0 +1,434 @@ +// Copyright 2020 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +#ifndef PACKAGER_MEDIA_FORMATS_MP2T_ES_PARSER_TELETEXT_TABLES_H_ +#define PACKAGER_MEDIA_FORMATS_MP2T_ES_PARSER_TELETEXT_TABLES_H_ + +#include + +namespace shaka { +namespace media { +namespace mp2t { + +const uint8_t TELETEXT_BITREVERSE_8[] = { + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, + 0x30, 0xb0, 0x70, 0xf0, 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, + 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, 0x04, 0x84, 0x44, 0xc4, + 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, + 0x3c, 0xbc, 0x7c, 0xfc, 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, + 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, 0x0a, 0x8a, 0x4a, 0xca, + 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, + 0x36, 0xb6, 0x76, 0xf6, 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, + 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 0x01, 0x81, 0x41, 0xc1, + 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, + 0x39, 0xb9, 0x79, 0xf9, 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, + 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, 0x0d, 0x8d, 0x4d, 0xcd, + 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, + 0x33, 0xb3, 0x73, 0xf3, 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, + 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, 0x07, 0x87, 0x47, 0xc7, + 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, + 0x3f, 0xbf, 0x7f, 0xff}; + +const uint8_t TELETEXT_HAMMING_8_4[] = { + 0x00, 0x08, 0x00, 0x08, 0x04, 0x0c, 0x04, 0x0c, 0x00, 0x08, 0x00, 0x08, + 0x04, 0x0c, 0x04, 0x0c, 0x02, 0x0a, 0x02, 0x0a, 0x06, 0x0e, 0x06, 0x0e, + 0x02, 0x0a, 0x02, 0x0a, 0x06, 0x0e, 0x06, 0x0e, 0x00, 0x08, 0x00, 0x08, + 0x04, 0x0c, 0x04, 0x0c, 0x00, 0x08, 0x00, 0x08, 0x04, 0x0c, 0x04, 0x0c, + 0x02, 0x0a, 0x02, 0x0a, 0x06, 0x0e, 0x06, 0x0e, 0x02, 0x0a, 0x02, 0x0a, + 0x06, 0x0e, 0x06, 0x0e, 0x01, 0x09, 0x01, 0x09, 0x05, 0x0d, 0x05, 0x0d, + 0x01, 0x09, 0x01, 0x09, 0x05, 0x0d, 0x05, 0x0d, 0x03, 0x0b, 0x03, 0x0b, + 0x07, 0x0f, 0x07, 0x0f, 0x03, 0x0b, 0x03, 0x0b, 0x07, 0x0f, 0x07, 0x0f, + 0x01, 0x09, 0x01, 0x09, 0x05, 0x0d, 0x05, 0x0d, 0x01, 0x09, 0x01, 0x09, + 0x05, 0x0d, 0x05, 0x0d, 0x03, 0x0b, 0x03, 0x0b, 0x07, 0x0f, 0x07, 0x0f, + 0x03, 0x0b, 0x03, 0x0b, 0x07, 0x0f, 0x07, 0x0f, 0x00, 0x08, 0x00, 0x08, + 0x04, 0x0c, 0x04, 0x0c, 0x00, 0x08, 0x00, 0x08, 0x04, 0x0c, 0x04, 0x0c, + 0x02, 0x0a, 0x02, 0x0a, 0x06, 0x0e, 0x06, 0x0e, 0x02, 0x0a, 0x02, 0x0a, + 0x06, 0x0e, 0x06, 0x0e, 0x00, 0x08, 0x00, 0x08, 0x04, 0x0c, 0x04, 0x0c, + 0x00, 0x08, 0x00, 0x08, 0x04, 0x0c, 0x04, 0x0c, 0x02, 0x0a, 0x02, 0x0a, + 0x06, 0x0e, 0x06, 0x0e, 0x02, 0x0a, 0x02, 0x0a, 0x06, 0x0e, 0x06, 0x0e, + 0x01, 0x09, 0x01, 0x09, 0x05, 0x0d, 0x05, 0x0d, 0x01, 0x09, 0x01, 0x09, + 0x05, 0x0d, 0x05, 0x0d, 0x03, 0x0b, 0x03, 0x0b, 0x07, 0x0f, 0x07, 0x0f, + 0x03, 0x0b, 0x03, 0x0b, 0x07, 0x0f, 0x07, 0x0f, 0x01, 0x09, 0x01, 0x09, + 0x05, 0x0d, 0x05, 0x0d, 0x01, 0x09, 0x01, 0x09, 0x05, 0x0d, 0x05, 0x0d, + 0x03, 0x0b, 0x03, 0x0b, 0x07, 0x0f, 0x07, 0x0f, 0x03, 0x0b, 0x03, 0x0b, + 0x07, 0x0f, 0x07, 0x0f}; + +const uint8_t TELETEXT_CHARSET_G0_LATIN[96][3] = { + {0x20, 0x0, 0x0}, {0x21, 0x0, 0x0}, {0x22, 0x0, 0x0}, {0xc2, 0xa3, 0x0}, + {0x24, 0x0, 0x0}, {0x25, 0x0, 0x0}, {0x26, 0x0, 0x0}, {0x27, 0x0, 0x0}, + {0x28, 0x0, 0x0}, {0x29, 0x0, 0x0}, {0x2a, 0x0, 0x0}, {0x2b, 0x0, 0x0}, + {0x2c, 0x0, 0x0}, {0x2d, 0x0, 0x0}, {0x2e, 0x0, 0x0}, {0x2f, 0x0, 0x0}, + {0x30, 0x0, 0x0}, {0x31, 0x0, 0x0}, {0x32, 0x0, 0x0}, {0x33, 0x0, 0x0}, + {0x34, 0x0, 0x0}, {0x35, 0x0, 0x0}, {0x36, 0x0, 0x0}, {0x37, 0x0, 0x0}, + {0x38, 0x0, 0x0}, {0x39, 0x0, 0x0}, {0x3a, 0x0, 0x0}, {0x3b, 0x0, 0x0}, + {0x3c, 0x0, 0x0}, {0x3d, 0x0, 0x0}, {0x3e, 0x0, 0x0}, {0x3f, 0x0, 0x0}, + {0x40, 0x0, 0x0}, {0x41, 0x0, 0x0}, {0x42, 0x0, 0x0}, {0x43, 0x0, 0x0}, + {0x44, 0x0, 0x0}, {0x45, 0x0, 0x0}, {0x46, 0x0, 0x0}, {0x47, 0x0, 0x0}, + {0x48, 0x0, 0x0}, {0x49, 0x0, 0x0}, {0x4a, 0x0, 0x0}, {0x4b, 0x0, 0x0}, + {0x4c, 0x0, 0x0}, {0x4d, 0x0, 0x0}, {0x4e, 0x0, 0x0}, {0x4f, 0x0, 0x0}, + {0x50, 0x0, 0x0}, {0x51, 0x0, 0x0}, {0x52, 0x0, 0x0}, {0x53, 0x0, 0x0}, + {0x54, 0x0, 0x0}, {0x55, 0x0, 0x0}, {0x56, 0x0, 0x0}, {0x57, 0x0, 0x0}, + {0x58, 0x0, 0x0}, {0x59, 0x0, 0x0}, {0x5a, 0x0, 0x0}, {0xc2, 0xab, 0x0}, + {0xc2, 0xbd, 0x0}, {0xc2, 0xbb, 0x0}, {0x5e, 0x0, 0x0}, {0x23, 0x0, 0x0}, + {0x2d, 0x0, 0x0}, {0x61, 0x0, 0x0}, {0x62, 0x0, 0x0}, {0x63, 0x0, 0x0}, + {0x64, 0x0, 0x0}, {0x65, 0x0, 0x0}, {0x66, 0x0, 0x0}, {0x67, 0x0, 0x0}, + {0x68, 0x0, 0x0}, {0x69, 0x0, 0x0}, {0x6a, 0x0, 0x0}, {0x6b, 0x0, 0x0}, + {0x6c, 0x0, 0x0}, {0x6d, 0x0, 0x0}, {0x6e, 0x0, 0x0}, {0x6f, 0x0, 0x0}, + {0x70, 0x0, 0x0}, {0x71, 0x0, 0x0}, {0x72, 0x0, 0x0}, {0x73, 0x0, 0x0}, + {0x74, 0x0, 0x0}, {0x75, 0x0, 0x0}, {0x76, 0x0, 0x0}, {0x77, 0x0, 0x0}, + {0x78, 0x0, 0x0}, {0x79, 0x0, 0x0}, {0x7a, 0x0, 0x0}, {0xc2, 0xbc, 0x0}, + {0xc2, 0xa6, 0x0}, {0xc2, 0xbe, 0x0}, {0xc3, 0xb7, 0x0}, {0x7f, 0x0, 0x0}}; + +const size_t TELETEXT_NATIONAL_CHAR_INDEX_G0[13] = { + 0x03, 0x04, 0x20, 0x3b, 0x3c, 0x3d, 0x3e, + 0x3f, 0x40, 0x5b, 0x5c, 0x5d, 0x5e}; + +const uint8_t TELETEXT_NATIONAL_SUBSET_ENGLISH[13][3] = { + {0xc2, 0xa3, 0x0}, {0x24, 0x0, 0x0}, {0x40, 0x0, 0x0}, {0xc2, 0xab, 0x0}, + {0xc2, 0xbd, 0x0}, {0xc2, 0xbb, 0x0}, {0x5e, 0x0, 0x0}, {0x23, 0x0, 0x0}, + {0x2d, 0x0, 0x0}, {0xc2, 0xbc, 0x0}, {0xc2, 0xa6, 0x0}, {0xc2, 0xbe, 0x0}, + {0xc3, 0xb7, 0x0}}; + +const uint8_t TELETEXT_NATIONAL_SUBSET_FRENCH[13][3] = { + {0xc3, 0xa9, 0x0}, {0xc3, 0xaf, 0x0}, {0xc3, 0xa0, 0x0}, {0xc3, 0xab, 0x0}, + {0xc3, 0xaa, 0x0}, {0xc3, 0xb9, 0x0}, {0xc3, 0xae, 0x0}, {0x23, 0x0, 0x0}, + {0xc3, 0xa8, 0x0}, {0xc3, 0xa2, 0x0}, {0xc3, 0xb4, 0x0}, {0xc3, 0xbb, 0x0}, + {0xc3, 0xa7, 0x0}}; + +const uint8_t TELETEXT_NATIONAL_SUBSET_SWEDISH_FINNISH_HUNGARIAN[13][3] = { + {0x23, 0x0, 0x0}, {0xc2, 0xa4, 0x0}, {0xc3, 0x89, 0x0}, {0xc3, 0x84, 0x0}, + {0xc3, 0x96, 0x0}, {0xc3, 0x85, 0x0}, {0xc3, 0x9c, 0x0}, {0x5f, 0x0, 0x0}, + {0xc3, 0xa9, 0x0}, {0xc3, 0xa4, 0x0}, {0xc3, 0xb6, 0x0}, {0xc3, 0xa5, 0x0}, + {0xc3, 0xbc, 0x0}}; + +const uint8_t TELETEXT_NATIONAL_SUBSET_CZECH_SLOVAK[13][3] = { + {0x23, 0x0, 0x0}, {0xc5, 0xaf, 0x0}, {0xc4, 0x8d, 0x0}, {0xc5, 0xa5, 0x0}, + {0xc5, 0xbe, 0x0}, {0xc3, 0xbd, 0x0}, {0xc3, 0xad, 0x0}, {0xc5, 0x99, 0x0}, + {0xc3, 0xa9, 0x0}, {0xc3, 0xa1, 0x0}, {0xc4, 0x9b, 0x0}, {0xc3, 0xba, 0x0}, + {0xc5, 0xa1, 0x0}}; + +const uint8_t TELETEXT_NATIONAL_SUBSET_GERMAN[13][3] = { + {0x23, 0x0, 0x0}, {0x24, 0x0, 0x0}, {0xc2, 0xa7, 0x0}, {0xc3, 0x84, 0x0}, + {0xc3, 0x96, 0x0}, {0xc3, 0x9c, 0x0}, {0x5e, 0x0, 0x0}, {0x5f, 0x0, 0x0}, + {0xc2, 0xb0, 0x0}, {0xc3, 0xa4, 0x0}, {0xc3, 0xb6, 0x0}, {0xc3, 0xbc, 0x0}, + {0xc3, 0x9f, 0x0}}; + +const uint8_t TELETEXT_NATIONAL_SUBSET_PORTUGUESE_SPANISH[13][3] = { + {0xc3, 0xa7, 0x0}, {0x24, 0x0, 0x0}, {0xc2, 0xa1, 0x0}, {0xc3, 0xa1, 0x0}, + {0xc3, 0xa9, 0x0}, {0xc3, 0xad, 0x0}, {0xc3, 0xb3, 0x0}, {0xc3, 0xba, 0x0}, + {0xc2, 0xbf, 0x0}, {0xc3, 0xbc, 0x0}, {0xc3, 0xb1, 0x0}, {0xc3, 0xa8, 0x0}, + {0xc3, 0xa0, 0x0}}; + +const uint8_t TELETEXT_NATIONAL_SUBSET_ITALIAN[13][3] = { + {0xc2, 0xa3, 0x0}, {0x24, 0x0, 0x0}, {0xc3, 0xa9, 0x0}, {0xc2, 0xb0, 0x0}, + {0xc3, 0xa7, 0x0}, {0xc2, 0xbb, 0x0}, {0x5e, 0x0, 0x0}, {0x23, 0x0, 0x0}, + {0xc3, 0xb9, 0x0}, {0xc3, 0xa0, 0x0}, {0xc3, 0xb2, 0x0}, {0xc3, 0xa8, 0x0}, + {0xc3, 0xac, 0x0}}; + +enum class TELETEXT_NATIONAL_SUBSET : uint8_t { + ENGLISH = 0, + FRENCH = 1, + SWEDISH_FINNISH_HUNGARIAN = 2, + CZECH_SLOVAK = 3, + GERMAN = 4, + PORTUGUESE_SPANISH = 5, + ITALIAN = 6, + NONE = 7 +}; + +const uint8_t TELETEXT_CHARSET_G2_LATIN[96][3] = { + {0x20, 0x0, 0x0}, {0xc2, 0xa1, 0x0}, {0xc2, 0xa2, 0x0}, {0xc2, 0xa3, 0x0}, + {0x24, 0x0, 0x0}, {0xc2, 0xa5, 0x0}, {0x23, 0x0, 0x0}, {0xc2, 0xa7, 0x0}, + {0xc2, 0xa4, 0x0}, {0x20, 0x18, 0x0}, {0x20, 0x1c, 0x0}, {0xc2, 0xab, 0x0}, + {0x21, 0x90, 0x0}, {0x21, 0x91, 0x0}, {0x21, 0x92, 0x0}, {0x21, 0x93, 0x0}, + {0xc2, 0xb0, 0x0}, {0xc2, 0xb1, 0x0}, {0xc2, 0xb2, 0x0}, {0xc2, 0xb3, 0x0}, + {0xc3, 0x97, 0x0}, {0xc2, 0xb5, 0x0}, {0xc2, 0xb6, 0x0}, {0xc2, 0xb7, 0x0}, + {0xc3, 0xb7, 0x0}, {0x20, 0x19, 0x0}, {0x20, 0x1d, 0x0}, {0xc2, 0xbb, 0x0}, + {0xc2, 0xbc, 0x0}, {0xc2, 0xbd, 0x0}, {0xc2, 0xbe, 0x0}, {0xbf, 0x0, 0x0}, + {0x20, 0x0, 0x0}, {0xcc, 0x80, 0x0}, {0xcc, 0x81, 0x0}, {0xcc, 0x82, 0x0}, + {0xcc, 0x83, 0x0}, {0xcc, 0x84, 0x0}, {0xcc, 0x86, 0x0}, {0xcc, 0x87, 0x0}, + {0xcc, 0x88, 0x0}, {0x0, 0x0, 0x0}, {0xcc, 0x8a, 0x0}, {0xcc, 0xa7, 0x0}, + {0x5f, 0x0, 0x0}, {0xcc, 0x8b, 0x0}, {0xcc, 0xa8, 0x0}, {0xcc, 0x8c, 0x0}, + {0x20, 0x15, 0x0}, {0xc2, 0xb9, 0x0}, {0xc2, 0xae, 0x0}, {0xc2, 0xa9, 0x0}, + {0x21, 0x22, 0x0}, {0x26, 0x6a, 0x0}, {0x20, 0xac, 0x0}, {0x20, 0x30, 0x0}, + {0xce, 0xb1, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x21, 0x5b, 0x0}, {0x21, 0x5c, 0x0}, {0x21, 0x5d, 0x0}, {0x21, 0x5e, 0x0}, + {0xce, 0xa9, 0x0}, {0xc3, 0x86, 0x0}, {0xc4, 0x90, 0x0}, {0xc2, 0xaa, 0x0}, + {0xc4, 0xa6, 0x0}, {0x0, 0x0, 0x0}, {0xc4, 0xb2, 0x0}, {0xc4, 0xbf, 0x0}, + {0xc5, 0x81, 0x0}, {0xc3, 0x98, 0x0}, {0xc5, 0x92, 0x0}, {0xc2, 0xba, 0x0}, + {0xc3, 0x9e, 0x0}, {0xc5, 0xa6, 0x0}, {0xc5, 0x8a, 0x0}, {0xc5, 0x89, 0x0}, + {0xc4, 0xb8, 0x0}, {0xc3, 0xa6, 0x0}, {0xc4, 0x91, 0x0}, {0xc3, 0xb0, 0x0}, + {0xc4, 0xa7, 0x0}, {0xc4, 0xb1, 0x0}, {0xc4, 0xb3, 0x0}, {0xc5, 0x80, 0x0}, + {0xc5, 0x82, 0x0}, {0xc3, 0xb8, 0x0}, {0xc5, 0x93, 0x0}, {0xc3, 0x9f, 0x0}, + {0xc3, 0xbe, 0x0}, {0xc5, 0xa7, 0x0}, {0xc5, 0x8b, 0x0}, {0x20, 0x0, 0x0}}; + +const uint8_t TELETEXT_G2_LATIN_ACCENTS[15][52][3] = { + { + {0xc3, 0x80, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0xc3, 0x88, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0xc3, 0x8c, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0xc3, 0x92, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0xc3, 0x99, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0xc3, 0xa0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0xc3, 0xa8, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0xc3, 0xac, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0xc3, 0xb2, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0xc3, 0xb9, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + }, + { + {0xc3, 0x81, 0x0}, {0x0, 0x0, 0x0}, {0xc4, 0x86, 0x0}, + {0x0, 0x0, 0x0}, {0xc3, 0x89, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0xc3, 0x8d, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0xc4, 0xb9, 0x0}, + {0x0, 0x0, 0x0}, {0xc5, 0x83, 0x0}, {0xc3, 0x93, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0xc5, 0x94, 0x0}, + {0xc5, 0x9a, 0x0}, {0x0, 0x0, 0x0}, {0xc3, 0x9a, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0xc3, 0x9d, 0x0}, {0xc5, 0xb9, 0x0}, {0xc3, 0xa1, 0x0}, + {0x0, 0x0, 0x0}, {0xc4, 0x87, 0x0}, {0x0, 0x0, 0x0}, + {0xc3, 0xa9, 0x0}, {0x0, 0x0, 0x0}, {0xc4, 0xa3, 0x0}, + {0x0, 0x0, 0x0}, {0xc3, 0xad, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0xc4, 0xba, 0x0}, {0x0, 0x0, 0x0}, + {0xc5, 0x84, 0x0}, {0xc3, 0xb3, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0xc5, 0x95, 0x0}, {0xc5, 0x9b, 0x0}, + {0x0, 0x0, 0x0}, {0xc3, 0xba, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0xc3, 0xbd, 0x0}, + {0xc5, 0xba, 0x0}, + }, + { + {0xc3, 0x82, 0x0}, {0x0, 0x0, 0x0}, {0xc4, 0x88, 0x0}, + {0x0, 0x0, 0x0}, {0xc3, 0x8a, 0x0}, {0x0, 0x0, 0x0}, + {0xc4, 0x9c, 0x0}, {0xc4, 0xa4, 0x0}, {0xc3, 0x8e, 0x0}, + {0xc4, 0xb4, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0xc3, 0x94, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0xc5, 0x9c, 0x0}, {0x0, 0x0, 0x0}, {0xc3, 0x9b, 0x0}, + {0x0, 0x0, 0x0}, {0xc5, 0xb4, 0x0}, {0x0, 0x0, 0x0}, + {0xc5, 0xb6, 0x0}, {0x0, 0x0, 0x0}, {0xc3, 0xa2, 0x0}, + {0x0, 0x0, 0x0}, {0xc4, 0x89, 0x0}, {0x0, 0x0, 0x0}, + {0xc3, 0xaa, 0x0}, {0x0, 0x0, 0x0}, {0xc4, 0x9d, 0x0}, + {0xc4, 0xa5, 0x0}, {0xc3, 0xae, 0x0}, {0xc4, 0xb5, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0xc3, 0xb4, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0xc5, 0x9d, 0x0}, + {0x0, 0x0, 0x0}, {0xc3, 0xbb, 0x0}, {0x0, 0x0, 0x0}, + {0xc5, 0xb5, 0x0}, {0x0, 0x0, 0x0}, {0xc5, 0xb7, 0x0}, + {0x0, 0x0, 0x0}, + }, + { + {0xc3, 0x83, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0xc4, 0xa8, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0xc3, 0x91, 0x0}, {0xc3, 0x95, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0xc5, 0xa8, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0xc3, 0xa3, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0xc4, 0xa9, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0xc3, 0xb1, 0x0}, {0xc3, 0xb5, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0xc5, 0xa9, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, + }, + { + {0xc4, 0x80, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0xc4, 0x92, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0xc4, 0xaa, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0xc5, 0x8c, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0xc5, 0xaa, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0xc4, 0x81, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0xc4, 0x93, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0xc4, 0xab, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0xc5, 0x8d, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0xc5, 0xab, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + }, + { + {0xc4, 0x82, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0xc4, 0x9e, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0xc5, 0xac, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0xc4, 0x83, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0xc4, 0x9f, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0xc5, 0xad, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + }, + { + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0xc4, 0x8a, 0x0}, + {0x0, 0x0, 0x0}, {0xc4, 0x96, 0x0}, {0x0, 0x0, 0x0}, + {0xc4, 0xa0, 0x0}, {0x0, 0x0, 0x0}, {0xc4, 0xb0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0xc5, 0xbb, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0xc4, 0x8b, 0x0}, {0x0, 0x0, 0x0}, + {0xc4, 0x97, 0x0}, {0x0, 0x0, 0x0}, {0xc4, 0xa1, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0xc5, 0xbc, 0x0}, + }, + { + {0xc3, 0x84, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0xc3, 0x8b, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0xc3, 0x8f, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0xc3, 0x96, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0xc3, 0x9c, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0xc5, 0xb8, 0x0}, {0x0, 0x0, 0x0}, {0xc3, 0xa4, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0xc3, 0xab, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0xc3, 0xaf, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0xc3, 0xb6, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0xc3, 0xbc, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0xc3, 0xbf, 0x0}, {0x0, 0x0, 0x0}, + }, + { + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + }, + { + {0xc3, 0x85, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0xc5, 0xae, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0xc3, 0xa5, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0xc5, 0xaf, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + }, + { + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0xc3, 0x87, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0xc4, 0xa2, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0xc4, 0xb6, 0x0}, {0xc4, 0xbb, 0x0}, + {0x0, 0x0, 0x0}, {0xc5, 0x85, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0xc5, 0x96, 0x0}, + {0xc5, 0x9e, 0x0}, {0xc5, 0xa2, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0xc3, 0xa7, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0xc4, 0xb7, 0x0}, {0xc4, 0xbc, 0x0}, {0x0, 0x0, 0x0}, + {0xc5, 0x86, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0xc5, 0x97, 0x0}, {0xc5, 0x9f, 0x0}, + {0xc5, 0xa3, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, + }, + { + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + }, + { + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0xc5, 0x90, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0xc5, 0xb0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0xc5, 0x91, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0xc5, 0xb1, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + }, + { + {0xc4, 0x84, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0xc4, 0x98, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0xc4, 0xae, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0xc5, 0xb2, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0xc4, 0x85, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0xc4, 0x99, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0xc4, 0xaf, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0xc5, 0xb3, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + }, + { + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0xc4, 0x8c, 0x0}, + {0xc4, 0x8e, 0x0}, {0xc4, 0x9a, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0xc4, 0xbd, 0x0}, + {0x0, 0x0, 0x0}, {0xc5, 0x87, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0xc5, 0x98, 0x0}, + {0xc5, 0xa0, 0x0}, {0xc5, 0xa4, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0xc5, 0xbd, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0xc4, 0x8d, 0x0}, {0xc4, 0x8f, 0x0}, + {0xc4, 0x9b, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0xc4, 0xbe, 0x0}, {0x0, 0x0, 0x0}, + {0xc5, 0x88, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0xc5, 0x99, 0x0}, {0xc5, 0xa1, 0x0}, + {0xc5, 0xa5, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0, 0x0}, + {0xc5, 0xbe, 0x0}, + }}; + +} // namespace mp2t +} // namespace media +} // namespace shaka + +#endif // PACKAGER_MEDIA_FORMATS_MP2T_ES_PARSER_TELETEXT_TABLES_H_ diff --git a/packager/media/formats/mp2t/es_parser_teletext_unittest.cc b/packager/media/formats/mp2t/es_parser_teletext_unittest.cc new file mode 100644 index 0000000000..64f52b97a8 --- /dev/null +++ b/packager/media/formats/mp2t/es_parser_teletext_unittest.cc @@ -0,0 +1,427 @@ +// Copyright 2016 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +#include +#include +#include + +#include "packager/media/base/text_sample.h" +#include "packager/media/base/text_stream_info.h" +#include "packager/media/formats/mp2t/es_parser_teletext.h" + +namespace shaka { +namespace media { +namespace mp2t { + +namespace { + +const uint8_t DESCRIPTOR[] = {0x56, 0x0a, 0x63, 0x61, 0x74, 0x09, + 0x00, 0x63, 0x61, 0x74, 0x10, 0x88}; + +const uint8_t PES_283413[] = { + 0x10, 0x02, 0x2c, 0xe7, 0xe4, 0x92, 0x85, 0x80, 0xfe, 0x6b, 0x97, 0xce, + 0x97, 0x2f, 0xa7, 0xce, 0x40, 0xfe, 0x0b, 0xfb, 0x46, 0x37, 0x97, 0xc7, + 0xc1, 0x04, 0xfe, 0x32, 0x86, 0x04, 0x43, 0xf7, 0x2f, 0x97, 0xe6, 0x86, + 0x61, 0xfe, 0x05, 0x76, 0x26, 0xa7, 0x1f, 0x04, 0x2a, 0x6b, 0xc2, 0x03, + 0x2c, 0xe8, 0xe4, 0xa8, 0xa8, 0x0b, 0x0b, 0xa8, 0x0b, 0xa8, 0x0b, 0xf4, + 0xd9, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x23, 0xc7, 0x75, 0x8c, 0x1c, 0x04, 0x04, + 0x04, 0x8c, 0x8c, 0x8c, 0x04, 0x8c, 0x6d, 0x5d, 0x0d, 0x03, 0x2c, 0xe9, + 0xe4, 0xa8, 0xa8, 0x0b, 0x0b, 0xa8, 0x0b, 0xa8, 0x0b, 0xf4, 0xd9, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x23, 0xc7, 0x75, 0x8c, 0x1c, 0x04, 0x04, 0x04, 0x8c, + 0x8c, 0x8c, 0x04, 0x8c, 0x6d, 0x5d, 0x0d, 0x03, 0x2c, 0xea, 0xe4, 0xa8, + 0xa8, 0x0b, 0x0b, 0xa8, 0x0b, 0xa8, 0x0b, 0xf4, 0xd9, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x23, 0xc7, 0x75, 0x8c, 0x1c, 0x04, 0x04, 0x04, 0x8c, 0x8c, 0x8c, + 0x04, 0x8c, 0x6d, 0x5d, 0x0d, 0x03, 0x2c, 0xc7, 0xe4, 0xa8, 0x6d, 0xa8, + 0x2e, 0xfe, 0xff, 0x2e, 0xfe, 0xff, 0x2e, 0xfe, 0xff, 0x2e, 0xfe, 0xff, + 0x2e, 0xfe, 0xff, 0x2e, 0xfe, 0xff, 0x2e, 0xfe, 0xff, 0x2e, 0xfe, 0xff, + 0x2e, 0xfe, 0xff, 0x2e, 0xfe, 0xff, 0x2e, 0xfe, 0xff, 0x2e, 0xfe, 0xff, + 0xb7, 0xc0, 0x00, 0x03, 0x2c, 0xc8, 0xe4, 0x0b, 0x6d, 0xa8, 0x57, 0x57, + 0x57, 0xf4, 0x57, 0x7a, 0x57, 0x57, 0x57, 0xf4, 0x57, 0x7a, 0x57, 0x57, + 0x57, 0xf4, 0x57, 0x7a, 0x57, 0x57, 0x57, 0xf4, 0x57, 0x7a, 0x57, 0x57, + 0x57, 0xf4, 0x57, 0x7a, 0x57, 0x57, 0x57, 0xf4, 0x57, 0x7a, 0xa8, 0xec, + 0x82, 0x03, 0x2c, 0xc9, 0xe4, 0xa8, 0xd9, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0xb0, 0xd0, 0xd0, + 0x43, 0xf7, 0x76, 0x04, 0x26, 0x97, 0x86, 0x85, 0x51, 0x51, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04}; + +const uint8_t PES_407876[] = { + 0x10, 0x02, 0x2c, 0xe7, 0xe4, 0x7a, 0x85, 0x80, 0xfe, 0x0b, 0x7f, 0xe6, + 0x75, 0xd5, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x61, 0xfe, 0x0b, 0x7f, 0xe6, 0x75, 0xb5, 0x03, + 0x2c, 0xe8, 0xe4, 0xa8, 0xa8, 0x0b, 0x0b, 0xa8, 0x0b, 0xa8, 0x0b, 0xf4, + 0xd9, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x23, 0xc7, 0x75, 0x8c, 0x1c, 0x04, 0x04, + 0x04, 0x04, 0x8c, 0x6d, 0x5d, 0x0d, 0xcd, 0x5d, 0x4c, 0x03, 0x2c, 0xe9, + 0xe4, 0xa8, 0xa8, 0x0b, 0x0b, 0xa8, 0x0b, 0xa8, 0x0b, 0xf4, 0xd9, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x23, 0xc7, 0x75, 0x8c, 0x1c, 0x04, 0x04, 0x04, 0x04, + 0x8c, 0x6d, 0x5d, 0x0d, 0xcd, 0x5d, 0x4c, 0x03, 0x2c, 0xea, 0xe4, 0xa8, + 0xa8, 0x0b, 0x0b, 0xa8, 0x0b, 0xa8, 0x0b, 0xf4, 0xd9, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x23, 0xc7, 0x75, 0x8c, 0x1c, 0x04, 0x04, 0x04, 0x04, 0x8c, 0x6d, + 0x5d, 0x0d, 0xcd, 0x5d, 0x4c, 0x03, 0x2c, 0xc7, 0xe4, 0xa8, 0x6d, 0xa8, + 0x2e, 0xfe, 0xff, 0x2e, 0xfe, 0xff, 0x2e, 0xfe, 0xff, 0x2e, 0xfe, 0xff, + 0x2e, 0xfe, 0xff, 0x2e, 0xfe, 0xff, 0x2e, 0xfe, 0xff, 0x2e, 0xfe, 0xff, + 0x2e, 0xfe, 0xff, 0x2e, 0xfe, 0xff, 0x2e, 0xfe, 0xff, 0x2e, 0xfe, 0xff, + 0xf9, 0x2d, 0x00, 0x03, 0x2c, 0xc8, 0xe4, 0x0b, 0x6d, 0xa8, 0x57, 0x57, + 0x57, 0xf4, 0x57, 0x7a, 0x57, 0x57, 0x57, 0xf4, 0x57, 0x7a, 0x57, 0x57, + 0x57, 0xf4, 0x57, 0x7a, 0x57, 0x57, 0x57, 0xf4, 0x57, 0x7a, 0x57, 0x57, + 0x57, 0xf4, 0x57, 0x7a, 0x57, 0x57, 0x57, 0xf4, 0x57, 0x7a, 0xa8, 0x00, + 0x00, 0x02, 0x2c, 0xc9, 0xe4, 0x40, 0xa8, 0xa8, 0xa8, 0x40, 0x0b, 0xa8, + 0xa8, 0xa8, 0x31, 0x2a, 0x6b, 0xcd, 0x2a, 0xa2, 0x1a, 0x2a, 0xc1, 0x2a, + 0x6b, 0xcd, 0xe0, 0x8c, 0x0d, 0x0d, 0x04, 0x23, 0xc7, 0x75, 0x8c, 0x1c, + 0xc1, 0x04, 0x04, 0x04, 0x8c, 0x6d, 0x5d, 0x0d, 0xcd, 0x5d, 0x4c}; + +const uint8_t PES_8768632[] = { + 0x10, 0x02, 0x2c, 0xe7, 0xe4, 0x92, 0x0b, 0xc8, 0x34, 0x34, 0x34, 0x34, + 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, + 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, + 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x02, + 0x2c, 0xe8, 0xe4, 0x92, 0xe3, 0x40, 0x73, 0xf7, 0x2f, 0xba, 0xc7, 0x97, + 0xa7, 0xce, 0x04, 0xa7, 0xce, 0x0e, 0xf7, 0x4f, 0x2f, 0x97, 0x6e, 0xa7, + 0xce, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, + 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x8c, 0xad, 0x0d, 0x02, 0x2c, 0xe9, + 0xe4, 0x31, 0x31, 0xc8, 0xb9, 0x01, 0xb0, 0x0b, 0x4f, 0xf7, 0xe6, 0x4f, + 0x86, 0xb6, 0x86, 0xc7, 0x97, 0x7a, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, + 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, + 0x75, 0x75, 0x75, 0x75, 0xcd, 0x0d, 0x0d, 0x02, 0x2c, 0xea, 0xe4, 0x31, + 0xd9, 0xe0, 0xb9, 0x01, 0x04, 0x92, 0x76, 0x67, 0xf7, 0x4f, 0xb6, 0x86, + 0xc7, 0x97, 0x7a, 0x04, 0x2a, 0x6b, 0xc2, 0x75, 0x75, 0x75, 0x75, 0x75, + 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, + 0x75, 0x75, 0x4c, 0x1c, 0x0d, 0x02, 0x2c, 0xc7, 0xe4, 0x92, 0x85, 0x80, + 0xfe, 0x62, 0x75, 0xcb, 0x86, 0x37, 0x86, 0x04, 0xb6, 0x86, 0xce, 0x75, + 0x04, 0x40, 0xfe, 0x43, 0x7f, 0xce, 0x8f, 0xae, 0xa7, 0x2f, 0xc1, 0xfe, + 0x62, 0xae, 0x2f, 0x46, 0xf7, 0x37, 0x61, 0xfe, 0xa2, 0xce, 0x0e, 0xf7, + 0x4f, 0x2f, 0xce, 0x03, 0x2c, 0xc8, 0xe4, 0xa8, 0xa8, 0x0b, 0x0b, 0xa8, + 0x0b, 0xa8, 0x0b, 0xf4, 0xd9, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x23, 0xc7, 0x75, + 0x8c, 0x1c, 0x04, 0x04, 0x04, 0x97, 0x2f, 0xae, 0x37, 0x86, 0x4f, 0xce, + 0x75, 0x03, 0x2c, 0xc9, 0xe4, 0xa8, 0xa8, 0x0b, 0x0b, 0xa8, 0x0b, 0xa8, + 0x0b, 0xf4, 0xd9, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x23, 0xc7, 0x75, 0x8c, 0x1c, + 0x04, 0x04, 0x04, 0x97, 0x2f, 0xae, 0x37, 0x86, 0x4f, 0xce, 0x75}; + +const uint8_t PES_8773087[] = { + 0x10, 0x03, 0x2c, 0xe7, 0xe4, 0xa8, 0x6d, 0xa8, 0x2e, 0xfe, 0xff, 0x2e, + 0xfe, 0xff, 0x2e, 0xfe, 0xff, 0x2e, 0xfe, 0xff, 0x2e, 0xfe, 0xff, 0x2e, + 0xfe, 0xff, 0x2e, 0xfe, 0xff, 0x2e, 0xfe, 0xff, 0x2e, 0xfe, 0xff, 0x2e, + 0xfe, 0xff, 0x2e, 0xfe, 0xff, 0x2e, 0xfe, 0xff, 0xdc, 0x08, 0x00, 0x03, + 0x2c, 0xe8, 0xe4, 0x0b, 0x6d, 0xa8, 0x57, 0x57, 0x57, 0xf4, 0x57, 0x7a, + 0x57, 0x57, 0x57, 0xf4, 0x57, 0x7a, 0x57, 0x57, 0x57, 0xf4, 0x57, 0x7a, + 0x57, 0x57, 0x57, 0xf4, 0x57, 0x7a, 0x57, 0x57, 0x57, 0xf4, 0x57, 0x7a, + 0x57, 0x57, 0x57, 0xf4, 0x57, 0x7a, 0xa8, 0x25, 0xee, 0x03, 0x2c, 0xe9, + 0xe4, 0xa8, 0x31, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0xb0, 0xd0, 0xd0, 0xb5, 0xcb, + 0xba, 0xfd, 0x51, 0x51, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x2c, 0xea, 0xe4, 0xa8, + 0xd9, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0xb0, 0xd0, 0xd0, 0xb5, 0xcb, 0xba, 0x75, + 0x51, 0x51, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x02, 0x2c, 0xc7, 0xe4, 0x92, 0xa8, 0x40, + 0x1c, 0x40, 0x0b, 0xa8, 0xa8, 0x92, 0xd9, 0x2a, 0x6b, 0xcd, 0x2a, 0xa2, + 0x1a, 0x2a, 0xc1, 0x2a, 0x6b, 0xcd, 0xe0, 0x4c, 0x6d, 0x8c, 0x04, 0x23, + 0xc7, 0x75, 0x8c, 0x1c, 0xc1, 0x04, 0x04, 0x2f, 0xae, 0x37, 0x86, 0x4f, + 0xce, 0x75, 0x75, 0x02, 0x2c, 0xc8, 0xe4, 0x92, 0xa8, 0x40, 0x1c, 0x40, + 0x0b, 0xa8, 0xa8, 0x92, 0xd9, 0x2a, 0x6b, 0xcd, 0x2a, 0xa2, 0x1a, 0x2a, + 0xc1, 0x2a, 0x6b, 0xcd, 0xe0, 0x4c, 0x6d, 0x8c, 0x04, 0x23, 0xc7, 0x75, + 0x8c, 0x1c, 0xc1, 0x04, 0x04, 0x2f, 0xae, 0x37, 0x86, 0x4f, 0xce, 0x75, + 0x75, 0x02, 0x2c, 0xc9, 0xe4, 0x92, 0xa8, 0x40, 0x1c, 0x40, 0x0b, 0xa8, + 0xa8, 0x92, 0xd9, 0x2a, 0x6b, 0xcd, 0x2a, 0xa2, 0x1a, 0x2a, 0xc1, 0x2a, + 0x6b, 0xcd, 0xe0, 0x4c, 0x6d, 0x8c, 0x04, 0x23, 0xc7, 0x75, 0x8c, 0x1c, + 0xc1, 0x04, 0x04, 0x2f, 0xae, 0x37, 0x86, 0x4f, 0xce, 0x75, 0x75}; + +const uint8_t PES_8937764[] = { + 0x10, 0x02, 0x2c, 0xe7, 0xe4, 0x31, 0xe3, 0x04, 0x61, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x86, 0xc7, 0x86, 0xce, 0x86, + 0xce, 0x15, 0x86, 0x94, 0x2f, 0x6e, 0xcd, 0x75, 0xc7, 0x86, 0x2f, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x02, + 0x2c, 0xe8, 0xe4, 0x92, 0x31, 0x04, 0x80, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x15, 0x86, + 0x94, 0xbc, 0x86, 0x4f, 0x4f, 0xf7, 0x6e, 0x86, 0x04, 0x02, 0x2c, 0xe9, + 0xe4, 0x31, 0x31, 0x80, 0xb9, 0x01, 0xb0, 0xa2, 0x37, 0x04, 0x2f, 0xa7, + 0xb6, 0x0e, 0xce, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, + 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, + 0x75, 0x75, 0x75, 0x75, 0x8c, 0x6d, 0x0d, 0x02, 0x2c, 0xea, 0xe4, 0x31, + 0xd9, 0xe0, 0xb9, 0x01, 0x04, 0x05, 0x76, 0x26, 0xa7, 0x1f, 0x04, 0x4f, + 0x7f, 0x0e, 0x97, 0x26, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, + 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, + 0x75, 0x75, 0x4c, 0x1c, 0x4c, 0x02, 0x2c, 0xc7, 0xe4, 0x92, 0x85, 0x80, + 0xfe, 0x05, 0x76, 0x26, 0xa7, 0x1f, 0x40, 0xfe, 0x0b, 0x4f, 0xf7, 0xe6, + 0x4f, 0x86, 0xb6, 0x86, 0xc7, 0x97, 0x7a, 0xc1, 0xfe, 0xa2, 0xce, 0x0e, + 0xf7, 0x4f, 0x2f, 0xce, 0x61, 0xfe, 0x32, 0xf7, 0x2f, 0xa7, 0x4f, 0x97, + 0xa7, 0xce, 0x04, 0x03, 0x2c, 0xc8, 0xe4, 0xa8, 0xa8, 0x0b, 0x0b, 0xa8, + 0x0b, 0xa8, 0x0b, 0xf4, 0xd9, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x23, 0xc7, 0x75, + 0x8c, 0x1c, 0x04, 0x04, 0x04, 0x86, 0x4f, 0xce, 0x75, 0x75, 0x75, 0x8c, + 0x8c, 0x03, 0x2c, 0xc9, 0xe4, 0xa8, 0xa8, 0x0b, 0x0b, 0xa8, 0x0b, 0xa8, + 0x0b, 0xf4, 0xd9, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x23, 0xc7, 0x75, 0x8c, 0x1c, + 0x04, 0x04, 0x04, 0x86, 0x4f, 0xce, 0x75, 0x75, 0x75, 0x8c, 0x8c}; + +// Start (packet0, page88) with packet26 and row 18 left (packet18) +const uint8_t PES_867681[] = { + 0x10, 0x03, 0x2c, 0xf6, 0xe4, 0xa8, 0xa8, 0x0b, 0x0b, 0xa8, 0x0b, 0xa8, + 0x0b, 0xf4, 0xa8, 0xb3, 0x83, 0x32, 0xa2, 0x73, 0x2a, 0xa2, 0x73, 0x23, + 0x83, 0x73, 0x2a, 0xcb, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, + 0x2c, 0xd4, 0xe4, 0xa8, 0x6d, 0xa8, 0x9e, 0xc9, 0x00, 0x4e, 0x93, 0xa7, + 0x90, 0x53, 0xa7, 0x2e, 0xfe, 0xff, 0x2e, 0xfe, 0xff, 0x2e, 0xfe, 0xff, + 0x2e, 0xfe, 0xff, 0x2e, 0xfe, 0xff, 0x2e, 0xfe, 0xff, 0x2e, 0xfe, 0xff, + 0x2e, 0xfe, 0xff, 0x2e, 0xfe, 0xff, 0x2e, 0xfe, 0xff, 0x03, 0x2c, 0xd6, + 0xe4, 0xa8, 0xe3, 0xb0, 0x04, 0x04, 0x04, 0x04, 0xd0, 0xd0, 0xb5, 0x32, + 0xae, 0xd6, 0xa7, 0x04, 0x85, 0x51, 0x51, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04}; + +// row 22 right (packet22) +const uint8_t PES_871281[] = { + 0x10, 0x03, 0x2c, 0xf4, 0xe4, 0xa8, 0xd9, 0xb0, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0xd0, 0xd0, 0xb5, 0x52, 0xa7, 0x04, 0x6e, 0x86, 0x97, 0xce, + 0x04, 0x86, 0xae, 0x1f, 0x04, 0xc7, 0xf7, 0xae, 0x4f, 0xce, 0x04, 0x26, + 0xe5, 0xa7, 0x2f, 0xa7, 0x75, 0x51, 0x51, 0x04, 0x04, 0x04, 0x04}; + +// End (packet 0, page 88) +const uint8_t PES_1011695[] = { + 0x10, 0x03, 0x2c, 0xf6, 0xe4, 0xa8, 0xa8, 0x0b, 0x0b, 0xa8, 0x0b, 0xa8, + 0x0b, 0xf4, 0xa8, 0xb3, 0x83, 0x32, 0xa2, 0x73, 0x2a, 0xa2, 0x73, 0x23, + 0x83, 0x73, 0x2a, 0xcb, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04}; + +// Start (packet0, page 88) with packet26 and row 20 (centered yellow) +// (packet20) +const uint8_t PES_1033297[] = { + 0x10, 0x03, 0x2c, 0xf6, 0xe4, 0xa8, 0xa8, 0x0b, 0x0b, 0xa8, 0x0b, 0xa8, + 0x0b, 0xf4, 0xa8, 0xb3, 0x83, 0x32, 0xa2, 0x73, 0x2a, 0xa2, 0x73, 0x23, + 0x83, 0x73, 0x2a, 0xcb, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, + 0x2c, 0xd4, 0xe4, 0xa8, 0x6d, 0xa8, 0x06, 0xc9, 0x01, 0x62, 0x93, 0xa6, + 0x9e, 0xc9, 0x00, 0xf4, 0xa2, 0x86, 0x06, 0xa3, 0xa7, 0x2e, 0xfe, 0xff, + 0x2e, 0xfe, 0xff, 0x2e, 0xfe, 0xff, 0x2e, 0xfe, 0xff, 0x2e, 0xfe, 0xff, + 0x2e, 0xfe, 0xff, 0x2e, 0xfe, 0xff, 0x2e, 0xfe, 0xff, 0x03, 0x2c, 0xd6, + 0xe4, 0xa8, 0x31, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0xb0, 0xc1, 0xd0, 0xd0, 0x52, 0xe5, 0x86, 0x97, 0x04, + 0x37, 0xf7, 0xae, 0x0e, 0xa7, 0x51, 0x51, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04}; + +// row 22 centered blue on yellow background (packet22) +const uint8_t PES_1036900[] = { + 0x10, 0x03, 0x2c, 0xf4, 0xe4, 0xa8, 0xd9, 0x04, 0x04, 0xb0, 0xc1, 0xb9, + 0x20, 0xd0, 0xd0, 0x37, 0xe5, 0x97, 0x76, 0x97, 0x2f, 0x97, 0x86, 0x2f, + 0x97, 0xf7, 0x76, 0x04, 0x86, 0x04, 0x37, 0xe5, 0x86, 0x37, 0xe6, 0xa7, + 0x46, 0x4f, 0xa7, 0x75, 0x51, 0x51, 0x04, 0x04, 0x04, 0x04, 0x04}; + +// End (packet 0, page 88) +const uint8_t PES_1173713[] = { + 0x10, 0x03, 0x2c, 0xf6, 0xe4, 0xa8, 0xa8, 0x0b, 0x0b, 0xa8, 0x0b, 0xa8, + 0x0b, 0xf4, 0xa8, 0xb3, 0x83, 0x32, 0xa2, 0x73, 0x2a, 0xa2, 0x73, 0x23, + 0x83, 0x73, 0x2a, 0xcb, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04}; + +const uint32_t kPesPid = 123; + +} // namespace + +class EsParserTeletextTest : public ::testing::Test { + public: + void OnNewStreamInfo(uint32_t pes_pid, + std::shared_ptr stream_info) { + stream_info_ = stream_info; + } + void OnEmitTextSample(uint32_t pes_pid, + std::shared_ptr text_sample) { + text_sample_ = text_sample; + text_samples_.push_back(text_sample); + } + + protected: + std::shared_ptr stream_info_; + std::vector> text_samples_; + std::shared_ptr text_sample_; +}; + +TEST_F(EsParserTeletextTest, descriptor_substreams_has_index_888_language_cat) { + auto on_new_stream = std::bind(&EsParserTeletextTest::OnNewStreamInfo, this, + kPesPid, std::placeholders::_1); + auto on_emit_text = std::bind(&EsParserTeletextTest::OnEmitTextSample, this, + kPesPid, std::placeholders::_1); + + std::unique_ptr es_parser_teletext(new EsParserTeletext( + kPesPid, on_new_stream, on_emit_text, DESCRIPTOR, 12)); + + const auto parse_result = + es_parser_teletext->Parse(PES_283413, sizeof(PES_283413), 283413, 0); + EXPECT_TRUE(parse_result); + + EXPECT_NE(nullptr, stream_info_.get()); + auto text_stream_info = static_cast(stream_info_.get()); + auto& sub_streams = text_stream_info->sub_streams(); + + EXPECT_EQ(2, sub_streams.size()); + auto sub_streams_itr = sub_streams.find(888); + EXPECT_NE(sub_streams.end(), sub_streams_itr); + EXPECT_EQ("cat", sub_streams_itr->second.language); +} + +TEST_F(EsParserTeletextTest, pes_283413_line_emitted_on_next_pes) { + auto on_new_stream = std::bind(&EsParserTeletextTest::OnNewStreamInfo, this, + kPesPid, std::placeholders::_1); + auto on_emit_text = std::bind(&EsParserTeletextTest::OnEmitTextSample, this, + kPesPid, std::placeholders::_1); + + std::unique_ptr es_parser_teletext(new EsParserTeletext( + kPesPid, on_new_stream, on_emit_text, DESCRIPTOR, 12)); + + auto parse_result = + es_parser_teletext->Parse(PES_283413, sizeof(PES_283413), 283413, 0); + EXPECT_TRUE(parse_result); + + parse_result = + es_parser_teletext->Parse(PES_407876, sizeof(PES_407876), 407876, 0); + EXPECT_TRUE(parse_result); + + EXPECT_NE(nullptr, text_sample_.get()); + EXPECT_EQ(283413, text_sample_->start_time()); + EXPECT_EQ(407876, text_sample_->EndTime()); + EXPECT_EQ("Bon dia!", text_sample_->body().body); + EXPECT_EQ("black", text_sample_->body().style.backgroundColor); + EXPECT_EQ("white", text_sample_->body().style.color); + TextSettings settings = text_sample_->settings(); + EXPECT_EQ(TextAlignment::kCenter, settings.text_alignment); + EXPECT_TRUE(settings.line.has_value()); + EXPECT_EQ(11, settings.line.value().value); + EXPECT_EQ(TextUnitType::kLines, settings.line.value().type); +} + +TEST_F(EsParserTeletextTest, multiple_lines_with_same_pts) { + auto on_new_stream = std::bind(&EsParserTeletextTest::OnNewStreamInfo, this, + kPesPid, std::placeholders::_1); + auto on_emit_text = std::bind(&EsParserTeletextTest::OnEmitTextSample, this, + kPesPid, std::placeholders::_1); + + std::unique_ptr es_parser_teletext(new EsParserTeletext( + kPesPid, on_new_stream, on_emit_text, DESCRIPTOR, 12)); + + auto parse_result = + es_parser_teletext->Parse(PES_8768632, sizeof(PES_8768632), 8768632, 0); + EXPECT_TRUE(parse_result); + + parse_result = + es_parser_teletext->Parse(PES_8773087, sizeof(PES_8773087), 8773087, 0); + EXPECT_TRUE(parse_result); + + parse_result = + es_parser_teletext->Parse(PES_8937764, sizeof(PES_8937764), 8937764, 0); + EXPECT_TRUE(parse_result); + + EXPECT_NE(nullptr, text_sample_.get()); + EXPECT_EQ(8768632, text_sample_->start_time()); + EXPECT_EQ(8937764, text_sample_->EndTime()); + EXPECT_EQ(3, text_sample_->body().sub_fragments.size()); + EXPECT_EQ("-Sí?", text_sample_->body().sub_fragments[0].body); + EXPECT_TRUE(text_sample_->body().sub_fragments[1].newline); + EXPECT_EQ("-Sí.", text_sample_->body().sub_fragments[2].body); + TextSettings settings = text_sample_->settings(); + EXPECT_EQ(10, settings.line.value().value); + EXPECT_EQ("ttx_10", settings.region); + EXPECT_EQ(1, text_samples_.size()); +} + +// separate_lines_with_slightly_different_pts has the original lines +// 18 and 22, with different alignment, which means that they should +// result in two parallel text samples. +TEST_F(EsParserTeletextTest, separate_lines_with_slightly_different_pts) { + auto on_new_stream = std::bind(&EsParserTeletextTest::OnNewStreamInfo, this, + kPesPid, std::placeholders::_1); + auto on_emit_text = std::bind(&EsParserTeletextTest::OnEmitTextSample, this, + kPesPid, std::placeholders::_1); + + std::unique_ptr es_parser_teletext(new EsParserTeletext( + kPesPid, on_new_stream, on_emit_text, DESCRIPTOR, 12)); + + auto parse_result = + es_parser_teletext->Parse(PES_867681, sizeof(PES_867681), 867681, 0); + EXPECT_TRUE(parse_result); + + parse_result = + es_parser_teletext->Parse(PES_871281, sizeof(PES_871281), 871281, 0); + EXPECT_TRUE(parse_result); + + parse_result = + es_parser_teletext->Parse(PES_1011695, sizeof(PES_1011695), 1011695, 0); + EXPECT_TRUE(parse_result); + + EXPECT_NE(nullptr, text_sample_.get()); + EXPECT_EQ(2, text_samples_.size()); + // The subtitles should get the same start and end time + EXPECT_EQ(867681, text_samples_[0]->start_time()); + EXPECT_EQ(867681, text_samples_[1]->start_time()); + EXPECT_EQ(1011695, text_samples_[0]->EndTime()); + EXPECT_EQ(1011695, text_samples_[0]->EndTime()); + EXPECT_EQ(1, text_samples_[0]->body().sub_fragments.size()); + EXPECT_EQ(1, text_samples_[1]->body().sub_fragments.size()); + EXPECT_EQ("-Luke !", text_samples_[0]->body().sub_fragments[0].body); + EXPECT_EQ("ttx_9", text_samples_[0]->settings().region); + EXPECT_EQ(TextAlignment::kLeft, text_samples_[0]->settings().text_alignment); + EXPECT_EQ("-Je vais aux cours d'été.", + text_samples_[1]->body().sub_fragments[0].body); + EXPECT_EQ(11, text_samples_[1]->settings().line.value().value); + EXPECT_EQ("ttx_11", text_samples_[1]->settings().region); + EXPECT_EQ(TextAlignment::kCenter, + text_samples_[1]->settings().text_alignment); +} + +// consecutive_lines_with_slightly_different_pts has the original lines +// 20 and 22 with same alignment, which means that they should +// result in one text sample with two lines. +TEST_F(EsParserTeletextTest, consecutive_lines_with_slightly_different_pts) { + auto on_new_stream = std::bind(&EsParserTeletextTest::OnNewStreamInfo, this, + kPesPid, std::placeholders::_1); + auto on_emit_text = std::bind(&EsParserTeletextTest::OnEmitTextSample, this, + kPesPid, std::placeholders::_1); + + std::unique_ptr es_parser_teletext(new EsParserTeletext( + kPesPid, on_new_stream, on_emit_text, DESCRIPTOR, 12)); + + auto parse_result = + es_parser_teletext->Parse(PES_1033297, sizeof(PES_1033297), 1033297, 0); + EXPECT_TRUE(parse_result); + + parse_result = + es_parser_teletext->Parse(PES_1036900, sizeof(PES_1036900), 1036900, 0); + EXPECT_TRUE(parse_result); + + parse_result = + es_parser_teletext->Parse(PES_1173713, sizeof(PES_1173713), 1173713, 0); + EXPECT_TRUE(parse_result); + + EXPECT_NE(nullptr, text_sample_.get()); + EXPECT_EQ(1, text_samples_.size()); + // The subtitles should get the same start and end time + EXPECT_EQ(1033297, text_sample_->start_time()); + EXPECT_EQ(1173713, text_sample_->EndTime()); + EXPECT_EQ(3, text_sample_->body().sub_fragments.size()); + TextSettings settings = text_sample_->settings(); + EXPECT_EQ(10, settings.line.value().value); + EXPECT_EQ("ttx_10", settings.region); + EXPECT_EQ(TextAlignment::kCenter, settings.text_alignment); + EXPECT_EQ("J'ai loupé", text_sample_->body().sub_fragments[0].body); + EXPECT_EQ("yellow", text_sample_->body().sub_fragments[0].style.color); + EXPECT_TRUE(text_sample_->body().sub_fragments[1].newline); + EXPECT_EQ("l'initiation à l'algèbre.", + text_sample_->body().sub_fragments[2].body); + EXPECT_EQ("yellow", + text_sample_->body().sub_fragments[2].style.backgroundColor); + EXPECT_EQ("blue", text_sample_->body().sub_fragments[2].style.color); +} + +} // namespace mp2t +} // namespace media +} // namespace shaka diff --git a/packager/media/formats/mp2t/mp2t_media_parser.cc b/packager/media/formats/mp2t/mp2t_media_parser.cc index a7e4ed5adc..0683d954a3 100644 --- a/packager/media/formats/mp2t/mp2t_media_parser.cc +++ b/packager/media/formats/mp2t/mp2t_media_parser.cc @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -329,6 +330,12 @@ void Mp2tMediaParser::RegisterPes(int pmt_pid, descriptor, descriptor_length)); pid_type = PidState::kPidTextPes; break; + case TsStreamType::kTeletextSubtitles: + es_parser.reset(new EsParserTeletext(pes_pid, on_new_stream, on_emit_text, + descriptor, descriptor_length)); + pid_type = PidState::kPidTextPes; + break; + default: { auto type = static_cast(stream_type); DCHECK(type <= 0xff); diff --git a/packager/media/formats/mp2t/ts_muxer.cc b/packager/media/formats/mp2t/ts_muxer.cc index f9de46b634..c2a65f2653 100644 --- a/packager/media/formats/mp2t/ts_muxer.cc +++ b/packager/media/formats/mp2t/ts_muxer.cc @@ -8,6 +8,9 @@ #include +#include +#include + namespace shaka { namespace media { namespace mp2t { @@ -23,6 +26,16 @@ Status TsMuxer::InitializeMuxer() { if (streams().size() > 1u) return Status(error::MUXER_FAILURE, "Cannot handle more than one streams."); + if (options().segment_template.empty()) { + const std::string& file_name = options().output_file_name; + DCHECK(!file_name.empty()); + output_file_.reset(File::Open(file_name.c_str(), "w")); + if (!output_file_) { + return Status(error::FILE_FAILURE, + "Cannot open file for write " + file_name); + } + } + segmenter_.reset(new TsSegmenter(options(), muxer_listener())); Status status = segmenter_->Initialize(*streams()[0]); FireOnMediaStartEvent(); @@ -36,6 +49,9 @@ Status TsMuxer::Finalize() { Status TsMuxer::AddMediaSample(size_t stream_id, const MediaSample& sample) { DCHECK_EQ(stream_id, 0u); + + // The duration of the first sample may have been adjusted, so use + // the duration of the second sample instead. if (num_samples_ < 2) { sample_durations_[num_samples_] = sample.duration() * kTsTimescale / streams().front()->time_scale(); @@ -49,10 +65,81 @@ Status TsMuxer::AddMediaSample(size_t stream_id, const MediaSample& sample) { Status TsMuxer::FinalizeSegment(size_t stream_id, const SegmentInfo& segment_info) { DCHECK_EQ(stream_id, 0u); - return segment_info.is_subsegment - ? Status::OK - : segmenter_->FinalizeSegment(segment_info.start_timestamp, - segment_info.duration); + + if (segment_info.is_subsegment) + return Status::OK; + + Status s = segmenter_->FinalizeSegment(segment_info.start_timestamp, + segment_info.duration); + if (!s.ok()) + return s; + if (!segmenter_->segment_started()) + return Status::OK; + + int64_t segment_start_timestamp = segmenter_->segment_start_timestamp(); + + std::string segment_path = + options().segment_template.empty() + ? options().output_file_name + : GetSegmentName(options().segment_template, segment_start_timestamp, + segment_number_++, options().bandwidth); + + const int64_t file_size = segmenter_->segment_buffer()->Size(); + + RETURN_IF_ERROR(WriteSegment(segment_path, segmenter_->segment_buffer())); + + total_duration_ += segment_info.duration; + + if (muxer_listener()) { + muxer_listener()->OnNewSegment( + segment_path, + segment_info.start_timestamp * segmenter_->timescale() + + segmenter_->transport_stream_timestamp_offset(), + segment_info.duration * segmenter_->timescale(), file_size); + } + + segmenter_->set_segment_started(false); + + return Status::OK; +} + +Status TsMuxer::WriteSegment(const std::string& segment_path, + BufferWriter* segment_buffer) { + std::unique_ptr file; + + if (output_file_) { + // This is in single segment mode. + Range range; + range.start = media_ranges_.subsegment_ranges.empty() + ? 0 + : (media_ranges_.subsegment_ranges.back().end + 1); + range.end = range.start + segment_buffer->Size() - 1; + media_ranges_.subsegment_ranges.push_back(range); + } else { + file.reset(File::Open(segment_path.c_str(), "w")); + if (!file) { + return Status(error::FILE_FAILURE, + "Cannot open file for write " + segment_path); + } + } + + RETURN_IF_ERROR(segment_buffer->WriteToFile(output_file_ ? output_file_.get() + : file.get())); + + if (file) + RETURN_IF_ERROR(CloseFile(std::move(file))); + return Status::OK; +} + +Status TsMuxer::CloseFile(std::unique_ptr file) { + std::string file_name = file->file_name(); + if (!file.release()->Close()) { + return Status( + error::FILE_FAILURE, + "Cannot close file " + file_name + + ", possibly file permission issue or running out of disk space."); + } + return Status::OK; } void TsMuxer::FireOnMediaStartEvent() { @@ -66,10 +153,7 @@ void TsMuxer::FireOnMediaEndEvent() { if (!muxer_listener()) return; - // For now, there is no single file TS segmenter. So all the values passed - // here are left empty. - MuxerListener::MediaRanges range; - muxer_listener()->OnMediaEnd(range, 0); + muxer_listener()->OnMediaEnd(media_ranges_, total_duration_); } } // namespace mp2t diff --git a/packager/media/formats/mp2t/ts_muxer.h b/packager/media/formats/mp2t/ts_muxer.h index 9f55c2f0e4..d184c52587 100644 --- a/packager/media/formats/mp2t/ts_muxer.h +++ b/packager/media/formats/mp2t/ts_muxer.h @@ -30,12 +30,27 @@ class TsMuxer : public Muxer { Status FinalizeSegment(size_t stream_id, const SegmentInfo& sample) override; + Status WriteSegment(const std::string& segment_path, + BufferWriter* segment_buffer); + Status CloseFile(std::unique_ptr file); + void FireOnMediaStartEvent(); void FireOnMediaEndEvent(); std::unique_ptr segmenter_; - int64_t sample_durations_[2]; - int64_t num_samples_ = 0; + int64_t sample_durations_[2] = {0, 0}; + size_t num_samples_ = 0; + + // Used in multi-segment mode for segment template. + uint64_t segment_number_ = 0; + + // Used in single segment mode. + std::unique_ptr output_file_; + + // Keeps track of segment ranges in single segment mode. + MuxerListener::MediaRanges media_ranges_; + + uint64_t total_duration_ = 0; DISALLOW_COPY_AND_ASSIGN(TsMuxer); }; diff --git a/packager/media/formats/mp2t/ts_section_pmt.cc b/packager/media/formats/mp2t/ts_section_pmt.cc index f8748017e0..6c3b8895c2 100644 --- a/packager/media/formats/mp2t/ts_section_pmt.cc +++ b/packager/media/formats/mp2t/ts_section_pmt.cc @@ -21,6 +21,7 @@ namespace { const int kISO639LanguageDescriptor = 0x0A; const int kMaximumBitrateDescriptor = 0x0E; +const int kTeletextDescriptor = 0x56; const int kSubtitlingDescriptor = 0x59; } // namespace @@ -125,9 +126,17 @@ bool TsSectionPmt::ParsePsiSection(BitReader* bit_reader) { es_info_length -= 2; // See ETSI EN 300 468 Section 6.1 - if (stream_type == TsStreamType::kPesPrivateData && - descriptor_tag == kSubtitlingDescriptor) { - pid_info.back().stream_type = TsStreamType::kDvbSubtitles; + if (stream_type == TsStreamType::kPesPrivateData) { + switch (descriptor_tag) { + case kTeletextDescriptor: + pid_info.back().stream_type = TsStreamType::kTeletextSubtitles; + break; + case kSubtitlingDescriptor: + pid_info.back().stream_type = TsStreamType::kDvbSubtitles; + break; + default: + break; + } } else if (descriptor_tag == kISO639LanguageDescriptor && descriptor_length >= 4) { // See section 2.6.19 of ISO-13818 diff --git a/packager/media/formats/mp2t/ts_segmenter.cc b/packager/media/formats/mp2t/ts_segmenter.cc index f2daaeafde..c50a35d8d3 100644 --- a/packager/media/formats/mp2t/ts_segmenter.cc +++ b/packager/media/formats/mp2t/ts_segmenter.cc @@ -37,8 +37,7 @@ bool IsVideoCodec(Codec codec) { } // namespace TsSegmenter::TsSegmenter(const MuxerOptions& options, MuxerListener* listener) - : muxer_options_(options), - listener_(listener), + : listener_(listener), transport_stream_timestamp_offset_( options.transport_stream_timestamp_offset_ms * kTsTimescale / 1000), pes_packet_generator_( @@ -47,8 +46,6 @@ TsSegmenter::TsSegmenter(const MuxerOptions& options, MuxerListener* listener) TsSegmenter::~TsSegmenter() {} Status TsSegmenter::Initialize(const StreamInfo& stream_info) { - if (muxer_options_.segment_template.empty()) - return Status(error::MUXER_FAILURE, "Segment template not specified."); if (!pes_packet_generator_->Initialize(stream_info)) { return Status(error::MUXER_FAILURE, "Failed to initialize PesPacketGenerator."); @@ -172,40 +169,6 @@ Status TsSegmenter::FinalizeSegment(int64_t start_timestamp, int64_t duration) { Status status = WritePesPackets(); if (!status.ok()) return status; - - // This method may be called from Finalize() so segment_started_ could - // be false. - if (!segment_started_) - return Status::OK; - std::string segment_path = - GetSegmentName(muxer_options_.segment_template, segment_start_timestamp_, - segment_number_++, muxer_options_.bandwidth); - - const int64_t file_size = segment_buffer_.Size(); - std::unique_ptr segment_file; - segment_file.reset(File::Open(segment_path.c_str(), "w")); - if (!segment_file) { - return Status(error::FILE_FAILURE, - "Cannot open file for write " + segment_path); - } - - RETURN_IF_ERROR(segment_buffer_.WriteToFile(segment_file.get())); - - if (!segment_file.release()->Close()) { - return Status( - error::FILE_FAILURE, - "Cannot close file " + segment_path + - ", possibly file permission issue or running out of disk space."); - } - - if (listener_) { - listener_->OnNewSegment(segment_path, - start_timestamp * timescale_scale_ + - transport_stream_timestamp_offset_, - duration * timescale_scale_, file_size); - } - segment_started_ = false; - return Status::OK; } diff --git a/packager/media/formats/mp2t/ts_segmenter.h b/packager/media/formats/mp2t/ts_segmenter.h index 7c765a6029..e551feea87 100644 --- a/packager/media/formats/mp2t/ts_segmenter.h +++ b/packager/media/formats/mp2t/ts_segmenter.h @@ -24,9 +24,6 @@ class MuxerListener; namespace mp2t { -// TODO(rkuroiwa): For now, this implements multifile segmenter. Like other -// make this an abstract super class and implement multifile and single file -// segmenters. class TsSegmenter { public: // TODO(rkuroiwa): Add progress listener? @@ -72,13 +69,22 @@ class TsSegmenter { /// Only for testing. void SetSegmentStartedForTesting(bool value); + int64_t segment_start_timestamp() const { return segment_start_timestamp_; } + BufferWriter* segment_buffer() { return &segment_buffer_; } + void set_segment_started(bool value) { segment_started_ = value; } + bool segment_started() const { return segment_started_; } + + double timescale() const { return timescale_scale_; } + uint32_t transport_stream_timestamp_offset() const { + return transport_stream_timestamp_offset_; + } + private: Status StartSegmentIfNeeded(int64_t next_pts); // Writes PES packets (carried in TsPackets) to a buffer. Status WritePesPackets(); - const MuxerOptions& muxer_options_; MuxerListener* const listener_; // Codec for the stream. @@ -87,18 +93,16 @@ class TsSegmenter { const int32_t transport_stream_timestamp_offset_ = 0; // Scale used to scale the input stream to TS's timesccale (which is 90000). + // Used for calculating the duration in seconds fo the current segment. double timescale_scale_ = 1.0; - // Used for segment template. - uint64_t segment_number_ = 0; - std::unique_ptr ts_writer_; BufferWriter segment_buffer_; // Set to true if segment_buffer_ is initialized, set to false after - // FinalizeSegment() succeeds. + // FinalizeSegment() succeeds in ts_muxer. bool segment_started_ = false; std::unique_ptr pes_packet_generator_; diff --git a/packager/media/formats/mp2t/ts_segmenter_unittest.cc b/packager/media/formats/mp2t/ts_segmenter_unittest.cc index 30fa07abf4..8e3a45ba4a 100644 --- a/packager/media/formats/mp2t/ts_segmenter_unittest.cc +++ b/packager/media/formats/mp2t/ts_segmenter_unittest.cc @@ -207,11 +207,6 @@ TEST_F(TsSegmenterTest, PassedSegmentDuration) { // Doesn't really matter how long this is. sample2->set_duration(kInputTimescale * 7); - EXPECT_CALL(mock_listener, - OnNewSegment("memory://file1.ts", - kFirstPts * kTimeScale / kInputTimescale, - kTimeScale * 11, _)); - Sequence writer_sequence; EXPECT_CALL(*mock_ts_writer_, NewSegment(_)) .InSequence(writer_sequence) @@ -245,10 +240,6 @@ TEST_F(TsSegmenterTest, PassedSegmentDuration) { EXPECT_CALL(*mock_pes_packet_generator_, Flush()) .WillOnce(Return(true)); - EXPECT_CALL(*mock_ts_writer_, NewSegment(_)) - .InSequence(writer_sequence) - .WillOnce(Return(true)); - EXPECT_CALL(*mock_ts_writer_, AddPesPacketMock(_, _)) .Times(2) .WillRepeatedly(Return(true)); @@ -391,8 +382,6 @@ TEST_F(TsSegmenterTest, EncryptedSample) { .InSequence(pes_packet_sequence) .WillOnce(Return(new PesPacket())); - EXPECT_CALL(mock_listener, OnNewSegment("memory://file1.ts", _, _, _)); - MockTsWriter* mock_ts_writer_raw = mock_ts_writer_.get(); segmenter.InjectPesPacketGeneratorForTesting( diff --git a/packager/media/formats/mp2t/ts_stream_type.h b/packager/media/formats/mp2t/ts_stream_type.h index 459311da0d..27abbb9e85 100644 --- a/packager/media/formats/mp2t/ts_stream_type.h +++ b/packager/media/formats/mp2t/ts_stream_type.h @@ -45,6 +45,7 @@ enum class TsStreamType { // Below are internal values used to select other stream types based on other // info in headers. kDvbSubtitles = 0x100, + kTeletextSubtitles = 0x101, }; } // namespace mp2t diff --git a/packager/media/formats/mp4/box_definitions.cc b/packager/media/formats/mp4/box_definitions.cc index 6ce12b5500..81d175889a 100644 --- a/packager/media/formats/mp4/box_definitions.cc +++ b/packager/media/formats/mp4/box_definitions.cc @@ -1969,6 +1969,27 @@ size_t FlacSpecific::ComputeSizeInternal() { return HeaderSize() + data.size(); } +ALACSpecific::ALACSpecific() = default; +ALACSpecific::~ALACSpecific() = default; + +FourCC ALACSpecific::BoxType() const { + return FOURCC_alac; +} + +bool ALACSpecific::ReadWriteInternal(BoxBuffer* buffer) { + RCHECK(ReadWriteHeaderInternal(buffer)); + size_t size = buffer->Reading() ? buffer->BytesLeft() : data.size(); + RCHECK(buffer->ReadWriteVector(&data, size)); + return true; +} + +size_t ALACSpecific::ComputeSizeInternal() { + // This box is optional. Skip it if not initialized. + if (data.empty()) + return 0; + return HeaderSize() + data.size(); +} + AudioSampleEntry::AudioSampleEntry() = default; AudioSampleEntry::~AudioSampleEntry() = default; @@ -2011,6 +2032,7 @@ bool AudioSampleEntry::ReadWriteInternal(BoxBuffer* buffer) { RCHECK(buffer->TryReadWriteChild(&dops)); RCHECK(buffer->TryReadWriteChild(&dfla)); RCHECK(buffer->TryReadWriteChild(&mhac)); + RCHECK(buffer->TryReadWriteChild(&alac)); // Somehow Edge does not support having sinf box before codec_configuration, // box, so just do it in the end of AudioSampleEntry. See @@ -2037,6 +2059,7 @@ size_t AudioSampleEntry::ComputeSizeInternal() { esds.ComputeSize() + ddts.ComputeSize() + dac3.ComputeSize() + dec3.ComputeSize() + dops.ComputeSize() + dfla.ComputeSize() + dac4.ComputeSize() + mhac.ComputeSize() + udts.ComputeSize() + + alac.ComputeSize() + // Reserved and predefined bytes. 6 + 8 + // 6 + 8 bytes reserved. 4; // 4 bytes predefined. diff --git a/packager/media/formats/mp4/box_definitions.h b/packager/media/formats/mp4/box_definitions.h index e299f8dba6..a3debb60c1 100644 --- a/packager/media/formats/mp4/box_definitions.h +++ b/packager/media/formats/mp4/box_definitions.h @@ -382,6 +382,15 @@ struct FlacSpecific : FullBox { std::vector data; }; +// ALAC specific decoder configuration box: +// https://wiki.multimedia.cx/index.php/Apple_Lossless_Audio_Coding +// We do not care about the actual data inside, which is simply copied over. +struct ALACSpecific : FullBox { + DECLARE_BOX_METHODS(ALACSpecific); + + std::vector data; +}; + struct AudioSampleEntry : Box { DECLARE_BOX_METHODS(AudioSampleEntry); @@ -409,6 +418,7 @@ struct AudioSampleEntry : Box { OpusSpecific dops; FlacSpecific dfla; MHAConfiguration mhac; + ALACSpecific alac; }; struct WebVTTConfigurationBox : Box { diff --git a/packager/media/formats/mp4/mp4_media_parser.cc b/packager/media/formats/mp4/mp4_media_parser.cc index 53aa1fc0a1..b3be126385 100644 --- a/packager/media/formats/mp4/mp4_media_parser.cc +++ b/packager/media/formats/mp4/mp4_media_parser.cc @@ -102,6 +102,8 @@ Codec FourCCToCodec(FourCC fourcc) { return kCodecEAC3; case FOURCC_ac_4: return kCodecAC4; + case FOURCC_alac: + return kCodecALAC; case FOURCC_fLaC: return kCodecFlac; case FOURCC_mha1: @@ -514,6 +516,9 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) { return false; } break; + case FOURCC_alac: + codec_config = entry.alac.data; + break; case FOURCC_fLaC: codec_config = entry.dfla.data; break; diff --git a/packager/media/formats/mp4/mp4_muxer.cc b/packager/media/formats/mp4/mp4_muxer.cc index faae886f8e..a599283839 100644 --- a/packager/media/formats/mp4/mp4_muxer.cc +++ b/packager/media/formats/mp4/mp4_muxer.cc @@ -76,6 +76,8 @@ FourCC CodecToFourCC(Codec codec, H26xStreamFormat h26x_stream_format) { return FOURCC_mp4a; case kCodecAC3: return FOURCC_ac_3; + case kCodecALAC: + return FOURCC_alac; case kCodecDTSC: return FOURCC_dtsc; case kCodecDTSH: @@ -512,6 +514,9 @@ bool MP4Muxer::GenerateAudioTrak(const AudioStreamInfo* audio_info, case kCodecAC4: audio.dac4.data = audio_info->codec_config(); break; + case kCodecALAC: + audio.alac.data = audio_info->codec_config(); + break; case kCodecFlac: audio.dfla.data = audio_info->codec_config(); break; diff --git a/packager/media/formats/mp4/segmenter.cc b/packager/media/formats/mp4/segmenter.cc index 0df3a81db9..9313a0eb5e 100644 --- a/packager/media/formats/mp4/segmenter.cc +++ b/packager/media/formats/mp4/segmenter.cc @@ -141,8 +141,12 @@ Status Segmenter::AddSample(size_t stream_id, const MediaSample& sample) { if (!status.ok()) return status; - if (sample_duration_ == 0) - sample_duration_ = sample.duration(); + // The duration of the first sample may have been adjusted, so use + // the duration of the second sample instead. + if (num_samples_ < 2) { + sample_durations_[num_samples_] = sample.duration(); + num_samples_++; + } stream_durations_[stream_id] += sample.duration(); return Status::OK; } diff --git a/packager/media/formats/mp4/segmenter.h b/packager/media/formats/mp4/segmenter.h index 5333ffe35c..df48812122 100644 --- a/packager/media/formats/mp4/segmenter.h +++ b/packager/media/formats/mp4/segmenter.h @@ -99,7 +99,9 @@ class Segmenter { /// @return The sample duration in the timescale of the media. /// Returns 0 if no samples are added yet. - int32_t sample_duration() const { return sample_duration_; } + int64_t sample_duration() const { + return sample_durations_[num_samples_ < 2 ? 0 : 1]; + } protected: /// Update segmentation progress using ProgressListener. @@ -147,7 +149,8 @@ class Segmenter { ProgressListener* progress_listener_ = nullptr; uint64_t progress_target_ = 0u; uint64_t accumulated_progress_ = 0u; - int32_t sample_duration_ = 0; + int64_t sample_durations_[2] = {0, 0}; + size_t num_samples_ = 0; std::vector stream_durations_; std::vector key_frame_infos_; diff --git a/packager/media/formats/ttml/ttml_generator.cc b/packager/media/formats/ttml/ttml_generator.cc index e7fc596fc3..230582cb4f 100644 --- a/packager/media/formats/ttml/ttml_generator.cc +++ b/packager/media/formats/ttml/ttml_generator.cc @@ -18,6 +18,7 @@ namespace ttml { namespace { constexpr const char* kRegionIdPrefix = "_shaka_region_"; +constexpr const char* kRegionTeletextPrefix = "ttx_"; std::string ToTtmlTime(int64_t time, int32_t timescale) { int64_t remaining = time * 1000 / timescale; @@ -54,6 +55,18 @@ void TtmlGenerator::Initialize(const std::map& regions, regions_ = regions; language_ = language; time_scale_ = time_scale; + // Add ebu_tt_d_regions + float step = 74.1f / 11; + for (int i = 0; i < 12; i++) { + TextRegion region; + float verPos = 10.0 + int(float(step) * float(i)); + region.width = TextNumber(80, TextUnitType::kPercent); + region.height = TextNumber(15, TextUnitType::kPercent); + region.window_anchor_x = TextNumber(10, TextUnitType::kPercent); + region.window_anchor_y = TextNumber(verPos, TextUnitType::kPercent); + const std::string id = kRegionTeletextPrefix + std::to_string(i); + regions_.emplace(id, region); + } } void TtmlGenerator::AddSample(const TextSample& sample) { @@ -66,72 +79,92 @@ void TtmlGenerator::Reset() { bool TtmlGenerator::Dump(std::string* result) const { xml::XmlNode root("tt"); + bool ebuTTDFormat = isEbuTTTD(); RCHECK(root.SetStringAttribute("xmlns", kTtNamespace)); RCHECK(root.SetStringAttribute("xmlns:tts", "http://www.w3.org/ns/ttml#styling")); - - bool did_log = false; - xml::XmlNode head("head"); + RCHECK(root.SetStringAttribute("xmlns:tts", + "http://www.w3.org/ns/ttml#styling")); RCHECK(root.SetStringAttribute("xml:lang", language_)); - for (const auto& pair : regions_) { - if (!did_log && (pair.second.region_anchor_x.value != 0 && - pair.second.region_anchor_y.value != 0)) { - LOG(WARNING) << "TTML doesn't support non-0 region anchor"; - did_log = true; - } - xml::XmlNode region("region"); - const auto origin = - ToTtmlSize(pair.second.window_anchor_x, pair.second.window_anchor_y); - const auto extent = ToTtmlSize(pair.second.width, pair.second.height); - RCHECK(region.SetStringAttribute("xml:id", pair.first)); - RCHECK(region.SetStringAttribute("tts:origin", origin)); - RCHECK(region.SetStringAttribute("tts:extent", extent)); - RCHECK(head.AddChild(std::move(region))); + if (ebuTTDFormat) { + RCHECK(root.SetStringAttribute("xmlns:ttp", + "http://www.w3.org/ns/ttml#parameter")); + RCHECK(root.SetStringAttribute("xmlns:ttm", + "http://www.w3.org/ns/ttml#metadata")); + RCHECK(root.SetStringAttribute("xmlns:ebuttm", "urn:ebu:tt:metadata")); + RCHECK(root.SetStringAttribute("xmlns:ebutts", "urn:ebu:tt:style")); + RCHECK(root.SetStringAttribute("xml:space", "default")); + RCHECK(root.SetStringAttribute("ttp:timeBase", "media")); + RCHECK(root.SetStringAttribute("ttp:cellResolution", "32 15")); } - RCHECK(root.AddChild(std::move(head))); - size_t image_count = 0; + xml::XmlNode head("head"); + xml::XmlNode styling("styling"); xml::XmlNode metadata("metadata"); + xml::XmlNode layout("layout"); + RCHECK(addRegions(layout)); + xml::XmlNode body("body"); + if (ebuTTDFormat) { + RCHECK(body.SetStringAttribute("style", "default")); + } + size_t image_count = 0; + std::unordered_set fragmentStyles; xml::XmlNode div("div"); for (const auto& sample : samples_) { - RCHECK(AddSampleToXml(sample, &div, &metadata, &image_count)); + RCHECK( + AddSampleToXml(sample, &div, &metadata, fragmentStyles, &image_count)); } - RCHECK(body.AddChild(std::move(div))); if (image_count > 0) { RCHECK(root.SetStringAttribute( "xmlns:smpte", "http://www.smpte-ra.org/schemas/2052-1/2010/smpte-tt")); - RCHECK(root.AddChild(std::move(metadata))); } + RCHECK(body.AddChild(std::move(div))); + RCHECK(head.AddChild(std::move(metadata))); + RCHECK(addStyling(styling, fragmentStyles)); + RCHECK(head.AddChild(std::move(styling))); + RCHECK(head.AddChild(std::move(layout))); + RCHECK(root.AddChild(std::move(head))); + RCHECK(root.AddChild(std::move(body))); *result = root.ToString(/* comment= */ ""); return true; } -bool TtmlGenerator::AddSampleToXml(const TextSample& sample, - xml::XmlNode* body, - xml::XmlNode* metadata, - size_t* image_count) const { +bool TtmlGenerator::AddSampleToXml( + const TextSample& sample, + xml::XmlNode* body, + xml::XmlNode* metadata, + std::unordered_set& fragmentStyles, + size_t* image_count) const { xml::XmlNode p("p"); - RCHECK(p.SetStringAttribute("xml:space", "preserve")); + if (!isEbuTTTD()) { + RCHECK(p.SetStringAttribute("xml:space", "preserve")); + } RCHECK(p.SetStringAttribute("begin", ToTtmlTime(sample.start_time(), time_scale_))); RCHECK( p.SetStringAttribute("end", ToTtmlTime(sample.EndTime(), time_scale_))); - RCHECK(ConvertFragmentToXml(sample.body(), &p, metadata, image_count)); + RCHECK(ConvertFragmentToXml(sample.body(), &p, metadata, fragmentStyles, + image_count)); if (!sample.id().empty()) RCHECK(p.SetStringAttribute("xml:id", sample.id())); const auto& settings = sample.settings(); - if (settings.line || settings.position || settings.width || settings.height) { - // TTML positioning needs to be from a region. - if (!settings.region.empty()) { - LOG(WARNING) - << "Using both text regions and positioning isn't supported in TTML"; + bool regionFound = false; + if (!settings.region.empty()) { + auto reg = regions_.find(settings.region); + if (reg != regions_.end()) { + regionFound = true; + RCHECK(p.SetStringAttribute("region", settings.region)); } + } + if (!regionFound && (settings.line || settings.position || settings.width || + settings.height)) { + // TTML positioning needs to be from a region. const auto origin = ToTtmlSize( settings.position.value_or(TextNumber(0, TextUnitType::kPixels)), settings.line.value_or(TextNumber(0, TextUnitType::kPixels))); @@ -146,8 +179,6 @@ bool TtmlGenerator::AddSampleToXml(const TextSample& sample, RCHECK(region.SetStringAttribute("tts:extent", extent)); RCHECK(p.SetStringAttribute("region", id)); RCHECK(body->AddChild(std::move(region))); - } else if (!settings.region.empty()) { - RCHECK(p.SetStringAttribute("region", settings.region)); } if (settings.writing_direction != WritingDirection::kHorizontal) { @@ -179,19 +210,22 @@ bool TtmlGenerator::AddSampleToXml(const TextSample& sample, return true; } -bool TtmlGenerator::ConvertFragmentToXml(const TextFragment& body, - xml::XmlNode* parent, - xml::XmlNode* metadata, - size_t* image_count) const { +bool TtmlGenerator::ConvertFragmentToXml( + const TextFragment& body, + xml::XmlNode* parent, + xml::XmlNode* metadata, + std::unordered_set& fragmentStyles, + size_t* image_count) const { if (body.newline) { xml::XmlNode br("br"); return parent->AddChild(std::move(br)); } - - // If we have new styles, add a new . xml::XmlNode span("span"); xml::XmlNode* node = parent; - if (body.style.bold || body.style.italic || body.style.underline) { + bool useSpan = + (body.style.bold || body.style.italic || body.style.underline || + !body.style.color.empty() || !body.style.backgroundColor.empty()); + if (useSpan) { node = &span; if (body.style.bold) { RCHECK(span.SetStringAttribute("tts:fontWeight", @@ -206,6 +240,20 @@ bool TtmlGenerator::ConvertFragmentToXml(const TextFragment& body, "tts:textDecoration", *body.style.underline ? "underline" : "noUnderline")); } + std::string color = "white"; + std::string backgroundColor = "black"; + + if (!body.style.color.empty()) { + color = body.style.color; + } + + if (!body.style.backgroundColor.empty()) { + backgroundColor = body.style.backgroundColor; + } + + const std::string fragStyle = color + "_" + backgroundColor; + fragmentStyles.insert(fragStyle); + RCHECK(span.SetStringAttribute("style", fragStyle)); } if (!body.body.empty()) { @@ -226,16 +274,91 @@ bool TtmlGenerator::ConvertFragmentToXml(const TextFragment& body, RCHECK(node->SetStringAttribute("smpte:backgroundImage", "#" + id)); } else { for (const auto& frag : body.sub_fragments) { - if (!ConvertFragmentToXml(frag, node, metadata, image_count)) + if (!ConvertFragmentToXml(frag, node, metadata, fragmentStyles, + image_count)) return false; } } - if (body.style.bold || body.style.italic || body.style.underline) + if (useSpan) RCHECK(parent->AddChild(std::move(span))); return true; } +std::vector TtmlGenerator::usedRegions() const { + std::vector uRegions; + for (const auto& sample : samples_) { + if (!sample.settings().region.empty()) { + uRegions.push_back(sample.settings().region); + } + } + return uRegions; +} + +bool TtmlGenerator::addRegions(xml::XmlNode& layout) const { + auto regNames = usedRegions(); + for (const auto& r : regions_) { + bool used = false; + for (const auto& name : regNames) { + if (r.first == name) { + used = true; + } + } + if (used) { + xml::XmlNode region("region"); + const auto origin = + ToTtmlSize(r.second.window_anchor_x, r.second.window_anchor_y); + const auto extent = ToTtmlSize(r.second.width, r.second.height); + RCHECK(region.SetStringAttribute("xml:id", r.first)); + RCHECK(region.SetStringAttribute("tts:origin", origin)); + RCHECK(region.SetStringAttribute("tts:extent", extent)); + RCHECK(region.SetStringAttribute("tts:overflow", "visible")); + RCHECK(layout.AddChild(std::move(region))); + } + } + return true; +} + +bool TtmlGenerator::addStyling( + xml::XmlNode& styling, + const std::unordered_set& fragmentStyles) const { + if (fragmentStyles.empty()) { + return true; + } + // Add default style + xml::XmlNode defaultStyle("style"); + RCHECK(defaultStyle.SetStringAttribute("xml:id", "default")); + RCHECK(defaultStyle.SetStringAttribute("tts:fontStyle", "normal")); + RCHECK(defaultStyle.SetStringAttribute("tts:fontFamily", "sansSerif")); + RCHECK(defaultStyle.SetStringAttribute("tts:fontSize", "100%")); + RCHECK(defaultStyle.SetStringAttribute("tts:lineHeight", "normal")); + RCHECK(defaultStyle.SetStringAttribute("tts:textAlign", "center")); + RCHECK(defaultStyle.SetStringAttribute("ebutts:linePadding", "0.5c")); + RCHECK(styling.AddChild(std::move(defaultStyle))); + + for (const auto& name : fragmentStyles) { + auto pos = name.find('_'); + auto color = name.substr(0, pos); + auto backgroundColor = name.substr(pos + 1, name.size()); + xml::XmlNode fragStyle("style"); + RCHECK(fragStyle.SetStringAttribute("xml:id", name)); + RCHECK( + fragStyle.SetStringAttribute("tts:backgroundColor", backgroundColor)); + RCHECK(fragStyle.SetStringAttribute("tts:color", color)); + RCHECK(styling.AddChild(std::move(fragStyle))); + } + return true; +} + +bool TtmlGenerator::isEbuTTTD() const { + for (const auto& sample : samples_) { + if (sample.settings().region.rfind(kRegionTeletextPrefix, 0) == 0) { + return true; + } + } + return false; +} + } // namespace ttml } // namespace media } // namespace shaka diff --git a/packager/media/formats/ttml/ttml_generator.h b/packager/media/formats/ttml/ttml_generator.h index 303a3e4b33..9c1afbdfaf 100644 --- a/packager/media/formats/ttml/ttml_generator.h +++ b/packager/media/formats/ttml/ttml_generator.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -38,12 +39,20 @@ class TtmlGenerator { bool AddSampleToXml(const TextSample& sample, xml::XmlNode* body, xml::XmlNode* metadata, + std::unordered_set& fragmentStyles, size_t* image_count) const; bool ConvertFragmentToXml(const TextFragment& fragment, xml::XmlNode* parent, xml::XmlNode* metadata, + std::unordered_set& fragmentStyles, size_t* image_count) const; + bool addStyling(xml::XmlNode& styling, + const std::unordered_set& fragmentStyles) const; + bool addRegions(xml::XmlNode& layout) const; + std::vector usedRegions() const; + bool isEbuTTTD() const; + std::list samples_; std::map regions_; std::string language_; diff --git a/packager/media/formats/ttml/ttml_generator_unittest.cc b/packager/media/formats/ttml/ttml_generator_unittest.cc index 23728d223f..0f0d04dcbd 100644 --- a/packager/media/formats/ttml/ttml_generator_unittest.cc +++ b/packager/media/formats/ttml/ttml_generator_unittest.cc @@ -64,7 +64,11 @@ TEST_F(TtmlMuxerTest, WithOneSegmentAndWithOneSample) { "\n" "\n" - " \n" + " \n" + " \n" + " \n" + " \n" + " \n" " \n" "
\n" "

\n" "\n" - " \n" + " \n" + " \n" + " \n" + " \n" + " \n" " \n" "

\n" "

\n" "\n" - " \n" + " \n" + " \n" + " \n" + " \n" + " \n" " \n" "

\n" "

\n" " \n" - " \n" + " \n" + " \n" + " \n" + " \n" + " \n" " \n" " \n" "

\n" @@ -166,7 +182,11 @@ TEST_F(TtmlMuxerTest, HandlesLanguage) { "\n" "\n" - " \n" + " \n" + " \n" + " \n" + " \n" + " \n" " \n" "
\n" "

\n" "\n" - " \n" + " \n" + " \n" + " \n" + " \n" + " \n" " \n" "

\n" " \n" "\n" - " \n" + " \n" + " \n" + " \n" + " \n" + " \n" " \n" "
\n" "

\n" "\n" - " \n" + " \n" + " \n" + " \n" + " \n" + " \n" " \n" "

\n" "

\n" " \n" - " \n" + " \n" + " \n" + " \n" + " \n" + " \n" " \n" " \n" "

\n" @@ -287,7 +323,11 @@ TEST_F(TtmlMuxerTest, HandlesReset) { "\n" "\n" - " \n" + " \n" + " \n" + " \n" + " \n" + " \n" " \n" "
\n" "

\n" "\n" - " \n" + " \n" + " \n" + " \n" + " \n" + " \n" " \n" "

\n" "

\n" - " \n" - " \n" - " " + " \n" + " \n" + " " "AQID\n" - " \n" + " \n" + " \n" + " \n" + " \n" " \n" "

\n" "

\n" "\n" - " \n" + " \n" + " \n" + " \n" + " \n" + " \n" " \n" "

\n" "

\n" + "\n" + " \n" + " \n" + " \n" + "