From ed01efd0a0621d8e12123dd24df60147ac5de2a3 Mon Sep 17 00:00:00 2001 From: Hitesh Kumar Saini Date: Mon, 21 Oct 2024 17:02:12 +0530 Subject: [PATCH] build --- .github/workflows/mpv_clang.yml | 108 +- .github/workflows/mpv_gcc.yml | 292 -- CMakeLists.txt | 2 +- README.md | 270 -- cmake/custom_steps.cmake | 3 +- cmake/packages_check.cmake | 12 +- packages/CMakeLists.txt | 3 - packages/ffmpeg-dash-base-url-escape.patch | 26 + packages/ffmpeg.cmake | 245 +- ...add-win32-utf-8-filesystem-functions.patch | 855 ------ packages/luajit.cmake | 58 - packages/luajit.pc.in | 25 - packages/mpv-0001-remove-libass.patch | 2498 +++++++++++++++++ packages/mpv-release.cmake | 35 +- packages/mpv.cmake | 37 +- packages/spirv-cross.cmake | 4 + packages/xz.cmake | 4 +- 17 files changed, 2737 insertions(+), 1740 deletions(-) delete mode 100644 .github/workflows/mpv_gcc.yml delete mode 100644 README.md create mode 100644 packages/ffmpeg-dash-base-url-escape.patch delete mode 100644 packages/luajit-0001-add-win32-utf-8-filesystem-functions.patch delete mode 100644 packages/luajit.cmake delete mode 100644 packages/luajit.pc.in create mode 100644 packages/mpv-0001-remove-libass.patch diff --git a/.github/workflows/mpv_clang.yml b/.github/workflows/mpv_clang.yml index f70735a5e..457b26f89 100644 --- a/.github/workflows/mpv_clang.yml +++ b/.github/workflows/mpv_clang.yml @@ -10,11 +10,6 @@ on: description: 'Run custom command before building' required: false type: string - sourceforge: - description: 'Upload to Sourceforge' - required: false - default: false - type: boolean github_release: description: 'Upload to Github release' required: false @@ -121,11 +116,6 @@ jobs: ninja -C build_$BIT mpv-packaging; mv build_$BIT/mpv*.7z release_$BIT if [[ $BIT == x86_64_v3 ]]; then for dir in release_$BIT/mpv-*; do name=$(basename $dir); mv $dir $(dirname $dir)/${name/x86_64/x86_64-v3}; done; fi - - name: Copying ffmpeg - run: | - hash=$(git -C src_packages/ffmpeg rev-parse --short HEAD) - 7z a -m0=lzma2 -mx=9 -ms=on release_$BIT/ffmpeg-${{ env.arch }}${{ env.x86_64_level }}-git-$hash.7z ./build_$BIT/$BIT-w64-mingw32/bin/ffmpeg.exe - - name: Collecting logs if: always() run: | @@ -250,30 +240,6 @@ jobs: mv release_i686/* release_x86_64/* release_x86_64_v3/* release_aarch64/* release du -ah release/* - - name: Uploading packages to Sourceforge - id: upload_packages_sf - if: ${{ github.event.inputs.sourceforge == 'true' }} - continue-on-error: true - env: - MPV_VER: ${{ needs.build_mpv.outputs.mpv_ver }} - run: | - mkdir -p /root/.ssh/ - echo "${{ secrets.SF_PRIVATE_KEY }}" > sf_key - pubkey=$(cat < /root/.ssh/known_hosts - chmod 400 sf_key - chmod -R 700 /root - sftp -i sf_key shinchiro@frs.sourceforge.net:/home/frs/project/mpv-player-windows <> $GITHUB_ENV - elif [[ $BIT == "x86_64" ]]; then - echo "arch=x86_64" >> $GITHUB_ENV - elif [[ $BIT == "x86_64_v3" ]]; then - echo "arch=x86_64" >> $GITHUB_ENV - echo "gcc_arch=-DGCC_ARCH=x86-64-v3" >> $GITHUB_ENV - echo "x86_64_level=-v3" >> $GITHUB_ENV - fi - - - name: Setup git config - run: | - git config --global user.name "github-actions" - git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" - git config --global pull.rebase true - git config --global rebase.autoStash true - git config --global fetch.prune true - git config --global --add safe.directory $PWD - - - uses: actions/checkout@main - with: - ref: master - - - name: Loading ${{ matrix.bit }} toolchain cache - uses: actions/cache/restore@main - with: - path: build_${{ matrix.bit }} - key: ${{ secrets.CACHE_VERSION }}-gcc-${{ matrix.bit }}_toolchain-${{ github.run_id }} - restore-keys: | - ${{ secrets.CACHE_VERSION }}-gcc-${{ matrix.bit }}_toolchain - - - name: Loading repository cache - uses: actions/cache/restore@main - with: - path: src_packages - key: ${{ secrets.CACHE_VERSION }}-repository-${{ github.run_id }} - restore-keys: | - ${{ secrets.CACHE_VERSION }}-repository - - - name: Loading rustup cache - uses: actions/cache/restore@main - with: - path: | - clang_root - key: ${{ secrets.CACHE_VERSION }}-clang_root-${{ github.run_id }} - restore-keys: | - ${{ secrets.CACHE_VERSION }}-clang_root - - - name: Running custom command - if: ${{ github.event.inputs.command != '' }} - continue-on-error: true - run: ${{ github.event.inputs.command }} - - - name: Downloading source - run: | - cmake -DTARGET_ARCH=${{ env.arch }}-w64-mingw32 ${{ env.gcc_arch }} -DSINGLE_SOURCE_LOCATION=$PWD/src_packages -DRUSTUP_LOCATION=$PWD/clang_root/install_rustup -DENABLE_CCACHE=ON --fresh -G Ninja -B build_$BIT -S $PWD - ninja -C build_$BIT download || true - - - name: Building toolchain - run: | - if [[ ! -f "build_$BIT/install/bin/cross-gcc" ]]; then ninja -C build_$BIT gcc && rm -rf build_$BIT/toolchain; fi - - - name: Building mpv - id: build_mpv_step - env: - MPV_TARBALL: ${{ github.event.inputs.mpv_tarball }} - run: | - ninja -C build_$BIT update - $MPV_TARBALL && ninja -C build_$BIT mpv-release || ninja -C build_$BIT mpv - $MPV_TARBALL && echo "mpv_ver=$(cat build_$BIT/packages/mpv-release-prefix/VERSION)" >> $GITHUB_OUTPUT || echo "mpv_ver=UNKNOWN" >> $GITHUB_OUTPUT - - - name: Packaging mpv - run: | - mkdir -p release_$BIT - rm -rf build_$BIT/mpv-debug*; ninja -C build_$BIT mpv-packaging; mv build_$BIT/mpv*.7z release_$BIT - - - name: Copying ffmpeg - run: | - hash=$(git -C src_packages/ffmpeg rev-parse --short HEAD) - 7z a -m0=lzma2 -mx=9 -ms=on release_$BIT/ffmpeg-${{ env.arch }}${{ env.x86_64_level }}-git-$hash.7z ./build_$BIT/install/mingw/bin/ffmpeg.exe - - - name: Collecting logs - if: always() - run: | - mkdir -p build_${BIT}_logs - cp -fr $(find build_$BIT -type f -iname "*-*.log" -or -wholename "*/ffbuild/config.log") build_${BIT}_logs || true - 7z a -m0=lzma2 -mx=9 -ms=on logs.7z build*logs - - - name: Uploading logs - uses: actions/upload-artifact@master - if: always() - with: - name: mpv-${{ matrix.bit }}-logs - path: logs.7z - retention-days: 1 - - - name: Uploading ${{ matrix.bit }} build - uses: actions/upload-artifact@master - with: - name: mpv-${{ matrix.bit }} - path: release_${{ matrix.bit }}/mpv-${{ env.arch }}* - - - name: Cleaning build directory - if: always() - run: | - rm -rf build_$BIT/mpv* - - - name: Cleaning rust toolchain directory - if: always() - run: | - ninja -C build_$BIT cargo-clean - - - name: Saving ${{ matrix.bit }} toolchain cache - uses: actions/cache/save@main - if: always() - with: - path: build_${{ matrix.bit }} - key: ${{ secrets.CACHE_VERSION }}-gcc-${{ matrix.bit }}_toolchain-${{ github.run_id }} - - - name: Saving repository cache - uses: actions/cache/save@main - if: false - with: - path: src_packages - key: ${{ secrets.CACHE_VERSION }}-repository-${{ github.run_id }} - - - name: Saving release_${{ matrix.bit }} cache - uses: actions/cache/save@main - if: ${{ github.event.inputs.github_release == 'true' }} - with: - path: release_${{ matrix.bit }} - key: ${{ secrets.CACHE_VERSION }}-gcc-release_${{ matrix.bit }}-${{ github.run_id }} - - release: - name: Upload releases - runs-on: ubuntu-latest - needs: build_mpv - if: ${{ github.event.inputs.github_release == 'true' }} - env: - GH_TOKEN: ${{ github.token }} - CURL_RETRIES: "--connect-timeout 60 --retry 999 --retry-delay 5 --retry-all-errors" - release_x86_64_key: ${{ secrets.CACHE_VERSION }}-gcc-release_x86_64-${{ github.run_id }} - container: - image: docker://alpine:latest - - steps: - - name: Installing dependencies - shell: sh - run: | - apk add --update --no-cache bash git file openssh curl tar zstd jq - apk add perl-file-rename --repository=https://dl-cdn.alpinelinux.org/alpine/edge/testing - git config --global pull.rebase true - git config --global fetch.prune true - git config --global --add safe.directory $PWD - - - uses: actions/checkout@main - with: - ref: master - - - name: Loading release_x86_64 cache - uses: actions/cache/restore@main - with: - path: release_x86_64 - key: ${{ env.release_x86_64_key }} - - - name: Moving archives - run: | - mkdir -p release - mv release_x86_64/* release - rm release/ffmpeg*.7z - file-rename 's/mpv-(.*)x86_64-(.+)/mpv-${1}x86_64-gcc-${2}/s' $PWD/release/mpv*.7z - du -ah release/* - - - name: Uploading packages to Sourceforge - id: upload_packages_sf - if: false - continue-on-error: true - env: - MPV_VER: ${{ needs.build_mpv.outputs.mpv_ver }} - run: | - mkdir -p /root/.ssh/ - echo "${{ secrets.SF_PRIVATE_KEY }}" > sf_key - pubkey=$(cat < /root/.ssh/known_hosts - chmod 400 sf_key - chmod -R 700 /root - sftp -i sf_key shinchiro@frs.sourceforge.net:/home/frs/project/mpv-player-windows < --upgrade`. - -### Cygwin - -Download Cygwin installer and run: - - setup-x86_64.exe -R "C:\cygwin64" -q --packages="bash,binutils,bzip2,cygwin,gcc-core,gcc-g++,cygwin32-gcc-core,cygwin32-gcc-g++,gzip,m4,pkgconf,make,unzip,zip,diffutils,wget,git,patch,cmake,gperf,yasm,enca,asciidoc,bison,flex,gettext-devel,mercurial,python-devel,python-docutils,docbook2X,texinfo,libmpfr-devel,libgmp-devel,libmpc-devel,libtool,autoconf2.5,automake,automake1.9,libxml2-devel,libxslt-devel,meson,libunistring5" - -Additionally, some packages, `re2c`, `ninja`, `ragel`, `gyp`, `rst2pdf`, `nasm` need to be [installed manually](https://gist.github.com/shinchiro/705b0afcc7b6c0accffba1bedb067abf). - -### MSYS2 - -Install MSYS2 and run it via `MSYS2 MSYS` shortcut. -Don't use `MSYS2 MinGW 32-bit` or `MSYS2 MinGW 64-bit` shortcuts, that's important! - -These packages need to be installed first before compiling mpv: - - pacman -S base-devel cmake gcc yasm nasm git mercurial subversion gyp tar gmp-devel mpc-devel mpfr-devel python zlib-devel unzip zip p7zip meson libunistring5 - -Don't install anything from the `mingw32` and `mingw64` repositories, -it's better to completely disable them in `/etc/pacman.conf` just to be safe. - -Additionally, some packages, `re2c`, `ninja`, `ragel`, `libjpeg`, `rst2pdf`, `jinja2` need to be [installed manually](https://gist.github.com/shinchiro/705b0afcc7b6c0accffba1bedb067abf). - - -## Compiling with GCC - -Example: - - cmake -DTARGET_ARCH=x86_64-w64-mingw32 \ - -DGCC_ARCH=x86-64-v3 \ - -DSINGLE_SOURCE_LOCATION="/home/user/packages" \ - -DRUSTUP_LOCATION="/home/user/install_rustup" \ - -G Ninja -B build64 -S mpv-winbuild-cmake - -This cmake command will create `build64` folder for `x86_64-w64-mingw32`. Set `-DTARGET_ARCH=i686-w64-mingw32` for compiling 32-bit. - -`-DGCC_ARCH=x86-64-v3` will set `-march` option when compiling gcc with `x86-64-v3` instructions. Other value like `native`, `znver3` should work too. - -Enter `build64` folder and build toolchain once. By default, it will be installed in `install` folder. - - ninja download # download all packages at once (optional) - ninja gcc # build gcc only once (take around ~20 minutes) - ninja mpv # build mpv and all its dependencies - -On **WSL2**, you might see it stuck with 100% disk usage and never finished. See [below](#wsl-workaround). - -The final `build64` folder's size will be around ~3GB. - -## Building Software (Second Time) - -To build mpv for a second time: - - ninja update # perform git pull on all packages that used git - -After that, build mpv as usual: - - ninja mpv - -## Compiling with Clang - -Supported target architecture (`TARGET_ARCH`) with clang is: `x86_64-w64-mingw32` , `i686-w64-mingw32` , `aarch64-w64-mingw32`. The `aarch64` are untested. - -Example: - - cmake -DTARGET_ARCH=x86_64-w64-mingw32 \ - -DCMAKE_INSTALL_PREFIX="/home/user/clang_root" \ - -DCOMPILER_TOOLCHAIN=clang \ - -DGCC_ARCH=x86-64-v3 \ - -DSINGLE_SOURCE_LOCATION="/home/user/packages" \ - -DRUSTUP_LOCATION="/home/user/install_rustup" \ - -DMINGW_INSTALL_PREFIX="/home/user/build_x86_64_v3/x86_64_v3-w64-mingw32" \ - -G Ninja -B build_x86_64_v3 -S mpv-winbuild-cmake - -The cmake command will create `clang_root` as clang sysroot where llvm tools installed. `build_x86_64` is build directory to compiling packages. - - cd build_x86_64 - ninja llvm # build LLVM (take around ~2 hours) - ninja rustup # build rust toolchain - ninja llvm-clang # build clang on specified target - ninja mpv # build mpv and all its dependencies - -If you want add another target (ex. `i686-w64-mingw32`), change `TARGET_ARCH` and build folder. - - cmake -DTARGET_ARCH=i686-w64-mingw32 \ - -DCMAKE_INSTALL_PREFIX="/home/user/clang_root" \ - -DCOMPILER_TOOLCHAIN=clang \ - -DSINGLE_SOURCE_LOCATION="/home/user/packages" \ - -DRUSTUP_LOCATION="/home/user/install_rustup" \ - -DMINGW_INSTALL_PREFIX="/home/user/build_i686/i686-w64-mingw32" \ - -G Ninja -B build_i686 -S mpv-winbuild-cmake - cd build_i686 - ninja llvm-clang # same as above - -If you've changed `GCC_ARCH` option, you need to run: - - ninja rebuild_cache - -to update flags which will pass on gcc, g++ and etc. - -## Available Commands - -| Commands | Description | -| -------------------------- | ----------- | -| ninja package | compile a package | -| ninja clean | remove all stamp files in all packages. | -| ninja download | Download all packages' sources at once without compiling. | -| ninja update | Update all git repos. When a package pulls new changes, all of its stamp files will be deleted and will be forced rebuild. If there is no change, it will not remove the stamp files and no rebuild occur. Use this instead of `ninja clean` if you don't want to rebuild everything in the next run. | -| ninja package-fullclean | Remove all stamp files of a package. | -| ninja package-liteclean | Remove build, clean stamp files only. This will skip re-configure in the next running `ninja package` (after the first compile). Updating repo or patching need to do manually. Ideally, all `DEPENDS` targets in `package.cmake` should be temporarily commented or deleted. Might be useful in some cases. | -| ninja package-removebuild | Remove 'build' directory of a package. | -| ninja package-removeprefix | Remove 'prefix' directory. | -| ninja package-force-update | Update a package. Only git repo will be updated. | - -`package` is package's name found in `packages` folder. - -## Information about packages - -- Git/Hg - - ANGLE - - FFmpeg - - xz - - x264 - - x265 (multilib) - - uchardet - - rubberband (with libsamplerate) - - opus - - openal-soft - - mpv - - luajit - - libvpx - - libwebp - - libpng - - libsoxr - - libzimg (with graphengine) - - libdvdread - - libdvdnav - - libdvdcss - - libudfread - - libbluray - - libunibreak - - libass - - libmysofa - - lcms2 - - lame - - harfbuzz - - game-music-emu - - freetype2 - - flac - - opus-tools - - mujs - - libarchive - - libjpeg - - shaderc (with spirv-headers, spirv-tools, glslang) - - vulkan-header - - vulkan - - spirv-cross - - fribidi - - ~~nettle~~ - - curl - - libxml2 - - amf-headers - - avisynth-headers - - nvcodec-headers - - libvpl - - megasdk (with termcap, readline, cryptopp, sqlite, libuv, libsodium) - - aom - - dav1d - - libplacebo (with glad, fast_float, xxhash) - - fontconfig - - libbs2b - - libssh - - libsrt - - libjxl (with brotli, highway) - - libmodplug - - uavs3d - - davs2 - - libsixel - - libdovi - - libva - - libzvbi - - svtav1 - - libaribcaption - - zlib (zlib-ng) - - zstd - - expat - - openssl - - libsdl2 - - speex - - vorbis - - ogg - - bzip2 - -- Zip - - xvidcore (1.3.7) - - lzo (2.10) - - libopenmpt (0.7.8) - - libiconv (1.17) - - ~~gmp (6.3.0)~~ - - vapoursynth (R65/R63) - - ~~mbedtls (3.5.0)~~ - - ~~libressl (3.1.5)~~ - - -### WSL workaround - -Place the file on specified location to limit ram & cpu usage to avoid getting stuck while building mpv. - - # /etc/wsl.conf - [interop] - #enabled=false - appendWindowsPath=false - - [automount] - enabled = true - options = "metadata" - mountFsTab = false - - [user] - default= - --------------------------------------- - # C:\Users\\.wslconfig - [wsl2] - memory=4GB - swap=0 - pageReporting=false - -## Acknowledgements - -This project was originally created and maintained [lachs0r](https://github.com/lachs0r/mingw-w64-cmake). Since then, it heavily modified to suit my own need. diff --git a/cmake/custom_steps.cmake b/cmake/custom_steps.cmake index faa158810..77c16a507 100644 --- a/cmake/custom_steps.cmake +++ b/cmake/custom_steps.cmake @@ -96,6 +96,7 @@ file(WRITE ${stamp_dir}/reset_head.sh "#!/bin/bash set -e if [[ ! -f \"${stamp_dir}/${_name}-patch\" || \"${stamp_dir}/${_name}-download\" -nt \"${stamp_dir}/${_name}-patch\" || ! -f \"${stamp_dir}/HEAD\" || \"$(cat ${stamp_dir}/HEAD)\" != \"$(git -C ${source_dir} rev-parse @{u})\" ]]; then + echo ${source_dir} git -C ${source_dir} reset --hard ${reset} -q if [[ -z \"${git_reset}\" ]]; then find \"${stamp_dir}\" -type f ! -iname '*.cmake' -size 0c -delete @@ -114,7 +115,7 @@ PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_ INDEPENDENT TRUE WORKING_DIRECTORY COMMAND bash -c "git am --abort 2> /dev/null || true" - COMMAND bash -c "git fetch --filter=tree:0 --no-recurse-submodules" + COMMAND bash -c "git fetch --no-recurse-submodules" COMMAND ${stamp_dir}/reset_head.sh ) ExternalProject_Add_StepTargets(${_name} force-update) diff --git a/cmake/packages_check.cmake b/cmake/packages_check.cmake index e303e10dc..4b9c5509c 100644 --- a/cmake/packages_check.cmake +++ b/cmake/packages_check.cmake @@ -41,11 +41,7 @@ if(TARGET_CPU STREQUAL "x86_64") set(xxhash_dispatch "-DDISPATCH=ON") set(xxhash_cflags "-DXXH_X86DISPATCH_ALLOW_AVX=1") set(nvcodec_headers "nvcodec-headers") - set(ffmpeg_cuda "--enable-cuda-llvm --enable-cuvid --enable-nvdec --enable-nvenc") - set(ffmpeg_uavs3d "uavs3d") - set(ffmpeg_uavs3d_cmd "--enable-libuavs3d") - set(ffmpeg_davs2 "davs2") - set(ffmpeg_davs2_cmd "--enable-libdavs2") + set(ffmpeg_cuda "--enable-cuda-llvm --enable-cuvid --enable-nvdec --enable-nvenc --enable-ffnvcodec") elseif(TARGET_CPU STREQUAL "i686") set(dlltool_image "i386") set(vulkan_asm "-DUSE_MASM=OFF") @@ -55,11 +51,7 @@ elseif(TARGET_CPU STREQUAL "i686") set(xxhash_dispatch "-DDISPATCH=ON") set(xxhash_cflags "-DXXH_X86DISPATCH_ALLOW_AVX=1") set(nvcodec_headers "nvcodec-headers") - set(ffmpeg_cuda "--enable-cuda-llvm --enable-cuvid --enable-nvdec --enable-nvenc") - set(ffmpeg_uavs3d "uavs3d") - set(ffmpeg_uavs3d_cmd "--enable-libuavs3d") - set(ffmpeg_davs2 "davs2") - set(ffmpeg_davs2_cmd "--enable-libdavs2") + set(ffmpeg_cuda "--enable-cuda-llvm --enable-cuvid --enable-nvdec --enable-nvenc --enable-ffnvcodec") elseif(TARGET_CPU STREQUAL "aarch64") set(dlltool_image "arm64") set(vulkan_asm "-DUSE_GAS=ON") diff --git a/packages/CMakeLists.txt b/packages/CMakeLists.txt index d9100604c..296b41bc0 100644 --- a/packages/CMakeLists.txt +++ b/packages/CMakeLists.txt @@ -29,7 +29,6 @@ list(APPEND ep libdvdread libdvdnav libiconv - luajit fribidi expat nettle @@ -80,11 +79,9 @@ list(APPEND ep glad fast_float libdovi - svtav1 libplacebo libva libzvbi - libaribcaption libsixel ffmpeg mpv diff --git a/packages/ffmpeg-dash-base-url-escape.patch b/packages/ffmpeg-dash-base-url-escape.patch new file mode 100644 index 000000000..36ed6d5b2 --- /dev/null +++ b/packages/ffmpeg-dash-base-url-escape.patch @@ -0,0 +1,26 @@ +diff --git a/libavformat/dashdec.c b/libavformat/dashdec.c +index 29d4680..c50afaf 100644 +--- a/libavformat/dashdec.c ++++ b/libavformat/dashdec.c +@@ -768,7 +768,9 @@ static int resolve_content_path(AVFormatContext *s, const char *url, int *max_ur + baseurl = xmlNodeGetContent(node); + root_url = (av_strcasecmp(baseurl, "")) ? baseurl : path; + if (node) { +- xmlNodeSetContent(node, root_url); ++ char* root_url_content = xmlEncodeSpecialChars(NULL, root_url); ++ xmlNodeSetContent(node, root_url_content); ++ xmlFree(root_url_content); + updated = 1; + } + +@@ -802,7 +804,9 @@ static int resolve_content_path(AVFormatContext *s, const char *url, int *max_ur + memset(p + 1, 0, strlen(p)); + } + av_strlcat(tmp_str, text + start, tmp_max_url_size); +- xmlNodeSetContent(baseurl_nodes[i], tmp_str); ++ char* tmp_str_content = xmlEncodeSpecialChars(NULL, tmp_str); ++ xmlNodeSetContent(baseurl_nodes[i], tmp_str_content); ++ xmlFree(tmp_str_content); + updated = 1; + xmlFree(text); + } diff --git a/packages/ffmpeg.cmake b/packages/ffmpeg.cmake index 49a0f6e95..566adf556 100644 --- a/packages/ffmpeg.cmake +++ b/packages/ffmpeg.cmake @@ -1,5 +1,5 @@ ExternalProject_Add(ffmpeg - DEPENDS + DEPENDS amf-headers avisynth-headers ${nvcodec_headers} @@ -17,7 +17,6 @@ ExternalProject_Add(ffmpeg libpng libsoxr libbs2b - libvpx libwebp libzimg libmysofa @@ -26,30 +25,18 @@ ExternalProject_Add(ffmpeg opus speex vorbis - x264 - ${ffmpeg_x265} - xvidcore - libxml2 libvpl - libopenmpt libjxl + libxml2 shaderc libplacebo - libzvbi - libaribcaption - aom - svtav1 dav1d - vapoursynth - ${ffmpeg_uavs3d} - ${ffmpeg_davs2} - rubberband - libva openal-soft GIT_REPOSITORY https://github.com/FFmpeg/FFmpeg.git SOURCE_DIR ${SOURCE_LOCATION} GIT_CLONE_FLAGS "--sparse --filter=tree:0" GIT_CLONE_POST_COMMAND "sparse-checkout set --no-cone /* !tests/ref/fate" + PATCH_COMMAND ${EXEC} git apply ${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg-*.patch UPDATE_COMMAND "" CONFIGURE_COMMAND ${EXEC} CONF=1 /configure --cross-prefix=${TARGET_ARCH}- @@ -58,69 +45,185 @@ ExternalProject_Add(ffmpeg --target-os=mingw32 --pkg-config-flags=--static --enable-cross-compile - --enable-runtime-cpudetect - --enable-gpl + + --disable-gpl + --disable-nonfree --enable-version3 - --enable-postproc - --enable-avisynth - --enable-vapoursynth - --enable-libass - --enable-libbluray - --enable-libdvdnav - --enable-libdvdread - --enable-libfreetype - --enable-libfribidi - --enable-libfontconfig - --enable-libharfbuzz - --enable-libmodplug - --enable-libopenmpt - --enable-libmp3lame - --enable-lcms2 - --enable-libopus - --enable-libsoxr - --enable-libspeex - --enable-libvorbis - --enable-libbs2b - --enable-librubberband - --enable-libvpx - --enable-libwebp - --enable-libx264 - --enable-libx265 - --enable-libaom - --enable-libsvtav1 - --enable-libdav1d - ${ffmpeg_davs2_cmd} - ${ffmpeg_uavs3d_cmd} - --enable-libxvid - --enable-libzimg + --enable-static + --disable-shared + --disable-vulkan + --disable-iconv + --enable-stripping + + --disable-muxers + --disable-decoders + --disable-encoders + --disable-demuxers + --disable-parsers + --disable-protocols + --disable-filters + --disable-doc + --disable-postproc + --disable-programs + --disable-gray + --disable-swscale-alpha + + --disable-bsfs + + --disable-amf + --disable-cuda + --disable-nvdec + --disable-nvenc + --disable-cuvid + --disable-dxva2 + --disable-libmfx + --disable-d3d11va + --disable-vaapi + --disable-vdpau + --disable-bzlib + --disable-libmfx + --disable-libuavs3d + --disable-ffnvcodec + --disable-linux-perf + --disable-videotoolbox + --disable-audiotoolbox + + --enable-small + --enable-hwaccels + --enable-optimizations + --enable-runtime-cpudetect + --enable-openssl - --enable-libxml2 - --enable-libmysofa --enable-libssh - --enable-libsrt - --enable-libvpl + --enable-libjxl - --enable-libplacebo - --enable-libshaderc - --enable-libzvbi - --enable-libaribcaption + + --enable-avutil + --enable-avcodec + --enable-avfilter + --enable-avformat + --enable-avdevice + --enable-swscale + --enable-swresample + + --enable-decoder=aac* + --enable-decoder=ac3 + --enable-decoder=alac + --enable-decoder=als + --enable-decoder=ape + --enable-decoder=atrac* + --enable-decoder=eac3 + --enable-decoder=flac + --enable-decoder=gsm* + --enable-decoder=mp1* + --enable-decoder=mp2* + --enable-decoder=mp3* + --enable-decoder=mpc* + --enable-decoder=opus + --enable-decoder=ra* + --enable-decoder=ralf + --enable-decoder=shorten + --enable-decoder=tak + --enable-decoder=tta + --enable-decoder=vorbis + --enable-decoder=wavpack + --enable-decoder=wma* + --enable-decoder=pcm* + --enable-decoder=dsd* + --enable-decoder=dca + + --enable-decoder=mjpeg + --enable-decoder=ljpeg + --enable-decoder=jpegls + --enable-decoder=jpeg2000 + --enable-decoder=png + --enable-decoder=gif + --enable-decoder=bmp + --enable-decoder=tiff + --enable-decoder=webp + --enable-decoder=jpegls + + --enable-demuxer=aac + --enable-demuxer=ac3 + --enable-demuxer=aiff + --enable-demuxer=ape + --enable-demuxer=asf + --enable-demuxer=au + --enable-demuxer=avi + --enable-demuxer=flac + --enable-demuxer=flv + --enable-demuxer=matroska + --enable-demuxer=mov + --enable-demuxer=m4v + --enable-demuxer=mp3 + --enable-demuxer=mpc* + --enable-demuxer=ogg + --enable-demuxer=pcm* + --enable-demuxer=rm + --enable-demuxer=shorten + --enable-demuxer=tak + --enable-demuxer=tta + --enable-demuxer=wav + --enable-demuxer=wv + --enable-demuxer=xwma + --enable-demuxer=dsf + --enable-demuxer=dts + --enable-demuxer=truehd + --enable-demuxer=dts + --enable-demuxer=dtshd + + --enable-parser=aac* + --enable-parser=ac3 + --enable-parser=cook + --enable-parser=flac + --enable-parser=gsm + --enable-parser=mpegaudio + --enable-parser=tak + --enable-parser=vorbis + --enable-parser=dca + + --enable-filter=overlay + --enable-filter=equalizer + + --enable-protocol=async + --enable-protocol=cache + --enable-protocol=crypto + --enable-protocol=data + --enable-protocol=ffrtmphttp + --enable-protocol=file + --enable-protocol=ftp + --enable-protocol=hls + --enable-protocol=http + --enable-protocol=httpproxy + --enable-protocol=https + --enable-protocol=pipe + --enable-protocol=rtmp + --enable-protocol=rtmps + --enable-protocol=rtmpt + --enable-protocol=rtmpts + --enable-protocol=rtp + --enable-protocol=subfile + --enable-protocol=tcp + --enable-protocol=tls + --enable-protocol=srt + --enable-protocol=udp + + --enable-encoder=mjpeg + --enable-encoder=ljpeg + --enable-encoder=jpegls + --enable-encoder=jpeg2000 + --enable-encoder=png + --enable-encoder=jpegls + + --enable-network + ${ffmpeg_cuda} - --enable-amf - --enable-openal - --enable-opengl - --disable-doc - --disable-ffplay - --disable-ffprobe - --enable-vaapi - --disable-vdpau - --disable-videotoolbox - --disable-decoder=libaom_av1 ${ffmpeg_lto} --extra-cflags='-Wno-error=int-conversion' "--extra-libs='${ffmpeg_extra_libs}'" # -lstdc++ / -lc++ needs by libjxl and shaderc - BUILD_COMMAND ${MAKE} - INSTALL_COMMAND ${MAKE} install - LOG_DOWNLOAD 1 LOG_UPDATE 1 LOG_CONFIGURE 1 LOG_BUILD 1 LOG_INSTALL 1 + BUILD_COMMAND ${MAKE} + INSTALL_COMMAND ${MAKE} install + LOG_DOWNLOAD 1 LOG_UPDATE 1 LOG_CONFIGURE 1 LOG_BUILD 1 LOG_INSTALL 1 ) force_rebuild_git(ffmpeg) diff --git a/packages/luajit-0001-add-win32-utf-8-filesystem-functions.patch b/packages/luajit-0001-add-win32-utf-8-filesystem-functions.patch deleted file mode 100644 index 7e36a4ca2..000000000 --- a/packages/luajit-0001-add-win32-utf-8-filesystem-functions.patch +++ /dev/null @@ -1,855 +0,0 @@ -From c92ec4586cb91e430aa2c9d7894cf0128a729633 Mon Sep 17 00:00:00 2001 -From: shinchiro -Date: Tue, 25 Jul 2023 22:53:21 +0800 -Subject: [PATCH] Add win32 UTF-8 filesystem functions - -Taken from https://github.com/wm4/libinsanity ---- - src/Makefile | 2 +- - src/Makefile.dep | 3 +- - src/lib_io.c | 1 + - src/lib_os.c | 1 + - src/lib_package.c | 1 + - src/lj_clib.c | 1 + - src/lj_fs_io.c | 582 +++++++++++++++++++++++++++++++++++++++++ - src/lj_fs_io.h | 60 +++++ - src/lj_fs_io_replace.h | 50 ++++ - src/lj_load.c | 1 + - src/lj_trace.c | 1 + - src/ljamalg.c | 1 + - 12 files changed, 702 insertions(+), 2 deletions(-) - create mode 100644 src/lj_fs_io.c - create mode 100644 src/lj_fs_io.h - create mode 100644 src/lj_fs_io_replace.h - -diff --git a/src/Makefile b/src/Makefile -index 68a9a7c..26137e4 100644 ---- a/src/Makefile -+++ b/src/Makefile -@@ -513,7 +513,7 @@ LJCORE_O= lj_assert.o lj_gc.o lj_err.o lj_char.o lj_bc.o lj_obj.o lj_buf.o \ - lj_asm.o lj_trace.o lj_gdbjit.o \ - lj_ctype.o lj_cdata.o lj_cconv.o lj_ccall.o lj_ccallback.o \ - lj_carith.o lj_clib.o lj_cparse.o \ -- lj_lib.o lj_alloc.o lib_aux.o \ -+ lj_lib.o lj_alloc.o lj_fs_io.o lib_aux.o \ - $(LJLIB_O) lib_init.o lj_str_hash.o - - ifeq (x64,$(TARGET_LJARCH)) -diff --git a/src/Makefile.dep b/src/Makefile.dep -index 400ef8b..5f26250 100644 ---- a/src/Makefile.dep -+++ b/src/Makefile.dep -@@ -47,6 +47,7 @@ lib_table.o: lib_table.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \ - lj_tab.h lj_ff.h lj_ffdef.h lj_lib.h lj_libdef.h - lj_alloc.o: lj_alloc.c lj_def.h lua.h luaconf.h lj_arch.h lj_alloc.h \ - lj_prng.h -+lj_fs_io.o: lj_fs_io.c lj_fs_io.h - lj_api.o: lj_api.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ - lj_err.h lj_errmsg.h lj_debug.h lj_str.h lj_tab.h lj_func.h lj_udata.h \ - lj_meta.h lj_state.h lj_bc.h lj_frame.h lj_trace.h lj_jit.h lj_ir.h \ -@@ -237,7 +238,7 @@ ljamalg.o: ljamalg.c lua.h luaconf.h lauxlib.h lj_assert.c lj_obj.h \ - lj_opt_narrow.c lj_opt_dce.c lj_opt_loop.c lj_snap.h lj_opt_split.c \ - lj_opt_sink.c lj_mcode.c lj_snap.c lj_record.c lj_record.h lj_ffrecord.h \ - lj_crecord.c lj_crecord.h lj_ffrecord.c lj_recdef.h lj_asm.c lj_asm.h \ -- lj_emit_*.h lj_asm_*.h lj_trace.c lj_gdbjit.h lj_gdbjit.c lj_alloc.c \ -+ lj_emit_*.h lj_asm_*.h lj_trace.c lj_gdbjit.h lj_gdbjit.c lj_alloc.c lj_fs_io.c \ - lib_aux.c lib_base.c lj_libdef.h lib_math.c lib_string.c lib_table.c \ - lib_io.c lib_os.c lib_package.c lib_debug.c lib_bit.c lib_jit.c \ - lib_ffi.c lib_buffer.c lib_init.c -diff --git a/src/lib_io.c b/src/lib_io.c -index c22faa2..3014b06 100644 ---- a/src/lib_io.c -+++ b/src/lib_io.c -@@ -25,6 +25,7 @@ - #include "lj_strfmt.h" - #include "lj_ff.h" - #include "lj_lib.h" -+#include "lj_fs_io_replace.h" - - /* Userdata payload for I/O file. */ - typedef struct IOFileUD { -diff --git a/src/lib_os.c b/src/lib_os.c -index 4b4e150..0515579 100644 ---- a/src/lib_os.c -+++ b/src/lib_os.c -@@ -22,6 +22,7 @@ - #include "lj_buf.h" - #include "lj_str.h" - #include "lj_lib.h" -+#include "lj_fs_io_replace.h" - - #if LJ_TARGET_POSIX - #include -diff --git a/src/lib_package.c b/src/lib_package.c -index 63a9121..70fdc53 100644 ---- a/src/lib_package.c -+++ b/src/lib_package.c -@@ -16,6 +16,7 @@ - #include "lj_obj.h" - #include "lj_err.h" - #include "lj_lib.h" -+#include "lj_fs_io_replace.h" - - /* ------------------------------------------------------------------------ */ - -diff --git a/src/lj_clib.c b/src/lj_clib.c -index f0ef6ed..8b22824 100644 ---- a/src/lj_clib.c -+++ b/src/lj_clib.c -@@ -17,6 +17,7 @@ - #include "lj_cdata.h" - #include "lj_clib.h" - #include "lj_strfmt.h" -+#include "lj_fs_io_replace.h" - - /* -- OS-specific functions ----------------------------------------------- */ - -diff --git a/src/lj_fs_io.c b/src/lj_fs_io.c -new file mode 100644 -index 0000000..80668b7 ---- /dev/null -+++ b/src/lj_fs_io.c -@@ -0,0 +1,582 @@ -+// This file is copyrighted. -+ -+#define lj_fs_io_c -+#define LUA_CORE -+ -+#ifdef __MINGW32__ -+ -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "lj_fs_io.h" -+ -+// Like free(), but do not clobber errno and the win32 error state. It is -+// unknown whether MSVCRT's free() clobbers these, but do not take any chances. -+static void free_keep_err(void *ptr) -+{ -+ int err = errno; -+ DWORD bill = GetLastError(); -+ -+ free(ptr); -+ -+ errno = err; -+ SetLastError(bill); -+} -+ -+static void set_errno_from_lasterror(void) -+{ -+ // This just handles the error codes expected from CreateFile at the moment -+ switch (GetLastError()) { -+ case ERROR_FILE_NOT_FOUND: -+ errno = ENOENT; -+ break; -+ case ERROR_SHARING_VIOLATION: -+ case ERROR_ACCESS_DENIED: -+ errno = EACCES; -+ break; -+ case ERROR_FILE_EXISTS: -+ case ERROR_ALREADY_EXISTS: -+ errno = EEXIST; -+ break; -+ case ERROR_PIPE_BUSY: -+ errno = EAGAIN; -+ break; -+ default: -+ errno = EINVAL; -+ break; -+ } -+} -+ -+// Returns NULL + errno on failure. -+static wchar_t *lin_utf8_to_winchar(const char *in) -+{ -+ int res = MultiByteToWideChar(CP_UTF8, 0, in, -1, NULL, 0); -+ if (res <= 0) { -+ set_errno_from_lasterror(); -+ return NULL; -+ } -+ wchar_t *str = malloc(res * sizeof(wchar_t)); -+ if (!str) { -+ errno = ENOMEM; -+ return NULL; -+ } -+ if (MultiByteToWideChar(CP_UTF8, 0, in, -1, str, res) <= 0) { -+ set_errno_from_lasterror(); -+ return NULL; -+ } -+ return str; -+} -+ -+// Returns NULL + errno on failure. -+static char *lin_winchar_to_utf8(const wchar_t *in) -+{ -+ int res = WideCharToMultiByte(CP_UTF8, 0, in, -1, NULL, 0, NULL, NULL); -+ if (res <= 0) { -+ set_errno_from_lasterror(); -+ return NULL; -+ } -+ char *str = malloc(res); -+ if (!str) { -+ errno = ENOMEM; -+ return NULL; -+ } -+ if (WideCharToMultiByte(CP_UTF8, 0, in, -1, str, res, NULL, NULL) <= 0) { -+ set_errno_from_lasterror(); -+ return NULL; -+ } -+ return str; -+} -+ -+static time_t filetime_to_unix_time(int64_t wintime) -+{ -+ static const int64_t hns_per_second = 10000000ll; -+ static const int64_t win_to_unix_epoch = 11644473600ll; -+ return wintime / hns_per_second - win_to_unix_epoch; -+} -+ -+static bool get_file_ids_win8(HANDLE h, lin_dev_t *dev, lin_ino_t *ino) -+{ -+ FILE_ID_INFO ii; -+ if (!GetFileInformationByHandleEx(h, FileIdInfo, &ii, sizeof(ii))) -+ return false; -+ *dev = ii.VolumeSerialNumber; -+ // The definition of FILE_ID_128 differs between mingw-w64 and the Windows -+ // SDK, but we can ignore that by just memcpying it. This will also -+ // truncate the file ID on 32-bit Windows, which doesn't support __int128. -+ // 128-bit file IDs are only used for ReFS, so that should be okay. -+ assert(sizeof(*ino) <= sizeof(ii.FileId)); -+ memcpy(ino, &ii.FileId, sizeof(*ino)); -+ return true; -+} -+ -+static bool get_file_ids(HANDLE h, lin_dev_t *dev, lin_ino_t *ino) -+{ -+ // GetFileInformationByHandle works on FAT partitions and Windows 7, but -+ // doesn't work in UWP and can produce non-unique IDs on ReFS -+ BY_HANDLE_FILE_INFORMATION bhfi; -+ if (!GetFileInformationByHandle(h, &bhfi)) -+ return false; -+ *dev = bhfi.dwVolumeSerialNumber; -+ *ino = ((lin_ino_t)bhfi.nFileIndexHigh << 32) | bhfi.nFileIndexLow; -+ return true; -+} -+ -+// Like fstat(), but with a Windows HANDLE -+static int hstat(HANDLE h, struct lin_stat *buf) -+{ -+ // Handle special (or unknown) file types first -+ switch (GetFileType(h) & ~FILE_TYPE_REMOTE) { -+ case FILE_TYPE_PIPE: -+ *buf = (struct lin_stat){ .st_nlink = 1, .st_mode = _S_IFIFO | 0644 }; -+ return 0; -+ case FILE_TYPE_CHAR: // character device -+ *buf = (struct lin_stat){ .st_nlink = 1, .st_mode = _S_IFCHR | 0644 }; -+ return 0; -+ case FILE_TYPE_UNKNOWN: -+ errno = EBADF; -+ return -1; -+ } -+ -+ struct lin_stat st = { 0 }; -+ -+ FILE_BASIC_INFO bi; -+ if (!GetFileInformationByHandleEx(h, FileBasicInfo, &bi, sizeof(bi))) { -+ errno = EBADF; -+ return -1; -+ } -+ st.st_atime = filetime_to_unix_time(bi.LastAccessTime.QuadPart); -+ st.st_mtime = filetime_to_unix_time(bi.LastWriteTime.QuadPart); -+ st.st_ctime = filetime_to_unix_time(bi.ChangeTime.QuadPart); -+ -+ FILE_STANDARD_INFO si; -+ if (!GetFileInformationByHandleEx(h, FileStandardInfo, &si, sizeof(si))) { -+ errno = EBADF; -+ return -1; -+ } -+ st.st_nlink = si.NumberOfLinks; -+ -+ // Here we pretend Windows has POSIX permissions by pretending all -+ // directories are 755 and regular files are 644 -+ if (si.Directory) { -+ st.st_mode |= _S_IFDIR | 0755; -+ } else { -+ st.st_mode |= _S_IFREG | 0644; -+ st.st_size = si.EndOfFile.QuadPart; -+ } -+ -+ if (!get_file_ids_win8(h, &st.st_dev, &st.st_ino)) { -+ // Fall back to the Windows 7 method (also used for FAT in Win8) -+ if (!get_file_ids(h, &st.st_dev, &st.st_ino)) { -+ errno = EBADF; -+ return -1; -+ } -+ } -+ -+ *buf = st; -+ return 0; -+} -+ -+int lin_stat(const char *path, struct lin_stat *buf) -+{ -+ wchar_t *wpath = lin_utf8_to_winchar(path); -+ if (!wpath) -+ return -1; -+ HANDLE h = CreateFileW(wpath, FILE_READ_ATTRIBUTES, -+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, -+ OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | SECURITY_SQOS_PRESENT | -+ SECURITY_IDENTIFICATION, NULL); -+ free(wpath); -+ if (h == INVALID_HANDLE_VALUE) { -+ set_errno_from_lasterror(); -+ return -1; -+ } -+ -+ int ret = hstat(h, buf); -+ CloseHandle(h); -+ return ret; -+} -+ -+int lin_fstat(int fd, struct lin_stat *buf) -+{ -+ HANDLE h = (HANDLE)_get_osfhandle(fd); -+ if (h == INVALID_HANDLE_VALUE) { -+ errno = EBADF; -+ return -1; -+ } -+ // Use our hstat() function rather than MSVCRT's fstat() because ours -+ // supports directories and device/inode numbers. -+ return hstat(h, buf); -+} -+ -+int lin_open(const char *filename, int oflag, ...) -+{ -+ // Always use all share modes, which is useful for opening files that are -+ // open in other processes, and also more POSIX-like -+ static const DWORD share = -+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; -+ // Setting FILE_APPEND_DATA and avoiding GENERIC_WRITE/FILE_WRITE_DATA -+ // will make the file handle use atomic append behavior -+ static const DWORD append = -+ FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA; -+ -+ DWORD access = 0; -+ DWORD disposition = 0; -+ DWORD flags = 0; -+ -+ switch (oflag & (_O_RDONLY | _O_RDWR | _O_WRONLY | _O_APPEND)) { -+ case _O_RDONLY: -+ access = GENERIC_READ; -+ flags |= FILE_FLAG_BACKUP_SEMANTICS; // For opening directories -+ break; -+ case _O_RDWR: -+ access = GENERIC_READ | GENERIC_WRITE; -+ break; -+ case _O_RDWR | _O_APPEND: -+ case _O_RDONLY | _O_APPEND: -+ access = GENERIC_READ | append; -+ break; -+ case _O_WRONLY: -+ access = GENERIC_WRITE; -+ break; -+ case _O_WRONLY | _O_APPEND: -+ access = append; -+ break; -+ default: -+ errno = EINVAL; -+ return -1; -+ } -+ -+ switch (oflag & (_O_CREAT | _O_EXCL | _O_TRUNC)) { -+ case 0: -+ case _O_EXCL: // Like MSVCRT, ignore invalid use of _O_EXCL -+ disposition = OPEN_EXISTING; -+ break; -+ case _O_TRUNC: -+ case _O_TRUNC | _O_EXCL: -+ disposition = TRUNCATE_EXISTING; -+ break; -+ case _O_CREAT: -+ disposition = OPEN_ALWAYS; -+ flags |= FILE_ATTRIBUTE_NORMAL; -+ break; -+ case _O_CREAT | _O_TRUNC: -+ disposition = CREATE_ALWAYS; -+ break; -+ case _O_CREAT | _O_EXCL: -+ case _O_CREAT | _O_EXCL | _O_TRUNC: -+ disposition = CREATE_NEW; -+ flags |= FILE_ATTRIBUTE_NORMAL; -+ break; -+ } -+ -+ // Opening a named pipe as a file can allow the pipe server to impersonate -+ // the calling process, which could be a security issue. Set SQOS flags, so -+ // pipe servers can only identify our process, not impersonate it. -+ if (disposition != CREATE_NEW) -+ flags |= SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION; -+ -+ // Keep the same semantics for some MSVCRT-specific flags -+ if (oflag & _O_TEMPORARY) { -+ flags |= FILE_FLAG_DELETE_ON_CLOSE; -+ access |= DELETE; -+ } -+ if (oflag & _O_SHORT_LIVED) -+ flags |= FILE_ATTRIBUTE_TEMPORARY; -+ if (oflag & _O_SEQUENTIAL) { -+ flags |= FILE_FLAG_SEQUENTIAL_SCAN; -+ } else if (oflag & _O_RANDOM) { -+ flags |= FILE_FLAG_RANDOM_ACCESS; -+ } -+ -+ // Open the Windows file handle -+ wchar_t *wpath = lin_utf8_to_winchar(filename); -+ if (!wpath) -+ return -1; -+ HANDLE h = CreateFileW(wpath, access, share, NULL, disposition, flags, NULL); -+ free(wpath); -+ if (h == INVALID_HANDLE_VALUE) { -+ set_errno_from_lasterror(); -+ return -1; -+ } -+ -+ // Map the Windows file handle to a CRT file descriptor. Note: MSVCRT only -+ // cares about the following oflags. -+ oflag &= _O_APPEND | _O_RDONLY | _O_RDWR | _O_WRONLY; -+ oflag |= _O_NOINHERIT; // We never create inheritable handles -+ int fd = _open_osfhandle((intptr_t)h, oflag); -+ if (fd < 0) { -+ CloseHandle(h); -+ return -1; -+ } -+ -+ return fd; -+} -+ -+int lin_creat(const char *filename, int mode) -+{ -+ return lin_open(filename, _O_CREAT | _O_WRONLY | _O_TRUNC, mode); -+} -+ -+FILE *lin_fopen(const char *filename, const char *mode) -+{ -+ if (!mode[0]) { -+ errno = EINVAL; -+ return NULL; -+ } -+ -+ int rwmode; -+ int oflags = 0; -+ switch (mode[0]) { -+ case 'r': -+ rwmode = _O_RDONLY; -+ break; -+ case 'w': -+ rwmode = _O_WRONLY; -+ oflags |= _O_CREAT | _O_TRUNC; -+ break; -+ case 'a': -+ rwmode = _O_WRONLY; -+ oflags |= _O_CREAT | _O_APPEND; -+ break; -+ default: -+ errno = EINVAL; -+ return NULL; -+ } -+ -+ // Parse extra mode flags -+ for (const char *pos = mode + 1; *pos; pos++) { -+ switch (*pos) { -+ case '+': rwmode = _O_RDWR; break; -+ case 'x': oflags |= _O_EXCL; break; -+ // Ignore unknown flags (glibc does too) -+ default: break; -+ } -+ } -+ -+ // Open a CRT file descriptor -+ int fd = lin_open(filename, rwmode | oflags); -+ if (fd < 0) -+ return NULL; -+ -+ // Add 'b' to the mode so the CRT knows the file is opened in binary mode -+ char bmode[] = { mode[0], 'b', rwmode == _O_RDWR ? '+' : '\0', '\0' }; -+ FILE *fp = fdopen(fd, bmode); -+ if (!fp) { -+ close(fd); -+ return NULL; -+ } -+ -+ return fp; -+} -+ -+// Windows' MAX_PATH/PATH_MAX/FILENAME_MAX is fixed to 260, but this limit -+// applies to unicode paths encoded with wchar_t (2 bytes on Windows). The UTF-8 -+// version could end up bigger in memory. In the worst case each wchar_t is -+// encoded to 3 bytes in UTF-8, so in the worst case we have: -+// wcslen(wpath) * 3 <= strlen(utf8path) -+// Thus we need LIN_PATH_MAX as the UTF-8/char version of PATH_MAX. -+// Also make sure there's free space for the terminating \0. -+// (For codepoints encoded as UTF-16 surrogate pairs, UTF-8 has the same length.) -+#define LIN_PATH_MAX (FILENAME_MAX * 3 + 1) -+ -+struct lin_dir { -+ DIR crap; // must be first member, unused, will be set to garbage -+ _WDIR *wdir; -+ union { -+ struct dirent dirent; -+ // dirent has space only for FILENAME_MAX bytes. _wdirent has space for -+ // FILENAME_MAX wchar_t, which might end up bigger as UTF-8 in some -+ // cases. Guarantee we can always hold _wdirent.d_name converted to -+ // UTF-8 (see LIN_PATH_MAX). -+ // This works because dirent.d_name is the last member of dirent. -+ char space[LIN_PATH_MAX]; -+ }; -+}; -+ -+DIR* lin_opendir(const char *path) -+{ -+ wchar_t *wpath = lin_utf8_to_winchar(path); -+ if (!wpath) -+ return NULL; -+ _WDIR *wdir = _wopendir(wpath); -+ free_keep_err(wpath); -+ if (!wdir) -+ return NULL; -+ struct lin_dir *ldir = calloc(1, sizeof(*ldir)); -+ if (!ldir) { -+ _wclosedir(wdir); -+ errno = ENOMEM; -+ return NULL; -+ } -+ // DIR is supposed to be opaque, but unfortunately the MinGW headers still -+ // define it. Make sure nobody tries to use it. -+ memset(&ldir->crap, 0xCD, sizeof(ldir->crap)); -+ ldir->wdir = wdir; -+ return (DIR*)ldir; -+} -+ -+struct dirent* lin_readdir(DIR *dir) -+{ -+ struct lin_dir *ldir = (struct lin_dir*)dir; -+ struct _wdirent *wdirent = _wreaddir(ldir->wdir); -+ if (!wdirent) -+ return NULL; -+ size_t buffersize = sizeof(ldir->space) - offsetof(struct dirent, d_name); -+ if (WideCharToMultiByte(CP_UTF8, 0, wdirent->d_name, -1, ldir->dirent.d_name, -+ buffersize, NULL, NULL) <= 0) -+ { -+ set_errno_from_lasterror(); -+ return NULL; -+ } -+ ldir->dirent.d_ino = 0; -+ ldir->dirent.d_reclen = 0; -+ ldir->dirent.d_namlen = strlen(ldir->dirent.d_name); -+ return &ldir->dirent; -+} -+ -+int lin_closedir(DIR *dir) -+{ -+ struct lin_dir *ldir = (struct lin_dir*)dir; -+ int res = _wclosedir(ldir->wdir); -+ free_keep_err(ldir); -+ return res; -+} -+ -+int lin_mkdir(const char *path, int mode) -+{ -+ wchar_t *wpath = lin_utf8_to_winchar(path); -+ if (!wpath) -+ return -1; -+ int res = _wmkdir(wpath); -+ free(wpath); -+ return res; -+} -+ -+char *lin_getcwd(char *buf, size_t size) -+{ -+ if (size >= SIZE_MAX / 3 - 1) { -+ errno = ENOMEM; -+ return NULL; -+ } -+ size_t wbuffer = size * 3 + 1; -+ wchar_t *wres = malloc(sizeof(wchar_t) * wbuffer); -+ if (!wres) { -+ errno = ENOMEM; -+ return NULL; -+ } -+ DWORD wlen = GetFullPathNameW(L".", wbuffer, wres, NULL); -+ if (wlen >= wbuffer || wlen == 0) { -+ free(wres); -+ errno = wlen ? ERANGE : ENOENT; -+ return NULL; -+ } -+ char *t = lin_winchar_to_utf8(wres); -+ free_keep_err(wres); -+ if (!t) -+ return NULL; -+ size_t st = strlen(t); -+ if (st >= size) { -+ free(t); -+ errno = ERANGE; -+ return NULL; -+ } -+ memcpy(buf, t, st + 1); -+ free(t); -+ return buf; -+} -+ -+off_t lin_lseek(int fd, off_t offset, int whence) -+{ -+ HANDLE h = (HANDLE)_get_osfhandle(fd); -+ if (h != INVALID_HANDLE_VALUE && GetFileType(h) != FILE_TYPE_DISK) { -+ errno = ESPIPE; -+ return (off_t)-1; -+ } -+ return _lseeki64(fd, offset, whence); -+} -+ -+static INIT_ONCE init_getenv_once = INIT_ONCE_STATIC_INIT; -+static char **utf8_environ; -+ -+static void free_env(void) -+{ -+ for (size_t n = 0; utf8_environ && utf8_environ[n]; n++) -+ free(utf8_environ[n]); -+ free(utf8_environ); -+ utf8_environ = NULL; -+} -+ -+// Note: UNIX getenv() returns static strings, and we try to do the same. Since -+// using putenv() is not multithreading safe, we don't expect env vars to change -+// at runtime, and converting/allocating them in advance is ok. -+static void init_getenv(void) -+{ -+ wchar_t *wenv = GetEnvironmentStringsW(); -+ if (!wenv) -+ goto done; -+ -+ wchar_t *wenv_cur = wenv; -+ size_t num_env = 0; -+ while (1) { -+ size_t len = wcslen(wenv_cur); -+ if (!len) -+ break; -+ num_env++; -+ wenv_cur += len + 1; -+ } -+ -+ utf8_environ = calloc(sizeof(utf8_environ[0]), num_env + 1); -+ if (!utf8_environ) -+ goto done; -+ -+ wenv_cur = wenv; -+ size_t cur_env = 0; -+ while (1) { -+ size_t len = wcslen(wenv_cur); -+ if (!len) -+ break; -+ assert(cur_env < num_env); -+ // On OOM, best-effort -+ utf8_environ[cur_env] = lin_winchar_to_utf8(wenv_cur); -+ if (utf8_environ[cur_env]) -+ cur_env++; -+ wenv_cur += len + 1; -+ } -+ -+done: -+ FreeEnvironmentStringsW(wenv); -+ -+ // Avoid showing up in leak detectors etc. -+ atexit(free_env); -+} -+ -+char *lin_getenv(const char *name) -+{ -+ BOOL pending; -+ if (!InitOnceBeginInitialize(&init_getenv_once, 0, &pending, NULL)) -+ return NULL; -+ if (pending) { -+ init_getenv(); -+ InitOnceComplete(&init_getenv_once, 0, NULL); -+ } -+ -+ size_t name_len = strlen(name); -+ for (size_t n = 0; utf8_environ && utf8_environ[n]; n++) { -+ char *env = utf8_environ[n]; -+ if (strncasecmp(env, name, name_len) == 0 && env[name_len] == '=') -+ return env + name_len + 1; -+ } -+ -+ return NULL; -+} -+ -+#endif -diff --git a/src/lj_fs_io.h b/src/lj_fs_io.h -new file mode 100644 -index 0000000..0bdcd20 ---- /dev/null -+++ b/src/lj_fs_io.h -@@ -0,0 +1,60 @@ -+// This file is copyrighted. -+ -+#ifndef _LJ_FS_IO_H -+#define _LJ_FS_IO_H -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#ifdef __MINGW32__ -+ -+int lin_open(const char *filename, int oflag, ...); -+int lin_creat(const char *filename, int mode); -+FILE *lin_fopen(const char *filename, const char *mode); -+DIR *lin_opendir(const char *path); -+struct dirent *lin_readdir(DIR *dir); -+int lin_closedir(DIR *dir); -+int lin_mkdir(const char *path, int mode); -+char *lin_getcwd(char *buf, size_t size); -+off_t lin_lseek(int fd, off_t offset, int whence); -+ -+// lin_stat types. MSVCRT's dev_t and ino_t are way too short to be unique. -+typedef uint64_t lin_dev_t; -+#ifdef _WIN64 -+typedef unsigned __int128 lin_ino_t; -+#else -+// 32-bit Windows doesn't have a __int128-type, which means ReFS file IDs will -+// be truncated and might collide. This is probably not a problem because ReFS -+// is not available in consumer versions of Windows. -+typedef uint64_t lin_ino_t; -+#endif -+ -+// lin_stat uses a different structure to MSVCRT, with 64-bit inodes -+struct lin_stat { -+ lin_dev_t st_dev; -+ lin_ino_t st_ino; -+ unsigned short st_mode; -+ unsigned int st_nlink; -+ short st_uid; -+ short st_gid; -+ lin_dev_t st_rdev; -+ int64_t st_size; -+ time_t st_atime; -+ time_t st_mtime; -+ time_t st_ctime; -+}; -+ -+int lin_stat(const char *path, struct lin_stat *buf); -+int lin_fstat(int fd, struct lin_stat *buf); -+ -+char *lin_getenv(const char *name); -+ -+#endif /* __MINGW32__ */ -+ -+#endif -diff --git a/src/lj_fs_io_replace.h b/src/lj_fs_io_replace.h -new file mode 100644 -index 0000000..604e279 ---- /dev/null -+++ b/src/lj_fs_io_replace.h -@@ -0,0 +1,50 @@ -+// This file is copyrighted. -+ -+#ifndef _LJ_FS_IO_REPLACE -+#define _LJ_FS_IO_REPLACE -+ -+#include "lj_fs_io.h" -+ -+#ifdef __MINGW32__ -+ -+#define dev_t lin_dev_t -+#define ino_t lin_ino_t -+ -+#define open(...) lin_open(__VA_ARGS__) -+#define creat(...) lin_creat(__VA_ARGS__) -+#define fopen(...) lin_fopen(__VA_ARGS__) -+#define opendir(...) lin_opendir(__VA_ARGS__) -+#define readdir(...) lin_readdir(__VA_ARGS__) -+#define closedir(...) lin_closedir(__VA_ARGS__) -+#define mkdir(...) lin_mkdir(__VA_ARGS__) -+#define getcwd(...) lin_getcwd(__VA_ARGS__) -+#define getenv(...) lin_getenv(__VA_ARGS__) -+ -+#undef lseek -+#define lseek(...) lin_lseek(__VA_ARGS__) -+ -+// Affects both "stat()" and "struct stat". -+#undef stat -+#define stat lin_stat -+ -+#undef fstat -+#define fstat(...) lin_fstat(__VA_ARGS__) -+ -+#define utime(...) _utime(__VA_ARGS__) -+#define utimbuf _utimbuf -+ -+#ifndef O_BINARY -+#define O_BINARY 0 -+#endif -+ -+#ifndef O_CLOEXEC -+#define O_CLOEXEC 0 -+#endif -+ -+#ifndef FD_CLOEXEC -+#define FD_CLOEXEC 0 -+#endif -+ -+#endif /* __MINGW32__ */ -+ -+#endif -diff --git a/src/lj_load.c b/src/lj_load.c -index 0aab488..150be4e 100644 ---- a/src/lj_load.c -+++ b/src/lj_load.c -@@ -22,6 +22,7 @@ - #include "lj_lex.h" - #include "lj_bcdump.h" - #include "lj_parse.h" -+#include "lj_fs_io_replace.h" - - /* -- Load Lua source code and bytecode ----------------------------------- */ - -diff --git a/src/lj_trace.c b/src/lj_trace.c -index f816337..f15c4c7 100644 ---- a/src/lj_trace.c -+++ b/src/lj_trace.c -@@ -31,6 +31,7 @@ - #include "lj_vmevent.h" - #include "lj_target.h" - #include "lj_prng.h" -+#include "lj_fs_io_replace.h" - - /* -- Error handling ------------------------------------------------------ */ - -diff --git a/src/ljamalg.c b/src/ljamalg.c -index 4d85950..68fb34b 100644 ---- a/src/ljamalg.c -+++ b/src/ljamalg.c -@@ -18,6 +18,7 @@ - #include "lua.h" - #include "lauxlib.h" - -+#include "lj_fs_io.c" - #include "lj_assert.c" - #include "lj_gc.c" - #include "lj_err.c" --- -2.41.0 - diff --git a/packages/luajit.cmake b/packages/luajit.cmake deleted file mode 100644 index 4499eb6a9..000000000 --- a/packages/luajit.cmake +++ /dev/null @@ -1,58 +0,0 @@ -if(CYGWIN OR MSYS) - # It's much easier to just use the target CC on Cygwin than to worry about - # pointer size mismatches - set(LUAJIT_HOST_GCC ${TARGET_ARCH}-gcc) -else() - set(LUAJIT_HOST_GCC gcc) -endif() - -if(${TARGET_CPU} MATCHES "i686") - set(LUAJIT_GCC_ARGS "-m32") - set(DISABLE_JIT "-DLUAJIT_DISABLE_JIT") -else() - set(LUAJIT_GCC_ARGS "-m64") -endif() - -set(EXPORT - "CROSS=${TARGET_ARCH}- - TARGET_SYS=Windows - BUILDMODE=static - FILE_T=luajit.exe - CFLAGS='-DUNICODE' - XCFLAGS='-DLUAJIT_ENABLE_LUA52COMPAT ${DISABLE_JIT}' - PREFIX=${MINGW_INSTALL_PREFIX} Q=" -) - -# luajit ships with a broken pkg-config file -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/luajit.pc.in ${CMAKE_CURRENT_BINARY_DIR}/luajit.pc @ONLY) - -ExternalProject_Add(luajit - DEPENDS - libiconv - GIT_REPOSITORY https://github.com/openresty/luajit2.git - SOURCE_DIR ${SOURCE_LOCATION} - GIT_CLONE_FLAGS "--filter=tree:0" - GIT_REMOTE_NAME origin - GIT_TAG v2.1-agentzh - PATCH_COMMAND ${EXEC} git am --3way ${CMAKE_CURRENT_SOURCE_DIR}/luajit-*.patch - UPDATE_COMMAND "" - CONFIGURE_COMMAND "" - BUILD_COMMAND ${MAKE} -C /src - "HOST_CC='${LUAJIT_HOST_GCC} ${LUAJIT_GCC_ARGS}'" - ${EXPORT} - amalg - INSTALL_COMMAND ${MAKE} - "HOST_CC='${LUAJIT_HOST_GCC} ${LUAJIT_GCC_ARGS}'" - ${EXPORT} - install - BUILD_IN_SOURCE 1 - LOG_DOWNLOAD 1 LOG_UPDATE 1 LOG_CONFIGURE 1 LOG_BUILD 1 LOG_INSTALL 1 -) - -ExternalProject_Add_Step(luajit install-pc - DEPENDEES install - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/luajit.pc ${MINGW_INSTALL_PREFIX}/lib/pkgconfig/luajit.pc -) - -force_rebuild_git(luajit) -cleanup(luajit install-pc) diff --git a/packages/luajit.pc.in b/packages/luajit.pc.in deleted file mode 100644 index 506e1501d..000000000 --- a/packages/luajit.pc.in +++ /dev/null @@ -1,25 +0,0 @@ -# Package information for LuaJIT to be used by pkg-config. -majver=2 -minver=1 -relver=0 -version=${majver}.${minver}.${relver}-beta3 -abiver=5.1 - -prefix=@MINGW_INSTALL_PREFIX@ -multilib=lib -exec_prefix=${prefix} -libdir=${exec_prefix}/${multilib} -libname=luajit-${abiver} -includedir=${prefix}/include/luajit-${majver}.${minver} - -INSTALL_LMOD=${prefix}/share/lua/${abiver} -INSTALL_CMOD=${prefix}/${multilib}/lua/${abiver} - -Name: LuaJIT -Description: Just-in-time compiler for Lua -URL: http://luajit.org -Version: ${version} -Requires: -Libs: -L${libdir} -l${libname} -Libs.private: -lm -liconv -Cflags: -I${includedir} diff --git a/packages/mpv-0001-remove-libass.patch b/packages/mpv-0001-remove-libass.patch new file mode 100644 index 000000000..2f2f89a02 --- /dev/null +++ b/packages/mpv-0001-remove-libass.patch @@ -0,0 +1,2498 @@ +diff --git a/meson.build b/meson.build +index b7bcb1b0ba..b7bc29f896 100644 +--- a/meson.build ++++ b/meson.build +@@ -29,12 +29,7 @@ libswscale = dependency('libswscale', version: '>= 7.5.100') + libplacebo = dependency('libplacebo', version: '>=6.338.2', + default_options: ['default_library=static', 'demos=false']) + +-libass = dependency('libass', version: '>= 0.12.2') +- +-# the dependency order of libass -> ffmpeg is necessary due to +-# static linking symbol resolution between fontconfig and MinGW +-dependencies = [libass, +- libavcodec, ++dependencies = [libavcodec, + libavfilter, + libavformat, + libavutil, +@@ -47,7 +42,6 @@ features = { + 'debug': get_option('debug'), + 'ffmpeg': true, + 'gpl': get_option('gpl'), +- 'libass': true, + 'libplacebo': true, + } + +@@ -187,15 +181,12 @@ sources = files( + 'stream/stream_slice.c', + + ## Subtitles +- 'sub/ass_mp.c', + 'sub/dec_sub.c', + 'sub/draw_bmp.c', + 'sub/filter_sdh.c', + 'sub/img_convert.c', + 'sub/lavc_conv.c', + 'sub/osd.c', +- 'sub/osd_libass.c', +- 'sub/sd_ass.c', + 'sub/sd_lavc.c', + + ## Video +diff --git a/player/command.c b/player/command.c +index 6891f4e70f..faae79ed11 100644 +--- a/player/command.c ++++ b/player/command.c +@@ -25,7 +25,6 @@ + #include + #include + +-#include + #include + #include + +@@ -3518,7 +3517,7 @@ static int mp_property_ffmpeg(void *ctx, struct m_property *prop, + static int mp_property_libass_version(void *ctx, struct m_property *prop, + int action, void *arg) + { +- return m_property_int64_ro(action, arg, ass_library_version()); ++ return m_property_int64_ro(action, arg, 0); + } + + static int mp_property_platform(void *ctx, struct m_property *prop, +diff --git a/sub/ass_mp.c b/sub/ass_mp.c +deleted file mode 100644 +index 255d0a077f..0000000000 +--- a/sub/ass_mp.c ++++ /dev/null +@@ -1,419 +0,0 @@ +-/* +- * Copyright (C) 2006 Evgeniy Stepanov +- * +- * This file is part of mpv. +- * +- * mpv is free software; you can redistribute it and/or +- * modify it under the terms of the GNU Lesser General Public +- * License as published by the Free Software Foundation; either +- * version 2.1 of the License, or (at your option) any later version. +- * +- * mpv is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU Lesser General Public License for more details. +- * +- * You should have received a copy of the GNU Lesser General Public +- * License along with mpv. If not, see . +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +-#include +- +-#include "common/common.h" +-#include "common/global.h" +-#include "common/msg.h" +-#include "options/path.h" +-#include "ass_mp.h" +-#include "img_convert.h" +-#include "osd.h" +-#include "stream/stream.h" +-#include "options/options.h" +-#include "video/out/bitmap_packer.h" +-#include "video/mp_image.h" +- +-// res_y should be track->PlayResY +-// It determines scaling of font sizes and more. +-void mp_ass_set_style(ASS_Style *style, double res_y, +- const struct osd_style_opts *opts) +-{ +- if (!style) +- return; +- +- if (opts->font) { +- if (!style->FontName || strcmp(style->FontName, opts->font) != 0) { +- free(style->FontName); +- style->FontName = strdup(opts->font); +- } +- } +- +- // libass_font_size = FontSize * (window_height / res_y) +- // scale translates parameters from PlayResY=720 to res_y +- double scale = res_y / 720.0; +- +- style->FontSize = opts->font_size * scale; +- style->PrimaryColour = MP_ASS_COLOR(opts->color); +- style->SecondaryColour = style->PrimaryColour; +- style->OutlineColour = MP_ASS_COLOR(opts->outline_color); +- style->BackColour = MP_ASS_COLOR(opts->back_color); +- style->BorderStyle = opts->border_style; +- style->Outline = opts->outline_size * scale; +- style->Shadow = opts->shadow_offset * scale; +- style->Spacing = opts->spacing * scale; +- style->MarginL = opts->margin_x * scale; +- style->MarginR = style->MarginL; +- style->MarginV = opts->margin_y * scale; +- style->ScaleX = 1.; +- style->ScaleY = 1.; +- style->Alignment = 1 + (opts->align_x + 1) + (opts->align_y + 2) % 3 * 4; +-#ifdef ASS_JUSTIFY_LEFT +- style->Justify = opts->justify; +-#endif +- style->Blur = opts->blur; +- style->Bold = opts->bold; +- style->Italic = opts->italic; +-} +- +-void mp_ass_configure_fonts(ASS_Renderer *priv, struct osd_style_opts *opts, +- struct mpv_global *global, struct mp_log *log) +-{ +- void *tmp = talloc_new(NULL); +- char *default_font = mp_find_config_file(tmp, global, "subfont.ttf"); +- char *config = mp_find_config_file(tmp, global, "fonts.conf"); +- +- if (default_font && !mp_path_exists(default_font)) +- default_font = NULL; +- +- int font_provider = ASS_FONTPROVIDER_AUTODETECT; +- if (opts->font_provider == 1) +- font_provider = ASS_FONTPROVIDER_NONE; +- if (opts->font_provider == 2) +- font_provider = ASS_FONTPROVIDER_FONTCONFIG; +- +- mp_verbose(log, "Setting up fonts...\n"); +- ass_set_fonts(priv, default_font, opts->font, font_provider, config, 1); +- mp_verbose(log, "Done.\n"); +- +- talloc_free(tmp); +-} +- +-static const int map_ass_level[] = { +- MSGL_ERR, // 0 "FATAL errors" +- MSGL_WARN, +- MSGL_INFO, +- MSGL_V, +- MSGL_V, +- MSGL_DEBUG, // 5 application recommended level +- MSGL_TRACE, +- MSGL_TRACE, // 7 "verbose DEBUG" +-}; +- +-PRINTF_ATTRIBUTE(2, 0) +-static void message_callback(int level, const char *format, va_list va, void *ctx) +-{ +- struct mp_log *log = ctx; +- if (!log) +- return; +- level = map_ass_level[level]; +- mp_msg_va(log, level, format, va); +- // libass messages lack trailing \n +- mp_msg(log, level, "\n"); +-} +- +-ASS_Library *mp_ass_init(struct mpv_global *global, +- struct osd_style_opts *opts, struct mp_log *log) +-{ +- char *path = opts->fonts_dir && opts->fonts_dir[0] ? +- mp_get_user_path(NULL, global, opts->fonts_dir) : +- mp_find_config_file(NULL, global, "fonts"); +- mp_dbg(log, "ASS library version: 0x%x (runtime 0x%x)\n", +- (unsigned)LIBASS_VERSION, ass_library_version()); +- ASS_Library *priv = ass_library_init(); +- if (!priv) +- abort(); +- ass_set_message_cb(priv, message_callback, log); +- if (path) +- ass_set_fonts_dir(priv, path); +- talloc_free(path); +- return priv; +-} +- +-void mp_ass_flush_old_events(ASS_Track *track, long long ts) +-{ +- int n = 0; +- for (; n < track->n_events; n++) { +- if ((track->events[n].Start + track->events[n].Duration) >= ts) +- break; +- ass_free_event(track, n); +- track->n_events--; +- } +- for (int i = 0; n > 0 && i < track->n_events; i++) { +- track->events[i] = track->events[i+n]; +- } +-} +- +-static void draw_ass_rgba(unsigned char *src, int src_w, int src_h, +- int src_stride, unsigned char *dst, size_t dst_stride, +- int dst_x, int dst_y, uint32_t color) +-{ +- const unsigned int r = (color >> 24) & 0xff; +- const unsigned int g = (color >> 16) & 0xff; +- const unsigned int b = (color >> 8) & 0xff; +- const unsigned int a = 0xff - (color & 0xff); +- +- dst += dst_y * dst_stride + dst_x * 4; +- +- for (int y = 0; y < src_h; y++, dst += dst_stride, src += src_stride) { +- uint32_t *dstrow = (uint32_t *) dst; +- for (int x = 0; x < src_w; x++) { +- const unsigned int v = src[x]; +- int rr = (r * a * v); +- int gg = (g * a * v); +- int bb = (b * a * v); +- int aa = a * v; +- uint32_t dstpix = dstrow[x]; +- unsigned int dstb = dstpix & 0xFF; +- unsigned int dstg = (dstpix >> 8) & 0xFF; +- unsigned int dstr = (dstpix >> 16) & 0xFF; +- unsigned int dsta = (dstpix >> 24) & 0xFF; +- dstb = (bb + dstb * (255 * 255 - aa)) / (255 * 255); +- dstg = (gg + dstg * (255 * 255 - aa)) / (255 * 255); +- dstr = (rr + dstr * (255 * 255 - aa)) / (255 * 255); +- dsta = (aa * 255 + dsta * (255 * 255 - aa)) / (255 * 255); +- dstrow[x] = dstb | (dstg << 8) | (dstr << 16) | (dsta << 24); +- } +- } +-} +- +-struct mp_ass_packer { +- struct sub_bitmap *cached_parts; // only for the array memory +- struct mp_image *cached_img; +- struct sub_bitmaps cached_subs; +- bool cached_subs_valid; +- struct sub_bitmap rgba_imgs[MP_SUB_BB_LIST_MAX]; +- struct bitmap_packer *packer; +-}; +- +-// Free with talloc_free(). +-struct mp_ass_packer *mp_ass_packer_alloc(void *ta_parent) +-{ +- struct mp_ass_packer *p = talloc_zero(ta_parent, struct mp_ass_packer); +- p->packer = talloc_zero(p, struct bitmap_packer); +- return p; +-} +- +-static bool pack(struct mp_ass_packer *p, struct sub_bitmaps *res, int imgfmt) +-{ +- packer_set_size(p->packer, res->num_parts); +- +- for (int n = 0; n < res->num_parts; n++) +- p->packer->in[n] = (struct pos){res->parts[n].w, res->parts[n].h}; +- +- if (p->packer->count == 0 || packer_pack(p->packer) < 0) +- return false; +- +- struct pos bb[2]; +- packer_get_bb(p->packer, bb); +- +- res->packed_w = bb[1].x; +- res->packed_h = bb[1].y; +- +- if (!p->cached_img || p->cached_img->w < res->packed_w || +- p->cached_img->h < res->packed_h || +- p->cached_img->imgfmt != imgfmt) +- { +- talloc_free(p->cached_img); +- p->cached_img = mp_image_alloc(imgfmt, p->packer->w, p->packer->h); +- if (!p->cached_img) { +- packer_reset(p->packer); +- return false; +- } +- talloc_steal(p, p->cached_img); +- } +- +- if (!mp_image_make_writeable(p->cached_img)) { +- packer_reset(p->packer); +- return false; +- } +- +- res->packed = p->cached_img; +- +- for (int n = 0; n < res->num_parts; n++) { +- struct sub_bitmap *b = &res->parts[n]; +- struct pos pos = p->packer->result[n]; +- +- b->src_x = pos.x; +- b->src_y = pos.y; +- } +- +- return true; +-} +- +-static bool pack_libass(struct mp_ass_packer *p, struct sub_bitmaps *res) +-{ +- if (!pack(p, res, IMGFMT_Y8)) +- return false; +- +- for (int n = 0; n < res->num_parts; n++) { +- struct sub_bitmap *b = &res->parts[n]; +- +- int stride = res->packed->stride[0]; +- void *pdata = +- (uint8_t *)res->packed->planes[0] + b->src_y * stride + b->src_x; +- memcpy_pic(pdata, b->bitmap, b->w, b->h, stride, b->stride); +- +- b->bitmap = pdata; +- b->stride = stride; +- } +- +- return true; +-} +- +-static bool pack_rgba(struct mp_ass_packer *p, struct sub_bitmaps *res) +-{ +- struct mp_rect bb_list[MP_SUB_BB_LIST_MAX]; +- int num_bb = mp_get_sub_bb_list(res, bb_list, MP_SUB_BB_LIST_MAX); +- +- struct sub_bitmaps imgs = { +- .change_id = res->change_id, +- .format = SUBBITMAP_BGRA, +- .parts = p->rgba_imgs, +- .num_parts = num_bb, +- }; +- +- for (int n = 0; n < imgs.num_parts; n++) { +- imgs.parts[n].w = bb_list[n].x1 - bb_list[n].x0; +- imgs.parts[n].h = bb_list[n].y1 - bb_list[n].y0; +- } +- +- if (!pack(p, &imgs, IMGFMT_BGRA)) +- return false; +- +- for (int n = 0; n < num_bb; n++) { +- struct mp_rect bb = bb_list[n]; +- struct sub_bitmap *b = &imgs.parts[n]; +- +- b->x = bb.x0; +- b->y = bb.y0; +- b->w = b->dw = bb.x1 - bb.x0; +- b->h = b->dh = bb.y1 - bb.y0; +- b->stride = imgs.packed->stride[0]; +- b->bitmap = (uint8_t *)imgs.packed->planes[0] + +- b->stride * b->src_y + b->src_x * 4; +- +- memset_pic(b->bitmap, 0, b->w * 4, b->h, b->stride); +- +- for (int i = 0; i < res->num_parts; i++) { +- struct sub_bitmap *s = &res->parts[i]; +- +- // Assume mp_get_sub_bb_list() never splits sub bitmaps +- // So we don't clip/adjust the size of the sub bitmap +- if (s->x > bb.x1 || s->x + s->w < bb.x0 || +- s->y > bb.y1 || s->y + s->h < bb.y0) +- continue; +- +- draw_ass_rgba(s->bitmap, s->w, s->h, s->stride, +- b->bitmap, b->stride, +- s->x - bb.x0, s->y - bb.y0, +- s->libass.color); +- } +- } +- +- *res = imgs; +- return true; +-} +- +-// Pack the contents of image_lists[0] to image_lists[num_image_lists-1] into +-// a single image, and make *out point to it. *out is completely overwritten. +-// If libass reported any change, image_lists_changed must be set (it then +-// repacks all images). preferred_osd_format can be set to a desired +-// sub_bitmap_format. Currently, only SUBBITMAP_LIBASS is supported. +-void mp_ass_packer_pack(struct mp_ass_packer *p, ASS_Image **image_lists, +- int num_image_lists, bool image_lists_changed, bool video_color_space, +- int preferred_osd_format, struct sub_bitmaps *out) +-{ +- int format = preferred_osd_format == SUBBITMAP_BGRA ? SUBBITMAP_BGRA +- : SUBBITMAP_LIBASS; +- +- if (p->cached_subs_valid && !image_lists_changed && +- p->cached_subs.format == format) +- { +- *out = p->cached_subs; +- return; +- } +- +- *out = (struct sub_bitmaps){.change_id = 1}; +- p->cached_subs_valid = false; +- +- struct sub_bitmaps res = { +- .change_id = image_lists_changed, +- .format = SUBBITMAP_LIBASS, +- .parts = p->cached_parts, +- .video_color_space = video_color_space, +- }; +- +- for (int n = 0; n < num_image_lists; n++) { +- for (struct ass_image *img = image_lists[n]; img; img = img->next) { +- if (img->w == 0 || img->h == 0) +- continue; +- MP_TARRAY_GROW(p, p->cached_parts, res.num_parts); +- res.parts = p->cached_parts; +- struct sub_bitmap *b = &res.parts[res.num_parts]; +- b->bitmap = img->bitmap; +- b->stride = img->stride; +- b->libass.color = img->color; +- b->dw = b->w = img->w; +- b->dh = b->h = img->h; +- b->x = img->dst_x; +- b->y = img->dst_y; +- res.num_parts++; +- } +- } +- +- bool r = false; +- if (format == SUBBITMAP_BGRA) { +- r = pack_rgba(p, &res); +- } else { +- r = pack_libass(p, &res); +- } +- +- if (!r) +- return; +- +- *out = res; +- p->cached_subs = res; +- p->cached_subs.change_id = 0; +- p->cached_subs_valid = true; +-} +- +-// Set *out_rc to [x0, y0, x1, y1] of the graphical bounding box in script +-// coordinates. +-// Set it to [inf, inf, -inf, -inf] if empty. +-void mp_ass_get_bb(ASS_Image *image_list, ASS_Track *track, +- struct mp_osd_res *res, double *out_rc) +-{ +- double rc[4] = {INFINITY, INFINITY, -INFINITY, -INFINITY}; +- +- for (ASS_Image *img = image_list; img; img = img->next) { +- if (img->w == 0 || img->h == 0) +- continue; +- rc[0] = MPMIN(rc[0], img->dst_x); +- rc[1] = MPMIN(rc[1], img->dst_y); +- rc[2] = MPMAX(rc[2], img->dst_x + img->w); +- rc[3] = MPMAX(rc[3], img->dst_y + img->h); +- } +- +- double scale = track->PlayResY / (double)MPMAX(res->h, 1); +- if (scale > 0) { +- for (int i = 0; i < 4; i++) +- out_rc[i] = rc[i] * scale; +- } +-} +diff --git a/sub/ass_mp.h b/sub/ass_mp.h +deleted file mode 100644 +index b4e16d6650..0000000000 +--- a/sub/ass_mp.h ++++ /dev/null +@@ -1,65 +0,0 @@ +-/* +- * Copyright (C) 2006 Evgeniy Stepanov +- * +- * This file is part of mpv. +- * +- * mpv is free software; you can redistribute it and/or +- * modify it under the terms of the GNU Lesser General Public +- * License as published by the Free Software Foundation; either +- * version 2.1 of the License, or (at your option) any later version. +- * +- * mpv is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU Lesser General Public License for more details. +- * +- * You should have received a copy of the GNU Lesser General Public +- * License along with mpv. If not, see . +- */ +- +-#ifndef MPLAYER_ASS_MP_H +-#define MPLAYER_ASS_MP_H +- +-#include +-#include +- +-#include +-#include +- +-// These PlayResX and PlayResY values are arbitrary and taken from lavc. +-// lavc assumes these values when converting to ass generally. Moreover, these +-// values are also used by default in VSFilter, so it isn't that arbitrary. +-#define MP_ASS_FONT_PLAYRESX 384 +-#define MP_ASS_FONT_PLAYRESY 288 +- +-#define MP_ASS_RGBA(r, g, b, a) \ +- (((unsigned)(r) << 24) | ((g) << 16) | ((b) << 8) | (0xFF - (a))) +- +-// m_color argument +-#define MP_ASS_COLOR(c) MP_ASS_RGBA((c).r, (c).g, (c).b, (c).a) +- +-struct MPOpts; +-struct mpv_global; +-struct mp_osd_res; +-struct osd_style_opts; +-struct mp_log; +- +-void mp_ass_flush_old_events(ASS_Track *track, long long ts); +-void mp_ass_set_style(ASS_Style *style, double res_y, +- const struct osd_style_opts *opts); +- +-void mp_ass_configure_fonts(ASS_Renderer *priv, struct osd_style_opts *opts, +- struct mpv_global *global, struct mp_log *log); +-ASS_Library *mp_ass_init(struct mpv_global *global, +- struct osd_style_opts *opts, struct mp_log *log); +- +-struct sub_bitmaps; +-struct mp_ass_packer; +-struct mp_ass_packer *mp_ass_packer_alloc(void *ta_parent); +-void mp_ass_packer_pack(struct mp_ass_packer *p, ASS_Image **image_lists, +- int num_image_lists, bool changed, bool video_color_space, +- int preferred_osd_format, struct sub_bitmaps *out); +-void mp_ass_get_bb(ASS_Image *image_list, ASS_Track *track, +- struct mp_osd_res *res, double *out_rc); +- +-#endif /* MPLAYER_ASS_MP_H */ +diff --git a/sub/dec_sub.c b/sub/dec_sub.c +index ce51fdf8c8..c0297ab590 100644 +--- a/sub/dec_sub.c ++++ b/sub/dec_sub.c +@@ -33,12 +33,10 @@ + #include "misc/dispatch.h" + #include "osdep/threads.h" + +-extern const struct sd_functions sd_ass; + extern const struct sd_functions sd_lavc; + + static const struct sd_functions *const sd_list[] = { + &sd_lavc, +- &sd_ass, + NULL + }; + +diff --git a/sub/osd_libass.c b/sub/osd_libass.c +deleted file mode 100644 +index 8ce203e66e..0000000000 +--- a/sub/osd_libass.c ++++ /dev/null +@@ -1,687 +0,0 @@ +-/* +- * This file is part of mpv. +- * +- * mpv is free software; you can redistribute it and/or +- * modify it under the terms of the GNU Lesser General Public +- * License as published by the Free Software Foundation; either +- * version 2.1 of the License, or (at your option) any later version. +- * +- * mpv is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU Lesser General Public License for more details. +- * +- * You should have received a copy of the GNU Lesser General Public +- * License along with mpv. If not, see . +- */ +- +-#include +-#include +-#include +-#include +-#include +- +-#include "mpv_talloc.h" +-#include "misc/bstr.h" +-#include "common/common.h" +-#include "common/msg.h" +-#include "osd.h" +-#include "osd_state.h" +- +-static const char osd_font_pfb[] = +-#include "sub/osd_font.otf.inc" +-; +- +-#include "sub/ass_mp.h" +-#include "options/options.h" +- +- +-#define ASS_USE_OSD_FONT "{\\fnmpv-osd-symbols}" +- +-static void append_ass(struct ass_state *ass, struct mp_osd_res *res, +- ASS_Image **img_list, bool *changed); +- +-static void create_ass_renderer(struct osd_state *osd, struct ass_state *ass) +-{ +- if (ass->render) +- return; +- +- ass->log = mp_log_new(NULL, osd->log, "libass"); +- ass->library = mp_ass_init(osd->global, osd->opts->osd_style, ass->log); +- ass_add_font(ass->library, "mpv-osd-symbols", (void *)osd_font_pfb, +- sizeof(osd_font_pfb) - 1); +- +- ass->render = ass_renderer_init(ass->library); +- if (!ass->render) +- abort(); +- +- mp_ass_configure_fonts(ass->render, osd->opts->osd_style, +- osd->global, ass->log); +- ass_set_pixel_aspect(ass->render, 1.0); +-} +- +-static void destroy_ass_renderer(struct ass_state *ass) +-{ +- if (ass->track) +- ass_free_track(ass->track); +- ass->track = NULL; +- if (ass->render) +- ass_renderer_done(ass->render); +- ass->render = NULL; +- if (ass->library) +- ass_library_done(ass->library); +- ass->library = NULL; +- talloc_free(ass->log); +- ass->log = NULL; +-} +- +-static void destroy_external(struct osd_external *ext) +-{ +- destroy_ass_renderer(&ext->ass); +- talloc_free(ext); +-} +- +-void osd_destroy_backend(struct osd_state *osd) +-{ +- for (int n = 0; n < MAX_OSD_PARTS; n++) { +- struct osd_object *obj = osd->objs[n]; +- destroy_ass_renderer(&obj->ass); +- for (int i = 0; i < obj->num_externals; i++) +- destroy_external(obj->externals[i]); +- obj->num_externals = 0; +- } +-} +- +-static void update_playres(struct ass_state *ass, struct mp_osd_res *vo_res) +-{ +- ASS_Track *track = ass->track; +- int old_res_x = track->PlayResX; +- int old_res_y = track->PlayResY; +- +- ass->vo_res = *vo_res; +- +- double aspect = 1.0 * vo_res->w / MPMAX(vo_res->h, 1); +- if (vo_res->display_par > 0) +- aspect = aspect / vo_res->display_par; +- +- track->PlayResY = ass->res_y ? ass->res_y : MP_ASS_FONT_PLAYRESY; +- track->PlayResX = ass->res_x ? ass->res_x : track->PlayResY * aspect; +- +- // Force libass to clear its internal cache - it doesn't check for +- // PlayRes changes itself. +- if (old_res_x != track->PlayResX || old_res_y != track->PlayResY) +- ass_set_frame_size(ass->render, 1, 1); +-} +- +-static void create_ass_track(struct osd_state *osd, struct osd_object *obj, +- struct ass_state *ass) +-{ +- create_ass_renderer(osd, ass); +- +- ASS_Track *track = ass->track; +- if (!track) +- track = ass->track = ass_new_track(ass->library); +- +- track->track_type = TRACK_TYPE_ASS; +- track->Timer = 100.; +- track->WrapStyle = 1; // end-of-line wrapping instead of smart wrapping +- track->Kerning = true; +- track->ScaledBorderAndShadow = true; +-#if LIBASS_VERSION >= 0x01600010 +- ass_track_set_feature(track, ASS_FEATURE_WRAP_UNICODE, 1); +-#endif +- update_playres(ass, &obj->vo_res); +-} +- +-static int find_style(ASS_Track *track, const char *name, int def) +-{ +- for (int n = 0; n < track->n_styles; n++) { +- if (track->styles[n].Name && strcmp(track->styles[n].Name, name) == 0) +- return n; +- } +- return def; +-} +- +-// Find a given style, or add it if it's missing. +-static ASS_Style *get_style(struct ass_state *ass, char *name) +-{ +- ASS_Track *track = ass->track; +- if (!track) +- return NULL; +- +- int sid = find_style(track, name, -1); +- if (sid >= 0) +- return &track->styles[sid]; +- +- sid = ass_alloc_style(track); +- ASS_Style *style = &track->styles[sid]; +- style->Name = strdup(name); +- // Set to neutral base direction, as opposed to VSFilter LTR default +- style->Encoding = -1; +- return style; +-} +- +-static ASS_Event *add_osd_ass_event(ASS_Track *track, const char *style, +- const char *text) +-{ +- int n = ass_alloc_event(track); +- ASS_Event *event = track->events + n; +- event->Start = 0; +- event->Duration = 100; +- event->Style = find_style(track, style, 0); +- event->ReadOrder = n; +- assert(event->Text == NULL); +- if (text) +- event->Text = strdup(text); +- return event; +-} +- +-static void clear_ass(struct ass_state *ass) +-{ +- if (ass->track) +- ass_flush_events(ass->track); +-} +- +-void osd_get_function_sym(char *buffer, size_t buffer_size, int osd_function) +-{ +- // 0xFF is never valid UTF-8, so we can use it to escape OSD symbols. +- // (Same trick as OSD_ASS_0/OSD_ASS_1.) +- snprintf(buffer, buffer_size, "\xFF%c", osd_function); +-} +- +-void osd_mangle_ass(bstr *dst, const char *in, bool replace_newlines) +-{ +- const char *start = in; +- bool escape_ass = true; +- while (*in) { +- // As used by osd_get_function_sym(). +- if (in[0] == '\xFF' && in[1]) { +- bstr_xappend(NULL, dst, bstr0(ASS_USE_OSD_FONT)); +- mp_append_utf8_bstr(NULL, dst, OSD_CODEPOINTS + in[1]); +- bstr_xappend(NULL, dst, bstr0("{\\r}")); +- in += 2; +- continue; +- } +- if (*in == OSD_ASS_0[0] || *in == OSD_ASS_1[0]) { +- escape_ass = *in == OSD_ASS_1[0]; +- in += 1; +- continue; +- } +- if (escape_ass && *in == '{') +- bstr_xappend(NULL, dst, bstr0("\\")); +- // Replace newlines with \N for escape-ass. This is necessary to apply +- // ASS tags past newlines and to preserve consecutive newlines with +- // osd-overlay because update_external() adds a ASS event per line. +- if (replace_newlines && *in == '\n') { +- bstr_xappend(NULL, dst, bstr0("\\N")); +- in += 1; +- continue; +- } +- // Libass will strip leading whitespace +- if (in[0] == ' ' && (in == start || in[-1] == '\n')) { +- bstr_xappend(NULL, dst, bstr0("\\h")); +- in += 1; +- continue; +- } +- bstr_xappend(NULL, dst, (bstr){(char *)in, 1}); +- // Break ASS escapes with U+2060 WORD JOINER +- if (escape_ass && *in == '\\') +- mp_append_utf8_bstr(NULL, dst, 0x2060); +- in++; +- } +-} +- +-static ASS_Event *add_osd_ass_event_escaped(ASS_Track *track, const char *style, +- const char *text) +-{ +- bstr buf = {0}; +- osd_mangle_ass(&buf, text, false); +- ASS_Event *e = add_osd_ass_event(track, style, buf.start); +- talloc_free(buf.start); +- return e; +-} +- +-static ASS_Style *prepare_osd_ass(struct osd_state *osd, struct osd_object *obj) +-{ +- struct mp_osd_render_opts *opts = osd->opts; +- +- create_ass_track(osd, obj, &obj->ass); +- +- struct osd_style_opts font = *opts->osd_style; +- font.font_size *= opts->osd_scale; +- +- double playresy = obj->ass.track->PlayResY; +- // Compensate for libass and mp_ass_set_style scaling the font etc. +- if (!opts->osd_scale_by_window) +- playresy *= 720.0 / obj->vo_res.h; +- +- ASS_Style *style = get_style(&obj->ass, "OSD"); +- mp_ass_set_style(style, playresy, &font); +- return style; +-} +- +-static void update_osd_text(struct osd_state *osd, struct osd_object *obj) +-{ +- +- if (!obj->text[0]) +- return; +- +- prepare_osd_ass(osd, obj); +- add_osd_ass_event_escaped(obj->ass.track, "OSD", obj->text); +-} +- +-void osd_get_text_size(struct osd_state *osd, int *out_screen_h, int *out_font_h) +-{ +- mp_mutex_lock(&osd->lock); +- struct osd_object *obj = osd->objs[OSDTYPE_OSD]; +- ASS_Style *style = prepare_osd_ass(osd, obj); +- *out_screen_h = obj->ass.track->PlayResY - style->MarginV; +- *out_font_h = style->FontSize; +- mp_mutex_unlock(&osd->lock); +-} +- +-// align: -1 .. +1 +-// frame: size of the containing area +-// obj: size of the object that should be positioned inside the area +-// margin: min. distance from object to frame (as long as -1 <= align <= +1) +-static float get_align(float align, float frame, float obj, float margin) +-{ +- frame -= margin * 2; +- return margin + frame / 2 - obj / 2 + (frame - obj) / 2 * align; +-} +- +-struct ass_draw { +- int scale; +- char *text; +-}; +- +-static void ass_draw_start(struct ass_draw *d) +-{ +- d->scale = MPMAX(d->scale, 1); +- d->text = talloc_asprintf_append(d->text, "{\\p%d}", d->scale); +-} +- +-static void ass_draw_stop(struct ass_draw *d) +-{ +- d->text = talloc_strdup_append(d->text, "{\\p0}"); +-} +- +-static void ass_draw_c(struct ass_draw *d, float x, float y) +-{ +- int ix = round(x * (1 << (d->scale - 1))); +- int iy = round(y * (1 << (d->scale - 1))); +- d->text = talloc_asprintf_append(d->text, " %d %d", ix, iy); +-} +- +-static void ass_draw_append(struct ass_draw *d, const char *t) +-{ +- d->text = talloc_strdup_append(d->text, t); +-} +- +-static void ass_draw_move_to(struct ass_draw *d, float x, float y) +-{ +- ass_draw_append(d, " m"); +- ass_draw_c(d, x, y); +-} +- +-static void ass_draw_line_to(struct ass_draw *d, float x, float y) +-{ +- ass_draw_append(d, " l"); +- ass_draw_c(d, x, y); +-} +- +-static void ass_draw_rect_ccw(struct ass_draw *d, float x0, float y0, +- float x1, float y1) +-{ +- ass_draw_move_to(d, x0, y0); +- ass_draw_line_to(d, x0, y1); +- ass_draw_line_to(d, x1, y1); +- ass_draw_line_to(d, x1, y0); +-} +- +-static void ass_draw_rect_cw(struct ass_draw *d, float x0, float y0, +- float x1, float y1) +-{ +- ass_draw_move_to(d, x0, y0); +- ass_draw_line_to(d, x1, y0); +- ass_draw_line_to(d, x1, y1); +- ass_draw_line_to(d, x0, y1); +-} +- +-static void ass_draw_reset(struct ass_draw *d) +-{ +- talloc_free(d->text); +- d->text = NULL; +-} +- +-static void get_osd_bar_box(struct osd_state *osd, struct osd_object *obj, +- float *o_x, float *o_y, float *o_w, float *o_h, +- float *o_border) +-{ +- struct mp_osd_render_opts *opts = osd->opts; +- +- create_ass_track(osd, obj, &obj->ass); +- ASS_Track *track = obj->ass.track; +- +- ASS_Style *style = get_style(&obj->ass, "progbar"); +- if (!style) { +- *o_x = *o_y = *o_w = *o_h = *o_border = 0; +- return; +- } +- +- mp_ass_set_style(style, track->PlayResY, opts->osd_style); +- +- // override the default osd opaque-box into plain outline. Otherwise +- // the opaque box is not aligned with the bar (even without shadow), +- // and each bar ass event gets its own opaque box - breaking the bar. +- style->BorderStyle = 1; // outline +- +- *o_w = track->PlayResX * (opts->osd_bar_w / 100.0); +- *o_h = track->PlayResY * (opts->osd_bar_h / 100.0); +- +- style->Outline = opts->osd_bar_outline_size; +- // Rendering with shadow is broken (because there's more than one shape) +- style->Shadow = 0; +- +- style->Alignment = 5; +- +- *o_border = style->Outline; +- +- *o_x = get_align(opts->osd_bar_align_x, track->PlayResX, *o_w, *o_border); +- *o_y = get_align(opts->osd_bar_align_y, track->PlayResY, *o_h, *o_border); +-} +- +-static void update_progbar(struct osd_state *osd, struct osd_object *obj) +-{ +- if (obj->progbar_state.type < 0) +- return; +- +- float px, py, width, height, border; +- get_osd_bar_box(osd, obj, &px, &py, &width, &height, &border); +- +- ASS_Track *track = obj->ass.track; +- +- float sx = px - border * 2 - height / 4; // includes additional spacing +- float sy = py + height / 2; +- +- bstr buf = bstr0(talloc_asprintf(NULL, "{\\an6\\pos(%f,%f)}", sx, sy)); +- +- if (obj->progbar_state.type == 0 || obj->progbar_state.type >= 256) { +- // no sym +- } else if (obj->progbar_state.type >= 32) { +- mp_append_utf8_bstr(NULL, &buf, obj->progbar_state.type); +- } else { +- bstr_xappend(NULL, &buf, bstr0(ASS_USE_OSD_FONT)); +- mp_append_utf8_bstr(NULL, &buf, OSD_CODEPOINTS + obj->progbar_state.type); +- bstr_xappend(NULL, &buf, bstr0("{\\r}")); +- } +- +- add_osd_ass_event(track, "progbar", buf.start); +- talloc_free(buf.start); +- +- struct ass_draw *d = &(struct ass_draw) { .scale = 4 }; +- +- if (osd->opts->osd_style->back_color.a && osd->opts->osd_style->border_style != 1) { +- // the bar style always ignores the --osd-back-color config - it messes +- // up the bar. draw an artificial box at the original back color. +- struct m_color bc = osd->opts->osd_style->back_color; +- d->text = talloc_asprintf_append(d->text, +- "{\\pos(%f,%f)\\bord0\\1a&H%02X\\1c&H%02X%02X%02X&}", +- px, py, 255 - bc.a, (int)bc.b, (int)bc.g, (int)bc.r); +- +- ass_draw_start(d); +- ass_draw_rect_cw(d, -border, -border, width + border, height + border); +- ass_draw_stop(d); +- add_osd_ass_event(track, "progbar", d->text); +- ass_draw_reset(d); +- } +- +- // filled area +- d->text = talloc_asprintf_append(d->text, "{\\bord0\\pos(%f,%f)}", px, py); +- ass_draw_start(d); +- float pos = obj->progbar_state.value * width - border / 2; +- ass_draw_rect_cw(d, 0, 0, pos, height); +- ass_draw_stop(d); +- add_osd_ass_event(track, "progbar", d->text); +- ass_draw_reset(d); +- +- // position marker +- d->text = talloc_asprintf_append(d->text, "{\\bord%f\\pos(%f,%f)}", +- border / 2, px, py); +- ass_draw_start(d); +- ass_draw_move_to(d, pos + border / 2, 0); +- ass_draw_line_to(d, pos + border / 2, height); +- ass_draw_stop(d); +- add_osd_ass_event(track, "progbar", d->text); +- ass_draw_reset(d); +- +- d->text = talloc_asprintf_append(d->text, "{\\pos(%f,%f)}", px, py); +- ass_draw_start(d); +- +- // the box +- ass_draw_rect_cw(d, -border, -border, width + border, height + border); +- +- // the "hole" +- ass_draw_rect_ccw(d, 0, 0, width, height); +- +- // chapter marks +- for (int n = 0; n < obj->progbar_state.num_stops; n++) { +- float s = obj->progbar_state.stops[n] * width; +- float dent = MPMAX(border * 1.3, 1.6); +- +- if (s > dent && s < width - dent) { +- ass_draw_move_to(d, s + dent, 0); +- ass_draw_line_to(d, s, dent); +- ass_draw_line_to(d, s - dent, 0); +- +- ass_draw_move_to(d, s - dent, height); +- ass_draw_line_to(d, s, height - dent); +- ass_draw_line_to(d, s + dent, height); +- } +- } +- +- ass_draw_stop(d); +- add_osd_ass_event(track, "progbar", d->text); +- ass_draw_reset(d); +-} +- +-static void update_osd(struct osd_state *osd, struct osd_object *obj) +-{ +- obj->osd_changed = false; +- clear_ass(&obj->ass); +- update_osd_text(osd, obj); +- update_progbar(osd, obj); +-} +- +-static void update_external(struct osd_state *osd, struct osd_object *obj, +- struct osd_external *ext) +-{ +- bstr t = bstr0(ext->ov.data); +- ext->ass.res_x = ext->ov.res_x; +- ext->ass.res_y = ext->ov.res_y; +- create_ass_track(osd, obj, &ext->ass); +- +- clear_ass(&ext->ass); +- +- int resy = ext->ass.track->PlayResY; +- mp_ass_set_style(get_style(&ext->ass, "OSD"), resy, osd->opts->osd_style); +- +- // Some scripts will reference this style name with \r tags. +- const struct osd_style_opts *def = osd_style_conf.defaults; +- mp_ass_set_style(get_style(&ext->ass, "Default"), resy, def); +- +- while (t.len) { +- bstr line; +- bstr_split_tok(t, "\n", &line, &t); +- if (line.len) { +- char *tmp = bstrdup0(NULL, line); +- add_osd_ass_event(ext->ass.track, "OSD", tmp); +- talloc_free(tmp); +- } +- } +-} +- +-static int cmp_zorder(const void *pa, const void *pb) +-{ +- const struct osd_external *a = *(struct osd_external **)pa; +- const struct osd_external *b = *(struct osd_external **)pb; +- return a->ov.z == b->ov.z ? 0 : (a->ov.z > b->ov.z ? 1 : -1); +-} +- +-void osd_set_external(struct osd_state *osd, struct osd_external_ass *ov) +-{ +- mp_mutex_lock(&osd->lock); +- struct osd_object *obj = osd->objs[OSDTYPE_EXTERNAL]; +- bool zorder_changed = false; +- int index = -1; +- +- for (int n = 0; n < obj->num_externals; n++) { +- struct osd_external *e = obj->externals[n]; +- if (e->ov.id == ov->id && e->ov.owner == ov->owner) { +- index = n; +- break; +- } +- } +- +- if (index < 0) { +- if (!ov->format) +- goto done; +- struct osd_external *new = talloc_zero(NULL, struct osd_external); +- new->ov.owner = ov->owner; +- new->ov.id = ov->id; +- MP_TARRAY_APPEND(obj, obj->externals, obj->num_externals, new); +- index = obj->num_externals - 1; +- zorder_changed = true; +- } +- +- struct osd_external *entry = obj->externals[index]; +- +- if (!ov->format) { +- if (!entry->ov.hidden) { +- obj->changed = true; +- osd->want_redraw_notification = true; +- } +- destroy_external(entry); +- MP_TARRAY_REMOVE_AT(obj->externals, obj->num_externals, index); +- goto done; +- } +- +- if (!entry->ov.hidden || !ov->hidden) { +- obj->changed = true; +- osd->want_redraw_notification = true; +- } +- +- entry->ov.format = ov->format; +- if (!entry->ov.data) +- entry->ov.data = talloc_strdup(entry, ""); +- entry->ov.data[0] = '\0'; // reuse memory allocation +- entry->ov.data = talloc_strdup_append(entry->ov.data, ov->data); +- entry->ov.res_x = ov->res_x; +- entry->ov.res_y = ov->res_y; +- zorder_changed |= entry->ov.z != ov->z; +- entry->ov.z = ov->z; +- entry->ov.hidden = ov->hidden; +- +- update_external(osd, obj, entry); +- +- if (zorder_changed) { +- qsort(obj->externals, obj->num_externals, sizeof(obj->externals[0]), +- cmp_zorder); +- } +- +- if (ov->out_rc) { +- struct mp_osd_res vo_res = entry->ass.vo_res; +- // Defined fallback if VO has not drawn this yet +- if (vo_res.w < 1 || vo_res.h < 1) { +- vo_res = (struct mp_osd_res){ +- .w = entry->ov.res_x, +- .h = entry->ov.res_y, +- .display_par = 1, +- }; +- // According to osd-overlay command description. +- if (vo_res.w < 1) +- vo_res.w = 1280; +- if (vo_res.h < 1) +- vo_res.h = 720; +- } +- +- ASS_Image *img_list = NULL; +- append_ass(&entry->ass, &vo_res, &img_list, NULL); +- +- mp_ass_get_bb(img_list, entry->ass.track, &vo_res, ov->out_rc); +- } +- +-done: +- mp_mutex_unlock(&osd->lock); +-} +- +-void osd_set_external_remove_owner(struct osd_state *osd, void *owner) +-{ +- mp_mutex_lock(&osd->lock); +- struct osd_object *obj = osd->objs[OSDTYPE_EXTERNAL]; +- for (int n = obj->num_externals - 1; n >= 0; n--) { +- struct osd_external *e = obj->externals[n]; +- if (e->ov.owner == owner) { +- destroy_external(e); +- MP_TARRAY_REMOVE_AT(obj->externals, obj->num_externals, n); +- obj->changed = true; +- osd->want_redraw_notification = true; +- } +- } +- mp_mutex_unlock(&osd->lock); +-} +- +-static void append_ass(struct ass_state *ass, struct mp_osd_res *res, +- ASS_Image **img_list, bool *changed) +-{ +- if (!ass->render || !ass->track) { +- *img_list = NULL; +- return; +- } +- +- update_playres(ass, res); +- +- ass_set_frame_size(ass->render, res->w, res->h); +- ass_set_pixel_aspect(ass->render, res->display_par); +- +- int ass_changed; +- *img_list = ass_render_frame(ass->render, ass->track, 0, &ass_changed); +- +- ass->changed |= ass_changed; +- +- if (changed) { +- *changed |= ass->changed; +- ass->changed = false; +- } +-} +- +-struct sub_bitmaps *osd_object_get_bitmaps(struct osd_state *osd, +- struct osd_object *obj, int format) +-{ +- if (obj->type == OSDTYPE_OSD && obj->osd_changed) +- update_osd(osd, obj); +- +- if (!obj->ass_packer) +- obj->ass_packer = mp_ass_packer_alloc(obj); +- +- MP_TARRAY_GROW(obj, obj->ass_imgs, obj->num_externals + 1); +- +- append_ass(&obj->ass, &obj->vo_res, &obj->ass_imgs[0], &obj->changed); +- for (int n = 0; n < obj->num_externals; n++) { +- if (obj->externals[n]->ov.hidden) { +- update_playres(&obj->externals[n]->ass, &obj->vo_res); +- obj->ass_imgs[n + 1] = NULL; +- } else { +- append_ass(&obj->externals[n]->ass, &obj->vo_res, +- &obj->ass_imgs[n + 1], &obj->changed); +- } +- } +- +- struct sub_bitmaps out_imgs = {0}; +- mp_ass_packer_pack(obj->ass_packer, obj->ass_imgs, obj->num_externals + 1, +- obj->changed, false, format, &out_imgs); +- +- obj->changed = false; +- +- return sub_bitmaps_copy(&obj->copy_cache, &out_imgs); +-} +diff --git a/sub/osd_state.h b/sub/osd_state.h +index ef0ff20a57..725568234f 100644 +--- a/sub/osd_state.h ++++ b/sub/osd_state.h +@@ -87,7 +87,7 @@ struct osd_state { + + // defined in osd_libass.c + struct sub_bitmaps *osd_object_get_bitmaps(struct osd_state *osd, +- struct osd_object *obj, int format); +-void osd_destroy_backend(struct osd_state *osd); ++ struct osd_object *obj, int format) { return NULL; } ++void osd_destroy_backend(struct osd_state *osd) {} + + #endif +diff --git a/sub/sd.h b/sub/sd.h +index cb80c2eb22..10cde787ba 100644 +--- a/sub/sd.h ++++ b/sub/sd.h +@@ -108,17 +108,27 @@ extern const struct sd_filter_functions sd_filter_jsre; + + // num commas to skip at an ass-event before the "Text" field (always last) + // (doesn't change, can be retrieved once on filter init) +-int sd_ass_fmt_offset(const char *event_format); ++inline int sd_ass_fmt_offset(const char *event_format) { return 0; } + + // the event (pkt->buffer) "Text" content according to the calculated offset. + // on malformed event: warns and returns (bstr){NULL,0} +-bstr sd_ass_pkt_text(struct sd_filter *ft, struct demux_packet *pkt, int offset); ++inline bstr sd_ass_pkt_text(struct sd_filter *ft, struct demux_packet *pkt, int offset) { ++ struct bstr r; ++ r.start = NULL; ++ r.len = 0; ++ return r; ++} + + // convert \0-terminated "Text" (ass) content to plaintext, possibly in-place. + // result.start is *out, result.len is strlen(in) or smaller. + // (*out)[result.len] is always set to \0. *out == in is allowed. + // *out must be a talloc-allocated buffer or NULL, and will be reallocated if needed. + // *out will not be reallocated if *out == in. +-bstr sd_ass_to_plaintext(char **out, const char *in); ++inline bstr sd_ass_to_plaintext(char **out, const char *in) { ++ struct bstr r; ++ r.start = NULL; ++ r.len = 0; ++ return r; ++} + + #endif +diff --git a/sub/sd_ass.c b/sub/sd_ass.c +deleted file mode 100644 +index 47b3109ec7..0000000000 +--- a/sub/sd_ass.c ++++ /dev/null +@@ -1,1174 +0,0 @@ +-/* +- * This file is part of mpv. +- * +- * mpv is free software; you can redistribute it and/or +- * modify it under the terms of the GNU Lesser General Public +- * License as published by the Free Software Foundation; either +- * version 2.1 of the License, or (at your option) any later version. +- * +- * mpv is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU Lesser General Public License for more details. +- * +- * You should have received a copy of the GNU Lesser General Public +- * License along with mpv. If not, see . +- */ +- +-#include +-#include +-#include +-#include +-#include +- +-#include +-#include +- +-#include "mpv_talloc.h" +- +-#include "config.h" +-#include "options/m_config.h" +-#include "options/options.h" +-#include "common/common.h" +-#include "common/msg.h" +-#include "demux/demux.h" +-#include "video/csputils.h" +-#include "video/mp_image.h" +-#include "dec_sub.h" +-#include "ass_mp.h" +-#include "sd.h" +- +-struct sd_ass_priv { +- struct ass_library *ass_library; +- struct ass_renderer *ass_renderer; +- struct ass_track *ass_track; +- struct ass_track *shadow_track; // for --sub-ass=no rendering +- bool ass_configured; +- bool is_converted; +- struct lavc_conv *converter; +- struct sd_filter **filters; +- int num_filters; +- bool clear_once; +- struct mp_ass_packer *packer; +- struct sub_bitmap_copy_cache *copy_cache; +- bstr last_text; +- struct mp_image_params video_params; +- struct mp_image_params last_params; +- struct mp_osd_res osd; +- struct seen_packet *seen_packets; +- int num_seen_packets; +- int *packets_animated; +- int num_packets_animated; +- bool check_animated; +- bool duration_unknown; +-}; +- +-struct seen_packet { +- int64_t pos; +- double pts; +-}; +- +-static void mangle_colors(struct sd *sd, struct sub_bitmaps *parts); +-static void fill_plaintext(struct sd *sd, double pts); +- +-static const struct sd_filter_functions *const filters[] = { +- // Note: list order defines filter order. +- &sd_filter_sdh, +-#if HAVE_POSIX +- &sd_filter_regex, +-#endif +-#if HAVE_JAVASCRIPT +- &sd_filter_jsre, +-#endif +- NULL, +-}; +- +-// Add default styles, if the track does not have any styles yet. +-// Apply style overrides if the user provides any. +-static void mp_ass_add_default_styles(ASS_Track *track, struct mp_subtitle_opts *opts, +- struct mp_subtitle_shared_opts *shared_opts, int order) +-{ +- if (opts->ass_styles_file && shared_opts->ass_style_override[order]) +- ass_read_styles(track, opts->ass_styles_file, NULL); +- +- if (track->n_styles == 0) { +- if (!track->PlayResY) { +- track->PlayResX = MP_ASS_FONT_PLAYRESX; +- track->PlayResY = MP_ASS_FONT_PLAYRESY; +- } +- track->Kerning = true; +- int sid = ass_alloc_style(track); +- track->default_style = sid; +- ASS_Style *style = track->styles + sid; +- style->Name = strdup("Default"); +- mp_ass_set_style(style, track->PlayResY, opts->sub_style); +- } +- +- if (shared_opts->ass_style_override[order]) +- ass_process_force_style(track); +-} +- +-static const char *const font_mimetypes[] = { +- "application/x-truetype-font", +- "application/vnd.ms-opentype", +- "application/x-font-ttf", +- "application/x-font", // probably incorrect +- "application/font-sfnt", +- "font/collection", +- "font/otf", +- "font/sfnt", +- "font/ttf", +- NULL +-}; +- +-static const char *const font_exts[] = {".ttf", ".ttc", ".otf", ".otc", NULL}; +- +-static bool attachment_is_font(struct mp_log *log, struct demux_attachment *f) +-{ +- if (!f->name || !f->type || !f->data || !f->data_size) +- return false; +- for (int n = 0; font_mimetypes[n]; n++) { +- if (strcmp(font_mimetypes[n], f->type) == 0) +- return true; +- } +- // fallback: match against file extension +- char *ext = strlen(f->name) > 4 ? f->name + strlen(f->name) - 4 : ""; +- for (int n = 0; font_exts[n]; n++) { +- if (strcasecmp(ext, font_exts[n]) == 0) { +- mp_warn(log, "Loading font attachment '%s' with MIME type %s. " +- "Assuming this is a broken Matroska file, which was " +- "muxed without setting a correct font MIME type.\n", +- f->name, f->type); +- return true; +- } +- } +- return false; +-} +- +-static void add_subtitle_fonts(struct sd *sd) +-{ +- struct sd_ass_priv *ctx = sd->priv; +- struct mp_subtitle_opts *opts = sd->opts; +- if (!opts->ass_enabled || !opts->use_embedded_fonts || !sd->attachments) +- return; +- for (int i = 0; i < sd->attachments->num_entries; i++) { +- struct demux_attachment *f = &sd->attachments->entries[i]; +- if (attachment_is_font(sd->log, f)) +- ass_add_font(ctx->ass_library, f->name, f->data, f->data_size); +- } +-} +- +-static void filters_destroy(struct sd *sd) +-{ +- struct sd_ass_priv *ctx = sd->priv; +- +- for (int n = 0; n < ctx->num_filters; n++) { +- struct sd_filter *ft = ctx->filters[n]; +- if (ft->driver->uninit) +- ft->driver->uninit(ft); +- talloc_free(ft); +- } +- ctx->num_filters = 0; +-} +- +-static void filters_init(struct sd *sd) +-{ +- struct sd_ass_priv *ctx = sd->priv; +- +- filters_destroy(sd); +- +- for (int n = 0; filters[n]; n++) { +- struct sd_filter *ft = talloc_ptrtype(ctx, ft); +- *ft = (struct sd_filter){ +- .global = sd->global, +- .log = sd->log, +- .opts = mp_get_config_group(ft, sd->global, &mp_sub_filter_opts), +- .driver = filters[n], +- .codec = "ass", +- .event_format = talloc_strdup(ft, ctx->ass_track->event_format), +- }; +- if (ft->driver->init(ft)) { +- MP_TARRAY_APPEND(ctx, ctx->filters, ctx->num_filters, ft); +- } else { +- talloc_free(ft); +- } +- } +-} +- +-static void enable_output(struct sd *sd, bool enable) +-{ +- struct sd_ass_priv *ctx = sd->priv; +- if (enable == !!ctx->ass_renderer) +- return; +- if (ctx->ass_renderer) { +- ass_renderer_done(ctx->ass_renderer); +- ctx->ass_renderer = NULL; +- } else { +- ctx->ass_renderer = ass_renderer_init(ctx->ass_library); +- +- mp_ass_configure_fonts(ctx->ass_renderer, sd->opts->sub_style, +- sd->global, sd->log); +- } +-} +- +-static void assobjects_init(struct sd *sd) +-{ +- struct sd_ass_priv *ctx = sd->priv; +- struct mp_subtitle_opts *opts = sd->opts; +- struct mp_subtitle_shared_opts *shared_opts = sd->shared_opts; +- +- ctx->ass_library = mp_ass_init(sd->global, sd->opts->sub_style, sd->log); +- ass_set_extract_fonts(ctx->ass_library, opts->use_embedded_fonts); +- +- add_subtitle_fonts(sd); +- +- if (shared_opts->ass_style_override[sd->order]) +- ass_set_style_overrides(ctx->ass_library, opts->ass_style_override_list); +- +- ctx->ass_track = ass_new_track(ctx->ass_library); +- ctx->ass_track->track_type = TRACK_TYPE_ASS; +- +- ctx->shadow_track = ass_new_track(ctx->ass_library); +- ctx->shadow_track->PlayResX = MP_ASS_FONT_PLAYRESX; +- ctx->shadow_track->PlayResY = MP_ASS_FONT_PLAYRESY; +- mp_ass_add_default_styles(ctx->shadow_track, opts, shared_opts, sd->order); +- +- char *extradata = sd->codec->extradata; +- int extradata_size = sd->codec->extradata_size; +- if (ctx->converter) { +- extradata = lavc_conv_get_extradata(ctx->converter); +- extradata_size = extradata ? strlen(extradata) : 0; +- } +- if (extradata) +- ass_process_codec_private(ctx->ass_track, extradata, extradata_size); +- +- mp_ass_add_default_styles(ctx->ass_track, opts, shared_opts, sd->order); +- +-#if LIBASS_VERSION >= 0x01302000 +- ass_set_check_readorder(ctx->ass_track, sd->opts->sub_clear_on_seek ? 0 : 1); +-#endif +- +- enable_output(sd, true); +-} +- +-static void assobjects_destroy(struct sd *sd) +-{ +- struct sd_ass_priv *ctx = sd->priv; +- +- ass_free_track(ctx->ass_track); +- ass_free_track(ctx->shadow_track); +- enable_output(sd, false); +- ass_library_done(ctx->ass_library); +-} +- +-static int init(struct sd *sd) +-{ +- struct sd_ass_priv *ctx = talloc_zero(sd, struct sd_ass_priv); +- sd->priv = ctx; +- +- // Note: accept "null" as alias for "ass", so EDL delay_open subtitle +- // streams work. +- if (strcmp(sd->codec->codec, "ass") != 0 && +- strcmp(sd->codec->codec, "null") != 0) +- { +- ctx->is_converted = true; +- ctx->converter = lavc_conv_create(sd); +- if (!ctx->converter) +- return -1; +- +- if (strcmp(sd->codec->codec, "eia_608") == 0) +- ctx->duration_unknown = 1; +- } +- +- assobjects_init(sd); +- filters_init(sd); +- +- ctx->packer = mp_ass_packer_alloc(ctx); +- +- // Subtitles does not have any profile value, so put the converted type as a profile. +- const char **desc = ctx->converter ? &sd->codec->codec_profile : &sd->codec->codec_desc; +- switch (ctx->ass_track->track_type) { +- case TRACK_TYPE_ASS: +- *desc = "Advanced Sub Station Alpha"; +- break; +- case TRACK_TYPE_SSA: +- *desc = "Sub Station Alpha"; +- break; +- } +- +- return 0; +-} +- +-// Check if subtitle has events that would cause it to be animated inside {} +-static bool is_animated(const char *str) +-{ +- const char *begin = str; +- while ((str = strchr(str, '{'))) { +- if (str++ > begin && str[-2] == '\\') +- continue; +- +- const char *end = strchr(str, '}'); +- if (!end) +- return false; +- +- while ((str = memchr(str, '\\', end - str))) { +- while (str[0] == '\\') +- ++str; +- while (str[0] == ' ' || str[0] == '\t') +- ++str; +- if (str[0] == 'k' || str[0] == 'K' || str[0] == 't' || +- (str[0] == 'f' && str[1] == 'a' && str[2] == 'd') || +- (str[0] == 'm' && str[1] == 'o' && str[2] == 'v' && str[3] == 'e')) +- { +- return true; +- } +- } +- +- str = end + 1; +- } +- +- return false; +-} +- +-// Note: pkt is not necessarily a fully valid refcounted packet. +-static void filter_and_add(struct sd *sd, struct demux_packet *pkt) +-{ +- struct sd_ass_priv *ctx = sd->priv; +- struct demux_packet *orig_pkt = pkt; +- ASS_Track *track = ctx->ass_track; +- int old_n_events = track->n_events; +- +- for (int n = 0; n < ctx->num_filters; n++) { +- struct sd_filter *ft = ctx->filters[n]; +- struct demux_packet *npkt = ft->driver->filter(ft, pkt); +- if (pkt != npkt && pkt != orig_pkt) +- talloc_free(pkt); +- pkt = npkt; +- if (!pkt) +- return; +- } +- +- ass_process_chunk(ctx->ass_track, pkt->buffer, pkt->len, +- llrint(pkt->pts * 1000), +- llrint(pkt->duration * 1000)); +- +- // This bookkeeping only has any practical use for ASS subs +- // over a VO with no video. +- if (!ctx->is_converted) { +- if (!pkt->seen) { +- for (int n = track->n_events - 1; n >= 0; n--) { +- if (n + 1 == old_n_events || pkt->animated == 1) +- break; +- ASS_Event *event = &track->events[n]; +- // Might as well mark pkt->animated here with effects if we can. +- pkt->animated = (event->Effect && event->Effect[0]) ? 1 : -1; +- if (ctx->check_animated && pkt->animated != 1) +- pkt->animated = is_animated(event->Text); +- } +- MP_TARRAY_APPEND(ctx, ctx->packets_animated, ctx->num_packets_animated, pkt->animated); +- } else { +- if (ctx->check_animated && ctx->packets_animated[pkt->seen_pos] == -1) { +- for (int n = track->n_events - 1; n >= 0; n--) { +- if (n + 1 == old_n_events || pkt->animated == 1) +- break; +- ASS_Event *event = &track->events[n]; +- ctx->packets_animated[pkt->seen_pos] = is_animated(event->Text); +- pkt->animated = ctx->packets_animated[pkt->seen_pos]; +- } +- } else { +- pkt->animated = ctx->packets_animated[pkt->seen_pos]; +- } +- } +- } +- +- if (pkt != orig_pkt) +- talloc_free(pkt); +-} +- +-// Test if the packet with the given file position and pts was already consumed. +-// Return false if the packet is new (and add it to the internal list), and +-// return true if it was already seen. +-static bool check_packet_seen(struct sd *sd, struct demux_packet *packet) +-{ +- struct sd_ass_priv *priv = sd->priv; +- int a = 0; +- int b = priv->num_seen_packets; +- while (a < b) { +- int mid = a + (b - a) / 2; +- struct seen_packet *seen_packet = &priv->seen_packets[mid]; +- if (packet->pos == seen_packet->pos && packet->pts == seen_packet->pts) { +- packet->seen_pos = mid; +- return true; +- } +- if (packet->pos > seen_packet->pos || +- (packet->pos == seen_packet->pos && packet->pts > seen_packet->pts)) { +- a = mid + 1; +- } else { +- b = mid; +- } +- } +- packet->seen_pos = a; +- MP_TARRAY_INSERT_AT(priv, priv->seen_packets, priv->num_seen_packets, a, +- (struct seen_packet){packet->pos, packet->pts}); +- return false; +-} +- +-#define UNKNOWN_DURATION (INT_MAX / 1000) +- +-static void decode(struct sd *sd, struct demux_packet *packet) +-{ +- struct sd_ass_priv *ctx = sd->priv; +- ASS_Track *track = ctx->ass_track; +- +- packet->sub_duration = packet->duration; +- +- if (ctx->converter) { +- if (!sd->opts->sub_clear_on_seek && packet->pos >= 0 && +- check_packet_seen(sd, packet)) +- return; +- +- double sub_pts = 0; +- double sub_duration = 0; +- char **r = lavc_conv_decode(ctx->converter, packet, &sub_pts, +- &sub_duration); +- if (sd->opts->sub_stretch_durations || +- packet->duration < 0 || sub_duration == UINT32_MAX) { +- if (!ctx->duration_unknown) { +- MP_VERBOSE(sd, "Subtitle with unknown duration.\n"); +- ctx->duration_unknown = true; +- } +- sub_duration = UNKNOWN_DURATION; +- } +- +- for (int n = 0; r && r[n]; n++) { +- struct demux_packet pkt2 = { +- .pts = sub_pts, +- .duration = sub_duration, +- .buffer = r[n], +- .len = strlen(r[n]), +- }; +- filter_and_add(sd, &pkt2); +- } +- if (ctx->duration_unknown) { +- for (int n = track->n_events - 2; n >= 0; n--) { +- if (track->events[n].Duration == UNKNOWN_DURATION * 1000) { +- if (track->events[n].Start != track->events[n + 1].Start) { +- track->events[n].Duration = track->events[n + 1].Start - +- track->events[n].Start; +- } else { +- track->events[n].Duration = track->events[n + 1].Duration; +- } +- } +- } +- } +- } else { +- // Note that for this packet format, libass has an internal mechanism +- // for discarding duplicate (already seen) packets but we check this +- // anyways for our purposes for ASS subtitles. +- packet->seen = check_packet_seen(sd, packet); +- filter_and_add(sd, packet); +- } +-} +- +-// Calculate the height used for scaling subtitle text size so --sub-scale-with-window +-// can undo this scale and use frame size instead. The algorithm used is the following: +-// - If use_margins is disabled, the text is scaled with the visual size of the video. +-// - If use_margins is enabled, the text is scaled with the size of the video +-// as if the video is resized to "fit" the size of the frame. +-static float get_libass_scale_height(struct mp_osd_res *dim, bool use_margins) +-{ +- float vidw = dim->w - (dim->ml + dim->mr); +- float vidh = dim->h - (dim->mt + dim->mb); +- if (!use_margins || vidw < 1.0) +- return vidh; +- else +- return MPMIN(dim->h, dim->w / vidw * vidh); +-} +- +-static void configure_ass(struct sd *sd, struct mp_osd_res *dim, +- bool converted, ASS_Track *track) +-{ +- struct mp_subtitle_opts *opts = sd->opts; +- struct mp_subtitle_shared_opts *shared_opts = sd->shared_opts; +- struct sd_ass_priv *ctx = sd->priv; +- ASS_Renderer *priv = ctx->ass_renderer; +- +- ass_set_frame_size(priv, dim->w, dim->h); +- ass_set_margins(priv, dim->mt, dim->mb, dim->ml, dim->mr); +- +- bool set_use_margins = false; +- float set_sub_pos = 0.0f; +- float set_line_spacing = 0; +- float set_font_scale = 1; +- int set_hinting = 0; +- bool set_scale_with_window = false; +- bool set_scale_by_window = true; +- bool total_override = false; +- // With forced overrides, apply the --sub-* specific options +- if (converted || shared_opts->ass_style_override[sd->order] == ASS_STYLE_OVERRIDE_FORCE) { +- set_scale_with_window = opts->sub_scale_with_window; +- set_use_margins = opts->sub_use_margins; +- set_scale_by_window = opts->sub_scale_by_window; +- total_override = true; +- } else { +- set_scale_with_window = opts->ass_scale_with_window; +- set_use_margins = opts->ass_use_margins; +- } +- if (converted || shared_opts->ass_style_override[sd->order]) { +- set_sub_pos = 100.0f - shared_opts->sub_pos[sd->order]; +- set_line_spacing = opts->ass_line_spacing; +- set_hinting = opts->ass_hinting; +- } +- if (total_override || shared_opts->ass_style_override[sd->order] == ASS_STYLE_OVERRIDE_SCALE) { +- set_font_scale = opts->sub_scale; +- } +- if (set_scale_with_window) { +- set_font_scale *= dim->h / MPMAX(get_libass_scale_height(dim, set_use_margins), 1); +- } +- if (!set_scale_by_window) { +- double factor = dim->h / 720.0; +- if (factor != 0.0) +- set_font_scale /= factor; +- } +- ass_set_use_margins(priv, set_use_margins); +- ass_set_line_position(priv, set_sub_pos); +- ass_set_shaper(priv, opts->ass_shaper); +- int set_force_flags = 0; +- if (total_override) { +- set_force_flags |= ASS_OVERRIDE_BIT_FONT_NAME +- | ASS_OVERRIDE_BIT_FONT_SIZE_FIELDS +- | ASS_OVERRIDE_BIT_COLORS +- | ASS_OVERRIDE_BIT_BORDER +- | ASS_OVERRIDE_BIT_SELECTIVE_FONT_SCALE; +- } +- if (shared_opts->ass_style_override[sd->order] == ASS_STYLE_OVERRIDE_SCALE) +- set_force_flags |= ASS_OVERRIDE_BIT_SELECTIVE_FONT_SCALE; +- if (converted) +- set_force_flags |= ASS_OVERRIDE_BIT_ALIGNMENT; +-#if LIBASS_VERSION >= 0x01306000 +- if ((converted || shared_opts->ass_style_override[sd->order]) && opts->ass_justify) +- set_force_flags |= ASS_OVERRIDE_BIT_JUSTIFY; +-#endif +- ass_set_selective_style_override_enabled(priv, set_force_flags); +- ASS_Style style = {0}; +- mp_ass_set_style(&style, MP_ASS_FONT_PLAYRESY, opts->sub_style); +- ass_set_selective_style_override(priv, &style); +- free(style.FontName); +- if (converted && track->default_style < track->n_styles) { +- mp_ass_set_style(track->styles + track->default_style, +- track->PlayResY, opts->sub_style); +- } +- ass_set_font_scale(priv, set_font_scale); +- ass_set_hinting(priv, set_hinting); +- ass_set_line_spacing(priv, set_line_spacing); +-#if LIBASS_VERSION >= 0x01600010 +- if (converted) { +- ass_track_set_feature(track, ASS_FEATURE_WRAP_UNICODE, 1); +- if (!opts->sub_vsfilter_bidi_compat) { +- for (int n = 0; n < track->n_styles; n++) { +- track->styles[n].Encoding = -1; +- } +- ass_track_set_feature(track, ASS_FEATURE_BIDI_BRACKETS, 1); +- ass_track_set_feature(track, ASS_FEATURE_WHOLE_TEXT_LAYOUT, 1); +- } +- } +-#endif +- if (converted) { +- bool override_playres = true; +- char **ass_style_override_list = opts->ass_style_override_list; +- for (int i = 0; ass_style_override_list && ass_style_override_list[i]; i++) { +- if (bstr_find0(bstr0(ass_style_override_list[i]), "PlayResX") >= 0) +- override_playres = false; +- } +- +- // srt to ass conversion from ffmpeg has fixed PlayResX of 384 with an +- // aspect of 4:3. Starting with libass f08f8ea5 (pre 0.17) PlayResX +- // affects shadow and border widths, among others, so to render borders +- // and shadows correctly, we adjust PlayResX according to the DAR. +- // But PlayResX also affects margins, so we adjust those too. +- // This should ensure basic srt-to-ass ffmpeg conversion has correct +- // borders, but there could be other issues with some srt extensions +- // and/or different source formats which would be exposed over time. +- // Make these adjustments only if the user didn't set PlayResX. +- if (override_playres) { +- int vidw = dim->w - (dim->ml + dim->mr); +- int vidh = dim->h - (dim->mt + dim->mb); +- int old_playresx = track->PlayResX; +- track->PlayResX = track->PlayResY * (double)vidw / MPMAX(vidh, 1); +- double fix_margins = track->PlayResX / (double)old_playresx; +- for (int n = 0; n < track->n_styles; n++) { +- track->styles[n].MarginL = lrint(track->styles[n].MarginL * fix_margins); +- track->styles[n].MarginR = lrint(track->styles[n].MarginR * fix_margins); +- track->styles[n].MarginV = lrint(track->styles[n].MarginV * set_font_scale); +- } +- } +- } +-} +- +-static bool has_overrides(char *s) +-{ +- if (!s) +- return false; +- return strstr(s, "\\pos") || strstr(s, "\\move") || strstr(s, "\\clip") || +- strstr(s, "\\iclip") || strstr(s, "\\org") || strstr(s, "\\p"); +-} +- +-#define END(ev) ((ev)->Start + (ev)->Duration) +- +-static long long find_timestamp(struct sd *sd, double pts) +-{ +- struct sd_ass_priv *priv = sd->priv; +- if (pts == MP_NOPTS_VALUE) +- return 0; +- +- long long ts = llrint(pts * 1000); +- +- if (!sd->opts->sub_fix_timing || +- sd->shared_opts->ass_style_override[sd->order] == ASS_STYLE_OVERRIDE_NONE) +- return ts; +- +- // Try to fix small gaps and overlaps. +- ASS_Track *track = priv->ass_track; +- int threshold = SUB_GAP_THRESHOLD * 1000; +- int keep = SUB_GAP_KEEP * 1000; +- +- // Find the "current" event. +- ASS_Event *ev[2] = {0}; +- int n_ev = 0; +- for (int n = 0; n < track->n_events; n++) { +- ASS_Event *event = &track->events[n]; +- if (ts >= event->Start - threshold && ts <= END(event) + threshold) { +- if (n_ev >= MP_ARRAY_SIZE(ev)) +- return ts; // multiple overlaps - give up (probably complex subs) +- ev[n_ev++] = event; +- } +- } +- +- if (n_ev != 2) +- return ts; +- +- // Simple/minor heuristic against destroying typesetting. +- if (ev[0]->Style != ev[1]->Style || has_overrides(ev[0]->Text) || +- has_overrides(ev[1]->Text)) +- return ts; +- +- // Sort by start timestamps. +- if (ev[0]->Start > ev[1]->Start) +- MPSWAP(ASS_Event*, ev[0], ev[1]); +- +- // We want to fix partial overlaps only. +- if (END(ev[0]) >= END(ev[1])) +- return ts; +- +- if (ev[0]->Duration < keep || ev[1]->Duration < keep) +- return ts; +- +- // Gap between the events -> move ts to show the end of the first event. +- if (ts >= END(ev[0]) && ts < ev[1]->Start && END(ev[0]) < ev[1]->Start && +- END(ev[0]) + threshold >= ev[1]->Start) +- return END(ev[0]) - 1; +- +- // Overlap -> move ts to the (exclusive) end of the first event. +- // Relies on the fact that the ASS_Renderer has no overlap registered, even +- // if there is one. This happens to work because we never render the +- // overlapped state, and libass never resolves a collision. +- if (ts >= ev[1]->Start && ts <= END(ev[0]) && END(ev[0]) > ev[1]->Start && +- END(ev[0]) <= ev[1]->Start + threshold) +- return END(ev[0]); +- +- return ts; +-} +- +-#undef END +- +-static struct sub_bitmaps *get_bitmaps(struct sd *sd, struct mp_osd_res dim, +- int format, double pts) +-{ +- struct sd_ass_priv *ctx = sd->priv; +- struct mp_subtitle_opts *opts = sd->opts; +- struct mp_subtitle_shared_opts *shared_opts = sd->shared_opts; +- bool no_ass = !opts->ass_enabled || +- shared_opts->ass_style_override[sd->order] == ASS_STYLE_OVERRIDE_STRIP; +- bool converted = (ctx->is_converted && !lavc_conv_is_styled(ctx->converter)) || no_ass; +- ASS_Track *track = no_ass ? ctx->shadow_track : ctx->ass_track; +- ASS_Renderer *renderer = ctx->ass_renderer; +- struct sub_bitmaps *res = &(struct sub_bitmaps){0}; +- +- // Always update the osd_res +- struct mp_osd_res old_osd = ctx->osd; +- ctx->osd = dim; +- +- if (pts == MP_NOPTS_VALUE || !renderer) +- goto done; +- +- // Currently no supported text sub formats support a distinction between forced +- // and unforced lines, so we just assume everything's unforced and discard everything. +- // If we ever see a format that makes this distinction, we can add support here. +- if (opts->sub_forced_events_only) +- goto done; +- +- double scale = dim.display_par; +- if (!converted && (!shared_opts->ass_style_override[sd->order] || +- opts->ass_use_video_data >= 1)) +- { +- // Let's factor in video PAR for vsfilter compatibility: +- double par = opts->ass_video_aspect > 0 ? +- opts->ass_video_aspect : +- ctx->video_params.p_w / (double)ctx->video_params.p_h; +- if (isnormal(par)) +- scale *= par; +- } +- if (!ctx->ass_configured || !osd_res_equals(old_osd, ctx->osd)) { +- configure_ass(sd, &dim, converted, track); +- ctx->ass_configured = true; +- } +- ass_set_pixel_aspect(renderer, scale); +- if (!converted && (!shared_opts->ass_style_override[sd->order] || +- opts->ass_use_video_data >= 2)) +- { +- ass_set_storage_size(renderer, ctx->video_params.w, ctx->video_params.h); +- } else { +- ass_set_storage_size(renderer, 0, 0); +- } +- long long ts = find_timestamp(sd, pts); +- +- if (no_ass) +- fill_plaintext(sd, pts); +- +- int changed; +- ASS_Image *imgs = ass_render_frame(renderer, track, ts, &changed); +- mp_ass_packer_pack(ctx->packer, &imgs, 1, changed, !converted, format, res); +- +-done: +- // mangle_colors() modifies the color field, so copy the thing _before_. +- res = sub_bitmaps_copy(&ctx->copy_cache, res); +- +- if (!converted && res) +- mangle_colors(sd, res); +- +- return res; +-} +- +-#define MAX_BUF_SIZE 1024 * 1024 +-#define MIN_EXPAND_SIZE 4096 +- +-static void append(bstr *b, char c) +-{ +- bstr_xappend(NULL, b, (bstr){&c, 1}); +-} +- +-static void ass_to_plaintext(bstr *b, const char *in) +-{ +- const char *open_tag_pos = NULL; +- bool in_drawing = false; +- while (*in) { +- if (open_tag_pos) { +- if (in[0] == '}') { +- in += 1; +- open_tag_pos = NULL; +- } else if (in[0] == '\\' && in[1] == 'p' && in[2] != 'o') { +- in += 2; +- // Skip text between \pN and \p0 tags. A \p without a number +- // is the same as \p0, and leading 0s are also allowed. +- in_drawing = false; +- while (in[0] >= '0' && in[0] <= '9') { +- if (in[0] != '0') +- in_drawing = true; +- in += 1; +- } +- } else { +- in += 1; +- } +- } else { +- if (in[0] == '\\' && (in[1] == 'N' || in[1] == 'n')) { +- in += 2; +- append(b, '\n'); +- } else if (in[0] == '\\' && in[1] == 'h') { +- in += 2; +- append(b, ' '); +- } else if (in[0] == '{') { +- open_tag_pos = in; +- in += 1; +- } else { +- if (!in_drawing) +- append(b, in[0]); +- in += 1; +- } +- } +- } +- // A '{' without a closing '}' is always visible. +- if (open_tag_pos) { +- bstr_xappend(NULL, b, bstr0(open_tag_pos)); +- } +-} +- +-// Empty string counts as whitespace. +-static bool is_whitespace_only(bstr b) +-{ +- for (int n = 0; n < b.len; n++) { +- if (b.start[n] != ' ' && b.start[n] != '\t') +- return false; +- } +- return true; +-} +- +-static bstr get_text_buf(struct sd *sd, double pts, enum sd_text_type type) +-{ +- struct sd_ass_priv *ctx = sd->priv; +- ASS_Track *track = ctx->ass_track; +- +- if (pts == MP_NOPTS_VALUE) +- return (bstr){0}; +- long long ipts = find_timestamp(sd, pts); +- +- bstr *b = &ctx->last_text; +- +- if (!b->start) +- b->start = talloc_size(ctx, 4096); +- +- b->len = 0; +- +- for (int i = 0; i < track->n_events; ++i) { +- ASS_Event *event = track->events + i; +- if (ipts >= event->Start && ipts < event->Start + event->Duration) { +- if (event->Text) { +- int start = b->len; +- if (type == SD_TEXT_TYPE_PLAIN) { +- ass_to_plaintext(b, event->Text); +- } else if (type == SD_TEXT_TYPE_ASS_FULL) { +- long long s = event->Start; +- long long e = s + event->Duration; +- +- ASS_Style *style = (event->Style < 0 || event->Style >= track->n_styles) ? NULL : &track->styles[event->Style]; +- +- int sh = (s / 60 / 60 / 1000); +- int sm = (s / 60 / 1000) % 60; +- int ss = (s / 1000) % 60; +- int sc = (s / 10) % 100; +- int eh = (e / 60 / 60 / 1000); +- int em = (e / 60 / 1000) % 60; +- int es = (e / 1000) % 60; +- int ec = (e / 10) % 100; +- +- bstr_xappend_asprintf(NULL, b, "Dialogue: %d,%d:%02d:%02d.%02d,%d:%02d:%02d.%02d,%s,%s,%04d,%04d,%04d,%s,%s", +- event->Layer, +- sh, sm, ss, sc, +- eh, em, es, ec, +- (style && style->Name) ? style->Name : "", event->Name, +- event->MarginL, event->MarginR, event->MarginV, +- event->Effect, event->Text); +- } else { +- bstr_xappend(NULL, b, bstr0(event->Text)); +- } +- if (is_whitespace_only(bstr_cut(*b, start))) { +- b->len = start; +- } else { +- append(b, '\n'); +- } +- } +- } +- } +- +- bstr_eatend(b, (bstr)bstr0_lit("\n")); +- +- return *b; +-} +- +-static char *get_text(struct sd *sd, double pts, enum sd_text_type type) +-{ +- return bstrto0(NULL, get_text_buf(sd, pts, type)); +-} +- +-static struct sd_times get_times(struct sd *sd, double pts) +-{ +- struct sd_ass_priv *ctx = sd->priv; +- ASS_Track *track = ctx->ass_track; +- struct sd_times res = { .start = MP_NOPTS_VALUE, .end = MP_NOPTS_VALUE }; +- +- if (pts == MP_NOPTS_VALUE) +- return res; +- +- long long ipts = find_timestamp(sd, pts); +- +- for (int i = 0; i < track->n_events; ++i) { +- ASS_Event *event = track->events + i; +- if (ipts >= event->Start && ipts < event->Start + event->Duration) { +- double start = event->Start / 1000.0; +- double end = event->Duration == UNKNOWN_DURATION ? +- MP_NOPTS_VALUE : (event->Start + event->Duration) / 1000.0; +- +- if (res.start == MP_NOPTS_VALUE || res.start > start) +- res.start = start; +- +- if (res.end == MP_NOPTS_VALUE || res.end < end) +- res.end = end; +- } +- } +- +- return res; +-} +- +-static void fill_plaintext(struct sd *sd, double pts) +-{ +- struct sd_ass_priv *ctx = sd->priv; +- ASS_Track *track = ctx->shadow_track; +- +- ass_flush_events(track); +- +- bstr text = get_text_buf(sd, pts, SD_TEXT_TYPE_PLAIN); +- if (!text.len) +- return; +- +- bstr dst = {0}; +- +- while (text.len) { +- if (*text.start == '{') { +- bstr_xappend(NULL, &dst, bstr0("\\{")); +- text = bstr_cut(text, 1); +- } else if (*text.start == '\\') { +- bstr_xappend(NULL, &dst, bstr0("\\")); +- // Break ASS escapes with U+2060 WORD JOINER +- mp_append_utf8_bstr(NULL, &dst, 0x2060); +- text = bstr_cut(text, 1); +- } +- +- int i = bstrcspn(text, "{\\"); +- bstr_xappend(NULL, &dst, (bstr){text.start, i}); +- text = bstr_cut(text, i); +- } +- +- if (!dst.start) +- return; +- +- int n = ass_alloc_event(track); +- ASS_Event *event = track->events + n; +- event->Start = 0; +- event->Duration = INT_MAX; +- event->Style = track->default_style; +- event->Text = strdup(dst.start); +- +- talloc_free(dst.start); +-} +- +-static void reset(struct sd *sd) +-{ +- struct sd_ass_priv *ctx = sd->priv; +- if (sd->opts->sub_clear_on_seek || ctx->clear_once) { +- ass_flush_events(ctx->ass_track); +- ctx->num_seen_packets = 0; +- sd->preload_ok = false; +- ctx->clear_once = false; +- } +- if (ctx->converter) +- lavc_conv_reset(ctx->converter); +-} +- +-static void uninit(struct sd *sd) +-{ +- struct sd_ass_priv *ctx = sd->priv; +- +- filters_destroy(sd); +- if (ctx->converter) +- lavc_conv_uninit(ctx->converter); +- assobjects_destroy(sd); +- talloc_free(ctx->copy_cache); +-} +- +-static int control(struct sd *sd, enum sd_ctrl cmd, void *arg) +-{ +- struct sd_ass_priv *ctx = sd->priv; +- switch (cmd) { +- case SD_CTRL_SUB_STEP: { +- double *a = arg; +- long long ts = llrint(a[0] * 1000.0); +- long long res = ass_step_sub(ctx->ass_track, ts, a[1]); +- if (!res) +- return false; +- // Try to account for overlapping durations +- a[0] += res / 1000.0 + SUB_SEEK_OFFSET; +- return true; +- } +- case SD_CTRL_SET_ANIMATED_CHECK: +- ctx->check_animated = *(bool *)arg; +- return CONTROL_OK; +- case SD_CTRL_SET_VIDEO_PARAMS: +- ctx->video_params = *(struct mp_image_params *)arg; +- return CONTROL_OK; +- case SD_CTRL_UPDATE_OPTS: { +- int flags = (uintptr_t)arg; +- if (flags & UPDATE_SUB_FILT) { +- filters_destroy(sd); +- filters_init(sd); +- ctx->clear_once = true; // allow reloading on seeks +- reset(sd); +- } +- if (flags & UPDATE_SUB_HARD) { +- // ass_track will be recreated, so clear duplicate cache +- ctx->clear_once = true; +- reset(sd); +- assobjects_destroy(sd); +- assobjects_init(sd); +- } +- ctx->ass_configured = false; // ass always needs to be reconfigured +- return CONTROL_OK; +- } +- default: +- return CONTROL_UNKNOWN; +- } +-} +- +-const struct sd_functions sd_ass = { +- .name = "ass", +- .accept_packets_in_advance = true, +- .init = init, +- .decode = decode, +- .get_bitmaps = get_bitmaps, +- .get_text = get_text, +- .get_times = get_times, +- .control = control, +- .reset = reset, +- .select = enable_output, +- .uninit = uninit, +-}; +- +-// Disgusting hack for (xy-)vsfilter color compatibility. +-static void mangle_colors(struct sd *sd, struct sub_bitmaps *parts) +-{ +- struct mp_subtitle_opts *opts = sd->opts; +- struct sd_ass_priv *ctx = sd->priv; +- enum pl_color_system csp = 0; +- enum pl_color_levels levels = 0; +- if (opts->ass_vsfilter_color_compat == 0) // "no" +- return; +- bool force_601 = opts->ass_vsfilter_color_compat == 3; +- ASS_Track *track = ctx->ass_track; +- static const int ass_csp[] = { +- [YCBCR_BT601_TV] = PL_COLOR_SYSTEM_BT_601, +- [YCBCR_BT601_PC] = PL_COLOR_SYSTEM_BT_601, +- [YCBCR_BT709_TV] = PL_COLOR_SYSTEM_BT_709, +- [YCBCR_BT709_PC] = PL_COLOR_SYSTEM_BT_709, +- [YCBCR_SMPTE240M_TV] = PL_COLOR_SYSTEM_SMPTE_240M, +- [YCBCR_SMPTE240M_PC] = PL_COLOR_SYSTEM_SMPTE_240M, +- }; +- static const int ass_levels[] = { +- [YCBCR_BT601_TV] = PL_COLOR_LEVELS_LIMITED, +- [YCBCR_BT601_PC] = PL_COLOR_LEVELS_FULL, +- [YCBCR_BT709_TV] = PL_COLOR_LEVELS_LIMITED, +- [YCBCR_BT709_PC] = PL_COLOR_LEVELS_FULL, +- [YCBCR_SMPTE240M_TV] = PL_COLOR_LEVELS_LIMITED, +- [YCBCR_SMPTE240M_PC] = PL_COLOR_LEVELS_FULL, +- }; +- int trackcsp = track->YCbCrMatrix; +- if (force_601) +- trackcsp = YCBCR_BT601_TV; +- // NONE is a bit random, but the intention is: don't modify colors. +- if (trackcsp == YCBCR_NONE) +- return; +- if (trackcsp < sizeof(ass_csp) / sizeof(ass_csp[0])) +- csp = ass_csp[trackcsp]; +- if (trackcsp < sizeof(ass_levels) / sizeof(ass_levels[0])) +- levels = ass_levels[trackcsp]; +- if (trackcsp == YCBCR_DEFAULT) { +- csp = PL_COLOR_SYSTEM_BT_601; +- levels = PL_COLOR_LEVELS_LIMITED; +- } +- // Unknown colorspace (either YCBCR_UNKNOWN, or a valid value unknown to us) +- if (!csp || !levels) +- return; +- +- struct mp_image_params params = ctx->video_params; +- +- if (force_601) { +- params.repr = (struct pl_color_repr){ +- .sys = PL_COLOR_SYSTEM_BT_709, +- .levels = PL_COLOR_LEVELS_LIMITED, +- }; +- } +- +- if ((csp == params.repr.sys && levels == params.repr.levels) || +- params.repr.sys == PL_COLOR_SYSTEM_RGB) // Even VSFilter doesn't mangle on RGB video +- return; +- +- bool basic_conv = params.repr.sys == PL_COLOR_SYSTEM_BT_709 && +- params.repr.levels == PL_COLOR_LEVELS_LIMITED && +- csp == PL_COLOR_SYSTEM_BT_601 && +- levels == PL_COLOR_LEVELS_LIMITED; +- +- // With "basic", only do as much as needed for basic compatibility. +- if (opts->ass_vsfilter_color_compat == 1 && !basic_conv) +- return; +- +- if (params.repr.sys != ctx->last_params.repr.sys || +- params.repr.levels != ctx->last_params.repr.levels) +- { +- int msgl = basic_conv ? MSGL_V : MSGL_WARN; +- ctx->last_params = params; +- MP_MSG(sd, msgl, "mangling colors like vsfilter: " +- "RGB -> %s %s -> %s %s -> RGB\n", +- m_opt_choice_str(pl_csp_names, csp), +- m_opt_choice_str(pl_csp_levels_names, levels), +- m_opt_choice_str(pl_csp_names, params.repr.sys), +- m_opt_choice_str(pl_csp_names, params.repr.levels)); +- } +- +- // Conversion that VSFilter would use +- struct mp_csp_params vs_params = MP_CSP_PARAMS_DEFAULTS; +- vs_params.repr.sys = csp; +- vs_params.repr.levels = levels; +- struct pl_transform3x3 vs_yuv2rgb; +- mp_get_csp_matrix(&vs_params, &vs_yuv2rgb); +- pl_transform3x3_invert(&vs_yuv2rgb); +- +- // Proper conversion to RGB +- struct mp_csp_params rgb_params = MP_CSP_PARAMS_DEFAULTS; +- rgb_params.repr = params.repr; +- rgb_params.color = params.color; +- struct pl_transform3x3 vs2rgb; +- mp_get_csp_matrix(&rgb_params, &vs2rgb); +- +- for (int n = 0; n < parts->num_parts; n++) { +- struct sub_bitmap *sb = &parts->parts[n]; +- uint32_t color = sb->libass.color; +- int r = (color >> 24u) & 0xff; +- int g = (color >> 16u) & 0xff; +- int b = (color >> 8u) & 0xff; +- int a = 0xff - (color & 0xff); +- int rgb[3] = {r, g, b}, yuv[3]; +- mp_map_fixp_color(&vs_yuv2rgb, 8, rgb, 8, yuv); +- mp_map_fixp_color(&vs2rgb, 8, yuv, 8, rgb); +- sb->libass.color = MP_ASS_RGBA(rgb[0], rgb[1], rgb[2], a); +- } +-} +- +-int sd_ass_fmt_offset(const char *evt_fmt) +-{ +- // "Text" is always last (as it's arbitrary content in buf), e.g. format: +- // "Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text" +- int n = 0; +- while (evt_fmt && (evt_fmt = strchr(evt_fmt, ','))) +- evt_fmt++, n++; +- return n-1; // buffer is without the format's Start/End, with ReadOrder +-} +- +-bstr sd_ass_pkt_text(struct sd_filter *ft, struct demux_packet *pkt, int offset) +-{ +- // e.g. pkt->buffer ("4" is ReadOrder): "4,0,Default,,0,0,0,,fifth line" +- bstr txt = {(char *)pkt->buffer, pkt->len}, t0 = txt; +- while (offset-- > 0) { +- int n = bstrchr(txt, ','); +- if (n < 0) { // shouldn't happen +- MP_WARN(ft, "Malformed event '%.*s'\n", BSTR_P(t0)); +- return (bstr){NULL, 0}; +- } +- txt = bstr_cut(txt, n+1); +- } +- return txt; +-} +- +-bstr sd_ass_to_plaintext(char **out, const char *in) +-{ +- bstr b = {*out}; +- ass_to_plaintext(&b, in); +- *out = b.start; +- return b; +-} diff --git a/packages/mpv-release.cmake b/packages/mpv-release.cmake index 60c1160be..eb03849c2 100644 --- a/packages/mpv-release.cmake +++ b/packages/mpv-release.cmake @@ -21,22 +21,15 @@ ExternalProject_Add(mpv-release lcms2 libarchive libass - libdvdnav - libdvdread - libiconv libjpeg libpng - luajit - rubberband uchardet - openal-soft - mujs + shaderc + spirv-cross vulkan shaderc libplacebo spirv-cross - vapoursynth - libsdl2 URL ${LINK} SOURCE_DIR ${SOURCE_LOCATION} CONFIGURE_COMMAND ${EXEC} CONF=1 meson setup @@ -45,26 +38,23 @@ ExternalProject_Add(mpv-release --cross-file=${MESON_CROSS} --default-library=shared --prefer-static - -Ddebug=true - -Db_ndebug=true + -Dgpl=false + -Ddebug=false + -Db_ndebug=false -Doptimization=3 -Db_lto=true ${mpv_lto_mode} -Dlibmpv=true -Dpdf-build=enabled - -Dlua=enabled - -Djavascript=enabled - -Dsdl2=enabled - -Dlibarchive=enabled - -Dlibbluray=enabled - -Ddvdnav=enabled + -Dlua=disabled + -Djavascript=disabled -Duchardet=enabled - -Drubberband=enabled -Dlcms2=enabled - -Dopenal=enabled - -Dspirv-cross=enabled - -Dvulkan=enabled - -Dvapoursynth=enabled + -Dopenal=disabled + -Dgl=disabled + -Dspirv-cross=disabled + -Dvulkan=disabled + -Dvapoursynth=disabled ${mpv_gl} -Dc_args='-Wno-error=int-conversion' BUILD_COMMAND ${EXEC} LTO_JOB=1 ninja -C @@ -91,7 +81,6 @@ ExternalProject_Add_Step(mpv-release copy-binary COMMAND ${CMAKE_COMMAND} -E copy /mpv.exe ${CMAKE_CURRENT_BINARY_DIR}/mpv-package/mpv.exe COMMAND ${CMAKE_COMMAND} -E copy /mpv.com ${CMAKE_CURRENT_BINARY_DIR}/mpv-package/mpv.com COMMAND ${CMAKE_COMMAND} -E copy /mpv.pdf ${CMAKE_CURRENT_BINARY_DIR}/mpv-package/doc/manual.pdf - COMMAND ${CMAKE_COMMAND} -E copy ${MINGW_INSTALL_PREFIX}/etc/fonts/fonts.conf ${CMAKE_CURRENT_BINARY_DIR}/mpv-package/mpv/fonts.conf COMMENT "Copying mpv binaries and manual" ) diff --git a/packages/mpv.cmake b/packages/mpv.cmake index b30e6e33f..ccf2e245d 100644 --- a/packages/mpv.cmake +++ b/packages/mpv.cmake @@ -6,25 +6,19 @@ ExternalProject_Add(mpv lcms2 libarchive libass - libdvdnav - libdvdread - libiconv libjpeg libpng - luajit - rubberband uchardet - openal-soft - mujs + shaderc + spirv-cross vulkan shaderc libplacebo spirv-cross - vapoursynth - libsdl2 GIT_REPOSITORY https://github.com/mpv-player/mpv.git SOURCE_DIR ${SOURCE_LOCATION} - GIT_CLONE_FLAGS "--filter=tree:0" + GIT_TAG v0.39.0 + PATCH_COMMAND ${EXEC} git apply ${CMAKE_CURRENT_SOURCE_DIR}/mpv-*.patch UPDATE_COMMAND "" CONFIGURE_COMMAND ${EXEC} CONF=1 meson setup --prefix=${MINGW_INSTALL_PREFIX} @@ -32,26 +26,23 @@ ExternalProject_Add(mpv --cross-file=${MESON_CROSS} --default-library=shared --prefer-static - -Ddebug=true - -Db_ndebug=true + -Dgpl=false + -Ddebug=false + -Db_ndebug=false -Doptimization=3 -Db_lto=true ${mpv_lto_mode} -Dlibmpv=true -Dpdf-build=enabled - -Dlua=enabled - -Djavascript=enabled - -Dsdl2=enabled - -Dlibarchive=enabled - -Dlibbluray=enabled - -Ddvdnav=enabled + -Dlua=disabled + -Djavascript=disabled -Duchardet=enabled - -Drubberband=enabled -Dlcms2=enabled - -Dopenal=enabled - -Dspirv-cross=enabled - -Dvulkan=enabled - -Dvapoursynth=enabled + -Dopenal=disabled + -Dgl=disabled + -Dspirv-cross=disabled + -Dvulkan=disabled + -Dvapoursynth=disabled ${mpv_gl} -Dc_args='-Wno-error=int-conversion' BUILD_COMMAND ${EXEC} LTO_JOB=1 PDB=1 ninja -C diff --git a/packages/spirv-cross.cmake b/packages/spirv-cross.cmake index d49b9d3e8..e5679a453 100644 --- a/packages/spirv-cross.cmake +++ b/packages/spirv-cross.cmake @@ -16,6 +16,10 @@ ExternalProject_Add(spirv-cross -DSPIRV_CROSS_SHARED=ON -DSPIRV_CROSS_CLI=OFF -DSPIRV_CROSS_ENABLE_TESTS=OFF + -DSPIRV_CROSS_ENABLE_MSL=OFF + -DSPIRV_CROSS_ENABLE_CPP=OFF + -DSPIRV_CROSS_ENABLE_REFLECT=OFF + -DSPIRV_CROSS_ENABLE_UTIL=OFF -DCMAKE_CXX_FLAGS='${CMAKE_CXX_FLAGS} -D__USE_MINGW_ANSI_STDIO' BUILD_COMMAND ${EXEC} ninja -C INSTALL_COMMAND ${EXEC} ninja -C install diff --git a/packages/xz.cmake b/packages/xz.cmake index 670e691d1..bf1cce3b1 100644 --- a/packages/xz.cmake +++ b/packages/xz.cmake @@ -1,9 +1,9 @@ ExternalProject_Add(xz GIT_REPOSITORY https://gitlab.com/shinchiro/xz.git SOURCE_DIR ${SOURCE_LOCATION} - GIT_CLONE_FLAGS "--filter=tree:0" + GIT_CLONE_FLAGS "" UPDATE_COMMAND "" - GIT_RESET 18b845e69752c975dfeda418ec00eda22605c2ee # v5.4.1 + GIT_TAG v5.4.1 CONFIGURE_COMMAND ${EXEC} CONF=1 autoreconf -fi && /configure --host=${TARGET_ARCH} --prefix=${MINGW_INSTALL_PREFIX}