diff --git a/.github/workflows/all.yml b/.github/workflows/all.yml deleted file mode 100644 index 1ccbb9bf2..000000000 --- a/.github/workflows/all.yml +++ /dev/null @@ -1,113 +0,0 @@ -on: - # push: - # branches: [master] - workflow_dispatch: - inputs: - os: - description: 'macos version' - required: false - type: choice - default: 'macos-14' - options: - - macos-13 - - macos-14 - - macos-15 - platform: - description: 'choose a platform for compile' - required: false - type: choice - default: 'all' - options: - - apple - - android - - all - - ios - - tvos - - macos - dryrun: - description: 'just run workflow,but not deploy' - required: false - type: choice - default: 'false' - options: - - true - - false - pull_request: - branches: [master] - -name: Create all library Release - -jobs: - build: - name: compile all libs for ${{ inputs.platform }} then deploy - runs-on: ${{ inputs.os }} - env: - GH_TOKEN: ${{ github.token }} - steps: - - uses: nttld/setup-ndk@v1 - id: setup-ndk - with: - ndk-version: r27c - add-to-path: true - local-cache: false - - name: Checkout code - uses: actions/checkout@v4 - - name: One Step - run: | - echo '------compile unibreak------------------------------------' - rm -rf build || git reset --hard || git pull origin - .github/workflows/onestep.sh unibreak ${{ inputs.platform }} ${{ inputs.dryrun }} - echo '------compile fribidi------------------------------------' - rm -rf build || git reset --hard || git pull origin - .github/workflows/onestep.sh fribidi ${{ inputs.platform }} ${{ inputs.dryrun }} - echo '------compile freetype------------------------------------' - rm -rf build || git reset --hard || git pull origin - .github/workflows/onestep.sh freetype ${{ inputs.platform }} ${{ inputs.dryrun }} - if [[ ${{ inputs.platform }} == android ]]; then - echo '------compile android fontconfig------------------------------------' - rm -rf build || git reset --hard || git pull origin - .github/workflows/install-dependencies.sh fontconfig android - .github/workflows/onestep.sh fontconfig android ${{ inputs.dryrun }} - fi - echo '------compile harfbuzz------------------------------------' - rm -rf build || git reset --hard || git pull origin - .github/workflows/install-dependencies.sh harfbuzz ${{ inputs.platform }} - .github/workflows/onestep.sh harfbuzz ${{ inputs.platform }} ${{ inputs.dryrun }} - echo '------compile ass------------------------------------' - rm -rf build || git reset --hard || git pull origin - .github/workflows/install-dependencies.sh ass ${{ inputs.platform }} - .github/workflows/onestep.sh ass ${{ inputs.platform }} ${{ inputs.dryrun }} - echo '------compile yuv------------------------------------' - rm -rf build || git reset --hard || git pull origin - .github/workflows/onestep.sh yuv ${{ inputs.platform }} ${{ inputs.dryrun }} - echo '------compile soundtouch------------------------------------' - rm -rf build || git reset --hard || git pull origin - .github/workflows/onestep.sh soundtouch ${{ inputs.platform }} ${{ inputs.dryrun }} - echo '------compile opus------------------------------------' - rm -rf build || git reset --hard || git pull origin - .github/workflows/onestep.sh opus ${{ inputs.platform }} ${{ inputs.dryrun }} - echo '------compile openssl------------------------------------' - rm -rf build || git reset --hard || git pull origin - .github/workflows/onestep.sh openssl ${{ inputs.platform }} ${{ inputs.dryrun }} - echo '------compile dvdread------------------------------------' - rm -rf build || git reset --hard || git pull origin - .github/workflows/onestep.sh dvdread ${{ inputs.platform }} ${{ inputs.dryrun }} - echo '------compile bluray------------------------------------' - rm -rf build || git reset --hard || git pull origin - .github/workflows/install-dependencies.sh bluray ${{ inputs.platform }} - .github/workflows/onestep.sh bluray ${{ inputs.platform }} ${{ inputs.dryrun }} - echo '------compile dav1d------------------------------------' - rm -rf build || git reset --hard || git pull origin - .github/workflows/onestep.sh dav1d ${{ inputs.platform }} ${{ inputs.dryrun }} - echo '------compile uavs3d------------------------------------' - rm -rf build || git reset --hard || git pull origin - .github/workflows/onestep.sh uavs3d ${{ inputs.platform }} ${{ inputs.dryrun }} - echo '------compile smb2------------------------------------' - rm -rf build || git reset --hard || git pull origin - .github/workflows/onestep.sh smb2 ${{ inputs.platform }} ${{ inputs.dryrun }} - echo '------compile ffmpeg------------------------------------' - rm -rf build || git reset --hard || git pull origin - .github/workflows/install-dependencies.sh ffmpeg ${{ inputs.platform }} - .github/workflows/onestep.sh ffmpeg ${{ inputs.platform }} ${{ inputs.dryrun }} - env: - ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }} diff --git a/.github/workflows/build all libs.yml b/.github/workflows/build all libs.yml new file mode 100644 index 000000000..bf027007d --- /dev/null +++ b/.github/workflows/build all libs.yml @@ -0,0 +1,238 @@ +on: + # push: + # branches: [master] + workflow_dispatch: + inputs: + os: + description: 'macos version' + required: false + type: choice + default: 'macos-15' + options: + - macos-13 + - macos-14 + - macos-15 + - macos-26 + platform: + description: 'choose a platform for compile' + required: false + type: choice + default: 'all' + options: + - apple + - android + - ios + - tvos + - macos + - all + first_library: + description: 'choose the first library for compile' + required: true + type: choice + default: 'xml2' + options: + - xml2 + - unibreak + - fribidi + - freetype + - fontconfig + - harfbuzz + - ass + - yuv + - soundtouch + - opus + - openssl3 + - lcms2 + - shaderc + - moltenvk + - dovi + - placebo + - dvdread + - dvdnav + - bluray + - dav1d + - uavs3d + - smb2 + - webp + - ijkffmpeg + - fftutorial + - ffmpeg4 + - ffmpeg5 + - ffmpeg6 + - ffmpeg7 + - ffmpeg8 + dryrun: + description: 'just run workflow,but not deploy' + required: false + type: choice + default: 'false' + options: + - true + - false + verbose: + description: 'print detail logs' + required: false + type: choice + default: 'false' + options: + - true + - false + pull_request: + branches: [master] + +name: build all libs + +jobs: + build: + name: compile all libs for ${{ inputs.platform }} then deploy + runs-on: ${{ inputs.os }} + env: + GH_TOKEN: ${{ github.token }} + steps: + - uses: nttld/setup-ndk@v1 + id: setup-ndk + if: | + github.event.inputs.platform == 'all' || + github.event.inputs.platform == 'android' + with: + ndk-version: r27c + add-to-path: true + link-to-sdk: true + local-cache: false + - name: Checkout code + uses: actions/checkout@v4 + - name: Install cargo-c + run: cargo install cargo-c + - name: One Step + run: | + + Platform=${{ inputs.platform }} + DryRun=${{ inputs.dryrun }} + Verbose=${{ inputs.verbose }} + compile_lib() { + local lib_name=$1 + local platform=$Platform + + if [[ "$lib_name" == "fontconfig" ]]; then + if [[ "$platform" == "all" ]]; then + echo "force platform to android for fontconfig" + platform='android' + elif [[ "$platform" != "android" ]]; then + echo "Skip fontconfig for $platform" + return + fi + fi + + if [[ "$lib_name" == "webp" ]]; then + if [[ "$platform" == "android" ]]; then + echo "Skip webp for android" + return + elif [[ "$platform" == "all" ]]; then + echo "Skip webp for android" + platform='apple' + fi + fi + + if [[ "$lib_name" == "moltenvk" ]]; then + if [[ "$platform" == "android" ]]; then + echo "Skip moltenvk for android" + return + elif [[ "$platform" == "all" ]]; then + echo "Skip moltenvk for android" + platform='apple' + fi + fi + + if [[ "$lib_name" == "shaderc" ]]; then + if [[ "$platform" == "android" ]]; then + echo "Skip shaderc for android" + return + elif [[ "$platform" == "all" ]]; then + echo "Skip shaderc for android" + platform='apple' + fi + fi + + if [[ "$lib_name" == "dovi" ]]; then + if [[ "$platform" == "android" ]]; then + echo "Skip dovi for android" + return + elif [[ "$platform" == "all" ]]; then + echo "Skip dovi for android" + platform='apple' + fi + fi + + if [[ "$lib_name" == "placebo" ]]; then + if [[ "$platform" == "android" ]]; then + echo "Skip placebo for android" + return + elif [[ "$platform" == "all" ]]; then + echo "Skip placebo for android" + platform='apple' + fi + fi + + echo "------compile $platform $lib_name------------------------------------" + rm -rf build || git reset --hard || git pull origin + .github/workflows/install-dependencies.sh $lib_name $platform # 补全依赖安装步骤 + .github/workflows/onestep.sh $lib_name $platform $DryRun $Verbose + } + + libs=( + "xml2" + "unibreak" + "fribidi" + "freetype" + "fontconfig" + "harfbuzz" + "ass" + "yuv" + "soundtouch" + "opus" + "openssl3" + "dvdread" + "dvdnav" + "bluray" + "dav1d" + "uavs3d" + "smb2" + "webp" + "lcms2" + "shaderc" + "moltenvk" + "dovi" + "placebo" + "ijkffmpeg" + "fftutorial" + "ffmpeg4" + "ffmpeg5" + "ffmpeg6" + "ffmpeg7" + "ffmpeg8" + ) + + first=${{ inputs.first_library }} + echo "------compile from $first------------------------------------" + # 找到first_library在数组中的索引 + start_index=-1 + for i in "${!libs[@]}"; do + if [[ "${libs[$i]}" == "$first" ]]; then + start_index=$i + break + fi + done + # 如果找到了 first_library,则截取数组 + if [[ $start_index -ne -1 ]]; then + # 仅保留从 start_index 开始到最后的部分 + libs=("${libs[@]:$start_index}") + else + echo "Warning: first_library $first not found in the library list." + fi + + # 循环遍历所有库执行编译 + for lib in "${libs[@]}"; do + compile_lib "$lib" + done + env: + ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }} diff --git a/.github/workflows/build custom libs.yml b/.github/workflows/build custom libs.yml new file mode 100644 index 000000000..58928d1c4 --- /dev/null +++ b/.github/workflows/build custom libs.yml @@ -0,0 +1,117 @@ +on: + # push: + # branches: [master] + workflow_dispatch: + inputs: + os: + description: 'macos version' + required: false + type: choice + default: 'macos-15' + options: + - macos-13 + - macos-14 + - macos-15 + - macos-26 + platform: + description: 'choose a platform for compile' + required: false + type: choice + default: 'all' + options: + - apple + - android + - ios + - tvos + - macos + - all + libraries: + description: 'input libraries for compile' + required: true + default: 'xml2' + dryrun: + description: 'just run workflow,but not deploy' + required: false + type: choice + default: 'false' + options: + - true + - false + verbose: + description: 'print detail logs' + required: false + type: choice + default: 'false' + options: + - true + - false + pull_request: + branches: [master] + +name: build custom libs + +jobs: + build: + name: compile all libs for ${{ inputs.platform }} then deploy + runs-on: ${{ inputs.os }} + env: + GH_TOKEN: ${{ github.token }} + steps: + - uses: nttld/setup-ndk@v1 + id: setup-ndk + if: | + github.event.inputs.platform == 'all' || + github.event.inputs.platform == 'android' + with: + ndk-version: r27c + add-to-path: true + link-to-sdk: true + local-cache: false + - name: Checkout code + uses: actions/checkout@v4 + - name: Install cargo-c + if: contains(inputs.libraries, 'dovi') + run: cargo install cargo-c + - name: One Step + run: | + + Platform=${{ inputs.platform }} + DryRun=${{ inputs.dryrun }} + Verbose=${{ inputs.verbose }} + compile_lib() { + local lib_name=$1 + local platform=$Platform + + if [[ "$lib_name" == "fontconfig" ]]; then + if [[ "$platform" == "all" ]]; then + echo "force platform to android for fontconfig" + platform='android' + elif [[ "$platform" != "android" ]]; then + echo "Skip fontconfig for $platform" + return + fi + fi + + if [[ "$lib_name" == "webp" ]]; then + if [[ "$platform" == "android" ]]; then + echo "Skip webp for android" + return + elif [[ "$platform" == "all" ]]; then + echo "Skip webp for android" + platform='apple' + fi + fi + + echo "------compile $platform $lib_name------------------------------------" + rm -rf build || git reset --hard || git pull origin + .github/workflows/install-dependencies.sh $lib_name $platform # 补全依赖安装步骤 + .github/workflows/onestep.sh $lib_name $platform $DryRun $Verbose + } + + # 将逗号分隔转为循环处理 + IFS=',' read -ra ADDR <<< "${{ inputs.libraries }}" + for lib in "${ADDR[@]}"; do + compile_lib "$lib" + done + env: + ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }} diff --git a/.github/workflows/apple-android-common.yml b/.github/workflows/build one lib.yml similarity index 69% rename from .github/workflows/apple-android-common.yml rename to .github/workflows/build one lib.yml index 6bd60ce01..f6eaaf8b6 100644 --- a/.github/workflows/apple-android-common.yml +++ b/.github/workflows/build one lib.yml @@ -9,11 +9,12 @@ on: description: 'macos version' required: false type: choice - default: 'macos-14' + default: 'macos-15' options: - macos-13 - macos-14 - macos-15 + - macos-26 platform: description: 'choose a platform for compile' required: false @@ -34,34 +35,55 @@ on: options: - true - false + verbose: + description: 'print detail logs' + required: false + type: choice + default: 'false' + options: + - true + - false lib: description: 'choose a lib for compile' required: true type: choice - default: 'ffmpeg' + default: 'ffmpeg7' options: + - ffmpeg8 + - ffmpeg7 + - ffmpeg6 + - ffmpeg5 + - ffmpeg4 + - ijkffmpeg + - fftutorial - ass - bluray - dav1d - dvdread - - ffmpeg - - ijkffmpeg + - dvdnav - harfbuzz - fontconfig - freetype - fribidi + - lcms2 + - placebo + - moltenvk + - openssl3 - openssl - opus + - shaderc - smb2 - soundtouch - uavs3d - unibreak - yuv - xml2 + - webp + - dovi pull_request: branches: [master] -name: apple-android-common +name: build a lib jobs: build: @@ -72,15 +94,22 @@ jobs: steps: - uses: nttld/setup-ndk@v1 id: setup-ndk + if: | + github.event.inputs.platform == 'all' || + github.event.inputs.platform == 'android' with: ndk-version: r27c add-to-path: true + link-to-sdk: true local-cache: false - name: Checkout code uses: actions/checkout@v4 + - name: Install cargo-c + if: inputs.lib == 'dovi' + run: cargo install cargo-c - name: Install dependencies run: .github/workflows/install-dependencies.sh ${{ inputs.lib }} ${{ inputs.platform }} - name: One Step - run: .github/workflows/onestep.sh ${{ inputs.lib }} ${{ inputs.platform }} ${{ inputs.dryrun }} + run: .github/workflows/onestep.sh ${{ inputs.lib }} ${{ inputs.platform }} ${{ inputs.dryrun }} ${{ inputs.verbose }} env: ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }} \ No newline at end of file diff --git a/.github/workflows/compare ffmpeg features.yaml b/.github/workflows/compare ffmpeg features.yaml new file mode 100644 index 000000000..c557ee831 --- /dev/null +++ b/.github/workflows/compare ffmpeg features.yaml @@ -0,0 +1,50 @@ +name: Compare FFmpeg Features + +on: + push: + branches: + - master # 或者 main,根据你的主分支名字修改 + +# 给 Action 赋予修改 Pages 的权限 +permissions: + contents: read + pages: write + id-token: write + +# 确保同一时间只有一个部署任务在运行 +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + build-and-deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: macos-latest # 因为你的工具链是 build/src/macos,必须在 Mac 环境下运行才能生成 macos-arm64 的配置 + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + - name: Setup Pages + uses: actions/configure-pages@v5 + with: + enablement: true + # custom domain cause 404 There isn't a GitHub Pages site here. + - name: Install Markdown Parser + run: pip3 install markdown --break-system-packages + + - name: Run Toolchain and Generate Matrix + run: | + chmod +x ./tools/publish-feature-html.sh + chmod +x ./tools/list-all-feature.sh + ./tools/publish-feature-html.sh + + - name: Upload Artifact + uses: actions/upload-pages-artifact@v3 + with: + path: './docs' # 指定刚才在脚本里创建的输出目录 + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 \ No newline at end of file diff --git a/.github/workflows/install-dependencies.sh b/.github/workflows/install-dependencies.sh index 2b7adcbaf..d8cfc454d 100755 --- a/.github/workflows/install-dependencies.sh +++ b/.github/workflows/install-dependencies.sh @@ -35,22 +35,96 @@ case $LIB_NAME in ./main.sh install -l 'xml2' -p $PLAT fi ;; - dav1d) + ffmpeg8) + if [[ $PLAT == all ]];then + ./main.sh install -l 'openssl3 opus bluray dav1d dvdnav uavs3d smb2 webp' -p ios + ./main.sh install -l 'openssl3 opus bluray dav1d dvdnav uavs3d smb2 webp' -p tvos + ./main.sh install -l 'openssl3 opus bluray dav1d dvdnav uavs3d smb2 webp' -p macos + ./main.sh install -l 'openssl3 opus bluray dav1d dvdnav uavs3d smb2 soundtouch' -p android + elif [[ $PLAT == apple ]];then + ./main.sh install -l 'openssl3 opus bluray dav1d dvdnav uavs3d smb2 webp' -p ios + ./main.sh install -l 'openssl3 opus bluray dav1d dvdnav uavs3d smb2 webp' -p tvos + ./main.sh install -l 'openssl3 opus bluray dav1d dvdnav uavs3d smb2 webp' -p macos + else + ./main.sh install -l 'openssl3 opus bluray dav1d dvdnav uavs3d smb2' -p $PLAT + if [[ $PLAT == android ]];then + ./main.sh install -l 'soundtouch' -p android + else + ./main.sh install -l 'webp' -p $PLAT + fi + fi + ;; + ffmpeg7) + if [[ $PLAT == all ]];then + ./main.sh install -l 'openssl3 opus bluray dav1d dvdnav uavs3d smb2 webp' -p ios + ./main.sh install -l 'openssl3 opus bluray dav1d dvdnav uavs3d smb2 webp' -p tvos + ./main.sh install -l 'openssl3 opus bluray dav1d dvdnav uavs3d smb2 webp' -p macos + ./main.sh install -l 'openssl3 opus bluray dav1d dvdnav uavs3d smb2 soundtouch' -p android + elif [[ $PLAT == apple ]];then + ./main.sh install -l 'openssl3 opus bluray dav1d dvdnav uavs3d smb2 webp' -p ios + ./main.sh install -l 'openssl3 opus bluray dav1d dvdnav uavs3d smb2 webp' -p tvos + ./main.sh install -l 'openssl3 opus bluray dav1d dvdnav uavs3d smb2 webp' -p macos + else + ./main.sh install -l 'openssl3 opus bluray dav1d dvdnav uavs3d smb2' -p $PLAT + if [[ $PLAT == android ]];then + ./main.sh install -l 'soundtouch' -p android + else + ./main.sh install -l 'webp' -p $PLAT + fi + fi + ;; + ffmpeg6) + if [[ $PLAT == all ]];then + ./main.sh install -l 'openssl3 opus bluray dav1d dvdread uavs3d smb2' -p ios + ./main.sh install -l 'openssl3 opus bluray dav1d dvdread uavs3d smb2' -p tvos + ./main.sh install -l 'openssl3 opus bluray dav1d dvdread uavs3d smb2' -p macos + ./main.sh install -l 'openssl3 opus bluray dav1d dvdread uavs3d smb2' -p android + elif [[ $PLAT == apple ]];then + ./main.sh install -l 'openssl3 opus bluray dav1d dvdread uavs3d smb2' -p ios + ./main.sh install -l 'openssl3 opus bluray dav1d dvdread uavs3d smb2' -p tvos + ./main.sh install -l 'openssl3 opus bluray dav1d dvdread uavs3d smb2' -p macos + else + ./main.sh install -l 'openssl3 opus bluray dav1d dvdread uavs3d smb2' -p $PLAT + fi + + if [[ $PLAT == android ]];then + ./main.sh install -l 'soundtouch' -p android + fi ;; - dvdread) + ffmpeg5) + if [[ $PLAT == all ]];then + ./main.sh install -l 'openssl3 opus bluray dav1d dvdread uavs3d' -p ios + ./main.sh install -l 'openssl3 opus bluray dav1d dvdread uavs3d' -p tvos + ./main.sh install -l 'openssl3 opus bluray dav1d dvdread uavs3d' -p macos + ./main.sh install -l 'openssl3 opus bluray dav1d dvdread uavs3d' -p android + elif [[ $PLAT == apple ]];then + ./main.sh install -l 'openssl3 opus bluray dav1d dvdread uavs3d' -p ios + ./main.sh install -l 'openssl3 opus bluray dav1d dvdread uavs3d' -p tvos + ./main.sh install -l 'openssl3 opus bluray dav1d dvdread uavs3d' -p macos + else + ./main.sh install -l 'openssl3 opus bluray dav1d dvdread uavs3d' -p $PLAT + fi + + if [[ $PLAT == android ]];then + ./main.sh install -l 'soundtouch' -p android + fi ;; - ffmpeg) + ffmpeg4) if [[ $PLAT == all ]];then - ./main.sh install -l 'openssl opus dav1d dvdread uavs3d smb2 bluray' -p ios - ./main.sh install -l 'openssl opus dav1d dvdread uavs3d smb2 bluray' -p tvos - ./main.sh install -l 'openssl opus dav1d dvdread uavs3d smb2 bluray' -p macos - ./main.sh install -l 'openssl opus dav1d dvdread uavs3d smb2 bluray' -p android + ./main.sh install -l 'openssl3 opus bluray' -p ios + ./main.sh install -l 'openssl3 opus bluray' -p tvos + ./main.sh install -l 'openssl3 opus bluray' -p macos + ./main.sh install -l 'openssl3 opus bluray' -p android elif [[ $PLAT == apple ]];then - ./main.sh install -l 'openssl opus dav1d dvdread uavs3d smb2 bluray' -p ios - ./main.sh install -l 'openssl opus dav1d dvdread uavs3d smb2 bluray' -p tvos - ./main.sh install -l 'openssl opus dav1d dvdread uavs3d smb2 bluray' -p macos + ./main.sh install -l 'openssl3 opus bluray' -p ios + ./main.sh install -l 'openssl3 opus bluray' -p tvos + ./main.sh install -l 'openssl3 opus bluray' -p macos else - ./main.sh install -l 'openssl opus dav1d dvdread uavs3d smb2 bluray' -p $PLAT + ./main.sh install -l 'openssl3 opus bluray' -p $PLAT + fi + + if [[ $PLAT == android ]];then + ./main.sh install -l 'soundtouch' -p android fi ;; ijkffmpeg) @@ -66,6 +140,24 @@ case $LIB_NAME in else ./main.sh install -l 'openssl' -p $PLAT fi + + if [[ $PLAT == android ]];then + ./main.sh install -l 'soundtouch' -p android + fi + ;; + fftutorial) + if [[ $PLAT == all ]];then + ./main.sh install -l 'openssl' -p ios + ./main.sh install -l 'openssl' -p tvos + ./main.sh install -l 'openssl' -p macos + ./main.sh install -l 'openssl' -p android + elif [[ $PLAT == apple ]];then + ./main.sh install -l 'openssl' -p ios + ./main.sh install -l 'openssl' -p tvos + ./main.sh install -l 'openssl' -p macos + else + ./main.sh install -l 'openssl' -p $PLAT + fi ;; harfbuzz) if [[ $PLAT == all ]];then @@ -98,22 +190,39 @@ case $LIB_NAME in ./main.sh install -p android -l 'freetype' fi ;; - freetype) - ;; - fribidi) - ;; - openssl) - ;; - opus) - ;; - smb2) + dvdnav) + if [[ $PLAT == all ]];then + ./main.sh install -l 'dvdread' -p ios + ./main.sh install -l 'dvdread' -p tvos + ./main.sh install -l 'dvdread' -p macos + ./main.sh install -l 'dvdread' -p android + elif [[ $PLAT == apple ]];then + ./main.sh install -l 'dvdread' -p ios + ./main.sh install -l 'dvdread' -p tvos + ./main.sh install -l 'dvdread' -p macos + else + ./main.sh install -l 'dvdread' -p $PLAT + fi ;; - soundtouch) + moltenvk) + echo "MoltenVK has no external dependencies, it fetches its own dependencies" ;; - uavs3d) + shaderc) + echo "shaderc has no external dependencies, it fetches its own dependencies via git-sync-deps" ;; - unibreak) + placebo) + if [[ $PLAT == all ]];then + ./main.sh install -l 'lcms2 shaderc moltenvk dovi' -p ios + ./main.sh install -l 'lcms2 shaderc moltenvk dovi' -p tvos + ./main.sh install -l 'lcms2 shaderc moltenvk dovi' -p macos + elif [[ $PLAT == apple ]];then + ./main.sh install -l 'lcms2 shaderc moltenvk dovi' -p ios + ./main.sh install -l 'lcms2 shaderc moltenvk dovi' -p tvos + ./main.sh install -l 'lcms2 shaderc moltenvk dovi' -p macos + else + ./main.sh install -l 'lcms2 shaderc moltenvk dovi' -p $PLAT + fi ;; - yuv) + *) ;; esac diff --git a/.github/workflows/onestep.sh b/.github/workflows/onestep.sh index fd9ed7675..9d5ee0c29 100755 --- a/.github/workflows/onestep.sh +++ b/.github/workflows/onestep.sh @@ -5,6 +5,7 @@ # git describe --tags --always | awk -F - '{printf "RELEASE_VERSION=V1.0-%s",$NF}' | xargs > constants.env set -e +set -o pipefail export LIB_NAME=$1 export PLAT=$2 @@ -15,6 +16,12 @@ else export DRYRUN= fi +if [[ -n $4 && "$4" == 'true' ]];then + export VERBOSE=1 +else + export VERBOSE= +fi + export HOMEBREW_NO_AUTO_UPDATE=1 export RELEASE_DATE=$(TZ=UTC-8 date +'%y%m%d%H%M%S') export RELEASE_VERSION=$(grep GIT_REPO_VERSION= ./configs/libs/${LIB_NAME}.sh | tail -n 1 | awk -F = '{printf "%s",$2}') @@ -33,14 +40,22 @@ function init_platform echo "---generate src log--------------------------------------" cd build/src/$plat - ls | awk -F ' ' '{printf "echo %s\n echo -------------\ngit -C %s log -n 1 | cat\n",$0,$0}' | bash > $DIST_DIR/$plat-src-log-$RELEASE_VERSION.md + ls | awk -F ' ' '{printf "echo %s\n echo -------------\ngit -C %s log -n 1 | cat\n",$0,$0}' | bash > $DIST_DIR/$plat-compile-log-$RELEASE_VERSION.md cd $ROOT_DIR } function compile_ios_platform { echo "---do compile ios libs--------------------------------------" - ./main.sh compile -p ios -c build -l ${LIB_NAME} + + local log_file="$DIST_DIR/ios-compile-log-$RELEASE_VERSION.md" + + if [[ $VERBOSE ]];then + ./main.sh compile -p ios -c build -l ${LIB_NAME} 2>&1 | tee -a "$log_file" + else + ./main.sh compile -p ios -c build -l ${LIB_NAME} >> "$log_file" 2>&1 + fi + cd build/product/ios/universal zip -ryq $DIST_DIR/${LIB_NAME}-ios-universal-${RELEASE_VERSION}.zip ./* @@ -52,7 +67,15 @@ function compile_ios_platform function compile_macos_platform { echo "---do compile macos libs--------------------------------------" - ./main.sh compile -p macos -c build -l ${LIB_NAME} + + local log_file="$DIST_DIR/macos-compile-log-$RELEASE_VERSION.md" + + if [[ $VERBOSE ]];then + ./main.sh compile -p macos -c build -l ${LIB_NAME} 2>&1 | tee -a "$log_file" + else + ./main.sh compile -p macos -c build -l ${LIB_NAME} >> "$log_file" 2>&1 + fi + cd build/product/macos/universal zip -ryq $DIST_DIR/${LIB_NAME}-macos-universal-${RELEASE_VERSION}.zip ./* cd $ROOT_DIR @@ -61,7 +84,15 @@ function compile_macos_platform function compile_tvos_platform { echo "---do compile tvos libs--------------------------------------" - ./main.sh compile -p tvos -c build -l ${LIB_NAME} + + local log_file="$DIST_DIR/android-compile-log-$RELEASE_VERSION.md" + + if [[ $VERBOSE ]];then + ./main.sh compile -p tvos -c build -l ${LIB_NAME} 2>&1 | tee -a "$log_file" + else + ./main.sh compile -p tvos -c build -l ${LIB_NAME} >> "$log_file" 2>&1 + fi + cd build/product/tvos/universal zip -ryq $DIST_DIR/${LIB_NAME}-tvos-universal-${RELEASE_VERSION}.zip ./* @@ -74,7 +105,15 @@ function compile_tvos_platform function compile_android_platform { echo "---do compile android libs--------------------------------------" - ./main.sh compile -p android -c build -l ${LIB_NAME} + + local log_file="$DIST_DIR/android-compile-log-$RELEASE_VERSION.md" + + if [[ $VERBOSE ]];then + ./main.sh compile -p android -c build -l ${LIB_NAME} 2>&1 | tee -a "$log_file" + else + ./main.sh compile -p android -c build -l ${LIB_NAME} >> "$log_file" 2>&1 + fi + cd build/product/android/universal zip -ryq $DIST_DIR/${LIB_NAME}-android-universal-${RELEASE_VERSION}.zip ./* cd $ROOT_DIR @@ -82,10 +121,11 @@ function compile_android_platform function make_xcfmwk_bundle() { - echo "---Zip apple xcframework--------------------------------------" - cd build/product/xcframework - zip -ryq $DIST_DIR/${LIB_NAME}-apple-xcframework-${RELEASE_VERSION}.zip ./* - cd $ROOT_DIR + echo "---skip apple xcframework--------------------------------------" + # echo "---Zip apple xcframework--------------------------------------" + # cd build/product/xcframework + # zip -ryq $DIST_DIR/${LIB_NAME}-apple-xcframework-${RELEASE_VERSION}.zip ./* + # cd $ROOT_DIR } function replace_tag() @@ -98,15 +138,9 @@ function replace_tag() # replace PRE_COMPILE_TAG_IOS=new_tag sed -i "" "s/^export $key=.*/export $key=$TAG/" $file else - # PRE_COMPILE_TAG_IOS not found, check PRE_COMPILE_TAG - if grep -q "PRE_COMPILE_TAG" "$file"; then - # insert PRE_COMPILE_TAG_IOS=new_tag after PRE_COMPILE_TAG -sed -i "" "/PRE_COMPILE_TAG=/a\\ -export $key=$TAG -" "$file" - else - echo "can't find PRE_COMPILE_TAG in $file" - fi + # PRE_COMPILE_TAG_IOS not found, append PRE_COMPILE_TAG_IOS + [ -n "$(tail -c1 "$file")" ] && echo "" >> "$file" + echo "export $key=$TAG" >> "$file" fi } @@ -132,7 +166,6 @@ function upgrade() replace_tag $file PRE_COMPILE_TAG_ANDROID ;; all) - replace_tag $file PRE_COMPILE_TAG replace_tag $file PRE_COMPILE_TAG_IOS replace_tag $file PRE_COMPILE_TAG_MACOS replace_tag $file PRE_COMPILE_TAG_TVOS diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 064ebf81b..8e38ad467 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,11 +10,13 @@ name: Test Release jobs: build: name: compile none then deploy - runs-on: macos-14 + runs-on: macos-15 env: GH_TOKEN: ${{ github.token }} steps: - name: Checkout code uses: actions/checkout@v4 + - name: Install cargo-c + run: cargo install cargo-c - name: One Step run: .github/workflows/onestep.sh test all \ No newline at end of file diff --git a/LICENCE b/LICENCE new file mode 100644 index 000000000..d8cc959d7 --- /dev/null +++ b/LICENCE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) [2020] [debugly] + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index 0757c832d..42235d574 100644 --- a/README.md +++ b/README.md @@ -1,199 +1,160 @@ -## MRFFToolChain Build Shell +## MRFFToolChain Build Shell [[中文版](./README_zh-CN.md)] + +![](https://img.shields.io/github/downloads/debugly/MRFFToolChainBuildShell/total) **What's MRFFToolChain?** -MRFFToolChain products was built for ijkplayer : [https://github.com/debugly/ijkplayer](https://github.com/debugly/ijkplayer). +MRFFToolChain is a mature set of compilation tools specifically designed for compiling third-party libraries for iOS, macOS, tvOS, and Android platforms. It's products was built for [fsplayer](https://github.com/debugly/fsplayer) 、[ijkplayer](https://github.com/debugly/ijkplayer) 、[FFmpegTutorial](https://github.com/debugly/FFmpegTutorial). -At present MRFFToolChain contained `ass、bluray、dav1d、dvdread、ffmpeg、freetype、fribidi、harfbuzz、openssl、opus、unibreak、uavs3d、smb2、yuv、soundtouch`. +At present MRFFToolChain contained `ass、bluray、dav1d、dovi、dvdread、dvdnav、ffmpeg、freetype、fribidi、harfbuzz、lcms2、placebo、moltenvk、openssl、opus、shaderc、smb2、soundtouch、unibreak、uavs3d、xml2、yuv、webp`. ## Supported Plat -| platform | architectures | minimum deployment target | -| ----- | -------------------------------------- |----- | -| iOS | arm64、arm64_simulator、x86_64_simulator | 11.0 | -| tvOS | arm64、arm64_simulator、x86_64_simulator | 12.0 | -| macOS | arm64、x86_64 | 10.11 | -| Android | arm64、armv7a、x86_64、x86 | 21 | +| platform | architectures | minimum deployment target | +| -------- | --------------------------------------- | ------------------------- | +| iOS | arm64、arm64_simulator、x86_64_simulator | 12.0 | +| tvOS | arm64、arm64_simulator、x86_64_simulator | 12.0 | +| macOS | arm64、x86_64 | 10.14 | +| Android | arm64、armv7a、x86_64、x86 | 21 | ## News +- FFmpeg **8.1.1** is ready - upgrade all libs to lastest,Improved optimizations -- using macOS 14, remove bitcode support - -[https://developer.apple.com/documentation/xcode-release-notes/xcode-14-release-notes#Deprecations](https://developer.apple.com/documentation/xcode-release-notes/xcode-14-release-notes#Deprecations) +- using macOS 15,Xcode_16.4 ## Denpendency - Fontconfig: xml2,freetype - Bluray: xml2 - Harfbuzz: freetype -- Ass for Appple: harfbuzz,fribidi,unibreak +- Dvdnav: dvdread +- Placebo for Appple: shaderc,moltenvk,dovi,lcms2 +- Ass for Appple: harfbuzz,fribidi,unibreak - Ass for Android: harfbuzz,fribidi,unibreak,fontconfig -- FFmpeg for Appple: openssl,opus,dav1d,dvdread,uavs3d,smb2 -- FFmpeg for Android: openssl,opus,dav1d,dvdread,uavs3d,smb2,soundtouch +- IJKFFmpeg: openssl +- FFmpeg4 for Appple: openssl3,opus,bluray +- FFmpeg5 for Appple: openssl3,opus,bluray,dav1d,dvdread,uavs3d +- FFmpeg6 for Appple: openssl3,opus,bluray,dav1d,dvdread,uavs3d,smb2 +- FFmpeg7 for Appple: openssl3,opus,bluray,dav1d,dvdnav,uavs3d,smb2,webp +- FFmpeg8 for Appple: openssl3,opus,bluray,dav1d,dvdnav,uavs3d,smb2,webp +- FFmpeg4 for Android: openssl3,opus,bluray,soundtouch +- FFmpeg5 for Android: openssl3,opus,bluray,dav1d,dvdread,uavs3d,soundtouch +- FFmpeg6 for Android: openssl3,opus,bluray,dav1d,dvdread,uavs3d,smb2,soundtouch +- FFmpeg7 for Android: openssl3,opus,bluray,dav1d,dvdnav,uavs3d,smb2,soundtouch,libplacebo,shaderc,moltenvk,dovi,lcms2 Tips: ``` -1、FFmpeg is not denpendent on Ass. -2、ijkplayer is denpendent on FFmpeg and Ass. -3、when install pre-compiled lib, will containes it's denpendencies. -``` - -## Folder structure - -``` -├── README.md -├── build #编译目录 -│   ├── extra #源码仓库 -│   ├── pre #下载的预编译库 -│   ├── product #编译产物 -│   └── src #构建时源码仓库 -├── configs #三方库配置信息 -│   ├── default.sh -│   ├── ffconfig #FFmpeg功能裁剪选项 -│   ├── libs #三方库具体配置,包括库名,git仓库地址等信息 -│   └── meson-crossfiles -├── do-compile #三方库编译过程 -│   ├── android #安卓平台 -│   └── apple #苹果平台 -├── do-init #初始化三方库仓库 -│   ├── copy-local-repo.sh -│   ├── init-repo.sh -│   └── main.sh -├── do-install #下载安装预编译的三方库 -│   ├── download-uncompress.sh -│   ├── install-pre-lib.sh -│   ├── install-pre-xcf.sh -│   └── main.sh -├── main.sh #脚本入口 -├── patches #给三方库打的补丁 -│   ├── bluray -│   ├── ffmpeg -> ffmpeg-n6.1 -│   ├── ffmpeg-n4.0 -│   ├── ffmpeg-n5.1 -│   ├── ffmpeg-n6.1 -│   ├── ffmpeg-release-5.1 -│   ├── smb2 -│   ├── smb2-4.0.0 -│   ├── uavs3d -│   └── yuv -└── tools #通用工具方法 - ├── export-android-build-env.sh - ├── export-android-host-env.sh - ├── export-android-pkg-config-dir.sh - ├── export-apple-build-env.sh - ├── export-apple-host-env.sh - ├── export-apple-pkg-config-dir.sh - ├── gas-preprocessor.pl - ├── ios.toolchain.cmake - ├── parse-arguments.sh - └── prepare-build-workspace.sh +1、ffmpeg is not denpendent on ass and placebo. +2、fsplayer is denpendent on ffmpeg and ass and placebo. +3、ijkplayer is denpendent on ijkffmpeg. +4、FFmpegTutorial is denpendent on fftutorial. +5、when install pre-compiled lib, will containes it's denpendencies. ``` -## Download/Install Pre-compiled libs - -直接从 github 下载我预编译好的库,这种方式可节省大量时间。 - -预编译库已经将 patches 目录下的补丁全部打上了。 +## Download/Install Pre-compiled Libs -安装方法: +Save yourself a great deal of time by directly downloading the pre-compiled libraries from GitHub. +These pre-compiled libraries already applied patches which in the patches directory. ```bash -#查看帮助是个好习惯 +#Check the help first ./main.sh install --help -# 使用方式随便举例: +# Examples of usage: ./main.sh install -p macos -l ffmpeg ./main.sh install -p ios -l 'ass ffmpeg' -./main.sh install -p tvos -l all -./main.sh install -p android -l all +./main.sh install -p android -l openssl3 ``` -## Compile by yourself +## Compile by Yourself -### Init lib repos +### Initialize Library Repositories -不要浪费自己的时间去编译这些库,除非你修改了源码! -直接下载我白嫖 github 预先编译好的库不好么! +Don't waste your time compiling these libraries unless you've modified the source code! +Why not just download the pre-compiled libraries I've prepared using GitHub actions? -脚本参数比较灵活,可根据需要搭配使用,常用方式举例: +The script parameters are flexible and can be combined as needed. Here are some common examples: ``` -#查看帮助是个好习惯 +# Check the help first ./main.sh init --help -#准备 iOS 平台源码所有库的源码 -./main.sh init -p ios -l all -#准备 iOS 平台x86架构下所有库的源码 -./main.sh init -p ios -l all -a x86_64_simulator -#准备 macOS 平台源码所有库的源码 -./main.sh init -p macos -l all -#准备 iOS 平台的某些库的源码 -./main.sh init -p ios -l "openssl ffmpeg" -#准备 Android 平台的某些库的源码 -./main.sh init -p anroid -l "openssl ffmpeg" +# Prepare libass source code for the iOS platform +./main.sh init -p ios -l ass +# Prepare ffmpeg7 source code for the x86 architecture on iOS +./main.sh init -p ios -l ffmpeg7 -a x86_64_simulator +# Prepare source code for specific libraries for the Android platform +./main.sh init -p android -l "openssl ffmpeg" ``` ### Compile -查看帮助是个好习惯 +Once the source code repository initialization is complete, you can start the compilation process. ``` +# Check the help first ./main.sh compile --help -# 根据帮助可知 -p 参数指定平台;-c 参数指定行为,比如:build是编译,rebuild是重编等; -l 指定要编译的库;-a 指定 cpu 架构。 +# As shown in the help: +# -p specifies the platform +# -c specifies the action (e.g build for compilation, rebuild for recompilation) +# -l specifies the libraries to compile +# -a specifies the CPU architecture ``` -使用方式随便举例: + +The following code demonstrates how to compile FFmpeg 7 for the iOS platform: ``` -#比如编译 ios 平台所有依赖库 -./main.sh compile -c build -p ios -l all -#比如编译 ios 平台 arm64 架构下的 libass 库 -./main.sh compile -c build -p ios -a arm64 -l ass +# install FFmpeg7's dependencies has two choices +# recommend choice (because ffmpeg7 was pre-compiled,it contained all dependencies) +./main.sh install -p ios -l ffmpeg7 +# other choice (you must know ffmpeg7's dependent lib name) +./main.sh install -p ios -l "openssl3 opus bluray dav1d dvdnav uavs3d smb2" +# Compile FFmpeg7 for the arm64 architecture on iOS with xcframework +./main.sh compile -p ios -a arm64 -l ffmepg7 --fmwk ``` -脚本对于这些参数的顺序没有要求,可以随意摆放。 +The order of these parameters does not matter; they can be arranged in any sequence. ### Support Mirror -如果 github 上的仓库克隆较慢,或者需要使用内网私有仓库,可在执行编译脚本前声明对应的环境变量! - -| 名称 | 当前版本| 仓库地址 | 使用镜像 | -| ----------- | -------| ------------------------------------------------------- | -------------------------------------------------------- | -| FFmpeg | 6.1.2 | https://github.com/FFmpeg/FFmpeg.git | export GIT_FFMPEG_UPSTREAM = git@xx:yy/FFmpeg.git | -| ass | 0.17.3 | https://github.com/libass/libass.git | export GIT_ASS_UPSTREAM = git@xx:yy/libass.git | -| bluray | 1.3.4 | https://code.videolan.org/videolan/libbluray.git | export GIT_BLURAY_UPSTREAM = git@xx:yy/libbluray.git | -| dav1d | 1.5.1 | https://code.videolan.org/videolan/dav1d.git | export GIT_DAV1D_UPSTREAM = git@xx:yy/dav1d.git | -| dvdread | 6.1.3 | https://code.videolan.org/videolan/libdvdread.git | export GIT_DVDREAD_UPSTREAM = git@xx:yy/libdvdread.git | -| fontconfig | 2.16.0 | https://gitlab.freedesktop.org/fontconfig/fontconfig.git| export GIT_FONTCONFIG_UPSTREAM=git@xx:yy/fontconfig.git | -| freetype | 2.13.3 | https://gitlab.freedesktop.org/freetype/freetype.git | export GIT_FREETYPE_UPSTREAM = git@xx:yy/freetype.git | -| fribidi | 1.0.16 | https://github.com/fribidi/fribidi.git | export GIT_FRIBIDI_UPSTREAM = git@xx:yy/fribidi.git | -| harfbuzz | 10.2.0 | https://github.com/harfbuzz/harfbuzz.git | export GIT_HARFBUZZ_UPSTREAM = git@xx:yy/harfbuzz.git | -| openssl | 1.1.1w | https://github.com/openssl/openssl.git | export GIT_OPENSSL_UPSTREAM = git@xx:yy/openssl.git | -| opus | 1.5.2 | https://gitlab.xiph.org/xiph/opus.git | export GIT_OPUS_UPSTREAM = git@xx:yy/opus.git | -| smb2 | 6.2 | https://github.com/sahlberg/libsmb2.git | export GIT_SMB2_UPSTREAM=git@xx:yy/libsmb2.git | -| soundtouch | 2.3.3 | https://codeberg.org/soundtouch/soundtouch.git | export GIT_SOUNDTOUCH_UPSTREAM=git@xx:yy/soundtouch.git | -| unibreak | 6.1 | https://github.com/adah1972/libunibreak.git | export GIT_UNIBREAK_UPSTREAM = git@xx:yy/libunibreak.git | -| uavs3d | 1.2.1 | https://github.com/uavs3/uavs3d.git | export GIT_UAVS3D_UPSTREAM=git@xx:yy/UAVS3D.git | -| xml2 | 2.13.6 | https://github.com/GNOME/libxml2.git | export GIT_FONTCONFIG_UPSTREAM=git@xx:yy/fontconfig.git | -| yuv | stable-eb6e7bb | https://github.com/debugly/libyuv.git | export GIT_YUV_UPSTREAM=git@xx:yy/yuv.git | +If cloning repositories from GitHub is slow, or if you need to use an internal private repository, you can declare the corresponding environment variables before running the compilation script! + +| Lib Name | Current Version | Repository URL | Mirror Repository URL | +| ---------- | --------------- | -------------------------------------------------------- | -------------------------------------------------------- | +| ffmpeg8 | 8.1.1 | https://github.com/FFmpeg/FFmpeg.git | export GIT_FFMPEG_UPSTREAM=git@xx:yy/FFmpeg.git | +| ffmpeg7 | 7.1.3 | https://github.com/FFmpeg/FFmpeg.git | export GIT_FFMPEG_UPSTREAM=git@xx:yy/FFmpeg.git | +| ffmpeg6 | 6.1.1 | https://github.com/FFmpeg/FFmpeg.git | export GIT_FFMPEG_UPSTREAM=git@xx:yy/FFmpeg.git | +| ffmpeg5 | 5.1.6 | https://github.com/FFmpeg/FFmpeg.git | export GIT_FFMPEG_UPSTREAM=git@xx:yy/FFmpeg.git | +| ffmpeg4 | 4.0.5 | https://github.com/FFmpeg/FFmpeg.git | export GIT_FFMPEG_UPSTREAM=git@xx:yy/FFmpeg.git | +| ijkffmpeg | ff4.0--ijk0.8.8--20210426--001 | https://github.com/bilibili/FFmpeg.git | export GIT_IJKFFMPEG_UPSTREAM=git@xx:yy/FFmpeg.git | +| ass | 0.17.4 | https://github.com/libass/libass.git | export GIT_ASS_UPSTREAM=git@xx:yy/libass.git | +| bluray | 1.3.4 | https://code.videolan.org/videolan/libbluray.git | export GIT_BLURAY_UPSTREAM=git@xx:yy/libbluray.git | +| dav1d | 1.5.3 | https://code.videolan.org/videolan/dav1d.git | export GIT_DAV1D_UPSTREAM=git@xx:yy/dav1d.git | +| dvdread | 6.1.3 | https://code.videolan.org/videolan/libdvdread.git | export GIT_DVDREAD_UPSTREAM=git@xx:yy/libdvdread.git | +| dvdnav | master-9831fe01 | https://code.videolan.org/videolan/libdvdnav.git | export GIT_DVDNAV_UPSTREAM=git@xx:yy/libdvdnav.git | +| fontconfig | 2.17.1 | https://gitlab.freedesktop.org/fontconfig/fontconfig.git | export GIT_FONTCONFIG_UPSTREAM=git@xx:yy/fontconfig.git | +| freetype | 2.14.1 | https://gitlab.freedesktop.org/freetype/freetype.git | export GIT_FREETYPE_UPSTREAM=git@xx:yy/freetype.git | +| fribidi | 1.0.16 | https://github.com/fribidi/fribidi.git | export GIT_FRIBIDI_UPSTREAM=git@xx:yy/fribidi.git | +| harfbuzz | 12.3.2 | https://github.com/harfbuzz/harfbuzz.git | export GIT_HARFBUZZ_UPSTREAM=git@xx:yy/harfbuzz.git | +| openssl | 1.1.1w | https://github.com/openssl/openssl.git | export GIT_OPENSSL_UPSTREAM=git@xx:yy/openssl.git | +| openssl3 | 3.6.2 | https://github.com/openssl/openssl.git | export GIT_OPENSSL_UPSTREAM=git@xx:yy/openssl.git | +| opus | 1.6.1 | https://gitlab.xiph.org/xiph/opus.git | export GIT_OPUS_UPSTREAM=git@xx:yy/opus.git | +| smb2 | 6.2 | https://github.com/sahlberg/libsmb2.git | export GIT_SMB2_UPSTREAM=git@xx:yy/libsmb2.git | +| soundtouch | 2.4.0 | https://codeberg.org/soundtouch/soundtouch.git | export GIT_SOUNDTOUCH_UPSTREAM=git@xx:yy/soundtouch.git | +| unibreak | 6.1 | https://github.com/adah1972/libunibreak.git | export GIT_UNIBREAK_UPSTREAM=git@xx:yy/libunibreak.git | +| uavs3d | 1.2.1 | https://github.com/uavs3/uavs3d.git | export GIT_UAVS3D_UPSTREAM=git@xx:yy/UAVS3D.git | +| xml2 | 2.15.1 | https://github.com/GNOME/libxml2.git | export GIT_FONTCONFIG_UPSTREAM=git@xx:yy/fontconfig.git | +| yuv | main-f94b8cf7 | https://github.com/debugly/libyuv.git | export GIT_YUV_UPSTREAM=git@xx:yy/yuv.git | +| webp | v1.6.0 | https://github.com/debugly/libwebp.git | export GIT_WEBP_UPSTREAM=git@xx:yy/webp.git | +| placebo | 7.349.0 | https://github.com/haasn/libplacebo.git | export GIT_LIBPLACEBO_UPSTREAM=git@xx:yy/libplacebo.git | +| shaderc | 2025.1 | https://github.com/google/shaderc.git | export GIT_SHADERC_UPSTREAM=git@xx:yy/shaderc.git | +| moltenvk | 1.3.1 | https://github.com/KhronosGroup/MoltenVK.git | export GIT_MOLTENVK_UPSTREAM=git@xx:yy/MoltenVK.git | +| lcms2 | 2.16 | https://github.com/mm2/Little-CMS.git | export GIT_LCMS2_UPSTREAM=git@xx:yy/Little-CMS.git | +| dovi | 1.6.79 | https://github.com/AMDEXA/libdovi.git | export GIT_DOVI_UPSTREAM=git@xx:yy/libdovi.git | ## Tips -- 可下载预编译的 xcframework 库,只需要在 install 时加上 --fmwk 参数 -- 初始化仓库时,可跳过拉取远端到本地,只需要在 init 时加上 --skip-pull-base 参数 -- 初始化仓库时,可跳过应用 FFmpeg 的补丁,只需要在 init 时加上 --skip-patches 参数 -- 目前 FFmpeg 使用的是 module-full.sh 配置选项,所以包体积略大 -- 可以自己把 Github 预编译的库全部下载放到自己的服务器上,在 install 前使用 MR_DOWNLOAD_BASEURL 指定自己的服务器地址 - -## Donate - -编译三方库很费时间,本人想为开源社区贡献一份微薄的力量,因此将 debugly/ijkplayer 依赖的三方库,全部预编成静态库和 xcframework 供大家使用。 - -如果您想要为开源社区贡献一份力量,请买杯咖啡给我提提神儿。 - -![donate.jpg](https://i.postimg.cc/xdVqnBLp/IMG-7481.jpg) - -感谢以下朋友对 debugly/MRFFToolChainBuildShell 的支持: - -- 海阔天也空 -- 小猪猪 -- 1996GJ +- To download pre-compiled xcframework libraries, add the --fmwk parameter when using the install command. +- To skip pulling remote repositories during initialization, add the --skip-pull-base parameter when using the init command. +- Currently, FFmpeg uses the **module-full.sh** configuration, resulting in slightly larger package sizes. +- You can download all pre-compiled GitHub libraries to your own server and specify your server address using MR_DOWNLOAD_BASEURL before running the install command. diff --git a/README_zh-CN.md b/README_zh-CN.md new file mode 100644 index 000000000..fb7b0f403 --- /dev/null +++ b/README_zh-CN.md @@ -0,0 +1,163 @@ +## MRFFToolChain 构建脚本 + +![](https://img.shields.io/github/downloads/debugly/MRFFToolChainBuildShell/total) + + +**MRFFToolChain 是什么?** + +MRFFToolChain 是一套成熟的编译工具,专门用来编译 iOS、macOS、tvOS、Android 平台的三方库,其构建产物为 [fsplayer](https://github.com/debugly/fsplayer) 、 [ijkplayer](https://github.com/debugly/ijkplayer) 、[FFmpegTutorial](https://github.com/debugly/FFmpegTutorial) 所用. + +目前包含了这些库:`ass、bluray、dav1d、dovi、dvdread、dvdnav、ffmpeg、freetype、fribidi、harfbuzz、lcms2、placebo、moltenvk、openssl、opus、shaderc、smb2、soundtouch、unibreak、uavs3d、xml2、yuv、webp`. + +## 支持的平台 + +| 平台 | 架构 | 最低部署目标版本 | +| -------- | -------------------------------------- | ------------------------- | +| iOS | arm64、arm64_simulator、x86_64_simulator | 12.0 | +| tvOS | arm64、arm64_simulator、x86_64_simulator | 12.0 | +| macOS | arm64、x86_64 | 10.14 | +| Android | arm64、armv7a、x86_64、x86 | 21 | + +## 最新动态 + +- FFmpeg **8.1.1** 已经准备好了 +- 将所有库升级至最新版本,不少库提升了性能 +- 使用 macOS 15,Xcode_16.4构建 + +## 依赖关系 + +编译了适用于安卓和 iOS 平台的 FFmpeg4,FFmpeg5,FFmpeg6,FFmpeg7,FFmpeg8。 + +- Fontconfig:xml2、freetype +- Bluray:xml2 +- Harfbuzz:freetype +- dvdnav:dvdread +- 适用于 ijkplayer 的 FFmpeg: openssl +- 适用于苹果的 Placebo: shaderc,moltenvk,dovi,lcms2 +- 适用于苹果的 Ass:harfbuzz、fribidi、unibreak +- 适用于安卓的 Ass:harfbuzz、fribidi、unibreak、fontconfig +- 适用于苹果的 FFmpeg4:openssl3、opus、bluray +- 适用于苹果的 FFmpeg5:openssl3、opus、bluray、dav1d、dvdread、uavs3d +- 适用于苹果的 FFmpeg6:openssl3、opus、bluray、dav1d、dvdread、uavs3d、smb2 +- 适用于苹果的 FFmpeg7:openssl3、opus、bluray、dav1d、dvdnav、uavs3d、smb2、webp +- 适用于苹果的 FFmpeg8:openssl3、opus、bluray、dav1d、dvdnav、uavs3d、smb2、webp +- 适用于安卓的 FFmpeg4:openssl3、opus、bluray、soundtouch +- 适用于安卓的 FFmpeg5:openssl3、opus、bluray、dav1d、dvdread、uavs3d、soundtouch +- 适用于安卓的 FFmpeg6:openssl3、opus、bluray、dav1d、dvdread、uavs3d、smb2、soundtouch +- 适用于安卓的 FFmpeg7:openssl3、opus、bluray、dav1d、dvdnav、uavs3d、smb2、soundtouch + +提示: + +``` +1、ffmpeg 不依赖 ass 和 placebo +2、fsplayer 依赖 ffmpeg 和 ass 和 placebo +3、ijkplayer 依赖 ijkffmpeg +4、FFmpegTutorial 依赖 fftutorial +5、安装预编译库时,会包含其所有依赖项 +``` + +## 下载 / 安装预编译库 + +直接从 GitHub 下载预编译库可以为您节省大量时间。 + +这些预编译库已经应用了 patches 目录下的补丁。 + +```bash +# 先查看帮助 +./main.sh install --help +# 使用示例 +./main.sh install -p macos -l ffmpeg +./main.sh install -p ios -l 'ass ffmpeg' +./main.sh install -p android -l openssl3 +``` + +## 自行编译 + +### 初始化目标库仓库 + +除非您修改了源代码,否则不要浪费时间编译这些库! +何不直接下载我通过 GitHub 动作准备好的预编译库呢? +脚本参数灵活,可根据需要组合使用。以下是一些常见示例: + +``` +# 先查看帮助 +./main.sh init --help +# 为 iOS 平台准备 libass 源代码 +./main.sh init -p ios -l ass +# 为 iOS 的 x86 架构准备 ffmpeg7 源代码 +./main.sh init -p ios -l ffmpeg7 -a x86_64_simulator +# 为 Android 平台准备特定库的源代码 +./main.sh init -p android -l "openssl ffmpeg" +``` + +### 编译 + +当源代码仓库初始化完成后,就可以开始编译了。 + +``` +# 先查看帮助 +./main.sh compile --help +# 如帮助所示: +# -p 指定平台 +# -c 指定操作(例如 build 用于编译,rebuild 用于重新编译) +# -l 指定要编译的库 +# -a 指定 CPU 架构 +``` + +以下代码演示如何为 iOS 平台编译 FFmpeg 7: + +``` +# 安装 FFmpeg7 的依赖有两种选择 +# 推荐选择安装预编译方式(因为预编译的 FFmpeg7 已经包含所有依赖项) +./main.sh install -p ios -l ffmpeg7 +# 另外一个选择,自己选择性地安装 FFmpeg7 的依赖库 +./main.sh install -p ios -l "openssl3 opus bluray dav1d dvdnav uavs3d smb2" +# 编译 iOS 平台 arm64 架构的 FFmpeg7,并且生成 xcframework +./main.sh compile -p ios -a arm64 -l ffmepg7 --fmwk +``` + +这些参数的顺序无关紧要,可以按任意顺序排列。 + +### 支持镜像 + +如果从 GitHub 克隆仓库速度较慢,或者需要使用内部私有仓库,可以在运行编译脚本之前声明相应的环境变量! + +| 库名称 | 当前版本 | 仓库 URL | 镜像仓库 URL | +| --------------- |----------- | ----------- | ------------ | +| ffmpeg8 | 8.1.1 | https://github.com/FFmpeg/FFmpeg.git | export GIT_FFMPEG_UPSTREAM=git@xx:yy/FFmpeg.git | +| ffmpeg7 | 7.1.3 | https://github.com/FFmpeg/FFmpeg.git | export GIT_FFMPEG_UPSTREAM=git@xx:yy/FFmpeg.git | +| ffmpeg6 | 6.1.1 | https://github.com/FFmpeg/FFmpeg.git | export GIT_FFMPEG_UPSTREAM=git@xx:yy/FFmpeg.git | +| ffmpeg5 | 5.1.6 | https://github.com/FFmpeg/FFmpeg.git | export GIT_FFMPEG_UPSTREAM=git@xx:yy/FFmpeg.git | +| ffmpeg4 | 4.0.5 | https://github.com/FFmpeg/FFmpeg.git | export GIT_FFMPEG_UPSTREAM=git@xx:yy/FFmpeg.git | +| ijkffmpeg | ff4.0--ijk0.8.8--20210426--001 | https://github.com/bilibili/FFmpeg.git | export GIT_IJKFFMPEG_UPSTREAM=git@xx:yy/FFmpeg.git | +| ass | 0.17.4 | https://github.com/libass/libass.git | export GIT_ASS_UPSTREAM=git@xx:yy/libass.git | +| bluray | 1.3.4 | https://code.videolan.org/videolan/libbluray.git | export GIT_BLURAY_UPSTREAM=git@xx:yy/libbluray.git | +| dav1d | 1.5.3 | https://code.videolan.org/videolan/dav1d.git | export GIT_DAV1D_UPSTREAM=git@xx:yy/dav1d.git | +| dvdread | 6.1.3 | https://code.videolan.org/videolan/libdvdread.git | export GIT_DVDREAD_UPSTREAM=git@xx:yy/libdvdread.git | +| dvdnav | master-9831fe01 | https://code.videolan.org/videolan/libdvdnav.git | export GIT_DVDNAV_UPSTREAM=git@xx:yy/libdvdnav.git | +| fontconfig | 2.17.1 | https://gitlab.freedesktop.org/fontconfig/fontconfig.git | export GIT_FONTCONFIG_UPSTREAM=git@xx:yy/fontconfig.git | +| freetype | 2.14.1 | https://gitlab.freedesktop.org/freetype/freetype.git | export GIT_FREETYPE_UPSTREAM=git@xx:yy/freetype.git | +| fribidi | 1.0.16 | https://github.com/fribidi/fribidi.git | export GIT_FRIBIDI_UPSTREAM=git@xx:yy/fribidi.git | +| harfbuzz | 12.3.2 | https://github.com/harfbuzz/harfbuzz.git | export GIT_HARFBUZZ_UPSTREAM=git@xx:yy/harfbuzz.git | +| openssl | 1.1.1w | https://github.com/openssl/openssl.git | export GIT_OPENSSL_UPSTREAM=git@xx:yy/openssl.git | +| openssl3 | 3.6.2 | https://github.com/openssl/openssl.git | export GIT_OPENSSL_UPSTREAM=git@xx:yy/openssl.git | +| opus | 1.6.1 | https://gitlab.xiph.org/xiph/opus.git | export GIT_OPUS_UPSTREAM=git@xx:yy/opus.git | +| smb2 | 6.2 | https://github.com/sahlberg/libsmb2.git | export GIT_SMB2_UPSTREAM=git@xx:yy/libsmb2.git | +| soundtouch | 2.4.0 | https://codeberg.org/soundtouch/soundtouch.git | export GIT_SOUNDTOUCH_UPSTREAM=git@xx:yy/soundtouch.git | +| unibreak | 6.1 | https://github.com/adah1972/libunibreak.git | export GIT_UNIBREAK_UPSTREAM=git@xx:yy/libunibreak.git | +| uavs3d | 1.2.1 | https://github.com/uavs3/uavs3d.git | export GIT_UAVS3D_UPSTREAM=git@xx:yy/UAVS3D.git | +| xml2 | 2.15.1 | https://github.com/GNOME/libxml2.git | export GIT_FONTCONFIG_UPSTREAM=git@xx:yy/fontconfig.git | +| yuv | main-f94b8cf7 | https://github.com/debugly/libyuv.git | export GIT_YUV_UPSTREAM=git@xx:yy/yuv.git | +| webp | v1.6.0 | https://github.com/debugly/libwebp.git | export GIT_WEBP_UPSTREAM=git@xx:yy/webp.git | +| placebo | 7.349.0 | https://github.com/haasn/libplacebo.git | export GIT_LIBPLACEBO_UPSTREAM=git@xx:yy/libplacebo.git | +| shaderc | 2025.1 | https://github.com/google/shaderc.git | export GIT_SHADERC_UPSTREAM=git@xx:yy/shaderc.git | +| moltenvk | 1.3.1 | https://github.com/KhronosGroup/MoltenVK.git | export GIT_MOLTENVK_UPSTREAM=git@xx:yy/MoltenVK.git | +| lcms2 | 2.16 | https://github.com/mm2/Little-CMS.git | export GIT_LCMS2_UPSTREAM=git@xx:yy/Little-CMS.git | +| dovi | 1.6.79 | https://github.com/AMDEXA/libdovi.git | export GIT_DOVI_UPSTREAM=git@xx:yy/libdovi.git | + +## 提示 + +- 要下载预编译的 xcframework 库,使用 install 命令时添加 --fmwk 参数 +- 初始化时要跳过拉取远程仓库,使用 init 命令时添加 --skip-pull-base 参数 +- 目前 FFmpeg 使用 module-full.sh 配置,功能全但同时导致包体积略大 +- 可以将所有预编译的 GitHub 库下载到自己的服务器,并在运行 install 命令前通过 MR\_DOWNLOAD\_BASEURL 指定你的服务器地址 diff --git a/configs/ffconfig/auto-detect-third-libs.sh b/configs/ffconfig/auto-detect-third-libs.sh index 29f773e63..0acbfbb47 100644 --- a/configs/ffconfig/auto-detect-third-libs.sh +++ b/configs/ffconfig/auto-detect-third-libs.sh @@ -22,6 +22,7 @@ # pkg-config --libs dav1d # pkg-config --cflags --libs libbluray + THIRD_CFG_FLAGS= # echo "----------------------" @@ -57,6 +58,62 @@ THIRD_CFG_FLAGS= # echo "[❌] --disable-libmp3lame" # fi + +# Function to compare two version numbers +# Returns: +# 1 if v1 == v2 or if v1 > v2 +# 0 if v1 < v2 +gt_or_equal() { + local v1=$1 + local v2=$2 + local IFS=. + # 将版本字符串按 "." 分割成数组 + # 例如 "7..1" 会变成 (7 "" 1) + local v1_parts=($v1) v2_parts=($v2) + local i + + # 确定两个版本号数组中的最大长度 + local len_v1=${#v1_parts[@]} + local len_v2=${#v2_parts[@]} + local max_len=$((len_v1 > len_v2 ? len_v1 : len_v2)) + + for ((i=0; i<$max_len; i++)); do + # 获取版本号的对应部分,如果部分缺失或为空字符串,则默认为 "0" + local p1_val=${v1_parts[i]:-0} + local p2_val=${v2_parts[i]:-0} + + # 进一步确保 p1_val 和 p2_val 是纯数字;如果不是,则视为 0 + # 这能处理像 "7.a.1" 这样包含非数字部分的情况 + if ! [[ "$p1_val" =~ ^[0-9]+$ ]]; then + p1_val=0 + fi + if ! [[ "$p2_val" =~ ^[0-9]+$ ]]; then + p2_val=0 + fi + + # 进行数字比较 + # 使用 10# 前缀确保按十进制处理 (例如 "08" 被视为 8 而不是八进制) + if ((10#$p1_val > 10#$p2_val)); then + echo 1 # v1 > v2 + return 0 + fi + if ((10#$p1_val < 10#$p2_val)); then + echo # v1 < v2 + return 0 + fi + done + + echo 1 # v1 == v2 (所有部分都相等) + return 0 +} + +has_feature() { + local feature=$1 + $MR_BUILD_SOURCE/configure --help | grep -q -- "$feature" && enable_feature=1 || enable_feature=0 + echo $enable_feature + return 0 +} + echo "----------------------" # use pkg-config fix ff4.0--ijk0.8.8--20210426--001 use openssl 1_1_1m occur can't find openssl error. @@ -81,32 +138,53 @@ else fi echo "----------------------" + # FFmpeg 4.2 支持AV1、AVS2等格式 # dav1d由VideoLAN,VLC和FFmpeg联合开发,项目由AOM联盟赞助,和libaom相比,dav1d性能普遍提升100%,最高提升400% -#just wait videotoolbox support decode av1 -# THIRD_CFG_FLAGS="$THIRD_CFG_FLAGS --enable-decoder=av1" -pkg-config --libs dav1d --silence-errors >/dev/null && enable_dav1d=1 +result=$(gt_or_equal "$GIT_REPO_VERSION" "4.2") +if [[ $result ]]; then + + pkg-config --libs dav1d --silence-errors >/dev/null && enable_dav1d=1 + + if [[ $enable_dav1d ]];then + echo "[✅] --enable-libdav1d : $(pkg-config --modversion dav1d)" + THIRD_CFG_FLAGS="$THIRD_CFG_FLAGS --enable-libdav1d --enable-decoder=libdav1d" + else + echo "[❌] --disable-libdav1d --disable-decoder=libdav1d" + fi + echo "----------------------" +fi -if [[ $enable_dav1d ]];then - echo "[✅] --enable-libdav1d : $(pkg-config --modversion dav1d)" - THIRD_CFG_FLAGS="$THIRD_CFG_FLAGS --enable-libdav1d --enable-decoder=libdav1d" +#从FFmpeg7.1.1开始支持硬解av1,苹果需要M3芯片 +result=$(gt_or_equal "$GIT_REPO_VERSION" "7.1.1") +if [[ $result ]]; then + echo "[✅] --enable hw av1 decoder" + THIRD_CFG_FLAGS="$THIRD_CFG_FLAGS --enable-decoder=av1" else - echo "[❌] --disable-libdav1d --disable-decoder=libdav1d" + echo "[❌] --disable hw av1 decoder" fi echo "----------------------" -pkg-config --libs libsmb2 --silence-errors >/dev/null && enable_smb2=1 +# 从6开始支持的 smb2 协议 +result=$(gt_or_equal "$GIT_REPO_VERSION" "6") +if [[ $result ]]; then + pkg-config --libs libsmb2 --silence-errors >/dev/null && enable_smb2=1 -if [[ $enable_smb2 ]];then - echo "[✅] --enable-libsmb2 : $(pkg-config --modversion libsmb2)" - THIRD_CFG_FLAGS="$THIRD_CFG_FLAGS --enable-libsmb2 --enable-protocol=libsmb2" -else - echo "[❌] --disable-libsmb2 --disable-protocol=libsmb2" -fi + if [[ $enable_smb2 ]];then + echo "[✅] --enable-libsmb2 : $(pkg-config --modversion libsmb2)" + THIRD_CFG_FLAGS="$THIRD_CFG_FLAGS --enable-libsmb2 --enable-protocol=libsmb2" + else + echo "[❌] --disable-libsmb2 --disable-protocol=libsmb2" + fi -echo "----------------------" + echo "----------------------" + + echo "[✅] --enable-parser=av3a" + THIRD_CFG_FLAGS="$THIRD_CFG_FLAGS --enable-parser=av3a --enable-demuxer=av3a" + echo "----------------------" +fi pkg-config --libs libbluray --silence-errors >/dev/null && enable_bluray=1 @@ -116,29 +194,54 @@ if [[ $enable_bluray ]];then else echo "[❌] --disable-libbluray --disable-protocol=bluray" fi -echo "----------------------" -pkg-config --libs dvdread --silence-errors >/dev/null && enable_dvdread=1 +echo "----------------------" -if [[ $enable_dvdread ]];then - echo "[✅] --enable-libdvdread : $(pkg-config --modversion dvdread)" - THIRD_CFG_FLAGS="$THIRD_CFG_FLAGS --enable-libdvdread --enable-protocol=dvd" +#不确定7代之前的版本是否支持dvdvideo +result=$(gt_or_equal "$GIT_REPO_VERSION" "7.1.1") +if [[ $result ]]; then + pkg-config --libs dvdread --silence-errors >/dev/null && enable_dvdread=1 + pkg-config --libs dvdnav --silence-errors >/dev/null && enable_dvdnav=1 + if [[ $enable_dvdread && $enable_dvdnav ]];then + echo "[✅] --enable-demuxer=dvdvideo --enable-gpl --enable-libdvdread : $(pkg-config --modversion dvdread)" + #libdvdread is gpl and --enable-gpl is not specified. + THIRD_CFG_FLAGS="$THIRD_CFG_FLAGS --enable-libdvdread --enable-libdvdnav --enable-demuxer=dvdvideo --enable-gpl" + else + echo "[❌] --disable-dvdvideo" + fi + echo "----------------------" else - echo "[❌] --disable-libdvdread --disable-protocol=dvd" + dvd_feature=$(has_feature "libdvdread") + if [[ $dvd_feature ]]; then + pkg-config --libs dvdread --silence-errors >/dev/null && enable_dvdread=1 + if [[ $enable_dvdread ]];then + echo "[✅] --enable-libdvdread : $(pkg-config --modversion dvdread)" + THIRD_CFG_FLAGS="$THIRD_CFG_FLAGS --enable-libdvdread --enable-protocol=dvd" + else + echo "[❌] --disable-dvd protocol" + fi + echo "----------------------" + fi fi -echo "----------------------" +result=$(gt_or_equal "$GIT_REPO_VERSION" "5") +if [[ $result ]]; then + pkg-config --libs uavs3d --silence-errors >/dev/null && enable_uavs3d=1 -pkg-config --libs uavs3d --silence-errors >/dev/null && enable_uavs3d=1 + if [[ $enable_uavs3d ]];then + echo "[✅] --enable-libuavs3d : $(pkg-config --modversion uavs3d)" + THIRD_CFG_FLAGS="$THIRD_CFG_FLAGS --enable-libuavs3d --enable-decoder=libuavs3d" + else + echo "[❌] --disable-libuavs3d --disable-decoder=libuavs3d" + fi -if [[ $enable_uavs3d ]];then - echo "[✅] --enable-libuavs3d : $(pkg-config --modversion uavs3d)" - THIRD_CFG_FLAGS="$THIRD_CFG_FLAGS --enable-libuavs3d --enable-decoder=libuavs3d" -else - echo "[❌] --disable-libuavs3d --disable-decoder=libuavs3d" + echo "----------------------" fi -echo "----------------------" +# only for ffmpeg 8.1.1 need disable postproc module +if [[ "8.1.1" == "$GIT_REPO_VERSION" ]]; then + THIRD_CFG_FLAGS="$THIRD_CFG_FLAGS --disable-postproc" +fi pkg-config --libs libxml-2.0 --silence-errors >/dev/null && enable_xml2=1 @@ -151,6 +254,20 @@ fi echo "----------------------" +result=$(gt_or_equal "$GIT_REPO_VERSION" "7") +if [[ $result && $MR_PLAT != 'android' ]]; then + enable_webp= + pkg-config --libs libwebp --silence-errors >/dev/null && enable_webp=1 + if [[ $enable_webp ]];then + echo "[✅] --enable-libwebp --enable-decoder=webp : $(pkg-config --modversion libwebp)" + THIRD_CFG_FLAGS="$THIRD_CFG_FLAGS --enable-libwebp --enable-demuxer=webp --enable-decoder=libwebp" + else + echo "[❌] --disable-libwebp --disable-decoder=libwebp" + THIRD_CFG_FLAGS="$THIRD_CFG_FLAGS --disable-libwebp --disable-demuxer=webp --disable-decoder=libwebp" + fi + echo "----------------------" +fi + # export PKG_CONFIG_LIBDIR=$PKG_CONFIG_LIBDIR:/opt/homebrew/Cellar/shaderc/2024.0/lib/pkgconfig:/opt/homebrew/Cellar/little-cms2/2.16/lib/pkgconfig # pkg-config --libs libplacebo --silence-errors >/dev/null && enable_placebo=1 @@ -171,9 +288,6 @@ echo "----------------------" # echo "[❌] --disable-decoder=av3a" # fi -echo "[✅] --enable-parser=av3a" -THIRD_CFG_FLAGS="$THIRD_CFG_FLAGS --enable-parser=av3a --enable-demuxer=av3a" -echo "----------------------" # -------------------------------------------------------------- THIRD_CFG_FLAGS="$THIRD_CFG_FLAGS --pkg-config-flags=--static" diff --git a/configs/ffconfig/module-default.sh b/configs/ffconfig/module-default.sh index 9f02c4e43..3f6403d6a 100755 --- a/configs/ffconfig/module-default.sh +++ b/configs/ffconfig/module-default.sh @@ -39,9 +39,9 @@ export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-avformat" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-avutil" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-swresample" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-swscale" -export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-postproc" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-avfilter" -export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-avresample" +#ffmpeg 5 has no avresample lib. +# export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-avresample" # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-pthreads" # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-w32threads" # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-os2threads" diff --git a/configs/ffconfig/module-full.sh b/configs/ffconfig/module-full.sh index 38d24e72f..7fbfae1a2 100755 --- a/configs/ffconfig/module-full.sh +++ b/configs/ffconfig/module-full.sh @@ -41,7 +41,6 @@ export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-swresample" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-swscale" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-avfilter" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-avdevice" -export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-postproc" #ffmpeg 5 has no avresample lib. # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-avresample" # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-pthreads" @@ -202,6 +201,8 @@ export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=adpcm_ea_r2" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=adpcm_ea_r3" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=adpcm_ea_xas" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=adpcm_g726" +export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=adpcm_g726le" +export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=adpcm_g726be" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=adpcm_ima_amv" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=adpcm_ima_apc" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=adpcm_ima_dat4" @@ -334,19 +335,15 @@ export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=srt" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=vobsub" # ./configure --list-muxers export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-muxers" -# mp4 muxer depends below: export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-muxer=mov" -export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-bsf=vp9_superframe" -export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-bsf=aac_adtstoasc" -export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-parser=ac3" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-muxer=mp4" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-muxer=gif" -# export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-muxer=flv" -# export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-muxer=mkv" -# export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-muxer=webm" -# export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-muxer=avi" +export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-muxer=flv" +export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-muxer=matroska" +export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-muxer=avi" +export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-muxer=webm" + # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-muxer=rm" -# export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-muxer=mov" # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-muxer=mpegts" # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-muxer=image2" # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-muxer=asf" @@ -402,22 +399,22 @@ export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=dsf" #for .dsf # ./configure --list-bsf export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-bsfs" -export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=chomp" -export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=dca_core" -export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=dump_extradata" -export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=imx_dump_header" -export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=mjpeg2jpeg" -export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=mjpega_dump_header" -export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=mov2textsub" -export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=mp3_header_decompress" -export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=mpeg4_unpack_bframes" -export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=noise" -export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=remove_extradata" -export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=text2movsub" +# export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=chomp" +# export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=dca_core" +# export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=dump_extradata" +# export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=imx_dump_header" +# export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=mjpeg2jpeg" +# export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=mjpega_dump_header" +# export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=mov2textsub" +# export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=mp3_header_decompress" +# export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=mpeg4_unpack_bframes" +# export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=noise" +# export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=remove_extradata" +# export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=text2movsub" # Undefined symbols for architecture x86_64: # "_ff_ac3_parse_header", referenced from: # _eac3_core_filter in libavcodec.a(eac3_core_bsf.o) -export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=eac3_core" +# export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=eac3_core" # ./configure --list-protocols export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocols" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=async" @@ -426,8 +423,8 @@ export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=rtmp*" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=rtp" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=srtp" -export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=concat" -export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=crypto" +export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=concat" +export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=crypto" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=ffrtmpcrypt" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=ffrtmphttp" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=gopher" @@ -444,6 +441,8 @@ export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=unix" # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-devices" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-filters" +# transpose_vt available macos(13.0), ios(16.0), tvos(16.0), visionos(1.0) +export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-filter=transpose_vt" # External library support: export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-audiotoolbox" diff --git a/configs/ffconfig/module-lite-hevc.sh b/configs/ffconfig/module-lite-hevc.sh index 1c30d2248..1999e0ae7 100755 --- a/configs/ffconfig/module-lite-hevc.sh +++ b/configs/ffconfig/module-lite-hevc.sh @@ -39,9 +39,9 @@ export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-avformat" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-avutil" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-swresample" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-swscale" -export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-postproc" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-avfilter" -export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-avresample" +#ffmpeg 5 has no avresample lib. +# export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-avresample" # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-pthreads" # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-w32threads" # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-os2threads" diff --git a/configs/ffconfig/module-lite.sh b/configs/ffconfig/module-lite.sh index b839e10ca..7627f1cf2 100755 --- a/configs/ffconfig/module-lite.sh +++ b/configs/ffconfig/module-lite.sh @@ -39,9 +39,9 @@ export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-avformat" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-avutil" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-swresample" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-swscale" -export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-postproc" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-avfilter" -export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-avresample" +#ffmpeg 5 has no avresample lib. +# export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-avresample" # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-pthreads" # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-w32threads" # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-os2threads" diff --git a/configs/ffconfig/module.sh b/configs/ffconfig/module.sh deleted file mode 120000 index 7e9493d70..000000000 --- a/configs/ffconfig/module.sh +++ /dev/null @@ -1 +0,0 @@ -module-full.sh \ No newline at end of file diff --git a/configs/ijk-ffmpeg-config/module-lite.sh b/configs/ijk-ffmpeg-config/module-lite.sh index 6dbecfac2..82556ab57 100755 --- a/configs/ijk-ffmpeg-config/module-lite.sh +++ b/configs/ijk-ffmpeg-config/module-lite.sh @@ -79,7 +79,8 @@ export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=flac" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=hevc" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=vp8" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=vp9" - +#117 +export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=pcm_s16le --enable-decoder=pcm_s16be --enable-decoder=pcm_u16le --enable-decoder=pcm_u16be --enable-decoder=pcm_alaw --enable-decoder=pcm_mulaw" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-hwaccels" # ./configure --list-muxers @@ -102,6 +103,7 @@ export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=mpegvideo" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=flac" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=hevc" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=webm_dash_manifest" +export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=rtsp" # ./configure --list-parsers export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-parsers" @@ -135,7 +137,7 @@ export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocols" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=async" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=bluray" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=concat" -export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=crypto" +export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=crypto" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=ffrtmpcrypt" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=ffrtmphttp" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=gopher" @@ -148,9 +150,9 @@ export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=mmst" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=rtmp*" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=rtmp" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=rtmpt" -export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=rtp" +export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=rtp" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=sctp" -export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=srtp" +export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=srtp" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=subfile" export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=unix" diff --git a/configs/ijk-ffmpeg-config/module.sh b/configs/ijk-ffmpeg-config/module.sh deleted file mode 120000 index 0f16f539b..000000000 --- a/configs/ijk-ffmpeg-config/module.sh +++ /dev/null @@ -1 +0,0 @@ -module-lite.sh \ No newline at end of file diff --git a/configs/libs/ass.sh b/configs/libs/ass.sh index 15bd7918e..c9a7e266a 100644 --- a/configs/libs/ass.sh +++ b/configs/libs/ass.sh @@ -26,9 +26,9 @@ export LIB_NAME='ass' export LIPO_LIBS="libass" export LIB_DEPENDS_BIN="meson cmake pkg-config" export GIT_LOCAL_REPO=extra/ass -export GIT_COMMIT=0.17.3 +export GIT_COMMIT=0.17.4 export REPO_DIR=ass -export GIT_REPO_VERSION=0.17.3 +export GIT_REPO_VERSION=0.17.4 # you can export GIT_ASS_UPSTREAM=git@xx:yy/ASS.git use your mirror if [[ "$GIT_ASS_UPSTREAM" != "" ]] ;then @@ -38,8 +38,7 @@ else fi # pre compiled -export PRE_COMPILE_TAG=ass-0.17.3-250226075040 -export PRE_COMPILE_TAG_TVOS=ass-0.17.3-250226211935 -export PRE_COMPILE_TAG_MACOS=ass-0.17.3-250226205926 -export PRE_COMPILE_TAG_IOS=ass-0.17.3-250226175156 -export PRE_COMPILE_TAG_ANDROID=ass-0.17.3-250310112227 +export PRE_COMPILE_TAG_TVOS=ass-0.17.4-260408155409 +export PRE_COMPILE_TAG_MACOS=ass-0.17.4-260408155409 +export PRE_COMPILE_TAG_IOS=ass-0.17.4-260408155409 +export PRE_COMPILE_TAG_ANDROID=ass-0.17.4-260408155409 diff --git a/configs/libs/bluray.sh b/configs/libs/bluray.sh index 000ea02c5..0f6861fbb 100644 --- a/configs/libs/bluray.sh +++ b/configs/libs/bluray.sh @@ -30,6 +30,7 @@ export GIT_COMMIT=1.3.4 export GIT_WITH_SUBMODULE=1 export REPO_DIR=bluray export GIT_REPO_VERSION=1.3.4 +export PATCH_DIR=../../patches/bluray # you can export GIT_BLURAY_UPSTREAM=git@xx:yy/libbluray.git use your mirror if [[ "$GIT_BLURAY_UPSTREAM" != "" ]] ;then @@ -39,8 +40,7 @@ else fi # pre compiled -export PRE_COMPILE_TAG=bluray-1.3.4-250226080029 -export PRE_COMPILE_TAG_TVOS=bluray-1.3.4-250226212622 -export PRE_COMPILE_TAG_MACOS=bluray-1.3.4-250226210447 -export PRE_COMPILE_TAG_IOS=bluray-1.3.4-250226175920 -export PRE_COMPILE_TAG_ANDROID=bluray-1.3.4-250310112732 +export PRE_COMPILE_TAG_TVOS=bluray-1.3.4-260509162704 +export PRE_COMPILE_TAG_MACOS=bluray-1.3.4-260509162704 +export PRE_COMPILE_TAG_IOS=bluray-1.3.4-260509162704 +export PRE_COMPILE_TAG_ANDROID=bluray-1.3.4-260408145206 diff --git a/configs/libs/dav1d.sh b/configs/libs/dav1d.sh index 07232d233..e54a7d966 100644 --- a/configs/libs/dav1d.sh +++ b/configs/libs/dav1d.sh @@ -20,10 +20,10 @@ export LIB_NAME='dav1d' export LIPO_LIBS="libdav1d" export LIB_DEPENDS_BIN="meson ninja nasm" export GIT_LOCAL_REPO=extra/dav1d -export GIT_COMMIT=1.5.1 +export GIT_COMMIT=1.5.3 export GIT_WITH_SUBMODULE=0 export REPO_DIR=dav1d -export GIT_REPO_VERSION=1.5.1 +export GIT_REPO_VERSION=1.5.3 # you can export GIT_DAV1D_UPSTREAM=git@xx:yy/dav1d.git use your mirror if [[ "$GIT_DAV1D_UPSTREAM" != "" ]] ;then @@ -33,8 +33,7 @@ else fi # pre compiled -export PRE_COMPILE_TAG=dav1d-1.5.1-250226080235 -export PRE_COMPILE_TAG_TVOS=dav1d-1.5.1-250226212720 -export PRE_COMPILE_TAG_MACOS=dav1d-1.5.1-250226210526 -export PRE_COMPILE_TAG_IOS=dav1d-1.5.1-250226180013 -export PRE_COMPILE_TAG_ANDROID=dav1d-1.5.1-250310112840 +export PRE_COMPILE_TAG_TVOS=dav1d-1.5.3-260228142936 +export PRE_COMPILE_TAG_MACOS=dav1d-1.5.3-260228142936 +export PRE_COMPILE_TAG_IOS=dav1d-1.5.3-260228142936 +export PRE_COMPILE_TAG_ANDROID=dav1d-1.5.3-260228142936 diff --git a/configs/libs/dovi.sh b/configs/libs/dovi.sh new file mode 100644 index 000000000..56aff043d --- /dev/null +++ b/configs/libs/dovi.sh @@ -0,0 +1,33 @@ +#! /usr/bin/env bash +# +# Copyright (C) 2021 Matt Reach + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +export LIB_NAME='dovi' +export LIPO_LIBS="libdovi" +export LIB_DEPENDS_BIN="rustup cargo" +export GIT_LOCAL_REPO=extra/dovi +export GIT_COMMIT=libdovi-3.3.2 +export GIT_WITH_SUBMODULE=0 +export REPO_DIR=dovi +export GIT_REPO_VERSION=3.3.2 + +if [[ "$GIT_DOVI_UPSTREAM" != "" ]] ;then + export GIT_UPSTREAM="$GIT_DOVI_UPSTREAM" +else + export GIT_UPSTREAM=https://github.com/quietvoid/dovi_tool.git +fi + +export PRE_COMPILE_TAG_MACOS=dovi-3.3.2-260326175522 \ No newline at end of file diff --git a/configs/libs/dvdnav.sh b/configs/libs/dvdnav.sh new file mode 100644 index 000000000..ddb0dcc97 --- /dev/null +++ b/configs/libs/dvdnav.sh @@ -0,0 +1,44 @@ +#! /usr/bin/env bash +# +# Copyright (C) 2021 Matt Reach + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# +# brew install nasm +# If you really want to compile without asm, configure with --disable-asm. + +# LIB_DEPENDS_BIN using string because bash can't export array chttps://stackoverflow.com/questions/5564418/exporting-an-array-in-bash-script +# configure: error: Package requirements (openssl) were not met + +export LIB_NAME='dvdnav' +export LIPO_LIBS="libdvdnav" +export LIB_DEPENDS_BIN="automake autoconf libtool" +export REPO_DIR=dvdnav +export GIT_LOCAL_REPO=extra/$REPO_DIR +export GIT_COMMIT=9831fe01 +export GIT_REPO_VERSION=9831fe01 + +# you can export GIT_DVDNAV_UPSTREAM=git@xx:yy/opusfile.git use your mirror +if [[ "$GIT_DVDNAV_UPSTREAM" != "" ]] ;then + export GIT_UPSTREAM="$GIT_DVDNAV_UPSTREAM" +else + export GIT_UPSTREAM=https://code.videolan.org/videolan/libdvdnav.git +fi + +# pre compiled +export PRE_COMPILE_TAG_TVOS=dvdnav-9831fe01-260228142335 +export PRE_COMPILE_TAG_MACOS=dvdnav-9831fe01-260228142335 +export PRE_COMPILE_TAG_IOS=dvdnav-9831fe01-260228142335 +export PRE_COMPILE_TAG_ANDROID=dvdnav-9831fe01-260228142335 diff --git a/configs/libs/dvdread.sh b/configs/libs/dvdread.sh index 573cbd068..dc2b684fe 100644 --- a/configs/libs/dvdread.sh +++ b/configs/libs/dvdread.sh @@ -38,8 +38,7 @@ else fi # pre compiled -export PRE_COMPILE_TAG=dvdread-6.1.3-250226075913 -export PRE_COMPILE_TAG_TVOS=dvdread-6.1.3-250226212540 -export PRE_COMPILE_TAG_MACOS=dvdread-6.1.3-250226210420 -export PRE_COMPILE_TAG_IOS=dvdread-6.1.3-250226175845 -export PRE_COMPILE_TAG_ANDROID=dvdread-6.1.3-250310112649 +export PRE_COMPILE_TAG_TVOS=dvdread-6.1.3-260228142110 +export PRE_COMPILE_TAG_MACOS=dvdread-6.1.3-260228142110 +export PRE_COMPILE_TAG_IOS=dvdread-6.1.3-260228142110 +export PRE_COMPILE_TAG_ANDROID=dvdread-6.1.3-260228142110 diff --git a/configs/libs/ffmpeg.sh b/configs/libs/ffmpeg.sh deleted file mode 100644 index bc883d81d..000000000 --- a/configs/libs/ffmpeg.sh +++ /dev/null @@ -1,48 +0,0 @@ -#! /usr/bin/env bash -# -# Copyright (C) 2021 Matt Reach - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# -# brew install nasm -# If you really want to compile without asm, configure with --disable-asm. - -export LIB_NAME='ffmpeg' -export LIPO_LIBS="libavcodec libavformat libavutil libswscale libswresample libavfilter libavdevice" -export LIB_DEPENDS_BIN="nasm pkg-config" -export GIT_LOCAL_REPO=extra/ffmpeg -export REPO_DIR=ffmpeg - -# you can export GIT_FFMPEG_UPSTREAM=git@xx:yy/FFmpeg.git use your mirror -if [[ "$GIT_FFMPEG_UPSTREAM" != "" ]] ;then - export GIT_UPSTREAM="$GIT_FFMPEG_UPSTREAM" -else - export GIT_UPSTREAM=https://github.com/FFmpeg/FFmpeg.git -fi - -if [[ "$GIT_FFMPEG_COMMIT" != "" ]] ;then - export GIT_COMMIT="$GIT_FFMPEG_COMMIT" - export GIT_REPO_VERSION="$GIT_FFMPEG_COMMIT" -else - export GIT_COMMIT=n6.1.2 #origin/release/5.1 - export GIT_REPO_VERSION=6.1.2 -fi - -# pre compiled -export PRE_COMPILE_TAG=ffmpeg-6.1.2-250227145407 -export PRE_COMPILE_TAG_TVOS=ffmpeg-6.1.2-250314152040 -export PRE_COMPILE_TAG_MACOS=ffmpeg-6.1.2-250314174518 -export PRE_COMPILE_TAG_IOS=ffmpeg-6.1.2-250314152040 -export PRE_COMPILE_TAG_ANDROID=ffmpeg-6.1.2-250310113110 - diff --git a/configs/libs/ffmpeg.sh b/configs/libs/ffmpeg.sh new file mode 120000 index 000000000..d35f71de2 --- /dev/null +++ b/configs/libs/ffmpeg.sh @@ -0,0 +1 @@ +ffmpeg7.sh \ No newline at end of file diff --git a/configs/libs/ffmpeg4.sh b/configs/libs/ffmpeg4.sh new file mode 100644 index 000000000..90215e1b3 --- /dev/null +++ b/configs/libs/ffmpeg4.sh @@ -0,0 +1,42 @@ +#! /usr/bin/env bash +# +# Copyright (C) 2021 Matt Reach + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# brew install nasm +# If you really want to compile without asm, configure with --disable-asm. + +export LIB_NAME='ffmpeg' +export LIPO_LIBS="libavcodec libavformat libavutil libswscale libswresample libavfilter libavdevice" +export LIB_DEPENDS_BIN="nasm pkg-config" +export GIT_LOCAL_REPO=extra/ffmpeg +export REPO_DIR=ffmpeg4 +export PATCH_DIR=../../patches/ffmpeg-n4.0 + +# you can export GIT_FFMPEG_UPSTREAM=git@xx:yy/FFmpeg.git use your mirror +if [[ "$GIT_FFMPEG_UPSTREAM" != "" ]] ;then + export GIT_UPSTREAM="$GIT_FFMPEG_UPSTREAM" +else + export GIT_UPSTREAM=https://github.com/FFmpeg/FFmpeg.git +fi + +export GIT_COMMIT=n4.0.5 +export GIT_REPO_VERSION=4.0.5 + +# pre compiled +export PRE_COMPILE_TAG_TVOS=ffmpeg4-4.0.5-260313191646 +export PRE_COMPILE_TAG_MACOS=ffmpeg4-4.0.5-260313191646 +export PRE_COMPILE_TAG_IOS=ffmpeg4-4.0.5-260313191646 +export PRE_COMPILE_TAG_ANDROID=ffmpeg4-4.0.5-260313191646 \ No newline at end of file diff --git a/configs/libs/ffmpeg5.sh b/configs/libs/ffmpeg5.sh new file mode 100644 index 000000000..697e44c19 --- /dev/null +++ b/configs/libs/ffmpeg5.sh @@ -0,0 +1,42 @@ +#! /usr/bin/env bash +# +# Copyright (C) 2021 Matt Reach + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# brew install nasm +# If you really want to compile without asm, configure with --disable-asm. + +export LIB_NAME='ffmpeg' +export LIPO_LIBS="libavcodec libavformat libavutil libswscale libswresample libavfilter libavdevice" +export LIB_DEPENDS_BIN="nasm pkg-config" +export GIT_LOCAL_REPO=extra/ffmpeg +export REPO_DIR=ffmpeg5 +export PATCH_DIR=../../patches/ffmpeg-n5.1 + +# you can export GIT_FFMPEG_UPSTREAM=git@xx:yy/FFmpeg.git use your mirror +if [[ "$GIT_FFMPEG_UPSTREAM" != "" ]] ;then + export GIT_UPSTREAM="$GIT_FFMPEG_UPSTREAM" +else + export GIT_UPSTREAM=https://github.com/FFmpeg/FFmpeg.git +fi + +export GIT_COMMIT=n5.1.6 +export GIT_REPO_VERSION=5.1.6 + +# pre compiled +export PRE_COMPILE_TAG_TVOS=ffmpeg5-5.1.6-260313193857 +export PRE_COMPILE_TAG_MACOS=ffmpeg5-5.1.6-260313193857 +export PRE_COMPILE_TAG_IOS=ffmpeg5-5.1.6-260313193857 +export PRE_COMPILE_TAG_ANDROID=ffmpeg5-5.1.6-260313193857 \ No newline at end of file diff --git a/configs/libs/ffmpeg6.sh b/configs/libs/ffmpeg6.sh new file mode 100644 index 000000000..9cbcb2031 --- /dev/null +++ b/configs/libs/ffmpeg6.sh @@ -0,0 +1,42 @@ +#! /usr/bin/env bash +# +# Copyright (C) 2021 Matt Reach + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# brew install nasm +# If you really want to compile without asm, configure with --disable-asm. + +export LIB_NAME='ffmpeg' +export LIPO_LIBS="libavcodec libavformat libavutil libswscale libswresample libavfilter libavdevice" +export LIB_DEPENDS_BIN="nasm pkg-config" +export GIT_LOCAL_REPO=extra/ffmpeg +export REPO_DIR=ffmpeg6 +export PATCH_DIR=../../patches/ffmpeg-n6.1 + +# you can export GIT_FFMPEG_UPSTREAM=git@xx:yy/FFmpeg.git use your mirror +if [[ "$GIT_FFMPEG_UPSTREAM" != "" ]] ;then + export GIT_UPSTREAM="$GIT_FFMPEG_UPSTREAM" +else + export GIT_UPSTREAM=https://github.com/FFmpeg/FFmpeg.git +fi + +export GIT_COMMIT=n6.1.1 #origin/release/5.1 +export GIT_REPO_VERSION=6.1.1 + +# pre compiled +export PRE_COMPILE_TAG_TVOS=ffmpeg6-6.1.1-260313200314 +export PRE_COMPILE_TAG_MACOS=ffmpeg6-6.1.1-260313200314 +export PRE_COMPILE_TAG_IOS=ffmpeg6-6.1.1-260313200314 +export PRE_COMPILE_TAG_ANDROID=ffmpeg6-6.1.1-260313200314 \ No newline at end of file diff --git a/configs/libs/ffmpeg7.sh b/configs/libs/ffmpeg7.sh new file mode 100644 index 000000000..7520fbd40 --- /dev/null +++ b/configs/libs/ffmpeg7.sh @@ -0,0 +1,44 @@ +#! /usr/bin/env bash +# +# Copyright (C) 2021 Matt Reach + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# brew install nasm +# If you really want to compile without asm, configure with --disable-asm. + +export LIB_NAME='ffmpeg' +export LIPO_LIBS="libavcodec libavformat libavutil libswscale libswresample libavfilter libavdevice libpostproc" +export LIB_DEPENDS_BIN="nasm pkg-config" +export GIT_LOCAL_REPO=extra/ffmpeg +export REPO_DIR=ffmpeg7 +export PATCH_DIR=../../patches/ffmpeg-n7.1.1 + +# you can export GIT_FFMPEG_UPSTREAM=git@xx:yy/FFmpeg.git use your mirror +if [[ "$GIT_FFMPEG_UPSTREAM" != "" ]] ;then + export GIT_UPSTREAM="$GIT_FFMPEG_UPSTREAM" +else + export GIT_UPSTREAM=https://github.com/FFmpeg/FFmpeg.git +fi + +export GIT_COMMIT=n7.1.3 #origin/release/5.1 +export GIT_REPO_VERSION=7.1.3 + +# pre compiled + +export PRE_COMPILE_TAG_TVOS=ffmpeg7-7.1.3-260512163026 +export PRE_COMPILE_TAG_MACOS=ffmpeg7-7.1.3-260512163026 +export PRE_COMPILE_TAG_IOS=ffmpeg7-7.1.3-260512163026 + +export PRE_COMPILE_TAG_ANDROID=ffmpeg7-7.1.1-250606143631 diff --git a/configs/libs/ffmpeg8.sh b/configs/libs/ffmpeg8.sh new file mode 100644 index 000000000..953fd6d71 --- /dev/null +++ b/configs/libs/ffmpeg8.sh @@ -0,0 +1,41 @@ +#! /usr/bin/env bash +# +# Copyright (C) 2021 Matt Reach + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# brew install nasm +# If you really want to compile without asm, configure with --disable-asm. + +export LIB_NAME='ffmpeg' +export LIPO_LIBS="libavcodec libavformat libavutil libswscale libswresample libavfilter libavdevice" +export LIB_DEPENDS_BIN="nasm pkg-config" +export GIT_LOCAL_REPO=extra/ffmpeg +export REPO_DIR=ffmpeg8 +export PATCH_DIR=../../patches/ffmpeg-n8.1.2 +export GIT_COMMIT=n8.1.2 +export GIT_REPO_VERSION=8.1.2 + +# you can export GIT_FFMPEG_UPSTREAM=git@xx:yy/FFmpeg.git use your mirror +if [[ "$GIT_FFMPEG_UPSTREAM" != "" ]] ;then + export GIT_UPSTREAM="$GIT_FFMPEG_UPSTREAM" +else + export GIT_UPSTREAM=https://github.com/FFmpeg/FFmpeg.git +fi + + +# pre compiled +export PRE_COMPILE_TAG_IOS=ffmpeg8-8.1.2-260625152857 +export PRE_COMPILE_TAG_MACOS=ffmpeg8-8.1.2-260625152857 +export PRE_COMPILE_TAG_TVOS=ffmpeg8-8.1.2-260625152857 diff --git a/configs/libs/fftutorial.sh b/configs/libs/fftutorial.sh new file mode 100644 index 000000000..02c822c88 --- /dev/null +++ b/configs/libs/fftutorial.sh @@ -0,0 +1,43 @@ +#! /usr/bin/env bash +# +# Copyright (C) 2021 Matt Reach + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# brew install nasm +# If you really want to compile without asm, configure with --disable-asm. + +export LIB_NAME='fftutorial' +export LIPO_LIBS="libavcodec libavformat libavutil libswscale libswresample" +export LIB_DEPENDS_BIN="nasm pkg-config" +export GIT_LOCAL_REPO=extra/ffmpeg +export REPO_DIR=ffmpeg +export PATCH_DIR=../../patches/fftutorial + +# you can export GIT_FFMPEG_UPSTREAM=git@xx:yy/FFmpeg.git use your mirror +if [[ "$GIT_FFMPEG_UPSTREAM" != "" ]] ;then + export GIT_UPSTREAM="$GIT_FFMPEG_UPSTREAM" +else + export GIT_UPSTREAM=https://github.com/FFmpeg/FFmpeg.git +fi + +export GIT_COMMIT=n6.1.1 #origin/release/5.1 +export GIT_REPO_VERSION=6.1.1 + +# pre compiled +export PRE_COMPILE_TAG_TVOS=fftutorial-6.1.1-260301153347 +export PRE_COMPILE_TAG_MACOS=fftutorial-6.1.1-260301153347 +export PRE_COMPILE_TAG_IOS=fftutorial-6.1.1-260301153347 +export PRE_COMPILE_TAG_ANDROID= + diff --git a/configs/libs/fontconfig.sh b/configs/libs/fontconfig.sh index 885e5f05b..34af4c71e 100644 --- a/configs/libs/fontconfig.sh +++ b/configs/libs/fontconfig.sh @@ -21,9 +21,9 @@ export LIB_NAME='fontconfig' export LIPO_LIBS="libfontconfig" export LIB_DEPENDS_BIN="meson pkg-config" export GIT_LOCAL_REPO=extra/fontconfig -export GIT_COMMIT=2.16.0 +export GIT_COMMIT=2.17.1 export REPO_DIR=fontconfig -export GIT_REPO_VERSION=2.16.0 +export GIT_REPO_VERSION=2.17.1 # you can export GIT_FONTCONFIG_UPSTREAM=git@xx:yy/fontconfig.git use your mirror if [[ "$GIT_FONTCONFIG_UPSTREAM" != "" ]] ;then @@ -33,5 +33,7 @@ else fi # pre compiled -export PRE_COMPILE_TAG=fontconfig-2.16.0-250226074147 -export PRE_COMPILE_TAG_ANDROID=fontconfig-2.16.0-250310111812 +export PRE_COMPILE_TAG_IOS=fontconfig-2.16.0-250226074147 +export PRE_COMPILE_TAG_TVOS=fontconfig-2.16.0-250226074147 +export PRE_COMPILE_TAG_MACOS=fontconfig-2.16.0-250226074147 +export PRE_COMPILE_TAG_ANDROID=fontconfig-2.17.1-260408154148 diff --git a/configs/libs/freetype.sh b/configs/libs/freetype.sh index 240dffbab..cc168450f 100644 --- a/configs/libs/freetype.sh +++ b/configs/libs/freetype.sh @@ -23,9 +23,9 @@ export LIB_NAME='freetype' export LIPO_LIBS="libfreetype" export LIB_DEPENDS_BIN="meson pkg-config" export GIT_LOCAL_REPO=extra/freetype -export GIT_COMMIT=VER-2-13-3 +export GIT_COMMIT=VER-2-14-1 export REPO_DIR=freetype -export GIT_REPO_VERSION=2.13.3 +export GIT_REPO_VERSION=2.14.1 export GIT_WITH_SUBMODULE=1 # you can export GIT_FREETYPE_UPSTREAM=git@xx:yy/FREETYPE.git use your mirror @@ -36,8 +36,7 @@ else fi # pre compiled -export PRE_COMPILE_TAG=freetype-2.13.3-250225223932 -export PRE_COMPILE_TAG_TVOS=freetype-2.13.3-250226211539 -export PRE_COMPILE_TAG_MACOS=freetype-2.13.3-250226205646 -export PRE_COMPILE_TAG_IOS=freetype-2.13.3-250226174825 -export PRE_COMPILE_TAG_ANDROID=freetype-2.13.3-250310111735 +export PRE_COMPILE_TAG_TVOS=freetype-2.14.1-260408153927 +export PRE_COMPILE_TAG_MACOS=freetype-2.14.1-260408153927 +export PRE_COMPILE_TAG_IOS=freetype-2.14.1-260408153927 +export PRE_COMPILE_TAG_ANDROID=freetype-2.14.1-260408153927 diff --git a/configs/libs/fribidi.sh b/configs/libs/fribidi.sh index 483d4357f..29540b54f 100644 --- a/configs/libs/fribidi.sh +++ b/configs/libs/fribidi.sh @@ -38,8 +38,7 @@ else fi # pre compiled -export PRE_COMPILE_TAG=fribidi-1.0.16-250225223849 -export PRE_COMPILE_TAG_TVOS=fribidi-1.0.16-250226211508 -export PRE_COMPILE_TAG_MACOS=fribidi-1.0.16-250226205618 -export PRE_COMPILE_TAG_IOS=fribidi-1.0.16-250226174802 -export PRE_COMPILE_TAG_ANDROID=fribidi-1.0.16-250310111706 +export PRE_COMPILE_TAG_TVOS=fribidi-1.0.16-260228125305 +export PRE_COMPILE_TAG_MACOS=fribidi-1.0.16-260228125305 +export PRE_COMPILE_TAG_IOS=fribidi-1.0.16-260228125305 +export PRE_COMPILE_TAG_ANDROID=fribidi-1.0.16-260228125305 diff --git a/configs/libs/harfbuzz.sh b/configs/libs/harfbuzz.sh index fbbd18f92..e9ae11bee 100644 --- a/configs/libs/harfbuzz.sh +++ b/configs/libs/harfbuzz.sh @@ -26,9 +26,9 @@ export LIB_NAME='harfbuzz' export LIPO_LIBS="libharfbuzz libharfbuzz-subset" export LIB_DEPENDS_BIN="meson pkg-config" export GIT_LOCAL_REPO=extra/harfbuzz -export GIT_COMMIT=10.2.0 +export GIT_COMMIT=12.3.2 export REPO_DIR=harfbuzz -export GIT_REPO_VERSION=10.2.0 +export GIT_REPO_VERSION=12.3.2 # you can export GIT_HARFBUZZ_UPSTREAM=git@xx:yy/HARFBUZZ.git use your mirror if [[ "$GIT_HARFBUZZ_UPSTREAM" != "" ]] ;then @@ -38,8 +38,7 @@ else fi # pre compiled -export PRE_COMPILE_TAG=harfbuzz-10.2.0-250226074405 -export PRE_COMPILE_TAG_TVOS=harfbuzz-10.2.0-250226211621 -export PRE_COMPILE_TAG_MACOS=harfbuzz-10.2.0-250226205723 -export PRE_COMPILE_TAG_IOS=harfbuzz-10.2.0-250226174909 -export PRE_COMPILE_TAG_ANDROID=harfbuzz-10.2.0-250310111849 +export PRE_COMPILE_TAG_TVOS=harfbuzz-12.3.2-260408154245 +export PRE_COMPILE_TAG_MACOS=harfbuzz-12.3.2-260408154245 +export PRE_COMPILE_TAG_IOS=harfbuzz-12.3.2-260408154245 +export PRE_COMPILE_TAG_ANDROID=harfbuzz-12.3.2-260408154245 diff --git a/configs/libs/ijkffmpeg.sh b/configs/libs/ijkffmpeg.sh index cbc08a8c1..d1a3e258b 100644 --- a/configs/libs/ijkffmpeg.sh +++ b/configs/libs/ijkffmpeg.sh @@ -40,9 +40,8 @@ else fi # pre compiled -export PRE_COMPILE_TAG=ijkffmpeg-4.0-250311090211 -export PRE_COMPILE_TAG_ANDROID=ijkffmpeg-4.0-250311173526 -export PRE_COMPILE_TAG_TVOS=ijkffmpeg-4.0-250311090211 -export PRE_COMPILE_TAG_MACOS=ijkffmpeg-4.0-250311090211 -export PRE_COMPILE_TAG_IOS=ijkffmpeg-4.0-250318094347 +export PRE_COMPILE_TAG_ANDROID=ijkffmpeg-4.0-260402143959 +export PRE_COMPILE_TAG_TVOS=ijkffmpeg-4.0-260228145716 +export PRE_COMPILE_TAG_MACOS=ijkffmpeg-4.0-260228145716 +export PRE_COMPILE_TAG_IOS=ijkffmpeg-4.0-260402145042 diff --git a/configs/libs/lcms2.sh b/configs/libs/lcms2.sh new file mode 100644 index 000000000..b91a03b96 --- /dev/null +++ b/configs/libs/lcms2.sh @@ -0,0 +1,32 @@ +#! /usr/bin/env bash +# +# Copyright (C) 2021 Matt Reach + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +export LIB_NAME='lcms2' +export LIPO_LIBS="liblcms2" +export LIB_DEPENDS_BIN="cmake" +export GIT_LOCAL_REPO=extra/lcms2 +export GIT_COMMIT=lcms2.17 +export REPO_DIR=lcms2 +export GIT_REPO_VERSION=lcms2.17 + +# you can export GIT_LCMS2_UPSTREAM=git@xx:yy/lcms2.git use your mirror +if [[ "$GIT_LCMS2_UPSTREAM" != "" ]] ;then + export GIT_UPSTREAM="$GIT_LCMS2_UPSTREAM" +else + export GIT_UPSTREAM=https://github.com/mm2/Little-CMS.git +fi +export PRE_COMPILE_TAG_MACOS=lcms2-lcms2.17-260325152711 diff --git a/configs/libs/moltenvk.sh b/configs/libs/moltenvk.sh new file mode 100644 index 000000000..b3cbd986a --- /dev/null +++ b/configs/libs/moltenvk.sh @@ -0,0 +1,37 @@ +#! /usr/bin/env bash +# +# Copyright (C) 2021 Matt Reach + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# + +export LIB_NAME='MoltenVK' +export LIPO_LIBS="libMoltenVK" +export GIT_LOCAL_REPO=extra/MoltenVK +export GIT_COMMIT=v1.4.1 +export REPO_DIR=MoltenVK +export GIT_REPO_VERSION=v1.4.1 + +# you can export GIT_MOLTENVK_UPSTREAM=git@xx:yy/MoltenVK.git use your mirror +if [[ "$GIT_MOLTENVK_UPSTREAM" != "" ]] ;then + export GIT_UPSTREAM="$GIT_MOLTENVK_UPSTREAM" +else + export GIT_UPSTREAM=https://github.com/KhronosGroup/MoltenVK.git +fi + +# pre compiled +export PRE_COMPILE_TAG_TVOS= +export PRE_COMPILE_TAG_MACOS=moltenvk-v1.4.1-260327175726 +export PRE_COMPILE_TAG_IOS= +export PRE_COMPILE_TAG_ANDROID= diff --git a/configs/libs/openssl.sh b/configs/libs/openssl.sh index 4fe200e46..2cc8d26da 100644 --- a/configs/libs/openssl.sh +++ b/configs/libs/openssl.sh @@ -30,7 +30,6 @@ else fi # pre compiled -export PRE_COMPILE_TAG=openssl-1.1.1w-250306132030 export PRE_COMPILE_TAG_TVOS=openssl-1.1.1w-250318092610 export PRE_COMPILE_TAG_MACOS=openssl-1.1.1w-250318092610 export PRE_COMPILE_TAG_IOS=openssl-1.1.1w-250318092610 diff --git a/configs/libs/openssl3.sh b/configs/libs/openssl3.sh new file mode 100644 index 000000000..755f83803 --- /dev/null +++ b/configs/libs/openssl3.sh @@ -0,0 +1,36 @@ +#! /usr/bin/env bash +# +# Copyright (C) 2021 Matt Reach + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +export LIB_NAME='openssl' +export LIPO_LIBS="libssl libcrypto" +export GIT_LOCAL_REPO=extra/openssl +export GIT_COMMIT=openssl-3.6.2 +export REPO_DIR=openssl +export GIT_REPO_VERSION=3.6.2 + +# you can export GIT_OPUS_UPSTREAM=git@xx:yy/openssl.git use your mirror +if [[ "$GIT_OPENSSL_UPSTREAM" != "" ]] ;then + export GIT_UPSTREAM="$GIT_OPENSSL_UPSTREAM" +else + export GIT_UPSTREAM=https://github.com/openssl/openssl.git +fi + +# pre compiled +export PRE_COMPILE_TAG_TVOS=openssl3-3.6.2-260407203333 +export PRE_COMPILE_TAG_MACOS=openssl3-3.6.2-260407203333 +export PRE_COMPILE_TAG_IOS=openssl3-3.6.2-260407203333 +export PRE_COMPILE_TAG_ANDROID=openssl3-3.6.2-260407203333 diff --git a/configs/libs/opus.sh b/configs/libs/opus.sh index 8a58435f5..70f76c938 100644 --- a/configs/libs/opus.sh +++ b/configs/libs/opus.sh @@ -25,11 +25,11 @@ export LIB_NAME='opus' export LIPO_LIBS="libopus" export LIB_DEPENDS_BIN="cmake" -export CMAKE_TARGET_NAME=opus +export CMAKE_TARGETS_NAME=opus export GIT_LOCAL_REPO=extra/opus -export GIT_COMMIT=v1.5.2 +export GIT_COMMIT=v1.6.1 export REPO_DIR=opus -export GIT_REPO_VERSION=1.5.2 +export GIT_REPO_VERSION=1.6.1 # you can export GIT_OPUS_UPSTREAM=git@xx:yy/opusfile.git use your mirror if [[ "$GIT_OPUS_UPSTREAM" != "" ]] ;then @@ -39,8 +39,7 @@ else fi # pre compiled -export PRE_COMPILE_TAG=opus-1.5.2-250226075128 -export PRE_COMPILE_TAG_TVOS=opus-1.5.2-250226212130 -export PRE_COMPILE_TAG_MACOS=opus-1.5.2-250226210054 -export PRE_COMPILE_TAG_IOS=opus-1.5.2-250226175406 -export PRE_COMPILE_TAG_ANDROID=opus-1.5.2-250310112341 +export PRE_COMPILE_TAG_TVOS=opus-1.6.1-260409182634 +export PRE_COMPILE_TAG_MACOS=opus-1.6.1-260409182634 +export PRE_COMPILE_TAG_IOS=opus-1.6.1-260409182634 +export PRE_COMPILE_TAG_ANDROID=opus-1.6.1-260409182634 diff --git a/configs/libs/placebo.sh b/configs/libs/placebo.sh new file mode 100644 index 000000000..965ebff95 --- /dev/null +++ b/configs/libs/placebo.sh @@ -0,0 +1,34 @@ +#! /usr/bin/env bash +# +# Copyright (C) 2021 Matt Reach + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +export LIB_NAME='placebo' +export LIPO_LIBS="libplacebo" +export LIB_DEPENDS_BIN="meson ninja" +# placebo depends on lcms2 and shaderc (for SPIRV), vulkan backend uses MoltenVK +export LIB_DEPENDS='lcms2 shaderc moltenvk dovi' +export GIT_LOCAL_REPO=extra/placebo +export GIT_COMMIT=v7.349.0 +export REPO_DIR=placebo +export GIT_REPO_VERSION=v7.349.0 + +# you can export GIT_LIBPLACEB0_UPSTREAM=git@xx:yy/libplacebo.git use your mirror +if [[ "$GIT_LIBPLACEB0_UPSTREAM" != "" ]] ;then + export GIT_UPSTREAM="$GIT_LIBPLACEB0_UPSTREAM" +else + export GIT_UPSTREAM=https://github.com/haasn/libplacebo.git +fi +export PRE_COMPILE_TAG_MACOS=placebo-v7.349.0-260330183040 diff --git a/configs/libs/shaderc.sh b/configs/libs/shaderc.sh new file mode 100644 index 000000000..aad17d71a --- /dev/null +++ b/configs/libs/shaderc.sh @@ -0,0 +1,35 @@ +#! /usr/bin/env bash +# +# Copyright (C) 2021 Matt Reach + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +export LIB_NAME='shaderc' +export LIPO_LIBS="libshaderc_combined" +export LIB_DEPENDS_BIN="cmake" +export GIT_LOCAL_REPO=extra/shaderc +export GIT_COMMIT=v2025.3 +export REPO_DIR=shaderc +export GIT_REPO_VERSION=v2025.3 + +# macOS deployment target must be 11.0+ for std::filesystem in glslang +export MR_DEPLOYMENT_TARGET_VER_MACOS=11.0 + +# you can export GIT_SHADERC_UPSTREAM=git@xx:yy/shaderc.git use your mirror +if [[ "$GIT_SHADERC_UPSTREAM" != "" ]] ;then + export GIT_UPSTREAM="$GIT_SHADERC_UPSTREAM" +else + export GIT_UPSTREAM=https://github.com/google/shaderc.git +fi +export PRE_COMPILE_TAG_MACOS=shaderc-v2025.3-260327181828 diff --git a/configs/libs/smb2.sh b/configs/libs/smb2.sh index db6f200bf..0ad213023 100644 --- a/configs/libs/smb2.sh +++ b/configs/libs/smb2.sh @@ -25,11 +25,12 @@ export LIB_NAME='smb2' export LIPO_LIBS="libsmb2" export LIB_DEPENDS_BIN="cmake" -export CMAKE_TARGET_NAME=smb2 +export CMAKE_TARGETS_NAME=smb2 export GIT_LOCAL_REPO=extra/smb2 export GIT_COMMIT=libsmb2-6.2 export REPO_DIR=smb2 export GIT_REPO_VERSION=6.2 +export PATCH_DIR=../../patches/smb2-6.2 # you can export GIT_SMB2_UPSTREAM=git@xx:yy/libsmb2.git use your mirror if [[ "$GIT_SMB2_UPSTREAM" != "" ]] ;then @@ -39,8 +40,7 @@ else fi # pre compiled -export PRE_COMPILE_TAG=smb2-6.2-250226080535 -export PRE_COMPILE_TAG_TVOS=smb2-6.2-250226212919 -export PRE_COMPILE_TAG_MACOS=smb2-6.2-250226210647 -export PRE_COMPILE_TAG_IOS=smb2-6.2-250226180157 -export PRE_COMPILE_TAG_ANDROID=smb2-6.2-250310113032 +export PRE_COMPILE_TAG_TVOS=smb2-6.2-260228143900 +export PRE_COMPILE_TAG_MACOS=smb2-6.2-260228143900 +export PRE_COMPILE_TAG_IOS=smb2-6.2-260228143900 +export PRE_COMPILE_TAG_ANDROID=smb2-6.2-260228143900 diff --git a/configs/libs/soundtouch.sh b/configs/libs/soundtouch.sh index 7c5c980c9..4d1c519f2 100644 --- a/configs/libs/soundtouch.sh +++ b/configs/libs/soundtouch.sh @@ -25,11 +25,12 @@ export LIB_NAME='soundtouch' export LIPO_LIBS="libsoundtouch" export LIB_DEPENDS_BIN="cmake" -export CMAKE_TARGET_NAME=SoundTouch +export CMAKE_TARGETS_NAME=SoundTouch export GIT_LOCAL_REPO=extra/soundtouch export REPO_DIR=soundtouch -export GIT_COMMIT=2.3.3 -export GIT_REPO_VERSION=2.3.3 +export GIT_COMMIT=2.4.0 +export GIT_REPO_VERSION=2.4.0 +export PATCH_DIR=../../patches/soundtouch # you can export GIT_SOUNDTOUCH_UPSTREAM=git@xx:yy/soundtouch.git use your mirror if [[ "$GIT_SOUNDTOUCH_UPSTREAM" != "" ]] ;then @@ -39,8 +40,7 @@ else fi # pre compiled -export PRE_COMPILE_TAG=soundtouch-2.3.3-250225223556 -export PRE_COMPILE_TAG_TVOS=soundtouch-2.3.3-250226212055 -export PRE_COMPILE_TAG_MACOS=soundtouch-2.3.3-250226210026 -export PRE_COMPILE_TAG_IOS=soundtouch-2.3.3-250226175330 -export PRE_COMPILE_TAG_ANDROID=soundtouch-2.3.3-250310112314 +export PRE_COMPILE_TAG_TVOS=soundtouch-2.4.0-260228135702 +export PRE_COMPILE_TAG_MACOS=soundtouch-2.4.0-260228135702 +export PRE_COMPILE_TAG_IOS=soundtouch-2.4.0-260228135702 +export PRE_COMPILE_TAG_ANDROID=soundtouch-2.4.0-260228135702 diff --git a/configs/libs/test.sh b/configs/libs/test.sh index 9416245e5..2af621806 100644 --- a/configs/libs/test.sh +++ b/configs/libs/test.sh @@ -38,7 +38,6 @@ else fi # pre compiled -export PRE_COMPILE_TAG=test-1.4-250312130817 export PRE_COMPILE_TAG_ANDROID=test-1.4-250312130817 export PRE_COMPILE_TAG_TVOS=test-1.4-250312130817 export PRE_COMPILE_TAG_MACOS=test-1.4-250312130817 diff --git a/configs/libs/uavs3d.sh b/configs/libs/uavs3d.sh index 768b72f6b..9ee7c2016 100644 --- a/configs/libs/uavs3d.sh +++ b/configs/libs/uavs3d.sh @@ -23,11 +23,12 @@ export LIB_NAME='uavs3d' export LIPO_LIBS="libuavs3d" export LIB_DEPENDS_BIN="cmake" -export CMAKE_TARGET_NAME=uavs3d +export CMAKE_TARGETS_NAME=uavs3d export GIT_LOCAL_REPO=extra/uavs3d export GIT_COMMIT=1fd0491 export REPO_DIR=uavs3d export GIT_REPO_VERSION=1.2.1 +export PATCH_DIR=../../patches/uavs3d # you can export GIT_UAVS3D_UPSTREAM=git@xx:yy/UAVS3D.git use your mirror if [[ "$GIT_UAVS3D_UPSTREAM" != "" ]] ;then @@ -37,8 +38,7 @@ else fi # pre compiled -export PRE_COMPILE_TAG=uavs3d-1.2.1-250226151333 -export PRE_COMPILE_TAG_ANDROID=uavs3d-1.2.1-250310113007 -export PRE_COMPILE_TAG_TVOS=uavs3d-1.2.1-250226212827 -export PRE_COMPILE_TAG_MACOS=uavs3d-1.2.1-250226210618 -export PRE_COMPILE_TAG_IOS=uavs3d-1.2.1-250226180112 +export PRE_COMPILE_TAG_ANDROID=uavs3d-1.2.1-260228143617 +export PRE_COMPILE_TAG_TVOS=uavs3d-1.2.1-260228143617 +export PRE_COMPILE_TAG_MACOS=uavs3d-1.2.1-260228143617 +export PRE_COMPILE_TAG_IOS=uavs3d-1.2.1-260228143617 diff --git a/configs/libs/unibreak.sh b/configs/libs/unibreak.sh index 289ae24de..482089b2c 100644 --- a/configs/libs/unibreak.sh +++ b/configs/libs/unibreak.sh @@ -38,8 +38,7 @@ else fi # pre compiled -export PRE_COMPILE_TAG=unibreak-6.1-250225223657 -export PRE_COMPILE_TAG_TVOS=unibreak-6.1-250226211354 -export PRE_COMPILE_TAG_MACOS=unibreak-6.1-250226205523 -export PRE_COMPILE_TAG_IOS=unibreak-6.1-250226174658 -export PRE_COMPILE_TAG_ANDROID=unibreak-6.1-250310111549 +export PRE_COMPILE_TAG_TVOS=unibreak-6.1-260228124943 +export PRE_COMPILE_TAG_MACOS=unibreak-6.1-260228124943 +export PRE_COMPILE_TAG_IOS=unibreak-6.1-260228124943 +export PRE_COMPILE_TAG_ANDROID=unibreak-6.1-260228124943 diff --git a/configs/libs/webp.sh b/configs/libs/webp.sh new file mode 100644 index 000000000..46775f9f5 --- /dev/null +++ b/configs/libs/webp.sh @@ -0,0 +1,40 @@ +#! /usr/bin/env bash +# +# Copyright (C) 2021 Matt Reach + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# + +export LIB_NAME='webp' +export LIPO_LIBS="libwebp libsharpyuv libwebpdecoder libwebpdemux" +export LIB_DEPENDS_BIN="cmake" +export CMAKE_TARGETS_NAME=webpdecoder,webpdemux +export GIT_LOCAL_REPO=extra/webp +export GIT_COMMIT=v1.6.0 +export REPO_DIR=webp +export GIT_REPO_VERSION=v1.6.0 + +# you can export GIT_WEBP_UPSTREAM=git@xx:yy/webp.git use your mirror +if [[ "$GIT_WEBP_UPSTREAM" != "" ]] ;then + export GIT_UPSTREAM="$GIT_WEBP_UPSTREAM" +else + export GIT_UPSTREAM=https://github.com/debugly/libwebp.git +fi + +# pre compiled +export PRE_COMPILE_TAG_TVOS=webp-v1.6.0-260228144739 +export PRE_COMPILE_TAG_MACOS=webp-v1.6.0-260228144739 +export PRE_COMPILE_TAG_IOS=webp-v1.6.0-260228144739 +export PRE_COMPILE_TAG_ANDROID= \ No newline at end of file diff --git a/configs/libs/xml2.sh b/configs/libs/xml2.sh index f6380e221..9006c6af3 100644 --- a/configs/libs/xml2.sh +++ b/configs/libs/xml2.sh @@ -24,11 +24,11 @@ export LIB_NAME='xml2' export LIPO_LIBS="libxml2" -export LIB_DEPENDS_BIN="autoconf automake libtool" +export LIB_DEPENDS_BIN="meson pkg-config" export GIT_LOCAL_REPO=extra/xml2 -export GIT_COMMIT=v2.13.6 +export GIT_COMMIT=v2.15.1 export REPO_DIR=xml2 -export GIT_REPO_VERSION=2.13.6 +export GIT_REPO_VERSION=2.15.1 # you can export GIT_XML2_UPSTREAM=git@xx:yy/xml2.git use your mirror if [[ "$GIT_XML2_UPSTREAM" != "" ]] ;then @@ -38,4 +38,7 @@ else fi # pre compiled -export PRE_COMPILE_TAG=xml2-2.13.6-250225111236 +export PRE_COMPILE_TAG_TVOS=xml2-2.15.1-260408112816 +export PRE_COMPILE_TAG_MACOS=xml2-2.15.1-260408112816 +export PRE_COMPILE_TAG_IOS=xml2-2.15.1-260408112816 +export PRE_COMPILE_TAG_ANDROID=xml2-2.15.1-260408112816 diff --git a/configs/libs/yuv.sh b/configs/libs/yuv.sh index b3c7cb1f7..c324a7e36 100644 --- a/configs/libs/yuv.sh +++ b/configs/libs/yuv.sh @@ -20,11 +20,22 @@ export LIB_NAME='yuv' export LIPO_LIBS="libyuv" export LIB_DEPENDS_BIN="cmake" -export CMAKE_TARGET_NAME=yuv +export CMAKE_TARGETS_NAME=yuv export GIT_LOCAL_REPO=extra/yuv -export GIT_COMMIT=eb6e7bb + +# ✅---------------- +# 4e8a843b 2024年4月1日 使用单个.a +# f94b8cf7 2024年4月8日 使用单个.a +# ❌---------------- +# b5a18f9d 2024年12月30日 使用多个.a +# efd164d6 2024年6月18日 使用多个.a +# 3af6cafe 2024年4月11日 使用多个.a +# e52007ef 2024年4月9日 中间编译报错 + +export GIT_COMMIT=f94b8cf7 export REPO_DIR=yuv -export GIT_REPO_VERSION=stable-eb6e7bb +export GIT_REPO_VERSION=main-f94b8cf7 +export PATCH_DIR=../../patches/yuv # you can export GIT_YUV_UPSTREAM=git@xx:yy/yuv.git use your mirror if [[ "$GIT_YUV_UPSTREAM" != "" ]] ;then @@ -34,8 +45,7 @@ else fi # pre compiled -export PRE_COMPILE_TAG=yuv-stable-eb6e7bb-250226150059 -export PRE_COMPILE_TAG_TVOS=yuv-stable-eb6e7bb-250226212002 -export PRE_COMPILE_TAG_MACOS=yuv-stable-eb6e7bb-250226205944 -export PRE_COMPILE_TAG_IOS=yuv-stable-eb6e7bb-250226175227 -export PRE_COMPILE_TAG_ANDROID=yuv-stable-eb6e7bb-250310112252 \ No newline at end of file +export PRE_COMPILE_TAG_TVOS=yuv-main-f94b8cf7-260228135452 +export PRE_COMPILE_TAG_MACOS=yuv-main-f94b8cf7-260228135452 +export PRE_COMPILE_TAG_IOS=yuv-main-f94b8cf7-260228135452 +export PRE_COMPILE_TAG_ANDROID=yuv-main-f94b8cf7-260228135452 \ No newline at end of file diff --git a/configs/meson-crossfiles/arm64-ios-simulator.meson b/configs/meson-crossfiles/arm64-ios-simulator.meson index 157dbba8d..85f5128b4 100644 --- a/configs/meson-crossfiles/arm64-ios-simulator.meson +++ b/configs/meson-crossfiles/arm64-ios-simulator.meson @@ -11,15 +11,15 @@ strip = 'strip' pkgconfig = 'pkg-config' [built-in options] -c_args = ['-arch', 'arm64', '-mios-simulator-version-min=9.0', '-Os', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk'] -cpp_args = ['-arch', 'arm64', '-mios-simulator-version-min=9.0', '-Os', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk'] -c_link_args = ['-arch', 'arm64', '-mios-simulator-version-min=9.0', '-Os', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk'] -cpp_link_args = ['-arch', 'arm64', '-mios-simulator-version-min=9.0', '-Os', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk'] -objc_args = ['-arch', 'arm64', '-mios-simulator-version-min=9.0', '-Os', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk'] -objcpp_args = ['-arch', 'arm64', '-mios-simulator-version-min=9.0', '-Os', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk'] +c_args = ['-arch', 'arm64', '__DEPLOYMENT_TARGET', '-Os', '-isysroot', '__XCODE_DEVELOPER/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk'] +cpp_args = ['-arch', 'arm64', '__DEPLOYMENT_TARGET', '-Os', '-isysroot', '__XCODE_DEVELOPER/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk'] +c_link_args = ['-arch', 'arm64', '__DEPLOYMENT_TARGET', '-Os', '-isysroot', '__XCODE_DEVELOPER/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk'] +cpp_link_args = ['-arch', 'arm64', '__DEPLOYMENT_TARGET', '-Os', '-isysroot', '__XCODE_DEVELOPER/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk'] +objc_args = ['-arch', 'arm64', '__DEPLOYMENT_TARGET', '-Os', '-isysroot', '__XCODE_DEVELOPER/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk'] +objcpp_args = ['-arch', 'arm64', '__DEPLOYMENT_TARGET', '-Os', '-isysroot', '__XCODE_DEVELOPER/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk'] [properties] -root = '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer' +root = '__XCODE_DEVELOPER/Platforms/iPhoneSimulator.platform/Developer' has_function_printf = true has_function_hfkerhisadf = false has_function_posix_memalign = true diff --git a/configs/meson-crossfiles/arm64-ios.meson b/configs/meson-crossfiles/arm64-ios.meson index 22e1278e0..ad6ca5fd9 100644 --- a/configs/meson-crossfiles/arm64-ios.meson +++ b/configs/meson-crossfiles/arm64-ios.meson @@ -10,15 +10,15 @@ strip = 'strip' pkgconfig = 'pkg-config' [built-in options] -c_args = ['-arch', 'arm64', '-miphoneos-version-min=9.0', '-Os', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk'] -cpp_args = ['-arch', 'arm64', '-miphoneos-version-min=9.0', '-Os', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk'] -c_link_args = ['-arch', 'arm64', '-miphoneos-version-min=9.0', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk'] -cpp_link_args = ['-arch', 'arm64', '-miphoneos-version-min=9.0', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk'] -objc_args = ['-arch', 'arm64', '-miphoneos-version-min=9.0', '-Os', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk'] -objcpp_args = ['-arch', 'arm64', '-miphoneos-version-min=9.0', '-Os', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk'] +c_args = ['-arch', 'arm64', '__DEPLOYMENT_TARGET', '-Os', '-isysroot', '__XCODE_DEVELOPER/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk'] +cpp_args = ['-arch', 'arm64', '__DEPLOYMENT_TARGET', '-Os', '-isysroot', '__XCODE_DEVELOPER/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk'] +c_link_args = ['-arch', 'arm64', '__DEPLOYMENT_TARGET', '-isysroot', '__XCODE_DEVELOPER/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk'] +cpp_link_args = ['-arch', 'arm64', '__DEPLOYMENT_TARGET', '-isysroot', '__XCODE_DEVELOPER/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk'] +objc_args = ['-arch', 'arm64', '__DEPLOYMENT_TARGET', '-Os', '-isysroot', '__XCODE_DEVELOPER/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk'] +objcpp_args = ['-arch', 'arm64', '__DEPLOYMENT_TARGET', '-Os', '-isysroot', '__XCODE_DEVELOPER/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk'] [properties] -root = '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer' +root = '__XCODE_DEVELOPER/Platforms/iPhoneOS.platform/Developer' has_function_printf = true has_function_hfkerhisadf = false has_function_posix_memalign = true diff --git a/configs/meson-crossfiles/arm64-macos.meson b/configs/meson-crossfiles/arm64-macos.meson index 7badcb922..ac7ba1a73 100644 --- a/configs/meson-crossfiles/arm64-macos.meson +++ b/configs/meson-crossfiles/arm64-macos.meson @@ -10,15 +10,15 @@ strip = 'strip' pkgconfig = 'pkg-config' [built-in options] -c_args = ['-arch', 'arm64', '-mmacosx-version-min=10.11', '-Os'] -cpp_args = ['-arch', 'arm64', '-mmacosx-version-min=10.11', '-Os'] -c_link_args = ['-arch', 'arm64', '-mmacosx-version-min=10.11', '-Os'] -cpp_link_args = ['-arch', 'arm64', '-mmacosx-version-min=10.11', '-Os'] -objc_args = ['-arch', 'arm64', '-mmacosx-version-min=10.11', '-Os'] -objcpp_args = ['-arch', 'arm64', '-mmacosx-version-min=10.11', '-Os'] +c_args = ['-arch', 'arm64', '__DEPLOYMENT_TARGET', '-Os'] +cpp_args = ['-arch', 'arm64', '__DEPLOYMENT_TARGET', '-Os'] +c_link_args = ['-arch', 'arm64', '__DEPLOYMENT_TARGET', '-Os'] +cpp_link_args = ['-arch', 'arm64', '__DEPLOYMENT_TARGET', '-Os'] +objc_args = ['-arch', 'arm64', '__DEPLOYMENT_TARGET', '-Os'] +objcpp_args = ['-arch', 'arm64', '__DEPLOYMENT_TARGET', '-Os'] [properties] -root = '/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer' +root = '__XCODE_DEVELOPER/Platforms/MacOSX.platform/Developer' has_function_printf = true has_function_hfkerhisadf = false has_function_posix_memalign = true diff --git a/configs/meson-crossfiles/arm64-tvos-simulator.meson b/configs/meson-crossfiles/arm64-tvos-simulator.meson index 8bec47b41..1846d868b 100644 --- a/configs/meson-crossfiles/arm64-tvos-simulator.meson +++ b/configs/meson-crossfiles/arm64-tvos-simulator.meson @@ -10,15 +10,15 @@ strip = 'strip' pkgconfig = 'pkg-config' [built-in options] -c_args = ['-arch', 'arm64', '-mtvos-simulator-version-min=12.0', '-Os', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/AppleTVSimulator.platform/Developer/SDKs/AppleTVSimulator.sdk'] -cpp_args = ['-arch', 'arm64', '-mtvos-simulator-version-min=12.0', '-Os', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/AppleTVSimulator.platform/Developer/SDKs/AppleTVSimulator.sdk'] -c_link_args = ['-arch', 'arm64', '-mtvos-simulator-version-min=12.0', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/AppleTVSimulator.platform/Developer/SDKs/AppleTVSimulator.sdk'] -cpp_link_args = ['-arch', 'arm64', '-mtvos-simulator-version-min=12.0', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/AppleTVSimulator.platform/Developer/SDKs/AppleTVSimulator.sdk'] -objc_args = ['-arch', 'arm64', '-mtvos-simulator-version-min=12.0', '-Os', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/AppleTVSimulator.platform/Developer/SDKs/AppleTVSimulator.sdk'] -objcpp_args = ['-arch', 'arm64', '-mtvos-simulator-version-min=12.0', '-Os', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/AppleTVSimulator.platform/Developer/SDKs/AppleTVSimulator.sdk'] +c_args = ['-arch', 'arm64', '__DEPLOYMENT_TARGET', '-Os', '-isysroot', '__XCODE_DEVELOPER/Platforms/AppleTVSimulator.platform/Developer/SDKs/AppleTVSimulator.sdk'] +cpp_args = ['-arch', 'arm64', '__DEPLOYMENT_TARGET', '-Os', '-isysroot', '__XCODE_DEVELOPER/Platforms/AppleTVSimulator.platform/Developer/SDKs/AppleTVSimulator.sdk'] +c_link_args = ['-arch', 'arm64', '__DEPLOYMENT_TARGET', '-isysroot', '__XCODE_DEVELOPER/Platforms/AppleTVSimulator.platform/Developer/SDKs/AppleTVSimulator.sdk'] +cpp_link_args = ['-arch', 'arm64', '__DEPLOYMENT_TARGET', '-isysroot', '__XCODE_DEVELOPER/Platforms/AppleTVSimulator.platform/Developer/SDKs/AppleTVSimulator.sdk'] +objc_args = ['-arch', 'arm64', '__DEPLOYMENT_TARGET', '-Os', '-isysroot', '__XCODE_DEVELOPER/Platforms/AppleTVSimulator.platform/Developer/SDKs/AppleTVSimulator.sdk'] +objcpp_args = ['-arch', 'arm64', '__DEPLOYMENT_TARGET', '-Os', '-isysroot', '__XCODE_DEVELOPER/Platforms/AppleTVSimulator.platform/Developer/SDKs/AppleTVSimulator.sdk'] [properties] -root = '/Applications/Xcode.app/Contents/Developer/Platforms/AppleTVSimulator.platform/Developer' +root = '__XCODE_DEVELOPER/Platforms/AppleTVSimulator.platform/Developer' has_function_printf = true has_function_hfkerhisadf = false has_function_posix_memalign = true diff --git a/configs/meson-crossfiles/arm64-tvos.meson b/configs/meson-crossfiles/arm64-tvos.meson index 460b78cb6..367a34361 100644 --- a/configs/meson-crossfiles/arm64-tvos.meson +++ b/configs/meson-crossfiles/arm64-tvos.meson @@ -10,15 +10,15 @@ strip = 'strip' pkgconfig = 'pkg-config' [built-in options] -c_args = ['-arch', 'arm64', '-mtvos-version-min=12.0', '-Os', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS.sdk'] -cpp_args = ['-arch', 'arm64', '-mtvos-version-min=12.0', '-Os', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS.sdk'] -c_link_args = ['-arch', 'arm64', '-mtvos-version-min=12.0', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS.sdk'] -cpp_link_args = ['-arch', 'arm64', '-mtvos-version-min=12.0', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS.sdk'] -objc_args = ['-arch', 'arm64', '-mtvos-version-min=12.0', '-Os', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS.sdk'] -objcpp_args = ['-arch', 'arm64', '-mtvos-version-min=12.0', '-Os', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS.sdk'] +c_args = ['-arch', 'arm64', '__DEPLOYMENT_TARGET', '-Os', '-isysroot', '__XCODE_DEVELOPER/Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS.sdk'] +cpp_args = ['-arch', 'arm64', '__DEPLOYMENT_TARGET', '-Os', '-isysroot', '__XCODE_DEVELOPER/Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS.sdk'] +c_link_args = ['-arch', 'arm64', '__DEPLOYMENT_TARGET', '-isysroot', '__XCODE_DEVELOPER/Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS.sdk'] +cpp_link_args = ['-arch', 'arm64', '__DEPLOYMENT_TARGET', '-isysroot', '__XCODE_DEVELOPER/Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS.sdk'] +objc_args = ['-arch', 'arm64', '__DEPLOYMENT_TARGET', '-Os', '-isysroot', '__XCODE_DEVELOPER/Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS.sdk'] +objcpp_args = ['-arch', 'arm64', '__DEPLOYMENT_TARGET', '-Os', '-isysroot', '__XCODE_DEVELOPER/Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS.sdk'] [properties] -root = '/Applications/Xcode.app/Contents/Developer/Platforms/AppleTVOS.platform/Developer' +root = '__XCODE_DEVELOPER/Platforms/AppleTVOS.platform/Developer' has_function_printf = true has_function_hfkerhisadf = false has_function_posix_memalign = true diff --git a/configs/meson-crossfiles/x86_64-ios-simulator.meson b/configs/meson-crossfiles/x86_64-ios-simulator.meson index eb5786482..d8dbfc53b 100644 --- a/configs/meson-crossfiles/x86_64-ios-simulator.meson +++ b/configs/meson-crossfiles/x86_64-ios-simulator.meson @@ -10,15 +10,15 @@ strip = 'strip' pkgconfig = 'pkg-config' [built-in options] -c_args = ['-arch', 'x86_64', '-mios-simulator-version-min=9.0', '-Os', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk'] -cpp_args = ['-arch', 'x86_64', '-mios-simulator-version-min=9.0', '-Os', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk'] -c_link_args = ['-arch', 'x86_64', '-mios-simulator-version-min=9.0', '-Os', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk'] -cpp_link_args = ['-arch', 'x86_64', '-mios-simulator-version-min=9.0', '-Os', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk'] -objc_args = ['-arch', 'x86_64', '-mios-simulator-version-min=9.0', '-Os', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk'] -objcpp_args = ['-arch', 'x86_64', '-mios-simulator-version-min=9.0', '-Os', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk'] +c_args = ['-arch', 'x86_64', '__DEPLOYMENT_TARGET', '-Os', '-isysroot', '__XCODE_DEVELOPER/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk'] +cpp_args = ['-arch', 'x86_64', '__DEPLOYMENT_TARGET', '-Os', '-isysroot', '__XCODE_DEVELOPER/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk'] +c_link_args = ['-arch', 'x86_64', '__DEPLOYMENT_TARGET', '-Os', '-isysroot', '__XCODE_DEVELOPER/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk'] +cpp_link_args = ['-arch', 'x86_64', '__DEPLOYMENT_TARGET', '-Os', '-isysroot', '__XCODE_DEVELOPER/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk'] +objc_args = ['-arch', 'x86_64', '__DEPLOYMENT_TARGET', '-Os', '-isysroot', '__XCODE_DEVELOPER/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk'] +objcpp_args = ['-arch', 'x86_64', '__DEPLOYMENT_TARGET', '-Os', '-isysroot', '__XCODE_DEVELOPER/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk'] [properties] -root = '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer' +root = '__XCODE_DEVELOPER/Platforms/iPhoneSimulator.platform/Developer' has_function_printf = true has_function_hfkerhisadf = false has_function_posix_memalign = true diff --git a/configs/meson-crossfiles/x86_64-macos.meson b/configs/meson-crossfiles/x86_64-macos.meson index e16585d42..3de2397e9 100644 --- a/configs/meson-crossfiles/x86_64-macos.meson +++ b/configs/meson-crossfiles/x86_64-macos.meson @@ -10,15 +10,15 @@ strip = 'strip' pkgconfig = 'pkg-config' [built-in options] -c_args = ['-arch', 'x86_64', '-mmacosx-version-min=10.11', '-Os'] -cpp_args = ['-arch', 'x86_64', '-mmacosx-version-min=10.11', '-Os'] -c_link_args = ['-arch', 'x86_64', '-mmacosx-version-min=10.11', '-Os'] -cpp_link_args = ['-arch', 'x86_64', '-mmacosx-version-min=10.11', '-Os'] -objc_args = ['-arch', 'x86_64', '-mmacosx-version-min=10.11', '-Os'] -objcpp_args = ['-arch', 'x86_64', '-mmacosx-version-min=10.11', '-Os'] +c_args = ['-arch', 'x86_64', '__DEPLOYMENT_TARGET', '-Os'] +cpp_args = ['-arch', 'x86_64', '__DEPLOYMENT_TARGET', '-Os'] +c_link_args = ['-arch', 'x86_64', '__DEPLOYMENT_TARGET', '-Os'] +cpp_link_args = ['-arch', 'x86_64', '__DEPLOYMENT_TARGET', '-Os'] +objc_args = ['-arch', 'x86_64', '__DEPLOYMENT_TARGET', '-Os'] +objcpp_args = ['-arch', 'x86_64', '__DEPLOYMENT_TARGET', '-Os'] [properties] -root = '/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer' +root = '__XCODE_DEVELOPER/Platforms/MacOSX.platform/Developer' has_function_printf = true has_function_hfkerhisadf = false has_function_posix_memalign = true diff --git a/configs/meson-crossfiles/x86_64-tvos-simulator.meson b/configs/meson-crossfiles/x86_64-tvos-simulator.meson index a4afe59f5..d6e0c1d9f 100644 --- a/configs/meson-crossfiles/x86_64-tvos-simulator.meson +++ b/configs/meson-crossfiles/x86_64-tvos-simulator.meson @@ -10,15 +10,15 @@ strip = 'strip' pkgconfig = 'pkg-config' [built-in options] -c_args = ['-arch', 'x86_64', '-mtvos-simulator-version-min=12.0', '-Os', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/AppleTVSimulator.platform/Developer/SDKs/AppleTVSimulator.sdk'] -cpp_args = ['-arch', 'x86_64', '-mtvos-simulator-version-min=12.0', '-Os', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/AppleTVSimulator.platform/Developer/SDKs/AppleTVSimulator.sdk'] -c_link_args = ['-arch', 'x86_64', '-mtvos-simulator-version-min=12.0', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/AppleTVSimulator.platform/Developer/SDKs/AppleTVSimulator.sdk'] -cpp_link_args = ['-arch', 'x86_64', '-mtvos-simulator-version-min=12.0', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/AppleTVSimulator.platform/Developer/SDKs/AppleTVSimulator.sdk'] -objc_args = ['-arch', 'x86_64', '-mtvos-simulator-version-min=12.0', '-Os', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/AppleTVSimulator.platform/Developer/SDKs/AppleTVSimulator.sdk'] -objcpp_args = ['-arch', 'x86_64', '-mtvos-simulator-version-min=12.0', '-Os', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/AppleTVSimulator.platform/Developer/SDKs/AppleTVSimulator.sdk'] +c_args = ['-arch', 'x86_64', '__DEPLOYMENT_TARGET', '-Os', '-isysroot', '__XCODE_DEVELOPER/Platforms/AppleTVSimulator.platform/Developer/SDKs/AppleTVSimulator.sdk'] +cpp_args = ['-arch', 'x86_64', '__DEPLOYMENT_TARGET', '-Os', '-isysroot', '__XCODE_DEVELOPER/Platforms/AppleTVSimulator.platform/Developer/SDKs/AppleTVSimulator.sdk'] +c_link_args = ['-arch', 'x86_64', '__DEPLOYMENT_TARGET', '-isysroot', '__XCODE_DEVELOPER/Platforms/AppleTVSimulator.platform/Developer/SDKs/AppleTVSimulator.sdk'] +cpp_link_args = ['-arch', 'x86_64', '__DEPLOYMENT_TARGET', '-isysroot', '__XCODE_DEVELOPER/Platforms/AppleTVSimulator.platform/Developer/SDKs/AppleTVSimulator.sdk'] +objc_args = ['-arch', 'x86_64', '__DEPLOYMENT_TARGET', '-Os', '-isysroot', '__XCODE_DEVELOPER/Platforms/AppleTVSimulator.platform/Developer/SDKs/AppleTVSimulator.sdk'] +objcpp_args = ['-arch', 'x86_64', '__DEPLOYMENT_TARGET', '-Os', '-isysroot', '__XCODE_DEVELOPER/Platforms/AppleTVSimulator.platform/Developer/SDKs/AppleTVSimulator.sdk'] [properties] -root = '/Applications/Xcode.app/Contents/Developer/Platforms/AppleTVSimulator.platform/Developer' +root = '__XCODE_DEVELOPER/Platforms/AppleTVSimulator.platform/Developer' has_function_printf = true has_function_hfkerhisadf = false has_function_posix_memalign = true diff --git a/do-compile/android/any.sh b/do-compile/android/any.sh index a825089de..9071cc055 100755 --- a/do-compile/android/any.sh +++ b/do-compile/android/any.sh @@ -29,8 +29,10 @@ do_lipo_lib() { for arch in $archs; do local lib_dir="$MR_PRODUCT_ROOT/$LIB_NAME-$arch" - sed -i "" "s|-lpthread|-pthread|" "$lib_dir"/lib/pkgconfig/*.pc + if [ -d "$lib_dir" ]; then + my_sed_i "s|-lpthread|-pthread|" "$lib_dir"/lib/pkgconfig/*.pc + # Copy the directory mkdir -p "$MR_UNI_PROD_DIR/$LIB_NAME" cp -Rf "$lib_dir" "$MR_UNI_PROD_DIR/$LIB_NAME" else @@ -57,7 +59,7 @@ function do_compile() { if [ ! -d $MR_BUILD_SOURCE ]; then echo "" echo "!! ERROR" - echo "!! Can not find $MR_BUILD_SOURCE directory for $MR_BUILD_NAME" + echo "!! Can not find lib source: $MR_BUILD_SOURCE" echo "!! Run init-any.sh ${LIB_NAME} first" echo "" exit 1 @@ -136,4 +138,4 @@ function main() { esac } -main \ No newline at end of file +main diff --git a/do-compile/android/ass.sh b/do-compile/android/ass.sh index 0e5862fab..c1853d43f 100755 --- a/do-compile/android/ass.sh +++ b/do-compile/android/ass.sh @@ -37,6 +37,7 @@ check_lib 'freetype2' check_lib 'fribidi' check_lib 'harfbuzz' check_lib 'libunibreak' +check_lib 'fontconfig' echo "----------------------" -./meson-compatible.sh "-Dtest=false -Dprofile=false -Dfontconfig=enabled -Dcoretext=disabled -Dasm=disabled -Dlibunibreak=enabled" \ No newline at end of file +./meson-compatible.sh "-Dtest=disabled -Dprofile=disabled -Dfontconfig=enabled -Dcoretext=disabled -Dasm=disabled -Dlibunibreak=enabled" diff --git a/do-compile/android/cmake-compatible.sh b/do-compile/android/cmake-compatible.sh index bc464d6c5..ba07ee728 100755 --- a/do-compile/android/cmake-compatible.sh +++ b/do-compile/android/cmake-compatible.sh @@ -34,7 +34,7 @@ echo "[*] cmake options: $CMAKE_OTHER_OPTS" echo "[*] cmake component: $CMAKE_COMPONENT" echo "----------------------" -build="${MR_BUILD_SOURCE}/camke_wksp" +build="${MR_BUILD_SOURCE}/cmake_wksp" rm -rf "$build" mkdir -p "$build" @@ -58,12 +58,25 @@ echo "----------------------" echo "[*] compile $LIB_NAME" echo "----------------------" +# 初始化构建命令 +camke_cmd="cmake --build ." + +# 以逗号分割目标名称,并为每个目标添加 --target 参数 +IFS=',' read -ra targets <<< "$CMAKE_TARGETS_NAME" +for target in "${targets[@]}"; do + camke_cmd="$camke_cmd --target $target" +done + + if [[ "$MR_DEBUG" == "debug" ]];then - cmake --build . --target $CMAKE_TARGET_NAME --config Debug -- CODE_SIGNING_ALLOWED=NO + camke_cmd="$camke_cmd --config Debug -- CODE_SIGNING_ALLOWED=NO" else - cmake --build . --target $CMAKE_TARGET_NAME --config Release -- CODE_SIGNING_ALLOWED=NO + camke_cmd="$camke_cmd --config Release -- CODE_SIGNING_ALLOWED=NO" fi +# 执行构建命令 +eval "$camke_cmd" + if [[ -n $CMAKE_COMPONENT ]];then cmake --install . --strip --component "$CMAKE_COMPONENT" else diff --git a/do-compile/android/dvdnav.sh b/do-compile/android/dvdnav.sh new file mode 100755 index 000000000..3cee4d28e --- /dev/null +++ b/do-compile/android/dvdnav.sh @@ -0,0 +1,75 @@ +#! /usr/bin/env bash +# +# Copyright (C) 2021 Matt Reach + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set -e + +THIS_DIR=$(DIRNAME=$(dirname "$0"); cd "$DIRNAME"; pwd) +cd "$THIS_DIR" + +# prepare build config +CFG_FLAGS="--prefix=$MR_BUILD_PREFIX --disable-dependency-tracking --disable-silent-rules --disable-apidoc --enable-static --disable-shared" +CFLAGS="$MR_DEFAULT_CFLAGS" + +if [[ "$MR_DEBUG" == "debug" ]];then + CFG_FLAGS="${CFG_FLAGS} use_examples=yes" +fi + +# for cross compile +if [[ $(uname -m) != "$MR_ARCH" || "$MR_FORCE_CROSS" ]];then + echo "[*] cross compile, on $(uname -m) compile $MR_PLAT $MR_ARCH." + # https://www.gnu.org/software/automake/manual/html_node/Cross_002dCompilation.html + # aarch64-linux-android21 + CFG_FLAGS="$CFG_FLAGS --host=$MR_FF_ARCH-linux-android$MR_ANDROID_API --with-sysroot=$MR_SYS_ROOT" +fi + +echo "----------------------" +echo "[*] configurate $LIB_NAME" +echo "----------------------" + +cd $MR_BUILD_SOURCE + +if [[ -f 'configure' ]]; then + echo "reuse configure" +else + echo "auto generate configure" + autoreconf -if >/dev/null +fi + + +echo +echo "CC: $MR_TRIPLE_CC" +echo "CFG_FLAGS: $CFG_FLAGS" +echo "CFLAGS: $CFLAGS" +echo + +export CFLAGS="$CFLAGS" +export LDFLAGS="$CFLAGS" +export STRIP="$MR_STRIP" +export CC="$MR_TRIPLE_CC" +export CXX="$MR_TRIPLE_CXX" +export AR="$MR_AR" +export AS="$MR_AS" +export RANLIB="$MR_RANLIB" + +./configure $CFG_FLAGS + +#---------------------- +echo "----------------------" +echo "[*] compile $LIB_NAME" +echo "----------------------" + +make install -j$MR_HOST_NPROC >/dev/null \ No newline at end of file diff --git a/do-compile/android/ffmpeg.sh b/do-compile/android/ffmpeg.sh index f731b7eb0..6b148f51d 100755 --- a/do-compile/android/ffmpeg.sh +++ b/do-compile/android/ffmpeg.sh @@ -31,13 +31,12 @@ THIS_DIR=$(DIRNAME=$(dirname "$0"); cd "$DIRNAME"; pwd) cd "$THIS_DIR" # ffmpeg config options -source $MR_SHELL_CONFIGS_DIR/ffconfig/module.sh +source $MR_SHELL_CONFIGS_DIR/ffconfig/module-full.sh source $MR_SHELL_CONFIGS_DIR/ffconfig/auto-detect-third-libs.sh CFG_FLAGS= CFG_FLAGS="$CFG_FLAGS $COMMON_FF_CFG_FLAGS" CFG_FLAGS="$CFG_FLAGS $THIRD_CFG_FLAGS" -CFG_FLAGS="$CFG_FLAGS --enable-demuxer=dash --enable-libxml2" # Android 15 with 16 kb page size support # https://developer.android.com/guide/practices/page-sizes#compile-r27 @@ -52,7 +51,7 @@ echo "[*] configure" if [[ ! -d $MR_BUILD_SOURCE ]]; then echo "" echo "!! ERROR" - echo "!! Can not find $MR_BUILD_SOURCE directory for $MR_BUILD_NAME" + echo "!! Can not find lib source: $MR_BUILD_SOURCE" echo "!! Run 'init-*.sh' first" echo "" exit 1 diff --git a/do-compile/android/ijkffmpeg.sh b/do-compile/android/ijkffmpeg.sh index 386e71148..9c3932c49 100755 --- a/do-compile/android/ijkffmpeg.sh +++ b/do-compile/android/ijkffmpeg.sh @@ -34,7 +34,7 @@ FFMPEG_CFG_FLAGS= FFMPEG_EXTRA_CFLAGS= export COMMON_FF_CFG_FLAGS= # use ijk ffmpeg config options -source $MR_SHELL_CONFIGS_DIR/ijk-ffmpeg-config/module.sh +source $MR_SHELL_CONFIGS_DIR/ijk-ffmpeg-config/module-lite.sh FFMPEG_CFG_FLAGS="$FFMPEG_CFG_FLAGS $COMMON_FF_CFG_FLAGS" # Advanced options (experts only): diff --git a/do-compile/android/openssl.sh b/do-compile/android/openssl.sh index 894682648..cdab62edd 100755 --- a/do-compile/android/openssl.sh +++ b/do-compile/android/openssl.sh @@ -50,7 +50,7 @@ case $_MR_ARCH in ;; esac -CFG_FLAGS="no-shared no-engine no-dynamic-engine no-static-engine \ +CFG_FLAGS="no-shared no-engine no-apps no-dynamic-engine no-static-engine \ no-dso no-ui-console no-tests \ --prefix=$MR_BUILD_PREFIX \ --openssldir=$MR_BUILD_PREFIX \ @@ -65,6 +65,7 @@ fi C_FLAGS="$MR_DEFAULT_CFLAGS" cd $MR_BUILD_SOURCE + if [ -f "./Makefile" ]; then echo 'reuse configure' echo "----------------------" @@ -83,6 +84,8 @@ else export AS="$MR_AS" export RANLIB="$MR_RANLIB" export STRIP="$MR_STRIP" + export ANDROID_NDK_ROOT="$MR_ANDROID_NDK_HOME" + ./Configure $CFG_FLAGS fi diff --git a/do-compile/android/xml2.sh b/do-compile/android/xml2.sh index 79f2cbec7..6c6eb4fb4 100755 --- a/do-compile/android/xml2.sh +++ b/do-compile/android/xml2.sh @@ -17,56 +17,10 @@ # can't use cmake,because ios undeclared function 'getentropy' # https://gitlab.gnome.org/GNOME/libxml2/-/issues/774#note_2174500 # ./cmake-compatible.sh "-DBUILD_SHARED_LIBS=0 -DLIBXML2_WITH_PROGRAMS=0 -DLIBXML2_WITH_ZLIB=1 -DLIBXML2_WITH_PYTHON=0 -DLIBXML2_WITH_ICONV=1" +# android platform not provide iconv,apple platform provided. +set -e -CFLAGS="$MR_DEFAULT_CFLAGS" +CFG_FLAGS="-Ddocs=disabled -Ddebugging=disabled -Dpython=disabled -Dzlib=enabled -Diconv=disabled" -# prepare build config -CFG_FLAGS="--prefix=$MR_BUILD_PREFIX" -# for cross compile -if [[ $(uname -m) != "$MR_ARCH" || "$MR_FORCE_CROSS" ]];then - echo "[*] cross compile, on $(uname -m) compile $MR_PLAT $MR_ARCH." - # https://www.gnu.org/software/automake/manual/html_node/Cross_002dCompilation.html - CFLAGS="$CFLAGS -isysroot $MR_SYS_ROOT" - # aarch64-linux-android21 - CFG_FLAGS="$CFG_FLAGS --host=$MR_FF_ARCH-linux-android$MR_ANDROID_API --with-sysroot=$MR_SYS_ROOT" -fi - -echo "----------------------" -echo "[*] configurate $LIB_NAME" -echo "----------------------" - -cd $MR_BUILD_SOURCE - -echo -echo "CC: $MR_TRIPLE_CC" -echo "CFG_FLAGS: $CFG_FLAGS" -echo "CFLAGS: $CFLAGS" -echo - -export CFLAGS="$CFLAGS" -export LDFLAGS="$CFLAGS" - -export CC="$MR_TRIPLE_CC" -export CXX="$MR_TRIPLE_CXX" -export AR="$MR_AR" -export AS="$MR_AS" -export RANLIB="$MR_RANLIB" -export STRIP="$MR_STRIP" -./autogen.sh \ - $CFG_FLAGS \ - --prefix=$MR_BUILD_PREFIX \ - --enable-static --disable-shared \ - --disable-fast-install \ - --without-python \ - --without-debug \ - --with-zlib \ - --with-pic \ - --without-lzma - -echo "----------------------" -echo "[*] compile $LIB_NAME" -echo "----------------------" - -make clean >/dev/null -make install -j${MR_HOST_NPROC} \ No newline at end of file +./meson-compatible.sh "$CFG_FLAGS" \ No newline at end of file diff --git a/do-compile/apple/any.sh b/do-compile/apple/any.sh index 2a96bcc62..2b4d2a633 100755 --- a/do-compile/apple/any.sh +++ b/do-compile/apple/any.sh @@ -88,20 +88,29 @@ do_lipo_all() { mkdir -p "$pc_dst_dir" echo "copy pkgconfig file to $pc_dst_dir" cp ${pc_src_dir}/*.pc "$pc_dst_dir" + + # fix absolute path which contains arch suffix bug,such as /path/to/opus-arch/lib + #-L/Users/runner/work/MRFFToolChainBuildShell/MRFFToolChainBuildShell/build/product/macos/opus-arch/lib + #-> + #-L/Users/runner/work/MRFFToolChainBuildShell/MRFFToolChainBuildShell/build/product/macos/universal/opus/lib + #my_sed_i "s|${LIB_NAME}-${arch}|universal/${LIB_NAME}|g" "$pc_dst_dir/"*.pc + #fix prefix path p="$uni_dir/$LIB_NAME" escaped_p=$(echo $p | sed 's/\//\\\//g') - sed -i "" "s/^prefix=.*/prefix=$escaped_p/" "$pc_dst_dir/"*.pc + my_sed_i "s|^prefix=.*|prefix=$escaped_p|" "$pc_dst_dir/"*.pc + my_sed_i "s|^libdir=.*|libdir=$escaped_p/lib|" "$pc_dst_dir/"*.pc + my_sed_i "s|^includedir=.*|includedir=$escaped_p/include|" "$pc_dst_dir/"*.pc fi fi done echo '----------------------' - if [[ "$MR_SKIP_MAKE_XCFRAMEWORK" ]]; then - echo "⚠️ skip make xcframework" - else + if [[ "$MR_MAKE_XCFRAMEWORK" ]]; then echo '[*] make xcframework' do_make_xcframework + else + echo "⚠️ skip make xcframework" fi echo '----------------------' echo @@ -111,6 +120,12 @@ function do_make_xcframework() { mkdir -p "$MR_XCFRMK_DIR" for lib in $LIPO_LIBS; do + local macos_inputs="" + local ios_inputs="" + local ios_sim_inputs="" + local tvos_inputs="" + local tvos_sim_inputs="" + # add macOS macos_lib=$MR_MACOS_PRODUCT_ROOT/universal/$LIB_NAME/lib/${lib}.a if [[ -f $macos_lib ]]; then @@ -143,11 +158,27 @@ function do_make_xcframework() { done } +function do_fix_pc() { + # fix tbd link path in pkgconfig + # matching: /path/to/libNAME.tbd or path/to/libNAME.tbd -> -lNAME + if [[ -d "${MR_BUILD_PREFIX}/lib/pkgconfig" ]]; then + for pc in "${MR_BUILD_PREFIX}/lib/pkgconfig/"*.pc; do + if [[ -f "$pc" ]]; then + echo "fix pkgconfig in $pc" + # fix tbd link path + my_sed_i 's|[^ ]*lib\([^ /]*\)\.tbd|-l\1|g' "$pc" + # remove xcode sdk include path + my_sed_i 's|-I/Applications/Xcode[^ ]*usr/include||g' "$pc" + fi + done + fi +} + function do_compile() { if [ ! -d $MR_BUILD_SOURCE ]; then echo "" echo "!! ERROR" - echo "!! Can not find $MR_BUILD_SOURCE directory for $MR_BUILD_NAME" + echo "!! Can not find lib source: $MR_BUILD_SOURCE" echo "!! Run init-any.sh ${LIB_NAME} first" echo "" exit 1 diff --git a/do-compile/apple/ass.sh b/do-compile/apple/ass.sh index ee7116777..a7fd5bd44 100755 --- a/do-compile/apple/ass.sh +++ b/do-compile/apple/ass.sh @@ -39,4 +39,4 @@ check_lib 'harfbuzz' check_lib 'libunibreak' echo "----------------------" -./meson-compatible.sh "-Dtest=false -Dprofile=false -Dfontconfig=disabled -Dcoretext=enabled -Dasm=disabled -Dlibunibreak=enabled" \ No newline at end of file +./meson-compatible.sh "-Dtest=disabled -Dprofile=disabled -Dfontconfig=disabled -Dcoretext=enabled -Dasm=disabled -Dlibunibreak=enabled" \ No newline at end of file diff --git a/do-compile/apple/bluray.sh b/do-compile/apple/bluray.sh index ea3f60ed4..e6b36ffe6 100755 --- a/do-compile/apple/bluray.sh +++ b/do-compile/apple/bluray.sh @@ -51,7 +51,21 @@ if [[ -f 'configure' ]]; then echo "reuse configure" else echo "auto generate configure" - ./bootstrap >/dev/null + if [[ "$(uname)" == "Darwin" ]]; then + # Homebrew may be in different locations depending on the CPU arch + if [[ -d "/opt/homebrew/share/aclocal" ]]; then + export ACLOCAL_PATH="/opt/homebrew/share/aclocal:$ACLOCAL_PATH" + elif [[ -d "/usr/local/share/aclocal" ]]; then + export ACLOCAL_PATH="/usr/local/share/aclocal:$ACLOCAL_PATH" + fi + + if command -v glibtoolize > /dev/null; then + glibtoolize --force --copy + elif command -v libtoolize > /dev/null; then + libtoolize --force --copy + fi + fi + ./bootstrap fi echo diff --git a/do-compile/apple/cmake-compatible.sh b/do-compile/apple/cmake-compatible.sh index 0f6566f5b..a97900743 100755 --- a/do-compile/apple/cmake-compatible.sh +++ b/do-compile/apple/cmake-compatible.sh @@ -28,9 +28,10 @@ echo "----------------------" echo "[*] configurate $LIB_NAME" echo "[*] cmake options: $CMAKE_OTHER_OPTS" echo "[*] cmake component: $CMAKE_COMPONENT" +echo "[*] deployment target:$MR_DEPLOYMENT_TARGET_VER" echo "----------------------" -build="${MR_BUILD_SOURCE}/camke_wksp" +build="${MR_BUILD_SOURCE}/cmake_wksp" rm -rf "$build" mkdir -p "$build" @@ -65,6 +66,7 @@ cmake -S ${MR_BUILD_SOURCE} \ -DCMAKE_INSTALL_PREFIX=${MR_BUILD_PREFIX} \ -DCMAKE_TOOLCHAIN_FILE="${MR_SHELL_TOOLS_DIR}/ios.toolchain.cmake" \ -DPLATFORM=$pf \ + -DDEPLOYMENT_TARGET=$MR_DEPLOYMENT_TARGET_VER \ ${CMAKE_OTHER_OPTS} \ -GXcode @@ -72,12 +74,27 @@ echo "----------------------" echo "[*] compile $LIB_NAME" echo "----------------------" + +# 初始化构建命令 +camke_cmd="cmake --build ." + +# 以逗号分割目标名称,并为每个目标添加 --target 参数 +IFS=',' read -ra targets <<< "$CMAKE_TARGETS_NAME" +for target in "${targets[@]}"; do + camke_cmd="$camke_cmd --target $target" +done + + if [[ "$MR_DEBUG" == "debug" ]];then - cmake --build . --target $CMAKE_TARGET_NAME --config Debug -- CODE_SIGNING_ALLOWED=NO + camke_cmd="$camke_cmd --config Debug -- CODE_SIGNING_ALLOWED=NO" else - cmake --build . --target $CMAKE_TARGET_NAME --config Release -- CODE_SIGNING_ALLOWED=NO + camke_cmd="$camke_cmd --config Release -- CODE_SIGNING_ALLOWED=NO" fi + +# 执行构建命令 +eval "$camke_cmd" + if [[ -n $CMAKE_COMPONENT ]];then cmake --install . --component "$CMAKE_COMPONENT" else diff --git a/configs/default.sh b/do-compile/apple/dovi.sh old mode 100644 new mode 100755 similarity index 64% rename from configs/default.sh rename to do-compile/apple/dovi.sh index 3882ea9cd..4e9b63716 --- a/configs/default.sh +++ b/do-compile/apple/dovi.sh @@ -15,10 +15,20 @@ # limitations under the License. # -# when '-l all' will use blew default config: -apple_default_libs="openssl opus dav1d dvdread freetype fribidi harfbuzz unibreak ass ffmpeg smb2 bluray" +set -e -export ios_default_libs="$apple_default_libs" -export macos_default_libs="$apple_default_libs" -export tvos_default_libs="$apple_default_libs" -export android_default_libs="$apple_default_libs" \ No newline at end of file +THIS_DIR=$(DIRNAME=$(dirname "$0"); cd "$DIRNAME"; pwd) +cd "$THIS_DIR" + +echo "----------------------" +echo "[*] configure $LIB_NAME" +echo "----------------------" + +source ./rust-c-compatible.sh + +cd $MR_BUILD_SOURCE + +rust_c_build \ + "${MR_BUILD_SOURCE}/dolby_vision/Cargo.toml" \ + "dolby_vision" \ + "--features capi" \ No newline at end of file diff --git a/do-compile/apple/dvdnav.sh b/do-compile/apple/dvdnav.sh new file mode 100755 index 000000000..88ba20b0a --- /dev/null +++ b/do-compile/apple/dvdnav.sh @@ -0,0 +1,70 @@ +#! /usr/bin/env bash +# +# Copyright (C) 2021 Matt Reach + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set -e + +THIS_DIR=$(DIRNAME=$(dirname "$0"); cd "$DIRNAME"; pwd) +cd "$THIS_DIR" + +# prepare build config +CFG_FLAGS="--prefix=$MR_BUILD_PREFIX --disable-dependency-tracking --disable-silent-rules --disable-apidoc --enable-static --disable-shared" +CFLAGS="$MR_DEFAULT_CFLAGS" + +if [[ "$MR_DEBUG" == "debug" ]];then + CFG_FLAGS="${CFG_FLAGS} use_examples=yes" +fi + +# for cross compile +if [[ $(uname -m) != "$MR_ARCH" || "$MR_FORCE_CROSS" ]];then + echo "[*] cross compile, on $(uname -m) compile $MR_PLAT $MR_ARCH." + # https://www.gnu.org/software/automake/manual/html_node/Cross_002dCompilation.html + CFLAGS="$CFLAGS -isysroot $MR_SYS_ROOT" + CFG_FLAGS="$CFG_FLAGS --host=$MR_ARCH-apple-darwin --with-sysroot=$MR_SYS_ROOT" +fi + +echo "----------------------" +echo "[*] configurate $LIB_NAME" +echo "----------------------" + +cd $MR_BUILD_SOURCE + +if [[ -f 'configure' ]]; then + echo "reuse configure" +else + echo "auto generate configure" + autoreconf -if >/dev/null +fi + + +echo +echo "CC: $MR_CC" +echo "CFG_FLAGS: $CFG_FLAGS" +echo "CFLAGS: $CFLAGS" +echo + +./configure $CFG_FLAGS \ + CC="$MR_CC" \ + CFLAGS="$CFLAGS" \ + LDFLAGS="$CFLAGS" \ + >/dev/null + +#---------------------- +echo "----------------------" +echo "[*] compile $LIB_NAME" +echo "----------------------" + +make install -j$MR_HOST_NPROC >/dev/null \ No newline at end of file diff --git a/do-compile/apple/ffmpeg.sh b/do-compile/apple/ffmpeg.sh index 7f9b3741c..4a5564dc7 100755 --- a/do-compile/apple/ffmpeg.sh +++ b/do-compile/apple/ffmpeg.sh @@ -31,7 +31,7 @@ THIS_DIR=$(DIRNAME=$(dirname "$0"); cd "$DIRNAME"; pwd) cd "$THIS_DIR" # ffmpeg config options -source $MR_SHELL_CONFIGS_DIR/ffconfig/module.sh +source $MR_SHELL_CONFIGS_DIR/ffconfig/module-full.sh source $MR_SHELL_CONFIGS_DIR/ffconfig/auto-detect-third-libs.sh CFG_FLAGS= @@ -53,7 +53,7 @@ echo "[*] configure" if [[ ! -d $MR_BUILD_SOURCE ]]; then echo "" echo "!! ERROR" - echo "!! Can not find $MR_BUILD_SOURCE directory for $MR_BUILD_NAME" + echo "!! Can not find lib source: $MR_BUILD_SOURCE" echo "!! Run 'init-*.sh' first" echo "" exit 1 @@ -88,15 +88,19 @@ cp config.* $MR_BUILD_PREFIX make install >/dev/null mkdir -p $MR_BUILD_PREFIX/include/libffmpeg cp -f config.h $MR_BUILD_PREFIX/include/libffmpeg/ -[ -e config_components.h ] && cp -f config_components.h $MR_BUILD_PREFIX/include/libffmpeg/ +cp -f config_components.h $MR_BUILD_PREFIX/include/libffmpeg/ &> /dev/null || true # copy private header for ffmpeg-kit. -[ -e $MR_BUILD_SOURCE/libavutil/getenv_utf8.h ] && cp -f $MR_BUILD_SOURCE/libavutil/getenv_utf8.h $MR_BUILD_PREFIX/include/libavutil/ +cp -f $MR_BUILD_SOURCE/libavutil/getenv_utf8.h $MR_BUILD_PREFIX/include/libavutil/ &> /dev/null || true cp -f $MR_BUILD_SOURCE/libavutil/internal.h $MR_BUILD_PREFIX/include/libavutil/ cp -f $MR_BUILD_SOURCE/libavutil/libm.h $MR_BUILD_PREFIX/include/libavutil/ -[ -e $MR_BUILD_SOURCE/libavutil/attributes_internal.h ] && cp -f $MR_BUILD_SOURCE/libavutil/attributes_internal.h $MR_BUILD_PREFIX/include/libavutil/ +cp -f $MR_BUILD_SOURCE/libavutil/attributes_internal.h $MR_BUILD_PREFIX/include/libavutil/ &> /dev/null || true cp -f $MR_BUILD_SOURCE/libavcodec/mathops.h $MR_BUILD_PREFIX/include/libavcodec/ mkdir -p $MR_BUILD_PREFIX/include/libavcodec/x86/ cp -f $MR_BUILD_SOURCE/libavcodec/x86/mathops.h $MR_BUILD_PREFIX/include/libavcodec/x86/ mkdir -p $MR_BUILD_PREFIX/include/libavutil/x86/ -cp -f $MR_BUILD_SOURCE/libavutil/x86/asm.h $MR_BUILD_PREFIX/include/libavutil/x86/ \ No newline at end of file +cp -f $MR_BUILD_SOURCE/libavutil/x86/asm.h $MR_BUILD_PREFIX/include/libavutil/x86/ +#copy private header for hls.c +cp -f $MR_BUILD_SOURCE/libavformat/demux.h $MR_BUILD_PREFIX/include/libavformat/ &> /dev/null || true +cp -f $MR_BUILD_SOURCE/libavformat/http.h $MR_BUILD_PREFIX/include/libavformat/ &> /dev/null || true +cp -f $MR_BUILD_SOURCE/libavformat/hls_sample_encryption.h $MR_BUILD_PREFIX/include/libavformat/ &> /dev/null || true \ No newline at end of file diff --git a/do-compile/apple/fftutorial.sh b/do-compile/apple/fftutorial.sh new file mode 100755 index 000000000..6ee6607fa --- /dev/null +++ b/do-compile/apple/fftutorial.sh @@ -0,0 +1,108 @@ +#! /usr/bin/env bash +# +# Copyright (C) 2021 Matt Reach + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This script is based on projects below +# https://github.com/bilibili/ijkplayer + +set -e + +error_handler() { + echo "An error occurred!" + tail -n20 ${MR_BUILD_SOURCE}/ffbuild/config.log +} + +trap 'error_handler' ERR + +THIS_DIR=$(DIRNAME=$(dirname "$0"); cd "$DIRNAME"; pwd) +cd "$THIS_DIR" + +# ffmpeg config options +source $MR_SHELL_CONFIGS_DIR/ffconfig/module-lite.sh +source $MR_SHELL_CONFIGS_DIR/ffconfig/auto-detect-third-libs.sh + +CFG_FLAGS= +CFG_FLAGS="$CFG_FLAGS $COMMON_FF_CFG_FLAGS" +CFG_FLAGS="$CFG_FLAGS $THIRD_CFG_FLAGS" +CFG_FLAGS="$CFG_FLAGS --disable-avfilter" +CFG_FLAGS="$CFG_FLAGS --disable-avdevice" + +C_FLAGS="$MR_DEFAULT_CFLAGS" +EXTRA_LDFLAGS= +LDFLAGS="$C_FLAGS $EXTRA_LDFLAGS" +# C_FLAGS="$C_FLAGS -I/Users/matt/GitWorkspace/MoltenVK/Package/Release/MoltenVK/include" +# use system xml2 lib +# C_FLAGS="$C_FLAGS $(xml2-config --prefix=${MR_SYS_ROOT}/usr --cflags)" +# LDFLAGS="$C_FLAGS $(xml2-config --prefix=${MR_SYS_ROOT}/usr --libs)" + +# LDFLAGS="$LDFLAGS -framework IOKit -framework Metal -framework IOSurface -framework CoreGraphics -framework QuartzCore -framework AppKit -framework Foundation -lc++ /Users/matt/GitWorkspace/MoltenVK/Package/Release/MoltenVK/static/MoltenVK.xcframework/macos-arm64_x86_64/libMoltenVK.a" +echo "----------------------" +echo "[*] configure" + +if [[ ! -d $MR_BUILD_SOURCE ]]; then + echo "" + echo "!! ERROR" + echo "!! Can not find lib source: $MR_BUILD_SOURCE" + echo "!! Run 'init-*.sh' first" + echo "" + exit 1 +fi + +cd $MR_BUILD_SOURCE +if [[ -f "./config.h" ]]; then + echo 'reuse configure' +else + echo + echo "CC: $MR_CC" + echo "CFLAGS: $C_FLAGS" + echo "LDFLAG:$LDFLAGS" + echo "FF_CFG_FLAGS: $CFG_FLAGS" + echo + ./configure \ + $CFG_FLAGS \ + --cc="$MR_CC" \ + --as="perl ${MR_GAS_PERL} -arch ${MR_ARCH} -- $MR_CC" \ + --extra-cflags="$C_FLAGS" \ + --extra-cxxflags="$C_FLAGS" \ + --extra-ldflags="$LDFLAGS" +fi + +#---------------------- +echo "----------------------" +echo "[*] compile" + +make -j$MR_HOST_NPROC >/dev/null + +cp config.* $MR_BUILD_PREFIX +make install >/dev/null +mkdir -p $MR_BUILD_PREFIX/include/libffmpeg +cp -f config.h $MR_BUILD_PREFIX/include/libffmpeg/ +[ -e config_components.h ] && cp -f config_components.h $MR_BUILD_PREFIX/include/libffmpeg/ +# copy private header for ffmpeg-kit. +[ -e $MR_BUILD_SOURCE/libavutil/getenv_utf8.h ] && cp -f $MR_BUILD_SOURCE/libavutil/getenv_utf8.h $MR_BUILD_PREFIX/include/libavutil/ +cp -f $MR_BUILD_SOURCE/libavutil/internal.h $MR_BUILD_PREFIX/include/libavutil/ +cp -f $MR_BUILD_SOURCE/libavutil/libm.h $MR_BUILD_PREFIX/include/libavutil/ +[ -e $MR_BUILD_SOURCE/libavutil/attributes_internal.h ] && cp -f $MR_BUILD_SOURCE/libavutil/attributes_internal.h $MR_BUILD_PREFIX/include/libavutil/ +cp -f $MR_BUILD_SOURCE/libavcodec/mathops.h $MR_BUILD_PREFIX/include/libavcodec/ + +mkdir -p $MR_BUILD_PREFIX/include/libavcodec/x86/ +cp -f $MR_BUILD_SOURCE/libavcodec/x86/mathops.h $MR_BUILD_PREFIX/include/libavcodec/x86/ +mkdir -p $MR_BUILD_PREFIX/include/libavutil/x86/ +cp -f $MR_BUILD_SOURCE/libavutil/x86/asm.h $MR_BUILD_PREFIX/include/libavutil/x86/ +#copy private header for hls.c +cp -f $MR_BUILD_SOURCE/libavformat/demux.h $MR_BUILD_PREFIX/include/libavformat/ +cp -f $MR_BUILD_SOURCE/libavformat/http.h $MR_BUILD_PREFIX/include/libavformat/ +cp -f $MR_BUILD_SOURCE/libavformat/hls_sample_encryption.h $MR_BUILD_PREFIX/include/libavformat/ diff --git a/do-compile/apple/ijkffmpeg.sh b/do-compile/apple/ijkffmpeg.sh index 293a4b24f..676e70878 100755 --- a/do-compile/apple/ijkffmpeg.sh +++ b/do-compile/apple/ijkffmpeg.sh @@ -34,7 +34,7 @@ FFMPEG_CFG_FLAGS= FFMPEG_EXTRA_CFLAGS= export COMMON_FF_CFG_FLAGS= # use ijk ffmpeg config options -source $MR_SHELL_CONFIGS_DIR/ijk-ffmpeg-config/module.sh +source $MR_SHELL_CONFIGS_DIR/ijk-ffmpeg-config/module-lite.sh FFMPEG_CFG_FLAGS="$FFMPEG_CFG_FLAGS $COMMON_FF_CFG_FLAGS" # Advanced options (experts only): diff --git a/do-compile/apple/lcms2.sh b/do-compile/apple/lcms2.sh new file mode 100755 index 000000000..e6037da2d --- /dev/null +++ b/do-compile/apple/lcms2.sh @@ -0,0 +1,54 @@ +#! /usr/bin/env bash +# +# Copyright (C) 2021 Matt Reach + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set -e + +THIS_DIR=$(DIRNAME=$(dirname "$0"); cd "$DIRNAME"; pwd) +cd "$THIS_DIR" + +CFG_FLAGS="--prefix=$MR_BUILD_PREFIX --enable-static --disable-shared" +CFLAGS="$MR_DEFAULT_CFLAGS" + +# for cross compile +if [[ $(uname -m) != "$MR_ARCH" || "$MR_FORCE_CROSS" ]]; then + echo "[*] cross compile, on $(uname -m) compile $MR_PLAT $MR_ARCH." + CFLAGS="$CFLAGS -isysroot $MR_SYS_ROOT" + CFG_FLAGS="$CFG_FLAGS --host=$MR_ARCH-apple-darwin --with-sysroot=$MR_SYS_ROOT" +fi + +cd $MR_BUILD_SOURCE + +echo +echo "CC: $MR_CC" +echo "CFG_FLAGS: $CFG_FLAGS" +echo "CFLAGS: $CFLAGS" +echo + +echo "----------------------" +echo "[*] configure $LIB_NAME" +echo "----------------------" + +./configure $CFG_FLAGS \ + CC="$MR_CC" \ + CFLAGS="$CFLAGS" \ + LDFLAGS="$CFLAGS" + +echo "----------------------" +echo "[*] compile $LIB_NAME" +echo "----------------------" + +make -j$MR_HOST_NPROC install diff --git a/do-compile/apple/main.sh b/do-compile/apple/main.sh index 3d6784782..0ba1e9864 100755 --- a/do-compile/apple/main.sh +++ b/do-compile/apple/main.sh @@ -22,14 +22,15 @@ set -e THIS_DIR=$(DIRNAME=$(dirname "$0"); cd "$DIRNAME"; pwd) cd "$THIS_DIR" -# 循环编译所有的库 -for lib in $MR_VENDOR_LIBS -do - [[ ! -f "$MR_SHELL_CONFIGS_DIR/libs/${lib}.sh" ]] && (echo "❌$lib config not exist, compile will stop.";exit 1;) +function do_compile_a_lib() +{ + local lib_config="$1" + lib_config=$(make_absolute_path "$lib_config") + [[ ! -f "$lib_config" ]] && (echo "❌$lib_config config not exist, compile will stop."; exit 1;) echo "===[$MR_CMD $lib]====================" - source "$MR_SHELL_CONFIGS_DIR/libs/${lib}.sh" - + source "$lib_config" + echo "LIB_NAME : [$LIB_NAME]" echo "GIT_COMMIT : [$GIT_COMMIT]" echo "LIPO_LIBS : [$LIPO_LIBS]" @@ -38,8 +39,43 @@ do ./any.sh if [[ $? -eq 0 ]];then echo "🎉 Congrats" - echo "🚀 ${LIB_NAME} successfully $MR_CMD." + echo "🚀 ${LIB_NAME} ${GIT_COMMIT} successfully $MR_CMD." echo fi echo "====================================" -done \ No newline at end of file +} + +function compile_libs() +{ + # 循环编译所有的库 + for lib in $MR_VENDOR_LIBS + do + do_compile_a_lib "configs/libs/${lib}.sh" + done + + if [[ -n "$LIB_CONFIG_PATH" ]];then + echo + echo "install specific lib config : [$LIB_CONFIG_PATH]" + do_compile_a_lib "$LIB_CONFIG_PATH" + fi +} + +function parse_args() { + while [[ $# -gt 0 ]]; do + case "$1" in + -lib-config) + shift + LIB_CONFIG_PATH="$1" + ;; + *) + echo "unknown option: $1" + sleep 2 + ;; + esac + shift + done +} + +parse_args "$@" +echo "LIB_CONFIG_PATH:$LIB_CONFIG_PATH" +compile_libs \ No newline at end of file diff --git a/do-compile/apple/meson-compatible.sh b/do-compile/apple/meson-compatible.sh index 67ae2919c..d45a313ac 100755 --- a/do-compile/apple/meson-compatible.sh +++ b/do-compile/apple/meson-compatible.sh @@ -28,7 +28,7 @@ echo "[*] other meson flags: $MESON_OTHER_FLAGS" echo "----------------------" # prepare build config -CFG_FLAGS="--prefix=$MR_BUILD_PREFIX --default-library static" +CFG_FLAGS="--prefix=${MR_BUILD_PREFIX} --default-library static --pkg-config-path=${PKG_CONFIG_LIBDIR}" if [[ "$MR_DEBUG" == "debug" ]]; then CFG_FLAGS="$CFG_FLAGS --buildtype=debug" @@ -39,14 +39,28 @@ fi export CC="$MR_CC" export CXX="$MR_CXX" +cd $MR_BUILD_SOURCE +build=./meson_wksp +rm -rf $build +mkdir -p $build + +cross_file= if [[ $(uname -m) != "$MR_ARCH" || "$MR_FORCE_CROSS" ]]; then if [[ $MR_IS_SIMULATOR == 1 ]]; then - echo "[*] cross compile, on $(uname -m) compile $MR_PLAT $MR_ARCH simulator." - CFG_FLAGS="$CFG_FLAGS --cross-file $MR_SHELL_CONFIGS_DIR/meson-crossfiles/$MR_ARCH-$MR_PLAT-simulator.meson" + raw_cross_file="$MR_SHELL_CONFIGS_DIR/meson-crossfiles/$MR_ARCH-$MR_PLAT-simulator.meson" else - echo "[*] cross compile, on $(uname -m) compile $MR_PLAT $MR_ARCH." - CFG_FLAGS="$CFG_FLAGS --cross-file $MR_SHELL_CONFIGS_DIR/meson-crossfiles/$MR_ARCH-$MR_PLAT.meson" + raw_cross_file="$MR_SHELL_CONFIGS_DIR/meson-crossfiles/$MR_ARCH-$MR_PLAT.meson" fi + + cross_file=$build/meson-crossfile.meson + touch "$cross_file" + cat "$raw_cross_file" > "$cross_file" + + my_sed_i "s|__XCODE_DEVELOPER|$MR_XCODE_DEVELOPER|" "$cross_file" + my_sed_i "s|__DEPLOYMENT_TARGET|$MR_DEPLOYMENT_TARGET|" "$cross_file" + + echo "[*] using cross compile, cross file: $cross_file" + CFG_FLAGS="$CFG_FLAGS --cross-file $cross_file" fi CFG_FLAGS="$CFG_FLAGS $MESON_OTHER_FLAGS" @@ -58,10 +72,6 @@ echo "CFG_FLAGS: $CFG_FLAGS" echo "----------------------" echo -cd $MR_BUILD_SOURCE -build=./meson_wksp -rm -rf $build - meson setup $build $CFG_FLAGS cd $build diff --git a/do-compile/apple/moltenvk.sh b/do-compile/apple/moltenvk.sh new file mode 100755 index 000000000..7c53b8001 --- /dev/null +++ b/do-compile/apple/moltenvk.sh @@ -0,0 +1,187 @@ +#! /usr/bin/env bash +# +# Copyright (C) 2021 Matt Reach + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set -e +set -o pipefail + +THIS_DIR=$(DIRNAME=$(dirname "$0"); cd "$DIRNAME"; pwd) +cd "$THIS_DIR" + +dest= +scheme_suffix= +arch_param= +fetch_deps_arg= + +if [[ "$MR_PLAT" == 'ios' ]];then + if [[ $_MR_ARCH == 'arm64_simulator' ]];then + dest='iOS Simulator' + arch_param='arm64' + fetch_deps_arg='--iossim' + elif [[ $_MR_ARCH == 'x86_64_simulator' ]];then + dest='iOS Simulator' + arch_param='x86_64' + fetch_deps_arg='--iossim' + else + dest='iOS' + arch_param='arm64' + fetch_deps_arg='--ios' + fi + scheme_suffix='iOS only' + export MR_DEPLOYMENT_TARGET_VER=14.0 +elif [[ "$MR_PLAT" == 'tvos' ]];then + if [[ $_MR_ARCH == 'arm64_simulator' ]];then + dest='tvOS Simulator' + arch_param='arm64' + fetch_deps_arg='--tvossim' + elif [[ $_MR_ARCH == 'x86_64_simulator' ]];then + dest='tvOS Simulator' + arch_param='x86_64' + fetch_deps_arg='--tvossim' + else + dest='tvOS' + arch_param='arm64' + fetch_deps_arg='--tvos' + fi + scheme_suffix='tvOS only' + export MR_DEPLOYMENT_TARGET_VER=14.0 +elif [[ "$MR_PLAT" == 'macos' ]];then + if [[ $_MR_ARCH == 'arm64' ]];then + arch_param='arm64' + elif [[ $_MR_ARCH == 'x86_64' ]];then + arch_param='x86_64' + fi + dest='macOS' + scheme_suffix='macOS only' + fetch_deps_arg='--macos' + export MR_DEPLOYMENT_TARGET_VER=11.0 +fi + +echo "----------------------" +echo "[*] fetch dependencies for $LIB_NAME" +echo "----------------------" + +cd $MR_BUILD_SOURCE +if [ -d "External/build" ]; then + echo "dependencies already exist" +else + if [ -f "./fetchDependencies" ]; then + echo "fetching dependencies for $fetch_deps_arg..." + chmod +x ./fetchDependencies + ./fetchDependencies $fetch_deps_arg + else + echo "fetchDependencies script not found, trying with CMake..." + fi +fi + +cd "$THIS_DIR" + +echo "----------------------" +echo "[*] configurate $LIB_NAME" +echo "[*] destination: $dest" +echo "[*] deployment target: $MR_DEPLOYMENT_TARGET_VER" +echo "----------------------" + +echo "----------------------" +echo "[*] compile $LIB_NAME" +echo "[*] arch: $arch_param" +echo "----------------------" + +mkdir -p "$MR_BUILD_PREFIX/lib" + +xcodebuild -project "$MR_BUILD_SOURCE/MoltenVKPackaging.xcodeproj" \ + -scheme "MoltenVK Package ($scheme_suffix)" \ + -destination "generic/platform=$dest" \ + -configuration Release \ + CODE_SIGNING_ALLOWED=NO \ + build + +pkg_static_dir="$MR_BUILD_SOURCE/Package/Release/MoltenVK/static" +if [[ -d "$pkg_static_dir/MoltenVK.xcframework" ]]; then + xcframework_dir="$pkg_static_dir/MoltenVK.xcframework" + archs_dir=$(ls "$xcframework_dir/" 2>/dev/null | grep -v Info.plist | head -1) + if [[ -n "$archs_dir" && -f "$xcframework_dir/$archs_dir/libMoltenVK.a" ]]; then + cp "$xcframework_dir/$archs_dir/libMoltenVK.a" "$MR_BUILD_PREFIX/lib/" + echo "Copied libMoltenVK.a from xcframework to $MR_BUILD_PREFIX/lib/" + fi +fi + +alt_header_dir="$MR_BUILD_SOURCE/MoltenVK/include" +if [[ -d "$alt_header_dir" ]]; then + mkdir -p "$MR_BUILD_PREFIX/include" + # -L: follow symbolic links, copy actual files instead of symlinks + # This is needed because the source includes symlinks to External dependencies + cp -RL "$alt_header_dir"/* "$MR_BUILD_PREFIX/include/" + echo "Copied headers from source to $MR_BUILD_PREFIX/include/" +fi + +vulkan_headers="$MR_BUILD_SOURCE/External/Vulkan-Headers/include/vulkan" +if [[ -d "$vulkan_headers" ]]; then + mkdir -p "$MR_BUILD_PREFIX/include/vulkan" + cp -RL "$vulkan_headers"/* "$MR_BUILD_PREFIX/include/vulkan/" + echo "Copied Vulkan headers to $MR_BUILD_PREFIX/include/vulkan/" +fi + +vk_video_headers="$MR_BUILD_SOURCE/External/Vulkan-Headers/include/vk_video" +if [[ -d "$vk_video_headers" ]]; then + mkdir -p "$MR_BUILD_PREFIX/include/vk_video" + cp -RL "$vk_video_headers"/* "$MR_BUILD_PREFIX/include/vk_video/" + echo "Copied vk_video headers to $MR_BUILD_PREFIX/include/vk_video/" +fi + +echo "----------------------" +echo "[*] generate pkg-config files" +echo "----------------------" + +mkdir -p "$MR_BUILD_PREFIX/lib/pkgconfig" + +MOLTENVK_MAJOR=$(grep "MVK_VERSION_MAJOR" "$MR_BUILD_PREFIX/include/MoltenVK/mvk_private_api.h" | grep -oE "[0-9]+" | head -1) +MOLTENVK_MINOR=$(grep "MVK_VERSION_MINOR" "$MR_BUILD_PREFIX/include/MoltenVK/mvk_private_api.h" | grep -oE "[0-9]+" | head -1) +MOLTENVK_PATCH=$(grep "MVK_VERSION_PATCH" "$MR_BUILD_PREFIX/include/MoltenVK/mvk_private_api.h" | grep -oE "[0-9]+" | head -1) +MOLTENVK_VERSION="${MOLTENVK_MAJOR}.${MOLTENVK_MINOR}.${MOLTENVK_PATCH}" + +VULKAN_VERSION="1.3.250" + +cat > "$MR_BUILD_PREFIX/lib/pkgconfig/vulkan.pc" << EOF +prefix=${MR_BUILD_PREFIX} +exec_prefix=\${prefix} +libdir=\${exec_prefix}/lib +includedir=\${prefix}/include + +Name: Vulkan +Description: Vulkan loader (MoltenVK) +Version: ${VULKAN_VERSION} +Libs: -L\${libdir} -lMoltenVK +Libs.private: -framework Metal -framework Foundation -framework QuartzCore -framework IOKit -framework IOSurface -lc++ +Cflags: -I\${includedir} -I\${includedir}/vulkan +EOF + +cat > "$MR_BUILD_PREFIX/lib/pkgconfig/moltenvk.pc" << EOF +prefix=${MR_BUILD_PREFIX} +exec_prefix=\${prefix} +libdir=\${exec_prefix}/lib +includedir=\${prefix}/include + +Name: MoltenVK +Description: Vulkan implementation on Metal for macOS and iOS +Version: ${MOLTENVK_VERSION} +Libs: -L\${libdir} -lMoltenVK +Libs.private: -framework Metal -framework Foundation -framework QuartzCore -framework IOKit -framework IOSurface -lc++ +Cflags: -I\${includedir} -I\${includedir}/vulkan -I\${includedir}/MoltenVK +Requires: vulkan +EOF + +echo "Generated vulkan.pc (${VULKAN_VERSION}) and moltenvk.pc (${MOLTENVK_VERSION})" diff --git a/do-compile/apple/openssl.sh b/do-compile/apple/openssl.sh index d2a0f0449..996b4ea00 100755 --- a/do-compile/apple/openssl.sh +++ b/do-compile/apple/openssl.sh @@ -42,7 +42,7 @@ echo "CROSS_TOP:$CROSS_TOP" echo "CROSS_SDK:$CROSS_SDK" # no-hw no-asm -CFG_FLAGS="no-shared no-engine no-dynamic-engine no-static-engine \ +CFG_FLAGS="no-shared no-engine no-apps no-dynamic-engine no-static-engine \ no-dso no-ui-console no-tests \ --prefix=$MR_BUILD_PREFIX \ --openssldir=$MR_BUILD_PREFIX \ diff --git a/do-compile/apple/placebo.sh b/do-compile/apple/placebo.sh new file mode 100755 index 000000000..efabebfb0 --- /dev/null +++ b/do-compile/apple/placebo.sh @@ -0,0 +1,90 @@ +#! /usr/bin/env bash +# +# Copyright (C) 2021 Matt Reach + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set -e + +THIS_DIR=$(DIRNAME=$(dirname "$0"); cd "$DIRNAME"; pwd) +cd "$THIS_DIR" + +echo "----------------------" +echo "[*] init submodules for $LIB_NAME" +echo "----------------------" + +cd $MR_BUILD_SOURCE +git submodule update --init --recursive + +# Fix Python 3.14 compatibility issue in utils_gen.py +# ET.parse() returns ElementTree, need to call .getroot() to get Element +sed -i '' 's/registry = VkXML(ET.parse(xmlfile))/registry = VkXML(ET.parse(xmlfile).getroot())/g' src/vulkan/utils_gen.py + +cd "$THIS_DIR" + +BUILD_DIR="${MR_BUILD_SOURCE}/meson_build" +rm -rf "$BUILD_DIR" +mkdir -p "$BUILD_DIR" + +echo "----------------------" +echo "[*] configure $LIB_NAME" +echo "----------------------" + +# libplacebo uses meson, and we need to specify MoltenVK as Vulkan ICD +# Only enable vulkan backend (no opengl/d3d11) +# Use shaderc for SPIRV compilation + +# Find lcms2 and shaderc from our build + +# Set deployment target for macOS +export MACOSX_DEPLOYMENT_TARGET=11.0 + +MESON_OPTS="--buildtype=release \ +--prefix=$MR_BUILD_PREFIX \ +--default-library=static \ +-Dvulkan=enabled \ +-Dshaderc=enabled \ +-Dglslang=disabled \ +-Dopengl=disabled \ +-Dd3d11=disabled \ +-Dlcms=enabled \ +-Dtests=false \ +-Dbench=false \ +-Ddemos=false \ +-Dxxhash=disabled" + + +# Set up environment for MoltenVK +export VK_ICD_FILENAMES="${MR_BUILD_PREFIX}/share/vulkan/icd.d/MoltenVK_icd.json" + +# Disable assertions to fix Xcode SDK compatibility issue +# IMPORTANT: Preserve original CFLAGS which contains -arch parameter +export CFLAGS="${MR_DEFAULT_CFLAGS} -U_LIBCPP_ENABLE_ASSERTIONS" +export CXXFLAGS="${MR_DEFAULT_CFLAGS} -U_LIBCPP_ENABLE_ASSERTIONS" + +meson setup ${BUILD_DIR} ${MR_BUILD_SOURCE} ${MESON_OPTS} + +echo "----------------------" +echo "[*] compile $LIB_NAME" +echo "----------------------" + +cd "$BUILD_DIR" + +meson compile + +echo "----------------------" +echo "[*] install $LIB_NAME" +echo "----------------------" + +meson install \ No newline at end of file diff --git a/do-compile/apple/rust-c-compatible.sh b/do-compile/apple/rust-c-compatible.sh new file mode 100644 index 000000000..fa56d4afc --- /dev/null +++ b/do-compile/apple/rust-c-compatible.sh @@ -0,0 +1,136 @@ +#! /usr/bin/env bash +# +# Copyright (C) 2021 Matt Reach + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set -e + +if ! command -v rustup &> /dev/null; then + return 0 +fi + +case "$MR_PLAT" in +macos) + rustup target add aarch64-apple-darwin x86_64-apple-darwin + ;; +tvos) + rustup target add aarch64-apple-tvos aarch64-apple-tvos-sim x86_64-apple-tvos + ;; +ios) + rustup target add aarch64-apple-ios aarch64-apple-ios-sim x86_64-apple-ios + ;; +esac + +arch_target() { + local arch="$1" + local plat="$2" + case "$plat" in + macos) + case "$arch" in + arm64) + echo "aarch64-apple-darwin" + ;; + x86_64) + echo "x86_64-apple-darwin" + ;; + *) + echo "$arch-apple-darwin" + ;; + esac + ;; + tvos) + case "$arch" in + arm64) + echo "aarch64-apple-tvos" + ;; + arm64_simulator) + echo "aarch64-apple-tvos-sim" + ;; + x86_64_simulator) + echo "x86_64-apple-tvos" + ;; + *) + echo "$arch-apple-tvos" + ;; + esac + ;; + *) + case "$arch" in + arm64) + echo "aarch64-apple-ios" + ;; + arm64_simulator) + echo "aarch64-apple-ios-sim" + ;; + x86_64_simulator) + echo "x86_64-apple-ios" + ;; + *) + echo "$arch-apple-ios" + ;; + esac + ;; + esac +} + +rust_c_build() { + local manifest_path="$1" + local sub_dir="$2" + shift 2 + local extra_args="$@" + + if [[ -n "$sub_dir" && -d "${MR_BUILD_SOURCE}/${sub_dir}" ]]; then + cd "${MR_BUILD_SOURCE}/${sub_dir}" + fi + + local build_dir="${MR_BUILD_SOURCE}/${LIB_NAME}_build" + rm -rf "$build_dir" + mkdir -p "$build_dir" + cd "$build_dir" + + export CC="$MR_CC" + export CXX="$MR_CXX" + export CFLAGS="-fPIC $CFLAGS $MR_DEPLOYMENT_TARGET" + export CXXFLAGS="-fPIC $CXXFLAGS" + + local build_type="release" + if [[ "$MR_DEBUG" == "debug" ]]; then + build_type="debug" + fi + + local target + target=$(arch_target "$MR_ARCH" "$MR_PLAT") + + echo "----------------------" + echo "[*] compile $LIB_NAME" + echo "---------------------" + echo "CC: $MR_CC" + echo "CFLAGS: $CFLAGS" + echo "build_type: $build_type" + echo "prefix: $MR_BUILD_PREFIX" + echo "cargo target: $target" + echo "----------------------" + + + cargo cinstall \ + --manifest-path="$manifest_path" \ + --target="$target" \ + --prefix="$MR_BUILD_PREFIX" \ + --lib \ + --library-type=staticlib \ + --$build_type \ + --no-default-features \ + $extra_args +} diff --git a/do-compile/apple/shaderc.sh b/do-compile/apple/shaderc.sh new file mode 100755 index 000000000..8bce6c6c6 --- /dev/null +++ b/do-compile/apple/shaderc.sh @@ -0,0 +1,105 @@ +#! /usr/bin/env bash +# +# Copyright (C) 2021 Matt Reach + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set -e + +THIS_DIR=$(DIRNAME=$(dirname "$0"); cd "$DIRNAME"; pwd) +cd "$THIS_DIR" + +echo "----------------------" +echo "[*] sync dependencies for $LIB_NAME" +echo "----------------------" + +cd $MR_BUILD_SOURCE +if [ -f "./utils/git-sync-deps" ]; then + echo "running git-sync-deps..." + chmod +x ./utils/git-sync-deps + ./utils/git-sync-deps +else + echo "git-sync-deps not found" + exit 1 +fi + +cd "$THIS_DIR" + +pf= +if [[ "$MR_PLAT" == 'ios' ]];then + if [[ $_MR_ARCH == 'arm64_simulator' ]];then + pf='SIMULATORARM64' + elif [[ $_MR_ARCH == 'x86_64_simulator' ]];then + pf='SIMULATOR64' + else + pf='OS64' + fi + export MR_DEPLOYMENT_TARGET_VER=13.0 +elif [[ "$MR_PLAT" == 'tvos' ]];then + if [[ $_MR_ARCH == 'arm64_simulator' ]];then + pf='SIMULATORARM64_TVOS' + elif [[ $_MR_ARCH == 'x86_64_simulator' ]];then + pf='SIMULATOR_TVOS' + else + pf='TVOS' + fi + export MR_DEPLOYMENT_TARGET_VER=13.0 +elif [[ "$MR_PLAT" == 'macos' ]];then + if [[ $_MR_ARCH == 'arm64' ]];then + pf='MAC_ARM64' + elif [[ $_MR_ARCH == 'x86_64' ]];then + pf='MAC' + fi + # glslang requires macOS 10.15+ for std::filesystem + export MR_DEPLOYMENT_TARGET_VER=11.0 +fi + +echo "----------------------" +echo "[*] configurate $LIB_NAME" +echo "----------------------" + +build="${MR_BUILD_SOURCE}/cmake_wksp" +rm -rf "$build" +mkdir -p "$build" +cd "$build" + +cmake -S ${MR_BUILD_SOURCE} \ + -DCMAKE_PREFIX_PATH=${PKG_CONFIG_LIBDIR} \ + -DCMAKE_INSTALL_PREFIX=${MR_BUILD_PREFIX} \ + -DCMAKE_TOOLCHAIN_FILE="${MR_SHELL_TOOLS_DIR}/ios.toolchain.cmake" \ + -DPLATFORM=$pf \ + -DDEPLOYMENT_TARGET=$MR_DEPLOYMENT_TARGET_VER \ + -DCMAKE_BUILD_TYPE=Release \ + -DSHADERC_SKIP_TESTS=ON \ + -DBUILD_SHARED_LIBS=OFF \ + -DCMAKE_MACOSX_BUNDLE=OFF \ + -GNinja + +echo "----------------------" +echo "[*] compile $LIB_NAME" +echo "----------------------" + +cmake --build . --config Release --parallel + +echo "----------------------" +echo "[*] install $LIB_NAME" +echo "----------------------" + +cmake --install . + +# clean up pkgconfig files +rm -f ${MR_BUILD_PREFIX}/lib/pkgconfig/SPIRV-Tools-shared.pc +rm -f ${MR_BUILD_PREFIX}/lib/pkgconfig/SPIRV-Tools.pc +rm -f ${MR_BUILD_PREFIX}/lib/pkgconfig/shaderc.pc +rm -f ${MR_BUILD_PREFIX}/lib/pkgconfig/shaderc_static.pc \ No newline at end of file diff --git a/do-compile/apple/webp.sh b/do-compile/apple/webp.sh new file mode 100755 index 000000000..829e10359 --- /dev/null +++ b/do-compile/apple/webp.sh @@ -0,0 +1,27 @@ +#! /usr/bin/env bash +# +# Copyright (C) 2021 Matt Reach + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# append "-DCMAKE_MACOSX_BUNDLE=NO" fix compile error: + +# CMake Error at CMakeLists.txt:537 (install): +# install TARGETS given no BUNDLE DESTINATION for MACOSX_BUNDLE executable +# target "dwebp". + +# call common cmake build shell +./cmake-compatible.sh "-DBUILD_SHARED_LIBS=OFF -DWEBP_LINK_STATIC=ON -DCMAKE_MACOSX_BUNDLE=NO -DWEBP_ENABLE_SIMD=ON -DWEBP_BUILD_ANIM_UTILS=OFF -DWEBP_BUILD_CWEBP=OFF -DWEBP_BUILD_DWEBP=OFF -DWEBP_BUILD_GIF2WEBP=OFF -DWEBP_BUILD_IMG2WEBP=OFF -DWEBP_BUILD_VWEBP=OFF -DWEBP_BUILD_WEBPINFO=OFF -DWEBP_BUILD_LIBWEBPMUX=OFF -DWEBP_BUILD_WEBPMUX=OFF -DWEBP_BUILD_EXTRAS=OFF -DWEBP_NEAR_LOSSLESS=OFF" + +# clean cmake +rm -rf ${MR_BUILD_PREFIX}/share \ No newline at end of file diff --git a/do-compile/apple/xml2.sh b/do-compile/apple/xml2.sh index ad575cb6c..fbbbd945a 100755 --- a/do-compile/apple/xml2.sh +++ b/do-compile/apple/xml2.sh @@ -19,52 +19,58 @@ # ./cmake-compatible.sh "-DBUILD_SHARED_LIBS=0 -DLIBXML2_WITH_PROGRAMS=0 -DLIBXML2_WITH_ZLIB=1 -DLIBXML2_WITH_PYTHON=0 -DLIBXML2_WITH_ICONV=1" -CFLAGS="$MR_DEFAULT_CFLAGS" - -# prepare build config -CFG_FLAGS="--prefix=$MR_BUILD_PREFIX" - -# for cross compile -if [[ $(uname -m) != "$MR_ARCH" || "$MR_FORCE_CROSS" ]];then - echo "[*] cross compile, on $(uname -m) compile $MR_PLAT $MR_ARCH." - # https://www.gnu.org/software/automake/manual/html_node/Cross_002dCompilation.html - CFLAGS="$CFLAGS -isysroot $MR_SYS_ROOT" - # $MR_ARCH-apple-darwin - CFG_FLAGS="$CFG_FLAGS --host=$MR_ARCH-apple-darwin --with-sysroot=$MR_SYS_ROOT" -fi - -echo "----------------------" -echo "[*] configurate $LIB_NAME" -echo "----------------------" - -cd $MR_BUILD_SOURCE - -echo -echo "CC: $MR_CC" -echo "CFG_FLAGS: $CFG_FLAGS" -echo "CFLAGS: $CFLAGS" -echo - -export CFLAGS="$CFLAGS" -export LDFLAGS="$CFLAGS" - -export CC="$MR_CC" -export CXX="$MR_CXX" - -./autogen.sh \ - $CFG_FLAGS \ - --prefix=$MR_BUILD_PREFIX \ - --enable-static --disable-shared \ - --disable-fast-install \ - --without-python \ - --without-debug \ - --with-zlib \ - --with-pic \ - --without-lzma - -echo "----------------------" -echo "[*] compile $LIB_NAME" -echo "----------------------" - -make clean >/dev/null -make install -j${MR_HOST_NPROC} \ No newline at end of file +# CFLAGS="$MR_DEFAULT_CFLAGS" + +# # prepare build config +# CFG_FLAGS="--prefix=$MR_BUILD_PREFIX" + +# # for cross compile +# if [[ $(uname -m) != "$MR_ARCH" || "$MR_FORCE_CROSS" ]];then +# echo "[*] cross compile, on $(uname -m) compile $MR_PLAT $MR_ARCH." +# # https://www.gnu.org/software/automake/manual/html_node/Cross_002dCompilation.html +# CFLAGS="$CFLAGS -isysroot $MR_SYS_ROOT" +# # $MR_ARCH-apple-darwin +# CFG_FLAGS="$CFG_FLAGS --host=$MR_ARCH-apple-darwin --with-sysroot=$MR_SYS_ROOT" +# fi + +# echo "----------------------" +# echo "[*] configurate $LIB_NAME" +# echo "----------------------" + +# cd $MR_BUILD_SOURCE + +# echo +# echo "CC: $MR_CC" +# echo "CFG_FLAGS: $CFG_FLAGS" +# echo "CFLAGS: $CFLAGS" +# echo + +# export CFLAGS="$CFLAGS" +# export LDFLAGS="$CFLAGS" + +# export CC="$MR_CC" +# export CXX="$MR_CXX" + +# ./autogen.sh \ +# $CFG_FLAGS \ +# --prefix=$MR_BUILD_PREFIX \ +# --enable-static --disable-shared \ +# --disable-fast-install \ +# --without-python \ +# --without-debug \ +# --with-zlib \ +# --with-pic \ +# --without-lzma + +# echo "----------------------" +# echo "[*] compile $LIB_NAME" +# echo "----------------------" + +# make clean >/dev/null +# make install -j${MR_HOST_NPROC} + +set -e + +CFG_FLAGS="-Ddocs=disabled -Ddebugging=disabled -Dpython=disabled -Dzlib=enabled" + +./meson-compatible.sh "$CFG_FLAGS" \ No newline at end of file diff --git a/do-init/copy-local-repo.sh b/do-init/copy-local-repo.sh index 6f4986bfc..adf91b041 100755 --- a/do-init/copy-local-repo.sh +++ b/do-init/copy-local-repo.sh @@ -26,7 +26,7 @@ function main() { local dest_repo=$2 cd $src_repo - local full_src_repo_path="file://"$(PWD) + local full_src_repo_path="file://$(pwd)" cd - >/dev/null if [[ -d $dest_repo ]]; then @@ -37,4 +37,4 @@ function main() { git clone -b localBranch "$full_src_repo_path" $dest_repo --depth=1 } -main $* \ No newline at end of file +main $* diff --git a/do-init/init-repo.sh b/do-init/init-repo.sh index 787377849..2517f3ae7 100755 --- a/do-init/init-repo.sh +++ b/do-init/init-repo.sh @@ -27,12 +27,16 @@ env_assert "GIT_COMMIT" env_assert "GIT_LOCAL_REPO" env_assert "GIT_UPSTREAM" env_assert "MR_WORKSPACE" +env_assert "SKIP_PULL_BASE" +env_assert "SMART_APPLY" +echo_env "MR_LIB_CONFIG_PATH" +echo_env "PATCH_DIR" echo "===check env end===" GIT_LOCAL_REPO="${MR_WORKSPACE}/${GIT_LOCAL_REPO}" function pull_common() { - echo "== pull $REPO_DIR base ==" + if [[ -d "$GIT_LOCAL_REPO" ]]; then cd "$GIT_LOCAL_REPO" [[ -d .git/rebase-apply ]] && git am --skip @@ -44,13 +48,14 @@ function pull_common() { git remote add origin "$GIT_UPSTREAM" echo "force update origin to: $GIT_UPSTREAM" fi - if [[ "$SKIP_PULL_BASE" ]]; then + if [[ "$SKIP_PULL_BASE" == "1" ]]; then echo "⚠️ skip pull $REPO_DIR because you set SKIP_PULL_BASE env." else + echo "== pull $REPO_DIR base ==" git fetch --all --tags fi else - if [[ "$SKIP_PULL_BASE" ]]; then + if [[ "$SKIP_PULL_BASE" == "1" ]]; then echo "== local repo $REPO_DIR not exist,must clone by net firstly. ==" echo "try:unset SKIP_PULL_BASE" exit -1 @@ -65,29 +70,80 @@ function pull_common() { cd - >/dev/null } +# 用于合并ffmpeg的patch +apply_patch_smart() { + local patch_file=$1 + + if [ -z "$patch_file" ]; then + echo "Usage: apply_patch_smart " + return 1 + fi + + # 尝试静默执行 git am + if git am "$patch_file" > /dev/null 2>&1; then + echo "Successfully applied $(basename "$patch_file")" + else + echo "git am failed. Falling back to [git apply --reject]..." + + # 终止失败的 am 进程 + git am --abort + + # 尝试使用 --reject 强制应用 + if git apply --reject "$patch_file"; then + echo "----------------------------------------------------" + echo "Patch partially applied with --reject." + echo "Please check for .rej files and resolve them manually." + echo "----------------------------------------------------" + + # 列出生成的 .rej 文件提醒用户 + find . -name "*.rej" + else + echo "Error: [git apply --reject] also failed. Please check the patch file." + return 1 + fi + fi +} + function apply_patches() { - if [[ "$SKIP_FFMPEG_PATHCHES" && $REPO_DIR == 'ffmpeg' ]]; then - echo "⚠️ skip apply $REPO_DIR patches,because you set SKIP_FFMPEG_PATHCHES env." + + if [[ -z "$PATCH_DIR" ]]; then + echo "$REPO_DIR hasn't any patch" return fi - - local plat="$MR_PLAT" - local patch_dir="${THIS_DIR}/../patches/$REPO_DIR" - - if [[ -d "${patch_dir}_${plat}" ]]; then - patch_dir="${patch_dir}_${plat}" - fi - if [[ -d "$patch_dir" ]]; then + + local patch_base_dir=$(dirname "$MR_LIB_CONFIG_PATH") + local patch_dir="${patch_base_dir}/$PATCH_DIR" + patch_dir=$(make_absolute_path "$patch_dir") + local patch_dirs=( + "$patch_dir" + "${patch_dir}-${MR_PLAT}" + "${patch_dir}-pro" + ) + + for patch_dir in "${patch_dirs[@]}"; do + if [[ ! -d "$patch_dir" ]]; then + echo "patch dir not exist: $patch_dir, skip." + continue + fi + echo - echo "== Applying patches: $(basename $patch_dir) → $(basename $PWD) ==" - git am --whitespace=fix --keep $patch_dir/*.patch - if [[ $? -ne 0 ]]; then - echo 'Apply patches failed!' - git am --skip - exit 1 + echo "== Applying patches: $(basename "$patch_dir") → $(basename "$PWD") ==" + if [[ "$SMART_APPLY" == "1" ]]; then + for patch_file in "$patch_dir"/*.patch; do + if ! apply_patch_smart "$patch_file"; then + echo "Apply patch failed: $patch_file" + exit 1 + fi + done + else + if ! git am --whitespace=fix --keep "$patch_dir"/*.patch; then + echo 'Apply patches failed!' + git am --skip + exit 1 + fi fi echo - fi + done } function make_arch_repo() { @@ -107,11 +163,6 @@ function make_arch_repo() { cd - >/dev/null } -function usage() { - echo "usage:" - echo "$0 ios|macos|tvos|all [arm64|x86_64]" -} - function main() { case "$MR_PLAT" in ios | macos | tvos | android) diff --git a/do-init/main.sh b/do-init/main.sh index 7b065d5e7..d452306dc 100755 --- a/do-init/main.sh +++ b/do-init/main.sh @@ -22,11 +22,57 @@ set -e THIS_DIR=$(DIRNAME=$(dirname "$0"); cd "$DIRNAME"; pwd) cd "$THIS_DIR" -for lib in $MR_VENDOR_LIBS -do - echo "===[init $lib]===========" - [[ ! -f "$MR_SHELL_CONFIGS_DIR/libs/${lib}.sh" ]] && (echo "❌$lib config not exist,init will stop.";exit 1;) - source "$MR_SHELL_CONFIGS_DIR/libs/${lib}.sh" +function parse_args() { + while [[ $# -gt 0 ]]; do + case "$1" in + --skip-pull-base) + SKIP_PULL_BASE=1 + ;; + --smart-apply) + SMART_APPLY=1 + ;; + -lib-config) + shift + LIB_CONFIG_PATH="$1" + ;; + *) + echo "unknown option: $1" + sleep 2 + ;; + esac + shift + done +} + +function do_init_a_lib() +{ + local lib_config="$1" + lib_config=$(make_absolute_path "$lib_config") + [[ ! -f "$lib_config" ]] && (echo "❌$lib_config config not exist,init will stop.";exit 1;) + echo "===[init $lib_config]====================" + [[ ! -f "$lib_config" ]] && (echo "❌$lib_config config not exist,init will stop.";exit 1;) + source "$lib_config" + export MR_LIB_CONFIG_PATH="$lib_config" ./init-repo.sh echo "=========================" -done \ No newline at end of file +} + +function main() { + + export SKIP_PULL_BASE=${SKIP_PULL_BASE:-0} + export SMART_APPLY=${SMART_APPLY:-0} + + for lib in $MR_VENDOR_LIBS + do + do_init_a_lib "configs/libs/${lib}.sh" + done + + if [[ -n "$LIB_CONFIG_PATH" ]];then + echo + echo "init specific lib config : [$LIB_CONFIG_PATH]" + do_init_a_lib "$LIB_CONFIG_PATH" + fi +} + +parse_args "$@" +main \ No newline at end of file diff --git a/do-install/correct-pc.sh b/do-install/correct-pc.sh new file mode 100755 index 000000000..9080e9c9d --- /dev/null +++ b/do-install/correct-pc.sh @@ -0,0 +1,81 @@ +#! /usr/bin/env bash +# +# Copyright (C) 2022 Matt Reach + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set -e + +THIS_DIR=$(DIRNAME=$(dirname "$0"); cd "$DIRNAME"; pwd) +cd "$THIS_DIR" + +function correct_pc_file(){ + local fix_path="$1" + local dir=${PWD} + + echo "fix pc files in folder: $fix_path" + cd "$fix_path" + for pc in `find . -type f -name "*.pc"` ; + do + local pkgconfig=$(cd $(dirname "$pc"); pwd) + local lib_dir=$(cd $(dirname "$pkgconfig"); pwd) + local base_dir=$(cd $(dirname "$lib_dir"); pwd) + local include_dir="${base_dir}/include" + local bin_dir="${base_dir}/bin" + + # fix absolute path which contains arch suffix bug,such as /path/to/opus-arch/lib + #-L/Users/runner/work/MRFFToolChainBuildShell/MRFFToolChainBuildShell/build/product/macos/opus-arch/lib + #-> + #-L/Users/runner/work/MRFFToolChainBuildShell/MRFFToolChainBuildShell/build/product/macos/universal/opus/lib + # my_sed_i "s|${LIB_NAME}-arm64[^/]*/|universal/${LIB_NAME}/|g" "$pc" + # 匹配逻辑: + # 1. 匹配 [^/]* -> 路径中最后一个斜杠后的字符(即 LIB_NAME) + # 2. 匹配 -(arm64|x86) -> 紧跟其后的架构标识 + # 3. 匹配 [^/]* -> 架构后的剩余后缀(如 _simulator) + # 4. 替换为 universal/\1 -> \1 就是第一对括号捕获到的 LIB_NAME + + + my_sed_i "s|universal-simulator|universal|g" "$pc" + my_sed_i "s|\([^/]*\)-arm64[^/]*|universal/\1|g" "$pc" + my_sed_i "s|\([^/]*\)-x86[^/]*|universal/\1|g" "$pc" + + # 全局替换 prefix= 开头后面的内容 + old_base=$(sed -n 's/^prefix=//p' "$pc") + my_sed_i "s|$old_base|$base_dir|g" "$pc" + + # 具有局限性,比如 includedir=/Users/matt/GitWorkspace/fsplayer/FFToolChain/build/product/ios/universal-simulator/bluray + # my_sed_i "s|^prefix=.*|prefix=$base_dir|" "$pc" + # my_sed_i "s|^exec_prefix=[^$].*|exec_prefix=$bin_dir|" $pc + # my_sed_i "s|^libdir=[^$].*|libdir=$lib_dir|" "$pc" + # my_sed_i "s|^includedir=[^$].*include|includedir=$include_dir|" "$pc" + + # Fix absolute paths to other internal dependencies + # Pattern: -L/any/path/PRODUCT_NAME/PLATFORM/universal/LIB_NAME/lib + # We want to replace the "/any/path/PRODUCT_NAME/PLATFORM" part with the local equivalent. + # Since we know the local product root is the parent of 'universal', we can use that. + # base_dir is lib folder, we need to go up two levels to get to product root,eg: ios + local product_root=$(cd "$base_dir";cd ..;cd ..;pwd) + # escaped for sed + local escaped_root=$(echo "$product_root" | sed 's/\//\\\//g') + + # 1. Fix -L/path/to/universal/LIB_NAME/lib -> -L/local/product/universal/LIB_NAME/lib + my_sed_i "s|-L/[^ ]*/universal/\([^ /]*\)/lib|-L$escaped_root/universal/\1/lib|g" "$pc" + # 2. Fix -I/path/to/universal/LIB_NAME/include -> -I/local/product/universal/LIB_NAME/include + my_sed_i "s|-I/[^ ]*/universal/\([^ /]*\)/include|-I$escaped_root/universal/\1/include|g" "$pc" + done + + cd "$dir" +} + +correct_pc_file "$1" \ No newline at end of file diff --git a/do-install/download-uncompress.sh b/do-install/download-uncompress.sh index 7ce21938a..c34210c8e 100755 --- a/do-install/download-uncompress.sh +++ b/do-install/download-uncompress.sh @@ -22,7 +22,6 @@ cd "$THIS_DIR" echo "=== [$0] check env begin===" env_assert "MR_WORKSPACE" -env_assert "MR_PRE_ROOT" env_assert "MR_DOWNLOAD_URL" env_assert "MR_DOWNLOAD_ONAME" env_assert "MR_UNCOMPRESS_DIR" @@ -35,10 +34,12 @@ function download() { mkdir -p $(dirname "$dst") local tname="${dst}.tmp" - curl -L "$MR_DOWNLOAD_URL" -o "$tname" + curl -fL --retry 3 --retry-delay 5 --retry-max-time 30 "$MR_DOWNLOAD_URL" -o "$tname" if [[ $? -eq 0 ]];then mv "$tname" "${dst}" + else + rm -f "$tname" fi } @@ -55,7 +56,7 @@ function extract(){ } function install() { - local dst="${MR_PRE_ROOT}/${MR_DOWNLOAD_ONAME}" + local dst="${MR_WORKSPACE}/pre/${MR_DOWNLOAD_ONAME}" if [[ -f "$dst" ]];then echo "$dst already exist,skip download." else diff --git a/do-install/install-pre-lib.sh b/do-install/install-pre-lib.sh index 93eca899f..cc24e1e97 100755 --- a/do-install/install-pre-lib.sh +++ b/do-install/install-pre-lib.sh @@ -38,6 +38,8 @@ function install_plat() { export MR_UNCOMPRESS_DIR="$MR_WORKSPACE/product/$MR_PLAT/universal${join}" ./download-uncompress.sh + #由于解压后可能包含依赖库,所以保险起见修正全部的pc文件 + ./correct-pc.sh "${MR_UNCOMPRESS_DIR}" } if [[ "$MR_PLAT" == 'ios' || "$MR_PLAT" == 'tvos' ]];then diff --git a/do-install/main.sh b/do-install/main.sh index bd1ced0ff..67a24aace 100755 --- a/do-install/main.sh +++ b/do-install/main.sh @@ -23,22 +23,16 @@ THIS_DIR=$(DIRNAME=$(dirname "$0"); cd "$DIRNAME"; pwd) cd "$THIS_DIR" function parse_lib_config() { - + local t=$(echo "PRE_COMPILE_TAG_$MR_PLAT" | tr '[:lower:]' '[:upper:]') local vt=$(eval echo "\$$t") - + if test -z $vt ;then - TAG="$PRE_COMPILE_TAG" - else - TAG="$vt" - fi - - if test -z $TAG ;then - echo "tag can't be nil" - usage + echo "$t can't be nil" exit fi - + + export TAG=$vt # opus-1.3.1-231124151836 # yuv-stable-eb6e7bb-250225223408 LIB_NAME=$(echo $TAG | awk -F - '{print $1}') @@ -48,19 +42,19 @@ function parse_lib_config() { local temp=${TAG#$prefix} # 去掉后缀 VER=${temp%$suffix} - + export VER - export TAG export LIB_NAME } -# 循环编译所有的库 -for lib in $MR_VENDOR_LIBS -do - [[ ! -f "$MR_SHELL_CONFIGS_DIR/libs/${lib}.sh" ]] && (echo "❌$lib config not exist,install will stop.";exit 1;) - - echo "===[install $lib]====================" - source "$MR_SHELL_CONFIGS_DIR/libs/${lib}.sh" +function do_install_a_lib() +{ + local lib_config="$1" + lib_config=$(make_absolute_path "$lib_config") + [[ ! -f "$lib_config" ]] && (echo "❌$lib_config config not exist,install will stop."; exit 1;) + + echo "===[install $lib_config]====================" + source "$lib_config" parse_lib_config if [[ $FORCE_XCFRAMEWORK ]];then ./install-pre-xcf.sh @@ -68,9 +62,48 @@ do ./install-pre-lib.sh fi echo "====================================" -done +} -if [[ ! "$FORCE_XCFRAMEWORK" ]];then - correct_pc_file "$MR_WORKSPACE/product/$MR_PLAT" -fi +function install_libs() +{ + # 循环安装所有的库 + for lib in $MR_VENDOR_LIBS + do + do_install_a_lib "configs/libs/${lib}.sh" + done + + if [[ -n "$LIB_CONFIG_PATH" ]];then + echo + echo "install specific lib config : [$LIB_CONFIG_PATH]" + do_install_a_lib "$LIB_CONFIG_PATH" + fi +} + +function parse_args() { + while [[ $# -gt 0 ]]; do + case "$1" in + -lib-config) + shift + LIB_CONFIG_PATH="$1" + ;; + -correct-pc) + shift + CORRECT_PC="$1" + ;; + *) + echo "unknown option: $1" + sleep 2 + ;; + esac + shift + done +} +parse_args "$@" + +if [[ -n "$CORRECT_PC" ]];then + echo "correct pc file : [$CORRECT_PC]" + ./correct-pc.sh "$CORRECT_PC" +else + install_libs +fi diff --git a/main.sh b/main.sh index e8a18d491..8a3d6dc38 100755 --- a/main.sh +++ b/main.sh @@ -56,12 +56,10 @@ case $MR_PLAT in esac if [[ "$MR_ACTION" == "init" || "$MR_ACTION" == "install" ]];then - ./do-$MR_ACTION/main.sh "$@" + ./do-$MR_ACTION/main.sh "${MR_UNKNOWN_OPTIONS[@]}" else - ./do-$MR_ACTION/$plat/main.sh "$@" + ./do-$MR_ACTION/$plat/main.sh "${MR_UNKNOWN_OPTIONS[@]}" fi echo "---$MR_ACTION end-------------------------------" -echo - -elapsed \ No newline at end of file +elapsed diff --git a/patches/bluray/0002-add-bd_open_fs-function.patch b/patches/bluray/0002-add-bd_open_fs-function.patch index feab0bcc5..19c04e18b 100644 --- a/patches/bluray/0002-add-bd_open_fs-function.patch +++ b/patches/bluray/0002-add-bd_open_fs-function.patch @@ -1,7 +1,7 @@ From 9e37c2db7c233b4696437a8921125920d4e7594c Mon Sep 17 00:00:00 2001 From: qianlongxu Date: Fri, 11 Oct 2024 11:48:11 +0800 -Subject: [PATCH] add bd_open_fs function +Subject: [PATCH 2] add bd_open_fs function --- Makefile.am | 1 + diff --git a/patches/ffmpeg b/patches/ffmpeg deleted file mode 120000 index 76e66e0b1..000000000 --- a/patches/ffmpeg +++ /dev/null @@ -1 +0,0 @@ -ffmpeg-n6.1 \ No newline at end of file diff --git a/patches/ffmpeg-n4.0/0001-h264_ps-null-pointer-fault-tolerant.patch b/patches/ffmpeg-n4.0/0001-h264_ps-null-pointer-fault-tolerant.patch index 44c016fc0..8355fea12 100644 --- a/patches/ffmpeg-n4.0/0001-h264_ps-null-pointer-fault-tolerant.patch +++ b/patches/ffmpeg-n4.0/0001-h264_ps-null-pointer-fault-tolerant.patch @@ -1,7 +1,7 @@ From 396e4f1c0021145ee42866a302930dbb1e812838 Mon Sep 17 00:00:00 2001 From: qianlongxu Date: Fri, 29 Jul 2022 12:48:33 +0800 -Subject: [PATCH 01/16] h264_ps null pointer fault tolerant +Subject: [PATCH 01] h264_ps null pointer fault tolerant --- libavcodec/h264_ps.c | 8 ++++---- diff --git a/patches/ffmpeg-n4.0/0002-flv-support-hevc.patch b/patches/ffmpeg-n4.0/0002-flv-support-hevc.patch index 38939a954..cdb98540a 100644 --- a/patches/ffmpeg-n4.0/0002-flv-support-hevc.patch +++ b/patches/ffmpeg-n4.0/0002-flv-support-hevc.patch @@ -1,7 +1,7 @@ From 8dd266cd98e193b467774a50bb1cb648aeefa3e0 Mon Sep 17 00:00:00 2001 From: qianlongxu Date: Fri, 29 Jul 2022 14:19:54 +0800 -Subject: [PATCH] flv support hevc +Subject: [PATCH 02] flv support hevc --- libavformat/flv.h | 1 + diff --git a/patches/ffmpeg-n4.0/0003-dict-add-more-util-method.patch b/patches/ffmpeg-n4.0/0003-dict-add-more-util-method.patch index f0a22be43..a93326e63 100644 --- a/patches/ffmpeg-n4.0/0003-dict-add-more-util-method.patch +++ b/patches/ffmpeg-n4.0/0003-dict-add-more-util-method.patch @@ -1,7 +1,7 @@ From 77387f8a47c69225f336a7aeaf6a3e6298ebe2c3 Mon Sep 17 00:00:00 2001 From: qianlongxu Date: Fri, 29 Jul 2022 12:50:10 +0800 -Subject: [PATCH 03/16] dict add more util method +Subject: [PATCH 03] dict add more util method --- libavutil/dict.c | 39 ++++++++++++++++++++++++++++++++++++++- diff --git a/patches/ffmpeg-n4.0/0004-file-use-file_seek.patch b/patches/ffmpeg-n4.0/0004-file-use-file_seek.patch index a8b8532a6..512cef796 100644 --- a/patches/ffmpeg-n4.0/0004-file-use-file_seek.patch +++ b/patches/ffmpeg-n4.0/0004-file-use-file_seek.patch @@ -1,7 +1,7 @@ From 3fe418f1962704b301d68d9898b6102ec04dcde0 Mon Sep 17 00:00:00 2001 From: qianlongxu Date: Fri, 29 Jul 2022 12:54:09 +0800 -Subject: [PATCH 04/16] file use file_seek +Subject: [PATCH 04] file use file_seek --- libavformat/file.c | 3 ++- diff --git a/patches/ffmpeg-n4.0/0005-avio-cache-about.patch b/patches/ffmpeg-n4.0/0005-avio-cache-about.patch index e1a5bdc19..ef7bea66c 100644 --- a/patches/ffmpeg-n4.0/0005-avio-cache-about.patch +++ b/patches/ffmpeg-n4.0/0005-avio-cache-about.patch @@ -1,7 +1,7 @@ From 05b73eb2c510fc25f82cfc136f51a06031e6af44 Mon Sep 17 00:00:00 2001 From: qianlongxu Date: Fri, 29 Jul 2022 12:57:13 +0800 -Subject: [PATCH 05/16] avio cache about +Subject: [PATCH 05] avio cache about --- libavformat/avio.c | 6 ++---- diff --git a/patches/ffmpeg-n4.0/0006-mov-add-allow_multi_extradata-opt.patch b/patches/ffmpeg-n4.0/0006-mov-add-allow_multi_extradata-opt.patch index 44d8ea873..150bcc934 100644 --- a/patches/ffmpeg-n4.0/0006-mov-add-allow_multi_extradata-opt.patch +++ b/patches/ffmpeg-n4.0/0006-mov-add-allow_multi_extradata-opt.patch @@ -1,7 +1,7 @@ From 871fff7c5c8e7e702dccc2129e1ae64d7fe3f33b Mon Sep 17 00:00:00 2001 From: qianlongxu Date: Fri, 29 Jul 2022 12:59:12 +0800 -Subject: [PATCH 06/16] mov add allow_multi_extradata opt +Subject: [PATCH 06] mov add allow_multi_extradata opt --- libavformat/isom.h | 2 ++ diff --git a/patches/ffmpeg-n4.0/0007-hls-read_header2.patch b/patches/ffmpeg-n4.0/0007-hls-read_header2.patch index df135a334..e7b528786 100644 --- a/patches/ffmpeg-n4.0/0007-hls-read_header2.patch +++ b/patches/ffmpeg-n4.0/0007-hls-read_header2.patch @@ -1,7 +1,7 @@ From b91fad7c8957a9eb97b9d383142b272f35f7778e Mon Sep 17 00:00:00 2001 From: qianlongxu Date: Fri, 29 Jul 2022 12:59:58 +0800 -Subject: [PATCH 07/16] hls read_header2 +Subject: [PATCH 07] hls read_header2 --- libavformat/avformat.h | 5 ++ diff --git a/patches/ffmpeg-n4.0/0008-add-AV_PKT_FLAG_NEW_SEG.patch b/patches/ffmpeg-n4.0/0008-add-AV_PKT_FLAG_NEW_SEG.patch index c11cb57ff..f6a4c7f55 100644 --- a/patches/ffmpeg-n4.0/0008-add-AV_PKT_FLAG_NEW_SEG.patch +++ b/patches/ffmpeg-n4.0/0008-add-AV_PKT_FLAG_NEW_SEG.patch @@ -1,7 +1,7 @@ From 717083123cc79ab41c69629af584f89547797072 Mon Sep 17 00:00:00 2001 From: qianlongxu Date: Fri, 29 Jul 2022 12:57:50 +0800 -Subject: [PATCH 08/16] add AV_PKT_FLAG_NEW_SEG +Subject: [PATCH 08] add AV_PKT_FLAG_NEW_SEG --- libavcodec/avcodec.h | 1 + diff --git a/patches/ffmpeg-n4.0/0009-network-ff_sendto.patch b/patches/ffmpeg-n4.0/0009-network-ff_sendto.patch index d65a16465..83e53cc93 100644 --- a/patches/ffmpeg-n4.0/0009-network-ff_sendto.patch +++ b/patches/ffmpeg-n4.0/0009-network-ff_sendto.patch @@ -1,7 +1,7 @@ From 134e70adfd4add1ffb8ebfcc4cfb4f13ba2944ed Mon Sep 17 00:00:00 2001 From: qianlongxu Date: Fri, 29 Jul 2022 13:00:17 +0800 -Subject: [PATCH 09/16] network ff_sendto +Subject: [PATCH 09] network ff_sendto --- libavformat/network.c | 46 +++++++++++++++++++++++++++++++++++++++++++ diff --git a/patches/ffmpeg-n4.0/0010-tcp-hook.patch b/patches/ffmpeg-n4.0/0010-tcp-hook.patch index fe97ab21d..d15e7784d 100644 --- a/patches/ffmpeg-n4.0/0010-tcp-hook.patch +++ b/patches/ffmpeg-n4.0/0010-tcp-hook.patch @@ -1,7 +1,7 @@ From 391125a7c250b027bded465ca88da11caaee8231 Mon Sep 17 00:00:00 2001 From: qianlongxu Date: Fri, 29 Jul 2022 13:01:01 +0800 -Subject: [PATCH 10/16] tcp hook +Subject: [PATCH 10] tcp hook --- libavformat/http.c | 53 +++- diff --git a/patches/ffmpeg-n4.0/0011-update-make-file.patch b/patches/ffmpeg-n4.0/0011-update-make-file.patch index 6cccb1033..cddd5c48a 100644 --- a/patches/ffmpeg-n4.0/0011-update-make-file.patch +++ b/patches/ffmpeg-n4.0/0011-update-make-file.patch @@ -1,7 +1,7 @@ From 0dc8f7a7858d66dd17d65614189c640dab2f68a8 Mon Sep 17 00:00:00 2001 From: qianlongxu Date: Fri, 29 Jul 2022 13:01:10 +0800 -Subject: [PATCH 11/16] update make file +Subject: [PATCH 11] update make file --- libavformat/Makefile | 9 +++++++++ diff --git a/patches/ffmpeg-n4.0/0012-fix-ffmpeg-constructed-wrong-avcc-for-videotoolbox-h.patch b/patches/ffmpeg-n4.0/0012-fix-ffmpeg-constructed-wrong-avcc-for-videotoolbox-h.patch deleted file mode 100644 index de661f940..000000000 --- a/patches/ffmpeg-n4.0/0012-fix-ffmpeg-constructed-wrong-avcc-for-videotoolbox-h.patch +++ /dev/null @@ -1,59 +0,0 @@ -From 86fccbeadf36bcee5a800d63d51dba8560434298 Mon Sep 17 00:00:00 2001 -From: qianlongxu -Date: Mon, 16 May 2022 16:15:10 +0800 -Subject: [PATCH 12/16] fix ffmpeg constructed wrong avcc for videotoolbox - hwaccel. - ---- - libavcodec/videotoolbox.c | 35 ++++++++++++++++++++++++++++++++++- - 1 file changed, 34 insertions(+), 1 deletion(-) - -diff --git a/libavcodec/videotoolbox.c b/libavcodec/videotoolbox.c -index 57b6698..79ab799 100644 ---- a/libavcodec/videotoolbox.c -+++ b/libavcodec/videotoolbox.c -@@ -152,7 +152,40 @@ CFDataRef ff_videotoolbox_avcc_extradata_create(AVCodecContext *avctx) - if (vtctx) - memcpy(vtctx->sps, h->ps.sps->data + 1, 3); - -- data = CFDataCreate(kCFAllocatorDefault, vt_extradata, vt_extradata_size); -+ /* -+ ffmpeg constructed avcc is wrong,but i dont't why;eg: -+ ff constructed avcc: -+ 014d401effe1001b674d401eec806c1ef3fff8140013f88000000080000019078b16cb01000468ebec4c -+ avctx->extradata avcc: -+ 014d401effe1001c674d401eec806c1ef3fff8140013f8800000030080000019078b16cb01000568ebec4c80 -+ */ -+ if (avctx->extradata_size != vt_extradata_size) { -+ char msg[256]; -+ { -+ char buffer[128]; -+ sprintf(buffer, "%s", "ff avcc maybe wrong:"); -+ int len = (int)strlen(buffer); -+ int size = FFMIN(vt_extradata_size, 127 - len) / 2; -+ for (int i = 0; i < size; i++) { -+ len += sprintf(buffer + len, "%02X", vt_extradata[i]); -+ } -+ sprintf(msg, "%s", buffer); -+ } -+ { -+ char buffer[128]; -+ sprintf(buffer, "%s", "\nuse origin avcc:"); -+ int len = (int)strlen(buffer); -+ int size = FFMIN(avctx->extradata_size, 127 - len) / 2; -+ for (int i = 0; i < size; i++) { -+ len += sprintf(buffer + len, "%02X", avctx->extradata[i]); -+ } -+ sprintf(msg + strlen(msg), "%s", buffer); -+ } -+ av_log(avctx, AV_LOG_INFO, "%s\n", msg); -+ data = CFDataCreate(kCFAllocatorDefault, avctx->extradata, avctx->extradata_size); -+ } else { -+ data = CFDataCreate(kCFAllocatorDefault, vt_extradata, vt_extradata_size); -+ } - av_free(vt_extradata); - return data; - } --- -2.30.1 (Apple Git-130) - diff --git a/patches/ffmpeg-n4.0/0012-fix-ffmpeg4-constructed-wrong-avcc-for-videotoolbox.patch b/patches/ffmpeg-n4.0/0012-fix-ffmpeg4-constructed-wrong-avcc-for-videotoolbox.patch new file mode 100644 index 000000000..0eff09a42 --- /dev/null +++ b/patches/ffmpeg-n4.0/0012-fix-ffmpeg4-constructed-wrong-avcc-for-videotoolbox.patch @@ -0,0 +1,122 @@ +From e662624b224ae76d275e2628664e6c91ded296c1 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Tue, 10 Jun 2025 18:24:40 +0800 +Subject: [PATCH 12] fix ffmpeg4 constructed wrong avcc for videotoolbox + +--- + libavcodec/h264_ps.c | 4 +++ + libavcodec/videotoolbox.c | 61 ++++++++++++++++++++++++++++++++++----- + 2 files changed, 57 insertions(+), 8 deletions(-) + +diff --git a/libavcodec/h264_ps.c b/libavcodec/h264_ps.c +index 15fb8b6..8cdb93c 100644 +--- a/libavcodec/h264_ps.c ++++ b/libavcodec/h264_ps.c +@@ -752,6 +752,10 @@ int ff_h264_decode_picture_parameter_set(GetBitContext *gb, AVCodecContext *avct + } + memcpy(pps->data, gb->buffer, pps->data_size); + ++ // Re-add the removed stop bit (may be used by hwaccels). ++ if (!(bit_length & 7) && pps->data_size < sizeof(pps->data)) ++ pps->data[pps->data_size++] = 0x80; ++ + pps->sps_id = get_ue_golomb_31(gb); + if ((unsigned)pps->sps_id >= MAX_SPS_COUNT || + !ps->sps_list[pps->sps_id]) { +diff --git a/libavcodec/videotoolbox.c b/libavcodec/videotoolbox.c +index 57b6698..064efe0 100644 +--- a/libavcodec/videotoolbox.c ++++ b/libavcodec/videotoolbox.c +@@ -118,14 +118,49 @@ int ff_videotoolbox_alloc_frame(AVCodecContext *avctx, AVFrame *frame) + + #define AV_W8(p, v) *(p) = (v) + ++static int escape_ps(uint8_t* dst, const uint8_t* src, int src_size) ++{ ++ int i; ++ int size = src_size; ++ uint8_t* p = dst; ++ ++ for (i = 0; i < src_size; i++) { ++ if (i + 2 < src_size && ++ src[i] == 0x00 && ++ src[i + 1] == 0x00 && ++ src[i + 2] <= 0x03) { ++ if (dst) { ++ *p++ = src[i++]; ++ *p++ = src[i]; ++ *p++ = 0x03; ++ } else { ++ i++; ++ } ++ size++; ++ } else if (dst) ++ *p++ = src[i]; ++ } ++ ++ if (dst) ++ av_assert0((p - dst) == size); ++ ++ return size; ++} ++ + CFDataRef ff_videotoolbox_avcc_extradata_create(AVCodecContext *avctx) + { + VTContext *vtctx = avctx->internal->hwaccel_priv_data; + H264Context *h = avctx->priv_data; + CFDataRef data = NULL; + uint8_t *p; +- int vt_extradata_size = 6 + 2 + h->ps.sps->data_size + 3 + h->ps.pps->data_size; +- uint8_t *vt_extradata = av_malloc(vt_extradata_size); ++ int sps_size = escape_ps(NULL, h->ps.sps->data, h->ps.sps->data_size); ++ int pps_size = escape_ps(NULL, h->ps.pps->data, h->ps.pps->data_size); ++ int vt_extradata_size; ++ uint8_t *vt_extradata; ++ ++ vt_extradata_size = 6 + 2 + sps_size + 3 + pps_size; ++ vt_extradata = av_malloc(vt_extradata_size); ++ + if (!vt_extradata) + return NULL; + +@@ -137,14 +172,14 @@ CFDataRef ff_videotoolbox_avcc_extradata_create(AVCodecContext *avctx) + AV_W8(p + 3, h->ps.sps->data[3]); /* level */ + AV_W8(p + 4, 0xff); /* 6 bits reserved (111111) + 2 bits nal size length - 3 (11) */ + AV_W8(p + 5, 0xe1); /* 3 bits reserved (111) + 5 bits number of sps (00001) */ +- AV_WB16(p + 6, h->ps.sps->data_size); +- memcpy(p + 8, h->ps.sps->data, h->ps.sps->data_size); +- p += 8 + h->ps.sps->data_size; ++ AV_WB16(p + 6, sps_size); ++ p += 8; ++ p += escape_ps(p, h->ps.sps->data, h->ps.sps->data_size); + AV_W8(p + 0, 1); /* number of pps */ +- AV_WB16(p + 1, h->ps.pps->data_size); +- memcpy(p + 3, h->ps.pps->data, h->ps.pps->data_size); ++ AV_WB16(p + 1, pps_size); ++ p += 3; ++ p += escape_ps(p, h->ps.pps->data, h->ps.pps->data_size); + +- p += 3 + h->ps.pps->data_size; + av_assert0(p - vt_extradata == vt_extradata_size); + + // save sps header (profile/level) used to create decoder session, +@@ -152,6 +187,16 @@ CFDataRef ff_videotoolbox_avcc_extradata_create(AVCodecContext *avctx) + if (vtctx) + memcpy(vtctx->sps, h->ps.sps->data + 1, 3); + ++// { ++// char buffer[128]; ++// sprintf(buffer, "%s", "\nnew avcc:"); ++// int len = (int)strlen(buffer); ++// for (int i = 0; i < vt_extradata_size; i++) { ++// len += sprintf(buffer + len, "%02x", avctx->extradata[i]); ++// } ++// av_log(avctx, AV_LOG_INFO, "%s\n", buffer); ++// } ++ + data = CFDataCreate(kCFAllocatorDefault, vt_extradata, vt_extradata_size); + av_free(vt_extradata); + return data; +-- +2.39.5 (Apple Git-154) + diff --git a/patches/ffmpeg-n4.0/0013-fix-lrcdec-read-line-bug-on-osx.patch b/patches/ffmpeg-n4.0/0013-fix-lrcdec-read-line-bug-on-osx.patch index 68a383c7e..afeeb81d3 100644 --- a/patches/ffmpeg-n4.0/0013-fix-lrcdec-read-line-bug-on-osx.patch +++ b/patches/ffmpeg-n4.0/0013-fix-lrcdec-read-line-bug-on-osx.patch @@ -1,7 +1,7 @@ From 015c7279c0e3580be66fe87063dcf48298191199 Mon Sep 17 00:00:00 2001 From: qianlongxu Date: Wed, 27 Apr 2022 17:01:17 +0800 -Subject: [PATCH 13/16] fix lrcdec read line bug on osx. +Subject: [PATCH 13] fix lrcdec read line bug on osx. --- libavformat/lrcdec.c | 20 +++++++++++--------- diff --git a/patches/ffmpeg-n4.0/0014-Correct-the-wrong-codecpar-codec_id-which-read-from-.patch b/patches/ffmpeg-n4.0/0014-Correct-the-wrong-codecpar-codec_id-which-read-from-.patch index 3ffc0f1da..cf0bfae73 100644 --- a/patches/ffmpeg-n4.0/0014-Correct-the-wrong-codecpar-codec_id-which-read-from-.patch +++ b/patches/ffmpeg-n4.0/0014-Correct-the-wrong-codecpar-codec_id-which-read-from-.patch @@ -1,7 +1,7 @@ From 3f27485e4d79e17ff598c0aabecd24d0d9381e43 Mon Sep 17 00:00:00 2001 From: YoushiQian Date: Mon, 9 May 2022 09:53:18 +0800 -Subject: [PATCH 14/16] Correct the wrong codecpar->codec_id which read from +Subject: [PATCH 14] Correct the wrong codecpar->codec_id which read from MIME of ID3tags, but the real data was encoded in PNG/JPEG/TIFF --- diff --git a/patches/ffmpeg-n4.0/0015-support-to-parse-all-tracks-language-meta-when-open-.patch b/patches/ffmpeg-n4.0/0015-support-to-parse-all-tracks-language-meta-when-open-.patch index 7f1ead3e0..3ff2af72a 100644 --- a/patches/ffmpeg-n4.0/0015-support-to-parse-all-tracks-language-meta-when-open-.patch +++ b/patches/ffmpeg-n4.0/0015-support-to-parse-all-tracks-language-meta-when-open-.patch @@ -1,7 +1,7 @@ From d8265ca339e8549f481d470e7a3943820a5ac44a Mon Sep 17 00:00:00 2001 From: YoushiQian Date: Wed, 8 Jun 2022 14:07:32 +0800 -Subject: [PATCH 15/16] support to parse all tracks' language meta when open +Subject: [PATCH 15] support to parse all tracks' language meta when open the bluray input --- diff --git a/patches/ffmpeg-n4.0/0016-lavf-mov-add-heic-demuxer.patch b/patches/ffmpeg-n4.0/0016-lavf-mov-add-heic-demuxer.patch index 6ce1f7e35..f558742de 100644 --- a/patches/ffmpeg-n4.0/0016-lavf-mov-add-heic-demuxer.patch +++ b/patches/ffmpeg-n4.0/0016-lavf-mov-add-heic-demuxer.patch @@ -1,7 +1,7 @@ From 509423fcdd289709d5ff30b17ef69499ae7492c1 Mon Sep 17 00:00:00 2001 From: youshiqian Date: Wed, 31 Aug 2022 11:44:24 +0800 -Subject: [PATCH] lavf/mov: add heic demuxer +Subject: [PATCH 16] lavf/mov: add heic demuxer (https://github.com/bluez-sh/FFmpeg/commit/9a885cddb3550ab863a60d02c5fb78e4ae206cf1). refer to below links for details: https://trac.ffmpeg.org/ticket/6521 https://trac.ffmpeg.org/ticket/7621#comment:10 diff --git a/patches/ffmpeg-n4.0/0017-fix-test.c-1-10-fatal-error-libxml2-libxml-xmlversio.patch b/patches/ffmpeg-n4.0/0017-fix-test.c-1-10-fatal-error-libxml2-libxml-xmlversio.patch new file mode 100644 index 000000000..88463abaa --- /dev/null +++ b/patches/ffmpeg-n4.0/0017-fix-test.c-1-10-fatal-error-libxml2-libxml-xmlversio.patch @@ -0,0 +1,26 @@ +From 0ca2211639dcef90bdf463998d6124b1eff86e8e Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Fri, 6 Jun 2025 13:08:04 +0800 +Subject: [PATCH 17] fix test.c:1:10: fatal error: 'libxml2/libxml/xmlversion.h' + file not found + +--- + configure | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/configure b/configure +index 15e6c32..3b25725 100755 +--- a/configure ++++ b/configure +@@ -6112,7 +6112,7 @@ enabled libzmq && require_pkg_config libzmq libzmq zmq.h zmq_ctx_new + enabled libzvbi && require_pkg_config libzvbi zvbi-0.2 libzvbi.h vbi_decoder_new && + { test_cpp_condition libzvbi.h "VBI_VERSION_MAJOR > 0 || VBI_VERSION_MINOR > 2 || VBI_VERSION_MINOR == 2 && VBI_VERSION_MICRO >= 28" || + enabled gpl || die "ERROR: libzvbi requires version 0.2.28 or --enable-gpl."; } +-enabled libxml2 && require_pkg_config libxml2 libxml-2.0 libxml2/libxml/xmlversion.h xmlCheckVersion ++enabled libxml2 && require_pkg_config libxml2 libxml-2.0 libxml/xmlversion.h xmlCheckVersion + enabled mediacodec && { enabled jni || die "ERROR: mediacodec requires --enable-jni"; } + enabled mmal && { check_lib mmal interface/mmal/mmal.h mmal_port_connect -lmmal_core -lmmal_util -lmmal_vc_client -lbcm_host || + { ! enabled cross_compile && +-- +2.39.5 (Apple Git-154) + diff --git a/patches/ffmpeg-n5.1/0006-fix-ffmpeg-constructed-wrong-avcc-for-vi.patch b/patches/ffmpeg-n5.1/0006-fix-ffmpeg-constructed-wrong-avcc-for-vi.patch deleted file mode 100644 index a5f843676..000000000 --- a/patches/ffmpeg-n5.1/0006-fix-ffmpeg-constructed-wrong-avcc-for-vi.patch +++ /dev/null @@ -1,59 +0,0 @@ -From 742d238b20c760356ece1e23522e740fcfd8737f Mon Sep 17 00:00:00 2001 -From: qianlongxu -Date: Mon, 16 May 2022 16:15:10 +0800 -Subject: [PATCH 06] fix ffmpeg constructed wrong avcc for - videotoolbox hwaccel. - ---- - libavcodec/videotoolbox.c | 35 ++++++++++++++++++++++++++++++++++- - 1 file changed, 34 insertions(+), 1 deletion(-) - -diff --git a/libavcodec/videotoolbox.c b/libavcodec/videotoolbox.c -index d61d310..82fa993 100644 ---- a/libavcodec/videotoolbox.c -+++ b/libavcodec/videotoolbox.c -@@ -222,7 +222,40 @@ CFDataRef ff_videotoolbox_avcc_extradata_create(AVCodecContext *avctx) - if (vtctx) - memcpy(vtctx->sps, h->ps.sps->data + 1, 3); - -- data = CFDataCreate(kCFAllocatorDefault, vt_extradata, vt_extradata_size); -+ /* -+ ffmpeg constructed avcc is wrong,but i dont't why;eg: -+ ff constructed avcc: -+ 014d401effe1001b674d401eec806c1ef3fff8140013f88000000080000019078b16cb01000468ebec4c -+ avctx->extradata avcc: -+ 014d401effe1001c674d401eec806c1ef3fff8140013f8800000030080000019078b16cb01000568ebec4c80 -+ */ -+ if (avctx->extradata_size != vt_extradata_size) { -+ char msg[256]; -+ { -+ char buffer[128]; -+ sprintf(buffer, "%s", "ff avcc maybe wrong:"); -+ int len = (int)strlen(buffer); -+ int size = FFMIN(vt_extradata_size, 127 - len) / 2; -+ for (int i = 0; i < size; i++) { -+ len += sprintf(buffer + len, "%02X", vt_extradata[i]); -+ } -+ sprintf(msg, "%s", buffer); -+ } -+ { -+ char buffer[128]; -+ sprintf(buffer, "%s", "\nuse origin avcc:"); -+ int len = (int)strlen(buffer); -+ int size = FFMIN(avctx->extradata_size, 127 - len) / 2; -+ for (int i = 0; i < size; i++) { -+ len += sprintf(buffer + len, "%02X", avctx->extradata[i]); -+ } -+ sprintf(msg + strlen(msg), "%s", buffer); -+ } -+ av_log(avctx, AV_LOG_INFO, "%s\n", msg); -+ data = CFDataCreate(kCFAllocatorDefault, avctx->extradata, avctx->extradata_size); -+ } else { -+ data = CFDataCreate(kCFAllocatorDefault, vt_extradata, vt_extradata_size); -+ } - av_free(vt_extradata); - return data; - } --- -2.39.3 (Apple Git-145) - diff --git a/patches/ffmpeg-n6.1/0019-supportsFamily-is-only-available-on-iOS-13.0-or-newe.patch b/patches/ffmpeg-n5.1/0020-supportsFamily-is-only-available-on-iOS-13.0-or-newe.patch similarity index 94% rename from patches/ffmpeg-n6.1/0019-supportsFamily-is-only-available-on-iOS-13.0-or-newe.patch rename to patches/ffmpeg-n5.1/0020-supportsFamily-is-only-available-on-iOS-13.0-or-newe.patch index 905b7462a..e0b83f798 100644 --- a/patches/ffmpeg-n6.1/0019-supportsFamily-is-only-available-on-iOS-13.0-or-newe.patch +++ b/patches/ffmpeg-n5.1/0020-supportsFamily-is-only-available-on-iOS-13.0-or-newe.patch @@ -1,7 +1,7 @@ From 1ebd4c062a886049f3c3255c50fe56f6621b9844 Mon Sep 17 00:00:00 2001 From: qianlongxu Date: Fri, 26 Jul 2024 16:11:14 +0800 -Subject: [PATCH 19] 'supportsFamily:' is only available on iOS 13.0 or newer +Subject: [PATCH 20] 'supportsFamily:' is only available on iOS 13.0 or newer --- libavfilter/metal/utils.m | 2 +- diff --git a/patches/ffmpeg-n5.1/0021-fix-android-ffmpeg5-test.c-1-10-fatal-error-libxml2-.patch b/patches/ffmpeg-n5.1/0021-fix-android-ffmpeg5-test.c-1-10-fatal-error-libxml2-.patch new file mode 100644 index 000000000..1572dc5e2 --- /dev/null +++ b/patches/ffmpeg-n5.1/0021-fix-android-ffmpeg5-test.c-1-10-fatal-error-libxml2-.patch @@ -0,0 +1,26 @@ +From ef9366b55686e04b680471a21b2d51829962fa30 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Fri, 6 Jun 2025 14:04:00 +0800 +Subject: [PATCH 21] fix android ffmpeg5 test.c:1:10: fatal error: + 'libxml2/libxml/xmlversion.h' + +--- + configure | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/configure b/configure +index 231b18d..4fe7922 100755 +--- a/configure ++++ b/configure +@@ -6688,7 +6688,7 @@ enabled libzmq && require_pkg_config libzmq "libzmq >= 4.2.1" zmq.h z + enabled libzvbi && require_pkg_config libzvbi zvbi-0.2 libzvbi.h vbi_decoder_new && + { test_cpp_condition libzvbi.h "VBI_VERSION_MAJOR > 0 || VBI_VERSION_MINOR > 2 || VBI_VERSION_MINOR == 2 && VBI_VERSION_MICRO >= 28" || + enabled gpl || die "ERROR: libzvbi requires version 0.2.28 or --enable-gpl."; } +-enabled libxml2 && require_pkg_config libxml2 libxml-2.0 libxml2/libxml/xmlversion.h xmlCheckVersion ++enabled libxml2 && require_pkg_config libxml2 libxml-2.0 libxml/xmlversion.h xmlCheckVersion + enabled mbedtls && { check_pkg_config mbedtls mbedtls mbedtls/x509_crt.h mbedtls_x509_crt_init || + check_pkg_config mbedtls mbedtls mbedtls/ssl.h mbedtls_ssl_init || + check_lib mbedtls mbedtls/ssl.h mbedtls_ssl_init -lmbedtls -lmbedx509 -lmbedcrypto || +-- +2.39.5 (Apple Git-154) + diff --git a/patches/ffmpeg-n5.1/0030-fix-dash-init-fragment-url-is-invalid-truncated-bug.patch b/patches/ffmpeg-n5.1/0030-fix-dash-init-fragment-url-is-invalid-truncated-bug.patch new file mode 100644 index 000000000..3d4cb7d3c --- /dev/null +++ b/patches/ffmpeg-n5.1/0030-fix-dash-init-fragment-url-is-invalid-truncated-bug.patch @@ -0,0 +1,43 @@ +From ca6bf697d914be79fcc4d4314c06101d279b6ef3 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Wed, 21 May 2025 18:05:52 +0800 +Subject: [PATCH] fix dash init fragment url is "invalid:truncated" bug + +--- + libavformat/dashdec.c | 12 +++++++++++- + 1 file changed, 11 insertions(+), 1 deletion(-) + +diff --git a/libavformat/dashdec.c b/libavformat/dashdec.c +index 0a6c46b..616187f 100644 +--- a/libavformat/dashdec.c ++++ b/libavformat/dashdec.c +@@ -477,6 +477,10 @@ static char *get_content_url(xmlNodePtr *baseurl_nodes, + int i; + char *text; + char *url = NULL; ++ ++ if (strlen(val) >= max_url_size) { ++ max_url_size += 256; ++ } + char *tmp_str = av_mallocz(max_url_size); + + if (!tmp_str) +@@ -495,8 +499,14 @@ static char *get_content_url(xmlNodePtr *baseurl_nodes, + } + } + +- if (val) ++ if (val) { ++ int tmp_max_url_size = strlen(tmp_str) + strlen(val) + 1; ++ if (tmp_max_url_size > max_url_size) { ++ max_url_size = tmp_max_url_size; ++ tmp_str = av_realloc(tmp_str, max_url_size); ++ } + ff_make_absolute_url(tmp_str, max_url_size, tmp_str, val); ++ } + + if (rep_id_val) { + url = av_strireplace(tmp_str, "$RepresentationID$", rep_id_val); +-- +2.39.5 (Apple Git-154) + diff --git a/patches/ffmpeg-n5.1/0031-fix-dash-file-error-unterminated-entity-reference-du.patch b/patches/ffmpeg-n5.1/0031-fix-dash-file-error-unterminated-entity-reference-du.patch new file mode 100644 index 000000000..faeaec17b --- /dev/null +++ b/patches/ffmpeg-n5.1/0031-fix-dash-file-error-unterminated-entity-reference-du.patch @@ -0,0 +1,29 @@ +From 147a176cf02538367918d931959dbb5071202777 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Thu, 29 May 2025 09:45:39 +0800 +Subject: [PATCH] fix dash file error "unterminated entity reference" due to + ampersand in tag + +--- + libavformat/dashdec.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/libavformat/dashdec.c b/libavformat/dashdec.c +index 71d7906..4540ee3 100644 +--- a/libavformat/dashdec.c ++++ b/libavformat/dashdec.c +@@ -805,8 +805,10 @@ 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); ++ xmlChar *escaped = xmlEncodeSpecialChars(NULL, tmp_str); ++ xmlNodeSetContent(baseurl_nodes[i], escaped); + updated = 1; ++ xmlFree(escaped); + xmlFree(text); + } + } +-- +2.39.5 (Apple Git-154) + diff --git a/patches/ffmpeg-n6.1/0005-fix-ffmpeg-constructed-wrong-avcc-for-videotoolbox-h.patch b/patches/ffmpeg-n6.1/0005-fix-ffmpeg-constructed-wrong-avcc-for-videotoolbox-h.patch deleted file mode 100644 index 774710dbe..000000000 --- a/patches/ffmpeg-n6.1/0005-fix-ffmpeg-constructed-wrong-avcc-for-videotoolbox-h.patch +++ /dev/null @@ -1,59 +0,0 @@ -From 71216d65ff98b8a36fd4ae7bb85106aa875c7f0a Mon Sep 17 00:00:00 2001 -From: qianlongxu -Date: Mon, 16 May 2022 16:15:10 +0800 -Subject: [PATCH 05] fix ffmpeg constructed wrong avcc for videotoolbox - hwaccel. - ---- - libavcodec/videotoolbox.c | 35 ++++++++++++++++++++++++++++++++++- - 1 file changed, 34 insertions(+), 1 deletion(-) - -diff --git a/libavcodec/videotoolbox.c b/libavcodec/videotoolbox.c -index 43fd2e3..2411309 100644 ---- a/libavcodec/videotoolbox.c -+++ b/libavcodec/videotoolbox.c -@@ -223,7 +223,40 @@ CFDataRef ff_videotoolbox_avcc_extradata_create(AVCodecContext *avctx) - if (vtctx) - memcpy(vtctx->sps, h->ps.sps->data + 1, 3); - -- data = CFDataCreate(kCFAllocatorDefault, vt_extradata, vt_extradata_size); -+ /* -+ ffmpeg constructed avcc is wrong,but i dont't why;eg: -+ ff constructed avcc: -+ 014d401effe1001b674d401eec806c1ef3fff8140013f88000000080000019078b16cb01000468ebec4c -+ avctx->extradata avcc: -+ 014d401effe1001c674d401eec806c1ef3fff8140013f8800000030080000019078b16cb01000568ebec4c80 -+ */ -+ if (avctx->extradata_size != vt_extradata_size) { -+ char msg[256]; -+ { -+ char buffer[128]; -+ sprintf(buffer, "%s", "ff avcc maybe wrong:"); -+ int len = (int)strlen(buffer); -+ int size = FFMIN(vt_extradata_size, 127 - len) / 2; -+ for (int i = 0; i < size; i++) { -+ len += sprintf(buffer + len, "%02X", vt_extradata[i]); -+ } -+ sprintf(msg, "%s", buffer); -+ } -+ { -+ char buffer[128]; -+ sprintf(buffer, "%s", "\nuse origin avcc:"); -+ int len = (int)strlen(buffer); -+ int size = FFMIN(avctx->extradata_size, 127 - len) / 2; -+ for (int i = 0; i < size; i++) { -+ len += sprintf(buffer + len, "%02X", avctx->extradata[i]); -+ } -+ sprintf(msg + strlen(msg), "%s", buffer); -+ } -+ av_log(avctx, AV_LOG_INFO, "%s\n", msg); -+ data = CFDataCreate(kCFAllocatorDefault, avctx->extradata, avctx->extradata_size); -+ } else { -+ data = CFDataCreate(kCFAllocatorDefault, vt_extradata, vt_extradata_size); -+ } - av_free(vt_extradata); - return data; - } --- -2.39.3 (Apple Git-146) - diff --git a/patches/ffmpeg-n6.1/0017-bluray-open-and-find-the-right-m2ts-then-read-seek-i.patch b/patches/ffmpeg-n6.1/0017-bluray-open-and-find-the-right-m2ts-then-read-seek-i.patch deleted file mode 100644 index 19cf1c65f..000000000 --- a/patches/ffmpeg-n6.1/0017-bluray-open-and-find-the-right-m2ts-then-read-seek-i.patch +++ /dev/null @@ -1,195 +0,0 @@ -From c8e0c7ab3cccbf6b7093b38830c4f51ad9eefa38 Mon Sep 17 00:00:00 2001 -From: qianlongxu -Date: Mon, 22 Jul 2024 15:24:00 +0800 -Subject: [PATCH 17] bluray open and find the right m2ts, then read\seek it - direactly instread of bluray logic. - ---- - libavformat/bluray.c | 108 ++++++++++++++++++++++++++++++++++++++++--- - 1 file changed, 102 insertions(+), 6 deletions(-) - -diff --git a/libavformat/bluray.c b/libavformat/bluray.c -index 1845551..bf5b88d 100644 ---- a/libavformat/bluray.c -+++ b/libavformat/bluray.c -@@ -25,6 +25,8 @@ - #include "libavutil/avstring.h" - #include "libavformat/url.h" - #include "libavutil/opt.h" -+#include "libavutil/dict.h" -+#include "libavformat/avformat.h" - - #define BLURAY_PROTO_PREFIX "bluray:" - #define MIN_PLAYLIST_LENGTH 180 /* 3 min */ -@@ -38,6 +40,8 @@ typedef struct { - int angle; - int chapter; - /*int region;*/ -+ int title_idx; -+ int stream_opened; - } BlurayContext; - - #define OFFSET(x) offsetof(BlurayContext, x) -@@ -110,8 +114,24 @@ static int bluray_close(URLContext *h) - return 0; - } - -+#ifdef DEBUG_BLURAY -+#include -+#define BLURAY_DEBUG_MASK 0xFFFFF //(0xFFFFF & ~DBG_STREAM) -+ -+static void bluray_DebugHandler(const char *psz) -+{ -+ size_t len = strlen(psz); -+ if(len < 1) return; -+ av_log(NULL, AV_LOG_INFO, "[bluray] %s\n",psz); -+} -+#endif -+ - static int bluray_open(URLContext *h, const char *path, int flags) - { -+#ifdef DEBUG_BLURAY -+ bd_set_debug_mask(BLURAY_DEBUG_MASK); -+ bd_set_debug_handler(bluray_DebugHandler); -+#endif - BlurayContext *bd = h->priv_data; - int num_title_idx; - const char *diskname = path; -@@ -159,12 +179,13 @@ static int bluray_open(URLContext *h, const char *path, int flags) - - if (info->duration > duration) { - bd->playlist = info->playlist; -+ bd->title_idx = i; - duration = info->duration; - } - - bd_free_title_info(info); - } -- av_log(h, AV_LOG_INFO, "selected %05d.mpls\n", bd->playlist); -+ av_log(h, AV_LOG_INFO, "select longest playlist: %05d.mpls\n", bd->playlist); - } - - /* select playlist */ -@@ -182,7 +203,7 @@ static int bluray_open(URLContext *h, const char *path, int flags) - if (bd->chapter > 1) { - bd_seek_chapter(bd->bd, bd->chapter - 1); - } -- -+ bd->stream_opened = 1; - return 0; - } - -@@ -194,7 +215,13 @@ static int bluray_read(URLContext *h, unsigned char *buf, int size) - if (!bd || !bd->bd) { - return AVERROR(EFAULT); - } -- -+ if (bd->stream_opened) { -+ int read = (int)bd_file_read(bd->bd, buf, size); -+ if (read == 0) { -+ return AVERROR_EOF; -+ } -+ return read; -+ } - len = bd_read(bd->bd, buf, size); - - return len == 0 ? AVERROR_EOF : len; -@@ -212,16 +239,84 @@ static int64_t bluray_seek(URLContext *h, int64_t pos, int whence) - case SEEK_SET: - case SEEK_CUR: - case SEEK_END: -- return bd_seek(bd->bd, pos); -- -+ if (bd->stream_opened) { -+ return bd_file_seek(bd->bd, pos, whence); -+ } else { -+ return bd_seek(bd->bd, pos); -+ } - case AVSEEK_SIZE: -- return bd_get_title_size(bd->bd); -+ if (bd->stream_opened) { -+ return bd_file_size(bd->bd); -+ } else { -+ return bd_get_title_size(bd->bd); -+ } - } - - av_log(h, AV_LOG_ERROR, "Unsupported whence operation %d\n", whence); - return AVERROR(EINVAL); - } - -+static int bluray_parse_priv(AVFormatContext *ic, URLContext *h) -+{ -+ BlurayContext *bd = h->priv_data; -+ BLURAY_TITLE_INFO *title_info = NULL; -+ BLURAY_CLIP_INFO clip_info; -+ -+ int v_idx = 0; -+ int a_idx = 0; -+ int s_idx = 0; -+ int ret = 0; -+ -+ if (!bd || !bd->bd) { -+ return AVERROR(EFAULT); -+ } -+ -+ title_info = bd_get_title_info(bd->bd, bd->title_idx, 0); -+ if (!title_info) { -+ return AVERROR(EFAULT); -+ } -+ -+ if (title_info->clip_count <= 0) { -+ ret = EFAULT; -+ goto fail; -+ } -+ clip_info = title_info->clips[0]; -+ -+ for (int i = 0; i < ic->nb_streams; i++) { -+ if (ic->streams[i] && ic->streams[i]->codecpar) { -+ switch (ic->streams[i]->codecpar->codec_type) { -+ case AVMEDIA_TYPE_VIDEO: -+ if (v_idx < clip_info.video_stream_count) { -+ av_log(h, AV_LOG_INFO, "video stream %d lang = %s\n", v_idx, clip_info.video_streams[v_idx].lang); -+ av_dict_set(&ic->streams[i]->metadata, "language", clip_info.video_streams[v_idx].lang, AV_DICT_DONT_OVERWRITE); -+ v_idx++; -+ } -+ break; -+ case AVMEDIA_TYPE_AUDIO: -+ if (a_idx < clip_info.audio_stream_count) { -+ av_log(h, AV_LOG_INFO, "audio stream %d lang = %s\n", a_idx, clip_info.audio_streams[a_idx].lang); -+ av_dict_set(&ic->streams[i]->metadata, "language", clip_info.audio_streams[a_idx].lang, AV_DICT_DONT_OVERWRITE); -+ a_idx++; -+ } -+ break; -+ case AVMEDIA_TYPE_SUBTITLE: -+ if (s_idx < clip_info.pg_stream_count) { -+ av_log(h, AV_LOG_INFO, "subtitle stream %d lang = %s\n", s_idx, clip_info.pg_streams[s_idx].lang); -+ av_dict_set(&ic->streams[i]->metadata, "language", clip_info.pg_streams[s_idx].lang, AV_DICT_DONT_OVERWRITE); -+ s_idx++; -+ } -+ break; -+ default: -+ break; -+ } -+ } -+ } -+ -+fail: -+ bd_free_title_info(title_info); -+ -+ return ret != 0 ? AVERROR(ret) : 0; -+} - - const URLProtocol ff_bluray_protocol = { - .name = "bluray", -@@ -229,6 +324,7 @@ const URLProtocol ff_bluray_protocol = { - .url_open = bluray_open, - .url_read = bluray_read, - .url_seek = bluray_seek, -+ .url_parse_priv = bluray_parse_priv, - .priv_data_size = sizeof(BlurayContext), - .priv_data_class = &bluray_context_class, - }; --- -2.39.3 (Apple Git-146) - diff --git a/patches/ffmpeg-n6.1/0018-fix-http-chunked-transfer-get-wrong-size-cause-av_re.patch b/patches/ffmpeg-n6.1/0017-fix-http-chunked-transfer-get-wrong-size-cause-av_re.patch similarity index 93% rename from patches/ffmpeg-n6.1/0018-fix-http-chunked-transfer-get-wrong-size-cause-av_re.patch rename to patches/ffmpeg-n6.1/0017-fix-http-chunked-transfer-get-wrong-size-cause-av_re.patch index 381916567..bff847552 100644 --- a/patches/ffmpeg-n6.1/0018-fix-http-chunked-transfer-get-wrong-size-cause-av_re.patch +++ b/patches/ffmpeg-n6.1/0017-fix-http-chunked-transfer-get-wrong-size-cause-av_re.patch @@ -1,7 +1,7 @@ From 2155673117b3d8377311b59a33b1afeae511554b Mon Sep 17 00:00:00 2001 From: qianlongxu Date: Thu, 27 Jun 2024 18:31:15 +0800 -Subject: [PATCH 18] fix http chunked transfer get wrong size cause av_read_frame +Subject: [PATCH 17] fix http chunked transfer get wrong size cause av_read_frame can not return eof bug --- diff --git a/patches/ffmpeg-n6.1/0018-supportsFamily-is-only-available-on-iOS-13.0-or-newe.patch b/patches/ffmpeg-n6.1/0018-supportsFamily-is-only-available-on-iOS-13.0-or-newe.patch new file mode 100644 index 000000000..1e5a4a6c8 --- /dev/null +++ b/patches/ffmpeg-n6.1/0018-supportsFamily-is-only-available-on-iOS-13.0-or-newe.patch @@ -0,0 +1,25 @@ +From 1ebd4c062a886049f3c3255c50fe56f6621b9844 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Fri, 26 Jul 2024 16:11:14 +0800 +Subject: [PATCH 18] 'supportsFamily:' is only available on iOS 13.0 or newer + +--- + libavfilter/metal/utils.m | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/libavfilter/metal/utils.m b/libavfilter/metal/utils.m +index f365d3c..bb1825a 100644 +--- a/libavfilter/metal/utils.m ++++ b/libavfilter/metal/utils.m +@@ -31,7 +31,7 @@ void ff_metal_compute_encoder_dispatch(id device, + BOOL fallback = YES; + // MAC_OS_X_VERSION_10_15 is only defined on SDKs new enough to include its functionality (including iOS, tvOS, etc) + #ifdef MAC_OS_X_VERSION_10_15 +- if (@available(macOS 10.15, iOS 11, tvOS 14.5, *)) { ++ if (@available(macOS 10.15, iOS 13, tvOS 14.5, *)) { + if ([device supportsFamily:MTLGPUFamilyCommon3]) { + MTLSize threadsPerGrid = MTLSizeMake(width, height, 1); + [encoder dispatchThreads:threadsPerGrid threadsPerThreadgroup:threadsPerThreadgroup]; +-- +2.39.3 (Apple Git-146) + diff --git a/patches/ffmpeg-n6.1/0020-add-built-in-smb2-protocol-via-libsmb2.patch b/patches/ffmpeg-n6.1/0019-add-built-in-smb2-protocol-via-libsmb2.patch similarity index 99% rename from patches/ffmpeg-n6.1/0020-add-built-in-smb2-protocol-via-libsmb2.patch rename to patches/ffmpeg-n6.1/0019-add-built-in-smb2-protocol-via-libsmb2.patch index 348b2fc08..006417084 100644 --- a/patches/ffmpeg-n6.1/0020-add-built-in-smb2-protocol-via-libsmb2.patch +++ b/patches/ffmpeg-n6.1/0019-add-built-in-smb2-protocol-via-libsmb2.patch @@ -1,7 +1,7 @@ From 229f417012635accc6506d0c179343654b984f38 Mon Sep 17 00:00:00 2001 From: qianlongxu Date: Thu, 27 Feb 2025 14:49:42 +0800 -Subject: [PATCH 20] add built-in smb2 protocol via libsmb2 +Subject: [PATCH 19] add built-in smb2 protocol via libsmb2 --- configure | 5 + diff --git a/patches/ffmpeg-n6.1/0023-fix-http-open-and-http_seek-redirect-authentication-.patch b/patches/ffmpeg-n6.1/0020-fix-http-open-and-http_seek-redirect-authentication-.patch similarity index 98% rename from patches/ffmpeg-n6.1/0023-fix-http-open-and-http_seek-redirect-authentication-.patch rename to patches/ffmpeg-n6.1/0020-fix-http-open-and-http_seek-redirect-authentication-.patch index 1a90a7c35..d1ea452cb 100644 --- a/patches/ffmpeg-n6.1/0023-fix-http-open-and-http_seek-redirect-authentication-.patch +++ b/patches/ffmpeg-n6.1/0020-fix-http-open-and-http_seek-redirect-authentication-.patch @@ -1,7 +1,7 @@ From 29b3f4f302406cc546d87e5e8d26a20417bfcb63 Mon Sep 17 00:00:00 2001 From: qianlongxu Date: Tue, 12 Nov 2024 16:00:23 +0800 -Subject: [PATCH 23] fix http open and http_seek (redirect) authentication bug +Subject: [PATCH 20] fix http open and http_seek (redirect) authentication bug --- libavformat/http.c | 25 ++++++++++++++++++++++++- diff --git a/patches/ffmpeg-n6.1/0024-Adapt-to-clang-16.patch b/patches/ffmpeg-n6.1/0021-Adapt-to-clang-16.patch similarity index 97% rename from patches/ffmpeg-n6.1/0024-Adapt-to-clang-16.patch rename to patches/ffmpeg-n6.1/0021-Adapt-to-clang-16.patch index 56bd49bc7..a1c792743 100644 --- a/patches/ffmpeg-n6.1/0024-Adapt-to-clang-16.patch +++ b/patches/ffmpeg-n6.1/0021-Adapt-to-clang-16.patch @@ -1,7 +1,7 @@ From 338d6fb305a992bc2a24347e4d7793e02b54345d Mon Sep 17 00:00:00 2001 From: qianlongxu Date: Thu, 31 Oct 2024 10:55:33 +0800 -Subject: [PATCH 24] Adapt to clang 16 +Subject: [PATCH 21] Adapt to clang 16 --- configure | 8 +++++++- diff --git a/patches/ffmpeg-n6.1/0025-fix-can-t-seek-to-00-00-bug-baidu-neddisk-hls-start_.patch b/patches/ffmpeg-n6.1/0022-fix-can-t-seek-to-00-00-bug-baidu-neddisk-hls-start_.patch similarity index 92% rename from patches/ffmpeg-n6.1/0025-fix-can-t-seek-to-00-00-bug-baidu-neddisk-hls-start_.patch rename to patches/ffmpeg-n6.1/0022-fix-can-t-seek-to-00-00-bug-baidu-neddisk-hls-start_.patch index 641d9e7e5..ec79fad6b 100644 --- a/patches/ffmpeg-n6.1/0025-fix-can-t-seek-to-00-00-bug-baidu-neddisk-hls-start_.patch +++ b/patches/ffmpeg-n6.1/0022-fix-can-t-seek-to-00-00-bug-baidu-neddisk-hls-start_.patch @@ -1,7 +1,7 @@ From 411a7fc1b4be73f655ca2881cc8e16a862bfe194 Mon Sep 17 00:00:00 2001 From: qianlongxu Date: Fri, 1 Nov 2024 16:56:53 +0800 -Subject: [PATCH 25] fix can't seek to 00:00 bug, baidu neddisk hls start_time is +Subject: [PATCH 22] fix can't seek to 00:00 bug, baidu neddisk hls start_time is less than first_timestamp --- diff --git a/patches/ffmpeg-n6.1/0022-support-probe-bluray-video.patch b/patches/ffmpeg-n6.1/0022-support-probe-bluray-video.patch deleted file mode 100644 index cc24e639a..000000000 --- a/patches/ffmpeg-n6.1/0022-support-probe-bluray-video.patch +++ /dev/null @@ -1,104 +0,0 @@ -From b3f5a8272c73164325f4c9f3847926012c51e99e Mon Sep 17 00:00:00 2001 -From: qianlongxu -Date: Sat, 12 Oct 2024 18:04:29 +0800 -Subject: [PATCH 22] support probe bluray video - ---- - libavformat/Makefile | 1 + - libavformat/bluray.c | 28 +++++++++++++++++++++++++++- - libavformat/bluray_util.h | 27 +++++++++++++++++++++++++++ - 3 files changed, 55 insertions(+), 1 deletion(-) - create mode 100644 libavformat/bluray_util.h - -diff --git a/libavformat/Makefile b/libavformat/Makefile -index ffc32b9..dbdc5b9 100644 ---- a/libavformat/Makefile -+++ b/libavformat/Makefile -@@ -15,6 +15,7 @@ HEADERS = avformat.h \ - metadata.h \ - application.h \ - dns_cache.h \ -+ bluray_util.h \ - - OBJS = allformats.o \ - avformat.o \ -diff --git a/libavformat/bluray.c b/libavformat/bluray.c -index 62c7727..f4de84d 100644 ---- a/libavformat/bluray.c -+++ b/libavformat/bluray.c -@@ -28,7 +28,7 @@ - #include "libavutil/dict.h" - #include "libavformat/avformat.h" - #include "bluray_custom_fs.h" -- -+#include "bluray_util.h" - #define BLURAY_PROTO_PREFIX "bluray://" - #define MIN_PLAYLIST_LENGTH 180 /* 3 min */ - -@@ -345,3 +345,29 @@ const URLProtocol ff_bluray_protocol = { - .priv_data_size = sizeof(BlurayContext), - .priv_data_class = &bluray_context_class, - }; -+ -+int ff_is_bluray_video(const char *path, AVDictionary **options) -+{ -+#ifdef DEBUG_BLURAY -+ bd_set_debug_mask(BLURAY_DEBUG_MASK); -+ bd_set_debug_handler(bluray_DebugHandler); -+#endif -+ -+ fs_access *access = NULL; -+ -+ if (av_strstart(path, "file://", NULL) || av_strstart(path, "/", NULL)) { -+ access = NULL; -+ } else { -+ access = create_bluray_custom_access(path, options); -+ } -+ -+ BLURAY *bd = bd_open_fs(path, NULL, access); -+ if (!bd) { -+ destroy_bluray_custom_access(&access); -+ av_log(NULL, AV_LOG_ERROR, "bd_open() failed\n"); -+ return 0; -+ } -+ bd_close(bd); -+ destroy_bluray_custom_access(&access); -+ return 1; -+} -diff --git a/libavformat/bluray_util.h b/libavformat/bluray_util.h -new file mode 100644 -index 0000000..62301b2 ---- /dev/null -+++ b/libavformat/bluray_util.h -@@ -0,0 +1,27 @@ -+// -+// bluray_custom_fs.h -+// -+// Created by Reach Matt on 2024/9/13. -+// -+// -+// Copyright (C) 2021 Matt Reach// -+// Licensed under the Apache License, Version 2.0 (the "License"); -+// you may not use this file except in compliance with the License. -+// You may obtain a copy of the License at -+// -+// http://www.apache.org/licenses/LICENSE-2.0 -+// -+// Unless required by applicable law or agreed to in writing, software -+// distributed under the License is distributed on an "AS IS" BASIS, -+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -+// See the License for the specific language governing permissions and -+// limitations under the License. -+ -+#ifndef bluray_util_h -+#define bluray_util_h -+ -+typedef struct AVDictionary AVDictionary; -+ -+int ff_is_bluray_video(const char *path, AVDictionary **options); -+ -+#endif -\ No newline at end of file --- -2.39.3 (Apple Git-146) - diff --git a/patches/ffmpeg-n6.1/0026-http-add-reconnect_first_delay-opt.patch b/patches/ffmpeg-n6.1/0023-http-add-reconnect_first_delay-opt.patch similarity index 97% rename from patches/ffmpeg-n6.1/0026-http-add-reconnect_first_delay-opt.patch rename to patches/ffmpeg-n6.1/0023-http-add-reconnect_first_delay-opt.patch index 4f4b0879a..3ff28c6b7 100644 --- a/patches/ffmpeg-n6.1/0026-http-add-reconnect_first_delay-opt.patch +++ b/patches/ffmpeg-n6.1/0023-http-add-reconnect_first_delay-opt.patch @@ -1,7 +1,7 @@ From 18f90d0efc60ba8bee97bbe1c7c1a6f139b9ff24 Mon Sep 17 00:00:00 2001 From: qianlongxu Date: Tue, 12 Nov 2024 14:42:37 +0800 -Subject: [PATCH 26] http add reconnect_first_delay opt +Subject: [PATCH 23] http add reconnect_first_delay opt --- libavformat/http.c | 4 +++- diff --git a/patches/ffmpeg-n6.1/0027-Audio-Vivid-Parser-and-Demuxer-but-av3a-Decoder-is-a.patch b/patches/ffmpeg-n6.1/0024-Audio-Vivid-Parser-and-Demuxer-but-av3a-Decoder-is-a.patch similarity index 99% rename from patches/ffmpeg-n6.1/0027-Audio-Vivid-Parser-and-Demuxer-but-av3a-Decoder-is-a.patch rename to patches/ffmpeg-n6.1/0024-Audio-Vivid-Parser-and-Demuxer-but-av3a-Decoder-is-a.patch index e86f90a88..c81701948 100644 --- a/patches/ffmpeg-n6.1/0027-Audio-Vivid-Parser-and-Demuxer-but-av3a-Decoder-is-a.patch +++ b/patches/ffmpeg-n6.1/0024-Audio-Vivid-Parser-and-Demuxer-but-av3a-Decoder-is-a.patch @@ -1,7 +1,7 @@ From f73942871770f5e268178c886b7d6100e1c5faba Mon Sep 17 00:00:00 2001 From: qianlongxu Date: Mon, 18 Nov 2024 17:12:20 +0800 -Subject: [PATCH 27] Audio Vivid Parser and Demuxer, but av3a Decoder is absent +Subject: [PATCH 24] Audio Vivid Parser and Demuxer, but av3a Decoder is absent --- libavcodec/Makefile | 1 + diff --git a/patches/ffmpeg-n6.1/0028-add-4-dummy-ijkplaceholder-demuxers.patch b/patches/ffmpeg-n6.1/0025-add-4-dummy-ijkplaceholder-demuxers.patch similarity index 95% rename from patches/ffmpeg-n6.1/0028-add-4-dummy-ijkplaceholder-demuxers.patch rename to patches/ffmpeg-n6.1/0025-add-4-dummy-ijkplaceholder-demuxers.patch index d034c13de..6ae8e8b83 100644 --- a/patches/ffmpeg-n6.1/0028-add-4-dummy-ijkplaceholder-demuxers.patch +++ b/patches/ffmpeg-n6.1/0025-add-4-dummy-ijkplaceholder-demuxers.patch @@ -1,7 +1,7 @@ From 6d78475920216bbc66a63d6aa7484b7ad57f7ff8 Mon Sep 17 00:00:00 2001 From: qianlongxu Date: Thu, 28 Nov 2024 11:28:43 +0800 -Subject: [PATCH 28] add 4 dummy ijkplaceholder demuxers +Subject: [PATCH 25] add 4 dummy ijkplaceholder demuxers --- libavformat/allformats.c | 5 +++++ diff --git a/patches/ffmpeg-n6.1/0029-add-3-dummy-ijkhttp-protocols-and-use-selected_http-.patch b/patches/ffmpeg-n6.1/0026-add-3-dummy-ijkhttp-protocols-and-use-selected_http-.patch similarity index 99% rename from patches/ffmpeg-n6.1/0029-add-3-dummy-ijkhttp-protocols-and-use-selected_http-.patch rename to patches/ffmpeg-n6.1/0026-add-3-dummy-ijkhttp-protocols-and-use-selected_http-.patch index 9cb5bb883..4464e3bf5 100644 --- a/patches/ffmpeg-n6.1/0029-add-3-dummy-ijkhttp-protocols-and-use-selected_http-.patch +++ b/patches/ffmpeg-n6.1/0026-add-3-dummy-ijkhttp-protocols-and-use-selected_http-.patch @@ -1,7 +1,7 @@ From ab8fe6d0c2de932980f6f1e9d31e24f25750da37 Mon Sep 17 00:00:00 2001 From: qianlongxu Date: Fri, 14 Mar 2025 17:41:28 +0800 -Subject: [PATCH 29] add 3 dummy ijkhttp protocols and use selected_http option +Subject: [PATCH 26] add 3 dummy ijkhttp protocols and use selected_http option choose --- diff --git a/patches/ffmpeg-n6.1/0027-bluray-protocol-add-dvd-fallback.patch b/patches/ffmpeg-n6.1/0027-bluray-protocol-add-dvd-fallback.patch new file mode 100644 index 000000000..51f688757 --- /dev/null +++ b/patches/ffmpeg-n6.1/0027-bluray-protocol-add-dvd-fallback.patch @@ -0,0 +1,61 @@ +From 7f96723900b9103059abc487f57aa34e049e8e9c Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Fri, 28 Mar 2025 11:15:04 +0800 +Subject: [PATCH 27] bluray protocol add dvd fallback + +--- + libavformat/demux.c | 31 ++++++++++++++++++++++++++----- + 1 file changed, 26 insertions(+), 5 deletions(-) + +diff --git a/libavformat/demux.c b/libavformat/demux.c +index 3c8f194..5620e0f 100644 +--- a/libavformat/demux.c ++++ b/libavformat/demux.c +@@ -158,7 +158,8 @@ static int init_input(AVFormatContext *s, const char *filename, + int ret; + AVProbeData pd = { filename, NULL, 0 }; + int score = AVPROBE_SCORE_RETRY; +- ++ AVDictionary *tmp_opts = NULL; ++ + if (s->pb) { + s->flags |= AVFMT_FLAG_CUSTOM_IO; + if (!s->iformat) +@@ -173,10 +174,30 @@ static int init_input(AVFormatContext *s, const char *filename, + if ((s->iformat && s->iformat->flags & AVFMT_NOFILE) || + (!s->iformat && (s->iformat = av_probe_input_format2(&pd, 0, &score)))) + return score; +- +- if ((ret = s->io_open(s, &s->pb, filename, AVIO_FLAG_READ | s->avio_flags, options)) < 0) +- return ret; +- ++ ++ if (options && (av_stristart(filename, "bluray://", NULL) || av_stristart(filename, "dvd://", NULL))) { ++ av_dict_copy(&tmp_opts, *options, 0); ++ } ++ ++ if ((ret = s->io_open(s, &s->pb, filename, AVIO_FLAG_READ | s->avio_flags, ++ options)) < 0) { ++ if (av_stristart(filename, "bluray://", NULL)) { ++ const char *dvd_name = ++ av_strireplace(filename, "bluray://", "dvd://"); ++ ret = init_input(s, dvd_name, &tmp_opts); ++ av_dict_free(&tmp_opts); ++ return ret; ++ } else if (av_stristart(filename, "dvd://", NULL)) { ++ const char *a_name = av_strireplace(filename, "dvd://", ""); ++ ret = init_input(s, a_name, &tmp_opts); ++ av_dict_free(&tmp_opts); ++ return ret; ++ } else { ++ av_dict_free(&tmp_opts); ++ return ret; ++ } ++ } ++ av_dict_free(&tmp_opts); + if (s->iformat) + return 0; + return av_probe_input_buffer2(s->pb, &s->iformat, filename, +-- +2.39.5 (Apple Git-154) + diff --git a/patches/ffmpeg-n6.1/0021-custom-bluray-fs-for-network-Blu-ray-Disc-and-BDMV.patch b/patches/ffmpeg-n6.1/0028-custom-bluray-fs-for-network-Blu-ray-Disc-and-BDMV.patch similarity index 77% rename from patches/ffmpeg-n6.1/0021-custom-bluray-fs-for-network-Blu-ray-Disc-and-BDMV.patch rename to patches/ffmpeg-n6.1/0028-custom-bluray-fs-for-network-Blu-ray-Disc-and-BDMV.patch index 8dcbb90c3..db78c2610 100644 --- a/patches/ffmpeg-n6.1/0021-custom-bluray-fs-for-network-Blu-ray-Disc-and-BDMV.patch +++ b/patches/ffmpeg-n6.1/0028-custom-bluray-fs-for-network-Blu-ray-Disc-and-BDMV.patch @@ -1,22 +1,22 @@ -From e6fdbabaf8902e834a745705c851db193b8d553f Mon Sep 17 00:00:00 2001 +From 22d3925e8fec79ca0dd23dbdb7eb6f9a9d53ee96 Mon Sep 17 00:00:00 2001 From: qianlongxu -Date: Tue, 17 Dec 2024 18:59:49 +0800 -Subject: [PATCH 21] custom bluray fs for network Blu-ray Disc and BDMV +Date: Mon, 24 Mar 2025 09:31:42 +0800 +Subject: [PATCH 28] custom bluray fs for network Blu-ray Disc and BDMV --- libavformat/Makefile | 2 +- - libavformat/bluray.c | 32 ++- + libavformat/bluray.c | 118 +++++++++- libavformat/bluray_custom_fs.c | 413 +++++++++++++++++++++++++++++++++ libavformat/bluray_custom_fs.h | 29 +++ - 4 files changed, 468 insertions(+), 8 deletions(-) + 4 files changed, 553 insertions(+), 9 deletions(-) create mode 100644 libavformat/bluray_custom_fs.c create mode 100644 libavformat/bluray_custom_fs.h diff --git a/libavformat/Makefile b/libavformat/Makefile -index 427c45a..ffc32b9 100644 +index 19ba54e..9ad6d87 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile -@@ -666,7 +666,7 @@ OBJS-$(CONFIG_VAPOURSYNTH_DEMUXER) += vapoursynth.o +@@ -667,7 +667,7 @@ OBJS-$(CONFIG_VAPOURSYNTH_DEMUXER) += vapoursynth.o # protocols I/O OBJS-$(CONFIG_ASYNC_PROTOCOL) += async.o OBJS-$(CONFIG_APPLEHTTP_PROTOCOL) += hlsproto.o @@ -26,10 +26,10 @@ index 427c45a..ffc32b9 100644 OBJS-$(CONFIG_CONCAT_PROTOCOL) += concat.o OBJS-$(CONFIG_CONCATF_PROTOCOL) += concat.o diff --git a/libavformat/bluray.c b/libavformat/bluray.c -index bf5b88d..96a799a 100644 +index 1845551..cd50523 100644 --- a/libavformat/bluray.c +++ b/libavformat/bluray.c -@@ -21,21 +21,22 @@ +@@ -21,23 +21,27 @@ */ #include @@ -38,9 +38,9 @@ index bf5b88d..96a799a 100644 #include "libavutil/avstring.h" #include "libavformat/url.h" #include "libavutil/opt.h" - #include "libavutil/dict.h" - #include "libavformat/avformat.h" +#include "bluray_custom_fs.h" ++#include "libavutil/dict.h" ++#include "libavformat/avformat.h" -#define BLURAY_PROTO_PREFIX "bluray:" +#define BLURAY_PROTO_PREFIX "bluray://" @@ -55,7 +55,12 @@ index bf5b88d..96a799a 100644 int playlist; int angle; int chapter; -@@ -110,7 +111,7 @@ static int bluray_close(URLContext *h) + /*int region;*/ ++ int title_idx; + } BlurayContext; + + #define OFFSET(x) offsetof(BlurayContext, x) +@@ -106,23 +110,58 @@ static int bluray_close(URLContext *h) if (bd->bd) { bd_close(bd->bd); } @@ -64,16 +69,30 @@ index bf5b88d..96a799a 100644 return 0; } -@@ -126,7 +127,7 @@ static void bluray_DebugHandler(const char *psz) - } - #endif - -static int bluray_open(URLContext *h, const char *path, int flags) -+static int bluray_open(URLContext *h, const char *path, int flags, AVDictionary **options) ++#ifdef DEBUG_BLURAY ++#include ++#define BLURAY_DEBUG_MASK 0xFFFFF //(0xFFFFF & ~DBG_STREAM) ++ ++static void bluray_DebugHandler(const char *psz) { - #ifdef DEBUG_BLURAY - bd_set_debug_mask(BLURAY_DEBUG_MASK); -@@ -138,11 +139,28 @@ static int bluray_open(URLContext *h, const char *path, int flags) ++ size_t len = strlen(psz); ++ if(len < 1) return; ++ av_log(NULL, AV_LOG_INFO, "[bluray] %s\n",psz); ++} ++#endif ++ ++ ++static int bluray_open(URLContext *h, const char *path, int flags, AVDictionary **options) ++{ ++#ifdef DEBUG_BLURAY ++ bd_set_debug_mask(BLURAY_DEBUG_MASK); ++ bd_set_debug_handler(bluray_DebugHandler); ++#endif ++ + BlurayContext *bd = h->priv_data; + int num_title_idx; + const char *diskname = path; av_strstart(path, BLURAY_PROTO_PREFIX, &diskname); @@ -81,7 +100,7 @@ index bf5b88d..96a799a 100644 + fs_access *access = NULL; + + diskname = ff_urldecode(diskname, 0); -+ ++ + if (av_strstart(diskname, "file://", NULL) || av_strstart(diskname, "/", NULL)) { + access = NULL; + } else { @@ -103,7 +122,87 @@ index bf5b88d..96a799a 100644 /* check if disc can be played */ if (check_disc_info(h) < 0) { -@@ -321,7 +339,7 @@ fail: +@@ -159,12 +198,13 @@ static int bluray_open(URLContext *h, const char *path, int flags) + + if (info->duration > duration) { + bd->playlist = info->playlist; ++ bd->title_idx = i; + duration = info->duration; + } + + bd_free_title_info(info); + } +- av_log(h, AV_LOG_INFO, "selected %05d.mpls\n", bd->playlist); ++ av_log(h, AV_LOG_INFO, "select longest playlist: %05d.mpls\n", bd->playlist); + } + + /* select playlist */ +@@ -222,13 +262,75 @@ static int64_t bluray_seek(URLContext *h, int64_t pos, int whence) + return AVERROR(EINVAL); + } + ++static int bluray_parse_priv(AVFormatContext *ic, URLContext *h) ++{ ++ BlurayContext *bd = h->priv_data; ++ BLURAY_TITLE_INFO *title_info = NULL; ++ BLURAY_CLIP_INFO clip_info; ++ ++ int v_idx = 0; ++ int a_idx = 0; ++ int s_idx = 0; ++ int ret = 0; ++ ++ if (!bd || !bd->bd) { ++ return AVERROR(EFAULT); ++ } ++ ++ title_info = bd_get_title_info(bd->bd, bd->title_idx, 0); ++ if (!title_info) { ++ return AVERROR(EFAULT); ++ } ++ ++ if (title_info->clip_count <= 0) { ++ ret = EFAULT; ++ goto fail; ++ } ++ clip_info = title_info->clips[0]; ++ ++ for (int i = 0; i < ic->nb_streams; i++) { ++ if (ic->streams[i] && ic->streams[i]->codecpar) { ++ switch (ic->streams[i]->codecpar->codec_type) { ++ case AVMEDIA_TYPE_VIDEO: ++ if (v_idx < clip_info.video_stream_count) { ++ av_log(h, AV_LOG_INFO, "video stream %d lang = %s\n", v_idx, clip_info.video_streams[v_idx].lang); ++ av_dict_set(&ic->streams[i]->metadata, "language", clip_info.video_streams[v_idx].lang, AV_DICT_DONT_OVERWRITE); ++ v_idx++; ++ } ++ break; ++ case AVMEDIA_TYPE_AUDIO: ++ if (a_idx < clip_info.audio_stream_count) { ++ av_log(h, AV_LOG_INFO, "audio stream %d lang = %s\n", a_idx, clip_info.audio_streams[a_idx].lang); ++ av_dict_set(&ic->streams[i]->metadata, "language", clip_info.audio_streams[a_idx].lang, AV_DICT_DONT_OVERWRITE); ++ a_idx++; ++ } ++ break; ++ case AVMEDIA_TYPE_SUBTITLE: ++ if (s_idx < clip_info.pg_stream_count) { ++ av_log(h, AV_LOG_INFO, "subtitle stream %d lang = %s\n", s_idx, clip_info.pg_streams[s_idx].lang); ++ av_dict_set(&ic->streams[i]->metadata, "language", clip_info.pg_streams[s_idx].lang, AV_DICT_DONT_OVERWRITE); ++ s_idx++; ++ } ++ break; ++ default: ++ break; ++ } ++ } ++ } ++ ++fail: ++ bd_free_title_info(title_info); ++ ++ return ret != 0 ? AVERROR(ret) : 0; ++} + const URLProtocol ff_bluray_protocol = { .name = "bluray", .url_close = bluray_close, @@ -111,10 +210,13 @@ index bf5b88d..96a799a 100644 + .url_open2 = bluray_open, .url_read = bluray_read, .url_seek = bluray_seek, - .url_parse_priv = bluray_parse_priv, ++ .url_parse_priv = bluray_parse_priv, + .priv_data_size = sizeof(BlurayContext), + .priv_data_class = &bluray_context_class, + }; diff --git a/libavformat/bluray_custom_fs.c b/libavformat/bluray_custom_fs.c new file mode 100644 -index 0000000..9062a2b +index 0000000..bdf2451 --- /dev/null +++ b/libavformat/bluray_custom_fs.c @@ -0,0 +1,413 @@ @@ -181,7 +283,7 @@ index 0000000..9062a2b + if (!io) { + return -1; + } -+ ++ + ff_builtin_io * app = av_mallocz(sizeof(ff_builtin_io)); + if (!app) { + return -2; @@ -215,7 +317,7 @@ index 0000000..9062a2b + if (is_dir) { + flags |= AVIO_FLAG_DIRECT; + } -+ ++ + int ret = ffurl_open_whitelist(&app->url_context, + url, + flags, @@ -226,8 +328,8 @@ index 0000000..9062a2b + NULL); + + av_application_did_http_open(app->app_ctx, (void*)app->url_context, url, ret < 0 ? AVERROR(errno) : 0, ret < 0 ? 500 : 200, 0); -+ -+ if (ret < 0) { ++ ++ if (ret < 0) { + close_builtin_io(app); + av_freep(&app); + } @@ -246,11 +348,11 @@ index 0000000..9062a2b + } + + av_application_will_http_seek(io->app_ctx, (void*)io->url_context, io->url_context->filename, offset); -+ ++ + int64_t pos = io->url_context->prot->url_seek(io->url_context, offset, origin); + if (pos < 0) { + av_application_did_http_seek(io->app_ctx, (void*)io->url_context, io->url_context->filename, offset, AVERROR(errno), 500); -+ return AVERROR(errno); ++ return AVERROR(errno); + } + io->offset = pos; + @@ -282,7 +384,7 @@ index 0000000..9062a2b + + int bytes = buf_size - buf_size1; + if (bytes == 0 && read == AVERROR_EOF) { -+ return AVERROR_EOF; ++ return AVERROR_EOF; + } else { + av_application_did_io_tcp_read(io->app_ctx, (void*)io->url_context, bytes); + return bytes; @@ -404,12 +506,12 @@ index 0000000..9062a2b + } + AVDictionary *opts = NULL; + av_dict_copy(&opts, access->opts, 0); -+ ++ + ff_builtin_io *io = NULL; + int ret = create_builtin_io(&io, url, &opts, 0); + av_dict_free(&opts); + av_free(url); -+ ++ + if (0 != ret) { + av_log(NULL, AV_LOG_ERROR, "can't open url %s,error:%s", url, av_err2str(ret)); + return NULL; @@ -455,11 +557,11 @@ index 0000000..9062a2b + } + + AVIODirEntry *next = NULL; -+ ++ + if (io->url_context->prot->url_read_dir(io->url_context, &next) < 0 || !next) { + return -2; + } -+ ++ + strncpy(entry->d_name, next->name, sizeof(entry->d_name)); + entry->d_name[sizeof(entry->d_name) - 1] = 0; + @@ -477,12 +579,12 @@ index 0000000..9062a2b + } + AVDictionary *opts = NULL; + av_dict_copy(&opts, access->opts, 0); -+ ++ + ff_builtin_io *io = NULL; + int ret = create_builtin_io(&io, url, &opts, 1); + av_dict_free(&opts); + av_free(url); -+ ++ + if (0 != ret) { + av_log(NULL, AV_LOG_ERROR, "can't open dir %s,error:%s", url, av_err2str(ret)); + return NULL; @@ -498,7 +600,7 @@ index 0000000..9062a2b + dir->internal = io; + dir->close = _dir_close; + dir->read = _dir_read; -+ ++ + return dir; +} + @@ -513,9 +615,9 @@ index 0000000..9062a2b + if (opaque) { + opaque->url = av_strdup(url); + if (options) { -+ av_dict_copy(&opaque->opts, *options, 0); ++ av_dict_copy(&opaque->opts, *options, 0); + } -+ ++ + int ret = create_builtin_io(&opaque->io, url, options, 0); + if (0 != ret) { + av_log(NULL, AV_LOG_ERROR, "can't open file %s,error:%s", url, av_err2str(ret)); @@ -526,7 +628,7 @@ index 0000000..9062a2b + access->read_blocks = read_blocks; + access->open_file = open_file; + access->open_dir = open_dir; -+ ++ + return access; + } + return NULL; diff --git a/patches/ffmpeg-n6.1/0029-bluray-open-and-find-the-right-m2ts-then-read-seek-i.patch b/patches/ffmpeg-n6.1/0029-bluray-open-and-find-the-right-m2ts-then-read-seek-i.patch new file mode 100644 index 000000000..9a0e1bdae --- /dev/null +++ b/patches/ffmpeg-n6.1/0029-bluray-open-and-find-the-right-m2ts-then-read-seek-i.patch @@ -0,0 +1,70 @@ +From 28cdd2169e400e113ef6f681d2e1eee8ebc83afa Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Fri, 21 Mar 2025 19:00:43 +0800 +Subject: [PATCH 29] bluray open and find the right m2ts, then read\seek it + direactly instread of bluray logic. + +--- + libavformat/bluray.c | 24 +++++++++++++++++++----- + 1 file changed, 19 insertions(+), 5 deletions(-) + +diff --git a/libavformat/bluray.c b/libavformat/bluray.c +index c3cdc03..628693d 100644 +--- a/libavformat/bluray.c ++++ b/libavformat/bluray.c +@@ -42,6 +42,7 @@ typedef struct { + int chapter; + /*int region;*/ + int title_idx; ++ int stream_opened; + } BlurayContext; + + #define OFFSET(x) offsetof(BlurayContext, x) +@@ -222,7 +223,7 @@ static int bluray_open(URLContext *h, const char *path, int flags, AVDictionary + if (bd->chapter > 1) { + bd_seek_chapter(bd->bd, bd->chapter - 1); + } +- ++ bd->stream_opened = 1; + return 0; + } + +@@ -234,7 +235,13 @@ static int bluray_read(URLContext *h, unsigned char *buf, int size) + if (!bd || !bd->bd) { + return AVERROR(EFAULT); + } +- ++ if (bd->stream_opened) { ++ int read = (int)bd_file_read(bd->bd, buf, size); ++ if (read == 0) { ++ return AVERROR_EOF; ++ } ++ return read; ++ } + len = bd_read(bd->bd, buf, size); + + return len == 0 ? AVERROR_EOF : len; +@@ -252,10 +259,17 @@ static int64_t bluray_seek(URLContext *h, int64_t pos, int whence) + case SEEK_SET: + case SEEK_CUR: + case SEEK_END: +- return bd_seek(bd->bd, pos); +- ++ if (bd->stream_opened) { ++ return bd_file_seek(bd->bd, pos, whence); ++ } else { ++ return bd_seek(bd->bd, pos); ++ } + case AVSEEK_SIZE: +- return bd_get_title_size(bd->bd); ++ if (bd->stream_opened) { ++ return bd_file_size(bd->bd); ++ } else { ++ return bd_get_title_size(bd->bd); ++ } + } + + av_log(h, AV_LOG_ERROR, "Unsupported whence operation %d\n", whence); +-- +2.39.5 (Apple Git-154) + diff --git a/patches/ffmpeg-n6.1/0030-not-very-useful-log-use-trace-level.patch b/patches/ffmpeg-n6.1/0030-not-very-useful-log-use-trace-level.patch new file mode 100644 index 000000000..7df5d2a9f --- /dev/null +++ b/patches/ffmpeg-n6.1/0030-not-very-useful-log-use-trace-level.patch @@ -0,0 +1,76 @@ +From 4a89fee5b4793c8a116d3d33b712601fb6abddcf Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Thu, 17 Apr 2025 10:21:12 +0800 +Subject: [PATCH 30] not very useful log use trace level + +--- + libavcodec/h2645_parse.c | 6 +++--- + libavcodec/h2645_vui.c | 2 +- + libavformat/demux.c | 4 ++-- + 3 files changed, 6 insertions(+), 6 deletions(-) + +diff --git a/libavcodec/h2645_parse.c b/libavcodec/h2645_parse.c +index 28db465..ac9e151 100644 +--- a/libavcodec/h2645_parse.c ++++ b/libavcodec/h2645_parse.c +@@ -353,7 +353,7 @@ static int vvc_parse_nal_header(H2645NAL *nal, void *logctx) + if ((nal->type >= VVC_IDR_W_RADL && nal->type <= VVC_RSV_IRAP_11) && nal->temporal_id) + return AVERROR_INVALIDDATA; + +- av_log(logctx, AV_LOG_DEBUG, ++ av_log(logctx, AV_LOG_TRACE, + "nal_unit_type: %d(%s), nuh_layer_id: %d, temporal_id: %d\n", + nal->type, vvc_nal_unit_name(nal->type), nal->nuh_layer_id, nal->temporal_id); + +@@ -374,7 +374,7 @@ static int hevc_parse_nal_header(H2645NAL *nal, void *logctx) + if (nal->temporal_id < 0) + return AVERROR_INVALIDDATA; + +- av_log(logctx, AV_LOG_DEBUG, ++ av_log(logctx, AV_LOG_TRACE, + "nal_unit_type: %d(%s), nuh_layer_id: %d, temporal_id: %d\n", + nal->type, hevc_nal_unit_name(nal->type), nal->nuh_layer_id, nal->temporal_id); + +@@ -391,7 +391,7 @@ static int h264_parse_nal_header(H2645NAL *nal, void *logctx) + nal->ref_idc = get_bits(gb, 2); + nal->type = get_bits(gb, 5); + +- av_log(logctx, AV_LOG_DEBUG, ++ av_log(logctx, AV_LOG_TRACE, + "nal_unit_type: %d(%s), nal_ref_idc: %d\n", + nal->type, h264_nal_unit_name(nal->type), nal->ref_idc); + +diff --git a/libavcodec/h2645_vui.c b/libavcodec/h2645_vui.c +index e5c7bf4..8301492 100644 +--- a/libavcodec/h2645_vui.c ++++ b/libavcodec/h2645_vui.c +@@ -36,7 +36,7 @@ + + void ff_h2645_decode_common_vui_params(GetBitContext *gb, H2645VUI *vui, void *logctx) + { +- av_log(logctx, AV_LOG_DEBUG, "Decoding VUI\n"); ++ av_log(logctx, AV_LOG_TRACE, "Decoding VUI\n"); + + vui->aspect_ratio_info_present_flag = get_bits1(gb); + if (vui->aspect_ratio_info_present_flag) { +diff --git a/libavformat/demux.c b/libavformat/demux.c +index 15807f6..535acaf 100644 +--- a/libavformat/demux.c ++++ b/libavformat/demux.c +@@ -1979,11 +1979,11 @@ static void estimate_timings(AVFormatContext *ic, int64_t old_offset) + for (unsigned i = 0; i < ic->nb_streams; i++) { + AVStream *const st = ic->streams[i]; + if (st->time_base.den) +- av_log(ic, AV_LOG_TRACE, "stream %u: start_time: %s duration: %s\n", i, ++ av_log(ic, AV_LOG_DEBUG, "stream %u: start_time: %s duration: %s\n", i, + av_ts2timestr(st->start_time, &st->time_base), + av_ts2timestr(st->duration, &st->time_base)); + } +- av_log(ic, AV_LOG_TRACE, ++ av_log(ic, AV_LOG_DEBUG, + "format: start_time: %s duration: %s (estimate from %s) bitrate=%"PRId64" kb/s\n", + av_ts2timestr(ic->start_time, &AV_TIME_BASE_Q), + av_ts2timestr(ic->duration, &AV_TIME_BASE_Q), +-- +2.39.5 (Apple Git-154) + diff --git a/patches/ffmpeg-n6.1/0031-fix-dash-init-fragment-url-is-invalid-truncated-bug.patch b/patches/ffmpeg-n6.1/0031-fix-dash-init-fragment-url-is-invalid-truncated-bug.patch new file mode 100644 index 000000000..83f0d8dbb --- /dev/null +++ b/patches/ffmpeg-n6.1/0031-fix-dash-init-fragment-url-is-invalid-truncated-bug.patch @@ -0,0 +1,43 @@ +From ca6bf697d914be79fcc4d4314c06101d279b6ef3 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Wed, 21 May 2025 18:05:52 +0800 +Subject: [PATCH 31] fix dash init fragment url is "invalid:truncated" bug + +--- + libavformat/dashdec.c | 12 +++++++++++- + 1 file changed, 11 insertions(+), 1 deletion(-) + +diff --git a/libavformat/dashdec.c b/libavformat/dashdec.c +index 0a6c46b..616187f 100644 +--- a/libavformat/dashdec.c ++++ b/libavformat/dashdec.c +@@ -477,6 +477,10 @@ static char *get_content_url(xmlNodePtr *baseurl_nodes, + int i; + char *text; + char *url = NULL; ++ ++ if (strlen(val) >= max_url_size) { ++ max_url_size += 256; ++ } + char *tmp_str = av_mallocz(max_url_size); + + if (!tmp_str) +@@ -495,8 +499,14 @@ static char *get_content_url(xmlNodePtr *baseurl_nodes, + } + } + +- if (val) ++ if (val) { ++ int tmp_max_url_size = strlen(tmp_str) + strlen(val) + 1; ++ if (tmp_max_url_size > max_url_size) { ++ max_url_size = tmp_max_url_size; ++ tmp_str = av_realloc(tmp_str, max_url_size); ++ } + ff_make_absolute_url(tmp_str, max_url_size, tmp_str, val); ++ } + + if (rep_id_val) { + url = av_strireplace(tmp_str, "$RepresentationID$", rep_id_val); +-- +2.39.5 (Apple Git-154) + diff --git a/patches/ffmpeg-n6.1/0032-fix-dash-file-error-unterminated-entity-reference-du.patch b/patches/ffmpeg-n6.1/0032-fix-dash-file-error-unterminated-entity-reference-du.patch new file mode 100644 index 000000000..c433a2222 --- /dev/null +++ b/patches/ffmpeg-n6.1/0032-fix-dash-file-error-unterminated-entity-reference-du.patch @@ -0,0 +1,29 @@ +From aa792f13138a726d80df869f62ebf4769a851fef Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Tue, 6 May 2025 17:49:09 +0800 +Subject: [PATCH 32] fix dash file error "unterminated entity reference" due to + ampersand in tag + +--- + libavformat/dashdec.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/libavformat/dashdec.c b/libavformat/dashdec.c +index 1215407..0a6c46b 100644 +--- a/libavformat/dashdec.c ++++ b/libavformat/dashdec.c +@@ -804,8 +804,10 @@ 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); ++ xmlChar *escaped = xmlEncodeSpecialChars(NULL, tmp_str); ++ xmlNodeSetContent(baseurl_nodes[i], escaped); + updated = 1; ++ xmlFree(escaped); + xmlFree(text); + } + } +-- +2.39.5 (Apple Git-154) + diff --git a/patches/ffmpeg-n6.1/0033-fix-android-ffmpeg6-test.c-1-10-fatal-error-libxml2-.patch b/patches/ffmpeg-n6.1/0033-fix-android-ffmpeg6-test.c-1-10-fatal-error-libxml2-.patch new file mode 100644 index 000000000..afa3593c0 --- /dev/null +++ b/patches/ffmpeg-n6.1/0033-fix-android-ffmpeg6-test.c-1-10-fatal-error-libxml2-.patch @@ -0,0 +1,26 @@ +From 57d11e82ad725d7c598378b70c6288bb96cc9e58 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Fri, 6 Jun 2025 14:07:11 +0800 +Subject: [PATCH 33] fix android ffmpeg6 test.c:1:10: fatal error: + 'libxml2/libxml/xmlversion.h' + +--- + configure | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/configure b/configure +index fb18d97..2cd5e11 100755 +--- a/configure ++++ b/configure +@@ -6871,7 +6871,7 @@ enabled libzmq && require_pkg_config libzmq "libzmq >= 4.2.1" zmq.h z + enabled libzvbi && require_pkg_config libzvbi zvbi-0.2 libzvbi.h vbi_decoder_new && + { test_cpp_condition libzvbi.h "VBI_VERSION_MAJOR > 0 || VBI_VERSION_MINOR > 2 || VBI_VERSION_MINOR == 2 && VBI_VERSION_MICRO >= 28" || + enabled gpl || die "ERROR: libzvbi requires version 0.2.28 or --enable-gpl."; } +-enabled libxml2 && require_pkg_config libxml2 libxml-2.0 libxml2/libxml/xmlversion.h xmlCheckVersion ++enabled libxml2 && require_pkg_config libxml2 libxml-2.0 libxml/xmlversion.h xmlCheckVersion + enabled mbedtls && { check_pkg_config mbedtls mbedtls mbedtls/x509_crt.h mbedtls_x509_crt_init || + check_pkg_config mbedtls mbedtls mbedtls/ssl.h mbedtls_ssl_init || + check_lib mbedtls mbedtls/ssl.h mbedtls_ssl_init -lmbedtls -lmbedx509 -lmbedcrypto || +-- +2.39.5 (Apple Git-154) + diff --git a/patches/ffmpeg-n7.1.1/0001-restore-ijk-h264_ps-null-pointer-fault-toleran.patch b/patches/ffmpeg-n7.1.1/0001-restore-ijk-h264_ps-null-pointer-fault-toleran.patch new file mode 100644 index 000000000..ce2c0934b --- /dev/null +++ b/patches/ffmpeg-n7.1.1/0001-restore-ijk-h264_ps-null-pointer-fault-toleran.patch @@ -0,0 +1,52 @@ +From 393e5aabe3e0726210ec906fcec1f8208ce66ffa Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Wed, 28 May 2025 17:28:22 +0800 +Subject: [PATCH] restore ijk h264_ps null pointer fault tolerant + +--- + libavcodec/h264_ps.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/libavcodec/h264_ps.c b/libavcodec/h264_ps.c +index 3c8fc33..9c0dc50 100644 +--- a/libavcodec/h264_ps.c ++++ b/libavcodec/h264_ps.c +@@ -437,7 +437,7 @@ int ff_h264_decode_seq_parameter_set(GetBitContext *gb, AVCodecContext *avctx, + } + + sps->ref_frame_count = get_ue_golomb_31(gb); +- if (avctx->codec_tag == MKTAG('S', 'M', 'V', '2')) ++ if (avctx && avctx->codec_tag == MKTAG('S', 'M', 'V', '2')) + sps->ref_frame_count = FFMAX(2, sps->ref_frame_count); + if (sps->ref_frame_count > H264_MAX_DPB_FRAMES) { + av_log(avctx, AV_LOG_ERROR, +@@ -480,7 +480,7 @@ int ff_h264_decode_seq_parameter_set(GetBitContext *gb, AVCodecContext *avctx, + int width = 16 * sps->mb_width; + int height = 16 * sps->mb_height; + +- if (avctx->flags2 & AV_CODEC_FLAG2_IGNORE_CROP) { ++ if (avctx && avctx->flags2 & AV_CODEC_FLAG2_IGNORE_CROP) { + av_log(avctx, AV_LOG_DEBUG, "discarding sps cropping, original " + "values are l:%d r:%d t:%d b:%d\n", + crop_left, crop_right, crop_top, crop_bottom); +@@ -552,7 +552,7 @@ int ff_h264_decode_seq_parameter_set(GetBitContext *gb, AVCodecContext *avctx, + if (!sps->vui.sar.den) + sps->vui.sar.den = 1; + +- if (avctx->debug & FF_DEBUG_PICT_INFO) { ++ if (avctx && avctx->debug & FF_DEBUG_PICT_INFO) { + static const char csp[4][5] = { "Gray", "420", "422", "444" }; + av_log(avctx, AV_LOG_DEBUG, + "sps:%u profile:%d/%d poc:%d ref:%d %dx%d %s %s crop:%u/%u/%u/%u %s %s %"PRId32"/%"PRId32" b%d reo:%d\n", +@@ -819,7 +819,7 @@ int ff_h264_decode_picture_parameter_set(GetBitContext *gb, AVCodecContext *avct + if (pps->chroma_qp_index_offset[0] != pps->chroma_qp_index_offset[1]) + pps->chroma_qp_diff = 1; + +- if (avctx->debug & FF_DEBUG_PICT_INFO) { ++ if (avctx && avctx->debug & FF_DEBUG_PICT_INFO) { + av_log(avctx, AV_LOG_DEBUG, + "pps:%u sps:%u %s slice_groups:%d ref:%u/%u %s qp:%d/%d/%d/%d %s %s %s %s\n", + pps_id, pps->sps_id, +-- +2.39.5 (Apple Git-154) + diff --git a/patches/ffmpeg-n7.1.1/0002-restore-ijk-hls-support-discontinuity-tag.patch b/patches/ffmpeg-n7.1.1/0002-restore-ijk-hls-support-discontinuity-tag.patch new file mode 100644 index 000000000..066602274 --- /dev/null +++ b/patches/ffmpeg-n7.1.1/0002-restore-ijk-hls-support-discontinuity-tag.patch @@ -0,0 +1,145 @@ +From 2a82e6a5d412189c243cac24b3c9352e5c80e240 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Wed, 28 May 2025 17:34:08 +0800 +Subject: [PATCH] restore ijk hls support discontinuity tag + +--- + libavformat/hls.c | 59 +++++++++++++++++++++++++++++++++++++++++------ + 1 file changed, 52 insertions(+), 7 deletions(-) + +diff --git a/libavformat/hls.c b/libavformat/hls.c +index b96c5ab..12b49c8 100644 +--- a/libavformat/hls.c ++++ b/libavformat/hls.c +@@ -75,6 +75,8 @@ enum KeyType { + }; + + struct segment { ++ int64_t previous_duration; ++ int64_t start_time; + int64_t duration; + int64_t url_offset; + int64_t size; +@@ -775,6 +777,8 @@ static int test_segment(AVFormatContext *s, const AVInputFormat *in_fmt, struct + static int parse_playlist(HLSContext *c, const char *url, + struct playlist *pls, AVIOContext *in) + { ++ int64_t previous_duration1 = 0, previous_duration = 0, total_duration = 0; ++ + int ret = 0, is_segment = 0, is_variant = 0; + int64_t duration = 0; + enum KeyType key_type = KEY_NONE; +@@ -846,6 +850,7 @@ static int parse_playlist(HLSContext *c, const char *url, + pls->finished = 0; + pls->type = PLS_TYPE_UNSPECIFIED; + } ++ int start_seq_no = -1; + while (!avio_feof(in)) { + ff_get_chomp_line(in, line, sizeof(line)); + if (av_strstart(line, "#EXT-X-STREAM-INF:", &ptr)) { +@@ -895,7 +900,11 @@ static int parse_playlist(HLSContext *c, const char *url, + "INT64_MAX/2, mask out the highest bit\n"); + seq_no &= INT64_MAX/2; + } +- pls->start_seq_no = seq_no; ++ /* Some buggy HLS servers write #EXT-X-MEDIA-SEQUENCE more than once */ ++ if (start_seq_no < 0) { ++ start_seq_no = seq_no; ++ pls->start_seq_no = seq_no; ++ } + } else if (av_strstart(line, "#EXT-X-PLAYLIST-TYPE:", &ptr)) { + ret = ensure_playlist(c, &pls, url); + if (ret < 0) +@@ -960,6 +969,8 @@ static int parse_playlist(HLSContext *c, const char *url, + } else if (av_strstart(line, "#EXT-X-ENDLIST", &ptr)) { + if (pls) + pls->finished = 1; ++ } else if (av_strstart(line, "#EXT-X-DISCONTINUITY", &ptr)) { ++ previous_duration = previous_duration1; + } else if (av_strstart(line, "#EXTINF:", &ptr)) { + is_segment = 1; + duration = atof(ptr) * AV_TIME_BASE; +@@ -1043,6 +1054,11 @@ static int parse_playlist(HLSContext *c, const char *url, + " set to default value to 1ms.\n", seg->url); + duration = 0.001 * AV_TIME_BASE; + } ++ previous_duration1 += duration; ++ seg->previous_duration = previous_duration; ++ seg->start_time = total_duration; ++ total_duration += duration; ++ + seg->duration = duration; + seg->key_type = key_type; + dynarray_add(&pls->segments, &pls->n_segments, seg); +@@ -2360,6 +2376,7 @@ static int hls_read_packet(AVFormatContext *s, AVPacket *pkt) + * stream */ + if (pls->needed && !pls->pkt->data) { + while (1) { ++ int64_t pkt_ts = AV_NOPTS_VALUE; + int64_t ts_diff; + AVRational tb; + struct segment *seg = NULL; +@@ -2373,12 +2390,40 @@ static int hls_read_packet(AVFormatContext *s, AVPacket *pkt) + if (pls->is_id3_timestamped && pls->pkt->stream_index == 0) { + /* audio elementary streams are id3 timestamped */ + fill_timing_for_id3_timestamped_stream(pls); ++ } else { ++ //discontinuity:ts pts need add up. ++ if (pls->finished) { ++ int seq_no = pls->cur_seq_no - pls->start_seq_no; ++ if (seq_no < pls->n_segments && s->streams[pkt->stream_index]) { ++ struct segment *seg = pls->segments[seq_no]; ++ if (seg->previous_duration > 0) { ++ int64_t pred = av_rescale_q(seg->previous_duration, ++ AV_TIME_BASE_Q, ++ s->streams[pkt->stream_index]->time_base); ++ int64_t max_ts = av_rescale_q(seg->start_time + seg->duration, ++ AV_TIME_BASE_Q, ++ s->streams[pkt->stream_index]->time_base); ++ /* EXTINF duration is not precise enough */ ++ max_ts += 2 * AV_TIME_BASE; ++ if (s->start_time > 0) { ++ max_ts += av_rescale_q(s->start_time, ++ AV_TIME_BASE_Q, ++ s->streams[pkt->stream_index]->time_base); ++ } ++ if (pls->pkt->dts != AV_NOPTS_VALUE && pls->pkt->dts + pred < max_ts) pls->pkt->dts += pred; ++ if (pls->pkt->pts != AV_NOPTS_VALUE && pls->pkt->pts + pred < max_ts) pls->pkt->pts += pred; ++ } ++ } ++ } + } + +- if (c->first_timestamp == AV_NOPTS_VALUE && +- pls->pkt->dts != AV_NOPTS_VALUE) +- c->first_timestamp = av_rescale_q(pls->pkt->dts, +- get_timebase(pls), AV_TIME_BASE_Q); ++ if (pls->pkt->pts != AV_NOPTS_VALUE) ++ pkt_ts = pls->pkt->pts; ++ else if (pls->pkt->dts != AV_NOPTS_VALUE) ++ pkt_ts = pls->pkt->dts; ++ ++ if (c->first_timestamp == AV_NOPTS_VALUE && pkt_ts != AV_NOPTS_VALUE) ++ c->first_timestamp = av_rescale_q(pkt_ts, get_timebase(pls), AV_TIME_BASE_Q); + } + + seg = current_segment(pls); +@@ -2395,13 +2440,13 @@ static int hls_read_packet(AVFormatContext *s, AVPacket *pkt) + if (pls->seek_stream_index < 0 || + pls->seek_stream_index == pls->pkt->stream_index) { + +- if (pls->pkt->dts == AV_NOPTS_VALUE) { ++ if (pkt_ts == AV_NOPTS_VALUE) { + pls->seek_timestamp = AV_NOPTS_VALUE; + break; + } + + tb = get_timebase(pls); +- ts_diff = av_rescale_rnd(pls->pkt->dts, AV_TIME_BASE, ++ ts_diff = av_rescale_rnd(pkt_ts, AV_TIME_BASE, + tb.den, AV_ROUND_DOWN) - + pls->seek_timestamp; + if (ts_diff >= 0 && (pls->seek_flags & AVSEEK_FLAG_ANY || +-- +2.39.5 (Apple Git-154) + diff --git a/patches/ffmpeg-n7.1.1/0003-restore-ijk-avformat-add-application-and-dns_c.patch b/patches/ffmpeg-n7.1.1/0003-restore-ijk-avformat-add-application-and-dns_c.patch new file mode 100644 index 000000000..c63420159 --- /dev/null +++ b/patches/ffmpeg-n7.1.1/0003-restore-ijk-avformat-add-application-and-dns_c.patch @@ -0,0 +1,670 @@ +From 00151c6634864504f71858aa015428294c6584bb Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Wed, 28 May 2025 17:37:31 +0800 +Subject: [PATCH] restore ijk avformat add application and dns_cache + +--- + libavformat/Makefile | 4 + + libavformat/application.c | 213 ++++++++++++++++++++++++++++++++++ + libavformat/application.h | 120 ++++++++++++++++++++ + libavformat/dns_cache.c | 232 ++++++++++++++++++++++++++++++++++++++ + libavformat/dns_cache.h | 39 +++++++ + 5 files changed, 608 insertions(+) + create mode 100644 libavformat/application.c + create mode 100644 libavformat/application.h + create mode 100644 libavformat/dns_cache.c + create mode 100644 libavformat/dns_cache.h + +diff --git a/libavformat/Makefile b/libavformat/Makefile +index 7ca68a7..e5934e9 100644 +--- a/libavformat/Makefile ++++ b/libavformat/Makefile +@@ -5,6 +5,8 @@ HEADERS = avformat.h \ + avio.h \ + version.h \ + version_major.h \ ++ application.h \ ++ dns_cache.h \ + + OBJS = allformats.o \ + avformat.o \ +@@ -30,6 +32,8 @@ OBJS = allformats.o \ + url.o \ + utils.o \ + version.o \ ++ application.o \ ++ dns_cache.o \ + + OBJS-$(HAVE_LIBC_MSVCRT) += file_open.o + +diff --git a/libavformat/application.c b/libavformat/application.c +new file mode 100644 +index 0000000..de093b9 +--- /dev/null ++++ b/libavformat/application.c +@@ -0,0 +1,213 @@ ++/* ++ * copyright (c) 2016 Zhang Rui ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg 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. ++ * ++ * FFmpeg 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 FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include "application.h" ++#include "libavformat/network.h" ++#include "libavutil/avstring.h" ++#include "libavutil/mem.h" ++ ++static int av_application_alloc(AVApplicationContext **ph, void *opaque) ++{ ++ AVApplicationContext *h = NULL; ++ ++ h = av_mallocz(sizeof(AVApplicationContext)); ++ if (!h) ++ return AVERROR(ENOMEM); ++ ++ h->opaque = opaque; ++ ++ *ph = h; ++ return 0; ++} ++ ++int av_application_open(AVApplicationContext **ph, void *opaque) ++{ ++ int ret = av_application_alloc(ph, opaque); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static void av_application_close(AVApplicationContext *h) ++{ ++ av_free(h); ++} ++ ++void av_application_closep(AVApplicationContext **ph) ++{ ++ if (!ph || !*ph) ++ return; ++ ++ av_application_close(*ph); ++ *ph = NULL; ++} ++ ++void av_application_on_http_event(AVApplicationContext *h, int event_type, AVAppHttpEvent *event) ++{ ++ if (h && h->func_on_app_event) ++ h->func_on_app_event(h, event_type, (void *)event, sizeof(AVAppHttpEvent)); ++} ++ ++void av_application_will_http_open(AVApplicationContext *h, void *obj, const char *url) ++{ ++ AVAppHttpEvent event = {0}; ++ ++ if (!h || !obj || !url) ++ return; ++ ++ event.obj = obj; ++ av_strlcpy(event.url, url, sizeof(event.url)); ++ ++ av_application_on_http_event(h, AVAPP_EVENT_WILL_HTTP_OPEN, &event); ++} ++ ++void av_application_did_http_open(AVApplicationContext *h, void *obj, const char *url, int error, int http_code, int64_t filesize) ++{ ++ AVAppHttpEvent event = {0}; ++ ++ if (!h || !obj || !url) ++ return; ++ ++ event.obj = obj; ++ av_strlcpy(event.url, url, sizeof(event.url)); ++ event.error = error; ++ event.http_code = http_code; ++ event.filesize = filesize; ++ ++ av_application_on_http_event(h, AVAPP_EVENT_DID_HTTP_OPEN, &event); ++} ++ ++void av_application_will_http_seek(AVApplicationContext *h, void *obj, const char *url, int64_t offset) ++{ ++ AVAppHttpEvent event = {0}; ++ ++ if (!h || !obj || !url) ++ return; ++ ++ event.obj = obj; ++ event.offset = offset; ++ av_strlcpy(event.url, url, sizeof(event.url)); ++ ++ av_application_on_http_event(h, AVAPP_EVENT_WILL_HTTP_SEEK, &event); ++} ++ ++void av_application_did_http_seek(AVApplicationContext *h, void *obj, const char *url, int64_t offset, int error, int http_code) ++{ ++ AVAppHttpEvent event = {0}; ++ ++ if (!h || !obj || !url) ++ return; ++ ++ event.obj = obj; ++ event.offset = offset; ++ av_strlcpy(event.url, url, sizeof(event.url)); ++ event.error = error; ++ event.http_code = http_code; ++ ++ av_application_on_http_event(h, AVAPP_EVENT_DID_HTTP_SEEK, &event); ++} ++ ++static void av_application_on_io_traffic(AVApplicationContext *h, AVAppIOTraffic *event) ++{ ++ if (h && h->func_on_app_event) ++ h->func_on_app_event(h, AVAPP_EVENT_IO_TRAFFIC, (void *)event, sizeof(AVAppIOTraffic)); ++} ++ ++int av_application_on_io_control(AVApplicationContext *h, int event_type, AVAppIOControl *control) ++{ ++ if (h && h->func_on_app_event) ++ return h->func_on_app_event(h, event_type, (void *)control, sizeof(AVAppIOControl)); ++ return 0; ++} ++ ++int av_application_on_tcp_will_open(AVApplicationContext *h) ++{ ++ if (h && h->func_on_app_event) { ++ AVAppTcpIOControl control = {0}; ++ return h->func_on_app_event(h, AVAPP_CTRL_WILL_TCP_OPEN, (void *)&control, sizeof(AVAppTcpIOControl)); ++ } ++ return 0; ++} ++ ++// only callback returns error ++int av_application_on_tcp_did_open(AVApplicationContext *h, int error, int fd, AVAppTcpIOControl *control) ++{ ++ struct sockaddr_storage so_stg; ++ int ret = 0; ++ socklen_t so_len = sizeof(so_stg); ++ int so_family; ++ char *so_ip_name = control->ip; ++ ++ if (!h || !h->func_on_app_event || fd <= 0) ++ return 0; ++ ++ ret = getpeername(fd, (struct sockaddr *)&so_stg, &so_len); ++ if (ret) ++ return 0; ++ control->error = error; ++ control->fd = fd; ++ ++ so_family = ((struct sockaddr*)&so_stg)->sa_family; ++ switch (so_family) { ++ case AF_INET: { ++ struct sockaddr_in* in4 = (struct sockaddr_in*)&so_stg; ++ if (inet_ntop(AF_INET, &(in4->sin_addr), so_ip_name, sizeof(control->ip))) { ++ control->family = AF_INET; ++ control->port = in4->sin_port; ++ } ++ break; ++ } ++ case AF_INET6: { ++ struct sockaddr_in6* in6 = (struct sockaddr_in6*)&so_stg; ++ if (inet_ntop(AF_INET6, &(in6->sin6_addr), so_ip_name, sizeof(control->ip))) { ++ control->family = AF_INET6; ++ control->port = in6->sin6_port; ++ } ++ break; ++ } ++ } ++ ++ return h->func_on_app_event(h, AVAPP_CTRL_DID_TCP_OPEN, (void *)control, sizeof(AVAppTcpIOControl)); ++} ++ ++void av_application_on_async_statistic(AVApplicationContext *h, AVAppAsyncStatistic *statistic) ++{ ++ if (h && h->func_on_app_event) ++ h->func_on_app_event(h, AVAPP_EVENT_ASYNC_STATISTIC, (void *)statistic, sizeof(AVAppAsyncStatistic)); ++} ++ ++void av_application_on_async_read_speed(AVApplicationContext *h, AVAppAsyncReadSpeed *speed) ++{ ++ if (h && h->func_on_app_event) ++ h->func_on_app_event(h, AVAPP_EVENT_ASYNC_READ_SPEED, (void *)speed, sizeof(AVAppAsyncReadSpeed)); ++} ++ ++void av_application_did_io_tcp_read(AVApplicationContext *h, void *obj, int bytes) ++{ ++ AVAppIOTraffic event = {0}; ++ if (!h || !obj || bytes <= 0) ++ return; ++ ++ event.obj = obj; ++ event.bytes = bytes; ++ ++ av_application_on_io_traffic(h, &event); ++} +diff --git a/libavformat/application.h b/libavformat/application.h +new file mode 100644 +index 0000000..b9e7f5b +--- /dev/null ++++ b/libavformat/application.h +@@ -0,0 +1,120 @@ ++/* ++ * copyright (c) 2016 Zhang Rui ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg 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. ++ * ++ * FFmpeg 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 FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#ifndef AVUTIL_APPLICATION_H ++#define AVUTIL_APPLICATION_H ++ ++#include ++ ++#define AVAPP_EVENT_WILL_HTTP_OPEN 1 //AVAppHttpEvent ++#define AVAPP_EVENT_DID_HTTP_OPEN 2 //AVAppHttpEvent ++#define AVAPP_EVENT_WILL_HTTP_SEEK 3 //AVAppHttpEvent ++#define AVAPP_EVENT_DID_HTTP_SEEK 4 //AVAppHttpEvent ++ ++#define AVAPP_EVENT_ASYNC_STATISTIC 0x11000 //AVAppAsyncStatistic ++#define AVAPP_EVENT_ASYNC_READ_SPEED 0x11001 //AVAppAsyncReadSpeed ++#define AVAPP_EVENT_IO_TRAFFIC 0x12204 //AVAppIOTraffic ++ ++#define AVAPP_CTRL_WILL_TCP_OPEN 0x20001 //AVAppTcpIOControl ++#define AVAPP_CTRL_DID_TCP_OPEN 0x20002 //AVAppTcpIOControl ++ ++#define AVAPP_CTRL_WILL_HTTP_OPEN 0x20003 //AVAppIOControl ++#define AVAPP_CTRL_WILL_LIVE_OPEN 0x20005 //AVAppIOControl ++ ++#define AVAPP_CTRL_WILL_CONCAT_SEGMENT_OPEN 0x20007 //AVAppIOControl ++ ++typedef struct AVAppIOControl { ++ size_t size; ++ char url[4096]; /* in, out */ ++ int segment_index; /* in, default = 0 */ ++ int retry_counter; /* in */ ++ ++ int is_handled; /* out, default = false */ ++ int is_url_changed; /* out, default = false */ ++} AVAppIOControl; ++ ++typedef struct AVAppTcpIOControl { ++ int error; ++ int family; ++ char ip[96]; ++ int port; ++ int fd; ++} AVAppTcpIOControl; ++ ++typedef struct AVAppAsyncStatistic { ++ size_t size; ++ int64_t buf_backwards; ++ int64_t buf_forwards; ++ int64_t buf_capacity; ++} AVAppAsyncStatistic; ++ ++typedef struct AVAppAsyncReadSpeed { ++ size_t size; ++ int is_full_speed; ++ int64_t io_bytes; ++ int64_t elapsed_milli; ++} AVAppAsyncReadSpeed; ++ ++typedef struct AVAppHttpEvent ++{ ++ void *obj; ++ char url[4096]; ++ int64_t offset; ++ int error; ++ int http_code; ++ int64_t filesize; ++} AVAppHttpEvent; ++ ++typedef struct AVAppIOTraffic ++{ ++ void *obj; ++ int bytes; ++} AVAppIOTraffic; ++ ++typedef struct AVApplicationContext AVApplicationContext; ++typedef struct AVClass AVClass; ++struct AVApplicationContext { ++ const AVClass *av_class; /**< information for av_log(). Set by av_application_open(). */ ++ void *opaque; /**< user data. */ ++ int (*func_on_app_event)(AVApplicationContext *h, int event_type ,void *obj, size_t size); ++}; ++ ++// open/close ++int av_application_open(AVApplicationContext **ph, void *opaque); ++void av_application_closep(AVApplicationContext **ph); ++ ++// custom protocol invoke ++void av_application_on_http_event(AVApplicationContext *h, int event_type, AVAppHttpEvent *event); ++int av_application_on_io_control(AVApplicationContext *h, int event_type, AVAppIOControl *control); ++void av_application_on_async_statistic(AVApplicationContext *h, AVAppAsyncStatistic *statistic); ++void av_application_on_async_read_speed(AVApplicationContext *h, AVAppAsyncReadSpeed *speed); ++ ++// http event ++void av_application_will_http_open(AVApplicationContext *h, void *obj, const char *url); ++void av_application_did_http_open(AVApplicationContext *h, void *obj, const char *url, int error, int http_code, int64_t filesize); ++void av_application_will_http_seek(AVApplicationContext *h, void *obj, const char *url, int64_t offset); ++void av_application_did_http_seek(AVApplicationContext *h, void *obj, const char *url, int64_t offset, int error, int http_code); ++//tcp event ++int av_application_on_tcp_will_open(AVApplicationContext *h); ++int av_application_on_tcp_did_open(AVApplicationContext *h, int error, int fd, AVAppTcpIOControl *control); ++//tcp speed ++void av_application_did_io_tcp_read(AVApplicationContext *h, void *obj, int bytes); ++ ++#endif /* AVUTIL_APPLICATION_H */ +diff --git a/libavformat/dns_cache.c b/libavformat/dns_cache.c +new file mode 100644 +index 0000000..aab2435 +--- /dev/null ++++ b/libavformat/dns_cache.c +@@ -0,0 +1,232 @@ ++/* ++ * copyright (c) 2017 Raymond Zheng ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg 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. ++ * ++ * FFmpeg 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 FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include "dns_cache.h" ++#include "libavutil/time.h" ++#include "libavutil/mem.h" ++#include "libavformat/network.h" ++#include ++#include ++ ++#if HAVE_PTHREADS ++#include ++#endif ++ ++typedef struct DnsCacheContext DnsCacheContext; ++typedef struct DnsCacheContext { ++ AVDictionary *dns_dictionary; ++ pthread_mutex_t dns_dictionary_mutex; ++ int initialized; ++} DnsCacheContext; ++ ++static DnsCacheContext *context = NULL; ++static pthread_once_t key_once = PTHREAD_ONCE_INIT; ++ ++static void inner_init(void) { ++ int ret = 0; ++ context = (DnsCacheContext *) av_mallocz(sizeof(DnsCacheContext)); ++ if (context) { ++ ret = pthread_mutex_init(&context->dns_dictionary_mutex, NULL); ++ if (!ret) { ++ context->initialized = 1; ++ } else { ++ av_freep(&context); ++ } ++ } ++} ++ ++static void free_private_addrinfo(struct addrinfo **p_ai) { ++ struct addrinfo *ai = *p_ai; ++ ++ if (ai) { ++ if (ai->ai_addr) { ++ av_freep(&ai->ai_addr); ++ } ++ av_freep(p_ai); ++ } ++} ++ ++static int inner_remove_dns_cache(const char *uri, DnsCacheEntry *dns_cache_entry) { ++ if (context && dns_cache_entry) { ++ if (dns_cache_entry->ref_count == 0) { ++ av_dict_set_int(&context->dns_dictionary, uri, 0, 0); ++ free_private_addrinfo(&dns_cache_entry->res); ++ av_freep(&dns_cache_entry); ++ } else { ++ dns_cache_entry->delete_flag = 1; ++ } ++ } ++ ++ return 0; ++} ++ ++static DnsCacheEntry *new_dns_cache_entry(const char *uri, struct addrinfo *cur_ai, int64_t timeout) { ++ DnsCacheEntry *new_entry = NULL; ++ int64_t cur_time = av_gettime_relative(); ++ ++ if (cur_time < 0) { ++ goto fail; ++ } ++ ++ new_entry = (DnsCacheEntry *) av_mallocz(sizeof(struct DnsCacheEntry)); ++ if (!new_entry) { ++ goto fail; ++ } ++ ++ new_entry->res = (struct addrinfo *) av_mallocz(sizeof(struct addrinfo)); ++ if (!new_entry->res) { ++ av_freep(&new_entry); ++ goto fail; ++ } ++ ++ memcpy(new_entry->res, cur_ai, sizeof(struct addrinfo)); ++ ++ new_entry->res->ai_addr = (struct sockaddr *) av_mallocz(sizeof(struct sockaddr)); ++ if (!new_entry->res->ai_addr) { ++ av_freep(&new_entry->res); ++ av_freep(&new_entry); ++ goto fail; ++ } ++ ++ memcpy(new_entry->res->ai_addr, cur_ai->ai_addr, sizeof(struct sockaddr)); ++ new_entry->res->ai_canonname = NULL; ++ new_entry->res->ai_next = NULL; ++ new_entry->ref_count = 0; ++ new_entry->delete_flag = 0; ++ new_entry->expired_time = cur_time + timeout * 1000; ++ ++ return new_entry; ++ ++fail: ++ return NULL; ++} ++ ++DnsCacheEntry *get_dns_cache_reference(const char *uri) { ++ AVDictionaryEntry *elem = NULL; ++ DnsCacheEntry *dns_cache_entry = NULL; ++ int64_t cur_time = av_gettime_relative(); ++ ++ if (cur_time < 0 || !uri || strlen(uri) == 0) { ++ return NULL; ++ } ++ ++ if (!context || !context->initialized) { ++#if HAVE_PTHREADS ++ pthread_once(&key_once, inner_init); ++#endif ++ } ++ ++ if (context && context->initialized) { ++ pthread_mutex_lock(&context->dns_dictionary_mutex); ++ elem = av_dict_get(context->dns_dictionary, uri, NULL, AV_DICT_MATCH_CASE); ++ if (elem) { ++ dns_cache_entry = (DnsCacheEntry *) (intptr_t) strtoll(elem->value, NULL, 10); ++ if (dns_cache_entry) { ++ if (dns_cache_entry->expired_time < cur_time) { ++ inner_remove_dns_cache(uri, dns_cache_entry); ++ dns_cache_entry = NULL; ++ } else { ++ dns_cache_entry->ref_count++; ++ } ++ } ++ } ++ pthread_mutex_unlock(&context->dns_dictionary_mutex); ++ } ++ ++ return dns_cache_entry; ++} ++ ++int release_dns_cache_reference(const char *uri, DnsCacheEntry **p_entry) { ++ DnsCacheEntry *entry = *p_entry; ++ ++ if (!uri || strlen(uri) == 0) { ++ return -1; ++ } ++ ++ if (context && context->initialized && entry) { ++ pthread_mutex_lock(&context->dns_dictionary_mutex); ++ entry->ref_count--; ++ if (entry->delete_flag && entry->ref_count == 0) { ++ inner_remove_dns_cache(uri, entry); ++ entry = NULL; ++ } ++ pthread_mutex_unlock(&context->dns_dictionary_mutex); ++ } ++ return 0; ++} ++ ++int remove_dns_cache_entry(const char *uri) { ++ AVDictionaryEntry *elem = NULL; ++ DnsCacheEntry *dns_cache_entry = NULL; ++ ++ if (!uri || strlen(uri) == 0) { ++ return -1; ++ } ++ ++ if (context && context->initialized) { ++ pthread_mutex_lock(&context->dns_dictionary_mutex); ++ elem = av_dict_get(context->dns_dictionary, uri, NULL, AV_DICT_MATCH_CASE); ++ if (elem) { ++ dns_cache_entry = (DnsCacheEntry *) (intptr_t) strtoll(elem->value, NULL, 10); ++ if (dns_cache_entry) { ++ inner_remove_dns_cache(uri, dns_cache_entry); ++ } ++ } ++ pthread_mutex_unlock(&context->dns_dictionary_mutex); ++ } ++ ++ return 0; ++} ++ ++int add_dns_cache_entry(const char *uri, struct addrinfo *cur_ai, int64_t timeout) { ++ DnsCacheEntry *new_entry = NULL; ++ DnsCacheEntry *old_entry = NULL; ++ AVDictionaryEntry *elem = NULL; ++ ++ if (!uri || strlen(uri) == 0 || timeout <= 0) { ++ goto fail; ++ } ++ ++ if (cur_ai == NULL || cur_ai->ai_addr == NULL) { ++ goto fail; ++ } ++ ++ if (context && context->initialized) { ++ pthread_mutex_lock(&context->dns_dictionary_mutex); ++ elem = av_dict_get(context->dns_dictionary, uri, NULL, AV_DICT_MATCH_CASE); ++ if (elem) { ++ old_entry = (DnsCacheEntry *) (intptr_t) strtoll(elem->value, NULL, 10); ++ if (old_entry) { ++ pthread_mutex_unlock(&context->dns_dictionary_mutex); ++ goto fail; ++ } ++ } ++ new_entry = new_dns_cache_entry(uri, cur_ai, timeout); ++ if (new_entry) { ++ av_dict_set_int(&context->dns_dictionary, uri, (int64_t) (intptr_t) new_entry, 0); ++ } ++ pthread_mutex_unlock(&context->dns_dictionary_mutex); ++ ++ return 0; ++ } ++ ++fail: ++ return -1; ++} +diff --git a/libavformat/dns_cache.h b/libavformat/dns_cache.h +new file mode 100644 +index 0000000..23c695e +--- /dev/null ++++ b/libavformat/dns_cache.h +@@ -0,0 +1,39 @@ ++/* ++ * copyright (c) 2017 Raymond Zheng ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg 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. ++ * ++ * FFmpeg 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 FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#ifndef AVUTIL_DNS_CACHE_H ++#define AVUTIL_DNS_CACHE_H ++ ++#include "libavutil/log.h" ++#include ++ ++typedef struct DnsCacheEntry { ++ volatile int ref_count; ++ volatile int delete_flag; ++ int64_t expired_time; ++ struct addrinfo *res; // construct by private function, not support ai_next and ai_canonname, can only be released using free_private_addrinfo ++} DnsCacheEntry; ++ ++DnsCacheEntry *get_dns_cache_reference(const char *uri); ++int release_dns_cache_reference(const char *uri, DnsCacheEntry **p_entry); ++int remove_dns_cache_entry(const char *uri); ++int add_dns_cache_entry(const char *uri, struct addrinfo *cur_ai, int64_t timeout); ++ ++#endif /* AVUTIL_DNS_CACHE_H */ +-- +2.39.5 (Apple Git-154) + diff --git a/patches/ffmpeg-n7.1.1/0004-restore-ijk-http-event-hooks.patch b/patches/ffmpeg-n7.1.1/0004-restore-ijk-http-event-hooks.patch new file mode 100644 index 000000000..e98614b61 --- /dev/null +++ b/patches/ffmpeg-n7.1.1/0004-restore-ijk-http-event-hooks.patch @@ -0,0 +1,287 @@ +From 3c47c2eaf11d33b44b1bf0e082969f654b48dcd1 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Wed, 28 May 2025 18:01:07 +0800 +Subject: [PATCH] restore ijk http event hooks + +--- + libavformat/http.c | 32 +++++++++++++++++++++++-- + libavformat/tcp.c | 60 ++++++++++++++++++++++++++++++++++++++++++---- + libavutil/error.h | 4 ++++ + 3 files changed, 89 insertions(+), 7 deletions(-) + +diff --git a/libavformat/http.c b/libavformat/http.c +index ec60bc0..1228735 100644 +--- a/libavformat/http.c ++++ b/libavformat/http.c +@@ -45,6 +45,7 @@ + #include "os_support.h" + #include "url.h" + #include "version.h" ++#include "application.h" + + /* XXX: POST protocol is not completely implemented because ffmpeg uses + * only a subset of it. */ +@@ -143,6 +144,9 @@ typedef struct HTTPContext { + unsigned int retry_after; + int reconnect_max_retries; + int reconnect_delay_total_max; ++ char *tcp_hook; ++ char *app_ctx_intptr; ++ AVApplicationContext *app_ctx; + } HTTPContext; + + #define OFFSET(x) offsetof(HTTPContext, x) +@@ -188,6 +192,8 @@ static const AVOption options[] = { + { "resource", "The resource requested by a client", OFFSET(resource), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E }, + { "reply_code", "The http status code to return to a client", OFFSET(reply_code), AV_OPT_TYPE_INT, { .i64 = 200}, INT_MIN, 599, E}, + { "short_seek_size", "Threshold to favor readahead over seek.", OFFSET(short_seek_size), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, D }, ++ { "http-tcp-hook", "hook protocol on tcp", OFFSET(tcp_hook), AV_OPT_TYPE_STRING, { .str = "tcp" }, 0, 0, D | E }, ++ { "ijkapplication", "AVApplicationContext", OFFSET(app_ctx_intptr), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, .flags = D }, + { NULL } + }; + +@@ -218,6 +224,7 @@ static int http_open_cnx_internal(URLContext *h, AVDictionary **options) + char buf[1024], urlbuf[MAX_URL_SIZE]; + int port, use_proxy, err = 0; + HTTPContext *s = h->priv_data; ++ lower_proto = s->tcp_hook; + + av_url_split(proto, sizeof(proto), auth, sizeof(auth), + hostname, sizeof(hostname), &port, +@@ -273,6 +280,13 @@ static int http_open_cnx_internal(URLContext *h, AVDictionary **options) + ff_url_join(buf, sizeof(buf), lower_proto, NULL, hostname, port, NULL); + + if (!s->hd) { ++ av_dict_set_intptr(options, "ijkapplication", (uintptr_t)s->app_ctx, 0); ++ ++ // AVDictionaryEntry *t = NULL; ++ // while ((t = av_dict_get(*options, "", t, AV_DICT_IGNORE_SUFFIX))) { ++ // av_log(NULL, AV_LOG_INFO, "%-*s: %-*s = %s\n", 12, "http open tcp", 28, t->key, t->value); ++ // } ++ + err = ffurl_open_whitelist(&s->hd, buf, AVIO_FLAG_READ_WRITE, + &h->interrupt_callback, options, + h->protocol_whitelist, h->protocol_blacklist, h); +@@ -713,6 +727,7 @@ static int http_open(URLContext *h, const char *uri, int flags, + { + HTTPContext *s = h->priv_data; + int ret; ++ s->app_ctx = (AVApplicationContext *)av_dict_strtoptr(s->app_ctx_intptr); + + if( s->seekable == 1 ) + h->is_streamed = 0; +@@ -749,7 +764,9 @@ static int http_open(URLContext *h, const char *uri, int flags, + if (s->listen) { + return http_listen(h, uri, flags, options); + } ++ av_application_will_http_open(s->app_ctx, (void*)h, uri); + ret = http_open_cnx(h, options); ++ av_application_did_http_open(s->app_ctx, (void*)h, uri, ret, s->http_code, s->filesize); + bail_out: + if (ret < 0) { + av_dict_free(&s->chained_options); +@@ -1667,7 +1684,14 @@ static int http_buf_read(URLContext *h, uint8_t *buf, int size) + uint64_t target_end = s->end_off ? s->end_off : s->filesize; + if ((!s->willclose || s->chunksize == UINT64_MAX) && s->off >= target_end) + return AVERROR_EOF; +- len = ffurl_read(s->hd, buf, size); ++ len = size; ++ if (s->filesize > 0 && s->filesize != UINT64_MAX && s->filesize != INT32_MAX) { ++ int64_t unread = s->filesize - s->off; ++ if (len > unread) ++ len = (int)unread; ++ } ++ if (len > 0) ++ len = ffurl_read(s->hd, buf, len); + if ((!len || len == AVERROR_EOF) && + (!s->willclose || s->chunksize == UINT64_MAX) && s->off < target_end) { + av_log(h, AV_LOG_ERROR, +@@ -2011,7 +2035,9 @@ static int64_t http_seek_internal(URLContext *h, int64_t off, int whence, int fo + s->hd = NULL; + + /* if it fails, continue on old connection */ ++ av_application_will_http_seek(s->app_ctx, (void*)h, s->location, off); + if ((ret = http_open_cnx(h, &options)) < 0) { ++ av_application_did_http_seek(s->app_ctx, (void*)h, s->location, off, ret, s->http_code); + av_dict_free(&options); + memcpy(s->buffer, old_buf, old_buf_size); + s->buf_ptr = s->buffer; +@@ -2020,6 +2046,7 @@ static int64_t http_seek_internal(URLContext *h, int64_t off, int whence, int fo + s->off = old_off; + return ret; + } ++ av_application_did_http_seek(s->app_ctx, (void*)h, s->location, off, ret, s->http_code); + av_dict_free(&options); + ffurl_close(old_hd); + return off; +@@ -2113,6 +2140,7 @@ static int http_proxy_open(URLContext *h, const char *uri, int flags) + HTTPAuthType cur_auth_type; + char *authstr; + ++ s->app_ctx = (AVApplicationContext *)av_dict_strtoptr(s->app_ctx_intptr); + if( s->seekable == 1 ) + h->is_streamed = 0; + else +@@ -2125,7 +2153,7 @@ static int http_proxy_open(URLContext *h, const char *uri, int flags) + if (*path == '/') + path++; + +- ff_url_join(lower_url, sizeof(lower_url), "tcp", NULL, hostname, port, ++ ff_url_join(lower_url, sizeof(lower_url), s->tcp_hook, NULL, hostname, port, + NULL); + redo: + ret = ffurl_open_whitelist(&s->hd, lower_url, AVIO_FLAG_READ_WRITE, +diff --git a/libavformat/tcp.c b/libavformat/tcp.c +index 755e3af..da8a350 100644 +--- a/libavformat/tcp.c ++++ b/libavformat/tcp.c +@@ -24,6 +24,8 @@ + #include "libavutil/parseutils.h" + #include "libavutil/opt.h" + #include "libavutil/time.h" ++#include "libavutil/avstring.h" ++#include "application.h" + + #include "network.h" + #include "os_support.h" +@@ -47,6 +49,9 @@ typedef struct TCPContext { + #if !HAVE_WINSOCK2_H + int tcp_mss; + #endif /* !HAVE_WINSOCK2_H */ ++ ++ char * app_ctx_intptr; ++ AVApplicationContext *app_ctx; + } TCPContext; + + #define OFFSET(x) offsetof(TCPContext, x) +@@ -64,6 +69,8 @@ static const AVOption options[] = { + #if !HAVE_WINSOCK2_H + { "tcp_mss", "Maximum segment size for outgoing TCP packets", OFFSET(tcp_mss), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E }, + #endif /* !HAVE_WINSOCK2_H */ ++ { "ijkapplication", "AVApplicationContext", OFFSET(app_ctx_intptr), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, .flags = D }, ++ { "connect_timeout", "set connect timeout (in microseconds) of socket", OFFSET(open_timeout), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E }, + { NULL } + }; + +@@ -147,7 +154,21 @@ static int tcp_open(URLContext *h, const char *uri, int flags) + int ret; + char hostname[1024],proto[1024],path[1024]; + char portstr[10]; +- s->open_timeout = 5000000; ++ AVAppTcpIOControl control = {0}; ++ ++ int ret2; ++ if (s->open_timeout < 0) { ++ s->open_timeout = 15000000; ++ } ++ // av_log(NULL, AV_LOG_INFO, "xql tcp_open uri %s", uri); ++ // av_log(NULL, AV_LOG_INFO, "%-*s: %-*s = %s\n", 12, "xql tcp_open verify", 28, "ijkapplication", s->app_ctx_intptr); ++ // av_log(NULL, AV_LOG_INFO, "%-*s: %-*s = %d\n", 12, "xql tcp_open verify", 28, "connect_timeout", s->open_timeout); ++ // av_log(NULL, AV_LOG_INFO, "%-*s: %-*s = %d\n", 12, "xql tcp_open verify", 28, "addrinfo_one_by_one", s->addrinfo_one_by_one); ++ // av_log(NULL, AV_LOG_INFO, "%-*s: %-*s = %d\n", 12, "xql tcp_open verify", 28, "addrinfo_timeout", s->addrinfo_timeout); ++ // av_log(NULL, AV_LOG_INFO, "%-*s: %-*s = %d\n", 12, "xql tcp_open verify", 28, "dns_cache_timeout", s->dns_cache_timeout); ++ // av_log(NULL, AV_LOG_INFO, "%-*s: %-*s = %d\n", 12, "xql tcp_open verify", 28, "dns_cache_clear", s->dns_cache_clear); ++ ++ s->app_ctx = (AVApplicationContext *)av_dict_strtoptr(s->app_ctx_intptr); + + av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname), + &port, path, sizeof(path), uri); +@@ -180,6 +201,9 @@ static int tcp_open(URLContext *h, const char *uri, int flags) + } + if (av_find_info_tag(buf, sizeof(buf), "timeout", p)) { + s->rw_timeout = strtol(buf, NULL, 10); ++ if (s->rw_timeout >= 0) { ++ s->open_timeout = s->rw_timeout; ++ } + } + if (av_find_info_tag(buf, sizeof(buf), "listen_timeout", p)) { + s->listen_timeout = strtol(buf, NULL, 10); +@@ -189,7 +213,7 @@ static int tcp_open(URLContext *h, const char *uri, int flags) + } + } + if (s->rw_timeout >= 0) { +- s->open_timeout = ++ //s->open_timeout = + h->rw_timeout = s->rw_timeout; + } + hints.ai_family = AF_UNSPEC; +@@ -247,9 +271,24 @@ static int tcp_open(URLContext *h, const char *uri, int flags) + // Socket descriptor already closed here. Safe to overwrite to client one. + fd = ret; + } else { +- ret = ff_connect_parallel(ai, s->open_timeout / 1000, 3, h, &fd, customize_fd, s); ++ ret = av_application_on_tcp_will_open(s->app_ctx); ++ if (ret) { ++ av_log(NULL, AV_LOG_WARNING, "terminated by application in AVAPP_CTRL_WILL_TCP_OPEN"); ++ goto fail1; ++ } ++ ret = ff_connect_parallel(cur_ai, s->open_timeout / 1000, 3, h, &fd, customize_fd, s); ++ ++ ret2 = av_application_on_tcp_did_open(s->app_ctx, ret, fd, &control); ++ + if (ret < 0) + goto fail1; ++ ++ if (ret2) { ++ av_log(NULL, AV_LOG_WARNING, "terminated by application in AVAPP_CTRL_DID_TCP_OPEN"); ++ ret = ret2; ++ goto fail1; ++ } ++ av_log(NULL, AV_LOG_INFO, "tcp did open uri = %s, ip = %s\n", uri , control.ip); + } + + h->is_streamed = 1; +@@ -290,12 +329,18 @@ static int tcp_read(URLContext *h, uint8_t *buf, int size) + + if (!(h->flags & AVIO_FLAG_NONBLOCK)) { + ret = ff_network_wait_fd_timeout(s->fd, 0, h->rw_timeout, &h->interrupt_callback); +- if (ret) ++ if (ret) { ++ if (ret == AVERROR(ETIMEDOUT)) { ++ ret = AVERROR_TCP_READ_TIMEOUT; ++ } + return ret; ++ } + } + ret = recv(s->fd, buf, size, 0); + if (ret == 0) + return AVERROR_EOF; ++ if (ret > 0) ++ av_application_did_io_tcp_read(s->app_ctx, (void*)h, ret); + return ret < 0 ? ff_neterrno() : ret; + } + +@@ -306,9 +351,14 @@ static int tcp_write(URLContext *h, const uint8_t *buf, int size) + + if (!(h->flags & AVIO_FLAG_NONBLOCK)) { + ret = ff_network_wait_fd_timeout(s->fd, 1, h->rw_timeout, &h->interrupt_callback); +- if (ret) ++ if (ret) { ++ if (ret == AVERROR(ETIMEDOUT)) { ++ ret = AVERROR_TCP_WRITE_TIMEOUT; ++ } + return ret; ++ } + } ++ + ret = send(s->fd, buf, size, MSG_NOSIGNAL); + return ret < 0 ? ff_neterrno() : ret; + } +diff --git a/libavutil/error.h b/libavutil/error.h +index 1efa86c..9dee755 100644 +--- a/libavutil/error.h ++++ b/libavutil/error.h +@@ -85,6 +85,10 @@ + + #define AV_ERROR_MAX_STRING_SIZE 64 + ++#define AVERROR_TCP_CONNECT_TIMEOUT -1001 ++#define AVERROR_TCP_READ_TIMEOUT -1002 ++#define AVERROR_TCP_WRITE_TIMEOUT -1003 ++ + /** + * Put a description of the AVERROR code errnum in errbuf. + * In case of failure the global variable errno is set to indicate the +-- +2.39.5 (Apple Git-154) + diff --git a/patches/ffmpeg-n7.1.1/0005-restore-ijk-tcp-dns-cache.patch b/patches/ffmpeg-n7.1.1/0005-restore-ijk-tcp-dns-cache.patch new file mode 100644 index 000000000..ef9840ff9 --- /dev/null +++ b/patches/ffmpeg-n7.1.1/0005-restore-ijk-tcp-dns-cache.patch @@ -0,0 +1,404 @@ +From 38ea0fc06f40c42cd8aaf6e864095284272274ad Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Wed, 28 May 2025 18:06:03 +0800 +Subject: [PATCH] restore ijk tcp dns cache + +--- + libavformat/tcp.c | 325 +++++++++++++++++++++++++++++++++++++++++++--- + 1 file changed, 310 insertions(+), 15 deletions(-) + +diff --git a/libavformat/tcp.c b/libavformat/tcp.c +index da8a350..5896949 100644 +--- a/libavformat/tcp.c ++++ b/libavformat/tcp.c +@@ -26,13 +26,16 @@ + #include "libavutil/time.h" + #include "libavutil/avstring.h" + #include "application.h" +- ++#include "dns_cache.h" + #include "network.h" + #include "os_support.h" + #include "url.h" + #if HAVE_POLL_H + #include + #endif ++#if HAVE_PTHREADS ++#include ++#endif + + typedef struct TCPContext { + const AVClass *class; +@@ -51,6 +54,10 @@ typedef struct TCPContext { + #endif /* !HAVE_WINSOCK2_H */ + + char * app_ctx_intptr; ++ int addrinfo_one_by_one; ++ int addrinfo_timeout; ++ int64_t dns_cache_timeout; ++ int dns_cache_clear; + AVApplicationContext *app_ctx; + } TCPContext; + +@@ -71,9 +78,261 @@ static const AVOption options[] = { + #endif /* !HAVE_WINSOCK2_H */ + { "ijkapplication", "AVApplicationContext", OFFSET(app_ctx_intptr), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, .flags = D }, + { "connect_timeout", "set connect timeout (in microseconds) of socket", OFFSET(open_timeout), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E }, ++ { "addrinfo_one_by_one", "parse addrinfo one by one in getaddrinfo()", OFFSET(addrinfo_one_by_one), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, .flags = D|E }, ++ { "addrinfo_timeout", "set timeout (in microseconds) for getaddrinfo()", OFFSET(addrinfo_timeout), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E }, ++ { "dns_cache_timeout", "dns cache TTL (in microseconds)", OFFSET(dns_cache_timeout), AV_OPT_TYPE_INT, { .i64 = 0 }, -1, INT64_MAX, .flags = D|E }, ++ { "dns_cache_clear", "clear dns cache", OFFSET(dns_cache_clear), AV_OPT_TYPE_INT, { .i64 = 0}, -1, INT_MAX, .flags = D|E }, + { NULL } + }; + ++int ijk_tcp_getaddrinfo_nonblock(const char *hostname, const char *servname, ++ const struct addrinfo *hints, struct addrinfo **res, ++ int64_t timeout, ++ const AVIOInterruptCB *int_cb, int one_by_one); ++#ifdef HAVE_PTHREADS ++ ++typedef struct TCPAddrinfoRequest ++{ ++ AVBufferRef *buffer; ++ ++ pthread_mutex_t mutex; ++ pthread_cond_t cond; ++ ++ AVIOInterruptCB interrupt_callback; ++ ++ char *hostname; ++ char *servname; ++ struct addrinfo hints; ++ struct addrinfo *res; ++ ++ volatile int finished; ++ int last_error; ++} TCPAddrinfoRequest; ++ ++static void tcp_getaddrinfo_request_free(TCPAddrinfoRequest *req) ++{ ++ av_assert0(req); ++ if (req->res) { ++ freeaddrinfo(req->res); ++ req->res = NULL; ++ } ++ ++ av_freep(&req->servname); ++ av_freep(&req->hostname); ++ pthread_cond_destroy(&req->cond); ++ pthread_mutex_destroy(&req->mutex); ++ av_freep(&req); ++} ++ ++static void tcp_getaddrinfo_request_free_buffer(void *opaque, uint8_t *data) ++{ ++ av_assert0(opaque); ++ TCPAddrinfoRequest *req = (TCPAddrinfoRequest *)opaque; ++ tcp_getaddrinfo_request_free(req); ++} ++ ++static int tcp_getaddrinfo_request_create(TCPAddrinfoRequest **request, ++ const char *hostname, ++ const char *servname, ++ const struct addrinfo *hints, ++ const AVIOInterruptCB *int_cb) ++{ ++ TCPAddrinfoRequest *req = (TCPAddrinfoRequest *) av_mallocz(sizeof(TCPAddrinfoRequest)); ++ if (!req) ++ return AVERROR(ENOMEM); ++ ++ if (pthread_mutex_init(&req->mutex, NULL)) { ++ av_freep(&req); ++ return AVERROR(ENOMEM); ++ } ++ ++ if (pthread_cond_init(&req->cond, NULL)) { ++ pthread_mutex_destroy(&req->mutex); ++ av_freep(&req); ++ return AVERROR(ENOMEM); ++ } ++ ++ if (int_cb) ++ req->interrupt_callback = *int_cb; ++ ++ if (hostname) { ++ req->hostname = av_strdup(hostname); ++ if (!req->hostname) ++ goto fail; ++ } ++ ++ if (servname) { ++ req->servname = av_strdup(servname); ++ if (!req->hostname) ++ goto fail; ++ } ++ ++ if (hints) { ++ req->hints.ai_family = hints->ai_family; ++ req->hints.ai_socktype = hints->ai_socktype; ++ req->hints.ai_protocol = hints->ai_protocol; ++ req->hints.ai_flags = hints->ai_flags; ++ } ++ ++ req->buffer = av_buffer_create(NULL, 0, tcp_getaddrinfo_request_free_buffer, req, 0); ++ if (!req->buffer) ++ goto fail; ++ ++ *request = req; ++ return 0; ++fail: ++ tcp_getaddrinfo_request_free(req); ++ return AVERROR(ENOMEM); ++} ++ ++static void *tcp_getaddrinfo_worker(void *arg) ++{ ++ TCPAddrinfoRequest *req = arg; ++ ++ getaddrinfo(req->hostname, req->servname, &req->hints, &req->res); ++ pthread_mutex_lock(&req->mutex); ++ req->finished = 1; ++ pthread_cond_signal(&req->cond); ++ pthread_mutex_unlock(&req->mutex); ++ av_buffer_unref(&req->buffer); ++ return NULL; ++} ++ ++static void *tcp_getaddrinfo_one_by_one_worker(void *arg) ++{ ++ struct addrinfo *temp_addrinfo = NULL; ++ struct addrinfo *cur = NULL; ++ int ret = EAI_FAIL; ++ int i = 0; ++ int option_length = 0; ++ ++ TCPAddrinfoRequest *req = (TCPAddrinfoRequest *)arg; ++ ++ int family_option[2] = {AF_INET, AF_INET6}; ++ ++ option_length = sizeof(family_option) / sizeof(family_option[0]); ++ ++ for (; i < option_length; ++i) { ++ struct addrinfo *hint = &req->hints; ++ hint->ai_family = family_option[i]; ++ ret = getaddrinfo(req->hostname, req->servname, hint, &temp_addrinfo); ++ if (ret) { ++ req->last_error = ret; ++ continue; ++ } ++ pthread_mutex_lock(&req->mutex); ++ if (!req->res) { ++ req->res = temp_addrinfo; ++ } else { ++ cur = req->res; ++ while (cur->ai_next) ++ cur = cur->ai_next; ++ cur->ai_next = temp_addrinfo; ++ } ++ pthread_mutex_unlock(&req->mutex); ++ } ++ pthread_mutex_lock(&req->mutex); ++ req->finished = 1; ++ pthread_cond_signal(&req->cond); ++ pthread_mutex_unlock(&req->mutex); ++ av_buffer_unref(&req->buffer); ++ return NULL; ++} ++ ++int ijk_tcp_getaddrinfo_nonblock(const char *hostname, const char *servname, ++ const struct addrinfo *hints, struct addrinfo **res, ++ int64_t timeout, ++ const AVIOInterruptCB *int_cb, int one_by_one) ++{ ++ int ret; ++ int64_t start; ++ int64_t now; ++ AVBufferRef *req_ref = NULL; ++ TCPAddrinfoRequest *req = NULL; ++ pthread_t work_thread; ++ ++ if (hostname && !hostname[0]) ++ hostname = NULL; ++ av_log(NULL, AV_LOG_DEBUG, "dns getaddrinfo uri = %s\n", hostname); ++ if (timeout <= 0) ++ return getaddrinfo(hostname, servname, hints, res); ++ av_log(NULL, AV_LOG_DEBUG, "dns tcp_getaddrinfo_request_create uri = %s\n", hostname); ++ ret = tcp_getaddrinfo_request_create(&req, hostname, servname, hints, int_cb); ++ if (ret) ++ goto fail; ++ ++ req_ref = av_buffer_ref(req->buffer); ++ if (req_ref == NULL) { ++ ret = AVERROR(ENOMEM); ++ goto fail; ++ } ++ ++ /* FIXME: using a thread pool would be better. */ ++ if (one_by_one) ++ ret = pthread_create(&work_thread, NULL, tcp_getaddrinfo_one_by_one_worker, req); ++ else ++ ret = pthread_create(&work_thread, NULL, tcp_getaddrinfo_worker, req); ++ ++ if (ret) { ++ ret = AVERROR(ret); ++ goto fail; ++ } ++ ++ pthread_detach(work_thread); ++ ++ start = av_gettime(); ++ now = start; ++ ++ pthread_mutex_lock(&req->mutex); ++ while (1) { ++ int64_t wait_time = now + 100000; ++ struct timespec tv = { .tv_sec = wait_time / 1000000, ++ .tv_nsec = (wait_time % 1000000) * 1000 }; ++ ++ if (req->finished || (start + timeout < now)) { ++ if (req->res) { ++ ret = 0; ++ *res = req->res; ++ req->res = NULL; ++ } else { ++ ret = req->last_error ? req->last_error : AVERROR_EXIT; ++ } ++ break; ++ } ++#if defined(__ANDROID__) && defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC) ++ ret = pthread_cond_timedwait_monotonic_np(&req->cond, &req->mutex, &tv); ++#else ++ ret = pthread_cond_timedwait(&req->cond, &req->mutex, &tv); ++#endif ++ if (ret != 0 && ret != ETIMEDOUT) { ++ av_log(NULL, AV_LOG_ERROR, "pthread_cond_timedwait failed: %d\n", ret); ++ ret = AVERROR_EXIT; ++ break; ++ } ++ ++ if (ff_check_interrupt(&req->interrupt_callback)) { ++ ret = AVERROR_EXIT; ++ break; ++ } ++ ++ now = av_gettime(); ++ } ++ pthread_mutex_unlock(&req->mutex); ++fail: ++ av_buffer_unref(&req_ref); ++ return ret; ++} ++ ++#else ++int ijk_tcp_getaddrinfo_nonblock(const char *hostname, const char *servname, ++ const struct addrinfo *hints, struct addrinfo **res, ++ int64_t timeout, ++ const AVIOInterruptCB *int_cb) ++{ ++ return getaddrinfo(hostname, servname, hints, res); ++} ++#endif ++ + static const AVClass tcp_class = { + .class_name = "tcp", + .item_name = av_default_item_name, +@@ -155,7 +414,7 @@ static int tcp_open(URLContext *h, const char *uri, int flags) + char hostname[1024],proto[1024],path[1024]; + char portstr[10]; + AVAppTcpIOControl control = {0}; +- ++ DnsCacheEntry *dns_entry = NULL; + int ret2; + if (s->open_timeout < 0) { + s->open_timeout = 15000000; +@@ -221,18 +480,37 @@ static int tcp_open(URLContext *h, const char *uri, int flags) + snprintf(portstr, sizeof(portstr), "%d", port); + if (s->listen) + hints.ai_flags |= AI_PASSIVE; +- if (!hostname[0]) +- ret = getaddrinfo(NULL, portstr, &hints, &ai); +- else +- ret = getaddrinfo(hostname, portstr, &hints, &ai); +- if (ret) { +- av_log(h, AV_LOG_ERROR, +- "Failed to resolve hostname %s: %s\n", +- hostname, gai_strerror(ret)); +- return AVERROR(EIO); ++ if (s->dns_cache_timeout > 0) { ++ if (s->dns_cache_clear) { ++ remove_dns_cache_entry(uri); ++ } else { ++ dns_entry = get_dns_cache_reference(uri); ++ } + } + +- cur_ai = ai; ++ if (!dns_entry) { ++#ifdef HAVE_PTHREADS ++ ret = ijk_tcp_getaddrinfo_nonblock(hostname, portstr, &hints, &ai, s->addrinfo_timeout, &h->interrupt_callback, s->addrinfo_one_by_one); ++#else ++ if (s->addrinfo_timeout > 0) ++ av_log(h, AV_LOG_WARNING, "Ignore addrinfo_timeout without pthreads support.\n"); ++ if (!hostname[0]) ++ ret = getaddrinfo(NULL, portstr, &hints, &ai); ++ else ++ ret = getaddrinfo(hostname, portstr, &hints, &ai); ++#endif ++ if (ret) { ++ av_log(h, AV_LOG_ERROR, ++ "Failed to resolve hostname %s: %s\n", ++ hostname, gai_strerror(ret)); ++ return AVERROR(EIO); ++ } ++ ++ cur_ai = ai; ++ } else { ++ av_log(NULL, AV_LOG_DEBUG, "hit dns cache uri = %s\n", uri); ++ cur_ai = dns_entry->res; ++ } + + #if HAVE_STRUCT_SOCKADDR_IN6 + // workaround for IOS9 getaddrinfo in IPv6 only network use hardcode IPv4 address can not resolve port number. +@@ -288,19 +566,36 @@ static int tcp_open(URLContext *h, const char *uri, int flags) + ret = ret2; + goto fail1; + } +- av_log(NULL, AV_LOG_INFO, "tcp did open uri = %s, ip = %s\n", uri , control.ip); ++ ++ if (!dns_entry && !strstr(uri, control.ip) && s->dns_cache_timeout > 0) { ++ add_dns_cache_entry(uri, cur_ai, s->dns_cache_timeout); ++ av_log(NULL, AV_LOG_DEBUG, "add dns cache uri = %s, ip = %s\n", uri , control.ip); ++ } ++ av_log(NULL, AV_LOG_DEBUG, "tcp did open uri = %s, ip = %s\n", uri , control.ip); + } + + h->is_streamed = 1; + s->fd = fd; + +- freeaddrinfo(ai); ++ if (dns_entry) { ++ release_dns_cache_reference(uri, &dns_entry); ++ } else { ++ freeaddrinfo(ai); ++ } + return 0; + + fail1: + if (fd >= 0) + closesocket(fd); +- freeaddrinfo(ai); ++ ++ if (dns_entry) { ++ av_log(NULL, AV_LOG_ERROR, "hit dns cache but connect fail uri = %s, ip = %s\n", uri , control.ip); ++ release_dns_cache_reference(uri, &dns_entry); ++ remove_dns_cache_entry(uri); ++ } else { ++ freeaddrinfo(cur_ai); ++ } ++ + return ret; + } + +-- +2.39.5 (Apple Git-154) + diff --git a/patches/ffmpeg-n7.1.1/0006-restore-ijk-custom-protocols-and-demuxers-except-lon.patch b/patches/ffmpeg-n7.1.1/0006-restore-ijk-custom-protocols-and-demuxers-except-lon.patch new file mode 100644 index 000000000..8fd50fae3 --- /dev/null +++ b/patches/ffmpeg-n7.1.1/0006-restore-ijk-custom-protocols-and-demuxers-except-lon.patch @@ -0,0 +1,275 @@ +From c106612677559c49d3560a502077128b468d011f Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Tue, 12 May 2026 09:42:01 +0800 +Subject: [PATCH] restore ijk custom protocols and demuxers except long url + and async + +--- + libavcodec/Makefile | 1 + + libavformat/Makefile | 10 ++++ + libavformat/allformats.c | 3 ++ + libavformat/demux.c | 13 +++++ + libavformat/demux.h | 2 + + libavformat/ijkutils.c | 107 +++++++++++++++++++++++++++++++++++++++ + libavformat/protocols.c | 6 +++ + libavutil/Makefile | 1 + + 8 files changed, 143 insertions(+) + create mode 100644 libavformat/ijkutils.c + +diff --git a/libavcodec/Makefile b/libavcodec/Makefile +index 153a9e3881..79903a108d 100644 +--- a/libavcodec/Makefile ++++ b/libavcodec/Makefile +@@ -25,6 +25,7 @@ HEADERS = ac3_parser.h \ + version_major.h \ + videotoolbox.h \ + vorbis_parser.h \ ++ packet_internal.h \ + + OBJS = ac3_parser.o \ + adts_parser.o \ +diff --git a/libavformat/Makefile b/libavformat/Makefile +index e5934e9f9d..5e880602e7 100644 +--- a/libavformat/Makefile ++++ b/libavformat/Makefile +@@ -7,6 +7,15 @@ HEADERS = avformat.h \ + version_major.h \ + application.h \ + dns_cache.h \ ++ demux.h \ ++ avc.h \ ++ url.h \ ++ internal.h \ ++ avio_internal.h \ ++ flv.h \ ++ id3v2.h \ ++ os_support.h \ ++ metadata.h \ + + OBJS = allformats.o \ + avformat.o \ +@@ -34,6 +43,7 @@ OBJS = allformats.o \ + version.o \ + application.o \ + dns_cache.o \ ++ ijkutils.o \ + + OBJS-$(HAVE_LIBC_MSVCRT) += file_open.o + +diff --git a/libavformat/allformats.c b/libavformat/allformats.c +index 305fa46532..e5fea1d021 100644 +--- a/libavformat/allformats.c ++++ b/libavformat/allformats.c +@@ -576,6 +576,9 @@ extern const FFInputFormat ff_libmodplug_demuxer; + extern const FFInputFormat ff_libopenmpt_demuxer; + extern const FFInputFormat ff_vapoursynth_demuxer; + ++// ijk custom demuxers ++extern FFInputFormat ff_ijklivehook_demuxer; ++ + #include "libavformat/muxer_list.c" + #include "libavformat/demuxer_list.c" + +diff --git a/libavformat/demux.c b/libavformat/demux.c +index 2796209239..ae0532b1d5 100644 +--- a/libavformat/demux.c ++++ b/libavformat/demux.c +@@ -218,6 +218,7 @@ int avformat_open_input(AVFormatContext **ps, const char *filename, + AVFormatContext *s = *ps; + FFFormatContext *si; + AVDictionary *tmp = NULL; ++ AVDictionary *tmp2 = NULL; + ID3v2ExtraMeta *id3v2_extra_meta = NULL; + int ret = 0; + +@@ -301,6 +302,16 @@ int avformat_open_input(AVFormatContext **ps, const char *filename, + if (s->pb) + ff_id3v2_read_dict(s->pb, &si->id3v2_meta, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta); + ++ if (ffifmt(s->iformat)->read_header2) { ++ if (options) ++ av_dict_copy(&tmp2,*options, 0); ++ if ((ret = ffifmt(s->iformat)->read_header2(s, &tmp2)) < 0) { ++ if (ffifmt(s->iformat)->flags_internal & FF_INFMT_FLAG_INIT_CLEANUP) ++ goto close; ++ goto fail; ++ } ++ } else ++ + if (ffifmt(s->iformat)->read_header) + if ((ret = ffifmt(s->iformat)->read_header(s)) < 0) { + if (ffifmt(s->iformat)->flags_internal & FF_INFMT_FLAG_INIT_CLEANUP) +@@ -343,6 +354,7 @@ int avformat_open_input(AVFormatContext **ps, const char *filename, + if (options) { + av_dict_free(options); + *options = tmp; ++ av_dict_free(&tmp2); + } + *ps = s; + return 0; +@@ -353,6 +365,7 @@ close: + fail: + ff_id3v2_free_extra_meta(&id3v2_extra_meta); + av_dict_free(&tmp); ++ av_dict_free(&tmp2); + if (s->pb && !(s->flags & AVFMT_FLAG_CUSTOM_IO)) + avio_closep(&s->pb); + avformat_free_context(s); +diff --git a/libavformat/demux.h b/libavformat/demux.h +index 9c76095662..8b0af4c9fe 100644 +--- a/libavformat/demux.h ++++ b/libavformat/demux.h +@@ -69,6 +69,8 @@ typedef struct FFInputFormat { + */ + int (*read_header)(struct AVFormatContext *); + ++ int (*read_header2)(struct AVFormatContext *, AVDictionary **options); ++ + /** + * Read one packet and put it in 'pkt'. pts and flags are also + * set. 'avformat_new_stream' can be called only if the flag +diff --git a/libavformat/ijkutils.c b/libavformat/ijkutils.c +new file mode 100644 +index 0000000000..f85eae90c7 +--- /dev/null ++++ b/libavformat/ijkutils.c +@@ -0,0 +1,107 @@ ++/* ++ * utils.c ++ * ++ * Copyright (c) 2003 Fabrice Bellard ++ * Copyright (c) 2013 Zhang Rui ++ * ++ * This file is part of ijkPlayer. ++ * ++ * ijkPlayer 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. ++ * ++ * ijkPlayer 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 ijkPlayer; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include ++#include "url.h" ++#include "demux.h" ++ ++ ++#define IJK_FF_PROTOCOL(x) \ ++extern URLProtocol ff_##x##_protocol; \ ++int ijkav_register_##x##_protocol(URLProtocol *protocol, int protocol_size); \ ++int ijkav_register_##x##_protocol(URLProtocol *protocol, int protocol_size) \ ++{ \ ++ if (protocol_size != sizeof(URLProtocol)) { \ ++ av_log(NULL, AV_LOG_ERROR, "ijkav_register_##x##_protocol: ABI mismatch.\n"); \ ++ return -1; \ ++ } \ ++ memcpy(&ff_##x##_protocol, protocol, protocol_size); \ ++ return 0; \ ++} ++ ++#define IJK_DUMMY_PROTOCOL(x) \ ++IJK_FF_PROTOCOL(x); \ ++static const AVClass ijk_##x##_context_class = { \ ++ .class_name = #x, \ ++ .item_name = av_default_item_name, \ ++ .version = LIBAVUTIL_VERSION_INT, \ ++ }; \ ++ \ ++URLProtocol ff_##x##_protocol = { \ ++ .name = #x, \ ++ .url_open2 = ijkdummy_open, \ ++ .priv_data_size = 1, \ ++ .priv_data_class = &ijk_##x##_context_class, \ ++}; ++ ++static int ijkdummy_open(URLContext *h, const char *arg, int flags, AVDictionary **options) ++{ ++ return -1; ++} ++ ++static int ijkdummy_probe(const AVProbeData *p) ++{ ++ return AVERROR_INVALIDDATA; ++} ++ ++IJK_DUMMY_PROTOCOL(ijkmediadatasource); ++IJK_DUMMY_PROTOCOL(ijkhttphook); ++IJK_DUMMY_PROTOCOL(ijksegment); ++IJK_DUMMY_PROTOCOL(ijktcphook); ++IJK_DUMMY_PROTOCOL(ijkio); ++ ++#define IJK_FF_DEMUXER(x) \ ++extern FFInputFormat ff_##x##_demuxer; \ ++int ijkav_register_##x##_demuxer(FFInputFormat *demuxer, int demuxer_size); \ ++int ijkav_register_##x##_demuxer(FFInputFormat *demuxer, int demuxer_size) \ ++{ \ ++ if (demuxer_size != sizeof(FFInputFormat)) { \ ++ av_log(NULL, AV_LOG_ERROR, "ijkav_register_##x##_demuxer: ABI mismatch.\n"); \ ++ return -1; \ ++ } \ ++ memcpy(&ff_##x##_demuxer, demuxer, demuxer_size); \ ++ return 0; \ ++} ++ ++#define IJK_DUMMY_DEMUXER(x) \ ++IJK_FF_DEMUXER(x); \ ++static const AVClass ijk_##x##_demuxer_class = { \ ++ .class_name = #x, \ ++ .item_name = av_default_item_name, \ ++ .version = LIBAVUTIL_VERSION_INT, \ ++ }; \ ++ \ ++FFInputFormat ff_##x##_demuxer = { \ ++ .p.name = #x, \ ++ .p.priv_class = &ijk_##x##_demuxer_class, \ ++ .priv_data_size = 1, \ ++ .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, \ ++ .read_probe = ijkdummy_probe, \ ++}; ++ ++/* ++ libavformat/ijkutils.c:99:1: error: field designator 'priv_data_size' does not refer to any field in type 'AVInputFormat' (aka 'struct AVInputFormat') ++ 99 | IJK_DUMMY_DEMUXER(ijklivehook); ++ ++ */ ++IJK_DUMMY_DEMUXER(ijklivehook); +diff --git a/libavformat/protocols.c b/libavformat/protocols.c +index 93a6d67261..719caf8492 100644 +--- a/libavformat/protocols.c ++++ b/libavformat/protocols.c +@@ -79,6 +79,12 @@ extern const URLProtocol ff_libzmq_protocol; + extern const URLProtocol ff_ipfs_gateway_protocol; + extern const URLProtocol ff_ipns_gateway_protocol; + ++extern const URLProtocol ff_ijkhttphook_protocol; ++extern const URLProtocol ff_ijkmediadatasource_protocol; ++extern const URLProtocol ff_ijksegment_protocol; ++extern const URLProtocol ff_ijktcphook_protocol; ++extern const URLProtocol ff_ijkio_protocol; ++ + #include "libavformat/protocol_list.c" + + const AVClass *ff_urlcontext_child_class_iterate(void **iter) +diff --git a/libavutil/Makefile b/libavutil/Makefile +index 6e6fa8d800..5f47b2074e 100644 +--- a/libavutil/Makefile ++++ b/libavutil/Makefile +@@ -82,6 +82,7 @@ HEADERS = adler32.h \ + spherical.h \ + stereo3d.h \ + threadmessage.h \ ++ thread.h \ + time.h \ + timecode.h \ + timestamp.h \ +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n7.1.1/0007-restore-ijk-av_dict_get-that-converts-the-value-to-a.patch b/patches/ffmpeg-n7.1.1/0007-restore-ijk-av_dict_get-that-converts-the-value-to-a.patch new file mode 100644 index 000000000..ec2ecedf7 --- /dev/null +++ b/patches/ffmpeg-n7.1.1/0007-restore-ijk-av_dict_get-that-converts-the-value-to-a.patch @@ -0,0 +1,90 @@ +From 83e6a1aa986fef7a5c44f008baae36e2e0059cac Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Wed, 28 May 2025 18:33:27 +0800 +Subject: [PATCH] restore ijk av_dict_get that converts the value to a pointer + +--- + libavutil/dict.c | 39 ++++++++++++++++++++++++++++++++++++++- + libavutil/dict.h | 9 +++++++++ + 2 files changed, 47 insertions(+), 1 deletion(-) + +diff --git a/libavutil/dict.c b/libavutil/dict.c +index 6fb0939..3727187 100644 +--- a/libavutil/dict.c ++++ b/libavutil/dict.c +@@ -65,7 +65,7 @@ AVDictionaryEntry *av_dict_get(const AVDictionary *m, const char *key, + const AVDictionaryEntry *entry = prev; + unsigned int j; + +- if (!key) ++ if (!m || !key) + return NULL; + + while ((entry = av_dict_iterate(m, entry))) { +@@ -173,6 +173,43 @@ int av_dict_set_int(AVDictionary **pm, const char *key, int64_t value, + return av_dict_set(pm, key, valuestr, flags); + } + ++int av_dict_set_intptr(AVDictionary **pm, const char *key, uintptr_t value, ++ int flags) ++{ ++ char valuestr[22]; ++ snprintf(valuestr, sizeof(valuestr), "%p", value); ++ flags &= ~AV_DICT_DONT_STRDUP_VAL; ++ return av_dict_set(pm, key, valuestr, flags); ++} ++ ++uintptr_t av_dict_get_intptr(const AVDictionary *m, const char* key) { ++ uintptr_t ptr = NULL; ++ AVDictionaryEntry *t = NULL; ++ if ((t = av_dict_get(m, key, NULL, 0))) { ++ return av_dict_strtoptr(t->value); ++ } ++ return NULL; ++} ++ ++uintptr_t av_dict_strtoptr(char * value) { ++ uintptr_t ptr = NULL; ++ char *next = NULL; ++ if(!value || value[0] !='0' || (value[1]|0x20)!='x') { ++ return NULL; ++ } ++ ptr = strtoull(value, &next, 16); ++ if (next == value) { ++ return NULL; ++ } ++ return ptr; ++} ++ ++char * av_dict_ptrtostr(uintptr_t value) { ++ char valuestr[22] = {0}; ++ snprintf(valuestr, sizeof(valuestr), "%p", value); ++ return av_strdup(valuestr); ++} ++ + static int parse_key_value_pair(AVDictionary **pm, const char **buf, + const char *key_val_sep, const char *pairs_sep, + int flags) +diff --git a/libavutil/dict.h b/libavutil/dict.h +index 713c9e3..fdcc167 100644 +--- a/libavutil/dict.h ++++ b/libavutil/dict.h +@@ -172,6 +172,15 @@ int av_dict_set(AVDictionary **pm, const char *key, const char *value, int flags + */ + int av_dict_set_int(AVDictionary **pm, const char *key, int64_t value, int flags); + ++/** ++ * Convenience wrapper for av_dict_get that converts the value to a pointer ++ * and stores it. ++ */ ++int av_dict_set_intptr(AVDictionary **pm, const char *key, uintptr_t value, int flags); ++uintptr_t av_dict_get_intptr(const AVDictionary *m, const char* key); ++uintptr_t av_dict_strtoptr(char * value); ++char * av_dict_ptrtostr(uintptr_t value); ++ + /** + * Parse the key/value pairs list and add the parsed entries to a dictionary. + * +-- +2.39.5 (Apple Git-154) + diff --git a/patches/ffmpeg-n7.1.1/0008-add-4-dummy-ijkplaceholder-demuxers.patch b/patches/ffmpeg-n7.1.1/0008-add-4-dummy-ijkplaceholder-demuxers.patch new file mode 100644 index 000000000..453be0aa0 --- /dev/null +++ b/patches/ffmpeg-n7.1.1/0008-add-4-dummy-ijkplaceholder-demuxers.patch @@ -0,0 +1,41 @@ +From cf2c1717314379bc6a5d483eda64a6db7126a42d Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Tue, 12 May 2026 09:45:35 +0800 +Subject: [PATCH] add 4 dummy ijkplaceholder demuxers + +--- + libavformat/allformats.c | 4 ++++ + libavformat/ijkutils.c | 4 ++++ + 2 files changed, 8 insertions(+) + +diff --git a/libavformat/allformats.c b/libavformat/allformats.c +index e5fea1d021..311b4358af 100644 +--- a/libavformat/allformats.c ++++ b/libavformat/allformats.c +@@ -578,6 +578,10 @@ extern const FFInputFormat ff_vapoursynth_demuxer; + + // ijk custom demuxers + extern FFInputFormat ff_ijklivehook_demuxer; ++extern FFInputFormat ff_ijkplaceholder1_demuxer; ++extern FFInputFormat ff_ijkplaceholder2_demuxer; ++extern FFInputFormat ff_ijkplaceholder3_demuxer; ++extern FFInputFormat ff_ijkplaceholder4_demuxer; + + #include "libavformat/muxer_list.c" + #include "libavformat/demuxer_list.c" +diff --git a/libavformat/ijkutils.c b/libavformat/ijkutils.c +index f85eae90c7..bc851d2914 100644 +--- a/libavformat/ijkutils.c ++++ b/libavformat/ijkutils.c +@@ -105,3 +105,7 @@ FFInputFormat ff_##x##_demuxer = { \ + + */ + IJK_DUMMY_DEMUXER(ijklivehook); ++IJK_DUMMY_DEMUXER(ijkplaceholder1); ++IJK_DUMMY_DEMUXER(ijkplaceholder2); ++IJK_DUMMY_DEMUXER(ijkplaceholder3); ++IJK_DUMMY_DEMUXER(ijkplaceholder4); +\ No newline at end of file +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n7.1.1/0009-add-3-dummy-ijkhttp-protocols.patch b/patches/ffmpeg-n7.1.1/0009-add-3-dummy-ijkhttp-protocols.patch new file mode 100644 index 000000000..ce3b4f7c9 --- /dev/null +++ b/patches/ffmpeg-n7.1.1/0009-add-3-dummy-ijkhttp-protocols.patch @@ -0,0 +1,41 @@ +From e294f9626d50ce1808253f0ba28c4a60e6c78f0e Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Tue, 12 May 2026 09:46:53 +0800 +Subject: [PATCH] add 3 dummy ijkhttp protocols + +--- + libavformat/ijkutils.c | 3 +++ + libavformat/protocols.c | 3 +++ + 2 files changed, 6 insertions(+) + +diff --git a/libavformat/ijkutils.c b/libavformat/ijkutils.c +index bc851d2914..31e1e8064b 100644 +--- a/libavformat/ijkutils.c ++++ b/libavformat/ijkutils.c +@@ -69,6 +69,9 @@ IJK_DUMMY_PROTOCOL(ijkhttphook); + IJK_DUMMY_PROTOCOL(ijksegment); + IJK_DUMMY_PROTOCOL(ijktcphook); + IJK_DUMMY_PROTOCOL(ijkio); ++IJK_DUMMY_PROTOCOL(ijkhttp1); ++IJK_DUMMY_PROTOCOL(ijkhttp2); ++IJK_DUMMY_PROTOCOL(ijkhttp3); + + #define IJK_FF_DEMUXER(x) \ + extern FFInputFormat ff_##x##_demuxer; \ +diff --git a/libavformat/protocols.c b/libavformat/protocols.c +index 719caf8492..b291b9f97c 100644 +--- a/libavformat/protocols.c ++++ b/libavformat/protocols.c +@@ -84,6 +84,9 @@ extern const URLProtocol ff_ijkmediadatasource_protocol; + extern const URLProtocol ff_ijksegment_protocol; + extern const URLProtocol ff_ijktcphook_protocol; + extern const URLProtocol ff_ijkio_protocol; ++extern const URLProtocol ff_ijkhttp1_protocol; ++extern const URLProtocol ff_ijkhttp2_protocol; ++extern const URLProtocol ff_ijkhttp3_protocol; + + #include "libavformat/protocol_list.c" + +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n7.1.1/0010-control-which-http-impl-was-used-by-set-selected_htt.patch b/patches/ffmpeg-n7.1.1/0010-control-which-http-impl-was-used-by-set-selected_htt.patch new file mode 100644 index 000000000..e36c9bdb6 --- /dev/null +++ b/patches/ffmpeg-n7.1.1/0010-control-which-http-impl-was-used-by-set-selected_htt.patch @@ -0,0 +1,128 @@ +From 32393a823175ffadba9d635b86f8836c503b837e Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Wed, 28 May 2025 18:25:51 +0800 +Subject: [PATCH] control which http impl was used by set selected_http option + +--- + libavformat/avio.c | 63 ++++++++++++++++++++++++++++++++++++++++++++-- + libavformat/url.h | 16 ++++++++++++ + 2 files changed, 77 insertions(+), 2 deletions(-) + +diff --git a/libavformat/avio.c b/libavformat/avio.c +index d109f3a..8c6b769 100644 +--- a/libavformat/avio.c ++++ b/libavformat/avio.c +@@ -359,6 +359,65 @@ int ffurl_alloc(URLContext **puc, const char *filename, int flags, + return AVERROR_PROTOCOL_NOT_FOUND; + } + ++ ++static const struct URLProtocol *url_find_the_protocol(const char *proto_str) ++{ ++ const URLProtocol **protocols = ffurl_get_protocols(NULL, NULL); ++ if (!protocols) ++ return NULL; ++ for (int i = 0; protocols[i]; i++) { ++ const URLProtocol *up = protocols[i]; ++ if (!strcmp(proto_str, up->name)) { ++ av_freep(&protocols); ++ return up; ++ } ++ } ++ av_freep(&protocols); ++ return NULL; ++} ++ ++int ffurl_alloc2(URLContext **puc, const char *filename, int flags, ++ const AVIOInterruptCB *int_cb, AVDictionary **options) ++{ ++ if (options && *options) { ++ AVDictionaryEntry *e = av_dict_get(*options, "selected_http", NULL, 0); ++ const char *selected_http; ++ if (e && (selected_http = e->value)) { ++ ++ char proto_str[128] = {0}; ++ size_t proto_len = strspn(filename, URL_SCHEME_CHARS); ++ if (filename[proto_len] != ':' && ++ (strncmp(filename, "subfile,", 8) || !strchr(filename + proto_len + 1, ':')) || ++ is_dos_path(filename)) ++ strcpy(proto_str, "file"); ++ else ++ av_strlcpy(proto_str, filename, ++ FFMIN(proto_len + 1, sizeof(proto_str))); ++ //only apply http protocol ++ if (!strcmp(proto_str, "http") || !strcmp(proto_str, "https")) { ++ if (!strcmp(selected_http, "ijkhttp1") || !strcmp(selected_http, "ijkhttp2") || !strcmp(selected_http, "ijkhttp3")) { ++ const URLProtocol *p = url_find_the_protocol(selected_http); ++ if (p) { ++ av_log(NULL, AV_LOG_DEBUG, "%s use %s send request\n",proto_str,selected_http); ++ return url_alloc_for_protocol(puc, p, filename, flags, int_cb); ++ } ++ *puc = NULL; ++ av_log(NULL, AV_LOG_ERROR, "some thing is fault,check %s protocol\n", selected_http); ++ return AVERROR_PROTOCOL_NOT_FOUND; ++ } else { ++ av_log(NULL, AV_LOG_ERROR, "invalid selected_http value: %s\n", selected_http); ++ av_assert0(0); ++ return AVERROR_PROTOCOL_NOT_FOUND; ++ } ++ } else { ++ av_log(NULL, AV_LOG_DEBUG, "%s not use %s\n",proto_str,selected_http); ++ } ++ } ++ } ++ ++ return ffurl_alloc(puc, filename, flags, int_cb); ++} ++ + int ffurl_open_whitelist(URLContext **puc, const char *filename, int flags, + const AVIOInterruptCB *int_cb, AVDictionary **options, + const char *whitelist, const char* blacklist, +@@ -366,7 +425,7 @@ int ffurl_open_whitelist(URLContext **puc, const char *filename, int flags, + { + AVDictionary *tmp_opts = NULL; + AVDictionaryEntry *e; +- int ret = ffurl_alloc(puc, filename, flags, int_cb); ++ int ret = ffurl_alloc2(puc, filename, flags, int_cb, options); + if (ret < 0) + return ret; + if (parent) { +@@ -735,7 +794,7 @@ int avio_open_dir(AVIODirContext **s, const char *url, AVDictionary **options) + goto fail; + } + +- if ((ret = ffurl_alloc(&h, url, AVIO_FLAG_READ, NULL)) < 0) ++ if ((ret = ffurl_alloc2(&h, url, AVIO_FLAG_READ, NULL, options)) < 0) + goto fail; + + if (h->prot->url_open_dir && h->prot->url_read_dir && h->prot->url_close_dir) { +diff --git a/libavformat/url.h b/libavformat/url.h +index 0784d77..53c6f13 100644 +--- a/libavformat/url.h ++++ b/libavformat/url.h +@@ -112,6 +112,22 @@ typedef struct URLProtocol { + int ffurl_alloc(URLContext **puc, const char *filename, int flags, + const AVIOInterruptCB *int_cb); + ++ /** ++ * Create a URLContext for accessing to the resource indicated by ++ * url, but do not initiate the connection yet. ++ * ++ * @param puc pointer to the location where, in case of success, the ++ * function puts the pointer to the created URLContext ++ * @param flags flags which control how the resource indicated by url ++ * is to be opened ++ * @param int_cb interrupt callback to use for the URLContext, may be ++ * NULL ++ * @param options A dictionary filled with options for replace http protocol ++ * @return >= 0 in case of success, a negative value corresponding to an ++ * AVERROR code in case of failure ++ */ ++int ffurl_alloc2(URLContext **puc, const char *filename, int flags, ++ const AVIOInterruptCB *int_cb, AVDictionary **options); + /** + * Connect an URLContext that has been allocated by ffurl_alloc + * +-- +2.39.5 (Apple Git-154) + diff --git a/patches/ffmpeg-n7.1.1/0011-correct-file-seekable-value-range-0-means-streamed-c.patch b/patches/ffmpeg-n7.1.1/0011-correct-file-seekable-value-range-0-means-streamed-c.patch new file mode 100644 index 000000000..f7a29f093 --- /dev/null +++ b/patches/ffmpeg-n7.1.1/0011-correct-file-seekable-value-range-0-means-streamed-c.patch @@ -0,0 +1,26 @@ +From 4980d45cd1a6c31f4b81b70818b6aa409f23d71e Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Wed, 28 May 2025 18:37:38 +0800 +Subject: [PATCH] correct file seekable value range, 0 means streamed can't + seek, 1 means not streamed can seek. + +--- + libavformat/file.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/libavformat/file.c b/libavformat/file.c +index 6a66040..c1d3ce7 100644 +--- a/libavformat/file.c ++++ b/libavformat/file.c +@@ -105,7 +105,7 @@ static const AVOption file_options[] = { + { "truncate", "truncate existing files on write", offsetof(FileContext, trunc), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM }, + { "blocksize", "set I/O operation maximum block size", offsetof(FileContext, blocksize), AV_OPT_TYPE_INT, { .i64 = INT_MAX }, 1, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, + { "follow", "Follow a file as it is being written", offsetof(FileContext, follow), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, AV_OPT_FLAG_DECODING_PARAM }, +- { "seekable", "Sets if the file is seekable", offsetof(FileContext, seekable), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 0, AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_ENCODING_PARAM }, ++ { "seekable", "Sets if the file is seekable", offsetof(FileContext, seekable), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1, AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_ENCODING_PARAM }, + { NULL } + }; + +-- +2.39.5 (Apple Git-154) + diff --git a/patches/ffmpeg-n7.1.1/0012-fix-lrcdec-read-line-bug-on-osx.patch b/patches/ffmpeg-n7.1.1/0012-fix-lrcdec-read-line-bug-on-osx.patch new file mode 100644 index 000000000..ad4b7733e --- /dev/null +++ b/patches/ffmpeg-n7.1.1/0012-fix-lrcdec-read-line-bug-on-osx.patch @@ -0,0 +1,43 @@ +From 30f20ff3ec0de99847be4da379f497bbacc61501 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Wed, 28 May 2025 18:39:38 +0800 +Subject: [PATCH] fix lrcdec read line bug on osx + +--- + libavformat/lrcdec.c | 20 +++++++++++--------- + 1 file changed, 11 insertions(+), 9 deletions(-) + +diff --git a/libavformat/lrcdec.c b/libavformat/lrcdec.c +index 68c44bc..2bea1e1 100644 +--- a/libavformat/lrcdec.c ++++ b/libavformat/lrcdec.c +@@ -105,15 +105,17 @@ static int64_t read_line(AVBPrint *buf, AVIOContext *pb) + int64_t pos = avio_tell(pb); + + av_bprint_clear(buf); +- while(!avio_feof(pb)) { +- int c = avio_r8(pb); +- if(c != '\r') { +- av_bprint_chars(buf, c, 1); +- } +- if(c == '\n') { +- break; +- } +- } ++ ff_read_line_to_bprint_overwrite(pb, buf); ++ ++ // while(!avio_feof(pb)) { ++ // int c = avio_r8(pb); ++ // if(c != '\r') { ++ // av_bprint_chars(buf, c, 1); ++ // } ++ // if(c == '\n') { ++ // break; ++ // } ++ // } + return pos; + } + +-- +2.39.5 (Apple Git-154) + diff --git a/patches/ffmpeg-n7.1.1/0013-avformat-mpegts-index-only-keyframes-to-ensure-accur.patch b/patches/ffmpeg-n7.1.1/0013-avformat-mpegts-index-only-keyframes-to-ensure-accur.patch new file mode 100644 index 000000000..7eeef4b94 --- /dev/null +++ b/patches/ffmpeg-n7.1.1/0013-avformat-mpegts-index-only-keyframes-to-ensure-accur.patch @@ -0,0 +1,47 @@ +From 72aa2e8ec05cd35a3cb2b4629220b0d37c9538ee Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Wed, 28 May 2025 18:48:39 +0800 +Subject: [PATCH] avformat/mpegts: index only keyframes to ensure accurate + seeks by default + +--- + libavformat/mpegts.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c +index 86b7c8e..58b50cb 100644 +--- a/libavformat/mpegts.c ++++ b/libavformat/mpegts.c +@@ -157,6 +157,7 @@ struct MpegTSContext { + + int skip_changes; + int skip_clear; ++ int seek_flag_keyframe; + int skip_unknown_pmt; + + int scan_all_pmts; +@@ -210,6 +211,8 @@ static const AVOption options[] = { + {.i64 = 0}, 0, 1, 0 }, + {"max_packet_size", "maximum size of emitted packet", offsetof(MpegTSContext, max_packet_size), AV_OPT_TYPE_INT, + {.i64 = 204800}, 1, INT_MAX/2, AV_OPT_FLAG_DECODING_PARAM }, ++ {"seek_flag_keyframe", "seek use keyframe mode", offsetof(MpegTSContext, seek_flag_keyframe), AV_OPT_TYPE_BOOL, ++ {.i64 = 1}, 0, 1, AV_OPT_FLAG_DECODING_PARAM }, + { NULL }, + }; + +@@ -3363,9 +3366,10 @@ static int64_t mpegts_get_dts(AVFormatContext *s, int stream_index, + av_packet_free(&pkt); + return AV_NOPTS_VALUE; + } +- if (pkt->dts != AV_NOPTS_VALUE && pkt->pos >= 0) { ++ ++ if (pkt->dts != AV_NOPTS_VALUE && pkt->pos >= 0 && (!ts->seek_flag_keyframe || (pkt->flags & AV_PKT_FLAG_KEY))) { + ff_reduce_index(s, pkt->stream_index); +- av_add_index_entry(s->streams[pkt->stream_index], pkt->pos, pkt->dts, 0, 0, AVINDEX_KEYFRAME /* FIXME keyframe? */); ++ av_add_index_entry(s->streams[pkt->stream_index], pkt->pos, pkt->dts, 0, 0, AVINDEX_KEYFRAME); + if (pkt->stream_index == stream_index && pkt->pos >= *ppos) { + int64_t dts = pkt->dts; + *ppos = pkt->pos; +-- +2.39.5 (Apple Git-154) + diff --git a/patches/ffmpeg-n7.1.1/0014-support-inherit-hls-opts.patch b/patches/ffmpeg-n7.1.1/0014-support-inherit-hls-opts.patch new file mode 100644 index 000000000..4d2be51a5 --- /dev/null +++ b/patches/ffmpeg-n7.1.1/0014-support-inherit-hls-opts.patch @@ -0,0 +1,113 @@ +From 887954a624271b7139f202c6f1336c74df4c3e9e Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Wed, 28 May 2025 18:49:10 +0800 +Subject: [PATCH] support inherit hls opts + +--- + libavformat/hls.c | 45 +++++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 41 insertions(+), 4 deletions(-) + +diff --git a/libavformat/hls.c b/libavformat/hls.c +index 12b49c8..069db99 100644 +--- a/libavformat/hls.c ++++ b/libavformat/hls.c +@@ -233,6 +233,7 @@ typedef struct HLSContext { + int seg_max_retry; + AVIOContext *playlist_pb; + HLSCryptoContext crypto_ctx; ++ char *seg_inherit_opts; + } HLSContext; + + static void free_segment_dynarray(struct segment **segments, int n_segments) +@@ -1992,7 +1993,32 @@ static int hls_close(AVFormatContext *s) + return 0; + } + +-static int hls_read_header(AVFormatContext *s) ++static int copy_hls_headers_for_http(AVDictionary **dst, const AVDictionary *src, const char *opts) ++{ ++ if (!opts) ++ return 0; ++ ++ char *my_opts = opts; ++ char *saved = NULL; ++ char *opt = NULL; ++ int ret = 0; ++ ++ while ((opt = av_strtok(my_opts, ",", &saved))) { ++ AVDictionaryEntry *t = NULL; ++ while ((t = av_dict_get(src, "", t, AV_DICT_IGNORE_SUFFIX))) { ++ if (t->key && !strcmp(t->key, opt)) { ++ ret = av_dict_set(dst, t->key, t->value, 0); ++ if (ret < 0) ++ return ret; ++ } ++ } ++ my_opts = saved; ++ } ++ ++ return ret; ++} ++ ++static int hls_read_header2(AVFormatContext *s, AVDictionary **a_options) + { + HLSContext *c = s->priv_data; + int ret = 0, i; +@@ -2005,9 +2031,20 @@ static int hls_read_header(AVFormatContext *s) + c->first_timestamp = AV_NOPTS_VALUE; + c->cur_timestamp = AV_NOPTS_VALUE; + ++ //pb only include keys which in hls_options list. + if ((ret = ffio_copy_url_options(s->pb, &c->avio_opts)) < 0) + return ret; + ++ //current a_options is original options,you can filter special keys ++ copy_hls_headers_for_http(&c->avio_opts, *a_options, c->seg_inherit_opts); ++ //use segment format options override inherit options. ++ av_dict_copy(&c->avio_opts, c->seg_format_opts, 0); ++ ++ // AVDictionaryEntry *t = NULL; ++ // while ((t = av_dict_get(c->avio_opts, "", t, AV_DICT_IGNORE_SUFFIX))) { ++ // av_log(NULL, AV_LOG_INFO, "%-*s: %-*s = %s\n", 12, "hls_read_header2", 28, t->key, t->value); ++ // } ++ + /* XXX: Some HLS servers don't like being sent the range header, + in this case, need to setting http_seekable = 0 to disable + the range header */ +@@ -2104,6 +2141,7 @@ static int hls_read_header(AVFormatContext *s) + pls->needed = 1; + pls->parent = s; + ++ av_dict_copy(&options, c->avio_opts, 0); + /* + * If this is a live stream and this playlist looks like it is one segment + * behind, try to sync it up so that every substream starts at the same +@@ -2221,8 +2259,6 @@ static int hls_read_header(AVFormatContext *s) + if ((ret = ff_copy_whiteblacklists(pls->ctx, s)) < 0) + return ret; + +- av_dict_copy(&options, c->seg_format_opts, 0); +- + ret = avformat_open_input(&pls->ctx, pls->segments[0]->url, in_fmt, &options); + av_dict_free(&options); + if (ret < 0) +@@ -2691,6 +2727,7 @@ static const AVOption hls_options[] = { + OFFSET(seg_format_opts), AV_OPT_TYPE_DICT, {.str = NULL}, 0, 0, FLAGS}, + {"seg_max_retry", "Maximum number of times to reload a segment on error.", + OFFSET(seg_max_retry), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, FLAGS}, ++ {"seg_inherit_options", "Special keys inherit form options,apply for segment demuxer", OFFSET(seg_inherit_opts), AV_OPT_TYPE_STRING, {.str = NULL}, INT_MIN, INT_MAX, FLAGS}, + {NULL} + }; + +@@ -2709,7 +2746,7 @@ const FFInputFormat ff_hls_demuxer = { + .priv_data_size = sizeof(HLSContext), + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, + .read_probe = hls_probe, +- .read_header = hls_read_header, ++ .read_header2 = hls_read_header2, + .read_packet = hls_read_packet, + .read_close = hls_close, + .read_seek = hls_read_seek, +-- +2.39.5 (Apple Git-154) + diff --git a/patches/ffmpeg-n7.1.1/0015-fix-can-t-seek-to-00-00-bug-baidu-neddisk-hls-start_.patch b/patches/ffmpeg-n7.1.1/0015-fix-can-t-seek-to-00-00-bug-baidu-neddisk-hls-start_.patch new file mode 100644 index 000000000..7a02382ca --- /dev/null +++ b/patches/ffmpeg-n7.1.1/0015-fix-can-t-seek-to-00-00-bug-baidu-neddisk-hls-start_.patch @@ -0,0 +1,29 @@ +From 6b759eeb996340b07fe1caa1846b48d722da34d6 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Wed, 28 May 2025 18:49:48 +0800 +Subject: [PATCH] fix can't seek to 00:00 bug, baidu neddisk hls start_time is + less than first_timestamp + +--- + libavformat/hls.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/libavformat/hls.c b/libavformat/hls.c +index 069db99..098307b 100644 +--- a/libavformat/hls.c ++++ b/libavformat/hls.c +@@ -1775,7 +1775,10 @@ static int find_timestamp_in_playlist(HLSContext *c, struct playlist *pls, + + if (timestamp < pos) { + *seq_no = pls->start_seq_no; +- return 0; ++ if (seg_start_ts) { ++ *seg_start_ts = pos; ++ } ++ return 1; + } + + for (i = 0; i < pls->n_segments; i++) { +-- +2.39.5 (Apple Git-154) + diff --git a/patches/ffmpeg-n7.1.1/0016-Correct-the-wrong-codecpar-codec_id-which-read-from-.patch b/patches/ffmpeg-n7.1.1/0016-Correct-the-wrong-codecpar-codec_id-which-read-from-.patch new file mode 100644 index 000000000..11901040b --- /dev/null +++ b/patches/ffmpeg-n7.1.1/0016-Correct-the-wrong-codecpar-codec_id-which-read-from-.patch @@ -0,0 +1,60 @@ +From 5cf9674b9c26494264cb4c41f7b813985d9c5cfe Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Wed, 28 May 2025 18:52:01 +0800 +Subject: [PATCH] Correct the wrong codecpar->codec_id which read from MIME of + ID3tags, but the real data was encoded in PNG/JPEG/TIFF + +--- + libavformat/id3v2.c | 7 ++++++- + libavformat/img2dec.c | 2 +- + libavformat/mov.c | 2 +- + 3 files changed, 8 insertions(+), 3 deletions(-) + +diff --git a/libavformat/id3v2.c b/libavformat/id3v2.c +index 3507885..cfbedd8 100644 +--- a/libavformat/id3v2.c ++++ b/libavformat/id3v2.c +@@ -1178,8 +1178,13 @@ int ff_id3v2_parse_apic(AVFormatContext *s, ID3v2ExtraMeta *extra_meta) + st = s->streams[s->nb_streams - 1]; + st->codecpar->codec_id = apic->id; + +- if (AV_RB64(st->attached_pic.data) == PNGSIG) ++ if (AV_RB64(st->attached_pic.data) == PNGSIG || AV_RB64(st->attached_pic.data) == MNGSIG) { + st->codecpar->codec_id = AV_CODEC_ID_PNG; ++ } else if (AV_RB24(st->attached_pic.data) == 0xffd8ff) { ++ st->codecpar->codec_id = AV_CODEC_ID_MJPEG; ++ } else if (AV_RB32(st->attached_pic.data) == 0x49492a00 || AV_RB32(st->attached_pic.data) == 0x4D4D002a) { ++ st->codecpar->codec_id = AV_CODEC_ID_TIFF; ++ } + + if (apic->description[0]) + av_dict_set(&st->metadata, "title", apic->description, 0); +diff --git a/libavformat/img2dec.c b/libavformat/img2dec.c +index 3389fa8..3c24955 100644 +--- a/libavformat/img2dec.c ++++ b/libavformat/img2dec.c +@@ -914,7 +914,7 @@ static int png_probe(const AVProbeData *p) + { + const uint8_t *b = p->buf; + +- if (AV_RB64(b) == 0x89504e470d0a1a0a) ++ if (AV_RB64(b) == 0x89504e470d0a1a0a || AV_RB64(b) == 0x8a4d4e470d0a1a0a) + return AVPROBE_SCORE_MAX - 1; + return 0; + } +diff --git a/libavformat/mov.c b/libavformat/mov.c +index 41ca280..7319202 100644 +--- a/libavformat/mov.c ++++ b/libavformat/mov.c +@@ -259,7 +259,7 @@ static int mov_read_covr(MOVContext *c, AVIOContext *pb, int type, int len) + sc->refcount = 1; + + if (st->attached_pic.size >= 8 && id != AV_CODEC_ID_BMP) { +- if (AV_RB64(st->attached_pic.data) == 0x89504e470d0a1a0a) { ++ if (AV_RB64(st->attached_pic.data) == 0x89504e470d0a1a0a || AV_RB64(st->attached_pic.data) == 0x8a4d4e470d0a1a0a) { + id = AV_CODEC_ID_PNG; + } else { + id = AV_CODEC_ID_MJPEG; +-- +2.39.5 (Apple Git-154) + diff --git a/patches/ffmpeg-n7.1.1/0017-avformat-mov-fix-to-detect-if-stream-position-has-be.patch b/patches/ffmpeg-n7.1.1/0017-avformat-mov-fix-to-detect-if-stream-position-has-be.patch new file mode 100644 index 000000000..3c28e5809 --- /dev/null +++ b/patches/ffmpeg-n7.1.1/0017-avformat-mov-fix-to-detect-if-stream-position-has-be.patch @@ -0,0 +1,74 @@ +From af0bfbe05ce031e3d602b2abb7595ece26a45a3a Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Wed, 28 May 2025 18:52:46 +0800 +Subject: [PATCH] avformat mov fix to detect if stream position has been reset + (https://patchwork.ffmpeg.org/project/ffmpeg/patch/20200424152042.29383-3-hello.vectronic@gmail.com/) + +--- + libavformat/mov.c | 35 ++++++++++++++++++++++++++++++++--- + 1 file changed, 32 insertions(+), 3 deletions(-) + +diff --git a/libavformat/mov.c b/libavformat/mov.c +index 7319202..d9d7b80 100644 +--- a/libavformat/mov.c ++++ b/libavformat/mov.c +@@ -10473,15 +10473,15 @@ static int mov_switch_root(AVFormatContext *s, int64_t target, int index) + + if (index >= 0 && index < mov->frag_index.nb_items) + target = mov->frag_index.item[index].moof_offset; +- if (avio_seek(s->pb, target, SEEK_SET) != target) { ++ if (target >= 0 && avio_seek(s->pb, target, SEEK_SET) != target) { + av_log(mov->fc, AV_LOG_ERROR, "root atom offset 0x%"PRIx64": partial file\n", target); + return AVERROR_INVALIDDATA; + } + + mov->next_root_atom = 0; +- if (index < 0 || index >= mov->frag_index.nb_items) ++ if ((index < 0 && target >= 0) || index >= mov->frag_index.nb_items) + index = search_frag_moof_offset(&mov->frag_index, target); +- if (index < mov->frag_index.nb_items && ++ if (index >= 0 && index < mov->frag_index.nb_items && + mov->frag_index.item[index].moof_offset == target) { + if (index + 1 < mov->frag_index.nb_items) + mov->next_root_atom = mov->frag_index.item[index + 1].moof_offset; +@@ -10661,8 +10661,37 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt) + AVStream *st = NULL; + int64_t current_index; + int ret; ++ int i; + mov->fc = s; + retry: ++ if (s->pb->pos == 0) { ++ // Discard current fragment index ++ if (mov->frag_index.allocated_size > 0) { ++ av_freep(&mov->frag_index.item); ++ mov->frag_index.nb_items = 0; ++ mov->frag_index.allocated_size = 0; ++ mov->frag_index.current = -1; ++ mov->frag_index.complete = 0; ++ } ++ ++ for (i = 0; i < s->nb_streams; i++) { ++ AVStream *avst = s->streams[i]; ++ MOVStreamContext *msc = avst->priv_data; ++ ++ // Clear current sample ++ mov_current_sample_set(msc, 0); ++ ++ // Discard current index entries ++ if (ffstream(avst)->index_entries_allocated_size > 0) { ++ av_freep(&ffstream(avst)->index_entries); ++ ffstream(avst)->index_entries_allocated_size = 0; ++ ffstream(avst)->nb_index_entries = 0; ++ } ++ } ++ ++ if ((ret = mov_switch_root(s, -1, -1)) < 0) ++ return ret; ++ } + sample = mov_find_next_sample(s, &st); + if (!sample || (mov->next_root_atom && sample->pos > mov->next_root_atom)) { + if (!mov->next_root_atom) +-- +2.39.5 (Apple Git-154) + diff --git a/patches/ffmpeg-n7.1.1/0018-fix-http-chunked-transfer-get-wrong-size-cause-av_re.patch b/patches/ffmpeg-n7.1.1/0018-fix-http-chunked-transfer-get-wrong-size-cause-av_re.patch new file mode 100644 index 000000000..049129f04 --- /dev/null +++ b/patches/ffmpeg-n7.1.1/0018-fix-http-chunked-transfer-get-wrong-size-cause-av_re.patch @@ -0,0 +1,32 @@ +From 005c1138ee841a98639b6a8cdcf361c09db18a5a Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Thu, 29 May 2025 08:56:02 +0800 +Subject: [PATCH] fix http chunked transfer get wrong size cause av_read_frame + can not return eof bug + +--- + libavformat/img2dec.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/libavformat/img2dec.c b/libavformat/img2dec.c +index 3c24955..b443602 100644 +--- a/libavformat/img2dec.c ++++ b/libavformat/img2dec.c +@@ -491,7 +491,13 @@ int ff_img_read_packet(AVFormatContext *s1, AVPacket *pkt) + if (s->frame_size > 0) { + size[0] = s->frame_size; + } else if (!ffstream(s1->streams[0])->parser) { +- size[0] = avio_size(s1->pb); ++ //http Transfer-Encoding: chunked the size is -78; ++ int64_t s = avio_size(s1->pb); ++ if (s < 0) { ++ size[0] = 4096; ++ } else { ++ size[0] = s; ++ } + } else { + size[0] = 4096; + } +-- +2.39.5 (Apple Git-154) + diff --git a/patches/ffmpeg-n7.1.1/0019-not-very-useful-log-use-trace-level.patch b/patches/ffmpeg-n7.1.1/0019-not-very-useful-log-use-trace-level.patch new file mode 100644 index 000000000..b758e878f --- /dev/null +++ b/patches/ffmpeg-n7.1.1/0019-not-very-useful-log-use-trace-level.patch @@ -0,0 +1,76 @@ +From 2ebc057a63774ca24a98c3cfbda7fc183da1082f Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Thu, 29 May 2025 09:02:16 +0800 +Subject: [PATCH] not very useful log use trace level + +--- + libavcodec/h2645_parse.c | 6 +++--- + libavcodec/h2645_vui.c | 2 +- + libavformat/demux.c | 4 ++-- + 3 files changed, 6 insertions(+), 6 deletions(-) + +diff --git a/libavcodec/h2645_parse.c b/libavcodec/h2645_parse.c +index 8281699..0aded68 100644 +--- a/libavcodec/h2645_parse.c ++++ b/libavcodec/h2645_parse.c +@@ -355,7 +355,7 @@ static int vvc_parse_nal_header(H2645NAL *nal, void *logctx) + if ((nal->type >= VVC_IDR_W_RADL && nal->type <= VVC_RSV_IRAP_11) && nal->temporal_id) + return AVERROR_INVALIDDATA; + +- av_log(logctx, AV_LOG_DEBUG, ++ av_log(logctx, AV_LOG_TRACE, + "nal_unit_type: %d(%s), nuh_layer_id: %d, temporal_id: %d\n", + nal->type, vvc_nal_unit_name(nal->type), nal->nuh_layer_id, nal->temporal_id); + +@@ -376,7 +376,7 @@ static int hevc_parse_nal_header(H2645NAL *nal, void *logctx) + if (nal->temporal_id < 0) + return AVERROR_INVALIDDATA; + +- av_log(logctx, AV_LOG_DEBUG, ++ av_log(logctx, AV_LOG_TRACE, + "nal_unit_type: %d(%s), nuh_layer_id: %d, temporal_id: %d\n", + nal->type, hevc_nal_unit_name(nal->type), nal->nuh_layer_id, nal->temporal_id); + +@@ -393,7 +393,7 @@ static int h264_parse_nal_header(H2645NAL *nal, void *logctx) + nal->ref_idc = get_bits(gb, 2); + nal->type = get_bits(gb, 5); + +- av_log(logctx, AV_LOG_DEBUG, ++ av_log(logctx, AV_LOG_TRACE, + "nal_unit_type: %d(%s), nal_ref_idc: %d\n", + nal->type, h264_nal_unit_name(nal->type), nal->ref_idc); + +diff --git a/libavcodec/h2645_vui.c b/libavcodec/h2645_vui.c +index e5c7bf4..8301492 100644 +--- a/libavcodec/h2645_vui.c ++++ b/libavcodec/h2645_vui.c +@@ -36,7 +36,7 @@ + + void ff_h2645_decode_common_vui_params(GetBitContext *gb, H2645VUI *vui, void *logctx) + { +- av_log(logctx, AV_LOG_DEBUG, "Decoding VUI\n"); ++ av_log(logctx, AV_LOG_TRACE, "Decoding VUI\n"); + + vui->aspect_ratio_info_present_flag = get_bits1(gb); + if (vui->aspect_ratio_info_present_flag) { +diff --git a/libavformat/demux.c b/libavformat/demux.c +index ff62292..c0d744d 100644 +--- a/libavformat/demux.c ++++ b/libavformat/demux.c +@@ -2024,11 +2024,11 @@ static void estimate_timings(AVFormatContext *ic, int64_t old_offset) + for (unsigned i = 0; i < ic->nb_streams; i++) { + AVStream *const st = ic->streams[i]; + if (st->time_base.den) +- av_log(ic, AV_LOG_TRACE, "stream %u: start_time: %s duration: %s\n", i, ++ av_log(ic, AV_LOG_DEBUG, "stream %u: start_time: %s duration: %s\n", i, + av_ts2timestr(st->start_time, &st->time_base), + av_ts2timestr(st->duration, &st->time_base)); + } +- av_log(ic, AV_LOG_TRACE, ++ av_log(ic, AV_LOG_DEBUG, + "format: start_time: %s duration: %s (estimate from %s) bitrate=%"PRId64" kb/s\n", + av_ts2timestr(ic->start_time, &AV_TIME_BASE_Q), + av_ts2timestr(ic->duration, &AV_TIME_BASE_Q), +-- +2.39.5 (Apple Git-154) + diff --git a/patches/ffmpeg-n7.1.1/0020-Audio-Vivid-Parser-and-Demuxer-but-av3a-Decoder-is-a.patch b/patches/ffmpeg-n7.1.1/0020-Audio-Vivid-Parser-and-Demuxer-but-av3a-Decoder-is-a.patch new file mode 100644 index 000000000..e0be786b1 --- /dev/null +++ b/patches/ffmpeg-n7.1.1/0020-Audio-Vivid-Parser-and-Demuxer-but-av3a-Decoder-is-a.patch @@ -0,0 +1,1351 @@ +From 110f494ac101540119bee647af782b9effe63b41 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Tue, 12 May 2026 10:02:14 +0800 +Subject: [PATCH] Audio Vivid Parser and Demuxer, but av3a Decoder is absent + +--- + libavcodec/Makefile | 1 + + libavcodec/av3a.h | 314 ++++++++++++++++++++++++++ + libavcodec/av3a_parser.c | 218 ++++++++++++++++++ + libavcodec/codec_desc.c | 7 + + libavcodec/codec_id.h | 1 + + libavcodec/parsers.c | 1 + + libavcodec/utils.c | 1 + + libavformat/Makefile | 1 + + libavformat/allformats.c | 1 + + libavformat/av3adec.c | 473 +++++++++++++++++++++++++++++++++++++++ + libavformat/isom_tags.c | 1 + + libavformat/mov.c | 135 +++++++++++ + libavformat/mpegts.c | 6 + + libavformat/mpegts.h | 1 + + 14 files changed, 1161 insertions(+) + create mode 100644 libavcodec/av3a.h + create mode 100644 libavcodec/av3a_parser.c + create mode 100644 libavformat/av3adec.c + +diff --git a/libavcodec/Makefile b/libavcodec/Makefile +index 79903a1..6db860f 100644 +--- a/libavcodec/Makefile ++++ b/libavcodec/Makefile +@@ -1185,6 +1185,7 @@ OBJS-$(CONFIG_AMR_PARSER) += amr_parser.o + OBJS-$(CONFIG_AV1_PARSER) += av1_parser.o av1_parse.o + OBJS-$(CONFIG_AVS2_PARSER) += avs2.o avs2_parser.o + OBJS-$(CONFIG_AVS3_PARSER) += avs3_parser.o ++OBJS-$(CONFIG_AV3A_PARSER) += av3a_parser.o + OBJS-$(CONFIG_BMP_PARSER) += bmp_parser.o + OBJS-$(CONFIG_CAVSVIDEO_PARSER) += cavs_parser.o + OBJS-$(CONFIG_COOK_PARSER) += cook_parser.o +diff --git a/libavcodec/av3a.h b/libavcodec/av3a.h +new file mode 100644 +index 0000000..14dc349 +--- /dev/null ++++ b/libavcodec/av3a.h +@@ -0,0 +1,314 @@ ++/* ++ * AV3A Common Header File ++ * ++ * Copyright (c) 2024 Shuai Liu ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg 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. ++ * ++ * FFmpeg 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 FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#ifndef AVCODEC_AV3A_H ++#define AVCODEC_AV3A_H ++ ++#include "libavutil/samplefmt.h" ++#include "libavutil/channel_layout.h" ++ ++/* AATF header */ ++#define AV3A_MAX_NBYTES_HEADER 9 ++#define AV3A_AUDIO_SYNC_WORD 0xFFF ++#define AV3A_AUDIO_FRAME_SIZE 1024 ++#define AV3A_CHANNEL_LAYOUT_SIZE 15 ++#define AV3A_BITRATE_TABLE_SIZE 16 ++#define AV3A_FS_TABLE_SIZE 9 ++#define AV3A_RESOLUTION_TABLE_SIZE 3 ++#define AV3A_DCA3_BOX_MIN_SIZE 5 ++ ++/* Channel Layout */ ++#define AV3A_CH_LAYOUT_MONO (AV_CH_LAYOUT_MONO) ++#define AV3A_CH_LAYOUT_STEREO (AV_CH_LAYOUT_STEREO) ++#define AV3A_CH_LAYOUT_4POINT0 (AV3A_CH_LAYOUT_STEREO|AV_CH_FRONT_CENTER|AV_CH_BACK_CENTER) ++#define AV3A_CH_LAYOUT_5POINT1 (AV_CH_LAYOUT_5POINT1) ++#define AV3A_CH_LAYOUT_7POINT1 (AV_CH_LAYOUT_5POINT1|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT) ++#define AV3A_CH_LAYOUT_5POINT1POINT2 (AV_CH_LAYOUT_5POINT1|AV_CH_TOP_SIDE_LEFT|AV_CH_TOP_SIDE_RIGHT) ++#define AV3A_CH_LAYOUT_7POINT1POINT2 (AV3A_CH_LAYOUT_7POINT1|AV_CH_TOP_SIDE_LEFT|AV_CH_TOP_SIDE_RIGHT) ++#define AV3A_CH_LAYOUT_5POINT1POINT4 (AV_CH_LAYOUT_5POINT1|AV_CH_TOP_FRONT_LEFT|AV_CH_TOP_FRONT_RIGHT|AV_CH_TOP_BACK_LEFT|AV_CH_TOP_BACK_RIGHT) ++#define AV3A_CH_LAYOUT_7POINT1POINT4 (AV3A_CH_LAYOUT_7POINT1|AV_CH_TOP_FRONT_LEFT|AV_CH_TOP_FRONT_RIGHT|AV_CH_TOP_BACK_LEFT|AV_CH_TOP_BACK_RIGHT) ++#define AV3A_CH_AUDIO_OBJECT (AV_CHAN_UNKNOWN) ++ ++/* AV3A Codec ID */ ++typedef enum { ++ AV3A_LOSSLESS_CODEC_ID = 1, ++ AV3A_LOSSY_CODEC_ID = 2 ++} Av3aCodecId; ++ ++/* Content Type */ ++typedef enum { ++ AV3A_CHANNEL_BASED_TYPE = 0, ++ AV3A_OBJECT_BASED_TYPE = 1, ++ AV3A_CHANNEL_OBJECT_TYPE = 2, ++ AV3A_AMBISONIC_TYPE = 3 ++} Av3aContentType; ++ ++/* Internal Coding Profile */ ++typedef enum { ++ AV3A_BASE_PROFILE = 0, ++ AV3A_OBJECT_METADATA_PROFILE = 1, ++ AV3A_AMBISONIC_PROFILE = 2 ++} Av3aCodingProfile; ++ ++/* NN Type */ ++typedef enum { ++ AV3A_BASELINE_NN_TYPE = 0, ++ AV3A_LC_NN_TYPE = 1 ++} Av3aNeuralNetworkType; ++ ++/* AV3A Channel Configuration */ ++typedef enum { ++ CHANNEL_CONFIG_MONO = 0, /* Mono = 0 */ ++ CHANNEL_CONFIG_STEREO = 1, /* Stereo = 1 */ ++ CHANNEL_CONFIG_MC_5_1 = 2, /* 5.1 = 2 */ ++ CHANNEL_CONFIG_MC_7_1 = 3, /* 7.1 = 3 */ ++ CHANNEL_CONFIG_MC_10_2 = 4, /* 10.2 = 4 */ ++ CHANNEL_CONFIG_MC_22_2 = 5, /* 22.2 = 5 */ ++ CHANNEL_CONFIG_MC_4_0 = 6, /* 4.0 = 6 */ ++ CHANNEL_CONFIG_MC_5_1_2 = 7, /* 5.1.2 = 7 */ ++ CHANNEL_CONFIG_MC_5_1_4 = 8, /* 5.1.4 = 8 */ ++ CHANNEL_CONFIG_MC_7_1_2 = 9, /* 7.1.2 = 9 */ ++ CHANNEL_CONFIG_MC_7_1_4 = 10, /* 7.1.4 = 10 */ ++ CHANNEL_CONFIG_HOA_ORDER1 = 11, /* HOA1 = 11 */ ++ CHANNEL_CONFIG_HOA_ORDER2 = 12, /* HOA2 = 12 */ ++ CHANNEL_CONFIG_HOA_ORDER3 = 13, /* HOA3 = 13 */ ++ CHANNEL_CONFIG_UNKNOWN = 14 /* UNKNOWN = 14 */ ++} Av3aChannelConfig; ++ ++typedef enum { ++ AV3A_AMBISONIC_FIRST_ORDER = 1, ++ AV3A_AMBISONIC_SECOND_ORDER = 2, ++ AV3A_AMBISONIC_THIRD_ORDER = 3 ++} Av3aAmbisonicOrder; ++ ++typedef struct { ++ int16_t sync_word; /* sync word */ ++ int16_t audio_codec_id; /* audio codec id */ ++ int16_t anc_data; /* anc data */ ++ int16_t nn_type; /* neural network type */ ++ int16_t coding_profile; /* coding profile */ ++ int16_t sampling_frequency_index; /* sampling frequency index */ ++ int16_t channel_number_index; /* channel number index */ ++ int16_t bitrate_index; /* bitrate index */ ++ int16_t soundbed_type; /* soundbed type */ ++ int16_t object_channel_number; /* object channel number */ ++ int16_t bitrate_index_per_channel; /* bitrate per object */ ++ int16_t order; /* ambisonics order */ ++ int16_t resolution_index; /* resolution index */ ++ ++ int32_t sampling_rate; /* sampling rate */ ++ int64_t total_bitrate; /* total bitrate */ ++ int16_t sample_format; /* sample format */ ++ int16_t resolution; /* resolution */ ++ int16_t content_type; /* internal content type */ ++ int16_t nb_channels; /* number of channels (channel configuration) */ ++ int16_t nb_objects; /* number of objects (object_channel_number + 1) */ ++ int16_t total_channels; /* total channels */ ++ int16_t hoa_order; /* ambisonic order (order + 1) */ ++ int32_t ch_layout_mask; /* channel layout mask */ ++} AATFHeaderInfo; ++ ++/* bitrate table for mono */ ++static const int64_t ff_av3a_mono_bitrate_table[AV3A_BITRATE_TABLE_SIZE] = { ++ 16000, 32000, 44000, 56000, 64000, 72000, 80000, 96000, 128000, 144000, ++ 164000, 192000, 0, 0, 0, 0 ++}; ++ ++/* bitrate table for stereo */ ++static const int64_t ff_av3a_stereo_bitrate_table[AV3A_BITRATE_TABLE_SIZE] = { ++ 24000, 32000, 48000, 64000, 80000, 96000, 128000, 144000, 192000, 256000, ++ 320000, 0, 0, 0, 0, 0 ++}; ++ ++/* bitrate table for MC 5.1 */ ++static const int64_t ff_av3a_mc5p1_bitrate_table[AV3A_BITRATE_TABLE_SIZE] = { ++ 192000, 256000, 320000, 384000, 448000, 512000, 640000, 720000, 144000, 96000, ++ 128000, 160000, 0, 0, 0, 0 ++}; ++ ++/* bitrate table for MC 7.1 */ ++static const int64_t ff_av3a_mc7p1_bitrate_table[AV3A_BITRATE_TABLE_SIZE] = { ++ 192000, 480000, 256000, 384000, 576000, 640000, 128000, 160000, 0, 0, ++ 0, 0, 0, 0, 0, 0 ++}; ++ ++/* bitrate table for MC 4.0 */ ++static const int64_t ff_av3a_mc4p0_bitrate_table[AV3A_BITRATE_TABLE_SIZE] = { ++ 48000, 96000, 128000, 192000, 256000, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0 ++}; ++ ++/* bitrate table for MC 5.1.2 */ ++static const int64_t ff_av3a_mc5p1p2_bitrate_table[AV3A_BITRATE_TABLE_SIZE] = { ++ 152000, 320000, 480000, 576000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ++}; ++ ++/* bitrate table for MC 5.1.4 */ ++static const int64_t ff_av3a_mc5p1p4_bitrate_table[AV3A_BITRATE_TABLE_SIZE] = { ++ 176000, 384000, 576000, 704000, 256000, 448000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ++}; ++ ++/* bitrate table for MC 7.1.2 */ ++static const int64_t ff_av3a_mc7p1p2_bitrate_table[AV3A_BITRATE_TABLE_SIZE] = { ++ 216000, 480000, 576000, 384000, 768000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ++}; ++ ++/* bitrate table for MC 7.1.4 */ ++static const int64_t ff_av3a_mc7p1p4_bitrate_table[AV3A_BITRATE_TABLE_SIZE] = { ++ 240000, 608000, 384000, 512000, 832000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ++}; ++ ++/* bitrate table for FOA */ ++static const int64_t ff_av3a_foa_bitrate_table[AV3A_BITRATE_TABLE_SIZE] = { ++ 48000, 96000, 128000, 192000, 256000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ++}; ++ ++/* bitrate table for HOA2 */ ++static const int64_t ff_av3a_hoa2_bitrate_table[AV3A_BITRATE_TABLE_SIZE] = { ++ 192000, 256000, 320000, 384000, 480000, 512000, 640000, 0, 0, 0, 0, 0, 0, 0, 0, 0 ++}; ++ ++/* bitrate table for HOA3 */ ++static const int64_t ff_av3a_hoa3_bitrate_table[AV3A_BITRATE_TABLE_SIZE] = { ++ 256000, 320000, 384000, 512000, 640000, 896000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; ++ ++static const int32_t ff_av3a_sampling_rate_table[AV3A_FS_TABLE_SIZE] = { ++ 192000, 96000, 48000, 44100, 32000, 24000, 22050, 16000, 8000 ++}; ++ ++typedef struct { ++ int16_t resolution; ++ enum AVSampleFormat sample_format; ++} Av3aSampleFormatMap; ++ ++static const Av3aSampleFormatMap ff_av3a_sample_format_map_table[AV3A_RESOLUTION_TABLE_SIZE] = { ++ {8, AV_SAMPLE_FMT_U8 }, /* 0: 8 bits */ ++ {16, AV_SAMPLE_FMT_S16}, /* 1: 16 bits */ ++ {24, AV_SAMPLE_FMT_S32}, /* 2: 24 bits */ ++}; ++ ++typedef struct { ++ Av3aChannelConfig channel_number_index; ++ int16_t channels; ++ const enum AVChannel* channel_layout; ++ uint64_t mask; ++} Av3aChannelConfigMap; ++ ++static const enum AVChannel ff_av3a_default_channel_layout_mono[1] = { ++ AV_CHAN_FRONT_CENTER ++}; ++ ++static const enum AVChannel ff_av3a_default_channel_layout_stereo[2] = { ++ AV_CHAN_FRONT_LEFT, AV_CHAN_FRONT_RIGHT ++}; ++ ++static const enum AVChannel ff_av3a_channel_layout_mc_4_0[4] = { ++ AV_CHAN_FRONT_LEFT, AV_CHAN_FRONT_RIGHT, ++ AV_CHAN_FRONT_CENTER, AV_CHAN_BACK_CENTER ++}; ++ ++static const enum AVChannel ff_av3a_default_channel_layout_mc_5_1[6] = { ++ AV_CHAN_FRONT_LEFT, AV_CHAN_FRONT_RIGHT, AV_CHAN_FRONT_CENTER, ++ AV_CHAN_LOW_FREQUENCY, ++ AV_CHAN_SIDE_LEFT, AV_CHAN_SIDE_RIGHT ++}; ++ ++static const enum AVChannel ff_av3a_default_channel_layout_mc_5_1_2[8] = { ++ AV_CHAN_FRONT_LEFT, AV_CHAN_FRONT_RIGHT, AV_CHAN_FRONT_CENTER, ++ AV_CHAN_LOW_FREQUENCY, ++ AV_CHAN_SIDE_LEFT, AV_CHAN_SIDE_RIGHT, ++ AV_CHAN_TOP_SIDE_LEFT, AV_CHAN_TOP_SIDE_RIGHT ++}; ++ ++static const enum AVChannel ff_av3a_default_channel_layout_mc_7_1[8] = { ++ AV_CHAN_FRONT_LEFT, AV_CHAN_FRONT_RIGHT, AV_CHAN_FRONT_CENTER, ++ AV_CHAN_LOW_FREQUENCY, ++ AV_CHAN_SIDE_LEFT, AV_CHAN_SIDE_RIGHT, ++ AV_CHAN_BACK_LEFT, AV_CHAN_BACK_RIGHT ++}; ++ ++static const enum AVChannel ff_av3a_default_channel_layout_mc_5_1_4[10] = { ++ AV_CHAN_FRONT_LEFT, AV_CHAN_FRONT_RIGHT, AV_CHAN_FRONT_CENTER, ++ AV_CHAN_LOW_FREQUENCY, ++ AV_CHAN_SIDE_LEFT, AV_CHAN_SIDE_RIGHT, ++ AV_CHAN_TOP_FRONT_LEFT, AV_CHAN_TOP_FRONT_RIGHT, ++ AV_CHAN_TOP_BACK_LEFT, AV_CHAN_TOP_BACK_RIGHT ++}; ++ ++static const enum AVChannel ff_av3a_default_channel_layout_mc_7_1_2[10] = { ++ AV_CHAN_FRONT_LEFT, AV_CHAN_FRONT_RIGHT, AV_CHAN_FRONT_CENTER, ++ AV_CHAN_LOW_FREQUENCY, ++ AV_CHAN_SIDE_LEFT, AV_CHAN_SIDE_RIGHT, ++ AV_CHAN_BACK_LEFT, AV_CHAN_BACK_RIGHT, ++ AV_CHAN_TOP_SIDE_LEFT, AV_CHAN_TOP_SIDE_RIGHT ++}; ++ ++static const enum AVChannel ff_av3a_default_channel_layout_mc_7_1_4[12] = { ++ AV_CHAN_FRONT_LEFT, AV_CHAN_FRONT_RIGHT, AV_CHAN_FRONT_CENTER, ++ AV_CHAN_LOW_FREQUENCY, ++ AV_CHAN_SIDE_LEFT, AV_CHAN_SIDE_RIGHT, ++ AV_CHAN_BACK_LEFT, AV_CHAN_BACK_RIGHT, ++ AV_CHAN_TOP_FRONT_LEFT, AV_CHAN_TOP_FRONT_RIGHT, ++ AV_CHAN_TOP_BACK_LEFT, AV_CHAN_TOP_BACK_RIGHT ++}; ++ ++static const Av3aChannelConfigMap ff_av3a_channels_map_table[AV3A_CHANNEL_LAYOUT_SIZE] = { ++ { CHANNEL_CONFIG_MONO, 1, ff_av3a_default_channel_layout_mono, AV3A_CH_LAYOUT_MONO }, ++ { CHANNEL_CONFIG_STEREO, 2, ff_av3a_default_channel_layout_stereo, AV3A_CH_LAYOUT_STEREO }, ++ { CHANNEL_CONFIG_MC_5_1, 6, ff_av3a_default_channel_layout_mc_5_1, AV3A_CH_LAYOUT_5POINT1 }, ++ { CHANNEL_CONFIG_MC_7_1, 8, ff_av3a_default_channel_layout_mc_7_1, AV3A_CH_LAYOUT_7POINT1 }, ++ { CHANNEL_CONFIG_MC_10_2, 12, NULL, 0L }, /* reserved */ ++ { CHANNEL_CONFIG_MC_22_2, 24, NULL, 0L }, /* reserved */ ++ { CHANNEL_CONFIG_MC_4_0, 4, ff_av3a_channel_layout_mc_4_0, AV3A_CH_LAYOUT_4POINT0 }, ++ { CHANNEL_CONFIG_MC_5_1_2, 8, ff_av3a_default_channel_layout_mc_5_1_2, AV3A_CH_LAYOUT_5POINT1POINT2 }, ++ { CHANNEL_CONFIG_MC_5_1_4, 10, ff_av3a_default_channel_layout_mc_5_1_4, AV3A_CH_LAYOUT_5POINT1POINT4 }, ++ { CHANNEL_CONFIG_MC_7_1_2, 10, ff_av3a_default_channel_layout_mc_7_1_2, AV3A_CH_LAYOUT_7POINT1POINT2 }, ++ { CHANNEL_CONFIG_MC_7_1_4, 12, ff_av3a_default_channel_layout_mc_7_1_4, AV3A_CH_LAYOUT_7POINT1POINT4 }, ++ { CHANNEL_CONFIG_HOA_ORDER1, 4, NULL, 0L }, ++ { CHANNEL_CONFIG_HOA_ORDER2, 9, NULL, 0L }, ++ { CHANNEL_CONFIG_HOA_ORDER3, 16, NULL, 0L }, ++ { CHANNEL_CONFIG_UNKNOWN, 0, NULL, 0L }, ++}; ++ ++typedef struct { ++ Av3aChannelConfig channel_number_index; ++ const int64_t *bitrate_table; ++} Av3aBitrateMap; ++ ++static const Av3aBitrateMap ff_av3a_bitrate_map_table[15] = { ++ {CHANNEL_CONFIG_MONO, ff_av3a_mono_bitrate_table }, ++ {CHANNEL_CONFIG_STEREO, ff_av3a_stereo_bitrate_table }, ++ {CHANNEL_CONFIG_MC_5_1, ff_av3a_mc5p1_bitrate_table }, ++ {CHANNEL_CONFIG_MC_7_1, ff_av3a_mc7p1_bitrate_table }, ++ {CHANNEL_CONFIG_MC_10_2, NULL }, /* reserved */ ++ {CHANNEL_CONFIG_MC_22_2, NULL }, /* reserved */ ++ {CHANNEL_CONFIG_MC_4_0, ff_av3a_mc4p0_bitrate_table }, ++ {CHANNEL_CONFIG_MC_5_1_2, ff_av3a_mc5p1p2_bitrate_table }, ++ {CHANNEL_CONFIG_MC_5_1_4, ff_av3a_mc5p1p4_bitrate_table }, ++ {CHANNEL_CONFIG_MC_7_1_2, ff_av3a_mc7p1p2_bitrate_table }, ++ {CHANNEL_CONFIG_MC_7_1_4, ff_av3a_mc7p1p4_bitrate_table }, ++ {CHANNEL_CONFIG_HOA_ORDER1, ff_av3a_foa_bitrate_table }, ++ {CHANNEL_CONFIG_HOA_ORDER2, ff_av3a_hoa2_bitrate_table }, ++ {CHANNEL_CONFIG_HOA_ORDER3, ff_av3a_hoa3_bitrate_table }, ++ {CHANNEL_CONFIG_UNKNOWN, NULL }, ++}; ++#endif /* AVCODEC_AV3A_H */ +diff --git a/libavcodec/av3a_parser.c b/libavcodec/av3a_parser.c +new file mode 100644 +index 0000000..a563b55 +--- /dev/null ++++ b/libavcodec/av3a_parser.c +@@ -0,0 +1,218 @@ ++/* ++ * AV3A Parser ++ * ++ * Copyright (c) 2024 Shuai Liu ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg 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. ++ * ++ * FFmpeg 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 FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include ++#include ++#include "libavutil/intreadwrite.h" ++#include "parser.h" ++#include "get_bits.h" ++#include "av3a.h" ++ ++typedef struct { ++ int16_t audio_codec_id; ++ int16_t nn_type; ++ int16_t frame_size; ++ int16_t resolution; ++ int32_t sample_rate; ++ int64_t bit_rate; ++ ++ int16_t content_type; ++ int16_t channel_number_index; ++ int16_t nb_channels; ++ int16_t nb_objects; ++ int16_t total_channels; ++} Av3aParseContext; ++ ++static int ff_read_av3a_header_parse(GetBitContext *gb, AATFHeaderInfo *hdf) ++{ ++ int64_t soundbed_bitrate = 0L; ++ int64_t object_bitrate = 0L; ++ ++ hdf->nb_channels = 0; ++ hdf->nb_objects = 0; ++ ++ hdf->sync_word = get_bits(gb, 12); ++ if (hdf->sync_word != AV3A_AUDIO_SYNC_WORD) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ hdf->audio_codec_id = get_bits(gb, 4); ++ if (hdf->audio_codec_id != AV3A_LOSSY_CODEC_ID) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ skip_bits(gb, 1); /* skip anc_data 1 bit */ ++ ++ hdf->nn_type = get_bits(gb, 3); ++ if ((hdf->nn_type > AV3A_LC_NN_TYPE) || (hdf->nn_type < AV3A_BASELINE_NN_TYPE)) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ hdf->coding_profile = get_bits(gb, 3); ++ ++ hdf->sampling_frequency_index = get_bits(gb, 4); ++ if ((hdf->sampling_frequency_index >= AV3A_FS_TABLE_SIZE) || (hdf->sampling_frequency_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->sampling_rate = ff_av3a_sampling_rate_table[hdf->sampling_frequency_index]; ++ ++ skip_bits(gb, 8); /* skip CRC 8 bits */ ++ ++ if (hdf->coding_profile == AV3A_BASE_PROFILE) { ++ hdf->content_type = AV3A_CHANNEL_BASED_TYPE; ++ hdf->channel_number_index = get_bits(gb, 7); ++ if ((hdf->channel_number_index >= CHANNEL_CONFIG_UNKNOWN) || ++ (hdf->channel_number_index == CHANNEL_CONFIG_MC_10_2) || ++ (hdf->channel_number_index == CHANNEL_CONFIG_MC_22_2) || ++ (hdf->channel_number_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->nb_channels = ff_av3a_channels_map_table[hdf->channel_number_index].channels; ++ } else if (hdf->coding_profile == AV3A_OBJECT_METADATA_PROFILE) { ++ hdf->soundbed_type = get_bits(gb, 2); ++ if (hdf->soundbed_type == 0) { ++ hdf->content_type = AV3A_OBJECT_BASED_TYPE; ++ hdf->object_channel_number = get_bits(gb, 7); ++ if (hdf->object_channel_number < 0) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->nb_objects = hdf->object_channel_number + 1; ++ ++ hdf->bitrate_index_per_channel = get_bits(gb, 4); ++ if ((hdf->bitrate_index_per_channel >= AV3A_BITRATE_TABLE_SIZE) || (hdf->bitrate_index_per_channel < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ object_bitrate = ff_av3a_bitrate_map_table[CHANNEL_CONFIG_MONO].bitrate_table[hdf->bitrate_index_per_channel]; ++ hdf->total_bitrate = object_bitrate * hdf->nb_objects; ++ } else if (hdf->soundbed_type == 1) { ++ hdf->content_type = AV3A_CHANNEL_OBJECT_TYPE; ++ hdf->channel_number_index = get_bits(gb, 7); ++ if ((hdf->channel_number_index >= CHANNEL_CONFIG_UNKNOWN) || ++ (hdf->channel_number_index == CHANNEL_CONFIG_MC_10_2) || ++ (hdf->channel_number_index == CHANNEL_CONFIG_MC_22_2) || ++ (hdf->channel_number_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ hdf->bitrate_index = get_bits(gb, 4); ++ if ((hdf->bitrate_index >= AV3A_BITRATE_TABLE_SIZE) || (hdf->bitrate_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->nb_channels = ff_av3a_channels_map_table[hdf->channel_number_index].channels; ++ soundbed_bitrate = ff_av3a_bitrate_map_table[hdf->channel_number_index].bitrate_table[hdf->bitrate_index]; ++ ++ hdf->object_channel_number = get_bits(gb, 7); ++ if (hdf->object_channel_number < 0) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->bitrate_index_per_channel = get_bits(gb, 4); ++ if ((hdf->bitrate_index_per_channel >= AV3A_BITRATE_TABLE_SIZE) || (hdf->bitrate_index_per_channel < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->nb_objects = hdf->object_channel_number + 1; ++ object_bitrate = ff_av3a_bitrate_map_table[CHANNEL_CONFIG_MONO].bitrate_table[hdf->bitrate_index_per_channel]; ++ hdf->total_bitrate = soundbed_bitrate + (object_bitrate * hdf->nb_objects); ++ } else { ++ return AVERROR_INVALIDDATA; ++ } ++ } else if (hdf->coding_profile == AV3A_AMBISONIC_PROFILE) { ++ hdf->content_type = AV3A_AMBISONIC_TYPE; ++ hdf->order = get_bits(gb, 4); ++ hdf->hoa_order = hdf->order + 1; ++ ++ switch (hdf->hoa_order) { ++ case AV3A_AMBISONIC_FIRST_ORDER: ++ hdf->channel_number_index = CHANNEL_CONFIG_HOA_ORDER1; ++ break; ++ case AV3A_AMBISONIC_SECOND_ORDER: ++ hdf->channel_number_index = CHANNEL_CONFIG_HOA_ORDER2; ++ break; ++ case AV3A_AMBISONIC_THIRD_ORDER: ++ hdf->channel_number_index = CHANNEL_CONFIG_HOA_ORDER3; ++ break; ++ default: ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->nb_channels = ff_av3a_channels_map_table[hdf->channel_number_index].channels; ++ } else { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->total_channels = hdf->nb_channels + hdf->nb_objects; ++ ++ hdf->resolution_index = get_bits(gb, 2); ++ if ((hdf->resolution_index >= AV3A_RESOLUTION_TABLE_SIZE) || (hdf->resolution_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->resolution = ff_av3a_sample_format_map_table[hdf->resolution_index].resolution; ++ hdf->sample_format = ff_av3a_sample_format_map_table[hdf->resolution_index].sample_format; ++ ++ if (hdf->coding_profile != AV3A_OBJECT_METADATA_PROFILE) { ++ hdf->bitrate_index = get_bits(gb, 4); ++ if ((hdf->bitrate_index >= AV3A_BITRATE_TABLE_SIZE) || (hdf->bitrate_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->total_bitrate = ff_av3a_bitrate_map_table[hdf->channel_number_index].bitrate_table[hdf->bitrate_index]; ++ } ++ ++ skip_bits(gb, 8); /* skip CRC 8 bits */ ++ ++ return 0; ++} ++ ++static int raw_av3a_parse(AVCodecParserContext *s, AVCodecContext *avctx, const uint8_t **poutbuf, ++ int32_t *poutbuf_size, const uint8_t *buf, int32_t buf_size) ++{ ++ int ret = 0; ++ uint8_t header[AV3A_MAX_NBYTES_HEADER]; ++ AATFHeaderInfo hdf; ++ GetBitContext gb; ++ ++ if (buf_size < AV3A_MAX_NBYTES_HEADER) { ++ return buf_size; ++ } ++ memcpy(header, buf, AV3A_MAX_NBYTES_HEADER); ++ ++ init_get_bits8(&gb, buf, AV3A_MAX_NBYTES_HEADER); ++ if ((ret = ff_read_av3a_header_parse(&gb, &hdf)) != 0) { ++ return ret; ++ } ++ ++ avctx->codec_id = AV_CODEC_ID_AVS3DA; ++ avctx->frame_size = AV3A_AUDIO_FRAME_SIZE; ++ avctx->bits_per_raw_sample = hdf.resolution; ++ avctx->sample_rate = hdf.sampling_rate; ++ avctx->bit_rate = hdf.total_bitrate; ++ ++ avctx->ch_layout.order = AV_CHANNEL_ORDER_UNSPEC; ++ avctx->ch_layout.nb_channels = hdf.total_channels; ++ ++ *poutbuf = buf; ++ *poutbuf_size = buf_size; ++ ++ return buf_size; ++} ++ ++const AVCodecParser ff_av3a_parser = { ++ .codec_ids = { AV_CODEC_ID_AVS3DA }, ++ .priv_data_size = sizeof(Av3aParseContext), ++ .parser_parse = raw_av3a_parse, ++}; +\ No newline at end of file +diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c +index d58c3d8..a91c0b5 100644 +--- a/libavcodec/codec_desc.c ++++ b/libavcodec/codec_desc.c +@@ -3716,6 +3716,13 @@ static const AVCodecDescriptor codec_descriptors[] = { + .long_name = NULL_IF_CONFIG_SMALL("AVFrame to AVPacket passthrough"), + .props = AV_CODEC_PROP_LOSSLESS, + }, ++ { ++ .id = AV_CODEC_ID_AVS3DA, ++ .type = AVMEDIA_TYPE_AUDIO, ++ .name = "av3a", ++ .long_name = NULL_IF_CONFIG_SMALL("Audio Vivid"), ++ .props = AV_CODEC_PROP_LOSSY, ++ }, + { + .id = AV_CODEC_ID_VNULL, + .type = AVMEDIA_TYPE_VIDEO, +diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h +index 0a8d3be..a95f400 100644 +--- a/libavcodec/codec_id.h ++++ b/libavcodec/codec_id.h +@@ -600,6 +600,7 @@ enum AVCodecID { + * stream (only used by libavformat) */ + AV_CODEC_ID_FFMETADATA = 0x21000, ///< Dummy codec for streams containing only metadata information. + AV_CODEC_ID_WRAPPED_AVFRAME = 0x21001, ///< Passthrough codec, AVFrames wrapped in AVPacket ++ AV_CODEC_ID_AVS3DA, + /** + * Dummy null video codec, useful mainly for development and debugging. + * Null encoder/decoder discard all input and never return any output. +diff --git a/libavcodec/parsers.c b/libavcodec/parsers.c +index 5128009..ad49461 100644 +--- a/libavcodec/parsers.c ++++ b/libavcodec/parsers.c +@@ -28,6 +28,7 @@ extern const AVCodecParser ff_amr_parser; + extern const AVCodecParser ff_av1_parser; + extern const AVCodecParser ff_avs2_parser; + extern const AVCodecParser ff_avs3_parser; ++extern const AVCodecParser ff_av3a_parser; + extern const AVCodecParser ff_bmp_parser; + extern const AVCodecParser ff_cavsvideo_parser; + extern const AVCodecParser ff_cook_parser; +diff --git a/libavcodec/utils.c b/libavcodec/utils.c +index 6909a5b..51074ba 100644 +--- a/libavcodec/utils.c ++++ b/libavcodec/utils.c +@@ -602,6 +602,7 @@ static int get_audio_frame_duration(enum AVCodecID id, int sr, int ch, int ba, + case AV_CODEC_ID_MP2: + case AV_CODEC_ID_MUSEPACK7: return 1152; + case AV_CODEC_ID_AC3: return 1536; ++ case AV_CODEC_ID_AVS3DA: return 1024; + case AV_CODEC_ID_FTR: return 1024; + } + +diff --git a/libavformat/Makefile b/libavformat/Makefile +index 5e88060..ada27f6 100644 +--- a/libavformat/Makefile ++++ b/libavformat/Makefile +@@ -159,6 +159,7 @@ OBJS-$(CONFIG_AVS2_DEMUXER) += avs2dec.o rawdec.o + OBJS-$(CONFIG_AVS2_MUXER) += rawenc.o + OBJS-$(CONFIG_AVS3_DEMUXER) += avs3dec.o rawdec.o + OBJS-$(CONFIG_AVS3_MUXER) += rawenc.o ++OBJS-$(CONFIG_AV3A_DEMUXER) += av3adec.o + OBJS-$(CONFIG_BETHSOFTVID_DEMUXER) += bethsoftvid.o + OBJS-$(CONFIG_BFI_DEMUXER) += bfi.o + OBJS-$(CONFIG_BINK_DEMUXER) += bink.o +diff --git a/libavformat/allformats.c b/libavformat/allformats.c +index 311b435..9d7a98a 100644 +--- a/libavformat/allformats.c ++++ b/libavformat/allformats.c +@@ -99,6 +99,7 @@ extern const FFInputFormat ff_avs2_demuxer; + extern const FFOutputFormat ff_avs2_muxer; + extern const FFInputFormat ff_avs3_demuxer; + extern const FFOutputFormat ff_avs3_muxer; ++extern const FFInputFormat ff_av3a_demuxer; + extern const FFInputFormat ff_bethsoftvid_demuxer; + extern const FFInputFormat ff_bfi_demuxer; + extern const FFInputFormat ff_bintext_demuxer; +diff --git a/libavformat/av3adec.c b/libavformat/av3adec.c +new file mode 100644 +index 0000000..9bb8729 +--- /dev/null ++++ b/libavformat/av3adec.c +@@ -0,0 +1,473 @@ ++/* ++ * AV3A Demuxer ++ * ++ * Copyright (c) 2024 Shuai Liu ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg 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. ++ * ++ * FFmpeg 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 FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++#include "avformat.h" ++#include "avio_internal.h" ++#include "internal.h" ++#include "rawdec.h" ++#include "libavutil/opt.h" ++#include "libavutil/avassert.h" ++#include "libavutil/intreadwrite.h" ++#include "libavutil/channel_layout.h" ++#include "libavcodec/get_bits.h" ++#include "libavcodec/av3a.h" ++#include ++ ++typedef struct { ++ uint8_t audio_codec_id; ++ uint8_t sampling_frequency_index; ++ uint8_t nn_type; ++ uint8_t content_type; ++ uint8_t channel_number_index; ++ uint8_t number_objects; ++ uint8_t hoa_order; ++ uint8_t resolution_index; ++ uint16_t total_bitrate_kbps; ++} Av3aFormatContext; ++ ++static int av3a_read_aatf_frame_header(AATFHeaderInfo *hdf, const uint8_t *buf) ++{ ++ int16_t sync_word; ++ GetBitContext gb; ++ ++ hdf->nb_channels = 0; ++ hdf->nb_objects = 0; ++ ++ init_get_bits8(&gb, buf, AV3A_MAX_NBYTES_HEADER); ++ ++ sync_word = get_bits(&gb, 12); ++ if (sync_word != AV3A_AUDIO_SYNC_WORD) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ /* codec id */ ++ hdf->audio_codec_id = get_bits(&gb, 4); ++ if (hdf->audio_codec_id != AV3A_LOSSY_CODEC_ID) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ /* anc data */ ++ hdf->anc_data = get_bits(&gb, 1); ++ if (hdf->anc_data) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ /* neural network type */ ++ hdf->nn_type = get_bits(&gb, 3); ++ if ((hdf->nn_type > AV3A_LC_NN_TYPE) || (hdf->nn_type < AV3A_BASELINE_NN_TYPE)) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ /* coding profile */ ++ hdf->coding_profile = get_bits(&gb, 3); ++ ++ /* sampling rate */ ++ hdf->sampling_frequency_index = get_bits(&gb, 4); ++ if ((hdf->sampling_frequency_index >= AV3A_FS_TABLE_SIZE) || (hdf->sampling_frequency_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->sampling_rate = ff_av3a_sampling_rate_table[hdf->sampling_frequency_index]; ++ ++ skip_bits(&gb, 8); ++ ++ if (hdf->coding_profile == AV3A_BASE_PROFILE) { ++ hdf->content_type = AV3A_CHANNEL_BASED_TYPE; ++ hdf->channel_number_index = get_bits(&gb, 7); ++ if ((hdf->channel_number_index >= CHANNEL_CONFIG_UNKNOWN) || ++ (hdf->channel_number_index == CHANNEL_CONFIG_MC_10_2) || ++ (hdf->channel_number_index == CHANNEL_CONFIG_MC_22_2) || ++ (hdf->channel_number_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->nb_channels = ff_av3a_channels_map_table[hdf->channel_number_index].channels; ++ } else if (hdf->coding_profile == AV3A_OBJECT_METADATA_PROFILE) { ++ hdf->soundbed_type = get_bits(&gb, 2); ++ if (hdf->soundbed_type == 0) { ++ hdf->content_type = AV3A_OBJECT_BASED_TYPE; ++ hdf->object_channel_number = get_bits(&gb, 7); ++ if (hdf->object_channel_number < 0) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->bitrate_index_per_channel = get_bits(&gb, 4); ++ if ((hdf->bitrate_index_per_channel >= AV3A_BITRATE_TABLE_SIZE) || (hdf->bitrate_index_per_channel < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->nb_objects = hdf->object_channel_number + 1; ++ hdf->total_bitrate = ff_av3a_bitrate_map_table[CHANNEL_CONFIG_MONO].bitrate_table[hdf->bitrate_index_per_channel] * hdf->nb_objects; ++ } else if (hdf->soundbed_type == 1) { ++ hdf->content_type = AV3A_CHANNEL_OBJECT_TYPE; ++ hdf->channel_number_index = get_bits(&gb, 7); ++ if ((hdf->channel_number_index >= CHANNEL_CONFIG_UNKNOWN) || ++ (hdf->channel_number_index == CHANNEL_CONFIG_MC_10_2) || ++ (hdf->channel_number_index == CHANNEL_CONFIG_MC_22_2) || ++ (hdf->channel_number_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->nb_channels = ff_av3a_channels_map_table[hdf->channel_number_index].channels; ++ hdf->bitrate_index = get_bits(&gb, 4); ++ if ((hdf->bitrate_index >= AV3A_BITRATE_TABLE_SIZE) || (hdf->bitrate_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ hdf->object_channel_number = get_bits(&gb, 7); ++ if (hdf->object_channel_number < 0) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->nb_objects = hdf->object_channel_number + 1; ++ hdf->bitrate_index_per_channel = get_bits(&gb, 4); ++ if ((hdf->bitrate_index_per_channel >= AV3A_BITRATE_TABLE_SIZE) || (hdf->bitrate_index_per_channel < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ hdf->total_bitrate = ff_av3a_bitrate_map_table[hdf->channel_number_index].bitrate_table[hdf->bitrate_index] + ++ ff_av3a_bitrate_map_table[CHANNEL_CONFIG_MONO].bitrate_table[hdf->bitrate_index_per_channel] * hdf->nb_objects; ++ } else { ++ return AVERROR_INVALIDDATA; ++ } ++ } else if (hdf->coding_profile == AV3A_AMBISONIC_PROFILE) { ++ hdf->content_type = AV3A_AMBISONIC_TYPE; ++ hdf->order = get_bits(&gb, 4); ++ hdf->hoa_order = hdf->order + 1; ++ ++ switch (hdf->hoa_order) { ++ case AV3A_AMBISONIC_FIRST_ORDER: ++ hdf->channel_number_index = CHANNEL_CONFIG_HOA_ORDER1; ++ break; ++ case AV3A_AMBISONIC_SECOND_ORDER: ++ hdf->channel_number_index = CHANNEL_CONFIG_HOA_ORDER2; ++ break; ++ case AV3A_AMBISONIC_THIRD_ORDER: ++ hdf->channel_number_index = CHANNEL_CONFIG_HOA_ORDER3; ++ break; ++ default: ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->nb_channels = ff_av3a_channels_map_table[hdf->channel_number_index].channels; ++ } else { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ hdf->total_channels = hdf->nb_channels + hdf->nb_objects; ++ ++ /* resolution */ ++ hdf->resolution_index = get_bits(&gb, 2); ++ if ((hdf->resolution_index >= AV3A_RESOLUTION_TABLE_SIZE) || (hdf->resolution_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->resolution = ff_av3a_sample_format_map_table[hdf->resolution_index].resolution; ++ hdf->sample_format = ff_av3a_sample_format_map_table[hdf->resolution_index].sample_format; ++ ++ if (hdf->coding_profile != AV3A_OBJECT_METADATA_PROFILE) { ++ hdf->bitrate_index = get_bits(&gb, 4); ++ if ((hdf->bitrate_index >= AV3A_BITRATE_TABLE_SIZE) || (hdf->bitrate_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->total_bitrate = ff_av3a_bitrate_map_table[hdf->channel_number_index].bitrate_table[hdf->bitrate_index]; ++ } ++ ++ skip_bits(&gb, 8); ++ ++ return 0; ++} ++ ++static int av3a_get_packet_size(AVFormatContext *s) ++{ ++ int ret = 0; ++ int read_bytes = 0; ++ uint16_t sync_word = 0; ++ int payload_bytes = 0; ++ int payloud_bits = 0; ++ uint8_t header[AV3A_MAX_NBYTES_HEADER]; ++ GetBitContext gb; ++ int32_t sampling_rate; ++ int16_t coding_profile, sampling_frequency_index, channel_number_index; ++ int16_t bitrate_index, bitrate_index_per_channel; ++ int16_t objects, hoa_order; ++ int64_t total_bitrate; ++ ++ if (!s) { ++ return AVERROR(ENOMEM); ++ } ++ ++ if (!s->pb) { ++ return AVERROR(ENOMEM); ++ } ++ ++ read_bytes = avio_read(s->pb, header, AV3A_MAX_NBYTES_HEADER); ++ if (read_bytes != AV3A_MAX_NBYTES_HEADER) { ++ return (read_bytes < 0) ? read_bytes : AVERROR_EOF; ++ } ++ ++ init_get_bits8(&gb, header, AV3A_MAX_NBYTES_HEADER); ++ ++ sync_word = get_bits(&gb, 12); ++ if (sync_word != AV3A_AUDIO_SYNC_WORD) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ skip_bits(&gb, 8); ++ ++ coding_profile = get_bits(&gb, 3); ++ sampling_frequency_index = get_bits(&gb, 4); ++ if ((sampling_frequency_index >= AV3A_FS_TABLE_SIZE) || (sampling_frequency_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ sampling_rate = ff_av3a_sampling_rate_table[sampling_frequency_index]; ++ ++ skip_bits(&gb, 8); ++ ++ if (coding_profile == AV3A_BASE_PROFILE) { ++ channel_number_index = get_bits(&gb, 7); ++ if ((channel_number_index >= CHANNEL_CONFIG_UNKNOWN) || ++ (channel_number_index == CHANNEL_CONFIG_MC_10_2) || ++ (channel_number_index == CHANNEL_CONFIG_MC_22_2) || ++ (channel_number_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ } else if (coding_profile == AV3A_OBJECT_METADATA_PROFILE) { ++ int64_t soundbed_bitrate, objects_bitrate; ++ int16_t soundbed_type = get_bits(&gb, 2); ++ if (soundbed_type == 0) { ++ objects = get_bits(&gb, 7); ++ if (objects < 0) { ++ return AVERROR_INVALIDDATA; ++ } ++ objects += 1; ++ ++ bitrate_index_per_channel = get_bits(&gb, 4); ++ if ((bitrate_index_per_channel >= AV3A_BITRATE_TABLE_SIZE) || (bitrate_index_per_channel < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ total_bitrate = ff_av3a_bitrate_map_table[CHANNEL_CONFIG_MONO].bitrate_table[bitrate_index_per_channel] * objects; ++ } else if (soundbed_type == 1) { ++ channel_number_index = get_bits(&gb, 7); ++ if ((channel_number_index >= CHANNEL_CONFIG_UNKNOWN) || ++ (channel_number_index == CHANNEL_CONFIG_MC_10_2) || ++ (channel_number_index == CHANNEL_CONFIG_MC_22_2) || ++ (channel_number_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ bitrate_index = get_bits(&gb, 4); ++ if ((bitrate_index >= AV3A_BITRATE_TABLE_SIZE) || (bitrate_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ soundbed_bitrate = ff_av3a_bitrate_map_table[channel_number_index].bitrate_table[bitrate_index]; ++ ++ objects = get_bits(&gb, 7); ++ if (objects < 0) { ++ return AVERROR_INVALIDDATA; ++ } ++ objects += 1; ++ bitrate_index_per_channel = get_bits(&gb, 4); ++ if ((bitrate_index_per_channel >= AV3A_BITRATE_TABLE_SIZE) || (bitrate_index_per_channel < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ objects_bitrate = ff_av3a_bitrate_map_table[CHANNEL_CONFIG_MONO].bitrate_table[bitrate_index_per_channel]; ++ total_bitrate = soundbed_bitrate + (objects_bitrate * objects); ++ } else { ++ return AVERROR_INVALIDDATA; ++ } ++ } else if (coding_profile == AV3A_AMBISONIC_PROFILE) { ++ hoa_order = get_bits(&gb, 4); ++ hoa_order += 1; ++ ++ switch (hoa_order) { ++ case AV3A_AMBISONIC_FIRST_ORDER: ++ channel_number_index = CHANNEL_CONFIG_HOA_ORDER1; ++ break; ++ case AV3A_AMBISONIC_SECOND_ORDER: ++ channel_number_index = CHANNEL_CONFIG_HOA_ORDER2; ++ break; ++ case AV3A_AMBISONIC_THIRD_ORDER: ++ channel_number_index = CHANNEL_CONFIG_HOA_ORDER3; ++ break; ++ default: ++ return AVERROR_INVALIDDATA; ++ } ++ } else { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ skip_bits(&gb, 2); ++ if (coding_profile != AV3A_OBJECT_METADATA_PROFILE) { ++ bitrate_index = get_bits(&gb, 4); ++ if ((bitrate_index >= AV3A_BITRATE_TABLE_SIZE) || (bitrate_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ total_bitrate = ff_av3a_bitrate_map_table[channel_number_index].bitrate_table[bitrate_index]; ++ } ++ ++ skip_bits(&gb, 8); ++ ++ if (sampling_rate == 44100) { ++ payloud_bits = (int)floor(((float) (total_bitrate) / sampling_rate) * AV3A_AUDIO_FRAME_SIZE); ++ payload_bytes = (int)ceil((float)payloud_bits / 8); ++ } else { ++ payload_bytes = (int)ceil((((float) (total_bitrate) / sampling_rate) * AV3A_AUDIO_FRAME_SIZE) / 8); ++ } ++ ++ if ((ret = avio_seek(s->pb, -read_bytes, SEEK_CUR)) < 0) { ++ return ret; ++ } ++ ++ return payload_bytes; ++} ++ ++static int av3a_probe(const AVProbeData *p) ++{ ++ uint16_t frame_sync_word; ++ uint16_t lval = ((uint16_t)(p->buf[0])); ++ uint16_t rval = ((uint16_t)(p->buf[1])); ++ frame_sync_word = ((lval << 8) | rval) >> 4; ++ ++ if (frame_sync_word == AV3A_AUDIO_SYNC_WORD && av_match_ext(p->filename, "av3a")) { ++ return AVPROBE_SCORE_MAX; ++ } ++ ++ return 0; ++} ++ ++static int av3a_read_header(AVFormatContext *s) ++{ ++ int ret = 0; ++ uint8_t header[AV3A_MAX_NBYTES_HEADER]; ++ AVStream *stream = NULL; ++ Av3aFormatContext av3afmtctx; ++ AATFHeaderInfo hdf; ++ ++ if (!s) { ++ return AVERROR(ENOMEM); ++ } ++ ++ if (!(stream = avformat_new_stream(s, NULL))) { ++ return AVERROR(ENOMEM); ++ } ++ ++ stream->start_time = 0; ++ ffstream(stream)->need_parsing = AVSTREAM_PARSE_FULL_RAW; ++ stream->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; ++ stream->codecpar->codec_id = ((const FFInputFormat*)(s->iformat))->raw_codec_id; ++ stream->codecpar->codec_tag = MKTAG('a', 'v', '3', 'a'); ++ ++ if ((ret = avio_read(s->pb, header, AV3A_MAX_NBYTES_HEADER)) != AV3A_MAX_NBYTES_HEADER) { ++ return (ret < 0) ? ret : AVERROR_EOF; ++ } ++ ++ ret = av3a_read_aatf_frame_header(&hdf, header); ++ if (ret) { ++ return ret; ++ } ++ ++ /* stream parameters */ ++ stream->codecpar->format = hdf.sample_format; ++ stream->codecpar->bits_per_raw_sample = hdf.resolution; ++ stream->codecpar->bit_rate = hdf.total_bitrate; ++ stream->codecpar->sample_rate = (int) (hdf.sampling_rate); ++ stream->codecpar->frame_size = AV3A_AUDIO_FRAME_SIZE; ++ stream->codecpar->ch_layout.order = AV_CHANNEL_ORDER_UNSPEC; ++ stream->codecpar->ch_layout.nb_channels = hdf.total_channels; ++ ++ /* extradata */ ++ av3afmtctx.audio_codec_id = hdf.audio_codec_id; ++ av3afmtctx.sampling_frequency_index = hdf.sampling_frequency_index; ++ av3afmtctx.nn_type = hdf.nn_type; ++ av3afmtctx.content_type = hdf.content_type; ++ av3afmtctx.channel_number_index = hdf.channel_number_index; ++ av3afmtctx.number_objects = hdf.nb_objects; ++ av3afmtctx.hoa_order = hdf.hoa_order; ++ av3afmtctx.resolution_index = hdf.resolution_index; ++ av3afmtctx.total_bitrate_kbps = (int) (hdf.total_bitrate / 1000); ++ ++ if ((ret = ff_alloc_extradata(stream->codecpar, sizeof(Av3aFormatContext))) < 0) { ++ return ret; ++ } ++ memcpy(stream->codecpar->extradata, &av3afmtctx, sizeof(Av3aFormatContext)); ++ ++ if ((ret = avio_seek(s->pb, -AV3A_MAX_NBYTES_HEADER, SEEK_CUR)) < 0) { ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int av3a_read_packet(AVFormatContext *s, AVPacket *pkt) ++{ ++ int64_t pos; ++ int packet_size = 0; ++ int read_bytes = 0; ++ int ret = 0; ++ ++ if (!s) { ++ return AVERROR(ENOMEM); ++ } ++ ++ if (avio_feof(s->pb)) { ++ return AVERROR_EOF; ++ } ++ pos = avio_tell(s->pb); ++ ++ if (!(packet_size = av3a_get_packet_size(s))) { ++ return AVERROR_EOF; ++ } ++ ++ if (packet_size < 0) { ++ return packet_size; ++ } ++ ++ if ((ret = av_new_packet(pkt, packet_size)) < 0) { ++ return ret; ++ } ++ ++ if (!s->streams[0]) { ++ return AVERROR(ENOMEM); ++ } ++ ++ if (!s->streams[0]->codecpar) { ++ return AVERROR(ENOMEM); ++ } ++ ++ pkt->stream_index = 0; ++ pkt->pos = pos; ++ pkt->duration = s->streams[0]->codecpar->frame_size; ++ ++ read_bytes = avio_read(s->pb, pkt->data, packet_size); ++ if (read_bytes != packet_size) { ++ return (read_bytes < 0) ? read_bytes : AVERROR_EOF; ++ } ++ ++ return 0; ++} ++ ++const FFInputFormat ff_av3a_demuxer = { ++ .p.name = "av3a", ++ .p.long_name = NULL_IF_CONFIG_SMALL("Audio Vivid"), ++ .raw_codec_id = AV_CODEC_ID_AVS3DA, ++ .p.priv_class = &ff_raw_demuxer_class, ++ .priv_data_size = sizeof(FFRawDemuxerContext), ++ .read_probe = av3a_probe, ++ .read_header = av3a_read_header, ++ .read_packet = av3a_read_packet, ++ .p.flags = AVFMT_GENERIC_INDEX, ++ .p.extensions = "av3a", ++ .p.mime_type = "audio/av3a", ++}; +\ No newline at end of file +diff --git a/libavformat/isom_tags.c b/libavformat/isom_tags.c +index 5dd72d5..1c52d7c 100644 +--- a/libavformat/isom_tags.c ++++ b/libavformat/isom_tags.c +@@ -353,6 +353,7 @@ const AVCodecTag ff_codec_movaudio_tags[] = { + { AV_CODEC_ID_TRUEHD, MKTAG('m', 'l', 'p', 'a') }, /* mp4ra.org */ + { AV_CODEC_ID_OPUS, MKTAG('O', 'p', 'u', 's') }, /* mp4ra.org */ + { AV_CODEC_ID_MPEGH_3D_AUDIO, MKTAG('m', 'h', 'm', '1') }, /* MPEG-H 3D Audio bitstream */ ++ { AV_CODEC_ID_AVS3DA, MKTAG('a', 'v', '3', 'a') }, /* AVS3 Audio */ + { AV_CODEC_ID_NONE, 0 }, + }; + +diff --git a/libavformat/mov.c b/libavformat/mov.c +index 495e2e7..486e2a3 100644 +--- a/libavformat/mov.c ++++ b/libavformat/mov.c +@@ -69,6 +69,10 @@ + #include "mov_chan.h" + #include "replaygain.h" + ++#if CONFIG_AV3A_DEMUXER ++#include "libavcodec/av3a.h" ++#endif ++ + #if CONFIG_ZLIB + #include + #endif +@@ -88,6 +92,134 @@ static void mov_free_stream_context(AVFormatContext *s, AVStream *st); + static int64_t add_ctts_entry(MOVCtts** ctts_data, unsigned int* ctts_count, unsigned int* allocated_size, + int count, int duration); + ++#if CONFIG_AV3A_DEMUXER ++static int mov_read_dca3(MOVContext *c, AVIOContext *pb, MOVAtom atom) ++{ ++ int ret = 0; ++ int i = 0; ++ int nb_channels = 0; ++ int nb_objects = 0; ++ AVStream *st = NULL; ++ GetBitContext gb; ++ uint8_t buffer[7]; ++ int audio_codec_id, sampling_frequency_index; ++ int nn_type, content_type, channel_number_index, number_objects; ++ int hoa_order, resolution_index, reserved; ++ int bitrate_kbps; ++ ++ if (atom.size < AV3A_DCA3_BOX_MIN_SIZE) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ init_get_bits8(&gb, buffer, sizeof(buffer)); ++ ++ if (c->fc->nb_streams < 1) { ++ return 0; ++ } ++ st = c->fc->streams[c->fc->nb_streams - 1]; ++ ++ if ((ret = avio_read(pb, buffer, sizeof(buffer))) < 0) { ++ return ret; ++ } ++ ++ audio_codec_id = get_bits(&gb, 4); ++ if (audio_codec_id != AV3A_LOSSY_CODEC_ID) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ st->codecpar->frame_size = AV3A_AUDIO_FRAME_SIZE; ++ sampling_frequency_index = get_bits(&gb, 4); ++ if ((sampling_frequency_index >= AV3A_FS_TABLE_SIZE) || (sampling_frequency_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ st->codecpar->sample_rate = ff_av3a_sampling_rate_table[sampling_frequency_index]; ++ ++ nn_type = get_bits(&gb, 3); ++ if ((nn_type > AV3A_LC_NN_TYPE) || (nn_type < AV3A_BASELINE_NN_TYPE)) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ reserved = get_bits(&gb, 1); ++ content_type = get_bits(&gb, 4); ++ if (content_type == AV3A_CHANNEL_BASED_TYPE) { ++ channel_number_index = get_bits(&gb, 7); ++ reserved = get_bits(&gb, 1); ++ if ((channel_number_index >= CHANNEL_CONFIG_UNKNOWN) || ++ (channel_number_index == CHANNEL_CONFIG_MC_10_2) || ++ (channel_number_index == CHANNEL_CONFIG_MC_22_2) || ++ (channel_number_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ nb_channels = ff_av3a_channels_map_table[channel_number_index].channels; ++ } else if (content_type == AV3A_OBJECT_BASED_TYPE) { ++ number_objects = get_bits(&gb, 7); ++ reserved = get_bits(&gb, 1); ++ nb_objects = number_objects; ++ if (nb_objects < 1) { ++ return AVERROR_INVALIDDATA; ++ } ++ } else if (content_type == AV3A_CHANNEL_OBJECT_TYPE) { ++ channel_number_index = get_bits(&gb, 7); ++ reserved = get_bits(&gb, 1); ++ if ((channel_number_index >= CHANNEL_CONFIG_UNKNOWN) || ++ (channel_number_index == CHANNEL_CONFIG_MC_10_2) || ++ (channel_number_index == CHANNEL_CONFIG_MC_22_2) || ++ (channel_number_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ number_objects = get_bits(&gb, 7); ++ reserved = get_bits(&gb, 1); ++ nb_channels = ff_av3a_channels_map_table[channel_number_index].channels; ++ nb_objects = number_objects; ++ if (nb_objects < 1) { ++ return AVERROR_INVALIDDATA; ++ } ++ } else if (content_type == AV3A_AMBISONIC_TYPE) { ++ hoa_order = get_bits(&gb, 4); ++ if ((hoa_order < AV3A_AMBISONIC_FIRST_ORDER) || (hoa_order > AV3A_AMBISONIC_THIRD_ORDER)) { ++ return AVERROR_INVALIDDATA; ++ } ++ nb_channels = (hoa_order + 1) * (hoa_order + 1); ++ } else { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ bitrate_kbps = get_bits(&gb, 16); ++ st->codecpar->bit_rate = bitrate_kbps * 1000; ++ ++ resolution_index = get_bits(&gb, 2); ++ if ((resolution_index >= AV3A_RESOLUTION_TABLE_SIZE) || (resolution_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ st->codecpar->format = ff_av3a_sample_format_map_table[resolution_index].sample_format; ++ st->codecpar->bits_per_raw_sample = ff_av3a_sample_format_map_table[resolution_index].resolution; ++ ++ av_channel_layout_uninit(&st->codecpar->ch_layout); ++ if (content_type != AV3A_AMBISONIC_TYPE) { ++ st->codecpar->ch_layout.order = AV_CHANNEL_ORDER_CUSTOM; ++ st->codecpar->ch_layout.nb_channels = (nb_channels + nb_objects); ++ st->codecpar->ch_layout.u.map = av_calloc(st->codecpar->ch_layout.nb_channels, sizeof(AVChannelCustom)); ++ if (!st->codecpar->ch_layout.u.map) { ++ return AVERROR(ENOMEM); ++ } ++ ++ if (content_type != AV3A_OBJECT_BASED_TYPE) { ++ for (i = 0; i < nb_channels; i ++) { ++ st->codecpar->ch_layout.u.map[i].id = ff_av3a_channels_map_table[channel_number_index].channel_layout[i]; ++ } ++ } ++ ++ for (i = nb_channels; i < st->codecpar->ch_layout.nb_channels; i++) { ++ st->codecpar->ch_layout.u.map[i].id = AV3A_CH_AUDIO_OBJECT; ++ } ++ } else { ++ st->codecpar->ch_layout.order = AV_CHANNEL_ORDER_AMBISONIC; ++ st->codecpar->ch_layout.nb_channels = nb_channels; ++ } ++ return 0; ++} ++#endif ++ + static int mov_metadata_track_or_disc_number(MOVContext *c, AVIOContext *pb, + unsigned len, const char *key) + { +@@ -9211,6 +9343,9 @@ static const MOVParseTableEntry mov_default_parse_table[] = { + #if CONFIG_IAMFDEC + { MKTAG('i','a','c','b'), mov_read_iacb }, + #endif ++#if CONFIG_AV3A_DEMUXER ++{ MKTAG('d','c','a','3'), mov_read_dca3 }, ++#endif + { 0, NULL } + }; + +diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c +index 58b50cb..59039ed 100644 +--- a/libavformat/mpegts.c ++++ b/libavformat/mpegts.c +@@ -822,6 +822,9 @@ static const StreamType ISO_types[] = { + { 0xd1, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_DIRAC }, + { 0xd2, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_AVS2 }, + { 0xd4, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_AVS3 }, ++#if CONFIG_AV3A_DEMUXER ++ { 0xd5, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AVS3DA }, /* avs3 audio */ ++#endif + { 0xea, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_VC1 }, + { 0 }, + }; +@@ -879,6 +882,9 @@ static const StreamType REGD_types[] = { + { MKTAG('I', 'D', '3', ' '), AVMEDIA_TYPE_DATA, AV_CODEC_ID_TIMED_ID3 }, + { MKTAG('V', 'C', '-', '1'), AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_VC1 }, + { MKTAG('O', 'p', 'u', 's'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_OPUS }, ++#if CONFIG_AV3A_DEMUXER ++ { MKTAG('a', 'v', '3', 'a'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AVS3DA}, ++#endif + { 0 }, + }; + +diff --git a/libavformat/mpegts.h b/libavformat/mpegts.h +index 14ae312..fdef48a 100644 +--- a/libavformat/mpegts.h ++++ b/libavformat/mpegts.h +@@ -135,6 +135,7 @@ + #define STREAM_TYPE_VIDEO_VC1 0xea + #define STREAM_TYPE_VIDEO_DIRAC 0xd1 + ++#define STREAM_TYPE_AUDIO_AV3A 0xd5 + #define STREAM_TYPE_AUDIO_AC3 0x81 + #define STREAM_TYPE_AUDIO_DTS 0x82 + #define STREAM_TYPE_AUDIO_TRUEHD 0x83 +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n7.1.1/0021-http-add-reconnect_first_delay-opt.patch b/patches/ffmpeg-n7.1.1/0021-http-add-reconnect_first_delay-opt.patch new file mode 100644 index 000000000..3f6b9ce14 --- /dev/null +++ b/patches/ffmpeg-n7.1.1/0021-http-add-reconnect_first_delay-opt.patch @@ -0,0 +1,41 @@ +From 8984a891f4bf976a3ab311591c7c6de499b434df Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Thu, 29 May 2025 09:57:28 +0800 +Subject: [PATCH] http add reconnect_first_delay opt + +--- + libavformat/http.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/libavformat/http.c b/libavformat/http.c +index 1228735..6e96aaa 100644 +--- a/libavformat/http.c ++++ b/libavformat/http.c +@@ -128,6 +128,7 @@ typedef struct HTTPContext { + int reconnect_on_network_error; + int reconnect_streamed; + int reconnect_delay_max; ++ int reconnect_first_delay; + char *reconnect_on_http_error; + int listen; + char *resource; +@@ -188,6 +189,7 @@ static const AVOption options[] = { + { "reconnect_max_retries", "the max number of times to retry a connection", OFFSET(reconnect_max_retries), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, D }, + { "reconnect_delay_total_max", "max total reconnect delay in seconds after which to give up", OFFSET(reconnect_delay_total_max), AV_OPT_TYPE_INT, { .i64 = 256 }, 0, UINT_MAX/1000/1000, D }, + { "respect_retry_after", "respect the Retry-After header when retrying connections", OFFSET(respect_retry_after), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, D }, ++ { "reconnect_first_delay", "first reconnect delay in seconds", OFFSET(reconnect_first_delay), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, UINT_MAX/1000/1000, D }, + { "listen", "listen on HTTP", OFFSET(listen), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 2, D | E }, + { "resource", "The resource requested by a client", OFFSET(resource), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E }, + { "reply_code", "The http status code to return to a client", OFFSET(reply_code), AV_OPT_TYPE_INT, { .i64 = 200}, INT_MIN, 599, E}, +@@ -379,7 +381,7 @@ static int http_open_cnx(URLContext *h, AVDictionary **options) + HTTPAuthType cur_auth_type, cur_proxy_auth_type; + HTTPContext *s = h->priv_data; + int ret, conn_attempts = 1, auth_attempts = 0, redirects = 0; +- int reconnect_delay = 0; ++ int reconnect_delay = s->reconnect_first_delay; + int reconnect_delay_total = 0; + uint64_t off; + char *cached; +-- +2.39.5 (Apple Git-154) + diff --git a/patches/ffmpeg-n7.1.1/0022-fix-http-open-and-http_seek-redirect-authentication-.patch b/patches/ffmpeg-n7.1.1/0022-fix-http-open-and-http_seek-redirect-authentication-.patch new file mode 100644 index 000000000..f15291901 --- /dev/null +++ b/patches/ffmpeg-n7.1.1/0022-fix-http-open-and-http_seek-redirect-authentication-.patch @@ -0,0 +1,92 @@ +From 8e8699cf879e1bfcd2bc659d5022e33900fc6069 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Thu, 29 May 2025 10:00:04 +0800 +Subject: [PATCH] fix http open and http_seek (redirect) authentication bug + +--- + libavformat/http.c | 25 ++++++++++++++++++++++++- + 1 file changed, 24 insertions(+), 1 deletion(-) + +diff --git a/libavformat/http.c b/libavformat/http.c +index 6e96aaa..87a2eb3 100644 +--- a/libavformat/http.c ++++ b/libavformat/http.c +@@ -80,6 +80,7 @@ typedef struct HTTPContext { + char *uri; + char *location; + HTTPAuthState auth_state; ++ int auth_type2; + HTTPAuthState proxy_auth_state; + char *http_proxy; + char *headers; +@@ -173,6 +174,7 @@ static const AVOption options[] = { + { "icy_metadata_packet", "return current ICY metadata packet", OFFSET(icy_metadata_packet), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, AV_OPT_FLAG_EXPORT }, + { "metadata", "metadata read from the bitstream", OFFSET(metadata), AV_OPT_TYPE_DICT, {0}, 0, 0, AV_OPT_FLAG_EXPORT }, + { "auth_type", "HTTP authentication type", OFFSET(auth_state.auth_type), AV_OPT_TYPE_INT, { .i64 = HTTP_AUTH_NONE }, HTTP_AUTH_NONE, HTTP_AUTH_BASIC, D | E, .unit = "auth_type"}, ++ { "auth_type2", "backup HTTP authentication type for seek request", OFFSET(auth_type2), AV_OPT_TYPE_INT, { .i64 = HTTP_AUTH_NONE }, HTTP_AUTH_NONE, HTTP_AUTH_BASIC, D | E, "auth_type"}, + { "none", "No auth method set, autodetect", 0, AV_OPT_TYPE_CONST, { .i64 = HTTP_AUTH_NONE }, 0, 0, D | E, .unit = "auth_type"}, + { "basic", "HTTP basic authentication", 0, AV_OPT_TYPE_CONST, { .i64 = HTTP_AUTH_BASIC }, 0, 0, D | E, .unit = "auth_type"}, + { "send_expect_100", "Force sending an Expect: 100-continue header for POST", OFFSET(send_expect_100), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, E }, +@@ -731,6 +733,11 @@ static int http_open(URLContext *h, const char *uri, int flags, + int ret; + s->app_ctx = (AVApplicationContext *)av_dict_strtoptr(s->app_ctx_intptr); + ++ if (s->auth_type2 == HTTP_AUTH_NONE) { ++ //backup the init auth_type, when not assign. ++ s->auth_type2 = s->auth_state.auth_type; ++ } ++ + if( s->seekable == 1 ) + h->is_streamed = 0; + else +@@ -1483,6 +1490,7 @@ static int http_connect(URLContext *h, const char *path, const char *local_path, + uint64_t off = s->off; + const char *method; + int send_expect_100 = 0; ++ int cur_auth_type = s->auth_state.auth_type; + + av_bprint_init_for_buffer(&request, s->buffer, sizeof(s->buffer)); + +@@ -1624,9 +1632,19 @@ static int http_connect(URLContext *h, const char *path, const char *local_path, + + if (s->new_location) + s->off = off; +- + err = (off == s->off) ? 0 : -1; ++ ++ //in http_seek_internal func reverted to the original uri,but the s->off is not zero,so err is -1,cause can't goto the 401 authenticate logic. ++ if (err != 0 && cur_auth_type != s->auth_state.auth_type && s->http_code == 401) { ++ //reverte the off,otherwise can't seek the target position. ++ s->off = off; ++ av_log(NULL, AV_LOG_ERROR, "http 401 error,need authenticate:%s,at:%llu\n", s->buffer, s->off); ++ err = 0; ++ } + done: ++ if (err != 0) { ++ av_log(NULL, AV_LOG_ERROR, "http error %d,%s\n", s->http_code,s->buffer); ++ } + av_freep(&authstr); + av_freep(&proxyauthstr); + return err; +@@ -2021,6 +2039,8 @@ static int64_t http_seek_internal(URLContext *h, int64_t off, int whence, int fo + return s->off; + } + ++ // http_seek use lasest redirect location, because after redirect, reset the auth_state: `memset(&s->auth_state, 0, sizeof(s->auth_state));` ++ + /* if the location changed (redirect), revert to the original uri */ + if (strcmp(s->uri, s->location)) { + char *new_uri; +@@ -2029,6 +2049,9 @@ static int64_t http_seek_internal(URLContext *h, int64_t off, int whence, int fo + return AVERROR(ENOMEM); + av_free(s->location); + s->location = new_uri; ++ if (s->auth_type2 != HTTP_AUTH_NONE) { ++ s->auth_state.auth_type = s->auth_type2; ++ } + } + + /* we save the old context in case the seek fails */ +-- +2.39.5 (Apple Git-154) + diff --git a/patches/ffmpeg-n7.1.1/0023-supportsFamily-is-only-available-on-iOS-13.0-or-newe.patch b/patches/ffmpeg-n7.1.1/0023-supportsFamily-is-only-available-on-iOS-13.0-or-newe.patch new file mode 100644 index 000000000..a1b64b8cb --- /dev/null +++ b/patches/ffmpeg-n7.1.1/0023-supportsFamily-is-only-available-on-iOS-13.0-or-newe.patch @@ -0,0 +1,25 @@ +From 5f2599819cafddaa5010462db07db7efbd05907e Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Thu, 29 May 2025 11:13:10 +0800 +Subject: [PATCH] 'supportsFamily:' is only available on iOS 13.0 or newer + +--- + libavfilter/metal/utils.m | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/libavfilter/metal/utils.m b/libavfilter/metal/utils.m +index f365d3c..bb1825a 100644 +--- a/libavfilter/metal/utils.m ++++ b/libavfilter/metal/utils.m +@@ -31,7 +31,7 @@ void ff_metal_compute_encoder_dispatch(id device, + BOOL fallback = YES; + // MAC_OS_X_VERSION_10_15 is only defined on SDKs new enough to include its functionality (including iOS, tvOS, etc) + #ifdef MAC_OS_X_VERSION_10_15 +- if (@available(macOS 10.15, iOS 11, tvOS 14.5, *)) { ++ if (@available(macOS 10.15, iOS 13, tvOS 14.5, *)) { + if ([device supportsFamily:MTLGPUFamilyCommon3]) { + MTLSize threadsPerGrid = MTLSizeMake(width, height, 1); + [encoder dispatchThreads:threadsPerGrid threadsPerThreadgroup:threadsPerThreadgroup]; +-- +2.39.5 (Apple Git-154) + diff --git a/patches/ffmpeg-n7.1.1/0024-add-built-in-smb2-protocol-via-libsmb2.patch b/patches/ffmpeg-n7.1.1/0024-add-built-in-smb2-protocol-via-libsmb2.patch new file mode 100644 index 000000000..fafd0d303 --- /dev/null +++ b/patches/ffmpeg-n7.1.1/0024-add-built-in-smb2-protocol-via-libsmb2.patch @@ -0,0 +1,495 @@ +From a9fb97ec6c7623c584f19411cf7def836ad72796 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Thu, 29 May 2025 10:49:16 +0800 +Subject: [PATCH] add built-in smb2 protocol via libsmb2 + +--- + configure | 5 + + libavformat/Makefile | 1 + + libavformat/libsmb2.c | 412 ++++++++++++++++++++++++++++++++++++++++ + libavformat/protocols.c | 1 + + 4 files changed, 419 insertions(+) + create mode 100644 libavformat/libsmb2.c + +diff --git a/configure b/configure +index ffa407d..f74d4de 100755 +--- a/configure ++++ b/configure +@@ -272,6 +272,7 @@ External library support: + --enable-libshaderc enable GLSL->SPIRV compilation via libshaderc [no] + --enable-libshine enable fixed-point MP3 encoding via libshine [no] + --enable-libsmbclient enable Samba protocol via libsmbclient [no] ++ --enable-libsmb2 enable Samba protocol via libsmb2 [no] + --enable-libsnappy enable Snappy compression, needed for hap encoding [no] + --enable-libsoxr enable Include libsoxr resampling [no] + --enable-libspeex enable Speex de/encoding via libspeex [no] +@@ -1953,6 +1954,7 @@ EXTERNAL_LIBRARY_LIST=" + libshaderc + libshine + libsmbclient ++ libsmb2 + libsnappy + libsoxr + libspeex +@@ -3816,6 +3818,7 @@ librtmps_protocol_deps="librtmp" + librtmpt_protocol_deps="librtmp" + librtmpte_protocol_deps="librtmp" + libsmbclient_protocol_deps="libsmbclient gplv3" ++libsmb2_protocol_deps="libsmb2" + libsrt_protocol_deps="libsrt" + libsrt_protocol_select="network" + libssh_protocol_deps="libssh" +@@ -6991,6 +6994,8 @@ enabled libshaderc && require_pkg_config spirv_compiler "shaderc >= 2019. + enabled libshine && require_pkg_config libshine shine shine/layer3.h shine_encode_buffer + enabled libsmbclient && { check_pkg_config libsmbclient smbclient libsmbclient.h smbc_init || + require libsmbclient libsmbclient.h smbc_init -lsmbclient; } ++enabled libsmb2 && { check_pkg_config libsmb2 libsmb2 smb2/smb2-errors.h SMB2_STATUS_SUCCESS || ++ require libsmb2 smb2/smb2-errors.h SMB2_STATUS_SUCCESS -lsmb2; } + enabled libsnappy && require libsnappy snappy-c.h snappy_compress -lsnappy -lstdc++ + enabled libsoxr && require libsoxr soxr.h soxr_create -lsoxr + enabled libssh && require_pkg_config libssh "libssh >= 0.6.0" libssh/sftp.h sftp_init +diff --git a/libavformat/Makefile b/libavformat/Makefile +index 17222ee..155dbe1 100644 +--- a/libavformat/Makefile ++++ b/libavformat/Makefile +@@ -735,6 +735,7 @@ OBJS-$(CONFIG_LIBRTMPS_PROTOCOL) += librtmp.o + OBJS-$(CONFIG_LIBRTMPT_PROTOCOL) += librtmp.o + OBJS-$(CONFIG_LIBRTMPTE_PROTOCOL) += librtmp.o + OBJS-$(CONFIG_LIBSMBCLIENT_PROTOCOL) += libsmbclient.o ++OBJS-$(CONFIG_LIBSMB2_PROTOCOL) += libsmb2.o + OBJS-$(CONFIG_LIBSRT_PROTOCOL) += libsrt.o urldecode.o + OBJS-$(CONFIG_LIBSSH_PROTOCOL) += libssh.o + OBJS-$(CONFIG_LIBZMQ_PROTOCOL) += libzmq.o +diff --git a/libavformat/libsmb2.c b/libavformat/libsmb2.c +new file mode 100644 +index 0000000..408f9e1 +--- /dev/null ++++ b/libavformat/libsmb2.c +@@ -0,0 +1,412 @@ ++/* ++ * Copyright (c) 2014 Lukasz Marek ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg 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. ++ * ++ * FFmpeg 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 FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++ ++#include ++#include ++#include "libavutil/avstring.h" ++#include "libavutil/opt.h" ++#include "libavutil/mem.h" ++#include "application.h" ++#include "url.h" ++#include "urldecode.h" ++//smb2.h:37:9: error: unknown type name 'time_t'; ++#include ++#include ++#include ++#include ++ ++typedef struct ++{ ++ const AVClass *class; ++ ++ struct smb2_context *ctx; ++ struct smb2_url *url; ++ struct smb2fh *fh; ++ struct smb2dir *dir; ++ ++ uint64_t filesize; ++ char *app_ctx_intptr; ++ AVApplicationContext *app_ctx; ++ int smb2_seal; ++} LIBSMB2Context; ++ ++static void destroy_smb2(LIBSMB2Context *libsmb2) ++{ ++ if (libsmb2->fh && libsmb2->ctx) { ++ smb2_close(libsmb2->ctx, libsmb2->fh); ++ } ++ ++ if (libsmb2->ctx) { ++ smb2_disconnect_share(libsmb2->ctx); ++ smb2_destroy_context(libsmb2->ctx); ++ } ++ ++ if (libsmb2->url) { ++ smb2_destroy_url(libsmb2->url); ++ } ++ libsmb2->fh = NULL; ++ libsmb2->ctx = NULL; ++ libsmb2->url = NULL; ++} ++ ++static av_cold int libsmb2_close(URLContext *h) ++{ ++ LIBSMB2Context *libsmb2 = h->priv_data; ++ destroy_smb2(libsmb2); ++ return 0; ++} ++ ++static av_cold int libsmb2_open(URLContext *h, const char *uri, int flags) ++{ ++ int ret = 0; ++ LIBSMB2Context *libsmb2 = h->priv_data; ++ libsmb2->filesize = -1; ++ libsmb2->ctx = smb2_init_context(); ++ libsmb2->app_ctx = (AVApplicationContext *)av_dict_strtoptr(libsmb2->app_ctx_intptr); ++ ++ av_application_will_http_open(libsmb2->app_ctx, (void *)h, uri); ++ ++ if (!libsmb2->ctx) { ++ av_log(h, AV_LOG_ERROR, "smb2 create context failed: %s.\n", smb2_get_error(libsmb2->ctx)); ++ ret = AVERROR(ENOMEM); ++ goto failed; ++ } ++ ++ const char *smb_url = av_strireplace(uri, "smb2", "smb"); ++ struct smb2_url *url = smb2_parse_url(libsmb2->ctx, smb_url); ++ ++ if (url == NULL) { ++ av_log(h, AV_LOG_ERROR, "smb2 parse url failed: %s\n", smb2_get_error(libsmb2->ctx)); ++ ret = AVERROR(ENOMEM); ++ goto failed; ++ } else { ++ if (url->user) { ++ char *user = strchr(url->user, ':'); ++ if (user) { ++ *user = '\0'; ++ char *password = user + 1; ++ if (strlen(password) > 0) { ++ password = ff_urldecode(password, 0); ++ smb2_set_password(libsmb2->ctx, password); ++ } ++ } ++ } ++ ++ if (url->domain) { ++ smb2_set_domain(libsmb2->ctx, url->domain); ++ } ++ ++ if (url->share) { ++ char *share = ff_urldecode(url->share, 0); ++ memset(url->share, 0, strlen(url->share)); ++ memcpy(url->share, share, strlen(share)); ++ } ++ ++ if (url->path) { ++ char *path = ff_urldecode(url->path, 0); ++ memset(url->path, 0, strlen(url->path)); ++ memcpy(url->path, path, strlen(path)); ++ } ++ ++ libsmb2->url = url; ++ } ++ ++ //https://github.com/sahlberg/libsmb2/issues/271 ++ //fix Very slow performance w/MacOS SMB server ++ //smb2_set_security_mode(libsmb2->ctx, SMB2_NEGOTIATE_SIGNING_ENABLED); ++ smb2_set_seal(libsmb2->ctx, libsmb2->smb2_seal); ++ smb2_set_authentication(libsmb2->ctx, 1);//SMB2_SEC_NTLMSSP ++ smb2_set_timeout(libsmb2->ctx, 60); ++ ++ if (smb2_connect_share(libsmb2->ctx, url->server, url->share, url->user) != 0) { ++ av_log(h, AV_LOG_ERROR, "smb2 connect share failed: %s\n", smb2_get_error(libsmb2->ctx)); ++ ret = AVERROR(ECONNREFUSED); ++ goto failed; ++ } ++ ++ int access; ++ if ((flags & AVIO_FLAG_WRITE) && (flags & AVIO_FLAG_READ)) { ++ access = O_CREAT | O_RDWR; ++ } else if (flags & AVIO_FLAG_WRITE) { ++ access = O_CREAT | O_WRONLY; ++ } else { ++ access = O_RDONLY; ++ } ++ ++ if (flags & AVIO_FLAG_DIRECT) { ++ if ((libsmb2->dir = smb2_opendir(libsmb2->ctx, url->path)) == NULL) { ++ av_log(h, AV_LOG_ERROR, "smb2 open dir failed: %s, error: %s\n", url->path, smb2_get_error(libsmb2->ctx)); ++ ret = AVERROR(ENOTDIR); ++ goto failed; ++ } ++ } else { ++ if ((libsmb2->fh = smb2_open(libsmb2->ctx, url->path, access)) == NULL) { ++ av_log(h, AV_LOG_ERROR, "smb2 open file failed: %s, error: %s\n", url->path, smb2_get_error(libsmb2->ctx)); ++ ret = AVERROR(ENOENT); ++ goto failed; ++ } ++ } ++ ++ struct smb2_stat_64 st = {0}; ++ ++ if (smb2_stat(libsmb2->ctx, url->path, &st) < 0) ++ av_log(h, AV_LOG_WARNING, "Cannot stat file: %s\n", smb2_get_error(libsmb2->ctx)); ++ else ++ libsmb2->filesize = st.smb2_size; ++ av_application_did_http_open(libsmb2->app_ctx, (void *)h, uri, 0, 200, libsmb2->filesize); ++ return 0; ++failed: ++ av_application_did_http_open(libsmb2->app_ctx, (void *)h, uri, ret, 500, 0); ++ if (libsmb2->fh && libsmb2->ctx) { ++ smb2_close(libsmb2->ctx, libsmb2->fh); ++ } ++ if (libsmb2->ctx) { ++ smb2_disconnect_share(libsmb2->ctx); ++ smb2_destroy_context(libsmb2->ctx); ++ } ++ if (libsmb2->url) { ++ smb2_destroy_url(libsmb2->url); ++ } ++ libsmb2->fh = NULL; ++ libsmb2->ctx = NULL; ++ libsmb2->url = NULL; ++ return -1; ++} ++ ++static int64_t libsmb2_seek(URLContext *h, int64_t pos, int whence) ++{ ++ LIBSMB2Context *libsmb2 = h->priv_data; ++ int64_t newpos; ++ ++ if (whence == AVSEEK_SIZE) { ++ if (libsmb2->filesize == -1) { ++ av_log(h, AV_LOG_ERROR, "smb2 seek failed,filesize is unknown.\n"); ++ return AVERROR(EIO); ++ } else { ++ return libsmb2->filesize; ++ } ++ } ++ ++ av_application_will_http_seek(libsmb2->app_ctx, (void *)h, h->filename, pos); ++ ++ if ((newpos = smb2_lseek(libsmb2->ctx, libsmb2->fh, pos, whence, NULL)) < 0) { ++ av_log(h, AV_LOG_ERROR, "smb2 seek failed: %s\n", smb2_get_error(libsmb2->ctx)); ++ av_application_did_http_seek(libsmb2->app_ctx, (void *)h, h->filename, pos, AVERROR(errno), 500); ++ return AVERROR(errno); ++ } ++ av_application_did_http_seek(libsmb2->app_ctx, (void *)h, h->filename, pos, 0, 200); ++ return newpos; ++} ++ ++static int libsmb2_read(URLContext *h, unsigned char *buf, int size) ++{ ++ LIBSMB2Context *libsmb2 = h->priv_data; ++ ++ uint8_t *buf1 = buf; ++ int buf_size1 = size; ++ int has_error = 0; ++ ++ while (buf_size1 > 0) { ++ int read = smb2_read(libsmb2->ctx, libsmb2->fh, buf1, buf_size1); ++ if (read < 0) { ++ av_log(h, AV_LOG_ERROR, "smb2 read file failed: %s\n", ++ smb2_get_error(libsmb2->ctx)); ++ has_error = 1; ++ break; ++ } ++ if (read == 0) { ++ // eof ++ break; ++ } ++ buf1 += read; ++ buf_size1 -= read; ++ } ++ ++ int bytes_read = size - buf_size1; ++ if (bytes_read > 0) ++ av_application_did_io_tcp_read(libsmb2->app_ctx, (void*)h, bytes_read); ++ ++ return bytes_read ? bytes_read : (has_error ? AVERROR(ENOTCONN) : AVERROR_EOF); ++} ++ ++static int libsmb2_write(URLContext *h, const unsigned char *buf, int size) ++{ ++ LIBSMB2Context *libsmb2 = h->priv_data; ++ int bytes_written; ++ ++ if ((bytes_written = smb2_write(libsmb2->ctx, libsmb2->fh, buf, size)) < 0) { ++ int ret = AVERROR(errno); ++ av_log(h, AV_LOG_ERROR, "smb2 write failed: %s\n", strerror(errno)); ++ return ret; ++ } ++ ++ return bytes_written; ++} ++ ++static int libsmb2_delete(URLContext *h) ++{ ++ LIBSMB2Context *libsmb2 = h->priv_data; ++ ++ struct smb2_url *url = smb2_parse_url(libsmb2->ctx, h->filename); ++ if (url == NULL) { ++ av_log(h, AV_LOG_ERROR, "smb2 parse url failed: %s\n", ++ smb2_get_error(libsmb2->ctx)); ++ return -1; ++ } else { ++ char *path = ff_urldecode(url->path, 0); ++ return smb2_unlink(libsmb2->ctx, path); ++ } ++} ++ ++static int libsmb2_move(URLContext *h_src, URLContext *h_dst) ++{ ++ LIBSMB2Context *libsmb2 = h_src->priv_data; ++ if (!libsmb2) ++ { ++ return -1; ++ } ++ ++ struct smb2_url *src_url = smb2_parse_url(libsmb2->ctx, h_src->filename); ++ struct smb2_url *dst_url = smb2_parse_url(libsmb2->ctx, h_dst->filename); ++ ++ if (src_url == NULL || dst_url == NULL) { ++ av_log(h_src, AV_LOG_ERROR, "smb2 parse url failed: %s\n", smb2_get_error(libsmb2->ctx)); ++ return -2; ++ } else { ++ char *src_path = ff_urldecode(src_url->path, 0); ++ char *dst_path = ff_urldecode(dst_url->path, 0); ++ return smb2_rename(libsmb2->ctx, src_path, dst_path); ++ } ++} ++ ++static int libsmb2_open_dir(URLContext *h) ++{ ++ LIBSMB2Context *libsmb2 = h->priv_data; ++ struct smb2_url *url = smb2_parse_url(libsmb2->ctx, h->filename); ++ if (url == NULL) { ++ av_log(h, AV_LOG_ERROR, "smb2 parse url failed: %s\n", smb2_get_error(libsmb2->ctx)); ++ return -1; ++ } else { ++ char *path = ff_urldecode(url->path, 0); ++ libsmb2->dir = smb2_opendir(libsmb2->ctx, path); ++ if (!libsmb2->dir){ ++ av_log(h, AV_LOG_ERROR, "smb2 open dir failed: %s\n", smb2_get_error(libsmb2->ctx)); ++ return 0; ++ } ++ return AVERROR(ENOTDIR); ++ } ++} ++ ++static int libsmb2_read_dir(URLContext *h, AVIODirEntry **next) ++{ ++ LIBSMB2Context *libsmb2 = h->priv_data; ++ AVIODirEntry *entry; ++ struct smb2dirent *dirent = NULL; ++ int skip_entry; ++ ++ *next = entry = ff_alloc_dir_entry(); ++ if (!entry) ++ return AVERROR(ENOMEM); ++ do { ++ skip_entry = 0; ++ dirent = smb2_readdir(libsmb2->ctx, libsmb2->dir); ++ if (!dirent) { ++ av_freep(next); ++ return 0; ++ } ++ } while (skip_entry || !strcmp(dirent->name, ".") || ++ !strcmp(dirent->name, "..")); ++ ++ entry->name = av_strdup(dirent->name); ++ if (!entry->name) { ++ av_freep(next); ++ return AVERROR(ENOMEM); ++ } ++ ++ struct smb2_stat_64 st = dirent->st; ++ switch (st.smb2_type) { ++ case SMB2_TYPE_DIRECTORY: ++ entry->type = AVIO_ENTRY_DIRECTORY; ++ break; ++ case SMB2_TYPE_FILE: ++ entry->type = AVIO_ENTRY_FILE; ++ break; ++ case SMB2_TYPE_LINK: ++ entry->type = AVIO_ENTRY_SYMBOLIC_LINK; ++ break; ++ default: ++ entry->type = AVIO_ENTRY_UNKNOWN; ++ break; ++ } ++ ++ entry->group_id = -1; ++ entry->user_id = -1; ++ entry->filemode = -1; ++ entry->size = st.smb2_size; ++ entry->modification_timestamp = INT64_C(1000000) * st.smb2_mtime; ++ entry->access_timestamp = INT64_C(1000000) * st.smb2_atime; ++ entry->status_change_timestamp = INT64_C(1000000) * st.smb2_ctime; ++ ++ return 0; ++} ++ ++static int libsmb2_close_dir(URLContext *h) ++{ ++ LIBSMB2Context *libsmb2 = h->priv_data; ++ if (libsmb2->dir) { ++ smb2_closedir(libsmb2->ctx, libsmb2->dir); ++ libsmb2->dir = NULL; ++ } ++ return 0; ++} ++ ++#define OFFSET(x) offsetof(LIBSMB2Context, x) ++#define D AV_OPT_FLAG_DECODING_PARAM ++#define E AV_OPT_FLAG_ENCODING_PARAM ++static const AVOption options[] = { ++ { "ijkapplication", "AVApplicationContext", OFFSET(app_ctx_intptr), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, .flags = D }, ++ { "smb2_seal", "enable smb3 encrypted connection", OFFSET(smb2_seal), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, .flags = D }, ++ {NULL} ++}; ++ ++static const AVClass libsmb2lient_context_class = { ++ .class_name = "libsmb2", ++ .item_name = av_default_item_name, ++ .option = options, ++ .version = LIBAVUTIL_VERSION_INT, ++}; ++ ++const URLProtocol ff_libsmb2_protocol = { ++ .name = "smb2", ++ .flags = URL_PROTOCOL_FLAG_NETWORK, ++ .priv_data_size = sizeof(LIBSMB2Context), ++ .priv_data_class = &libsmb2lient_context_class, ++ .url_open = libsmb2_open, ++ .url_read = libsmb2_read, ++ .url_write = libsmb2_write, ++ .url_seek = libsmb2_seek, ++ .url_close = libsmb2_close, ++ .url_delete = libsmb2_delete, ++ .url_move = libsmb2_move, ++ .url_open_dir = libsmb2_open_dir, ++ .url_read_dir = libsmb2_read_dir, ++ .url_close_dir = libsmb2_close_dir, ++}; +diff --git a/libavformat/protocols.c b/libavformat/protocols.c +index f5eb0db..ba08718 100644 +--- a/libavformat/protocols.c ++++ b/libavformat/protocols.c +@@ -75,6 +75,7 @@ extern const URLProtocol ff_librtmpte_protocol; + extern const URLProtocol ff_libsrt_protocol; + extern const URLProtocol ff_libssh_protocol; + extern const URLProtocol ff_libsmbclient_protocol; ++extern const URLProtocol ff_libsmb2_protocol; + extern const URLProtocol ff_libzmq_protocol; + extern const URLProtocol ff_ipfs_gateway_protocol; + extern const URLProtocol ff_ipns_gateway_protocol; +-- +2.39.5 (Apple Git-154) + diff --git a/patches/ffmpeg-n7.1.1/0025-URLProtocol-add-url_parse_priv-function-pointer.patch b/patches/ffmpeg-n7.1.1/0025-URLProtocol-add-url_parse_priv-function-pointer.patch new file mode 100644 index 000000000..723c819bd --- /dev/null +++ b/patches/ffmpeg-n7.1.1/0025-URLProtocol-add-url_parse_priv-function-pointer.patch @@ -0,0 +1,55 @@ +From e9eb133958e3c646b8e4232e0a05a96bfea4d56e Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Thu, 29 May 2025 11:14:13 +0800 +Subject: [PATCH] URLProtocol add url_parse_priv function pointer + +--- + libavformat/demux.c | 11 +++++++++++ + libavformat/url.h | 2 ++ + 2 files changed, 13 insertions(+) + +diff --git a/libavformat/demux.c b/libavformat/demux.c +index c0d744d..9ce4525 100644 +--- a/libavformat/demux.c ++++ b/libavformat/demux.c +@@ -341,6 +341,17 @@ int avformat_open_input(AVFormatContext **ps, const char *filename, + ff_id3v2_free_extra_meta(&id3v2_extra_meta); + } + ++ //fill stream info ++ if (s->pb) { ++ URLContext *url_context = ffio_geturlcontext(s->pb); ++ if (url_context && url_context->prot) { ++ URLProtocol *prot = url_context->prot; ++ if (prot->url_parse_priv) { ++ prot->url_parse_priv(s, url_context); ++ } ++ } ++ } ++ + if ((ret = avformat_queue_attached_pictures(s)) < 0) + goto close; + +diff --git a/libavformat/url.h b/libavformat/url.h +index 53c6f13..f7e5bb2 100644 +--- a/libavformat/url.h ++++ b/libavformat/url.h +@@ -48,6 +48,7 @@ typedef struct URLContext { + int min_packet_size; /**< if non zero, the stream is packetized with this min packet size */ + } URLContext; + ++typedef struct AVFormatContext AVFormatContext; + typedef struct URLProtocol { + const char *name; + int (*url_open)( URLContext *h, const char *url, int flags); +@@ -93,6 +94,7 @@ typedef struct URLProtocol { + int (*url_close_dir)(URLContext *h); + int (*url_delete)(URLContext *h); + int (*url_move)(URLContext *h_src, URLContext *h_dst); ++ int (*url_parse_priv)(AVFormatContext *ic, URLContext *h); + const char *default_whitelist; + } URLProtocol; + +-- +2.39.5 (Apple Git-154) + diff --git a/patches/ffmpeg-n7.1.1/0026-bluray-protocol-add-dvd-fallback.patch b/patches/ffmpeg-n7.1.1/0026-bluray-protocol-add-dvd-fallback.patch new file mode 100644 index 000000000..4fcfdec5b --- /dev/null +++ b/patches/ffmpeg-n7.1.1/0026-bluray-protocol-add-dvd-fallback.patch @@ -0,0 +1,54 @@ +From f57d809f553431b46a023a69035c30b0485d207d Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Thu, 29 May 2025 11:15:16 +0800 +Subject: [PATCH] bluray protocol add dvd fallback + +--- + libavformat/demux.c | 23 +++++++++++++++++++---- + 1 file changed, 19 insertions(+), 4 deletions(-) + +diff --git a/libavformat/demux.c b/libavformat/demux.c +index 9ce4525..d8aa824 100644 +--- a/libavformat/demux.c ++++ b/libavformat/demux.c +@@ -159,7 +159,8 @@ static int init_input(AVFormatContext *s, const char *filename, + int ret; + AVProbeData pd = { filename, NULL, 0 }; + int score = AVPROBE_SCORE_RETRY; +- ++ AVDictionary *tmp_opts = NULL; ++ + if (s->pb) { + s->flags |= AVFMT_FLAG_CUSTOM_IO; + if (!s->iformat) +@@ -174,10 +175,24 @@ static int init_input(AVFormatContext *s, const char *filename, + if ((s->iformat && s->iformat->flags & AVFMT_NOFILE) || + (!s->iformat && (s->iformat = av_probe_input_format2(&pd, 0, &score)))) + return score; ++ ++ if (options && av_stristart(filename, "bluray://", NULL)) { ++ av_dict_copy(&tmp_opts, *options, 0); ++ } + +- if ((ret = s->io_open(s, &s->pb, filename, AVIO_FLAG_READ | s->avio_flags, options)) < 0) +- return ret; +- ++ if ((ret = s->io_open(s, &s->pb, filename, AVIO_FLAG_READ | s->avio_flags, ++ options)) < 0) { ++ if (av_stristart(filename, "bluray://", NULL)) { ++ const char *a_name = av_strireplace(filename, "bluray://", ""); ++ ret = init_input(s, a_name, &tmp_opts); ++ av_dict_free(&tmp_opts); ++ return ret; ++ } else { ++ av_dict_free(&tmp_opts); ++ return ret; ++ } ++ } ++ av_dict_free(&tmp_opts); + if (s->iformat) + return 0; + return av_probe_input_buffer2(s->pb, &s->iformat, filename, +-- +2.39.5 (Apple Git-154) + diff --git a/patches/ffmpeg-n7.1.1/0027-custom-bluray-fs-for-network-Blu-ray-Disc-and-BDMV.patch b/patches/ffmpeg-n7.1.1/0027-custom-bluray-fs-for-network-Blu-ray-Disc-and-BDMV.patch new file mode 100644 index 000000000..f0a7f70d1 --- /dev/null +++ b/patches/ffmpeg-n7.1.1/0027-custom-bluray-fs-for-network-Blu-ray-Disc-and-BDMV.patch @@ -0,0 +1,744 @@ +From 5582bd1c8aa667f9dc0ca6c14ffb916e7a666c0e Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Tue, 12 May 2026 16:26:37 +0800 +Subject: [PATCH] custom bluray fs for network Blu-ray Disc and BDMV + +--- + libavformat/Makefile | 2 +- + libavformat/bluray.c | 128 ++++++++- + libavformat/bluray_custom_fs.c | 459 +++++++++++++++++++++++++++++++++ + libavformat/bluray_custom_fs.h | 33 +++ + 4 files changed, 610 insertions(+), 12 deletions(-) + create mode 100644 libavformat/bluray_custom_fs.c + create mode 100644 libavformat/bluray_custom_fs.h + +diff --git a/libavformat/Makefile b/libavformat/Makefile +index e299e72..1f32e4c 100644 +--- a/libavformat/Makefile ++++ b/libavformat/Makefile +@@ -679,7 +679,7 @@ OBJS-$(CONFIG_VAPOURSYNTH_DEMUXER) += vapoursynth.o + OBJS-$(CONFIG_ANDROID_CONTENT_PROTOCOL) += file.o + OBJS-$(CONFIG_ASYNC_PROTOCOL) += async.o + OBJS-$(CONFIG_APPLEHTTP_PROTOCOL) += hlsproto.o +-OBJS-$(CONFIG_BLURAY_PROTOCOL) += bluray.o ++OBJS-$(CONFIG_BLURAY_PROTOCOL) += bluray.o bluray_custom_fs.o + OBJS-$(CONFIG_CACHE_PROTOCOL) += cache.o + OBJS-$(CONFIG_CONCAT_PROTOCOL) += concat.o + OBJS-$(CONFIG_CONCATF_PROTOCOL) += concat.o +diff --git a/libavformat/bluray.c b/libavformat/bluray.c +index 1845551..eb43340 100644 +--- a/libavformat/bluray.c ++++ b/libavformat/bluray.c +@@ -21,23 +21,27 @@ + */ + + #include +- ++#include "libavformat/urldecode.h" + #include "libavutil/avstring.h" + #include "libavformat/url.h" + #include "libavutil/opt.h" ++#include "bluray_custom_fs.h" ++#include "libavutil/dict.h" ++#include "libavformat/avformat.h" + +-#define BLURAY_PROTO_PREFIX "bluray:" ++#define BLURAY_PROTO_PREFIX "bluray://" + #define MIN_PLAYLIST_LENGTH 180 /* 3 min */ + + typedef struct { + const AVClass *class; + + BLURAY *bd; +- ++ fs_access *access; + int playlist; + int angle; + int chapter; + /*int region;*/ ++ int title_idx; + } BlurayContext; + + #define OFFSET(x) offsetof(BlurayContext, x) +@@ -106,23 +110,58 @@ static int bluray_close(URLContext *h) + if (bd->bd) { + bd_close(bd->bd); + } +- ++ destroy_bluray_custom_access(&bd->access); + return 0; + } + +-static int bluray_open(URLContext *h, const char *path, int flags) ++#ifdef DEBUG_BLURAY ++#include ++#define BLURAY_DEBUG_MASK 0xFFFFF //(0xFFFFF & ~DBG_STREAM) ++ ++static void bluray_DebugHandler(const char *psz) + { ++ size_t len = strlen(psz); ++ if(len < 1) return; ++ av_log(NULL, AV_LOG_INFO, "[bluray] %s\n",psz); ++} ++#endif ++ ++ ++static int bluray_open(URLContext *h, const char *path, int flags, AVDictionary **options) ++{ ++#ifdef DEBUG_BLURAY ++ bd_set_debug_mask(BLURAY_DEBUG_MASK); ++ bd_set_debug_handler(bluray_DebugHandler); ++#endif ++ + BlurayContext *bd = h->priv_data; + int num_title_idx; + const char *diskname = path; + + av_strstart(path, BLURAY_PROTO_PREFIX, &diskname); + +- bd->bd = bd_open(diskname, NULL); +- if (!bd->bd) { ++ diskname = ff_urldecode(diskname, 0); ++ ++ fs_access *access = NULL; ++ int is_file = av_strstart(diskname, "file://", NULL) || av_strstart(diskname, "/", NULL); ++ ++ // file protocl can't handle AVIO_FLAG_DIRECT flag,so file not create custom access ++ if (!is_file) { ++ //set read packet buffer size is important! the default packet size is 32768, when use smb2 protocol, download speed is limited to 2MB; but when set the size to 1048576, download speed is 16MB; ++ h->max_packet_size = 1048576; ++ access = create_bluray_custom_access(diskname, options, &h->interrupt_callback); ++ } ++ ++ bd->bd = bd_open_fs(diskname, NULL, access); ++ ++ if (!bd->bd || is_bluray_custom_access_cancelled(access)) { + av_log(h, AV_LOG_ERROR, "bd_open() failed\n"); ++ if (access) { ++ destroy_bluray_custom_access(&access); ++ } + return AVERROR(EIO); + } ++ bd->access = access; + + /* check if disc can be played */ + if (check_disc_info(h) < 0) { +@@ -150,6 +189,9 @@ static int bluray_open(URLContext *h, const char *path, int flags) + int i; + for (i = 0; i < num_title_idx; i++) { + BLURAY_TITLE_INFO *info = bd_get_title_info(bd->bd, i, 0); ++ if (!info) { ++ continue; ++ } + + av_log(h, AV_LOG_INFO, "playlist %05d.mpls (%d:%02d:%02d)\n", + info->playlist, +@@ -159,17 +201,19 @@ static int bluray_open(URLContext *h, const char *path, int flags) + + if (info->duration > duration) { + bd->playlist = info->playlist; ++ bd->title_idx = i; + duration = info->duration; + } + + bd_free_title_info(info); + } +- av_log(h, AV_LOG_INFO, "selected %05d.mpls\n", bd->playlist); ++ av_log(h, AV_LOG_INFO, "select longest playlist: %05d.mpls\n", bd->playlist); + } + + /* select playlist */ +- if (bd_select_playlist(bd->bd, bd->playlist) <= 0) { +- av_log(h, AV_LOG_ERROR, "bd_select_playlist(%05d.mpls) failed\n", bd->playlist); ++ if (bd->playlist >= 0 && bd_select_playlist(bd->bd, bd->playlist) <= 0) { ++ av_log(h, AV_LOG_ERROR, "bd_select_playlist(%05d.mpls) failed\n", ++ bd->playlist); + return AVERROR(EIO); + } + +@@ -222,13 +266,75 @@ static int64_t bluray_seek(URLContext *h, int64_t pos, int whence) + return AVERROR(EINVAL); + } + ++static int bluray_parse_priv(AVFormatContext *ic, URLContext *h) ++{ ++ BlurayContext *bd = h->priv_data; ++ BLURAY_TITLE_INFO *title_info = NULL; ++ BLURAY_CLIP_INFO clip_info; ++ ++ int v_idx = 0; ++ int a_idx = 0; ++ int s_idx = 0; ++ int ret = 0; ++ ++ if (!bd || !bd->bd) { ++ return AVERROR(EFAULT); ++ } ++ ++ title_info = bd_get_title_info(bd->bd, bd->title_idx, 0); ++ if (!title_info) { ++ return AVERROR(EFAULT); ++ } ++ ++ if (title_info->clip_count <= 0) { ++ ret = EFAULT; ++ goto fail; ++ } ++ clip_info = title_info->clips[0]; ++ ++ for (int i = 0; i < ic->nb_streams; i++) { ++ if (ic->streams[i] && ic->streams[i]->codecpar) { ++ switch (ic->streams[i]->codecpar->codec_type) { ++ case AVMEDIA_TYPE_VIDEO: ++ if (v_idx < clip_info.video_stream_count) { ++ av_log(h, AV_LOG_INFO, "video stream %d lang = %s\n", v_idx, clip_info.video_streams[v_idx].lang); ++ av_dict_set(&ic->streams[i]->metadata, "language", clip_info.video_streams[v_idx].lang, AV_DICT_DONT_OVERWRITE); ++ v_idx++; ++ } ++ break; ++ case AVMEDIA_TYPE_AUDIO: ++ if (a_idx < clip_info.audio_stream_count) { ++ av_log(h, AV_LOG_INFO, "audio stream %d lang = %s\n", a_idx, clip_info.audio_streams[a_idx].lang); ++ av_dict_set(&ic->streams[i]->metadata, "language", clip_info.audio_streams[a_idx].lang, AV_DICT_DONT_OVERWRITE); ++ a_idx++; ++ } ++ break; ++ case AVMEDIA_TYPE_SUBTITLE: ++ if (s_idx < clip_info.pg_stream_count) { ++ av_log(h, AV_LOG_INFO, "subtitle stream %d lang = %s\n", s_idx, clip_info.pg_streams[s_idx].lang); ++ av_dict_set(&ic->streams[i]->metadata, "language", clip_info.pg_streams[s_idx].lang, AV_DICT_DONT_OVERWRITE); ++ s_idx++; ++ } ++ break; ++ default: ++ break; ++ } ++ } ++ } ++ ++fail: ++ bd_free_title_info(title_info); ++ ++ return ret != 0 ? AVERROR(ret) : 0; ++} + + const URLProtocol ff_bluray_protocol = { + .name = "bluray", + .url_close = bluray_close, +- .url_open = bluray_open, ++ .url_open2 = bluray_open, + .url_read = bluray_read, + .url_seek = bluray_seek, ++ .url_parse_priv = bluray_parse_priv, + .priv_data_size = sizeof(BlurayContext), + .priv_data_class = &bluray_context_class, + }; +diff --git a/libavformat/bluray_custom_fs.c b/libavformat/bluray_custom_fs.c +new file mode 100644 +index 0000000..b82e0b2 +--- /dev/null ++++ b/libavformat/bluray_custom_fs.c +@@ -0,0 +1,459 @@ ++// ++// bluray_custom_fs_smb2.c ++// ++// Created by Reach Matt on 2024/9/13. ++// ++// ++// Copyright (C) 2021 Matt Reach// ++// Licensed under the Apache License, Version 2.0 (the "License"); ++// you may not use this file except in compliance with the License. ++// You may obtain a copy of the License at ++// ++// http://www.apache.org/licenses/LICENSE-2.0 ++// ++// Unless required by applicable law or agreed to in writing, software ++// distributed under the License is distributed on an "AS IS" BASIS, ++// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++// See the License for the specific language governing permissions and ++// limitations under the License. ++ ++#include "bluray_custom_fs.h" ++#include "url.h" ++#include "application.h" ++#include "libavutil/mem.h" ++#include "libavutil/error.h" ++#include "libavutil/avstring.h" ++#include ++#include ++ ++#ifndef UDF_BLOCK_SIZE ++# define UDF_BLOCK_SIZE 2048 ++#endif ++ ++typedef struct ff_builtin_io { ++ URLContext *url_context; ++ int64_t offset; ++ AVApplicationContext *app_ctx; ++ int64_t last_read; ++} ff_builtin_io; ++ ++typedef struct ff_bluray_access { ++ const char *url; ++ AVDictionary *opts; ++ ff_builtin_io *io; ++ const AVIOInterruptCB *int_cb; ++ int cancel; ++} ff_bluray_access; ++ ++static void close_builtin_io(ff_builtin_io *io) ++{ ++ if (!io) { ++ return; ++ } ++ if (io->url_context) { ++ ffurl_closep(&io->url_context); ++ } ++} ++ ++static int create_builtin_io(ff_builtin_io **io, const char *url, ++ AVDictionary **opts, int is_dir, ++ const AVIOInterruptCB *int_cb) ++{ ++ if (!io) { ++ return -1; ++ } ++ ++ ff_builtin_io *app = av_mallocz(sizeof(ff_builtin_io)); ++ if (!app) { ++ return -2; ++ } ++ ++ const char *protocol_whitelist = NULL; ++ ++ if (opts) { ++ const AVDictionary *dict = *opts; ++ ++ if (!av_strstart(url, "http", NULL) && ++ !av_strstart(url, "smb2", NULL)) { ++ AVDictionaryEntry *app_dict = ++ av_dict_get(dict, "ijkapplication", NULL, 0); ++ if (app_dict) { ++ app->app_ctx = ++ (AVApplicationContext *)av_dict_strtoptr(app_dict->value); ++ av_application_will_http_open(app->app_ctx, NULL, url); ++ } ++ } ++ ++ AVDictionaryEntry *proto_dict = ++ av_dict_get(dict, "protocol_whitelist", NULL, 0); ++ if (proto_dict) { ++ protocol_whitelist = av_strdup(proto_dict->value); ++ } ++ } ++ ++ if (protocol_whitelist == NULL || strlen(protocol_whitelist) == 0) { ++ protocol_whitelist = "ijkio,ijkhttphook,http,tcp,https,tls,file,smb2"; ++ } ++ ++ int flags = AVIO_FLAG_READ; ++ if (is_dir) { ++ flags |= AVIO_FLAG_DIRECT; ++ } ++ ++ int ret = ffurl_open_whitelist(&app->url_context, url, flags, int_cb, opts, ++ protocol_whitelist, NULL, NULL); ++ ++ av_application_did_http_open(app->app_ctx, (void *)app->url_context, url, ++ ret < 0 ? AVERROR(errno) : 0, ++ ret < 0 ? 500 : 200, 0); ++ if (ret < 0) { ++ close_builtin_io(app); ++ av_freep(&app); ++ } ++ *io = app; ++ return ret; ++} ++ ++static int64_t seek_builtin_io(ff_builtin_io *io, int64_t offset, int origin) ++{ ++ if (!io) { ++ return 0; ++ } ++ ++ if (io->offset == offset && origin == SEEK_SET) { ++ io->last_read = offset; ++ return offset; ++ } ++ ++ av_application_will_http_seek(io->app_ctx, (void *)io->url_context, ++ io->url_context->filename, offset); ++ ++ int64_t pos = ++ io->url_context->prot->url_seek(io->url_context, offset, origin); ++ if (pos < 0) { ++ av_application_did_http_seek(io->app_ctx, (void *)io->url_context, ++ io->url_context->filename, offset, ++ AVERROR(errno), 500); ++ return AVERROR(errno); ++ } ++ io->last_read = pos; ++ io->offset = pos; ++ av_application_did_http_seek(io->app_ctx, (void *)io->url_context, ++ io->url_context->filename, offset, 0, 200); ++ return pos; ++} ++ ++static int write_builtin_io(ff_builtin_io *io, uint8_t *buf, int buf_size) ++{ ++ if (!io) { ++ return 0; ++ } ++ ++ return io->url_context->prot->url_write(io->url_context, buf, buf_size); ++} ++ ++static int read_builtin_io(ff_builtin_io *io, uint8_t *buf, int buf_size, ++ int64_t pos) ++{ ++ if (!io) { ++ return 0; ++ } ++ ++ uint8_t *buf1 = buf; ++ int buf_size1 = buf_size; ++ int read = 0; ++ ++ seek_builtin_io(io, pos, SEEK_SET); ++ ++ while (buf_size1 > 0) { ++ read = ++ io->url_context->prot->url_read(io->url_context, buf1, buf_size1); ++ if (read <= 0) { ++ // maybe AVERROR_EOF ++ break; ++ } ++ io->offset += read; ++ buf1 += read; ++ buf_size1 -= read; ++ } ++ if (read == AVERROR_EXIT) { ++ return AVERROR_EXIT; ++ } ++ int bytes = buf_size - buf_size1; ++ if (bytes == 0 && read == AVERROR_EOF) { ++ return AVERROR_EOF; ++ } else { ++ av_application_did_io_tcp_read(io->app_ctx, (void *)io->url_context, ++ bytes); ++ return bytes; ++ } ++} ++ ++static int read_blocks(void *fs_handle, void *buf, int lba, int num_blocks) ++{ ++ ff_bluray_access *access = fs_handle; ++ if (access->cancel == 1) { ++ return AVERROR_EXIT; ++ } ++ ++ ff_builtin_io *io = access->io; ++ if (!io) { ++ return -1; ++ } ++ int got = -1; ++ int64_t pos = (int64_t)lba * UDF_BLOCK_SIZE; ++ int buf_size = num_blocks * UDF_BLOCK_SIZE; ++ int bytes = read_builtin_io(io, buf, buf_size, pos); ++ if (bytes == AVERROR_EXIT) { ++ access->cancel = 1; ++ return AVERROR_EXIT; ++ } ++ ++ if (bytes > 0) { ++ got = (int)(bytes / UDF_BLOCK_SIZE); ++ } ++ return got; ++} ++ ++void destroy_bluray_custom_access(fs_access **p) ++{ ++ if (p) { ++ fs_access *access = *p; ++ if (access) { ++ ff_bluray_access* ba = access->fs_handle; ++ ff_builtin_io *io = ba->io; ++ if (io) { ++ close_builtin_io(io); ++ av_freep(&io); ++ } ++ av_free(ba->url); ++ av_dict_free(&ba->opts); ++ } ++ av_freep(p); ++ } ++} ++ ++// ------------------------------------------------------------------------------------------- ++// open_file for bdmv ++ ++static void _file_close(BD_FILE_H *file) ++{ ++ if (file) { ++ ff_builtin_io *io = file->internal; ++ if (io) { ++ close_builtin_io(io); ++ av_free(io); ++ file->internal = NULL; ++ } ++ av_freep(&file); ++ } ++} ++ ++static int64_t _file_read(BD_FILE_H *file, uint8_t *buf, int64_t size) ++{ ++ if (size <= 0) { ++ return 0; ++ } ++ ff_builtin_io *io = file->internal; ++ if (!io) { ++ return -1; ++ } ++ ++ int read = read_builtin_io(io, buf, size, io->last_read); ++ ++ if (read > 0) { ++ io->last_read += read; ++ } ++ ++ return read; ++} ++ ++static int64_t _file_write(BD_FILE_H *file, uint8_t *buf, int64_t size) ++{ ++ if (size <= 0) { ++ return 0; ++ } ++ ff_builtin_io *io = file->internal; ++ if (!io) { ++ return -1; ++ } ++ return write_builtin_io(io, buf, size); ++} ++ ++// origin: SEEK_SET, SEEK_CUR or SEEK_END ++static int64_t _file_seek(BD_FILE_H *file, int64_t offset, int32_t origin) ++{ ++ ff_builtin_io *io = file->internal; ++ if (!io) { ++ return -1; ++ } ++ return seek_builtin_io(io, offset, origin); ++} ++ ++static int64_t _file_tell(BD_FILE_H *file) ++{ ++ ff_builtin_io *io = file->internal; ++ if (!io) { ++ return -1; ++ } ++ return io->last_read; ++ // return seek_builtin_io(io, 0, SEEK_CUR); ++} ++ ++static struct bd_file_s *open_file(void *fs_handle, const char *rel_path) ++{ ++ ff_bluray_access *access = fs_handle; ++ ++ if (access->cancel == 1) { ++ return NULL; ++ } ++ ++ const char *url = av_append_path_component(access->url, rel_path); ++ if (!url) { ++ return NULL; ++ } ++ AVDictionary *opts = NULL; ++ av_dict_copy(&opts, access->opts, 0); ++ ++ ff_builtin_io *io = NULL; ++ int ret = create_builtin_io(&io, url, &opts, 0, access->int_cb); ++ av_dict_free(&opts); ++ ++ if (0 != ret) { ++ av_log(NULL, AV_LOG_ERROR, "[bluray] can't open %s,error:%s", url, ++ av_err2str(ret)); ++ av_free(url); ++ return NULL; ++ } ++ av_free(url); ++ BD_FILE_H *file = av_malloc(sizeof(BD_FILE_H)); ++ if (!file) { ++ close_builtin_io(io); ++ av_free(io); ++ return NULL; ++ } ++ ++ file->internal = io; ++ file->close = _file_close; ++ file->seek = _file_seek; ++ file->read = _file_read; ++ file->write = _file_write; ++ file->tell = _file_tell; ++ ++ return file; ++} ++ ++// open_dir for bdmv ++static void _dir_close(BD_DIR_H *dir) ++{ ++ if (dir) { ++ ff_builtin_io *io = dir->internal; ++ if (!io) { ++ return; ++ } ++ close_builtin_io(io); ++ av_free(io); ++ dir->internal = NULL; ++ av_freep(&dir); ++ } ++} ++ ++static int _dir_read(BD_DIR_H *dir, BD_DIRENT *entry) ++{ ++ ff_builtin_io *io = dir->internal; ++ if (!io) { ++ return -1; ++ } ++ ++ AVIODirEntry *next = NULL; ++ ++ if (io->url_context->prot->url_read_dir(io->url_context, &next) < 0 || !next) { ++ return -2; ++ } ++ ++ strncpy(entry->d_name, next->name, sizeof(entry->d_name)); ++ entry->d_name[sizeof(entry->d_name) - 1] = 0; ++ ++ return 0; ++} ++ ++static struct bd_dir_s* open_dir (void *fs_handle, const char *rel_path) ++{ ++ ff_bluray_access *access = fs_handle; ++ ++ if (access->cancel == 1) { ++ return NULL; ++ } ++ ++ const char *url = av_append_path_component(access->url, rel_path); ++ if (!url) { ++ return NULL; ++ } ++ AVDictionary *opts = NULL; ++ av_dict_copy(&opts, access->opts, 0); ++ ++ ff_builtin_io *io = NULL; ++ int ret = create_builtin_io(&io, url, &opts, 1, access->int_cb); ++ av_dict_free(&opts); ++ ++ if (0 != ret) { ++ av_log(NULL, AV_LOG_ERROR, "[bluray] can't open dir %s,error:%s", url, ++ av_err2str(ret)); ++ av_free(url); ++ return NULL; ++ } ++ av_free(url); ++ BD_DIR_H *dir = av_malloc(sizeof(BD_DIR_H)); ++ if (!dir) { ++ close_builtin_io(io); ++ av_free(io); ++ return NULL; ++ } ++ ++ dir->internal = io; ++ dir->close = _dir_close; ++ dir->read = _dir_read; ++ ++ return dir; ++} ++ ++int is_bluray_custom_access_cancelled(fs_access *access) ++{ ++ if (!access) { ++ return 0; ++ } ++ ++ ff_bluray_access *opaque = access->fs_handle; ++ return opaque->cancel; ++} ++ ++// 构建fs_access结构体 ++fs_access * create_bluray_custom_access(const char *url, AVDictionary **options, const AVIOInterruptCB *int_cb) ++{ ++ ff_bluray_access * opaque = av_mallocz(sizeof(ff_bluray_access)); ++ if (!opaque) { ++ return NULL; ++ } ++ ++ if (opaque) { ++ opaque->url = av_strdup(url); ++ if (options) { ++ av_dict_copy(&opaque->opts, *options, 0); ++ } ++ opaque->int_cb = int_cb; ++ int ret = create_builtin_io(&opaque->io, url, options, 0, int_cb); ++ if (0 != ret) { ++ av_log(NULL, AV_LOG_ERROR, "[bluray] can't open file %s,error:%s", ++ url, av_err2str(ret)); ++ } ++ ++ fs_access *access = av_malloc(sizeof(fs_access)); ++ access->fs_handle = opaque; ++ access->read_blocks = read_blocks; ++ access->open_file = open_file; ++ access->open_dir = open_dir; ++ ++ return access; ++ } ++ return NULL; ++} +\ No newline at end of file +diff --git a/libavformat/bluray_custom_fs.h b/libavformat/bluray_custom_fs.h +new file mode 100644 +index 0000000..058d2da +--- /dev/null ++++ b/libavformat/bluray_custom_fs.h +@@ -0,0 +1,33 @@ ++// ++// bluray_custom_fs.h ++// ++// Created by Reach Matt on 2024/9/13. ++// ++// ++// Copyright (C) 2021 Matt Reach// ++// Licensed under the Apache License, Version 2.0 (the "License"); ++// you may not use this file except in compliance with the License. ++// You may obtain a copy of the License at ++// ++// http://www.apache.org/licenses/LICENSE-2.0 ++// ++// Unless required by applicable law or agreed to in writing, software ++// distributed under the License is distributed on an "AS IS" BASIS, ++// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++// See the License for the specific language governing permissions and ++// limitations under the License. ++ ++#ifndef bluray_custom_fs_h ++#define bluray_custom_fs_h ++ ++#include ++ ++typedef struct fs_access fs_access; ++typedef struct AVDictionary AVDictionary; ++typedef struct AVIOInterruptCB AVIOInterruptCB; ++ ++void destroy_bluray_custom_access(fs_access **p); ++int is_bluray_custom_access_cancelled(fs_access *access); ++fs_access *create_bluray_custom_access(const char *url, AVDictionary **options, ++ const AVIOInterruptCB *int_cb); ++#endif /* bluray_custom_fs_h */ +\ No newline at end of file +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n7.1.1/0028-bluray-open-and-find-the-right-m2ts-then-read-seek-i.patch b/patches/ffmpeg-n7.1.1/0028-bluray-open-and-find-the-right-m2ts-then-read-seek-i.patch new file mode 100644 index 000000000..a6591dfa4 --- /dev/null +++ b/patches/ffmpeg-n7.1.1/0028-bluray-open-and-find-the-right-m2ts-then-read-seek-i.patch @@ -0,0 +1,71 @@ +From 6e7abe54b59f8227df2c1149665b314cd2ac5892 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Tue, 12 May 2026 11:45:56 +0800 +Subject: [PATCH] bluray open and find the right m2ts, then read/seek it + direactly instread of bluray logic. + +--- + libavformat/bluray.c | 25 ++++++++++++++++++++----- + 1 file changed, 20 insertions(+), 5 deletions(-) + +diff --git a/libavformat/bluray.c b/libavformat/bluray.c +index cd50523..7fdffdd 100644 +--- a/libavformat/bluray.c ++++ b/libavformat/bluray.c +@@ -42,6 +42,7 @@ typedef struct { + int chapter; + /*int region;*/ + int title_idx; ++ int stream_opened; + } BlurayContext; + + #define OFFSET(x) offsetof(BlurayContext, x) +@@ -222,7 +223,8 @@ static int bluray_open(URLContext *h, const char *path, int flags, AVDictionary + if (bd->chapter > 1) { + bd_seek_chapter(bd->bd, bd->chapter - 1); + } +- ++ /* will read ts soon */ ++ bd->stream_opened = 1; + return 0; + } + +@@ -234,7 +236,13 @@ static int bluray_read(URLContext *h, unsigned char *buf, int size) + if (!bd || !bd->bd) { + return AVERROR(EFAULT); + } +- ++ if (bd->stream_opened) { ++ int read = (int)bd_file_read(bd->bd, buf, size); ++ if (read == 0) { ++ return AVERROR_EOF; ++ } ++ return read; ++ } + len = bd_read(bd->bd, buf, size); + + return len == 0 ? AVERROR_EOF : len; +@@ -252,10 +260,17 @@ static int64_t bluray_seek(URLContext *h, int64_t pos, int whence) + case SEEK_SET: + case SEEK_CUR: + case SEEK_END: +- return bd_seek(bd->bd, pos); +- ++ if (bd->stream_opened) { ++ return bd_file_seek(bd->bd, pos, whence); ++ } else { ++ return bd_seek(bd->bd, pos); ++ } + case AVSEEK_SIZE: +- return bd_get_title_size(bd->bd); ++ if (bd->stream_opened) { ++ return bd_file_size(bd->bd); ++ } else { ++ return bd_get_title_size(bd->bd); ++ } + } + + av_log(h, AV_LOG_ERROR, "Unsupported whence operation %d\n", whence); +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n7.1.1/0029-fix-android-ffmpeg7-test.c-1-10-fatal-error-libxml2-.patch b/patches/ffmpeg-n7.1.1/0029-fix-android-ffmpeg7-test.c-1-10-fatal-error-libxml2-.patch new file mode 100644 index 000000000..6b3c9421f --- /dev/null +++ b/patches/ffmpeg-n7.1.1/0029-fix-android-ffmpeg7-test.c-1-10-fatal-error-libxml2-.patch @@ -0,0 +1,26 @@ +From 9c71daf077d488023f72d1d27eb1283bfecce00f Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Fri, 6 Jun 2025 14:09:50 +0800 +Subject: [PATCH] fix android ffmpeg7 test.c:1:10: fatal error: + 'libxml2/libxml/xmlversion.h' + +--- + configure | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/configure b/configure +index f74d4de..7daa9ff 100755 +--- a/configure ++++ b/configure +@@ -7063,7 +7063,7 @@ enabled libzmq && require_pkg_config libzmq "libzmq >= 4.2.1" zmq.h z + enabled libzvbi && require_pkg_config libzvbi zvbi-0.2 libzvbi.h vbi_decoder_new && + { test_cpp_condition libzvbi.h "VBI_VERSION_MAJOR > 0 || VBI_VERSION_MINOR > 2 || VBI_VERSION_MINOR == 2 && VBI_VERSION_MICRO >= 28" || + enabled gpl || die "ERROR: libzvbi requires version 0.2.28 or --enable-gpl."; } +-enabled libxml2 && require_pkg_config libxml2 libxml-2.0 libxml2/libxml/xmlversion.h xmlCheckVersion ++enabled libxml2 && require_pkg_config libxml2 libxml-2.0 libxml/xmlversion.h xmlCheckVersion + enabled mbedtls && { check_pkg_config mbedtls mbedtls mbedtls/x509_crt.h mbedtls_x509_crt_init || + check_pkg_config mbedtls mbedtls mbedtls/ssl.h mbedtls_ssl_init || + check_lib mbedtls mbedtls/ssl.h mbedtls_ssl_init -lmbedtls -lmbedx509 -lmbedcrypto || +-- +2.39.5 (Apple Git-154) + diff --git a/patches/ffmpeg-n7.1.1/0030-fix-dash-init-fragment-url-is-invalid-truncated-bug.patch b/patches/ffmpeg-n7.1.1/0030-fix-dash-init-fragment-url-is-invalid-truncated-bug.patch new file mode 100644 index 000000000..3d4cb7d3c --- /dev/null +++ b/patches/ffmpeg-n7.1.1/0030-fix-dash-init-fragment-url-is-invalid-truncated-bug.patch @@ -0,0 +1,43 @@ +From ca6bf697d914be79fcc4d4314c06101d279b6ef3 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Wed, 21 May 2025 18:05:52 +0800 +Subject: [PATCH] fix dash init fragment url is "invalid:truncated" bug + +--- + libavformat/dashdec.c | 12 +++++++++++- + 1 file changed, 11 insertions(+), 1 deletion(-) + +diff --git a/libavformat/dashdec.c b/libavformat/dashdec.c +index 0a6c46b..616187f 100644 +--- a/libavformat/dashdec.c ++++ b/libavformat/dashdec.c +@@ -477,6 +477,10 @@ static char *get_content_url(xmlNodePtr *baseurl_nodes, + int i; + char *text; + char *url = NULL; ++ ++ if (strlen(val) >= max_url_size) { ++ max_url_size += 256; ++ } + char *tmp_str = av_mallocz(max_url_size); + + if (!tmp_str) +@@ -495,8 +499,14 @@ static char *get_content_url(xmlNodePtr *baseurl_nodes, + } + } + +- if (val) ++ if (val) { ++ int tmp_max_url_size = strlen(tmp_str) + strlen(val) + 1; ++ if (tmp_max_url_size > max_url_size) { ++ max_url_size = tmp_max_url_size; ++ tmp_str = av_realloc(tmp_str, max_url_size); ++ } + ff_make_absolute_url(tmp_str, max_url_size, tmp_str, val); ++ } + + if (rep_id_val) { + url = av_strireplace(tmp_str, "$RepresentationID$", rep_id_val); +-- +2.39.5 (Apple Git-154) + diff --git a/patches/ffmpeg-n7.1.1/0031-fix-dash-file-error-unterminated-entity-reference-du.patch b/patches/ffmpeg-n7.1.1/0031-fix-dash-file-error-unterminated-entity-reference-du.patch new file mode 100644 index 000000000..faeaec17b --- /dev/null +++ b/patches/ffmpeg-n7.1.1/0031-fix-dash-file-error-unterminated-entity-reference-du.patch @@ -0,0 +1,29 @@ +From 147a176cf02538367918d931959dbb5071202777 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Thu, 29 May 2025 09:45:39 +0800 +Subject: [PATCH] fix dash file error "unterminated entity reference" due to + ampersand in tag + +--- + libavformat/dashdec.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/libavformat/dashdec.c b/libavformat/dashdec.c +index 71d7906..4540ee3 100644 +--- a/libavformat/dashdec.c ++++ b/libavformat/dashdec.c +@@ -805,8 +805,10 @@ 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); ++ xmlChar *escaped = xmlEncodeSpecialChars(NULL, tmp_str); ++ xmlNodeSetContent(baseurl_nodes[i], escaped); + updated = 1; ++ xmlFree(escaped); + xmlFree(text); + } + } +-- +2.39.5 (Apple Git-154) + diff --git a/patches/ffmpeg-n7.1.1/0032-add-webp-demuxer-and-libwebp-decoder.patch b/patches/ffmpeg-n7.1.1/0032-add-webp-demuxer-and-libwebp-decoder.patch new file mode 100644 index 000000000..7e1dc381c --- /dev/null +++ b/patches/ffmpeg-n7.1.1/0032-add-webp-demuxer-and-libwebp-decoder.patch @@ -0,0 +1,804 @@ +From 7ff0d4ac7726a91ea3659dae6cea56c154a1172d Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Tue, 12 May 2026 10:06:39 +0800 +Subject: [PATCH] add webp demuxer and libwebp decoder + +--- + configure | 7 +- + libavcodec/Makefile | 1 + + libavcodec/allcodecs.c | 1 + + libavcodec/codec_desc.c | 9 + + libavcodec/codec_id.h | 1 + + libavcodec/libwebpdec.c | 376 +++++++++++++++++++++++++++++++++++++++ + libavformat/Makefile | 1 + + libavformat/allformats.c | 1 + + libavformat/webpdec.c | 296 ++++++++++++++++++++++++++++++ + 9 files changed, 692 insertions(+), 1 deletion(-) + create mode 100644 libavcodec/libwebpdec.c + create mode 100644 libavformat/webpdec.c + +diff --git a/configure b/configure +index 0a4e28a..54493ff 100755 +--- a/configure ++++ b/configure +@@ -7065,7 +7065,12 @@ enabled libvpx && { + enabled libvvenc && require_pkg_config libvvenc "libvvenc >= 1.6.1" "vvenc/vvenc.h" vvenc_get_version + enabled libwebp && { + enabled libwebp_encoder && require_pkg_config libwebp "libwebp >= 0.2.0" webp/encode.h WebPGetEncoderVersion +- enabled libwebp_anim_encoder && check_pkg_config libwebp_anim_encoder "libwebpmux >= 0.4.0" webp/mux.h WebPAnimEncoderOptionsInit; } ++ enabled libwebp_anim_encoder && check_pkg_config libwebp_anim_encoder "libwebpmux >= 0.4.0" webp/mux.h WebPAnimEncoderOptionsInit ++ check_pkg_config libwebpdemux "libwebpdemux >= 1.0.0" webp/demux.h WebPDemux ++ if enabled libwebpdemux; then ++ enable webp_demuxer ++ enable libwebp_decoder ++ fi } + enabled libx264 && require_pkg_config libx264 x264 "stdint.h x264.h" x264_encoder_encode && + require_cpp_condition libx264 x264.h "X264_BUILD >= 155" && { + [ "$toolchain" != "msvc" ] || +diff --git a/libavcodec/Makefile b/libavcodec/Makefile +index 6db860f..12ccbff 100644 +--- a/libavcodec/Makefile ++++ b/libavcodec/Makefile +@@ -812,6 +812,7 @@ OBJS-$(CONFIG_WBMP_DECODER) += wbmpdec.o + OBJS-$(CONFIG_WBMP_ENCODER) += wbmpenc.o + OBJS-$(CONFIG_WCMV_DECODER) += wcmv.o + OBJS-$(CONFIG_WEBP_DECODER) += webp.o ++OBJS-$(CONFIG_LIBWEBP_DECODER) += libwebpdec.o + OBJS-$(CONFIG_WEBVTT_DECODER) += webvttdec.o ass.o + OBJS-$(CONFIG_WEBVTT_ENCODER) += webvttenc.o ass_split.o + OBJS-$(CONFIG_WMALOSSLESS_DECODER) += wmalosslessdec.o wma_common.o +diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c +index aa0fc47..fa448b8 100644 +--- a/libavcodec/allcodecs.c ++++ b/libavcodec/allcodecs.c +@@ -416,6 +416,7 @@ extern const FFCodec ff_zlib_encoder; + extern const FFCodec ff_zlib_decoder; + extern const FFCodec ff_zmbv_encoder; + extern const FFCodec ff_zmbv_decoder; ++extern const FFCodec ff_libwebp_decoder; + + /* audio codecs */ + extern const FFCodec ff_aac_encoder; +diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c +index a91c0b5..d1c4ed0 100644 +--- a/libavcodec/codec_desc.c ++++ b/libavcodec/codec_desc.c +@@ -3723,6 +3723,15 @@ static const AVCodecDescriptor codec_descriptors[] = { + .long_name = NULL_IF_CONFIG_SMALL("Audio Vivid"), + .props = AV_CODEC_PROP_LOSSY, + }, ++ { ++ .id = AV_CODEC_ID_LIBWEBP, ++ .type = AVMEDIA_TYPE_VIDEO, ++ .name = "libwebp", ++ .long_name = NULL_IF_CONFIG_SMALL("libWebP"), ++ .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY | ++ AV_CODEC_PROP_LOSSLESS, ++ .mime_types= MT("image/webp"), ++ }, + { + .id = AV_CODEC_ID_VNULL, + .type = AVMEDIA_TYPE_VIDEO, +diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h +index a95f400..aff41f6 100644 +--- a/libavcodec/codec_id.h ++++ b/libavcodec/codec_id.h +@@ -601,6 +601,7 @@ enum AVCodecID { + AV_CODEC_ID_FFMETADATA = 0x21000, ///< Dummy codec for streams containing only metadata information. + AV_CODEC_ID_WRAPPED_AVFRAME = 0x21001, ///< Passthrough codec, AVFrames wrapped in AVPacket + AV_CODEC_ID_AVS3DA, ++ AV_CODEC_ID_LIBWEBP, + /** + * Dummy null video codec, useful mainly for development and debugging. + * Null encoder/decoder discard all input and never return any output. +diff --git a/libavcodec/libwebpdec.c b/libavcodec/libwebpdec.c +new file mode 100644 +index 0000000..17b69d0 +--- /dev/null ++++ b/libavcodec/libwebpdec.c +@@ -0,0 +1,376 @@ ++/* ++ * libwebpdec.c ++ * ++ * Copyright (c) 2025 debugly ++ * ++ * This file is part of FSPlayer. ++ * ++ * FSPlayer 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 3 of the License, or (at your option) any later version. ++ * ++ * FSPlayer 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 FSPlayer; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include "codec_internal.h" ++#include "decode.h" ++#include "internal.h" ++#include "libavutil/common.h" ++#include "libavutil/imgutils.h" ++#include "libavutil/mem.h" ++#include "libavutil/opt.h" ++#include "libavutil/pixdesc.h" ++#include ++#include ++ ++typedef struct MyImageRef { ++ int width; ++ int height; ++ uint8_t *data; ++} MyImageRef; ++ ++typedef struct MyRect { ++ int x; ++ int y; ++ int w; ++ int h; ++} MyRect; ++ ++typedef struct LibWebPDecoderContext { ++ AVClass *class; ++ MyImageRef *canvas; // The main compositing canvas ++} LibWebPDecoderContext; ++ ++static void release_image(MyImageRef *img) ++{ ++ if (img) { ++ if (img->data) ++ av_free(img->data); ++ free(img); ++ } ++} ++ ++static int alloc_image(MyImageRef **img, int width, int height) ++{ ++ if (!img || width <= 0 || height <= 0) ++ return AVERROR(EINVAL); ++ ++ *img = malloc(sizeof(MyImageRef)); ++ if (!*img) ++ return AVERROR(ENOMEM); ++ ++ (*img)->width = width; ++ (*img)->height = height; ++ (*img)->data = av_malloc(width * height * 4); // RGBA ++ if (!(*img)->data) { ++ free(*img); ++ *img = NULL; ++ return AVERROR(ENOMEM); ++ } ++ ++ memset((*img)->data, 0, width * height * 4); // 初始化为透明 ++ return 0; ++} ++ ++static void clear_rect(MyImageRef *img, MyRect rect) ++{ ++ if (!img || !img->data) ++ return; ++ ++ int img_width = img->width; ++ int img_height = img->height; ++ uint8_t *data = img->data; ++ ++ // 确保矩形在图像范围内 ++ int start_x = FFMAX(0, rect.x); ++ int start_y = FFMAX(0, rect.y); ++ int end_x = FFMIN(img_width, rect.x + rect.w); ++ int end_y = FFMIN(img_height, rect.y + rect.h); ++ ++ for (int y = start_y; y < end_y; y++) { ++ uint8_t *row = data + y * img_width * 4; ++ memset(row + start_x * 4, 0, (end_x - start_x) * 4); ++ } ++} ++ ++static void blend_pixel(uint8_t *dst, const uint8_t *src) ++{ ++ uint8_t sa = src[3]; ++ if (sa == 0) { ++ return; // 源像素完全透明,直接返回 ++ } else if (sa == 255) { ++ // 源像素完全不透明,直接覆盖 ++ memcpy(dst, src, 4); ++ return; ++ } ++ ++ uint8_t da = dst[3]; ++ uint8_t out_a = sa + ((da * (255 - sa)) / 255); ++ ++ if (out_a == 0) { ++ memset(dst, 0, 4); ++ return; ++ } ++ ++ dst[0] = (src[0] * sa + dst[0] * da * (255 - sa) / 255) / out_a; ++ dst[1] = (src[1] * sa + dst[1] * da * (255 - sa) / 255) / out_a; ++ dst[2] = (src[2] * sa + dst[2] * da * (255 - sa) / 255) / out_a; ++ dst[3] = out_a; ++} ++ ++static void draw_image(MyImageRef *canvas, MyImageRef *image, MyRect rect) ++{ ++ if (!canvas || !canvas->data || !image || !image->data) ++ return; ++ ++ int canvas_width = canvas->width; ++ int canvas_height = canvas->height; ++ uint8_t *canvas_data = canvas->data; ++ ++ int image_width = image->width; ++ int image_height = image->height; ++ uint8_t *image_data = image->data; ++ ++ // 计算绘制区域 ++ int start_x = FFMAX(0, rect.x); ++ int start_y = FFMAX(0, rect.y); ++ int end_x = FFMIN(canvas_width, rect.x + rect.w); ++ int end_y = FFMIN(canvas_height, rect.y + rect.h); ++ ++ for (int y = start_y; y < end_y; y++) { ++ uint8_t *canvas_row = canvas_data + y * canvas_width * 4; ++ uint8_t *image_row = image_data + (y - rect.y) * image_width * 4; ++ for (int x = start_x; x < end_x; x++) { ++ uint8_t *canvas_pixel = canvas_row + x * 4; ++ uint8_t *image_pixel = image_row + (x - rect.x) * 4; ++ ++ blend_pixel(canvas_pixel, image_pixel); ++ } ++ } ++} ++ ++static av_cold int libwebp_decode_init(AVCodecContext *avctx) ++{ ++ LibWebPDecoderContext *s = avctx->priv_data; ++ int canvas_size; ++ ++ if (avctx->width <= 0 || avctx->height <= 0) { ++ av_log(avctx, AV_LOG_ERROR, ++ "Invalid canvas dimensions from avctx: %dx%d\n", avctx->width, ++ avctx->height); ++ return AVERROR_INVALIDDATA; ++ } ++ ++ if (alloc_image(&s->canvas, avctx->width, avctx->height)) { ++ av_log(avctx, AV_LOG_ERROR, "Failed to allocate canvas buffer\n"); ++ return AVERROR(ENOMEM); ++ } ++ ++ avctx->pix_fmt = AV_PIX_FMT_RGBA; ++ ++ return 0; ++} ++ ++static int get_origin_from_pkt_side_data(AVPacket *pkt, int *offsetx, ++ int *offsety, int *blend, int *dispose) ++{ ++ if (!pkt || !offsetx || !offsety) { ++ return AVERROR(EINVAL); ++ } ++ ++ // 初始化输出参数 ++ *offsetx = 0; ++ *offsety = 0; ++ *blend = 0; ++ *dispose = 0; ++ ++ // 获取元数据 ++ size_t metadata_len = 0; ++ uint8_t *metadata_data = av_packet_get_side_data( ++ pkt, AV_PKT_DATA_STRINGS_METADATA, &metadata_len); ++ ++ if (!metadata_data || metadata_len == 0) { ++ return -1; // ++ } ++ ++ // 解包元数据到AVDictionary ++ AVDictionary *dict = NULL; ++ int ret = av_packet_unpack_dictionary(metadata_data, metadata_len, &dict); ++ if (ret < 0) { ++ av_log(NULL, AV_LOG_ERROR, "Failed to unpack metadata: %d\n", ret); ++ return ret; ++ } ++ ++ // 解析offsetx和offsety和blend ++ AVDictionaryEntry *entry = NULL; ++ entry = av_dict_get(dict, "offsetx", NULL, 0); ++ if (entry && entry->value) { ++ *offsetx = atoi(entry->value); // 转换字符串为整数 ++ } ++ ++ entry = av_dict_get(dict, "offsety", NULL, 0); ++ if (entry && entry->value) { ++ *offsety = atoi(entry->value); ++ } ++ ++ entry = av_dict_get(dict, "blend", NULL, 0); ++ if (entry && entry->value) { ++ *blend = atoi(entry->value); ++ } ++ ++ entry = av_dict_get(dict, "dispose", NULL, 0); ++ if (entry && entry->value) { ++ *dispose = atoi(entry->value); ++ } ++ ++ // 释放字典 ++ av_dict_free(&dict); ++ return 0; ++} ++ ++static int libwebp_do_decode_frame(AVPacket *pkt, MyImageRef **imgp) ++{ ++ WebPDecoderConfig config; ++ // 1. 解码当前帧并获取其属性 ++ if (!WebPInitDecoderConfig(&config)) ++ return AVERROR_UNKNOWN; ++ ++ if (WebPGetFeatures(pkt->data, pkt->size, &config.input) != VP8_STATUS_OK) ++ return AVERROR_INVALIDDATA; ++ ++ int hasAlpha = config.input.has_alpha; ++ int input_width = config.input.width; ++ int input_height = config.input.height; ++ if (input_width <= 0 || input_height <= 0) ++ return AVERROR_INVALIDDATA; ++ ++ config.output.colorspace = MODE_RGBA; ++ config.output.is_external_memory = 1; ++ ++ MyImageRef *img; ++ ++ if (alloc_image(&img, input_width, input_height)) { ++ return AVERROR(ENOMEM); ++ } ++ ++ int rgba_stride = img->width * 4; ++ uint8_t *rgba = img->data; ++ config.output.u.RGBA.rgba = rgba; ++ config.output.u.RGBA.stride = rgba_stride; ++ config.output.u.RGBA.size = input_height * rgba_stride; ++ ++ if (WebPDecode(pkt->data, pkt->size, &config) != VP8_STATUS_OK) { ++ release_image(img); ++ return AVERROR_INVALIDDATA; ++ } ++ *imgp = img; ++ return 0; ++} ++ ++static int libwebp_decode_frame(AVCodecContext *avctx, AVFrame *frame, ++ int *got_frame, const AVPacket *pkt) ++{ ++ LibWebPDecoderContext *s = avctx->priv_data; ++ ++ int offsetx, offsety, blend, dispose; ++ if (get_origin_from_pkt_side_data(pkt, &offsetx, &offsety, &blend, ++ &dispose) < 0) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ // 使用 libwebp 解码当前帧 ++ MyImageRef *img = NULL; ++ int ret = libwebp_do_decode_frame((AVPacket *)pkt, &img); ++ if (ret < 0) { ++ av_log(avctx, AV_LOG_ERROR, "Failed to decode WebP frame: %d\n", ret); ++ return ret; ++ } ++ ++ // 将当前帧渲染到已准备好的画布上 ++ MyRect rect = {offsetx, offsety, img->width, img->height}; ++ draw_image(s->canvas, img, rect); ++ release_image(img); ++ ++ frame->width = avctx->width; ++ frame->height = avctx->height; ++ frame->format = avctx->pix_fmt; ++ ++ // 为 frame 申请内存 ++ if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) { ++ return ret; ++ } ++ ++ // 将合成好的画布复制到输出帧 ++ int canvas_linesize = s->canvas->width * 4; ++ av_image_copy_plane(frame->data[0], frame->linesize[0], s->canvas->data, ++ s->canvas->width * 4, s->canvas->width * 4, ++ s->canvas->height); ++ ++ // av_image_copy(frame->data, frame->linesize, ++ // (const uint8_t **)&s->canvas->data, &canvas_linesize, ++ // avctx->pix_fmt, output_width, output_height); ++ ++ // 执行 dispose 操作 ++ if (dispose == WEBP_MUX_DISPOSE_BACKGROUND) { ++ clear_rect(s->canvas, rect); ++ } ++ ++ *got_frame = 1; ++ ret = pkt->size; ++ ++ return ret; ++} ++ ++static int libwebp_decode_flush(AVCodecContext *avctx) ++{ ++ LibWebPDecoderContext *s = avctx->priv_data; ++ if (s->canvas && s->canvas->data) { ++ memset(s->canvas->data, 0, s->canvas->width * s->canvas->height * 4); ++ } ++ return 0; ++} ++ ++//when seek or seek to zero for loop,clear then canvas ++static av_cold int libwebp_decode_close(AVCodecContext *avctx) ++{ ++ LibWebPDecoderContext *s = avctx->priv_data; ++ release_image(s->canvas); ++ s->canvas = NULL; ++ return 0; ++} ++ ++#define OFFSET(x) offsetof(LibWebPDecoderContext, x) ++#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM ++ ++static const AVOption options[] = {{NULL}}; ++ ++static const AVClass libwebp_decoder_class = { ++ .class_name = "libwebp decoder", ++ .item_name = av_default_item_name, ++ .option = options, ++ .version = LIBAVUTIL_VERSION_INT, ++}; ++ ++const FFCodec ff_libwebp_decoder = { ++ .p.name = "libwebp", ++ .p.long_name = NULL_IF_CONFIG_SMALL("libwebp WebP image decoder"), ++ .p.type = AVMEDIA_TYPE_VIDEO, ++ .p.id = AV_CODEC_ID_LIBWEBP, ++ .priv_data_size = sizeof(LibWebPDecoderContext), ++ .init = libwebp_decode_init, ++ .cb.decode = libwebp_decode_frame, ++ .flush = libwebp_decode_flush, ++ .close = libwebp_decode_close, ++ .p.capabilities = AV_CODEC_CAP_DR1, ++ .p.priv_class = &libwebp_decoder_class, ++ .p.wrapper_name = "libwebp", ++ .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE, ++}; +diff --git a/libavformat/Makefile b/libavformat/Makefile +index 1f32e4c..90aef47 100644 +--- a/libavformat/Makefile ++++ b/libavformat/Makefile +@@ -644,6 +644,7 @@ OBJS-$(CONFIG_WEBM_MUXER) += matroskaenc.o matroska.o \ + OBJS-$(CONFIG_WEBM_DASH_MANIFEST_MUXER) += webmdashenc.o + OBJS-$(CONFIG_WEBM_CHUNK_MUXER) += webm_chunk.o + OBJS-$(CONFIG_WEBP_MUXER) += webpenc.o ++OBJS-$(CONFIG_WEBP_DEMUXER) += webpdec.o + OBJS-$(CONFIG_WEBVTT_DEMUXER) += webvttdec.o subtitles.o + OBJS-$(CONFIG_WEBVTT_MUXER) += webvttenc.o + OBJS-$(CONFIG_WSAUD_DEMUXER) += westwood_aud.o +diff --git a/libavformat/allformats.c b/libavformat/allformats.c +index 9d7a98a..0a778a9 100644 +--- a/libavformat/allformats.c ++++ b/libavformat/allformats.c +@@ -565,6 +565,7 @@ extern const FFInputFormat ff_image_svg_pipe_demuxer; + extern const FFInputFormat ff_image_sunrast_pipe_demuxer; + extern const FFInputFormat ff_image_tiff_pipe_demuxer; + extern const FFInputFormat ff_image_vbn_pipe_demuxer; ++extern const FFInputFormat ff_webp_demuxer; + extern const FFInputFormat ff_image_webp_pipe_demuxer; + extern const FFInputFormat ff_image_xbm_pipe_demuxer; + extern const FFInputFormat ff_image_xpm_pipe_demuxer; +diff --git a/libavformat/webpdec.c b/libavformat/webpdec.c +new file mode 100644 +index 0000000..df4a3af +--- /dev/null ++++ b/libavformat/webpdec.c +@@ -0,0 +1,296 @@ ++/* ++ * webpdec.c ++ * ++ * Copyright (c) 2025 debugly ++ * ++ * This file is part of FSPlayer. ++ * ++ * FSPlayer 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 3 of the License, or (at your option) any later version. ++ * ++ * FSPlayer 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 FSPlayer; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include "internal.h" ++#include "demux.h" ++#include "libavutil/intreadwrite.h" ++#include "libavutil/mem.h" ++#include "libavutil/common.h" ++#include ++ ++typedef struct WebPDemuxContext { ++ const AVClass *class; ++ WebPDemuxer *demuxer; ++ WebPIterator iter; // 帧迭代器 ++ int current_frame; // 当前帧索引 ++ int frame_count; // 总帧数 ++ int64_t duration; // 总时长(ms) ++ uint8_t *data; // 文件数据缓冲区 ++ size_t data_size; // 数据大小 ++ int64_t *frame_timestamps; // 帧时间戳数组(ms) ++ int *frame_durations; // 帧时长数组(ms) ++} WebPDemuxContext; ++ ++// 释放迭代器资源 ++static void webp_release_iterator(WebPIterator *iter) { ++ if (iter->fragment.bytes) { ++ WebPDemuxReleaseIterator(iter); ++ memset(iter, 0, sizeof(*iter)); ++ } ++} ++ ++// 探测WebP格式 ++static int webp_read_probe(const AVProbeData *p) { ++ // WebP文件格式以"RIFF"开头,后面跟着4字节大小和"WEBP"标识 ++ if (p->buf_size < 12) ++ return 0; ++ ++ // 检查RIFF标识和WEBP格式标识 ++ if (AV_RL32(p->buf) == MKTAG('R', 'I', 'F', 'F') && ++ AV_RL32(p->buf + 8) == MKTAG('W', 'E', 'B', 'P')) { ++ // 确定是WebP文件,返回较高的探测分数 ++ return AVPROBE_SCORE_MAX; ++ } ++ ++ return 0; ++} ++ ++static int webp_read_header(AVFormatContext *s) { ++ WebPDemuxContext *wp = s->priv_data; ++ AVIOContext *pb = s->pb; ++ AVStream *st; ++ int ret, i; ++ ++ // 读取文件到内存 ++ wp->data_size = avio_size(pb); ++ if (wp->data_size <= 0) { ++ av_log(s, AV_LOG_ERROR, "Invalid file size\n"); ++ return AVERROR_INVALIDDATA; ++ } ++ ++ wp->data = av_malloc(wp->data_size); ++ if (!wp->data) ++ return AVERROR(ENOMEM); ++ ++ if (avio_read(pb, wp->data, wp->data_size) != wp->data_size) { ++ av_log(s, AV_LOG_ERROR, "Failed to read file\n"); ++ ret = AVERROR(EIO); ++ goto fail; ++ } ++ ++ // 初始化WebP解复用器 ++ WebPData webp_data; ++ WebPDataInit(&webp_data); ++ webp_data.bytes = wp->data; ++ webp_data.size = wp->data_size; ++ ++ wp->demuxer = WebPDemux(&webp_data); ++ if (!wp->demuxer) { ++ av_log(s, AV_LOG_ERROR, "Failed to create WebP demuxer\n"); ++ ret = AVERROR_INVALIDDATA; ++ goto fail; ++ } ++ ++ wp->frame_count = WebPDemuxGetI(wp->demuxer, WEBP_FF_FRAME_COUNT); ++ // 获取画布尺寸 ++ int canvas_width = WebPDemuxGetI(wp->demuxer, WEBP_FF_CANVAS_WIDTH); ++ int canvas_height = WebPDemuxGetI(wp->demuxer, WEBP_FF_CANVAS_HEIGHT); ++ ++ // int loop_count = WebPDemuxGetI(wp->demuxer, WEBP_FF_LOOP_COUNT); ++ // uint32_t flags = WebPDemuxGetI(wp->demuxer, WEBP_FF_FORMAT_FLAGS); ++ ++ // int has_animation = flags & ANIMATION_FLAG; ++ // int has_alpha = flags & ALPHA_FLAG; ++ ++ // 分配帧时间戳和时长数组 ++ wp->frame_timestamps = av_malloc_array(wp->frame_count, sizeof(int64_t)); ++ wp->frame_durations = av_malloc_array(wp->frame_count, sizeof(int)); ++ if (!wp->frame_timestamps || !wp->frame_durations) { ++ ret = AVERROR(ENOMEM); ++ goto fail; ++ } ++ ++ memset(&wp->iter, 0, sizeof(wp->iter)); ++ // libwebp's index start with 1 ++ if (!WebPDemuxGetFrame(wp->demuxer, 1, &wp->iter)) { ++ av_log(s, AV_LOG_ERROR, "Failed to get first frame\n"); ++ ret = AVERROR_INVALIDDATA; ++ goto fail; ++ } ++ ++ wp->duration = 0; ++ for (i = 0; i < wp->frame_count; i++) { ++ int duration = wp->iter.duration; ++ if (duration <= 10) { ++ // WebP standard says 0 duration is used for canvas updating but not showing image, but actually Chrome and other implementations set it to 100ms if duration is lower or equal than 10ms ++ // Some animated WebP images also created without duration, we should keep compatibility ++ duration = 100; ++ } ++ wp->frame_durations[i] = duration; ++ wp->frame_timestamps[i] = wp->duration; ++ wp->duration += wp->frame_durations[i]; ++ if (i < wp->frame_count - 1) ++ WebPDemuxNextFrame(&wp->iter); ++ } ++ ++ // 重置迭代器到第一帧 ++ webp_release_iterator(&wp->iter); ++ if (!WebPDemuxGetFrame(wp->demuxer, 1, &wp->iter)) { ++ av_log(s, AV_LOG_ERROR, "Failed to reset to first frame\n"); ++ ret = AVERROR_INVALIDDATA; ++ goto fail; ++ } ++ ++ // 创建视频流 ++ st = avformat_new_stream(s, NULL); ++ if (!st) { ++ ret = AVERROR(ENOMEM); ++ goto fail; ++ } ++ ++ // 设置流参数 ++ st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; ++ // 使用自定义解码器 ++ st->codecpar->codec_id = AV_CODEC_ID_LIBWEBP; ++ st->codecpar->width = canvas_width; ++ st->codecpar->height = canvas_height; ++ st->duration = wp->duration; ++ avpriv_set_pts_info(st, 64, 1, 1000); // 时基:1ms ++ ++ wp->current_frame = 0; ++ return 0; ++ ++fail: ++ webp_release_iterator(&wp->iter); ++ WebPDemuxDelete(wp->demuxer); ++ av_free(wp->data); ++ av_free(wp->frame_timestamps); ++ av_free(wp->frame_durations); ++ return ret; ++} ++ ++/** ++ * Add this frame's source path and basename to packet's sidedata ++ * as a dictionary, so it can be used by filters like 'drawtext'. ++ */ ++static int add_origin_as_pkt_side_data(int offsetx, int offsety, int blend, int dispose, AVPacket *pkt) { ++ AVDictionary *d = NULL; ++ char *packed_metadata = NULL; ++ size_t metadata_len; ++ int ret; ++ ++ av_dict_set_int(&d, "offsetx", offsetx, 0); ++ av_dict_set_int(&d, "offsety", offsety, 0); ++ av_dict_set_int(&d, "blend", blend, 0); ++ av_dict_set_int(&d, "dispose", dispose, 0); ++ ++ packed_metadata = av_packet_pack_dictionary(d, &metadata_len); ++ av_dict_free(&d); ++ if (!packed_metadata) ++ return AVERROR(ENOMEM); ++ ret = av_packet_add_side_data(pkt, AV_PKT_DATA_STRINGS_METADATA, ++ packed_metadata, metadata_len); ++ if (ret < 0) { ++ av_freep(&packed_metadata); ++ return ret; ++ } ++ return 0; ++} ++ ++static int webp_read_packet(AVFormatContext *s, AVPacket *pkt) { ++ WebPDemuxContext *wp = s->priv_data; ++ int ret; ++ ++ if (wp->current_frame >= wp->frame_count) ++ return AVERROR_EOF; ++ ++ // 分配并填充数据包 ++ if ((ret = av_new_packet(pkt, wp->iter.fragment.size)) < 0) ++ return ret; ++ memcpy(pkt->data, wp->iter.fragment.bytes, wp->iter.fragment.size); ++ ++ add_origin_as_pkt_side_data(wp->iter.x_offset, wp->iter.y_offset, wp->iter.blend_method == WEBP_MUX_BLEND, wp->iter.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND, pkt); ++ // 设置数据包参数 ++ pkt->stream_index = 0; ++ pkt->pts = wp->frame_timestamps[wp->current_frame]; ++ pkt->duration = wp->frame_durations[wp->current_frame]; ++ pkt->flags |= AV_PKT_FLAG_KEY; ++ ++ // 准备下一帧 ++ wp->current_frame++; ++ if (wp->current_frame < wp->frame_count && !WebPDemuxNextFrame(&wp->iter)) ++ av_log(s, AV_LOG_WARNING, "Unexpected end of frames\n"); ++ ++ return 0; ++} ++ ++static int webp_read_seek(AVFormatContext *s, int stream_index, ++ int64_t timestamp, int flags) { ++ WebPDemuxContext *wp = s->priv_data; ++ int target_frame; ++ ++ if (stream_index != 0) ++ return -1; ++ ++ // 计算目标帧索引 ++ target_frame = av_rescale_rnd(timestamp, wp->frame_count, ++ wp->duration, flags & AVSEEK_FLAG_BACKWARD ? AV_ROUND_DOWN : AV_ROUND_UP); ++ ++ // 手动实现范围限制(替代av_clamp) ++ if (target_frame < 0) { ++ target_frame = 0; ++ } else if (target_frame >= wp->frame_count) { ++ target_frame = wp->frame_count - 1; ++ } ++ ++ // 定位到目标帧 ++ webp_release_iterator(&wp->iter); ++ if (!WebPDemuxGetFrame(wp->demuxer, target_frame + 1, &wp->iter)) { ++ av_log(s, AV_LOG_ERROR, "Failed to seek to frame %d\n", target_frame); ++ return AVERROR_INVALIDDATA; ++ } ++ ++ wp->current_frame = target_frame; ++ return 0; ++} ++ ++static int webp_read_close(AVFormatContext *s) { ++ WebPDemuxContext *wp = s->priv_data; ++ ++ webp_release_iterator(&wp->iter); ++ WebPDemuxDelete(wp->demuxer); ++ av_free(wp->data); ++ av_free(wp->frame_timestamps); ++ av_free(wp->frame_durations); ++ return 0; ++} ++ ++static const AVClass webp_demuxer_class = { ++ .class_name = "WebP demuxer", ++ .item_name = av_default_item_name, ++ .version = LIBAVUTIL_VERSION_INT, ++}; ++ ++// FFmpeg 7.x输入解复用器标准声明 ++const FFInputFormat ff_webp_demuxer = { ++ .p.name = "webp", ++ .p.long_name = NULL_IF_CONFIG_SMALL("WebP image format (libwebp 1.5.0+)"), ++ .p.flags = AVFMT_GENERIC_INDEX, ++ .p.extensions = "webp", ++ .read_probe = webp_read_probe, ++ .read_header = webp_read_header, ++ .read_packet = webp_read_packet, ++ .read_seek = webp_read_seek, ++ .read_close = webp_read_close, ++ .priv_data_size = sizeof(WebPDemuxContext), ++ .p.priv_class = &webp_demuxer_class, ++}; +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.1/0001-restore-ijk-h264_ps-null-pointer-fault-tolerant.patch b/patches/ffmpeg-n8.1.1/0001-restore-ijk-h264_ps-null-pointer-fault-tolerant.patch new file mode 100644 index 000000000..d7deded1d --- /dev/null +++ b/patches/ffmpeg-n8.1.1/0001-restore-ijk-h264_ps-null-pointer-fault-tolerant.patch @@ -0,0 +1,52 @@ +From 21a946850b1be93508b3d2d4478e60d347d9993f Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Wed, 28 May 2025 17:28:22 +0800 +Subject: restore ijk h264_ps null pointer fault tolerant + +--- + libavcodec/h264_ps.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/libavcodec/h264_ps.c b/libavcodec/h264_ps.c +index ac20417..2c35e0d 100644 +--- a/libavcodec/h264_ps.c ++++ b/libavcodec/h264_ps.c +@@ -437,7 +437,7 @@ int ff_h264_decode_seq_parameter_set(GetBitContext *gb, AVCodecContext *avctx, + } + + sps->ref_frame_count = get_ue_golomb_31(gb); +- if (avctx->codec_tag == MKTAG('S', 'M', 'V', '2')) ++ if (avctx && avctx->codec_tag == MKTAG('S', 'M', 'V', '2')) + sps->ref_frame_count = FFMAX(2, sps->ref_frame_count); + if (sps->ref_frame_count > H264_MAX_DPB_FRAMES) { + av_log(avctx, AV_LOG_ERROR, +@@ -480,7 +480,7 @@ int ff_h264_decode_seq_parameter_set(GetBitContext *gb, AVCodecContext *avctx, + int width = 16 * sps->mb_width; + int height = 16 * sps->mb_height; + +- if (avctx->flags2 & AV_CODEC_FLAG2_IGNORE_CROP) { ++ if (avctx && avctx->flags2 & AV_CODEC_FLAG2_IGNORE_CROP) { + av_log(avctx, AV_LOG_DEBUG, "discarding sps cropping, original " + "values are l:%d r:%d t:%d b:%d\n", + crop_left, crop_right, crop_top, crop_bottom); +@@ -552,7 +552,7 @@ int ff_h264_decode_seq_parameter_set(GetBitContext *gb, AVCodecContext *avctx, + if (!sps->vui.sar.den) + sps->vui.sar.den = 1; + +- if (avctx->debug & FF_DEBUG_PICT_INFO) { ++ if (avctx && avctx->debug & FF_DEBUG_PICT_INFO) { + static const char csp[4][5] = { "Gray", "420", "422", "444" }; + av_log(avctx, AV_LOG_DEBUG, + "sps:%u profile:%d/%d poc:%d ref:%d %dx%d %s %s crop:%u/%u/%u/%u %s %s %"PRId32"/%"PRId32" b%d reo:%d\n", +@@ -819,7 +819,7 @@ int ff_h264_decode_picture_parameter_set(GetBitContext *gb, AVCodecContext *avct + if (pps->chroma_qp_index_offset[0] != pps->chroma_qp_index_offset[1]) + pps->chroma_qp_diff = 1; + +- if (avctx->debug & FF_DEBUG_PICT_INFO) { ++ if (avctx && avctx->debug & FF_DEBUG_PICT_INFO) { + av_log(avctx, AV_LOG_DEBUG, + "pps:%u sps:%u %s slice_groups:%d ref:%u/%u %s qp:%d/%d/%d/%d %s %s %s %s\n", + pps_id, pps->sps_id, +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.1/0002-restore-ijk-avformat-add-application-and-dns_cache.patch b/patches/ffmpeg-n8.1.1/0002-restore-ijk-avformat-add-application-and-dns_cache.patch new file mode 100644 index 000000000..9e2745720 --- /dev/null +++ b/patches/ffmpeg-n8.1.1/0002-restore-ijk-avformat-add-application-and-dns_cache.patch @@ -0,0 +1,671 @@ +From c4bfe93521942a600c8ba3e477b74aca1d8c745d Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Fri, 8 May 2026 15:58:55 +0800 +Subject: restore ijk avformat add application and dns_cache + +--- + libavformat/Makefile | 5 + + libavformat/application.c | 213 ++++++++++++++++++++++++++++++++++ + libavformat/application.h | 120 ++++++++++++++++++++ + libavformat/dns_cache.c | 232 ++++++++++++++++++++++++++++++++++++++ + libavformat/dns_cache.h | 39 +++++++ + 5 files changed, 609 insertions(+) + create mode 100644 libavformat/application.c + create mode 100644 libavformat/application.h + create mode 100644 libavformat/dns_cache.c + create mode 100644 libavformat/dns_cache.h + +diff --git a/libavformat/Makefile b/libavformat/Makefile +index 4786a93..3c95fc4 100644 +--- a/libavformat/Makefile ++++ b/libavformat/Makefile +@@ -5,6 +5,9 @@ HEADERS = avformat.h \ + avio.h \ + version.h \ + version_major.h \ ++ application.h \ ++ dns_cache.h \ ++ + + OBJS = allformats.o \ + av1.o \ +@@ -35,6 +38,8 @@ OBJS = allformats.o \ + utils.o \ + version.o \ + vpcc.o \ ++ application.o \ ++ dns_cache.o \ + + OBJS-$(HAVE_LIBC_MSVCRT) += file_open.o + +diff --git a/libavformat/application.c b/libavformat/application.c +new file mode 100644 +index 0000000..de093b9 +--- /dev/null ++++ b/libavformat/application.c +@@ -0,0 +1,213 @@ ++/* ++ * copyright (c) 2016 Zhang Rui ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg 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. ++ * ++ * FFmpeg 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 FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include "application.h" ++#include "libavformat/network.h" ++#include "libavutil/avstring.h" ++#include "libavutil/mem.h" ++ ++static int av_application_alloc(AVApplicationContext **ph, void *opaque) ++{ ++ AVApplicationContext *h = NULL; ++ ++ h = av_mallocz(sizeof(AVApplicationContext)); ++ if (!h) ++ return AVERROR(ENOMEM); ++ ++ h->opaque = opaque; ++ ++ *ph = h; ++ return 0; ++} ++ ++int av_application_open(AVApplicationContext **ph, void *opaque) ++{ ++ int ret = av_application_alloc(ph, opaque); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static void av_application_close(AVApplicationContext *h) ++{ ++ av_free(h); ++} ++ ++void av_application_closep(AVApplicationContext **ph) ++{ ++ if (!ph || !*ph) ++ return; ++ ++ av_application_close(*ph); ++ *ph = NULL; ++} ++ ++void av_application_on_http_event(AVApplicationContext *h, int event_type, AVAppHttpEvent *event) ++{ ++ if (h && h->func_on_app_event) ++ h->func_on_app_event(h, event_type, (void *)event, sizeof(AVAppHttpEvent)); ++} ++ ++void av_application_will_http_open(AVApplicationContext *h, void *obj, const char *url) ++{ ++ AVAppHttpEvent event = {0}; ++ ++ if (!h || !obj || !url) ++ return; ++ ++ event.obj = obj; ++ av_strlcpy(event.url, url, sizeof(event.url)); ++ ++ av_application_on_http_event(h, AVAPP_EVENT_WILL_HTTP_OPEN, &event); ++} ++ ++void av_application_did_http_open(AVApplicationContext *h, void *obj, const char *url, int error, int http_code, int64_t filesize) ++{ ++ AVAppHttpEvent event = {0}; ++ ++ if (!h || !obj || !url) ++ return; ++ ++ event.obj = obj; ++ av_strlcpy(event.url, url, sizeof(event.url)); ++ event.error = error; ++ event.http_code = http_code; ++ event.filesize = filesize; ++ ++ av_application_on_http_event(h, AVAPP_EVENT_DID_HTTP_OPEN, &event); ++} ++ ++void av_application_will_http_seek(AVApplicationContext *h, void *obj, const char *url, int64_t offset) ++{ ++ AVAppHttpEvent event = {0}; ++ ++ if (!h || !obj || !url) ++ return; ++ ++ event.obj = obj; ++ event.offset = offset; ++ av_strlcpy(event.url, url, sizeof(event.url)); ++ ++ av_application_on_http_event(h, AVAPP_EVENT_WILL_HTTP_SEEK, &event); ++} ++ ++void av_application_did_http_seek(AVApplicationContext *h, void *obj, const char *url, int64_t offset, int error, int http_code) ++{ ++ AVAppHttpEvent event = {0}; ++ ++ if (!h || !obj || !url) ++ return; ++ ++ event.obj = obj; ++ event.offset = offset; ++ av_strlcpy(event.url, url, sizeof(event.url)); ++ event.error = error; ++ event.http_code = http_code; ++ ++ av_application_on_http_event(h, AVAPP_EVENT_DID_HTTP_SEEK, &event); ++} ++ ++static void av_application_on_io_traffic(AVApplicationContext *h, AVAppIOTraffic *event) ++{ ++ if (h && h->func_on_app_event) ++ h->func_on_app_event(h, AVAPP_EVENT_IO_TRAFFIC, (void *)event, sizeof(AVAppIOTraffic)); ++} ++ ++int av_application_on_io_control(AVApplicationContext *h, int event_type, AVAppIOControl *control) ++{ ++ if (h && h->func_on_app_event) ++ return h->func_on_app_event(h, event_type, (void *)control, sizeof(AVAppIOControl)); ++ return 0; ++} ++ ++int av_application_on_tcp_will_open(AVApplicationContext *h) ++{ ++ if (h && h->func_on_app_event) { ++ AVAppTcpIOControl control = {0}; ++ return h->func_on_app_event(h, AVAPP_CTRL_WILL_TCP_OPEN, (void *)&control, sizeof(AVAppTcpIOControl)); ++ } ++ return 0; ++} ++ ++// only callback returns error ++int av_application_on_tcp_did_open(AVApplicationContext *h, int error, int fd, AVAppTcpIOControl *control) ++{ ++ struct sockaddr_storage so_stg; ++ int ret = 0; ++ socklen_t so_len = sizeof(so_stg); ++ int so_family; ++ char *so_ip_name = control->ip; ++ ++ if (!h || !h->func_on_app_event || fd <= 0) ++ return 0; ++ ++ ret = getpeername(fd, (struct sockaddr *)&so_stg, &so_len); ++ if (ret) ++ return 0; ++ control->error = error; ++ control->fd = fd; ++ ++ so_family = ((struct sockaddr*)&so_stg)->sa_family; ++ switch (so_family) { ++ case AF_INET: { ++ struct sockaddr_in* in4 = (struct sockaddr_in*)&so_stg; ++ if (inet_ntop(AF_INET, &(in4->sin_addr), so_ip_name, sizeof(control->ip))) { ++ control->family = AF_INET; ++ control->port = in4->sin_port; ++ } ++ break; ++ } ++ case AF_INET6: { ++ struct sockaddr_in6* in6 = (struct sockaddr_in6*)&so_stg; ++ if (inet_ntop(AF_INET6, &(in6->sin6_addr), so_ip_name, sizeof(control->ip))) { ++ control->family = AF_INET6; ++ control->port = in6->sin6_port; ++ } ++ break; ++ } ++ } ++ ++ return h->func_on_app_event(h, AVAPP_CTRL_DID_TCP_OPEN, (void *)control, sizeof(AVAppTcpIOControl)); ++} ++ ++void av_application_on_async_statistic(AVApplicationContext *h, AVAppAsyncStatistic *statistic) ++{ ++ if (h && h->func_on_app_event) ++ h->func_on_app_event(h, AVAPP_EVENT_ASYNC_STATISTIC, (void *)statistic, sizeof(AVAppAsyncStatistic)); ++} ++ ++void av_application_on_async_read_speed(AVApplicationContext *h, AVAppAsyncReadSpeed *speed) ++{ ++ if (h && h->func_on_app_event) ++ h->func_on_app_event(h, AVAPP_EVENT_ASYNC_READ_SPEED, (void *)speed, sizeof(AVAppAsyncReadSpeed)); ++} ++ ++void av_application_did_io_tcp_read(AVApplicationContext *h, void *obj, int bytes) ++{ ++ AVAppIOTraffic event = {0}; ++ if (!h || !obj || bytes <= 0) ++ return; ++ ++ event.obj = obj; ++ event.bytes = bytes; ++ ++ av_application_on_io_traffic(h, &event); ++} +diff --git a/libavformat/application.h b/libavformat/application.h +new file mode 100644 +index 0000000..b9e7f5b +--- /dev/null ++++ b/libavformat/application.h +@@ -0,0 +1,120 @@ ++/* ++ * copyright (c) 2016 Zhang Rui ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg 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. ++ * ++ * FFmpeg 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 FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#ifndef AVUTIL_APPLICATION_H ++#define AVUTIL_APPLICATION_H ++ ++#include ++ ++#define AVAPP_EVENT_WILL_HTTP_OPEN 1 //AVAppHttpEvent ++#define AVAPP_EVENT_DID_HTTP_OPEN 2 //AVAppHttpEvent ++#define AVAPP_EVENT_WILL_HTTP_SEEK 3 //AVAppHttpEvent ++#define AVAPP_EVENT_DID_HTTP_SEEK 4 //AVAppHttpEvent ++ ++#define AVAPP_EVENT_ASYNC_STATISTIC 0x11000 //AVAppAsyncStatistic ++#define AVAPP_EVENT_ASYNC_READ_SPEED 0x11001 //AVAppAsyncReadSpeed ++#define AVAPP_EVENT_IO_TRAFFIC 0x12204 //AVAppIOTraffic ++ ++#define AVAPP_CTRL_WILL_TCP_OPEN 0x20001 //AVAppTcpIOControl ++#define AVAPP_CTRL_DID_TCP_OPEN 0x20002 //AVAppTcpIOControl ++ ++#define AVAPP_CTRL_WILL_HTTP_OPEN 0x20003 //AVAppIOControl ++#define AVAPP_CTRL_WILL_LIVE_OPEN 0x20005 //AVAppIOControl ++ ++#define AVAPP_CTRL_WILL_CONCAT_SEGMENT_OPEN 0x20007 //AVAppIOControl ++ ++typedef struct AVAppIOControl { ++ size_t size; ++ char url[4096]; /* in, out */ ++ int segment_index; /* in, default = 0 */ ++ int retry_counter; /* in */ ++ ++ int is_handled; /* out, default = false */ ++ int is_url_changed; /* out, default = false */ ++} AVAppIOControl; ++ ++typedef struct AVAppTcpIOControl { ++ int error; ++ int family; ++ char ip[96]; ++ int port; ++ int fd; ++} AVAppTcpIOControl; ++ ++typedef struct AVAppAsyncStatistic { ++ size_t size; ++ int64_t buf_backwards; ++ int64_t buf_forwards; ++ int64_t buf_capacity; ++} AVAppAsyncStatistic; ++ ++typedef struct AVAppAsyncReadSpeed { ++ size_t size; ++ int is_full_speed; ++ int64_t io_bytes; ++ int64_t elapsed_milli; ++} AVAppAsyncReadSpeed; ++ ++typedef struct AVAppHttpEvent ++{ ++ void *obj; ++ char url[4096]; ++ int64_t offset; ++ int error; ++ int http_code; ++ int64_t filesize; ++} AVAppHttpEvent; ++ ++typedef struct AVAppIOTraffic ++{ ++ void *obj; ++ int bytes; ++} AVAppIOTraffic; ++ ++typedef struct AVApplicationContext AVApplicationContext; ++typedef struct AVClass AVClass; ++struct AVApplicationContext { ++ const AVClass *av_class; /**< information for av_log(). Set by av_application_open(). */ ++ void *opaque; /**< user data. */ ++ int (*func_on_app_event)(AVApplicationContext *h, int event_type ,void *obj, size_t size); ++}; ++ ++// open/close ++int av_application_open(AVApplicationContext **ph, void *opaque); ++void av_application_closep(AVApplicationContext **ph); ++ ++// custom protocol invoke ++void av_application_on_http_event(AVApplicationContext *h, int event_type, AVAppHttpEvent *event); ++int av_application_on_io_control(AVApplicationContext *h, int event_type, AVAppIOControl *control); ++void av_application_on_async_statistic(AVApplicationContext *h, AVAppAsyncStatistic *statistic); ++void av_application_on_async_read_speed(AVApplicationContext *h, AVAppAsyncReadSpeed *speed); ++ ++// http event ++void av_application_will_http_open(AVApplicationContext *h, void *obj, const char *url); ++void av_application_did_http_open(AVApplicationContext *h, void *obj, const char *url, int error, int http_code, int64_t filesize); ++void av_application_will_http_seek(AVApplicationContext *h, void *obj, const char *url, int64_t offset); ++void av_application_did_http_seek(AVApplicationContext *h, void *obj, const char *url, int64_t offset, int error, int http_code); ++//tcp event ++int av_application_on_tcp_will_open(AVApplicationContext *h); ++int av_application_on_tcp_did_open(AVApplicationContext *h, int error, int fd, AVAppTcpIOControl *control); ++//tcp speed ++void av_application_did_io_tcp_read(AVApplicationContext *h, void *obj, int bytes); ++ ++#endif /* AVUTIL_APPLICATION_H */ +diff --git a/libavformat/dns_cache.c b/libavformat/dns_cache.c +new file mode 100644 +index 0000000..aab2435 +--- /dev/null ++++ b/libavformat/dns_cache.c +@@ -0,0 +1,232 @@ ++/* ++ * copyright (c) 2017 Raymond Zheng ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg 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. ++ * ++ * FFmpeg 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 FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include "dns_cache.h" ++#include "libavutil/time.h" ++#include "libavutil/mem.h" ++#include "libavformat/network.h" ++#include ++#include ++ ++#if HAVE_PTHREADS ++#include ++#endif ++ ++typedef struct DnsCacheContext DnsCacheContext; ++typedef struct DnsCacheContext { ++ AVDictionary *dns_dictionary; ++ pthread_mutex_t dns_dictionary_mutex; ++ int initialized; ++} DnsCacheContext; ++ ++static DnsCacheContext *context = NULL; ++static pthread_once_t key_once = PTHREAD_ONCE_INIT; ++ ++static void inner_init(void) { ++ int ret = 0; ++ context = (DnsCacheContext *) av_mallocz(sizeof(DnsCacheContext)); ++ if (context) { ++ ret = pthread_mutex_init(&context->dns_dictionary_mutex, NULL); ++ if (!ret) { ++ context->initialized = 1; ++ } else { ++ av_freep(&context); ++ } ++ } ++} ++ ++static void free_private_addrinfo(struct addrinfo **p_ai) { ++ struct addrinfo *ai = *p_ai; ++ ++ if (ai) { ++ if (ai->ai_addr) { ++ av_freep(&ai->ai_addr); ++ } ++ av_freep(p_ai); ++ } ++} ++ ++static int inner_remove_dns_cache(const char *uri, DnsCacheEntry *dns_cache_entry) { ++ if (context && dns_cache_entry) { ++ if (dns_cache_entry->ref_count == 0) { ++ av_dict_set_int(&context->dns_dictionary, uri, 0, 0); ++ free_private_addrinfo(&dns_cache_entry->res); ++ av_freep(&dns_cache_entry); ++ } else { ++ dns_cache_entry->delete_flag = 1; ++ } ++ } ++ ++ return 0; ++} ++ ++static DnsCacheEntry *new_dns_cache_entry(const char *uri, struct addrinfo *cur_ai, int64_t timeout) { ++ DnsCacheEntry *new_entry = NULL; ++ int64_t cur_time = av_gettime_relative(); ++ ++ if (cur_time < 0) { ++ goto fail; ++ } ++ ++ new_entry = (DnsCacheEntry *) av_mallocz(sizeof(struct DnsCacheEntry)); ++ if (!new_entry) { ++ goto fail; ++ } ++ ++ new_entry->res = (struct addrinfo *) av_mallocz(sizeof(struct addrinfo)); ++ if (!new_entry->res) { ++ av_freep(&new_entry); ++ goto fail; ++ } ++ ++ memcpy(new_entry->res, cur_ai, sizeof(struct addrinfo)); ++ ++ new_entry->res->ai_addr = (struct sockaddr *) av_mallocz(sizeof(struct sockaddr)); ++ if (!new_entry->res->ai_addr) { ++ av_freep(&new_entry->res); ++ av_freep(&new_entry); ++ goto fail; ++ } ++ ++ memcpy(new_entry->res->ai_addr, cur_ai->ai_addr, sizeof(struct sockaddr)); ++ new_entry->res->ai_canonname = NULL; ++ new_entry->res->ai_next = NULL; ++ new_entry->ref_count = 0; ++ new_entry->delete_flag = 0; ++ new_entry->expired_time = cur_time + timeout * 1000; ++ ++ return new_entry; ++ ++fail: ++ return NULL; ++} ++ ++DnsCacheEntry *get_dns_cache_reference(const char *uri) { ++ AVDictionaryEntry *elem = NULL; ++ DnsCacheEntry *dns_cache_entry = NULL; ++ int64_t cur_time = av_gettime_relative(); ++ ++ if (cur_time < 0 || !uri || strlen(uri) == 0) { ++ return NULL; ++ } ++ ++ if (!context || !context->initialized) { ++#if HAVE_PTHREADS ++ pthread_once(&key_once, inner_init); ++#endif ++ } ++ ++ if (context && context->initialized) { ++ pthread_mutex_lock(&context->dns_dictionary_mutex); ++ elem = av_dict_get(context->dns_dictionary, uri, NULL, AV_DICT_MATCH_CASE); ++ if (elem) { ++ dns_cache_entry = (DnsCacheEntry *) (intptr_t) strtoll(elem->value, NULL, 10); ++ if (dns_cache_entry) { ++ if (dns_cache_entry->expired_time < cur_time) { ++ inner_remove_dns_cache(uri, dns_cache_entry); ++ dns_cache_entry = NULL; ++ } else { ++ dns_cache_entry->ref_count++; ++ } ++ } ++ } ++ pthread_mutex_unlock(&context->dns_dictionary_mutex); ++ } ++ ++ return dns_cache_entry; ++} ++ ++int release_dns_cache_reference(const char *uri, DnsCacheEntry **p_entry) { ++ DnsCacheEntry *entry = *p_entry; ++ ++ if (!uri || strlen(uri) == 0) { ++ return -1; ++ } ++ ++ if (context && context->initialized && entry) { ++ pthread_mutex_lock(&context->dns_dictionary_mutex); ++ entry->ref_count--; ++ if (entry->delete_flag && entry->ref_count == 0) { ++ inner_remove_dns_cache(uri, entry); ++ entry = NULL; ++ } ++ pthread_mutex_unlock(&context->dns_dictionary_mutex); ++ } ++ return 0; ++} ++ ++int remove_dns_cache_entry(const char *uri) { ++ AVDictionaryEntry *elem = NULL; ++ DnsCacheEntry *dns_cache_entry = NULL; ++ ++ if (!uri || strlen(uri) == 0) { ++ return -1; ++ } ++ ++ if (context && context->initialized) { ++ pthread_mutex_lock(&context->dns_dictionary_mutex); ++ elem = av_dict_get(context->dns_dictionary, uri, NULL, AV_DICT_MATCH_CASE); ++ if (elem) { ++ dns_cache_entry = (DnsCacheEntry *) (intptr_t) strtoll(elem->value, NULL, 10); ++ if (dns_cache_entry) { ++ inner_remove_dns_cache(uri, dns_cache_entry); ++ } ++ } ++ pthread_mutex_unlock(&context->dns_dictionary_mutex); ++ } ++ ++ return 0; ++} ++ ++int add_dns_cache_entry(const char *uri, struct addrinfo *cur_ai, int64_t timeout) { ++ DnsCacheEntry *new_entry = NULL; ++ DnsCacheEntry *old_entry = NULL; ++ AVDictionaryEntry *elem = NULL; ++ ++ if (!uri || strlen(uri) == 0 || timeout <= 0) { ++ goto fail; ++ } ++ ++ if (cur_ai == NULL || cur_ai->ai_addr == NULL) { ++ goto fail; ++ } ++ ++ if (context && context->initialized) { ++ pthread_mutex_lock(&context->dns_dictionary_mutex); ++ elem = av_dict_get(context->dns_dictionary, uri, NULL, AV_DICT_MATCH_CASE); ++ if (elem) { ++ old_entry = (DnsCacheEntry *) (intptr_t) strtoll(elem->value, NULL, 10); ++ if (old_entry) { ++ pthread_mutex_unlock(&context->dns_dictionary_mutex); ++ goto fail; ++ } ++ } ++ new_entry = new_dns_cache_entry(uri, cur_ai, timeout); ++ if (new_entry) { ++ av_dict_set_int(&context->dns_dictionary, uri, (int64_t) (intptr_t) new_entry, 0); ++ } ++ pthread_mutex_unlock(&context->dns_dictionary_mutex); ++ ++ return 0; ++ } ++ ++fail: ++ return -1; ++} +diff --git a/libavformat/dns_cache.h b/libavformat/dns_cache.h +new file mode 100644 +index 0000000..23c695e +--- /dev/null ++++ b/libavformat/dns_cache.h +@@ -0,0 +1,39 @@ ++/* ++ * copyright (c) 2017 Raymond Zheng ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg 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. ++ * ++ * FFmpeg 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 FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#ifndef AVUTIL_DNS_CACHE_H ++#define AVUTIL_DNS_CACHE_H ++ ++#include "libavutil/log.h" ++#include ++ ++typedef struct DnsCacheEntry { ++ volatile int ref_count; ++ volatile int delete_flag; ++ int64_t expired_time; ++ struct addrinfo *res; // construct by private function, not support ai_next and ai_canonname, can only be released using free_private_addrinfo ++} DnsCacheEntry; ++ ++DnsCacheEntry *get_dns_cache_reference(const char *uri); ++int release_dns_cache_reference(const char *uri, DnsCacheEntry **p_entry); ++int remove_dns_cache_entry(const char *uri); ++int add_dns_cache_entry(const char *uri, struct addrinfo *cur_ai, int64_t timeout); ++ ++#endif /* AVUTIL_DNS_CACHE_H */ +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.1/0003-restore-ijk-http-event-hooks.patch b/patches/ffmpeg-n8.1.1/0003-restore-ijk-http-event-hooks.patch new file mode 100644 index 000000000..de76b7df4 --- /dev/null +++ b/patches/ffmpeg-n8.1.1/0003-restore-ijk-http-event-hooks.patch @@ -0,0 +1,278 @@ +From 91b1bc44f636fd2c2a4a40fabffadfd6fa9d22e8 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Fri, 8 May 2026 16:30:34 +0800 +Subject: restore ijk http event hooks + +--- + libavformat/http.c | 32 +++++++++++++++++++++++-- + libavformat/tcp.c | 58 +++++++++++++++++++++++++++++++++++++++++----- + libavutil/error.h | 4 ++++ + 3 files changed, 86 insertions(+), 8 deletions(-) + +diff --git a/libavformat/http.c b/libavformat/http.c +index 52073ff..f4bf225 100644 +--- a/libavformat/http.c ++++ b/libavformat/http.c +@@ -48,6 +48,7 @@ + #include "os_support.h" + #include "url.h" + #include "version.h" ++#include "application.h" + + /* XXX: POST protocol is not completely implemented because ffmpeg uses + * only a subset of it. */ +@@ -158,6 +159,9 @@ typedef struct HTTPContext { + int64_t sum_latency; /* divide by nb_requests */ + int64_t max_latency; + int max_redirects; ++ char *tcp_hook; ++ char *app_ctx_intptr; ++ AVApplicationContext *app_ctx; + } HTTPContext; + + #define OFFSET(x) offsetof(HTTPContext, x) +@@ -206,6 +210,8 @@ static const AVOption options[] = { + { "reply_code", "The http status code to return to a client", OFFSET(reply_code), AV_OPT_TYPE_INT, { .i64 = 200}, INT_MIN, 599, E}, + { "short_seek_size", "Threshold to favor readahead over seek.", OFFSET(short_seek_size), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, D }, + { "max_redirects", "Maximum number of redirects", OFFSET(max_redirects), AV_OPT_TYPE_INT, { .i64 = MAX_REDIRECTS }, 0, INT_MAX, D }, ++ { "http-tcp-hook", "hook protocol on tcp", OFFSET(tcp_hook), AV_OPT_TYPE_STRING, { .str = "tcp" }, 0, 0, D | E }, ++ { "ijkapplication", "AVApplicationContext", OFFSET(app_ctx_intptr), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, .flags = D }, + { NULL } + }; + +@@ -236,6 +242,7 @@ static int http_open_cnx_internal(URLContext *h, AVDictionary **options) + char buf[1024], urlbuf[MAX_URL_SIZE]; + int port, use_proxy, err = 0; + HTTPContext *s = h->priv_data; ++ lower_proto = s->tcp_hook; + + av_url_split(proto, sizeof(proto), auth, sizeof(auth), + hostname, sizeof(hostname), &port, +@@ -313,6 +320,13 @@ static int http_open_cnx_internal(URLContext *h, AVDictionary **options) + + if (!s->hd) { + s->nb_connections++; ++ av_dict_set_intptr(options, "ijkapplication", (uintptr_t)s->app_ctx, 0); ++ ++ // AVDictionaryEntry *t = NULL; ++ // while ((t = av_dict_get(*options, "", t, AV_DICT_IGNORE_SUFFIX))) { ++ // av_log(NULL, AV_LOG_INFO, "%-*s: %-*s = %s\n", 12, "http open tcp", 28, t->key, t->value); ++ // } ++ + err = ffurl_open_whitelist(&s->hd, buf, AVIO_FLAG_READ_WRITE, + &h->interrupt_callback, options, + h->protocol_whitelist, h->protocol_blacklist, h); +@@ -771,6 +785,7 @@ static int http_open(URLContext *h, const char *uri, int flags, + { + HTTPContext *s = h->priv_data; + int ret; ++ s->app_ctx = (AVApplicationContext *)av_dict_strtoptr(s->app_ctx_intptr); + + if( s->seekable == 1 ) + h->is_streamed = 0; +@@ -808,7 +823,9 @@ static int http_open(URLContext *h, const char *uri, int flags, + if (s->listen) { + return http_listen(h, uri, flags, options); + } ++ av_application_will_http_open(s->app_ctx, (void*)h, uri); + ret = http_open_cnx(h, options); ++ av_application_did_http_open(s->app_ctx, (void*)h, uri, ret, s->http_code, s->filesize); + bail_out: + if (ret < 0) { + av_dict_free(&s->chained_options); +@@ -1763,7 +1780,14 @@ static int http_buf_read(URLContext *h, uint8_t *buf, int size) + return AVERROR_EOF; + if (s->off == target_end && target_end < file_end) + return AVERROR(EAGAIN); /* reached end of content range */ +- len = ffurl_read(s->hd, buf, size); ++ len = size; ++ if (s->filesize > 0 && s->filesize != UINT64_MAX && s->filesize != INT32_MAX) { ++ int64_t unread = s->filesize - s->off; ++ if (len > unread) ++ len = (int)unread; ++ } ++ if (len > 0) ++ len = ffurl_read(s->hd, buf, len); + if ((!len || len == AVERROR_EOF) && + (!s->willclose || s->chunksize == UINT64_MAX) && s->off < target_end) { + av_log(h, AV_LOG_ERROR, +@@ -2158,7 +2182,9 @@ static int64_t http_seek_internal(URLContext *h, int64_t off, int whence, int fo + } + + /* if it fails, continue on old connection */ ++ av_application_will_http_seek(s->app_ctx, (void*)h, s->location, off); + if ((ret = http_open_cnx(h, &options)) < 0) { ++ av_application_did_http_seek(s->app_ctx, (void*)h, s->location, off, ret, s->http_code); + av_dict_free(&options); + memcpy(s->buffer, old_buf, old_buf_size); + s->buf_ptr = s->buffer; +@@ -2167,6 +2193,7 @@ static int64_t http_seek_internal(URLContext *h, int64_t off, int whence, int fo + s->off = old_off; + return ret; + } ++ av_application_did_http_seek(s->app_ctx, (void*)h, s->location, off, ret, s->http_code); + av_dict_free(&options); + ffurl_close(old_hd); + return off; +@@ -2260,6 +2287,7 @@ static int http_proxy_open(URLContext *h, const char *uri, int flags) + HTTPAuthType cur_auth_type; + char *authstr; + ++ s->app_ctx = (AVApplicationContext *)av_dict_strtoptr(s->app_ctx_intptr); + if( s->seekable == 1 ) + h->is_streamed = 0; + else +@@ -2272,7 +2300,7 @@ static int http_proxy_open(URLContext *h, const char *uri, int flags) + if (*path == '/') + path++; + +- ff_url_join(lower_url, sizeof(lower_url), "tcp", NULL, hostname, port, ++ ff_url_join(lower_url, sizeof(lower_url), s->tcp_hook, NULL, hostname, port, + NULL); + redo: + ret = ffurl_open_whitelist(&s->hd, lower_url, AVIO_FLAG_READ_WRITE, +diff --git a/libavformat/tcp.c b/libavformat/tcp.c +index ce9f69a..69b1e12 100644 +--- a/libavformat/tcp.c ++++ b/libavformat/tcp.c +@@ -24,7 +24,8 @@ + #include "libavutil/parseutils.h" + #include "libavutil/opt.h" + #include "libavutil/time.h" +- ++#include "libavutil/avstring.h" ++#include "application.h" + #include "internal.h" + #include "network.h" + #include "os_support.h" +@@ -49,6 +50,9 @@ typedef struct TCPContext { + #if !HAVE_WINSOCK2_H + int tcp_mss; + #endif /* !HAVE_WINSOCK2_H */ ++ ++ char * app_ctx_intptr; ++ AVApplicationContext *app_ctx; + } TCPContext; + + #define OFFSET(x) offsetof(TCPContext, x) +@@ -67,6 +71,8 @@ static const AVOption options[] = { + #if !HAVE_WINSOCK2_H + { "tcp_mss", "Maximum segment size for outgoing TCP packets", OFFSET(tcp_mss), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E }, + #endif /* !HAVE_WINSOCK2_H */ ++ { "ijkapplication", "AVApplicationContext", OFFSET(app_ctx_intptr), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, .flags = D }, ++ { "connect_timeout", "set connect timeout (in microseconds) of socket", OFFSET(open_timeout), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E }, + { NULL } + }; + +@@ -155,7 +161,21 @@ static int tcp_open(URLContext *h, const char *uri, int flags) + int ret; + char hostname[1024],proto[1024],path[1024]; + char portstr[10]; +- s->open_timeout = 5000000; ++ AVAppTcpIOControl control = {0}; ++ ++ int ret2; ++ if (s->open_timeout < 0) { ++ s->open_timeout = 15000000; ++ } ++ // av_log(NULL, AV_LOG_INFO, "xql tcp_open uri %s", uri); ++ // av_log(NULL, AV_LOG_INFO, "%-*s: %-*s = %s\n", 12, "xql tcp_open verify", 28, "ijkapplication", s->app_ctx_intptr); ++ // av_log(NULL, AV_LOG_INFO, "%-*s: %-*s = %d\n", 12, "xql tcp_open verify", 28, "connect_timeout", s->open_timeout); ++ // av_log(NULL, AV_LOG_INFO, "%-*s: %-*s = %d\n", 12, "xql tcp_open verify", 28, "addrinfo_one_by_one", s->addrinfo_one_by_one); ++ // av_log(NULL, AV_LOG_INFO, "%-*s: %-*s = %d\n", 12, "xql tcp_open verify", 28, "addrinfo_timeout", s->addrinfo_timeout); ++ // av_log(NULL, AV_LOG_INFO, "%-*s: %-*s = %d\n", 12, "xql tcp_open verify", 28, "dns_cache_timeout", s->dns_cache_timeout); ++ // av_log(NULL, AV_LOG_INFO, "%-*s: %-*s = %d\n", 12, "xql tcp_open verify", 28, "dns_cache_clear", s->dns_cache_clear); ++ ++ s->app_ctx = (AVApplicationContext *)av_dict_strtoptr(s->app_ctx_intptr); + + av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname), + &port, path, sizeof(path), uri); +@@ -172,7 +192,7 @@ static int tcp_open(URLContext *h, const char *uri, int flags) + return ret; + } + if (s->rw_timeout >= 0) { +- s->open_timeout = ++ //s->open_timeout = + h->rw_timeout = s->rw_timeout; + } + hints.ai_family = AF_UNSPEC; +@@ -230,9 +250,24 @@ static int tcp_open(URLContext *h, const char *uri, int flags) + // Socket descriptor already closed here. Safe to overwrite to client one. + fd = ret; + } else { +- ret = ff_connect_parallel(ai, s->open_timeout / 1000, 3, h, &fd, customize_fd, s); ++ ret = av_application_on_tcp_will_open(s->app_ctx); ++ if (ret) { ++ av_log(NULL, AV_LOG_WARNING, "terminated by application in AVAPP_CTRL_WILL_TCP_OPEN"); ++ goto fail1; ++ } ++ ret = ff_connect_parallel(cur_ai, s->open_timeout / 1000, 3, h, &fd, customize_fd, s); ++ ++ ret2 = av_application_on_tcp_did_open(s->app_ctx, ret, fd, &control); ++ + if (ret < 0) + goto fail1; ++ ++ if (ret2) { ++ av_log(NULL, AV_LOG_WARNING, "terminated by application in AVAPP_CTRL_DID_TCP_OPEN"); ++ ret = ret2; ++ goto fail1; ++ } ++ av_log(NULL, AV_LOG_INFO, "tcp did open uri = %s, ip = %s\n", uri , control.ip); + } + + h->is_streamed = 1; +@@ -273,12 +308,18 @@ static int tcp_read(URLContext *h, uint8_t *buf, int size) + + if (!(h->flags & AVIO_FLAG_NONBLOCK)) { + ret = ff_network_wait_fd_timeout(s->fd, 0, h->rw_timeout, &h->interrupt_callback); +- if (ret) ++ if (ret) { ++ if (ret == AVERROR(ETIMEDOUT)) { ++ ret = AVERROR_TCP_READ_TIMEOUT; ++ } + return ret; ++ } + } + ret = recv(s->fd, buf, size, 0); + if (ret == 0) + return AVERROR_EOF; ++ if (ret > 0) ++ av_application_did_io_tcp_read(s->app_ctx, (void*)h, ret); + return ret < 0 ? ff_neterrno() : ret; + } + +@@ -289,9 +330,14 @@ static int tcp_write(URLContext *h, const uint8_t *buf, int size) + + if (!(h->flags & AVIO_FLAG_NONBLOCK)) { + ret = ff_network_wait_fd_timeout(s->fd, 1, h->rw_timeout, &h->interrupt_callback); +- if (ret) ++ if (ret) { ++ if (ret == AVERROR(ETIMEDOUT)) { ++ ret = AVERROR_TCP_WRITE_TIMEOUT; ++ } + return ret; ++ } + } ++ + ret = send(s->fd, buf, size, MSG_NOSIGNAL); + return ret < 0 ? ff_neterrno() : ret; + } +diff --git a/libavutil/error.h b/libavutil/error.h +index 1efa86c..9dee755 100644 +--- a/libavutil/error.h ++++ b/libavutil/error.h +@@ -85,6 +85,10 @@ + + #define AV_ERROR_MAX_STRING_SIZE 64 + ++#define AVERROR_TCP_CONNECT_TIMEOUT -1001 ++#define AVERROR_TCP_READ_TIMEOUT -1002 ++#define AVERROR_TCP_WRITE_TIMEOUT -1003 ++ + /** + * Put a description of the AVERROR code errnum in errbuf. + * In case of failure the global variable errno is set to indicate the +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.1/0004-restore-ijk-tcp-dns-cache.patch b/patches/ffmpeg-n8.1.1/0004-restore-ijk-tcp-dns-cache.patch new file mode 100644 index 000000000..29a6b9611 --- /dev/null +++ b/patches/ffmpeg-n8.1.1/0004-restore-ijk-tcp-dns-cache.patch @@ -0,0 +1,404 @@ +From acffa5bb5305c8f28419c0f476e8d123f09d9bcd Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Fri, 8 May 2026 16:39:50 +0800 +Subject: restore ijk tcp dns cache + +--- + libavformat/tcp.c | 324 ++++++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 310 insertions(+), 14 deletions(-) + +diff --git a/libavformat/tcp.c b/libavformat/tcp.c +index 69b1e12..46b48e6 100644 +--- a/libavformat/tcp.c ++++ b/libavformat/tcp.c +@@ -26,6 +26,7 @@ + #include "libavutil/time.h" + #include "libavutil/avstring.h" + #include "application.h" ++#include "dns_cache.h" + #include "internal.h" + #include "network.h" + #include "os_support.h" +@@ -33,6 +34,9 @@ + #if HAVE_POLL_H + #include + #endif ++#if HAVE_PTHREADS ++#include ++#endif + + typedef struct TCPContext { + const AVClass *class; +@@ -52,6 +56,10 @@ typedef struct TCPContext { + #endif /* !HAVE_WINSOCK2_H */ + + char * app_ctx_intptr; ++ int addrinfo_one_by_one; ++ int addrinfo_timeout; ++ int64_t dns_cache_timeout; ++ int dns_cache_clear; + AVApplicationContext *app_ctx; + } TCPContext; + +@@ -73,9 +81,261 @@ static const AVOption options[] = { + #endif /* !HAVE_WINSOCK2_H */ + { "ijkapplication", "AVApplicationContext", OFFSET(app_ctx_intptr), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, .flags = D }, + { "connect_timeout", "set connect timeout (in microseconds) of socket", OFFSET(open_timeout), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E }, ++ { "addrinfo_one_by_one", "parse addrinfo one by one in getaddrinfo()", OFFSET(addrinfo_one_by_one), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, .flags = D|E }, ++ { "addrinfo_timeout", "set timeout (in microseconds) for getaddrinfo()", OFFSET(addrinfo_timeout), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E }, ++ { "dns_cache_timeout", "dns cache TTL (in microseconds)", OFFSET(dns_cache_timeout), AV_OPT_TYPE_INT, { .i64 = 0 }, -1, INT64_MAX, .flags = D|E }, ++ { "dns_cache_clear", "clear dns cache", OFFSET(dns_cache_clear), AV_OPT_TYPE_INT, { .i64 = 0}, -1, INT_MAX, .flags = D|E }, + { NULL } + }; + ++int ijk_tcp_getaddrinfo_nonblock(const char *hostname, const char *servname, ++ const struct addrinfo *hints, struct addrinfo **res, ++ int64_t timeout, ++ const AVIOInterruptCB *int_cb, int one_by_one); ++#ifdef HAVE_PTHREADS ++ ++typedef struct TCPAddrinfoRequest ++{ ++ AVBufferRef *buffer; ++ ++ pthread_mutex_t mutex; ++ pthread_cond_t cond; ++ ++ AVIOInterruptCB interrupt_callback; ++ ++ char *hostname; ++ char *servname; ++ struct addrinfo hints; ++ struct addrinfo *res; ++ ++ volatile int finished; ++ int last_error; ++} TCPAddrinfoRequest; ++ ++static void tcp_getaddrinfo_request_free(TCPAddrinfoRequest *req) ++{ ++ av_assert0(req); ++ if (req->res) { ++ freeaddrinfo(req->res); ++ req->res = NULL; ++ } ++ ++ av_freep(&req->servname); ++ av_freep(&req->hostname); ++ pthread_cond_destroy(&req->cond); ++ pthread_mutex_destroy(&req->mutex); ++ av_freep(&req); ++} ++ ++static void tcp_getaddrinfo_request_free_buffer(void *opaque, uint8_t *data) ++{ ++ av_assert0(opaque); ++ TCPAddrinfoRequest *req = (TCPAddrinfoRequest *)opaque; ++ tcp_getaddrinfo_request_free(req); ++} ++ ++static int tcp_getaddrinfo_request_create(TCPAddrinfoRequest **request, ++ const char *hostname, ++ const char *servname, ++ const struct addrinfo *hints, ++ const AVIOInterruptCB *int_cb) ++{ ++ TCPAddrinfoRequest *req = (TCPAddrinfoRequest *) av_mallocz(sizeof(TCPAddrinfoRequest)); ++ if (!req) ++ return AVERROR(ENOMEM); ++ ++ if (pthread_mutex_init(&req->mutex, NULL)) { ++ av_freep(&req); ++ return AVERROR(ENOMEM); ++ } ++ ++ if (pthread_cond_init(&req->cond, NULL)) { ++ pthread_mutex_destroy(&req->mutex); ++ av_freep(&req); ++ return AVERROR(ENOMEM); ++ } ++ ++ if (int_cb) ++ req->interrupt_callback = *int_cb; ++ ++ if (hostname) { ++ req->hostname = av_strdup(hostname); ++ if (!req->hostname) ++ goto fail; ++ } ++ ++ if (servname) { ++ req->servname = av_strdup(servname); ++ if (!req->hostname) ++ goto fail; ++ } ++ ++ if (hints) { ++ req->hints.ai_family = hints->ai_family; ++ req->hints.ai_socktype = hints->ai_socktype; ++ req->hints.ai_protocol = hints->ai_protocol; ++ req->hints.ai_flags = hints->ai_flags; ++ } ++ ++ req->buffer = av_buffer_create(NULL, 0, tcp_getaddrinfo_request_free_buffer, req, 0); ++ if (!req->buffer) ++ goto fail; ++ ++ *request = req; ++ return 0; ++fail: ++ tcp_getaddrinfo_request_free(req); ++ return AVERROR(ENOMEM); ++} ++ ++static void *tcp_getaddrinfo_worker(void *arg) ++{ ++ TCPAddrinfoRequest *req = arg; ++ ++ getaddrinfo(req->hostname, req->servname, &req->hints, &req->res); ++ pthread_mutex_lock(&req->mutex); ++ req->finished = 1; ++ pthread_cond_signal(&req->cond); ++ pthread_mutex_unlock(&req->mutex); ++ av_buffer_unref(&req->buffer); ++ return NULL; ++} ++ ++static void *tcp_getaddrinfo_one_by_one_worker(void *arg) ++{ ++ struct addrinfo *temp_addrinfo = NULL; ++ struct addrinfo *cur = NULL; ++ int ret = EAI_FAIL; ++ int i = 0; ++ int option_length = 0; ++ ++ TCPAddrinfoRequest *req = (TCPAddrinfoRequest *)arg; ++ ++ int family_option[2] = {AF_INET, AF_INET6}; ++ ++ option_length = sizeof(family_option) / sizeof(family_option[0]); ++ ++ for (; i < option_length; ++i) { ++ struct addrinfo *hint = &req->hints; ++ hint->ai_family = family_option[i]; ++ ret = getaddrinfo(req->hostname, req->servname, hint, &temp_addrinfo); ++ if (ret) { ++ req->last_error = ret; ++ continue; ++ } ++ pthread_mutex_lock(&req->mutex); ++ if (!req->res) { ++ req->res = temp_addrinfo; ++ } else { ++ cur = req->res; ++ while (cur->ai_next) ++ cur = cur->ai_next; ++ cur->ai_next = temp_addrinfo; ++ } ++ pthread_mutex_unlock(&req->mutex); ++ } ++ pthread_mutex_lock(&req->mutex); ++ req->finished = 1; ++ pthread_cond_signal(&req->cond); ++ pthread_mutex_unlock(&req->mutex); ++ av_buffer_unref(&req->buffer); ++ return NULL; ++} ++ ++int ijk_tcp_getaddrinfo_nonblock(const char *hostname, const char *servname, ++ const struct addrinfo *hints, struct addrinfo **res, ++ int64_t timeout, ++ const AVIOInterruptCB *int_cb, int one_by_one) ++{ ++ int ret; ++ int64_t start; ++ int64_t now; ++ AVBufferRef *req_ref = NULL; ++ TCPAddrinfoRequest *req = NULL; ++ pthread_t work_thread; ++ ++ if (hostname && !hostname[0]) ++ hostname = NULL; ++ av_log(NULL, AV_LOG_DEBUG, "dns getaddrinfo uri = %s\n", hostname); ++ if (timeout <= 0) ++ return getaddrinfo(hostname, servname, hints, res); ++ av_log(NULL, AV_LOG_DEBUG, "dns tcp_getaddrinfo_request_create uri = %s\n", hostname); ++ ret = tcp_getaddrinfo_request_create(&req, hostname, servname, hints, int_cb); ++ if (ret) ++ goto fail; ++ ++ req_ref = av_buffer_ref(req->buffer); ++ if (req_ref == NULL) { ++ ret = AVERROR(ENOMEM); ++ goto fail; ++ } ++ ++ /* FIXME: using a thread pool would be better. */ ++ if (one_by_one) ++ ret = pthread_create(&work_thread, NULL, tcp_getaddrinfo_one_by_one_worker, req); ++ else ++ ret = pthread_create(&work_thread, NULL, tcp_getaddrinfo_worker, req); ++ ++ if (ret) { ++ ret = AVERROR(ret); ++ goto fail; ++ } ++ ++ pthread_detach(work_thread); ++ ++ start = av_gettime(); ++ now = start; ++ ++ pthread_mutex_lock(&req->mutex); ++ while (1) { ++ int64_t wait_time = now + 100000; ++ struct timespec tv = { .tv_sec = wait_time / 1000000, ++ .tv_nsec = (wait_time % 1000000) * 1000 }; ++ ++ if (req->finished || (start + timeout < now)) { ++ if (req->res) { ++ ret = 0; ++ *res = req->res; ++ req->res = NULL; ++ } else { ++ ret = req->last_error ? req->last_error : AVERROR_EXIT; ++ } ++ break; ++ } ++#if defined(__ANDROID__) && defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC) ++ ret = pthread_cond_timedwait_monotonic_np(&req->cond, &req->mutex, &tv); ++#else ++ ret = pthread_cond_timedwait(&req->cond, &req->mutex, &tv); ++#endif ++ if (ret != 0 && ret != ETIMEDOUT) { ++ av_log(NULL, AV_LOG_ERROR, "pthread_cond_timedwait failed: %d\n", ret); ++ ret = AVERROR_EXIT; ++ break; ++ } ++ ++ if (ff_check_interrupt(&req->interrupt_callback)) { ++ ret = AVERROR_EXIT; ++ break; ++ } ++ ++ now = av_gettime(); ++ } ++ pthread_mutex_unlock(&req->mutex); ++fail: ++ av_buffer_unref(&req_ref); ++ return ret; ++} ++ ++#else ++int ijk_tcp_getaddrinfo_nonblock(const char *hostname, const char *servname, ++ const struct addrinfo *hints, struct addrinfo **res, ++ int64_t timeout, ++ const AVIOInterruptCB *int_cb) ++{ ++ return getaddrinfo(hostname, servname, hints, res); ++} ++#endif ++ + static const AVClass tcp_class = { + .class_name = "tcp", + .item_name = av_default_item_name, +@@ -162,7 +422,7 @@ static int tcp_open(URLContext *h, const char *uri, int flags) + char hostname[1024],proto[1024],path[1024]; + char portstr[10]; + AVAppTcpIOControl control = {0}; +- ++ DnsCacheEntry *dns_entry = NULL; + int ret2; + if (s->open_timeout < 0) { + s->open_timeout = 15000000; +@@ -200,18 +460,37 @@ static int tcp_open(URLContext *h, const char *uri, int flags) + snprintf(portstr, sizeof(portstr), "%d", port); + if (s->listen) + hints.ai_flags |= AI_PASSIVE; +- if (!hostname[0]) +- ret = getaddrinfo(NULL, portstr, &hints, &ai); +- else +- ret = getaddrinfo(hostname, portstr, &hints, &ai); +- if (ret) { +- av_log(h, AV_LOG_ERROR, +- "Failed to resolve hostname %s: %s\n", +- hostname, gai_strerror(ret)); +- return AVERROR(EIO); ++ if (s->dns_cache_timeout > 0) { ++ if (s->dns_cache_clear) { ++ remove_dns_cache_entry(uri); ++ } else { ++ dns_entry = get_dns_cache_reference(uri); ++ } + } + +- cur_ai = ai; ++ if (!dns_entry) { ++#ifdef HAVE_PTHREADS ++ ret = ijk_tcp_getaddrinfo_nonblock(hostname, portstr, &hints, &ai, s->addrinfo_timeout, &h->interrupt_callback, s->addrinfo_one_by_one); ++#else ++ if (s->addrinfo_timeout > 0) ++ av_log(h, AV_LOG_WARNING, "Ignore addrinfo_timeout without pthreads support.\n"); ++ if (!hostname[0]) ++ ret = getaddrinfo(NULL, portstr, &hints, &ai); ++ else ++ ret = getaddrinfo(hostname, portstr, &hints, &ai); ++#endif ++ if (ret) { ++ av_log(h, AV_LOG_ERROR, ++ "Failed to resolve hostname %s: %s\n", ++ hostname, gai_strerror(ret)); ++ return AVERROR(EIO); ++ } ++ ++ cur_ai = ai; ++ } else { ++ av_log(NULL, AV_LOG_DEBUG, "hit dns cache uri = %s\n", uri); ++ cur_ai = dns_entry->res; ++ } + + #if HAVE_STRUCT_SOCKADDR_IN6 + // workaround for IOS9 getaddrinfo in IPv6 only network use hardcode IPv4 address can not resolve port number. +@@ -267,19 +546,36 @@ static int tcp_open(URLContext *h, const char *uri, int flags) + ret = ret2; + goto fail1; + } +- av_log(NULL, AV_LOG_INFO, "tcp did open uri = %s, ip = %s\n", uri , control.ip); ++ ++ if (!dns_entry && !strstr(uri, control.ip) && s->dns_cache_timeout > 0) { ++ add_dns_cache_entry(uri, cur_ai, s->dns_cache_timeout); ++ av_log(NULL, AV_LOG_DEBUG, "add dns cache uri = %s, ip = %s\n", uri , control.ip); ++ } ++ av_log(NULL, AV_LOG_DEBUG, "tcp did open uri = %s, ip = %s\n", uri , control.ip); + } + + h->is_streamed = 1; + s->fd = fd; + +- freeaddrinfo(ai); ++ if (dns_entry) { ++ release_dns_cache_reference(uri, &dns_entry); ++ } else { ++ freeaddrinfo(ai); ++ } + return 0; + + fail1: + if (fd >= 0) + closesocket(fd); +- freeaddrinfo(ai); ++ ++ if (dns_entry) { ++ av_log(NULL, AV_LOG_ERROR, "hit dns cache but connect fail uri = %s, ip = %s\n", uri , control.ip); ++ release_dns_cache_reference(uri, &dns_entry); ++ remove_dns_cache_entry(uri); ++ } else { ++ freeaddrinfo(cur_ai); ++ } ++ + return ret; + } + +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.1/0005-restore-ijk-custom-protocols-and-demuxers-except-lon.patch b/patches/ffmpeg-n8.1.1/0005-restore-ijk-custom-protocols-and-demuxers-except-lon.patch new file mode 100644 index 000000000..220443a69 --- /dev/null +++ b/patches/ffmpeg-n8.1.1/0005-restore-ijk-custom-protocols-and-demuxers-except-lon.patch @@ -0,0 +1,277 @@ +From 75225b69a8442347087c02a7ee24944e7c3b41e6 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Fri, 8 May 2026 17:02:15 +0800 +Subject: restore ijk custom protocols and demuxers except long + url and async + +--- + libavcodec/Makefile | 1 + + libavformat/Makefile | 10 ++++ + libavformat/allformats.c | 3 ++ + libavformat/demux.c | 13 +++++ + libavformat/demux.h | 2 + + libavformat/ijkutils.c | 107 +++++++++++++++++++++++++++++++++++++++ + libavformat/protocols.c | 6 +++ + libavutil/Makefile | 3 +- + 8 files changed, 144 insertions(+), 1 deletion(-) + create mode 100644 libavformat/ijkutils.c + +diff --git a/libavcodec/Makefile b/libavcodec/Makefile +index 1410bd8..4445d9e 100644 +--- a/libavcodec/Makefile ++++ b/libavcodec/Makefile +@@ -26,6 +26,7 @@ HEADERS = ac3_parser.h \ + version_major.h \ + videotoolbox.h \ + vorbis_parser.h \ ++ packet_internal.h \ + + OBJS = ac3_parser.o \ + adts_parser.o \ +diff --git a/libavformat/Makefile b/libavformat/Makefile +index 3c95fc4..85ed05b 100644 +--- a/libavformat/Makefile ++++ b/libavformat/Makefile +@@ -7,6 +7,15 @@ HEADERS = avformat.h \ + version_major.h \ + application.h \ + dns_cache.h \ ++ demux.h \ ++ avc.h \ ++ url.h \ ++ internal.h \ ++ avio_internal.h \ ++ flv.h \ ++ id3v2.h \ ++ os_support.h \ ++ metadata.h \ + + + OBJS = allformats.o \ +@@ -40,6 +49,7 @@ OBJS = allformats.o \ + vpcc.o \ + application.o \ + dns_cache.o \ ++ ijkutils.o \ + + OBJS-$(HAVE_LIBC_MSVCRT) += file_open.o + +diff --git a/libavformat/allformats.c b/libavformat/allformats.c +index 6ec361f..0c2d5ca 100644 +--- a/libavformat/allformats.c ++++ b/libavformat/allformats.c +@@ -584,6 +584,9 @@ extern const FFInputFormat ff_libmodplug_demuxer; + extern const FFInputFormat ff_libopenmpt_demuxer; + extern const FFInputFormat ff_vapoursynth_demuxer; + ++// ijk custom demuxers ++extern FFInputFormat ff_ijklivehook_demuxer; ++ + #include "libavformat/muxer_list.c" + #include "libavformat/demuxer_list.c" + +diff --git a/libavformat/demux.c b/libavformat/demux.c +index a190923..05ab1e1 100644 +--- a/libavformat/demux.c ++++ b/libavformat/demux.c +@@ -235,6 +235,7 @@ int avformat_open_input(AVFormatContext **ps, const char *filename, + AVFormatContext *s = *ps; + FFFormatContext *si; + AVDictionary *tmp = NULL; ++ AVDictionary *tmp2 = NULL; + ID3v2ExtraMeta *id3v2_extra_meta = NULL; + int ret = 0; + +@@ -318,6 +319,16 @@ int avformat_open_input(AVFormatContext **ps, const char *filename, + /* e.g. AVFMT_NOFILE formats will not have an AVIOContext */ + if (s->pb && is_id3v2_format(s->iformat)) + ff_id3v2_read_dict(s->pb, &si->id3v2_meta, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta); ++ ++ if (ffifmt(s->iformat)->read_header2) { ++ if (options) ++ av_dict_copy(&tmp2,*options, 0); ++ if ((ret = ffifmt(s->iformat)->read_header2(s, &tmp2)) < 0) { ++ if (ffifmt(s->iformat)->flags_internal & FF_INFMT_FLAG_INIT_CLEANUP) ++ goto close; ++ goto fail; ++ } ++ } else + + if (ffifmt(s->iformat)->read_header) + if ((ret = ffifmt(s->iformat)->read_header(s)) < 0) { +@@ -357,6 +368,7 @@ int avformat_open_input(AVFormatContext **ps, const char *filename, + if (options) { + av_dict_free(options); + *options = tmp; ++ av_dict_free(&tmp2); + } + *ps = s; + return 0; +@@ -367,6 +379,7 @@ close: + fail: + ff_id3v2_free_extra_meta(&id3v2_extra_meta); + av_dict_free(&tmp); ++ av_dict_free(&tmp2); + if (s->pb && !(s->flags & AVFMT_FLAG_CUSTOM_IO)) + avio_closep(&s->pb); + avformat_free_context(s); +diff --git a/libavformat/demux.h b/libavformat/demux.h +index f09afc8..531c433 100644 +--- a/libavformat/demux.h ++++ b/libavformat/demux.h +@@ -98,6 +98,8 @@ typedef struct FFInputFormat { + */ + int (*read_header)(struct AVFormatContext *); + ++ int (*read_header2)(struct AVFormatContext *, AVDictionary **options); ++ + /** + * Read one packet and put it in 'pkt'. pts and flags are also + * set. 'avformat_new_stream' can be called only if the flag +diff --git a/libavformat/ijkutils.c b/libavformat/ijkutils.c +new file mode 100644 +index 0000000..0314c48 +--- /dev/null ++++ b/libavformat/ijkutils.c +@@ -0,0 +1,107 @@ ++/* ++ * utils.c ++ * ++ * Copyright (c) 2003 Fabrice Bellard ++ * Copyright (c) 2013 Zhang Rui ++ * ++ * This file is part of ijkPlayer. ++ * ++ * ijkPlayer 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. ++ * ++ * ijkPlayer 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 ijkPlayer; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include ++#include "url.h" ++#include "demux.h" ++ ++ ++#define IJK_FF_PROTOCOL(x) \ ++extern URLProtocol ff_##x##_protocol; \ ++int ijkav_register_##x##_protocol(URLProtocol *protocol, int protocol_size); \ ++int ijkav_register_##x##_protocol(URLProtocol *protocol, int protocol_size) \ ++{ \ ++ if (protocol_size != sizeof(URLProtocol)) { \ ++ av_log(NULL, AV_LOG_ERROR, "ijkav_register_##x##_protocol: ABI mismatch.\n"); \ ++ return -1; \ ++ } \ ++ memcpy(&ff_##x##_protocol, protocol, protocol_size); \ ++ return 0; \ ++} ++ ++#define IJK_DUMMY_PROTOCOL(x) \ ++IJK_FF_PROTOCOL(x); \ ++static const AVClass ijk_##x##_context_class = { \ ++ .class_name = #x, \ ++ .item_name = av_default_item_name, \ ++ .version = LIBAVUTIL_VERSION_INT, \ ++ }; \ ++ \ ++URLProtocol ff_##x##_protocol = { \ ++ .name = #x, \ ++ .url_open2 = ijkdummy_open, \ ++ .priv_data_size = 1, \ ++ .priv_data_class = &ijk_##x##_context_class, \ ++}; ++ ++static int ijkdummy_open(URLContext *h, const char *arg, int flags, AVDictionary **options) ++{ ++ return -1; ++} ++ ++static int ijkdummy_probe(const AVProbeData *p) ++{ ++ return AVERROR_INVALIDDATA; ++} ++ ++IJK_DUMMY_PROTOCOL(ijkmediadatasource); ++IJK_DUMMY_PROTOCOL(ijkhttphook); ++IJK_DUMMY_PROTOCOL(ijksegment); ++IJK_DUMMY_PROTOCOL(ijktcphook); ++IJK_DUMMY_PROTOCOL(ijkio); ++ ++#define IJK_FF_DEMUXER(x) \ ++extern FFInputFormat ff_##x##_demuxer; \ ++int ijkav_register_##x##_demuxer(FFInputFormat *demuxer, int demuxer_size); \ ++int ijkav_register_##x##_demuxer(FFInputFormat *demuxer, int demuxer_size) \ ++{ \ ++ if (demuxer_size != sizeof(FFInputFormat)) { \ ++ av_log(NULL, AV_LOG_ERROR, "ijkav_register_##x##_demuxer: ABI mismatch.\n"); \ ++ return -1; \ ++ } \ ++ memcpy(&ff_##x##_demuxer, demuxer, demuxer_size); \ ++ return 0; \ ++} ++ ++#define IJK_DUMMY_DEMUXER(x) \ ++IJK_FF_DEMUXER(x); \ ++static const AVClass ijk_##x##_demuxer_class = { \ ++ .class_name = #x, \ ++ .item_name = av_default_item_name, \ ++ .version = LIBAVUTIL_VERSION_INT, \ ++ }; \ ++ \ ++FFInputFormat ff_##x##_demuxer = { \ ++ .p.name = #x, \ ++ .p.priv_class = &ijk_##x##_demuxer_class, \ ++ .priv_data_size = 1, \ ++ .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, \ ++ .read_probe = ijkdummy_probe, \ ++}; ++ ++/* ++ libavformat/ijkutils.c:99:1: error: field designator 'priv_data_size' does not refer to any field in type 'AVInputFormat' (aka 'struct AVInputFormat') ++ 99 | IJK_DUMMY_DEMUXER(ijklivehook); ++ ++ */ ++IJK_DUMMY_DEMUXER(ijklivehook); +diff --git a/libavformat/protocols.c b/libavformat/protocols.c +index 207b6bf..de0926b 100644 +--- a/libavformat/protocols.c ++++ b/libavformat/protocols.c +@@ -79,6 +79,12 @@ extern const URLProtocol ff_libzmq_protocol; + extern const URLProtocol ff_ipfs_gateway_protocol; + extern const URLProtocol ff_ipns_gateway_protocol; + ++extern const URLProtocol ff_ijkhttphook_protocol; ++extern const URLProtocol ff_ijkmediadatasource_protocol; ++extern const URLProtocol ff_ijksegment_protocol; ++extern const URLProtocol ff_ijktcphook_protocol; ++extern const URLProtocol ff_ijkio_protocol; ++ + #include "libavformat/protocol_list.c" + + const AVClass *ff_urlcontext_child_class_iterate(void **iter) +diff --git a/libavutil/Makefile b/libavutil/Makefile +index c524189..a9fbf99 100644 +--- a/libavutil/Makefile ++++ b/libavutil/Makefile +@@ -99,7 +99,8 @@ HEADERS = adler32.h \ + xtea.h \ + tea.h \ + tx.h \ +- video_hint.h ++ video_hint.h \ ++ thread.h \ + + ARCH_HEADERS = bswap.h \ + intmath.h \ +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.1/0006-add-4-dummy-ijkplaceholder-demuxers.patch b/patches/ffmpeg-n8.1.1/0006-add-4-dummy-ijkplaceholder-demuxers.patch new file mode 100644 index 000000000..535f45598 --- /dev/null +++ b/patches/ffmpeg-n8.1.1/0006-add-4-dummy-ijkplaceholder-demuxers.patch @@ -0,0 +1,41 @@ +From 44ce37e539f1db0fce3804b192f2e48a7d72e040 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Fri, 8 May 2026 17:02:34 +0800 +Subject: add 4 dummy ijkplaceholder demuxers + +--- + libavformat/allformats.c | 4 ++++ + libavformat/ijkutils.c | 4 ++++ + 2 files changed, 8 insertions(+) + +diff --git a/libavformat/allformats.c b/libavformat/allformats.c +index 0c2d5ca..e2f37da 100644 +--- a/libavformat/allformats.c ++++ b/libavformat/allformats.c +@@ -586,6 +586,10 @@ extern const FFInputFormat ff_vapoursynth_demuxer; + + // ijk custom demuxers + extern FFInputFormat ff_ijklivehook_demuxer; ++extern FFInputFormat ff_ijkplaceholder1_demuxer; ++extern FFInputFormat ff_ijkplaceholder2_demuxer; ++extern FFInputFormat ff_ijkplaceholder3_demuxer; ++extern FFInputFormat ff_ijkplaceholder4_demuxer; + + #include "libavformat/muxer_list.c" + #include "libavformat/demuxer_list.c" +diff --git a/libavformat/ijkutils.c b/libavformat/ijkutils.c +index 0314c48..ac80e54 100644 +--- a/libavformat/ijkutils.c ++++ b/libavformat/ijkutils.c +@@ -105,3 +105,7 @@ FFInputFormat ff_##x##_demuxer = { \ + + */ + IJK_DUMMY_DEMUXER(ijklivehook); ++IJK_DUMMY_DEMUXER(ijkplaceholder1); ++IJK_DUMMY_DEMUXER(ijkplaceholder2); ++IJK_DUMMY_DEMUXER(ijkplaceholder3); ++IJK_DUMMY_DEMUXER(ijkplaceholder4); +\ No newline at end of file +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.1/0007-add-3-dummy-ijkhttp-protocols.patch b/patches/ffmpeg-n8.1.1/0007-add-3-dummy-ijkhttp-protocols.patch new file mode 100644 index 000000000..eefafb84e --- /dev/null +++ b/patches/ffmpeg-n8.1.1/0007-add-3-dummy-ijkhttp-protocols.patch @@ -0,0 +1,41 @@ +From 86da4f9eeb7824aa61cb59b690507a0772972a1d Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Fri, 8 May 2026 17:07:35 +0800 +Subject: add 3 dummy ijkhttp protocols + +--- + libavformat/ijkutils.c | 3 +++ + libavformat/protocols.c | 3 +++ + 2 files changed, 6 insertions(+) + +diff --git a/libavformat/ijkutils.c b/libavformat/ijkutils.c +index ac80e54..ba8eadd 100644 +--- a/libavformat/ijkutils.c ++++ b/libavformat/ijkutils.c +@@ -69,6 +69,9 @@ IJK_DUMMY_PROTOCOL(ijkhttphook); + IJK_DUMMY_PROTOCOL(ijksegment); + IJK_DUMMY_PROTOCOL(ijktcphook); + IJK_DUMMY_PROTOCOL(ijkio); ++IJK_DUMMY_PROTOCOL(ijkhttp1); ++IJK_DUMMY_PROTOCOL(ijkhttp2); ++IJK_DUMMY_PROTOCOL(ijkhttp3); + + #define IJK_FF_DEMUXER(x) \ + extern FFInputFormat ff_##x##_demuxer; \ +diff --git a/libavformat/protocols.c b/libavformat/protocols.c +index de0926b..d1c1095 100644 +--- a/libavformat/protocols.c ++++ b/libavformat/protocols.c +@@ -84,6 +84,9 @@ extern const URLProtocol ff_ijkmediadatasource_protocol; + extern const URLProtocol ff_ijksegment_protocol; + extern const URLProtocol ff_ijktcphook_protocol; + extern const URLProtocol ff_ijkio_protocol; ++extern const URLProtocol ff_ijkhttp1_protocol; ++extern const URLProtocol ff_ijkhttp2_protocol; ++extern const URLProtocol ff_ijkhttp3_protocol; + + #include "libavformat/protocol_list.c" + +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.1/0008-restore-ijk-av_dict_get-that-converts-the-value-to-a.patch b/patches/ffmpeg-n8.1.1/0008-restore-ijk-av_dict_get-that-converts-the-value-to-a.patch new file mode 100644 index 000000000..d4f4d3c69 --- /dev/null +++ b/patches/ffmpeg-n8.1.1/0008-restore-ijk-av_dict_get-that-converts-the-value-to-a.patch @@ -0,0 +1,91 @@ +From 3da3f038de2cd4067263b69a06ac42ceb01b56d5 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Wed, 28 May 2025 18:33:27 +0800 +Subject: restore ijk av_dict_get that converts the value to a + pointer + +--- + libavutil/dict.c | 39 ++++++++++++++++++++++++++++++++++++++- + libavutil/dict.h | 9 +++++++++ + 2 files changed, 47 insertions(+), 1 deletion(-) + +diff --git a/libavutil/dict.c b/libavutil/dict.c +index fafb454..db8ee05 100644 +--- a/libavutil/dict.c ++++ b/libavutil/dict.c +@@ -63,7 +63,7 @@ AVDictionaryEntry *av_dict_get(const AVDictionary *m, const char *key, + const AVDictionaryEntry *entry = prev; + unsigned int j; + +- if (!key) ++ if (!m || !key) + return NULL; + + while ((entry = av_dict_iterate(m, entry))) { +@@ -183,6 +183,43 @@ int av_dict_set_int(AVDictionary **pm, const char *key, int64_t value, + return av_dict_set(pm, key, valuestr, flags); + } + ++int av_dict_set_intptr(AVDictionary **pm, const char *key, uintptr_t value, ++ int flags) ++{ ++ char valuestr[22]; ++ snprintf(valuestr, sizeof(valuestr), "%p", value); ++ flags &= ~AV_DICT_DONT_STRDUP_VAL; ++ return av_dict_set(pm, key, valuestr, flags); ++} ++ ++uintptr_t av_dict_get_intptr(const AVDictionary *m, const char* key) { ++ uintptr_t ptr = NULL; ++ AVDictionaryEntry *t = NULL; ++ if ((t = av_dict_get(m, key, NULL, 0))) { ++ return av_dict_strtoptr(t->value); ++ } ++ return NULL; ++} ++ ++uintptr_t av_dict_strtoptr(char * value) { ++ uintptr_t ptr = NULL; ++ char *next = NULL; ++ if(!value || value[0] !='0' || (value[1]|0x20)!='x') { ++ return NULL; ++ } ++ ptr = strtoull(value, &next, 16); ++ if (next == value) { ++ return NULL; ++ } ++ return ptr; ++} ++ ++char * av_dict_ptrtostr(uintptr_t value) { ++ char valuestr[22] = {0}; ++ snprintf(valuestr, sizeof(valuestr), "%p", value); ++ return av_strdup(valuestr); ++} ++ + static int parse_key_value_pair(AVDictionary **pm, const char **buf, + const char *key_val_sep, const char *pairs_sep, + int flags) +diff --git a/libavutil/dict.h b/libavutil/dict.h +index 93c7cbf..d97a601 100644 +--- a/libavutil/dict.h ++++ b/libavutil/dict.h +@@ -173,6 +173,15 @@ int av_dict_set(AVDictionary **pm, const char *key, const char *value, int flags + */ + int av_dict_set_int(AVDictionary **pm, const char *key, int64_t value, int flags); + ++/** ++ * Convenience wrapper for av_dict_get that converts the value to a pointer ++ * and stores it. ++ */ ++int av_dict_set_intptr(AVDictionary **pm, const char *key, uintptr_t value, int flags); ++uintptr_t av_dict_get_intptr(const AVDictionary *m, const char* key); ++uintptr_t av_dict_strtoptr(char * value); ++char * av_dict_ptrtostr(uintptr_t value); ++ + /** + * Parse the key/value pairs list and add the parsed entries to a dictionary. + * +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.1/0009-control-which-http-impl-was-used-by-set-selected_htt.patch b/patches/ffmpeg-n8.1.1/0009-control-which-http-impl-was-used-by-set-selected_htt.patch new file mode 100644 index 000000000..f8ae3cb75 --- /dev/null +++ b/patches/ffmpeg-n8.1.1/0009-control-which-http-impl-was-used-by-set-selected_htt.patch @@ -0,0 +1,129 @@ +From 5b78a77e4274c7aa8199ad5cc0aa1aa464b381c8 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Wed, 28 May 2025 18:25:51 +0800 +Subject: control which http impl was used by set selected_http + option + +--- + libavformat/avio.c | 63 ++++++++++++++++++++++++++++++++++++++++++++-- + libavformat/url.h | 16 ++++++++++++ + 2 files changed, 77 insertions(+), 2 deletions(-) + +diff --git a/libavformat/avio.c b/libavformat/avio.c +index b146ac9..0287cc6 100644 +--- a/libavformat/avio.c ++++ b/libavformat/avio.c +@@ -360,6 +360,65 @@ int ffurl_alloc(URLContext **puc, const char *filename, int flags, + return AVERROR_PROTOCOL_NOT_FOUND; + } + ++ ++static const struct URLProtocol *url_find_the_protocol(const char *proto_str) ++{ ++ const URLProtocol **protocols = ffurl_get_protocols(NULL, NULL); ++ if (!protocols) ++ return NULL; ++ for (int i = 0; protocols[i]; i++) { ++ const URLProtocol *up = protocols[i]; ++ if (!strcmp(proto_str, up->name)) { ++ av_freep(&protocols); ++ return up; ++ } ++ } ++ av_freep(&protocols); ++ return NULL; ++} ++ ++int ffurl_alloc2(URLContext **puc, const char *filename, int flags, ++ const AVIOInterruptCB *int_cb, AVDictionary **options) ++{ ++ if (options && *options) { ++ AVDictionaryEntry *e = av_dict_get(*options, "selected_http", NULL, 0); ++ const char *selected_http; ++ if (e && (selected_http = e->value)) { ++ ++ char proto_str[128] = {0}; ++ size_t proto_len = strspn(filename, URL_SCHEME_CHARS); ++ if (filename[proto_len] != ':' && ++ (strncmp(filename, "subfile,", 8) || !strchr(filename + proto_len + 1, ':')) || ++ is_dos_path(filename)) ++ strcpy(proto_str, "file"); ++ else ++ av_strlcpy(proto_str, filename, ++ FFMIN(proto_len + 1, sizeof(proto_str))); ++ //only apply http protocol ++ if (!strcmp(proto_str, "http") || !strcmp(proto_str, "https")) { ++ if (!strcmp(selected_http, "ijkhttp1") || !strcmp(selected_http, "ijkhttp2") || !strcmp(selected_http, "ijkhttp3")) { ++ const URLProtocol *p = url_find_the_protocol(selected_http); ++ if (p) { ++ av_log(NULL, AV_LOG_DEBUG, "%s use %s send request\n",proto_str,selected_http); ++ return url_alloc_for_protocol(puc, p, filename, flags, int_cb); ++ } ++ *puc = NULL; ++ av_log(NULL, AV_LOG_ERROR, "some thing is fault,check %s protocol\n", selected_http); ++ return AVERROR_PROTOCOL_NOT_FOUND; ++ } else { ++ av_log(NULL, AV_LOG_ERROR, "invalid selected_http value: %s\n", selected_http); ++ av_assert0(0); ++ return AVERROR_PROTOCOL_NOT_FOUND; ++ } ++ } else { ++ av_log(NULL, AV_LOG_DEBUG, "%s not use %s\n",proto_str,selected_http); ++ } ++ } ++ } ++ ++ return ffurl_alloc(puc, filename, flags, int_cb); ++} ++ + int ffurl_open_whitelist(URLContext **puc, const char *filename, int flags, + const AVIOInterruptCB *int_cb, AVDictionary **options, + const char *whitelist, const char* blacklist, +@@ -367,7 +426,7 @@ int ffurl_open_whitelist(URLContext **puc, const char *filename, int flags, + { + AVDictionary *tmp_opts = NULL; + AVDictionaryEntry *e; +- int ret = ffurl_alloc(puc, filename, flags, int_cb); ++ int ret = ffurl_alloc2(puc, filename, flags, int_cb, options); + if (ret < 0) + return ret; + if (parent) { +@@ -736,7 +795,7 @@ int avio_open_dir(AVIODirContext **s, const char *url, AVDictionary **options) + goto fail; + } + +- if ((ret = ffurl_alloc(&h, url, AVIO_FLAG_READ, NULL)) < 0) ++ if ((ret = ffurl_alloc2(&h, url, AVIO_FLAG_READ, NULL, options)) < 0) + goto fail; + + if (h->prot->url_open_dir && h->prot->url_read_dir && h->prot->url_close_dir) { +diff --git a/libavformat/url.h b/libavformat/url.h +index 0784d77..53c6f13 100644 +--- a/libavformat/url.h ++++ b/libavformat/url.h +@@ -112,6 +112,22 @@ typedef struct URLProtocol { + int ffurl_alloc(URLContext **puc, const char *filename, int flags, + const AVIOInterruptCB *int_cb); + ++ /** ++ * Create a URLContext for accessing to the resource indicated by ++ * url, but do not initiate the connection yet. ++ * ++ * @param puc pointer to the location where, in case of success, the ++ * function puts the pointer to the created URLContext ++ * @param flags flags which control how the resource indicated by url ++ * is to be opened ++ * @param int_cb interrupt callback to use for the URLContext, may be ++ * NULL ++ * @param options A dictionary filled with options for replace http protocol ++ * @return >= 0 in case of success, a negative value corresponding to an ++ * AVERROR code in case of failure ++ */ ++int ffurl_alloc2(URLContext **puc, const char *filename, int flags, ++ const AVIOInterruptCB *int_cb, AVDictionary **options); + /** + * Connect an URLContext that has been allocated by ffurl_alloc + * +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.1/0010-correct-file-seekable-value-range-0-means-streamed-c.patch b/patches/ffmpeg-n8.1.1/0010-correct-file-seekable-value-range-0-means-streamed-c.patch new file mode 100644 index 000000000..f75a71451 --- /dev/null +++ b/patches/ffmpeg-n8.1.1/0010-correct-file-seekable-value-range-0-means-streamed-c.patch @@ -0,0 +1,26 @@ +From a238a10d136f21902de411f024536b43c87cae95 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Fri, 8 May 2026 17:11:02 +0800 +Subject: correct file seekable value range, 0 means streamed + can't seek, 1 means not streamed can seek. + +--- + libavformat/file.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/libavformat/file.c b/libavformat/file.c +index 3ceddc8..59a0442 100644 +--- a/libavformat/file.c ++++ b/libavformat/file.c +@@ -106,7 +106,7 @@ static const AVOption file_options[] = { + { "truncate", "truncate existing files on write", offsetof(FileContext, trunc), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM }, + { "blocksize", "set I/O operation maximum block size", offsetof(FileContext, blocksize), AV_OPT_TYPE_INT, { .i64 = INT_MAX }, 1, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, + { "follow", "Follow a file as it is being written", offsetof(FileContext, follow), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, AV_OPT_FLAG_DECODING_PARAM }, +- { "seekable", "Sets if the file is seekable", offsetof(FileContext, seekable), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 0, AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_ENCODING_PARAM }, ++ { "seekable", "Sets if the file is seekable", offsetof(FileContext, seekable), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1, AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_ENCODING_PARAM }, + { "pkt_size", "Maximum packet size", offsetof(FileContext, pkt_size), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_ENCODING_PARAM }, + { NULL } + }; +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.1/0011-fix-lrcdec-read-line-bug-on-osx.patch b/patches/ffmpeg-n8.1.1/0011-fix-lrcdec-read-line-bug-on-osx.patch new file mode 100644 index 000000000..0e34977cc --- /dev/null +++ b/patches/ffmpeg-n8.1.1/0011-fix-lrcdec-read-line-bug-on-osx.patch @@ -0,0 +1,43 @@ +From becb7439eaa9ab67f5317a58dbe183bd94d4fcce Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Wed, 28 May 2025 18:39:38 +0800 +Subject: fix lrcdec read line bug on osx + +--- + libavformat/lrcdec.c | 20 +++++++++++--------- + 1 file changed, 11 insertions(+), 9 deletions(-) + +diff --git a/libavformat/lrcdec.c b/libavformat/lrcdec.c +index 1f96305..b758662 100644 +--- a/libavformat/lrcdec.c ++++ b/libavformat/lrcdec.c +@@ -107,15 +107,17 @@ static int64_t read_line(AVBPrint *buf, AVIOContext *pb) + int64_t pos = avio_tell(pb); + + av_bprint_clear(buf); +- while(!avio_feof(pb)) { +- int c = avio_r8(pb); +- if(c != '\r') { +- av_bprint_chars(buf, c, 1); +- } +- if(c == '\n') { +- break; +- } +- } ++ ff_read_line_to_bprint_overwrite(pb, buf); ++ ++ // while(!avio_feof(pb)) { ++ // int c = avio_r8(pb); ++ // if(c != '\r') { ++ // av_bprint_chars(buf, c, 1); ++ // } ++ // if(c == '\n') { ++ // break; ++ // } ++ // } + return pos; + } + +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.1/0012-avformat-mpegts-index-only-keyframes-to-ensure-accur.patch b/patches/ffmpeg-n8.1.1/0012-avformat-mpegts-index-only-keyframes-to-ensure-accur.patch new file mode 100644 index 000000000..6ccd438cd --- /dev/null +++ b/patches/ffmpeg-n8.1.1/0012-avformat-mpegts-index-only-keyframes-to-ensure-accur.patch @@ -0,0 +1,47 @@ +From 5aef56e86381c8151a63486524fd475536f64fe4 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Wed, 28 May 2025 18:48:39 +0800 +Subject: avformat/mpegts: index only keyframes to ensure + accurate seeks by default + +--- + libavformat/mpegts.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c +index 4b326d3..47944c7 100644 +--- a/libavformat/mpegts.c ++++ b/libavformat/mpegts.c +@@ -158,6 +158,7 @@ struct MpegTSContext { + + int skip_changes; + int skip_clear; ++ int seek_flag_keyframe; + int skip_unknown_pmt; + + int scan_all_pmts; +@@ -211,6 +212,8 @@ static const AVOption options[] = { + {.i64 = 0}, 0, 1, 0 }, + {"max_packet_size", "maximum size of emitted packet", offsetof(MpegTSContext, max_packet_size), AV_OPT_TYPE_INT, + {.i64 = 204800}, 1, INT_MAX/2, AV_OPT_FLAG_DECODING_PARAM }, ++ {"seek_flag_keyframe", "seek use keyframe mode", offsetof(MpegTSContext, seek_flag_keyframe), AV_OPT_TYPE_BOOL, ++ {.i64 = 1}, 0, 1, AV_OPT_FLAG_DECODING_PARAM }, + { NULL }, + }; + +@@ -3582,9 +3585,10 @@ static int64_t mpegts_get_dts(AVFormatContext *s, int stream_index, + av_packet_free(&pkt); + return AV_NOPTS_VALUE; + } +- if (pkt->dts != AV_NOPTS_VALUE && pkt->pos >= 0) { ++ ++ if (pkt->dts != AV_NOPTS_VALUE && pkt->pos >= 0 && (!ts->seek_flag_keyframe || (pkt->flags & AV_PKT_FLAG_KEY))) { + ff_reduce_index(s, pkt->stream_index); +- av_add_index_entry(s->streams[pkt->stream_index], pkt->pos, pkt->dts, 0, 0, AVINDEX_KEYFRAME /* FIXME keyframe? */); ++ av_add_index_entry(s->streams[pkt->stream_index], pkt->pos, pkt->dts, 0, 0, AVINDEX_KEYFRAME); + if (pkt->stream_index == stream_index && pkt->pos >= *ppos) { + int64_t dts = pkt->dts; + *ppos = pkt->pos; +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.1/0013-Correct-the-wrong-codecpar-codec_id-which-read-from-.patch b/patches/ffmpeg-n8.1.1/0013-Correct-the-wrong-codecpar-codec_id-which-read-from-.patch new file mode 100644 index 000000000..609d2c426 --- /dev/null +++ b/patches/ffmpeg-n8.1.1/0013-Correct-the-wrong-codecpar-codec_id-which-read-from-.patch @@ -0,0 +1,60 @@ +From ccea039f4d65a0f46eaf8c316ed602b95b3f6b90 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Wed, 28 May 2025 18:52:01 +0800 +Subject: Correct the wrong codecpar->codec_id which read from + MIME of ID3tags, but the real data was encoded in PNG/JPEG/TIFF + +--- + libavformat/id3v2.c | 7 ++++++- + libavformat/img2dec.c | 2 +- + libavformat/mov.c | 2 +- + 3 files changed, 8 insertions(+), 3 deletions(-) + +diff --git a/libavformat/id3v2.c b/libavformat/id3v2.c +index 5fc82ad..4ae12b6 100644 +--- a/libavformat/id3v2.c ++++ b/libavformat/id3v2.c +@@ -1187,8 +1187,13 @@ int ff_id3v2_parse_apic(AVFormatContext *s, ID3v2ExtraMeta *extra_meta) + st = s->streams[s->nb_streams - 1]; + st->codecpar->codec_id = apic->id; + +- if (AV_RB64(st->attached_pic.data) == PNGSIG) ++ if (AV_RB64(st->attached_pic.data) == PNGSIG || AV_RB64(st->attached_pic.data) == MNGSIG) { + st->codecpar->codec_id = AV_CODEC_ID_PNG; ++ } else if (AV_RB24(st->attached_pic.data) == 0xffd8ff) { ++ st->codecpar->codec_id = AV_CODEC_ID_MJPEG; ++ } else if (AV_RB32(st->attached_pic.data) == 0x49492a00 || AV_RB32(st->attached_pic.data) == 0x4D4D002a) { ++ st->codecpar->codec_id = AV_CODEC_ID_TIFF; ++ } + + if (apic->description[0]) + av_dict_set(&st->metadata, "title", apic->description, 0); +diff --git a/libavformat/img2dec.c b/libavformat/img2dec.c +index ecb7ea8..0a26d5a 100644 +--- a/libavformat/img2dec.c ++++ b/libavformat/img2dec.c +@@ -892,7 +892,7 @@ static int png_probe(const AVProbeData *p) + { + const uint8_t *b = p->buf; + +- if (AV_RB64(b) == 0x89504e470d0a1a0a) ++ if (AV_RB64(b) == 0x89504e470d0a1a0a || AV_RB64(b) == 0x8a4d4e470d0a1a0a) + return AVPROBE_SCORE_MAX - 1; + return 0; + } +diff --git a/libavformat/mov.c b/libavformat/mov.c +index 222d79e..e114770 100644 +--- a/libavformat/mov.c ++++ b/libavformat/mov.c +@@ -259,7 +259,7 @@ static int mov_read_covr(MOVContext *c, AVIOContext *pb, int type, int len) + sc->refcount = 1; + + if (st->attached_pic.size >= 8 && id != AV_CODEC_ID_BMP) { +- if (AV_RB64(st->attached_pic.data) == 0x89504e470d0a1a0a) { ++ if (AV_RB64(st->attached_pic.data) == 0x89504e470d0a1a0a || AV_RB64(st->attached_pic.data) == 0x8a4d4e470d0a1a0a) { + id = AV_CODEC_ID_PNG; + } else { + id = AV_CODEC_ID_MJPEG; +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.1/0014-fix-http-chunked-transfer-get-wrong-size-cause-av_re.patch b/patches/ffmpeg-n8.1.1/0014-fix-http-chunked-transfer-get-wrong-size-cause-av_re.patch new file mode 100644 index 000000000..8e52c2f14 --- /dev/null +++ b/patches/ffmpeg-n8.1.1/0014-fix-http-chunked-transfer-get-wrong-size-cause-av_re.patch @@ -0,0 +1,32 @@ +From 74355a6170c59a122461ff0adeccaf178eeaf602 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Thu, 29 May 2025 08:56:02 +0800 +Subject: fix http chunked transfer get wrong size cause + av_read_frame can not return eof bug + +--- + libavformat/img2dec.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/libavformat/img2dec.c b/libavformat/img2dec.c +index 0a26d5a..088c219 100644 +--- a/libavformat/img2dec.c ++++ b/libavformat/img2dec.c +@@ -451,7 +451,13 @@ int ff_img_read_packet(AVFormatContext *s1, AVPacket *pkt) + if (s->frame_size > 0) { + size[0] = s->frame_size; + } else if (!ffstream(s1->streams[0])->parser) { +- size[0] = avio_size(s1->pb); ++ //http Transfer-Encoding: chunked the size is -78; ++ int64_t s = avio_size(s1->pb); ++ if (s < 0) { ++ size[0] = 4096; ++ } else { ++ size[0] = s; ++ } + } else { + size[0] = 4096; + } +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.1/0015-not-very-useful-log-use-trace-level.patch b/patches/ffmpeg-n8.1.1/0015-not-very-useful-log-use-trace-level.patch new file mode 100644 index 000000000..a729faba4 --- /dev/null +++ b/patches/ffmpeg-n8.1.1/0015-not-very-useful-log-use-trace-level.patch @@ -0,0 +1,76 @@ +From 6a0647fe06239adcca0d09afe5ac0e78cc0224aa Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Thu, 29 May 2025 09:02:16 +0800 +Subject: not very useful log use trace level + +--- + libavcodec/h2645_parse.c | 6 +++--- + libavcodec/h2645_vui.c | 2 +- + libavformat/demux.c | 4 ++-- + 3 files changed, 6 insertions(+), 6 deletions(-) + +diff --git a/libavcodec/h2645_parse.c b/libavcodec/h2645_parse.c +index 659e818..63f3460 100644 +--- a/libavcodec/h2645_parse.c ++++ b/libavcodec/h2645_parse.c +@@ -417,7 +417,7 @@ static int vvc_parse_nal_header(H2645NAL *nal, void *logctx) + if ((nal->type >= VVC_IDR_W_RADL && nal->type <= VVC_RSV_IRAP_11) && nal->temporal_id) + return AVERROR_INVALIDDATA; + +- av_log(logctx, AV_LOG_DEBUG, ++ av_log(logctx, AV_LOG_TRACE, + "nal_unit_type: %d(%s), nuh_layer_id: %d, temporal_id: %d\n", + nal->type, vvc_nal_unit_name(nal->type), nal->nuh_layer_id, nal->temporal_id); + +@@ -438,7 +438,7 @@ static int hevc_parse_nal_header(H2645NAL *nal, void *logctx) + if (nal->temporal_id < 0) + return AVERROR_INVALIDDATA; + +- av_log(logctx, AV_LOG_DEBUG, ++ av_log(logctx, AV_LOG_TRACE, + "nal_unit_type: %d(%s), nuh_layer_id: %d, temporal_id: %d\n", + nal->type, hevc_nal_unit_name(nal->type), nal->nuh_layer_id, nal->temporal_id); + +@@ -455,7 +455,7 @@ static int h264_parse_nal_header(H2645NAL *nal, void *logctx) + nal->ref_idc = get_bits(gb, 2); + nal->type = get_bits(gb, 5); + +- av_log(logctx, AV_LOG_DEBUG, ++ av_log(logctx, AV_LOG_TRACE, + "nal_unit_type: %d(%s), nal_ref_idc: %d\n", + nal->type, h264_nal_unit_name(nal->type), nal->ref_idc); + +diff --git a/libavcodec/h2645_vui.c b/libavcodec/h2645_vui.c +index 0e576c1..445e471 100644 +--- a/libavcodec/h2645_vui.c ++++ b/libavcodec/h2645_vui.c +@@ -36,7 +36,7 @@ + + void ff_h2645_decode_common_vui_params(GetBitContext *gb, H2645VUI *vui, void *logctx) + { +- av_log(logctx, AV_LOG_DEBUG, "Decoding VUI\n"); ++ av_log(logctx, AV_LOG_TRACE, "Decoding VUI\n"); + + vui->aspect_ratio_info_present_flag = get_bits1(gb); + if (vui->aspect_ratio_info_present_flag) { +diff --git a/libavformat/demux.c b/libavformat/demux.c +index 05ab1e1..494e15f 100644 +--- a/libavformat/demux.c ++++ b/libavformat/demux.c +@@ -2074,11 +2074,11 @@ static void estimate_timings(AVFormatContext *ic, int64_t old_offset) + for (unsigned i = 0; i < ic->nb_streams; i++) { + AVStream *const st = ic->streams[i]; + if (st->time_base.den) +- av_log(ic, AV_LOG_TRACE, "stream %u: start_time: %s duration: %s\n", i, ++ av_log(ic, AV_LOG_DEBUG, "stream %u: start_time: %s duration: %s\n", i, + av_ts2timestr(st->start_time, &st->time_base), + av_ts2timestr(st->duration, &st->time_base)); + } +- av_log(ic, AV_LOG_TRACE, ++ av_log(ic, AV_LOG_DEBUG, + "format: start_time: %s duration: %s (estimate from %s) bitrate=%"PRId64" kb/s\n", + av_ts2timestr(ic->start_time, &AV_TIME_BASE_Q), + av_ts2timestr(ic->duration, &AV_TIME_BASE_Q), +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.1/0016-Audio-Vivid-Parser-and-Demuxer-but-av3a-Decoder-is-a.patch b/patches/ffmpeg-n8.1.1/0016-Audio-Vivid-Parser-and-Demuxer-but-av3a-Decoder-is-a.patch new file mode 100644 index 000000000..2f94cc8b0 --- /dev/null +++ b/patches/ffmpeg-n8.1.1/0016-Audio-Vivid-Parser-and-Demuxer-but-av3a-Decoder-is-a.patch @@ -0,0 +1,1341 @@ +From 0b9dacdb9282e956e0cecb0d7d5df7f5aa41f8cc Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Fri, 8 May 2026 19:00:36 +0800 +Subject: Audio Vivid Parser and Demuxer, but av3a Decoder is + absent + +--- + libavcodec/Makefile | 1 + + libavcodec/av3a.h | 314 ++++++++++++++++++++++++++ + libavcodec/av3a_parser.c | 219 ++++++++++++++++++ + libavcodec/codec_desc.c | 7 + + libavcodec/codec_id.h | 1 + + libavcodec/parsers.c | 1 + + libavcodec/utils.c | 1 + + libavformat/Makefile | 1 + + libavformat/allformats.c | 1 + + libavformat/av3adec.c | 473 +++++++++++++++++++++++++++++++++++++++ + libavformat/isom_tags.c | 1 + + libavformat/mov.c | 124 ++++++++++ + libavformat/mpegts.c | 6 + + libavformat/mpegts.h | 1 + + 14 files changed, 1151 insertions(+) + create mode 100644 libavcodec/av3a.h + create mode 100644 libavcodec/av3a_parser.c + create mode 100644 libavformat/av3adec.c + +diff --git a/libavcodec/Makefile b/libavcodec/Makefile +index 4445d9e..c51a860 100644 +--- a/libavcodec/Makefile ++++ b/libavcodec/Makefile +@@ -1247,6 +1247,7 @@ OBJS-$(CONFIG_APV_PARSER) += apv_parser.o + OBJS-$(CONFIG_AV1_PARSER) += av1_parser.o av1_parse.o + OBJS-$(CONFIG_AVS2_PARSER) += avs2.o avs2_parser.o + OBJS-$(CONFIG_AVS3_PARSER) += avs3_parser.o ++OBJS-$(CONFIG_AV3A_PARSER) += av3a_parser.o + OBJS-$(CONFIG_BMP_PARSER) += bmp_parser.o + OBJS-$(CONFIG_CAVSVIDEO_PARSER) += cavs_parser.o + OBJS-$(CONFIG_COOK_PARSER) += cook_parser.o +diff --git a/libavcodec/av3a.h b/libavcodec/av3a.h +new file mode 100644 +index 0000000..14dc349 +--- /dev/null ++++ b/libavcodec/av3a.h +@@ -0,0 +1,314 @@ ++/* ++ * AV3A Common Header File ++ * ++ * Copyright (c) 2024 Shuai Liu ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg 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. ++ * ++ * FFmpeg 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 FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#ifndef AVCODEC_AV3A_H ++#define AVCODEC_AV3A_H ++ ++#include "libavutil/samplefmt.h" ++#include "libavutil/channel_layout.h" ++ ++/* AATF header */ ++#define AV3A_MAX_NBYTES_HEADER 9 ++#define AV3A_AUDIO_SYNC_WORD 0xFFF ++#define AV3A_AUDIO_FRAME_SIZE 1024 ++#define AV3A_CHANNEL_LAYOUT_SIZE 15 ++#define AV3A_BITRATE_TABLE_SIZE 16 ++#define AV3A_FS_TABLE_SIZE 9 ++#define AV3A_RESOLUTION_TABLE_SIZE 3 ++#define AV3A_DCA3_BOX_MIN_SIZE 5 ++ ++/* Channel Layout */ ++#define AV3A_CH_LAYOUT_MONO (AV_CH_LAYOUT_MONO) ++#define AV3A_CH_LAYOUT_STEREO (AV_CH_LAYOUT_STEREO) ++#define AV3A_CH_LAYOUT_4POINT0 (AV3A_CH_LAYOUT_STEREO|AV_CH_FRONT_CENTER|AV_CH_BACK_CENTER) ++#define AV3A_CH_LAYOUT_5POINT1 (AV_CH_LAYOUT_5POINT1) ++#define AV3A_CH_LAYOUT_7POINT1 (AV_CH_LAYOUT_5POINT1|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT) ++#define AV3A_CH_LAYOUT_5POINT1POINT2 (AV_CH_LAYOUT_5POINT1|AV_CH_TOP_SIDE_LEFT|AV_CH_TOP_SIDE_RIGHT) ++#define AV3A_CH_LAYOUT_7POINT1POINT2 (AV3A_CH_LAYOUT_7POINT1|AV_CH_TOP_SIDE_LEFT|AV_CH_TOP_SIDE_RIGHT) ++#define AV3A_CH_LAYOUT_5POINT1POINT4 (AV_CH_LAYOUT_5POINT1|AV_CH_TOP_FRONT_LEFT|AV_CH_TOP_FRONT_RIGHT|AV_CH_TOP_BACK_LEFT|AV_CH_TOP_BACK_RIGHT) ++#define AV3A_CH_LAYOUT_7POINT1POINT4 (AV3A_CH_LAYOUT_7POINT1|AV_CH_TOP_FRONT_LEFT|AV_CH_TOP_FRONT_RIGHT|AV_CH_TOP_BACK_LEFT|AV_CH_TOP_BACK_RIGHT) ++#define AV3A_CH_AUDIO_OBJECT (AV_CHAN_UNKNOWN) ++ ++/* AV3A Codec ID */ ++typedef enum { ++ AV3A_LOSSLESS_CODEC_ID = 1, ++ AV3A_LOSSY_CODEC_ID = 2 ++} Av3aCodecId; ++ ++/* Content Type */ ++typedef enum { ++ AV3A_CHANNEL_BASED_TYPE = 0, ++ AV3A_OBJECT_BASED_TYPE = 1, ++ AV3A_CHANNEL_OBJECT_TYPE = 2, ++ AV3A_AMBISONIC_TYPE = 3 ++} Av3aContentType; ++ ++/* Internal Coding Profile */ ++typedef enum { ++ AV3A_BASE_PROFILE = 0, ++ AV3A_OBJECT_METADATA_PROFILE = 1, ++ AV3A_AMBISONIC_PROFILE = 2 ++} Av3aCodingProfile; ++ ++/* NN Type */ ++typedef enum { ++ AV3A_BASELINE_NN_TYPE = 0, ++ AV3A_LC_NN_TYPE = 1 ++} Av3aNeuralNetworkType; ++ ++/* AV3A Channel Configuration */ ++typedef enum { ++ CHANNEL_CONFIG_MONO = 0, /* Mono = 0 */ ++ CHANNEL_CONFIG_STEREO = 1, /* Stereo = 1 */ ++ CHANNEL_CONFIG_MC_5_1 = 2, /* 5.1 = 2 */ ++ CHANNEL_CONFIG_MC_7_1 = 3, /* 7.1 = 3 */ ++ CHANNEL_CONFIG_MC_10_2 = 4, /* 10.2 = 4 */ ++ CHANNEL_CONFIG_MC_22_2 = 5, /* 22.2 = 5 */ ++ CHANNEL_CONFIG_MC_4_0 = 6, /* 4.0 = 6 */ ++ CHANNEL_CONFIG_MC_5_1_2 = 7, /* 5.1.2 = 7 */ ++ CHANNEL_CONFIG_MC_5_1_4 = 8, /* 5.1.4 = 8 */ ++ CHANNEL_CONFIG_MC_7_1_2 = 9, /* 7.1.2 = 9 */ ++ CHANNEL_CONFIG_MC_7_1_4 = 10, /* 7.1.4 = 10 */ ++ CHANNEL_CONFIG_HOA_ORDER1 = 11, /* HOA1 = 11 */ ++ CHANNEL_CONFIG_HOA_ORDER2 = 12, /* HOA2 = 12 */ ++ CHANNEL_CONFIG_HOA_ORDER3 = 13, /* HOA3 = 13 */ ++ CHANNEL_CONFIG_UNKNOWN = 14 /* UNKNOWN = 14 */ ++} Av3aChannelConfig; ++ ++typedef enum { ++ AV3A_AMBISONIC_FIRST_ORDER = 1, ++ AV3A_AMBISONIC_SECOND_ORDER = 2, ++ AV3A_AMBISONIC_THIRD_ORDER = 3 ++} Av3aAmbisonicOrder; ++ ++typedef struct { ++ int16_t sync_word; /* sync word */ ++ int16_t audio_codec_id; /* audio codec id */ ++ int16_t anc_data; /* anc data */ ++ int16_t nn_type; /* neural network type */ ++ int16_t coding_profile; /* coding profile */ ++ int16_t sampling_frequency_index; /* sampling frequency index */ ++ int16_t channel_number_index; /* channel number index */ ++ int16_t bitrate_index; /* bitrate index */ ++ int16_t soundbed_type; /* soundbed type */ ++ int16_t object_channel_number; /* object channel number */ ++ int16_t bitrate_index_per_channel; /* bitrate per object */ ++ int16_t order; /* ambisonics order */ ++ int16_t resolution_index; /* resolution index */ ++ ++ int32_t sampling_rate; /* sampling rate */ ++ int64_t total_bitrate; /* total bitrate */ ++ int16_t sample_format; /* sample format */ ++ int16_t resolution; /* resolution */ ++ int16_t content_type; /* internal content type */ ++ int16_t nb_channels; /* number of channels (channel configuration) */ ++ int16_t nb_objects; /* number of objects (object_channel_number + 1) */ ++ int16_t total_channels; /* total channels */ ++ int16_t hoa_order; /* ambisonic order (order + 1) */ ++ int32_t ch_layout_mask; /* channel layout mask */ ++} AATFHeaderInfo; ++ ++/* bitrate table for mono */ ++static const int64_t ff_av3a_mono_bitrate_table[AV3A_BITRATE_TABLE_SIZE] = { ++ 16000, 32000, 44000, 56000, 64000, 72000, 80000, 96000, 128000, 144000, ++ 164000, 192000, 0, 0, 0, 0 ++}; ++ ++/* bitrate table for stereo */ ++static const int64_t ff_av3a_stereo_bitrate_table[AV3A_BITRATE_TABLE_SIZE] = { ++ 24000, 32000, 48000, 64000, 80000, 96000, 128000, 144000, 192000, 256000, ++ 320000, 0, 0, 0, 0, 0 ++}; ++ ++/* bitrate table for MC 5.1 */ ++static const int64_t ff_av3a_mc5p1_bitrate_table[AV3A_BITRATE_TABLE_SIZE] = { ++ 192000, 256000, 320000, 384000, 448000, 512000, 640000, 720000, 144000, 96000, ++ 128000, 160000, 0, 0, 0, 0 ++}; ++ ++/* bitrate table for MC 7.1 */ ++static const int64_t ff_av3a_mc7p1_bitrate_table[AV3A_BITRATE_TABLE_SIZE] = { ++ 192000, 480000, 256000, 384000, 576000, 640000, 128000, 160000, 0, 0, ++ 0, 0, 0, 0, 0, 0 ++}; ++ ++/* bitrate table for MC 4.0 */ ++static const int64_t ff_av3a_mc4p0_bitrate_table[AV3A_BITRATE_TABLE_SIZE] = { ++ 48000, 96000, 128000, 192000, 256000, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0 ++}; ++ ++/* bitrate table for MC 5.1.2 */ ++static const int64_t ff_av3a_mc5p1p2_bitrate_table[AV3A_BITRATE_TABLE_SIZE] = { ++ 152000, 320000, 480000, 576000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ++}; ++ ++/* bitrate table for MC 5.1.4 */ ++static const int64_t ff_av3a_mc5p1p4_bitrate_table[AV3A_BITRATE_TABLE_SIZE] = { ++ 176000, 384000, 576000, 704000, 256000, 448000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ++}; ++ ++/* bitrate table for MC 7.1.2 */ ++static const int64_t ff_av3a_mc7p1p2_bitrate_table[AV3A_BITRATE_TABLE_SIZE] = { ++ 216000, 480000, 576000, 384000, 768000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ++}; ++ ++/* bitrate table for MC 7.1.4 */ ++static const int64_t ff_av3a_mc7p1p4_bitrate_table[AV3A_BITRATE_TABLE_SIZE] = { ++ 240000, 608000, 384000, 512000, 832000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ++}; ++ ++/* bitrate table for FOA */ ++static const int64_t ff_av3a_foa_bitrate_table[AV3A_BITRATE_TABLE_SIZE] = { ++ 48000, 96000, 128000, 192000, 256000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ++}; ++ ++/* bitrate table for HOA2 */ ++static const int64_t ff_av3a_hoa2_bitrate_table[AV3A_BITRATE_TABLE_SIZE] = { ++ 192000, 256000, 320000, 384000, 480000, 512000, 640000, 0, 0, 0, 0, 0, 0, 0, 0, 0 ++}; ++ ++/* bitrate table for HOA3 */ ++static const int64_t ff_av3a_hoa3_bitrate_table[AV3A_BITRATE_TABLE_SIZE] = { ++ 256000, 320000, 384000, 512000, 640000, 896000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; ++ ++static const int32_t ff_av3a_sampling_rate_table[AV3A_FS_TABLE_SIZE] = { ++ 192000, 96000, 48000, 44100, 32000, 24000, 22050, 16000, 8000 ++}; ++ ++typedef struct { ++ int16_t resolution; ++ enum AVSampleFormat sample_format; ++} Av3aSampleFormatMap; ++ ++static const Av3aSampleFormatMap ff_av3a_sample_format_map_table[AV3A_RESOLUTION_TABLE_SIZE] = { ++ {8, AV_SAMPLE_FMT_U8 }, /* 0: 8 bits */ ++ {16, AV_SAMPLE_FMT_S16}, /* 1: 16 bits */ ++ {24, AV_SAMPLE_FMT_S32}, /* 2: 24 bits */ ++}; ++ ++typedef struct { ++ Av3aChannelConfig channel_number_index; ++ int16_t channels; ++ const enum AVChannel* channel_layout; ++ uint64_t mask; ++} Av3aChannelConfigMap; ++ ++static const enum AVChannel ff_av3a_default_channel_layout_mono[1] = { ++ AV_CHAN_FRONT_CENTER ++}; ++ ++static const enum AVChannel ff_av3a_default_channel_layout_stereo[2] = { ++ AV_CHAN_FRONT_LEFT, AV_CHAN_FRONT_RIGHT ++}; ++ ++static const enum AVChannel ff_av3a_channel_layout_mc_4_0[4] = { ++ AV_CHAN_FRONT_LEFT, AV_CHAN_FRONT_RIGHT, ++ AV_CHAN_FRONT_CENTER, AV_CHAN_BACK_CENTER ++}; ++ ++static const enum AVChannel ff_av3a_default_channel_layout_mc_5_1[6] = { ++ AV_CHAN_FRONT_LEFT, AV_CHAN_FRONT_RIGHT, AV_CHAN_FRONT_CENTER, ++ AV_CHAN_LOW_FREQUENCY, ++ AV_CHAN_SIDE_LEFT, AV_CHAN_SIDE_RIGHT ++}; ++ ++static const enum AVChannel ff_av3a_default_channel_layout_mc_5_1_2[8] = { ++ AV_CHAN_FRONT_LEFT, AV_CHAN_FRONT_RIGHT, AV_CHAN_FRONT_CENTER, ++ AV_CHAN_LOW_FREQUENCY, ++ AV_CHAN_SIDE_LEFT, AV_CHAN_SIDE_RIGHT, ++ AV_CHAN_TOP_SIDE_LEFT, AV_CHAN_TOP_SIDE_RIGHT ++}; ++ ++static const enum AVChannel ff_av3a_default_channel_layout_mc_7_1[8] = { ++ AV_CHAN_FRONT_LEFT, AV_CHAN_FRONT_RIGHT, AV_CHAN_FRONT_CENTER, ++ AV_CHAN_LOW_FREQUENCY, ++ AV_CHAN_SIDE_LEFT, AV_CHAN_SIDE_RIGHT, ++ AV_CHAN_BACK_LEFT, AV_CHAN_BACK_RIGHT ++}; ++ ++static const enum AVChannel ff_av3a_default_channel_layout_mc_5_1_4[10] = { ++ AV_CHAN_FRONT_LEFT, AV_CHAN_FRONT_RIGHT, AV_CHAN_FRONT_CENTER, ++ AV_CHAN_LOW_FREQUENCY, ++ AV_CHAN_SIDE_LEFT, AV_CHAN_SIDE_RIGHT, ++ AV_CHAN_TOP_FRONT_LEFT, AV_CHAN_TOP_FRONT_RIGHT, ++ AV_CHAN_TOP_BACK_LEFT, AV_CHAN_TOP_BACK_RIGHT ++}; ++ ++static const enum AVChannel ff_av3a_default_channel_layout_mc_7_1_2[10] = { ++ AV_CHAN_FRONT_LEFT, AV_CHAN_FRONT_RIGHT, AV_CHAN_FRONT_CENTER, ++ AV_CHAN_LOW_FREQUENCY, ++ AV_CHAN_SIDE_LEFT, AV_CHAN_SIDE_RIGHT, ++ AV_CHAN_BACK_LEFT, AV_CHAN_BACK_RIGHT, ++ AV_CHAN_TOP_SIDE_LEFT, AV_CHAN_TOP_SIDE_RIGHT ++}; ++ ++static const enum AVChannel ff_av3a_default_channel_layout_mc_7_1_4[12] = { ++ AV_CHAN_FRONT_LEFT, AV_CHAN_FRONT_RIGHT, AV_CHAN_FRONT_CENTER, ++ AV_CHAN_LOW_FREQUENCY, ++ AV_CHAN_SIDE_LEFT, AV_CHAN_SIDE_RIGHT, ++ AV_CHAN_BACK_LEFT, AV_CHAN_BACK_RIGHT, ++ AV_CHAN_TOP_FRONT_LEFT, AV_CHAN_TOP_FRONT_RIGHT, ++ AV_CHAN_TOP_BACK_LEFT, AV_CHAN_TOP_BACK_RIGHT ++}; ++ ++static const Av3aChannelConfigMap ff_av3a_channels_map_table[AV3A_CHANNEL_LAYOUT_SIZE] = { ++ { CHANNEL_CONFIG_MONO, 1, ff_av3a_default_channel_layout_mono, AV3A_CH_LAYOUT_MONO }, ++ { CHANNEL_CONFIG_STEREO, 2, ff_av3a_default_channel_layout_stereo, AV3A_CH_LAYOUT_STEREO }, ++ { CHANNEL_CONFIG_MC_5_1, 6, ff_av3a_default_channel_layout_mc_5_1, AV3A_CH_LAYOUT_5POINT1 }, ++ { CHANNEL_CONFIG_MC_7_1, 8, ff_av3a_default_channel_layout_mc_7_1, AV3A_CH_LAYOUT_7POINT1 }, ++ { CHANNEL_CONFIG_MC_10_2, 12, NULL, 0L }, /* reserved */ ++ { CHANNEL_CONFIG_MC_22_2, 24, NULL, 0L }, /* reserved */ ++ { CHANNEL_CONFIG_MC_4_0, 4, ff_av3a_channel_layout_mc_4_0, AV3A_CH_LAYOUT_4POINT0 }, ++ { CHANNEL_CONFIG_MC_5_1_2, 8, ff_av3a_default_channel_layout_mc_5_1_2, AV3A_CH_LAYOUT_5POINT1POINT2 }, ++ { CHANNEL_CONFIG_MC_5_1_4, 10, ff_av3a_default_channel_layout_mc_5_1_4, AV3A_CH_LAYOUT_5POINT1POINT4 }, ++ { CHANNEL_CONFIG_MC_7_1_2, 10, ff_av3a_default_channel_layout_mc_7_1_2, AV3A_CH_LAYOUT_7POINT1POINT2 }, ++ { CHANNEL_CONFIG_MC_7_1_4, 12, ff_av3a_default_channel_layout_mc_7_1_4, AV3A_CH_LAYOUT_7POINT1POINT4 }, ++ { CHANNEL_CONFIG_HOA_ORDER1, 4, NULL, 0L }, ++ { CHANNEL_CONFIG_HOA_ORDER2, 9, NULL, 0L }, ++ { CHANNEL_CONFIG_HOA_ORDER3, 16, NULL, 0L }, ++ { CHANNEL_CONFIG_UNKNOWN, 0, NULL, 0L }, ++}; ++ ++typedef struct { ++ Av3aChannelConfig channel_number_index; ++ const int64_t *bitrate_table; ++} Av3aBitrateMap; ++ ++static const Av3aBitrateMap ff_av3a_bitrate_map_table[15] = { ++ {CHANNEL_CONFIG_MONO, ff_av3a_mono_bitrate_table }, ++ {CHANNEL_CONFIG_STEREO, ff_av3a_stereo_bitrate_table }, ++ {CHANNEL_CONFIG_MC_5_1, ff_av3a_mc5p1_bitrate_table }, ++ {CHANNEL_CONFIG_MC_7_1, ff_av3a_mc7p1_bitrate_table }, ++ {CHANNEL_CONFIG_MC_10_2, NULL }, /* reserved */ ++ {CHANNEL_CONFIG_MC_22_2, NULL }, /* reserved */ ++ {CHANNEL_CONFIG_MC_4_0, ff_av3a_mc4p0_bitrate_table }, ++ {CHANNEL_CONFIG_MC_5_1_2, ff_av3a_mc5p1p2_bitrate_table }, ++ {CHANNEL_CONFIG_MC_5_1_4, ff_av3a_mc5p1p4_bitrate_table }, ++ {CHANNEL_CONFIG_MC_7_1_2, ff_av3a_mc7p1p2_bitrate_table }, ++ {CHANNEL_CONFIG_MC_7_1_4, ff_av3a_mc7p1p4_bitrate_table }, ++ {CHANNEL_CONFIG_HOA_ORDER1, ff_av3a_foa_bitrate_table }, ++ {CHANNEL_CONFIG_HOA_ORDER2, ff_av3a_hoa2_bitrate_table }, ++ {CHANNEL_CONFIG_HOA_ORDER3, ff_av3a_hoa3_bitrate_table }, ++ {CHANNEL_CONFIG_UNKNOWN, NULL }, ++}; ++#endif /* AVCODEC_AV3A_H */ +diff --git a/libavcodec/av3a_parser.c b/libavcodec/av3a_parser.c +new file mode 100644 +index 0000000..66d461e +--- /dev/null ++++ b/libavcodec/av3a_parser.c +@@ -0,0 +1,219 @@ ++/* ++ * AV3A Parser ++ * ++ * Copyright (c) 2024 Shuai Liu ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg 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. ++ * ++ * FFmpeg 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 FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include ++#include ++#include "libavutil/intreadwrite.h" ++#include "parser.h" ++#include "get_bits.h" ++#include "av3a.h" ++#include "parser_internal.h" ++ ++typedef struct { ++ int16_t audio_codec_id; ++ int16_t nn_type; ++ int16_t frame_size; ++ int16_t resolution; ++ int32_t sample_rate; ++ int64_t bit_rate; ++ ++ int16_t content_type; ++ int16_t channel_number_index; ++ int16_t nb_channels; ++ int16_t nb_objects; ++ int16_t total_channels; ++} Av3aParseContext; ++ ++static int ff_read_av3a_header_parse(GetBitContext *gb, AATFHeaderInfo *hdf) ++{ ++ int64_t soundbed_bitrate = 0L; ++ int64_t object_bitrate = 0L; ++ ++ hdf->nb_channels = 0; ++ hdf->nb_objects = 0; ++ ++ hdf->sync_word = get_bits(gb, 12); ++ if (hdf->sync_word != AV3A_AUDIO_SYNC_WORD) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ hdf->audio_codec_id = get_bits(gb, 4); ++ if (hdf->audio_codec_id != AV3A_LOSSY_CODEC_ID) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ skip_bits(gb, 1); /* skip anc_data 1 bit */ ++ ++ hdf->nn_type = get_bits(gb, 3); ++ if ((hdf->nn_type > AV3A_LC_NN_TYPE) || (hdf->nn_type < AV3A_BASELINE_NN_TYPE)) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ hdf->coding_profile = get_bits(gb, 3); ++ ++ hdf->sampling_frequency_index = get_bits(gb, 4); ++ if ((hdf->sampling_frequency_index >= AV3A_FS_TABLE_SIZE) || (hdf->sampling_frequency_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->sampling_rate = ff_av3a_sampling_rate_table[hdf->sampling_frequency_index]; ++ ++ skip_bits(gb, 8); /* skip CRC 8 bits */ ++ ++ if (hdf->coding_profile == AV3A_BASE_PROFILE) { ++ hdf->content_type = AV3A_CHANNEL_BASED_TYPE; ++ hdf->channel_number_index = get_bits(gb, 7); ++ if ((hdf->channel_number_index >= CHANNEL_CONFIG_UNKNOWN) || ++ (hdf->channel_number_index == CHANNEL_CONFIG_MC_10_2) || ++ (hdf->channel_number_index == CHANNEL_CONFIG_MC_22_2) || ++ (hdf->channel_number_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->nb_channels = ff_av3a_channels_map_table[hdf->channel_number_index].channels; ++ } else if (hdf->coding_profile == AV3A_OBJECT_METADATA_PROFILE) { ++ hdf->soundbed_type = get_bits(gb, 2); ++ if (hdf->soundbed_type == 0) { ++ hdf->content_type = AV3A_OBJECT_BASED_TYPE; ++ hdf->object_channel_number = get_bits(gb, 7); ++ if (hdf->object_channel_number < 0) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->nb_objects = hdf->object_channel_number + 1; ++ ++ hdf->bitrate_index_per_channel = get_bits(gb, 4); ++ if ((hdf->bitrate_index_per_channel >= AV3A_BITRATE_TABLE_SIZE) || (hdf->bitrate_index_per_channel < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ object_bitrate = ff_av3a_bitrate_map_table[CHANNEL_CONFIG_MONO].bitrate_table[hdf->bitrate_index_per_channel]; ++ hdf->total_bitrate = object_bitrate * hdf->nb_objects; ++ } else if (hdf->soundbed_type == 1) { ++ hdf->content_type = AV3A_CHANNEL_OBJECT_TYPE; ++ hdf->channel_number_index = get_bits(gb, 7); ++ if ((hdf->channel_number_index >= CHANNEL_CONFIG_UNKNOWN) || ++ (hdf->channel_number_index == CHANNEL_CONFIG_MC_10_2) || ++ (hdf->channel_number_index == CHANNEL_CONFIG_MC_22_2) || ++ (hdf->channel_number_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ hdf->bitrate_index = get_bits(gb, 4); ++ if ((hdf->bitrate_index >= AV3A_BITRATE_TABLE_SIZE) || (hdf->bitrate_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->nb_channels = ff_av3a_channels_map_table[hdf->channel_number_index].channels; ++ soundbed_bitrate = ff_av3a_bitrate_map_table[hdf->channel_number_index].bitrate_table[hdf->bitrate_index]; ++ ++ hdf->object_channel_number = get_bits(gb, 7); ++ if (hdf->object_channel_number < 0) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->bitrate_index_per_channel = get_bits(gb, 4); ++ if ((hdf->bitrate_index_per_channel >= AV3A_BITRATE_TABLE_SIZE) || (hdf->bitrate_index_per_channel < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->nb_objects = hdf->object_channel_number + 1; ++ object_bitrate = ff_av3a_bitrate_map_table[CHANNEL_CONFIG_MONO].bitrate_table[hdf->bitrate_index_per_channel]; ++ hdf->total_bitrate = soundbed_bitrate + (object_bitrate * hdf->nb_objects); ++ } else { ++ return AVERROR_INVALIDDATA; ++ } ++ } else if (hdf->coding_profile == AV3A_AMBISONIC_PROFILE) { ++ hdf->content_type = AV3A_AMBISONIC_TYPE; ++ hdf->order = get_bits(gb, 4); ++ hdf->hoa_order = hdf->order + 1; ++ ++ switch (hdf->hoa_order) { ++ case AV3A_AMBISONIC_FIRST_ORDER: ++ hdf->channel_number_index = CHANNEL_CONFIG_HOA_ORDER1; ++ break; ++ case AV3A_AMBISONIC_SECOND_ORDER: ++ hdf->channel_number_index = CHANNEL_CONFIG_HOA_ORDER2; ++ break; ++ case AV3A_AMBISONIC_THIRD_ORDER: ++ hdf->channel_number_index = CHANNEL_CONFIG_HOA_ORDER3; ++ break; ++ default: ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->nb_channels = ff_av3a_channels_map_table[hdf->channel_number_index].channels; ++ } else { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->total_channels = hdf->nb_channels + hdf->nb_objects; ++ ++ hdf->resolution_index = get_bits(gb, 2); ++ if ((hdf->resolution_index >= AV3A_RESOLUTION_TABLE_SIZE) || (hdf->resolution_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->resolution = ff_av3a_sample_format_map_table[hdf->resolution_index].resolution; ++ hdf->sample_format = ff_av3a_sample_format_map_table[hdf->resolution_index].sample_format; ++ ++ if (hdf->coding_profile != AV3A_OBJECT_METADATA_PROFILE) { ++ hdf->bitrate_index = get_bits(gb, 4); ++ if ((hdf->bitrate_index >= AV3A_BITRATE_TABLE_SIZE) || (hdf->bitrate_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->total_bitrate = ff_av3a_bitrate_map_table[hdf->channel_number_index].bitrate_table[hdf->bitrate_index]; ++ } ++ ++ skip_bits(gb, 8); /* skip CRC 8 bits */ ++ ++ return 0; ++} ++ ++static int raw_av3a_parse(AVCodecParserContext *s, AVCodecContext *avctx, const uint8_t **poutbuf, ++ int32_t *poutbuf_size, const uint8_t *buf, int32_t buf_size) ++{ ++ int ret = 0; ++ uint8_t header[AV3A_MAX_NBYTES_HEADER]; ++ AATFHeaderInfo hdf; ++ GetBitContext gb; ++ ++ if (buf_size < AV3A_MAX_NBYTES_HEADER) { ++ return buf_size; ++ } ++ memcpy(header, buf, AV3A_MAX_NBYTES_HEADER); ++ ++ init_get_bits8(&gb, buf, AV3A_MAX_NBYTES_HEADER); ++ if ((ret = ff_read_av3a_header_parse(&gb, &hdf)) != 0) { ++ return ret; ++ } ++ ++ avctx->codec_id = AV_CODEC_ID_AVS3DA; ++ avctx->frame_size = AV3A_AUDIO_FRAME_SIZE; ++ avctx->bits_per_raw_sample = hdf.resolution; ++ avctx->sample_rate = hdf.sampling_rate; ++ avctx->bit_rate = hdf.total_bitrate; ++ ++ avctx->ch_layout.order = AV_CHANNEL_ORDER_UNSPEC; ++ avctx->ch_layout.nb_channels = hdf.total_channels; ++ ++ *poutbuf = buf; ++ *poutbuf_size = buf_size; ++ ++ return buf_size; ++} ++ ++const FFCodecParser ff_av3a_parser = { ++ PARSER_CODEC_LIST(AV_CODEC_ID_AVS3DA), ++ .priv_data_size = sizeof(Av3aParseContext), ++ .parse = raw_av3a_parse, ++}; +diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c +index a9f21f8..93877ed 100644 +--- a/libavcodec/codec_desc.c ++++ b/libavcodec/codec_desc.c +@@ -3863,6 +3863,13 @@ static const AVCodecDescriptor codec_descriptors[] = { + .long_name = NULL_IF_CONFIG_SMALL("AVFrame to AVPacket passthrough"), + .props = AV_CODEC_PROP_LOSSLESS, + }, ++ { ++ .id = AV_CODEC_ID_AVS3DA, ++ .type = AVMEDIA_TYPE_AUDIO, ++ .name = "av3a", ++ .long_name = NULL_IF_CONFIG_SMALL("Audio Vivid"), ++ .props = AV_CODEC_PROP_LOSSY, ++ }, + { + .id = AV_CODEC_ID_VNULL, + .type = AVMEDIA_TYPE_VIDEO, +diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h +index 6529f0a..43f7549 100644 +--- a/libavcodec/codec_id.h ++++ b/libavcodec/codec_id.h +@@ -628,6 +628,7 @@ enum AVCodecID { + * Dummy null video codec, useful mainly for development and debugging. + * Null encoder/decoder discard all input and never return any output. + */ ++ AV_CODEC_ID_AVS3DA, + AV_CODEC_ID_VNULL, + /** + * Dummy null audio codec, useful mainly for development and debugging. +diff --git a/libavcodec/parsers.c b/libavcodec/parsers.c +index 162b96c..415c842 100644 +--- a/libavcodec/parsers.c ++++ b/libavcodec/parsers.c +@@ -47,6 +47,7 @@ extern const FFCodecParser ff_apv_parser; + extern const FFCodecParser ff_av1_parser; + extern const FFCodecParser ff_avs2_parser; + extern const FFCodecParser ff_avs3_parser; ++extern const FFCodecParser ff_av3a_parser; + extern const FFCodecParser ff_bmp_parser; + extern const FFCodecParser ff_cavsvideo_parser; + extern const FFCodecParser ff_cook_parser; +diff --git a/libavcodec/utils.c b/libavcodec/utils.c +index 615d60c..46f2123 100644 +--- a/libavcodec/utils.c ++++ b/libavcodec/utils.c +@@ -603,6 +603,7 @@ static int get_audio_frame_duration(enum AVCodecID id, int sr, int ch, int ba, + case AV_CODEC_ID_MP2: + case AV_CODEC_ID_MUSEPACK7: return 1152; + case AV_CODEC_ID_AC3: return 1536; ++ case AV_CODEC_ID_AVS3DA: return 1024; + case AV_CODEC_ID_FTR: return 1024; + } + +diff --git a/libavformat/Makefile b/libavformat/Makefile +index 85ed05b..158a11d 100644 +--- a/libavformat/Makefile ++++ b/libavformat/Makefile +@@ -169,6 +169,7 @@ OBJS-$(CONFIG_AVS2_DEMUXER) += avs2dec.o rawdec.o + OBJS-$(CONFIG_AVS2_MUXER) += rawenc.o + OBJS-$(CONFIG_AVS3_DEMUXER) += avs3dec.o rawdec.o + OBJS-$(CONFIG_AVS3_MUXER) += rawenc.o ++OBJS-$(CONFIG_AV3A_DEMUXER) += av3adec.o + OBJS-$(CONFIG_BETHSOFTVID_DEMUXER) += bethsoftvid.o + OBJS-$(CONFIG_BFI_DEMUXER) += bfi.o + OBJS-$(CONFIG_BINK_DEMUXER) += bink.o +diff --git a/libavformat/allformats.c b/libavformat/allformats.c +index e2f37da..1a05db9 100644 +--- a/libavformat/allformats.c ++++ b/libavformat/allformats.c +@@ -101,6 +101,7 @@ extern const FFInputFormat ff_avs2_demuxer; + extern const FFOutputFormat ff_avs2_muxer; + extern const FFInputFormat ff_avs3_demuxer; + extern const FFOutputFormat ff_avs3_muxer; ++extern const FFInputFormat ff_av3a_demuxer; + extern const FFInputFormat ff_bethsoftvid_demuxer; + extern const FFInputFormat ff_bfi_demuxer; + extern const FFInputFormat ff_bintext_demuxer; +diff --git a/libavformat/av3adec.c b/libavformat/av3adec.c +new file mode 100644 +index 0000000..9bb8729 +--- /dev/null ++++ b/libavformat/av3adec.c +@@ -0,0 +1,473 @@ ++/* ++ * AV3A Demuxer ++ * ++ * Copyright (c) 2024 Shuai Liu ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg 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. ++ * ++ * FFmpeg 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 FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++#include "avformat.h" ++#include "avio_internal.h" ++#include "internal.h" ++#include "rawdec.h" ++#include "libavutil/opt.h" ++#include "libavutil/avassert.h" ++#include "libavutil/intreadwrite.h" ++#include "libavutil/channel_layout.h" ++#include "libavcodec/get_bits.h" ++#include "libavcodec/av3a.h" ++#include ++ ++typedef struct { ++ uint8_t audio_codec_id; ++ uint8_t sampling_frequency_index; ++ uint8_t nn_type; ++ uint8_t content_type; ++ uint8_t channel_number_index; ++ uint8_t number_objects; ++ uint8_t hoa_order; ++ uint8_t resolution_index; ++ uint16_t total_bitrate_kbps; ++} Av3aFormatContext; ++ ++static int av3a_read_aatf_frame_header(AATFHeaderInfo *hdf, const uint8_t *buf) ++{ ++ int16_t sync_word; ++ GetBitContext gb; ++ ++ hdf->nb_channels = 0; ++ hdf->nb_objects = 0; ++ ++ init_get_bits8(&gb, buf, AV3A_MAX_NBYTES_HEADER); ++ ++ sync_word = get_bits(&gb, 12); ++ if (sync_word != AV3A_AUDIO_SYNC_WORD) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ /* codec id */ ++ hdf->audio_codec_id = get_bits(&gb, 4); ++ if (hdf->audio_codec_id != AV3A_LOSSY_CODEC_ID) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ /* anc data */ ++ hdf->anc_data = get_bits(&gb, 1); ++ if (hdf->anc_data) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ /* neural network type */ ++ hdf->nn_type = get_bits(&gb, 3); ++ if ((hdf->nn_type > AV3A_LC_NN_TYPE) || (hdf->nn_type < AV3A_BASELINE_NN_TYPE)) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ /* coding profile */ ++ hdf->coding_profile = get_bits(&gb, 3); ++ ++ /* sampling rate */ ++ hdf->sampling_frequency_index = get_bits(&gb, 4); ++ if ((hdf->sampling_frequency_index >= AV3A_FS_TABLE_SIZE) || (hdf->sampling_frequency_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->sampling_rate = ff_av3a_sampling_rate_table[hdf->sampling_frequency_index]; ++ ++ skip_bits(&gb, 8); ++ ++ if (hdf->coding_profile == AV3A_BASE_PROFILE) { ++ hdf->content_type = AV3A_CHANNEL_BASED_TYPE; ++ hdf->channel_number_index = get_bits(&gb, 7); ++ if ((hdf->channel_number_index >= CHANNEL_CONFIG_UNKNOWN) || ++ (hdf->channel_number_index == CHANNEL_CONFIG_MC_10_2) || ++ (hdf->channel_number_index == CHANNEL_CONFIG_MC_22_2) || ++ (hdf->channel_number_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->nb_channels = ff_av3a_channels_map_table[hdf->channel_number_index].channels; ++ } else if (hdf->coding_profile == AV3A_OBJECT_METADATA_PROFILE) { ++ hdf->soundbed_type = get_bits(&gb, 2); ++ if (hdf->soundbed_type == 0) { ++ hdf->content_type = AV3A_OBJECT_BASED_TYPE; ++ hdf->object_channel_number = get_bits(&gb, 7); ++ if (hdf->object_channel_number < 0) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->bitrate_index_per_channel = get_bits(&gb, 4); ++ if ((hdf->bitrate_index_per_channel >= AV3A_BITRATE_TABLE_SIZE) || (hdf->bitrate_index_per_channel < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->nb_objects = hdf->object_channel_number + 1; ++ hdf->total_bitrate = ff_av3a_bitrate_map_table[CHANNEL_CONFIG_MONO].bitrate_table[hdf->bitrate_index_per_channel] * hdf->nb_objects; ++ } else if (hdf->soundbed_type == 1) { ++ hdf->content_type = AV3A_CHANNEL_OBJECT_TYPE; ++ hdf->channel_number_index = get_bits(&gb, 7); ++ if ((hdf->channel_number_index >= CHANNEL_CONFIG_UNKNOWN) || ++ (hdf->channel_number_index == CHANNEL_CONFIG_MC_10_2) || ++ (hdf->channel_number_index == CHANNEL_CONFIG_MC_22_2) || ++ (hdf->channel_number_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->nb_channels = ff_av3a_channels_map_table[hdf->channel_number_index].channels; ++ hdf->bitrate_index = get_bits(&gb, 4); ++ if ((hdf->bitrate_index >= AV3A_BITRATE_TABLE_SIZE) || (hdf->bitrate_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ hdf->object_channel_number = get_bits(&gb, 7); ++ if (hdf->object_channel_number < 0) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->nb_objects = hdf->object_channel_number + 1; ++ hdf->bitrate_index_per_channel = get_bits(&gb, 4); ++ if ((hdf->bitrate_index_per_channel >= AV3A_BITRATE_TABLE_SIZE) || (hdf->bitrate_index_per_channel < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ hdf->total_bitrate = ff_av3a_bitrate_map_table[hdf->channel_number_index].bitrate_table[hdf->bitrate_index] + ++ ff_av3a_bitrate_map_table[CHANNEL_CONFIG_MONO].bitrate_table[hdf->bitrate_index_per_channel] * hdf->nb_objects; ++ } else { ++ return AVERROR_INVALIDDATA; ++ } ++ } else if (hdf->coding_profile == AV3A_AMBISONIC_PROFILE) { ++ hdf->content_type = AV3A_AMBISONIC_TYPE; ++ hdf->order = get_bits(&gb, 4); ++ hdf->hoa_order = hdf->order + 1; ++ ++ switch (hdf->hoa_order) { ++ case AV3A_AMBISONIC_FIRST_ORDER: ++ hdf->channel_number_index = CHANNEL_CONFIG_HOA_ORDER1; ++ break; ++ case AV3A_AMBISONIC_SECOND_ORDER: ++ hdf->channel_number_index = CHANNEL_CONFIG_HOA_ORDER2; ++ break; ++ case AV3A_AMBISONIC_THIRD_ORDER: ++ hdf->channel_number_index = CHANNEL_CONFIG_HOA_ORDER3; ++ break; ++ default: ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->nb_channels = ff_av3a_channels_map_table[hdf->channel_number_index].channels; ++ } else { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ hdf->total_channels = hdf->nb_channels + hdf->nb_objects; ++ ++ /* resolution */ ++ hdf->resolution_index = get_bits(&gb, 2); ++ if ((hdf->resolution_index >= AV3A_RESOLUTION_TABLE_SIZE) || (hdf->resolution_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->resolution = ff_av3a_sample_format_map_table[hdf->resolution_index].resolution; ++ hdf->sample_format = ff_av3a_sample_format_map_table[hdf->resolution_index].sample_format; ++ ++ if (hdf->coding_profile != AV3A_OBJECT_METADATA_PROFILE) { ++ hdf->bitrate_index = get_bits(&gb, 4); ++ if ((hdf->bitrate_index >= AV3A_BITRATE_TABLE_SIZE) || (hdf->bitrate_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->total_bitrate = ff_av3a_bitrate_map_table[hdf->channel_number_index].bitrate_table[hdf->bitrate_index]; ++ } ++ ++ skip_bits(&gb, 8); ++ ++ return 0; ++} ++ ++static int av3a_get_packet_size(AVFormatContext *s) ++{ ++ int ret = 0; ++ int read_bytes = 0; ++ uint16_t sync_word = 0; ++ int payload_bytes = 0; ++ int payloud_bits = 0; ++ uint8_t header[AV3A_MAX_NBYTES_HEADER]; ++ GetBitContext gb; ++ int32_t sampling_rate; ++ int16_t coding_profile, sampling_frequency_index, channel_number_index; ++ int16_t bitrate_index, bitrate_index_per_channel; ++ int16_t objects, hoa_order; ++ int64_t total_bitrate; ++ ++ if (!s) { ++ return AVERROR(ENOMEM); ++ } ++ ++ if (!s->pb) { ++ return AVERROR(ENOMEM); ++ } ++ ++ read_bytes = avio_read(s->pb, header, AV3A_MAX_NBYTES_HEADER); ++ if (read_bytes != AV3A_MAX_NBYTES_HEADER) { ++ return (read_bytes < 0) ? read_bytes : AVERROR_EOF; ++ } ++ ++ init_get_bits8(&gb, header, AV3A_MAX_NBYTES_HEADER); ++ ++ sync_word = get_bits(&gb, 12); ++ if (sync_word != AV3A_AUDIO_SYNC_WORD) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ skip_bits(&gb, 8); ++ ++ coding_profile = get_bits(&gb, 3); ++ sampling_frequency_index = get_bits(&gb, 4); ++ if ((sampling_frequency_index >= AV3A_FS_TABLE_SIZE) || (sampling_frequency_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ sampling_rate = ff_av3a_sampling_rate_table[sampling_frequency_index]; ++ ++ skip_bits(&gb, 8); ++ ++ if (coding_profile == AV3A_BASE_PROFILE) { ++ channel_number_index = get_bits(&gb, 7); ++ if ((channel_number_index >= CHANNEL_CONFIG_UNKNOWN) || ++ (channel_number_index == CHANNEL_CONFIG_MC_10_2) || ++ (channel_number_index == CHANNEL_CONFIG_MC_22_2) || ++ (channel_number_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ } else if (coding_profile == AV3A_OBJECT_METADATA_PROFILE) { ++ int64_t soundbed_bitrate, objects_bitrate; ++ int16_t soundbed_type = get_bits(&gb, 2); ++ if (soundbed_type == 0) { ++ objects = get_bits(&gb, 7); ++ if (objects < 0) { ++ return AVERROR_INVALIDDATA; ++ } ++ objects += 1; ++ ++ bitrate_index_per_channel = get_bits(&gb, 4); ++ if ((bitrate_index_per_channel >= AV3A_BITRATE_TABLE_SIZE) || (bitrate_index_per_channel < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ total_bitrate = ff_av3a_bitrate_map_table[CHANNEL_CONFIG_MONO].bitrate_table[bitrate_index_per_channel] * objects; ++ } else if (soundbed_type == 1) { ++ channel_number_index = get_bits(&gb, 7); ++ if ((channel_number_index >= CHANNEL_CONFIG_UNKNOWN) || ++ (channel_number_index == CHANNEL_CONFIG_MC_10_2) || ++ (channel_number_index == CHANNEL_CONFIG_MC_22_2) || ++ (channel_number_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ bitrate_index = get_bits(&gb, 4); ++ if ((bitrate_index >= AV3A_BITRATE_TABLE_SIZE) || (bitrate_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ soundbed_bitrate = ff_av3a_bitrate_map_table[channel_number_index].bitrate_table[bitrate_index]; ++ ++ objects = get_bits(&gb, 7); ++ if (objects < 0) { ++ return AVERROR_INVALIDDATA; ++ } ++ objects += 1; ++ bitrate_index_per_channel = get_bits(&gb, 4); ++ if ((bitrate_index_per_channel >= AV3A_BITRATE_TABLE_SIZE) || (bitrate_index_per_channel < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ objects_bitrate = ff_av3a_bitrate_map_table[CHANNEL_CONFIG_MONO].bitrate_table[bitrate_index_per_channel]; ++ total_bitrate = soundbed_bitrate + (objects_bitrate * objects); ++ } else { ++ return AVERROR_INVALIDDATA; ++ } ++ } else if (coding_profile == AV3A_AMBISONIC_PROFILE) { ++ hoa_order = get_bits(&gb, 4); ++ hoa_order += 1; ++ ++ switch (hoa_order) { ++ case AV3A_AMBISONIC_FIRST_ORDER: ++ channel_number_index = CHANNEL_CONFIG_HOA_ORDER1; ++ break; ++ case AV3A_AMBISONIC_SECOND_ORDER: ++ channel_number_index = CHANNEL_CONFIG_HOA_ORDER2; ++ break; ++ case AV3A_AMBISONIC_THIRD_ORDER: ++ channel_number_index = CHANNEL_CONFIG_HOA_ORDER3; ++ break; ++ default: ++ return AVERROR_INVALIDDATA; ++ } ++ } else { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ skip_bits(&gb, 2); ++ if (coding_profile != AV3A_OBJECT_METADATA_PROFILE) { ++ bitrate_index = get_bits(&gb, 4); ++ if ((bitrate_index >= AV3A_BITRATE_TABLE_SIZE) || (bitrate_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ total_bitrate = ff_av3a_bitrate_map_table[channel_number_index].bitrate_table[bitrate_index]; ++ } ++ ++ skip_bits(&gb, 8); ++ ++ if (sampling_rate == 44100) { ++ payloud_bits = (int)floor(((float) (total_bitrate) / sampling_rate) * AV3A_AUDIO_FRAME_SIZE); ++ payload_bytes = (int)ceil((float)payloud_bits / 8); ++ } else { ++ payload_bytes = (int)ceil((((float) (total_bitrate) / sampling_rate) * AV3A_AUDIO_FRAME_SIZE) / 8); ++ } ++ ++ if ((ret = avio_seek(s->pb, -read_bytes, SEEK_CUR)) < 0) { ++ return ret; ++ } ++ ++ return payload_bytes; ++} ++ ++static int av3a_probe(const AVProbeData *p) ++{ ++ uint16_t frame_sync_word; ++ uint16_t lval = ((uint16_t)(p->buf[0])); ++ uint16_t rval = ((uint16_t)(p->buf[1])); ++ frame_sync_word = ((lval << 8) | rval) >> 4; ++ ++ if (frame_sync_word == AV3A_AUDIO_SYNC_WORD && av_match_ext(p->filename, "av3a")) { ++ return AVPROBE_SCORE_MAX; ++ } ++ ++ return 0; ++} ++ ++static int av3a_read_header(AVFormatContext *s) ++{ ++ int ret = 0; ++ uint8_t header[AV3A_MAX_NBYTES_HEADER]; ++ AVStream *stream = NULL; ++ Av3aFormatContext av3afmtctx; ++ AATFHeaderInfo hdf; ++ ++ if (!s) { ++ return AVERROR(ENOMEM); ++ } ++ ++ if (!(stream = avformat_new_stream(s, NULL))) { ++ return AVERROR(ENOMEM); ++ } ++ ++ stream->start_time = 0; ++ ffstream(stream)->need_parsing = AVSTREAM_PARSE_FULL_RAW; ++ stream->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; ++ stream->codecpar->codec_id = ((const FFInputFormat*)(s->iformat))->raw_codec_id; ++ stream->codecpar->codec_tag = MKTAG('a', 'v', '3', 'a'); ++ ++ if ((ret = avio_read(s->pb, header, AV3A_MAX_NBYTES_HEADER)) != AV3A_MAX_NBYTES_HEADER) { ++ return (ret < 0) ? ret : AVERROR_EOF; ++ } ++ ++ ret = av3a_read_aatf_frame_header(&hdf, header); ++ if (ret) { ++ return ret; ++ } ++ ++ /* stream parameters */ ++ stream->codecpar->format = hdf.sample_format; ++ stream->codecpar->bits_per_raw_sample = hdf.resolution; ++ stream->codecpar->bit_rate = hdf.total_bitrate; ++ stream->codecpar->sample_rate = (int) (hdf.sampling_rate); ++ stream->codecpar->frame_size = AV3A_AUDIO_FRAME_SIZE; ++ stream->codecpar->ch_layout.order = AV_CHANNEL_ORDER_UNSPEC; ++ stream->codecpar->ch_layout.nb_channels = hdf.total_channels; ++ ++ /* extradata */ ++ av3afmtctx.audio_codec_id = hdf.audio_codec_id; ++ av3afmtctx.sampling_frequency_index = hdf.sampling_frequency_index; ++ av3afmtctx.nn_type = hdf.nn_type; ++ av3afmtctx.content_type = hdf.content_type; ++ av3afmtctx.channel_number_index = hdf.channel_number_index; ++ av3afmtctx.number_objects = hdf.nb_objects; ++ av3afmtctx.hoa_order = hdf.hoa_order; ++ av3afmtctx.resolution_index = hdf.resolution_index; ++ av3afmtctx.total_bitrate_kbps = (int) (hdf.total_bitrate / 1000); ++ ++ if ((ret = ff_alloc_extradata(stream->codecpar, sizeof(Av3aFormatContext))) < 0) { ++ return ret; ++ } ++ memcpy(stream->codecpar->extradata, &av3afmtctx, sizeof(Av3aFormatContext)); ++ ++ if ((ret = avio_seek(s->pb, -AV3A_MAX_NBYTES_HEADER, SEEK_CUR)) < 0) { ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int av3a_read_packet(AVFormatContext *s, AVPacket *pkt) ++{ ++ int64_t pos; ++ int packet_size = 0; ++ int read_bytes = 0; ++ int ret = 0; ++ ++ if (!s) { ++ return AVERROR(ENOMEM); ++ } ++ ++ if (avio_feof(s->pb)) { ++ return AVERROR_EOF; ++ } ++ pos = avio_tell(s->pb); ++ ++ if (!(packet_size = av3a_get_packet_size(s))) { ++ return AVERROR_EOF; ++ } ++ ++ if (packet_size < 0) { ++ return packet_size; ++ } ++ ++ if ((ret = av_new_packet(pkt, packet_size)) < 0) { ++ return ret; ++ } ++ ++ if (!s->streams[0]) { ++ return AVERROR(ENOMEM); ++ } ++ ++ if (!s->streams[0]->codecpar) { ++ return AVERROR(ENOMEM); ++ } ++ ++ pkt->stream_index = 0; ++ pkt->pos = pos; ++ pkt->duration = s->streams[0]->codecpar->frame_size; ++ ++ read_bytes = avio_read(s->pb, pkt->data, packet_size); ++ if (read_bytes != packet_size) { ++ return (read_bytes < 0) ? read_bytes : AVERROR_EOF; ++ } ++ ++ return 0; ++} ++ ++const FFInputFormat ff_av3a_demuxer = { ++ .p.name = "av3a", ++ .p.long_name = NULL_IF_CONFIG_SMALL("Audio Vivid"), ++ .raw_codec_id = AV_CODEC_ID_AVS3DA, ++ .p.priv_class = &ff_raw_demuxer_class, ++ .priv_data_size = sizeof(FFRawDemuxerContext), ++ .read_probe = av3a_probe, ++ .read_header = av3a_read_header, ++ .read_packet = av3a_read_packet, ++ .p.flags = AVFMT_GENERIC_INDEX, ++ .p.extensions = "av3a", ++ .p.mime_type = "audio/av3a", ++}; +\ No newline at end of file +diff --git a/libavformat/isom_tags.c b/libavformat/isom_tags.c +index 1cd655b..528c518 100644 +--- a/libavformat/isom_tags.c ++++ b/libavformat/isom_tags.c +@@ -368,6 +368,7 @@ const AVCodecTag ff_codec_movaudio_tags[] = { + { AV_CODEC_ID_TRUEHD, MKTAG('m', 'l', 'p', 'a') }, /* mp4ra.org */ + { AV_CODEC_ID_OPUS, MKTAG('O', 'p', 'u', 's') }, /* mp4ra.org */ + { AV_CODEC_ID_MPEGH_3D_AUDIO, MKTAG('m', 'h', 'm', '1') }, /* MPEG-H 3D Audio bitstream */ ++ { AV_CODEC_ID_AVS3DA, MKTAG('a', 'v', '3', 'a') }, /* AVS3 Audio */ + { AV_CODEC_ID_NONE, 0 }, + }; + +diff --git a/libavformat/mov.c b/libavformat/mov.c +index e114770..b639fe1 100644 +--- a/libavformat/mov.c ++++ b/libavformat/mov.c +@@ -71,6 +71,10 @@ + #include "mov_chan.h" + #include "replaygain.h" + ++#if CONFIG_AV3A_DEMUXER ++#include "libavcodec/av3a.h" ++#endif ++ + #if CONFIG_ZLIB + #include + #endif +@@ -88,6 +92,123 @@ static int mov_read_default(MOVContext *c, AVIOContext *pb, MOVAtom atom); + static int mov_read_mfra(MOVContext *c, AVIOContext *f); + static void mov_free_stream_context(AVFormatContext *s, AVStream *st); + ++#if CONFIG_AV3A_DEMUXER ++static int mov_read_dca3(MOVContext *c, AVIOContext *pb, MOVAtom atom) ++{ ++ int ret; ++ int nb_channels = 0; ++ int nb_objects = 0; ++ AVStream *st; ++ GetBitContext gb; ++ uint8_t buffer[7]; ++ int audio_codec_id, sampling_frequency_index; ++ int nn_type, content_type, channel_number_index = 0, number_objects; ++ int hoa_order, resolution_index; ++ int bitrate_kbps; ++ ++ if (atom.size < AV3A_DCA3_BOX_MIN_SIZE) ++ return AVERROR_INVALIDDATA; ++ ++ if (c->fc->nb_streams < 1) ++ return 0; ++ st = c->fc->streams[c->fc->nb_streams - 1]; ++ ++ ret = avio_read(pb, buffer, sizeof(buffer)); ++ if (ret < 0) ++ return ret; ++ if (ret != (int)sizeof(buffer)) ++ return AVERROR_INVALIDDATA; ++ ++ init_get_bits8(&gb, buffer, sizeof(buffer)); ++ ++ audio_codec_id = get_bits(&gb, 4); ++ if (audio_codec_id != AV3A_LOSSY_CODEC_ID) ++ return AVERROR_INVALIDDATA; ++ ++ st->codecpar->frame_size = AV3A_AUDIO_FRAME_SIZE; ++ sampling_frequency_index = get_bits(&gb, 4); ++ if (sampling_frequency_index >= AV3A_FS_TABLE_SIZE) ++ return AVERROR_INVALIDDATA; ++ st->codecpar->sample_rate = ff_av3a_sampling_rate_table[sampling_frequency_index]; ++ ++ nn_type = get_bits(&gb, 3); ++ if (nn_type > AV3A_LC_NN_TYPE || nn_type < AV3A_BASELINE_NN_TYPE) ++ return AVERROR_INVALIDDATA; ++ ++ skip_bits(&gb, 1); ++ content_type = get_bits(&gb, 4); ++ if (content_type == AV3A_CHANNEL_BASED_TYPE) { ++ channel_number_index = get_bits(&gb, 7); ++ skip_bits(&gb, 1); ++ if (channel_number_index >= CHANNEL_CONFIG_UNKNOWN || ++ channel_number_index == CHANNEL_CONFIG_MC_10_2 || ++ channel_number_index == CHANNEL_CONFIG_MC_22_2) ++ return AVERROR_INVALIDDATA; ++ nb_channels = ff_av3a_channels_map_table[channel_number_index].channels; ++ } else if (content_type == AV3A_OBJECT_BASED_TYPE) { ++ number_objects = get_bits(&gb, 7); ++ skip_bits(&gb, 1); ++ nb_objects = number_objects; ++ if (nb_objects < 1) ++ return AVERROR_INVALIDDATA; ++ } else if (content_type == AV3A_CHANNEL_OBJECT_TYPE) { ++ channel_number_index = get_bits(&gb, 7); ++ skip_bits(&gb, 1); ++ if (channel_number_index >= CHANNEL_CONFIG_UNKNOWN || ++ channel_number_index == CHANNEL_CONFIG_MC_10_2 || ++ channel_number_index == CHANNEL_CONFIG_MC_22_2) ++ return AVERROR_INVALIDDATA; ++ number_objects = get_bits(&gb, 7); ++ skip_bits(&gb, 1); ++ nb_channels = ff_av3a_channels_map_table[channel_number_index].channels; ++ nb_objects = number_objects; ++ if (nb_objects < 1) ++ return AVERROR_INVALIDDATA; ++ } else if (content_type == AV3A_AMBISONIC_TYPE) { ++ hoa_order = get_bits(&gb, 4); ++ if (hoa_order < AV3A_AMBISONIC_FIRST_ORDER || ++ hoa_order > AV3A_AMBISONIC_THIRD_ORDER) ++ return AVERROR_INVALIDDATA; ++ nb_channels = (hoa_order + 1) * (hoa_order + 1); ++ } else { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ bitrate_kbps = get_bits(&gb, 16); ++ st->codecpar->bit_rate = bitrate_kbps * 1000; ++ ++ resolution_index = get_bits(&gb, 2); ++ if (resolution_index >= AV3A_RESOLUTION_TABLE_SIZE) ++ return AVERROR_INVALIDDATA; ++ st->codecpar->format = ff_av3a_sample_format_map_table[resolution_index].sample_format; ++ st->codecpar->bits_per_raw_sample = ff_av3a_sample_format_map_table[resolution_index].resolution; ++ ++ av_channel_layout_uninit(&st->codecpar->ch_layout); ++ if (content_type != AV3A_AMBISONIC_TYPE) { ++ st->codecpar->ch_layout.order = AV_CHANNEL_ORDER_CUSTOM; ++ st->codecpar->ch_layout.nb_channels = nb_channels + nb_objects; ++ st->codecpar->ch_layout.u.map = av_calloc(st->codecpar->ch_layout.nb_channels, ++ sizeof(*st->codecpar->ch_layout.u.map)); ++ if (!st->codecpar->ch_layout.u.map) ++ return AVERROR(ENOMEM); ++ ++ if (content_type != AV3A_OBJECT_BASED_TYPE) { ++ for (int i = 0; i < nb_channels; i++) ++ st->codecpar->ch_layout.u.map[i].id = ++ ff_av3a_channels_map_table[channel_number_index].channel_layout[i]; ++ } ++ ++ for (int i = nb_channels; i < st->codecpar->ch_layout.nb_channels; i++) ++ st->codecpar->ch_layout.u.map[i].id = AV3A_CH_AUDIO_OBJECT; ++ } else { ++ st->codecpar->ch_layout.order = AV_CHANNEL_ORDER_AMBISONIC; ++ st->codecpar->ch_layout.nb_channels = nb_channels; ++ } ++ ++ return 0; ++} ++#endif ++ + static int mov_metadata_track_or_disc_number(MOVContext *c, AVIOContext *pb, + unsigned len, const char *key) + { +@@ -9640,6 +9761,9 @@ static const MOVParseTableEntry mov_default_parse_table[] = { + #if CONFIG_IAMFDEC + { MKTAG('i','a','c','b'), mov_read_iacb }, + #endif ++#if CONFIG_AV3A_DEMUXER ++{ MKTAG('d','c','a','3'), mov_read_dca3 }, ++#endif + { MKTAG('s','r','a','t'), mov_read_srat }, + { 0, NULL } + }; +diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c +index 47944c7..d682526 100644 +--- a/libavformat/mpegts.c ++++ b/libavformat/mpegts.c +@@ -825,6 +825,9 @@ static const StreamType ISO_types[] = { + { STREAM_TYPE_VIDEO_DIRAC, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_DIRAC }, + { STREAM_TYPE_VIDEO_AVS2, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_AVS2 }, + { STREAM_TYPE_VIDEO_AVS3, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_AVS3 }, ++#if CONFIG_AV3A_DEMUXER ++ { STREAM_TYPE_AUDIO_AV3A, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AVS3DA }, ++#endif + { STREAM_TYPE_VIDEO_VC1, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_VC1 }, + { 0 }, + }; +@@ -883,6 +886,9 @@ static const StreamType REGD_types[] = { + { MKTAG('I', 'D', '3', ' '), AVMEDIA_TYPE_DATA, AV_CODEC_ID_TIMED_ID3 }, + { MKTAG('V', 'C', '-', '1'), AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_VC1 }, + { MKTAG('O', 'p', 'u', 's'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_OPUS }, ++#if CONFIG_AV3A_DEMUXER ++ { MKTAG('a', 'v', '3', 'a'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AVS3DA}, ++#endif + { 0 }, + }; + +diff --git a/libavformat/mpegts.h b/libavformat/mpegts.h +index 18f326b..92c95ef 100644 +--- a/libavformat/mpegts.h ++++ b/libavformat/mpegts.h +@@ -152,6 +152,7 @@ + #define STREAM_TYPE_VIDEO_AVS3 0xd4 + #define STREAM_TYPE_VIDEO_VC1 0xea + #define STREAM_TYPE_VIDEO_DIRAC 0xd1 ++#define STREAM_TYPE_AUDIO_AV3A 0xd5 + + /* stream_type values [0x80, 0xff] are User Private */ + #define STREAM_TYPE_BLURAY_AUDIO_PCM_BLURAY 0x80 +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.1/0017-http-add-reconnect_first_delay-opt.patch b/patches/ffmpeg-n8.1.1/0017-http-add-reconnect_first_delay-opt.patch new file mode 100644 index 000000000..23c0b615c --- /dev/null +++ b/patches/ffmpeg-n8.1.1/0017-http-add-reconnect_first_delay-opt.patch @@ -0,0 +1,41 @@ +From 63de5ca190448e027cf758f548b2213095638bd6 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Thu, 29 May 2025 09:57:28 +0800 +Subject: http add reconnect_first_delay opt + +--- + libavformat/http.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/libavformat/http.c b/libavformat/http.c +index f4bf225..cd39796 100644 +--- a/libavformat/http.c ++++ b/libavformat/http.c +@@ -131,6 +131,7 @@ typedef struct HTTPContext { + int reconnect_on_network_error; + int reconnect_streamed; + int reconnect_delay_max; ++ int reconnect_first_delay; + char *reconnect_on_http_error; + int listen; + char *resource; +@@ -205,6 +206,7 @@ static const AVOption options[] = { + { "reconnect_max_retries", "the max number of times to retry a connection", OFFSET(reconnect_max_retries), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, D }, + { "reconnect_delay_total_max", "max total reconnect delay in seconds after which to give up", OFFSET(reconnect_delay_total_max), AV_OPT_TYPE_INT, { .i64 = 256 }, 0, UINT_MAX/1000/1000, D }, + { "respect_retry_after", "respect the Retry-After header when retrying connections", OFFSET(respect_retry_after), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, D }, ++ { "reconnect_first_delay", "first reconnect delay in seconds", OFFSET(reconnect_first_delay), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, UINT_MAX/1000/1000, D }, + { "listen", "listen on HTTP", OFFSET(listen), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 2, D | E }, + { "resource", "The resource requested by a client", OFFSET(resource), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E }, + { "reply_code", "The http status code to return to a client", OFFSET(reply_code), AV_OPT_TYPE_INT, { .i64 = 200}, INT_MIN, 599, E}, +@@ -419,7 +421,7 @@ static int http_open_cnx(URLContext *h, AVDictionary **options) + HTTPAuthType cur_auth_type, cur_proxy_auth_type; + HTTPContext *s = h->priv_data; + int ret, conn_attempts = 1, auth_attempts = 0, redirects = 0; +- int reconnect_delay = 0; ++ int reconnect_delay = s->reconnect_first_delay; + int reconnect_delay_total = 0; + uint64_t off; + char *cached; +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.1/0018-fix-http-open-and-http_seek-redirect-authentication-.patch b/patches/ffmpeg-n8.1.1/0018-fix-http-open-and-http_seek-redirect-authentication-.patch new file mode 100644 index 000000000..b5bfa7259 --- /dev/null +++ b/patches/ffmpeg-n8.1.1/0018-fix-http-open-and-http_seek-redirect-authentication-.patch @@ -0,0 +1,102 @@ +From 0eb8def6e29ed4193013c3f7c2a01acf7eb40b28 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Fri, 8 May 2026 17:47:08 +0800 +Subject: fix http open and http_seek (redirect) authentication + bug + +--- + libavformat/http.c | 32 +++++++++++++++++++++++++++----- + 1 file changed, 27 insertions(+), 5 deletions(-) + +diff --git a/libavformat/http.c b/libavformat/http.c +index cd39796..b648596 100644 +--- a/libavformat/http.c ++++ b/libavformat/http.c +@@ -83,6 +83,7 @@ typedef struct HTTPContext { + char *uri; + char *location; + HTTPAuthState auth_state; ++ int auth_type2; + HTTPAuthState proxy_auth_state; + char *http_proxy; + char *headers; +@@ -190,6 +191,7 @@ static const AVOption options[] = { + { "icy_metadata_packet", "return current ICY metadata packet", OFFSET(icy_metadata_packet), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, AV_OPT_FLAG_EXPORT }, + { "metadata", "metadata read from the bitstream", OFFSET(metadata), AV_OPT_TYPE_DICT, {0}, 0, 0, AV_OPT_FLAG_EXPORT }, + { "auth_type", "HTTP authentication type", OFFSET(auth_state.auth_type), AV_OPT_TYPE_INT, { .i64 = HTTP_AUTH_NONE }, HTTP_AUTH_NONE, HTTP_AUTH_BASIC, D | E, .unit = "auth_type"}, ++ { "auth_type2", "backup HTTP authentication type for seek request", OFFSET(auth_type2), AV_OPT_TYPE_INT, { .i64 = HTTP_AUTH_NONE }, HTTP_AUTH_NONE, HTTP_AUTH_BASIC, D | E, "auth_type"}, + { "none", "No auth method set, autodetect", 0, AV_OPT_TYPE_CONST, { .i64 = HTTP_AUTH_NONE }, 0, 0, D | E, .unit = "auth_type"}, + { "basic", "HTTP basic authentication", 0, AV_OPT_TYPE_CONST, { .i64 = HTTP_AUTH_BASIC }, 0, 0, D | E, .unit = "auth_type"}, + { "send_expect_100", "Force sending an Expect: 100-continue header for POST", OFFSET(send_expect_100), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, E }, +@@ -789,6 +791,11 @@ static int http_open(URLContext *h, const char *uri, int flags, + int ret; + s->app_ctx = (AVApplicationContext *)av_dict_strtoptr(s->app_ctx_intptr); + ++ if (s->auth_type2 == HTTP_AUTH_NONE) { ++ //backup the init auth_type, when not assign. ++ s->auth_type2 = s->auth_state.auth_type; ++ } ++ + if( s->seekable == 1 ) + h->is_streamed = 0; + else +@@ -1550,6 +1557,7 @@ static int http_connect(URLContext *h, const char *path, const char *local_path, + uint64_t off = s->off; + const char *method; + int send_expect_100 = 0; ++ int cur_auth_type = s->auth_state.auth_type; + + av_bprint_init_for_buffer(&request, s->buffer, sizeof(s->buffer)); + +@@ -1708,15 +1716,24 @@ static int http_connect(URLContext *h, const char *path, const char *local_path, + s->off = off; + + if (off != s->off) { +- av_log(h, AV_LOG_ERROR, +- "Unexpected offset: expected %"PRIu64", got %"PRIu64"\n", +- off, s->off); +- err = AVERROR(EIO); +- goto done; ++ if (cur_auth_type != s->auth_state.auth_type && s->http_code == 401) { ++ s->off = off; ++ av_log(h, AV_LOG_ERROR, ++ "HTTP 401 needs authentication: %s, offset=%"PRIu64"\n", ++ s->buffer, s->off); ++ } else { ++ av_log(h, AV_LOG_ERROR, ++ "Unexpected offset: expected %"PRIu64", got %"PRIu64"\n", ++ off, s->off); ++ err = AVERROR(EIO); ++ goto done; ++ } + } + + err = 0; + done: ++ if (err < 0) ++ av_log(h, AV_LOG_ERROR, "HTTP error %d: %s\n", s->http_code, s->buffer); + av_freep(&authstr); + av_freep(&proxyauthstr); + return err; +@@ -2148,6 +2165,8 @@ static int64_t http_seek_internal(URLContext *h, int64_t off, int whence, int fo + return s->off; + } + ++ // http_seek use lasest redirect location, because after redirect, reset the auth_state: `memset(&s->auth_state, 0, sizeof(s->auth_state));` ++ + /* if the location changed (redirect), revert to the original uri */ + if (strcmp(s->uri, s->location)) { + char *new_uri; +@@ -2156,6 +2175,9 @@ static int64_t http_seek_internal(URLContext *h, int64_t off, int whence, int fo + return AVERROR(ENOMEM); + av_free(s->location); + s->location = new_uri; ++ if (s->auth_type2 != HTTP_AUTH_NONE) { ++ s->auth_state.auth_type = s->auth_type2; ++ } + } + + /* we save the old context in case the seek fails */ +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.1/0019-add-built-in-smb2-protocol-via-libsmb2.patch b/patches/ffmpeg-n8.1.1/0019-add-built-in-smb2-protocol-via-libsmb2.patch new file mode 100644 index 000000000..69938968f --- /dev/null +++ b/patches/ffmpeg-n8.1.1/0019-add-built-in-smb2-protocol-via-libsmb2.patch @@ -0,0 +1,495 @@ +From 633c6546e554aba1f6219f467b1759b6f39430a8 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Fri, 8 May 2026 17:58:33 +0800 +Subject: add built-in smb2 protocol via libsmb2 + +--- + configure | 5 + + libavformat/Makefile | 1 + + libavformat/libsmb2.c | 412 ++++++++++++++++++++++++++++++++++++++++ + libavformat/protocols.c | 1 + + 4 files changed, 419 insertions(+) + create mode 100644 libavformat/libsmb2.c + +diff --git a/configure b/configure +index 1759694..10c688c 100755 +--- a/configure ++++ b/configure +@@ -276,6 +276,7 @@ External library support: + --enable-libshaderc enable runtime GLSL->SPIRV compilation via libshaderc [no] + --enable-libshine enable fixed-point MP3 encoding via libshine [no] + --enable-libsmbclient enable Samba protocol via libsmbclient [no] ++ --enable-libsmb2 enable Samba protocol via libsmb2 [no] + --enable-libsnappy enable Snappy compression, needed for hap encoding [no] + --enable-libsoxr enable Include libsoxr resampling [no] + --enable-libspeex enable Speex de/encoding via libspeex [no] +@@ -2083,6 +2084,7 @@ EXTERNAL_LIBRARY_LIST=" + libshaderc + libshine + libsmbclient ++ libsmb2 + libsnappy + libsoxr + libspeex +@@ -4073,6 +4075,7 @@ librtmps_protocol_deps="librtmp" + librtmpt_protocol_deps="librtmp" + librtmpte_protocol_deps="librtmp" + libsmbclient_protocol_deps="libsmbclient gplv3" ++libsmb2_protocol_deps="libsmb2" + libsrt_protocol_deps="libsrt" + libsrt_protocol_select="network" + libssh_protocol_deps="libssh" +@@ -7369,6 +7372,8 @@ enabled libshaderc && require_pkg_config spirv_library "shaderc >= 2019.1 + enabled libshine && require_pkg_config libshine shine shine/layer3.h shine_encode_buffer + enabled libsmbclient && { check_pkg_config libsmbclient smbclient libsmbclient.h smbc_init || + require libsmbclient libsmbclient.h smbc_init -lsmbclient; } ++enabled libsmb2 && { check_pkg_config libsmb2 libsmb2 smb2/smb2-errors.h SMB2_STATUS_SUCCESS || ++ require libsmb2 smb2/smb2-errors.h SMB2_STATUS_SUCCESS -lsmb2; } + enabled libsnappy && require libsnappy snappy-c.h snappy_compress -lsnappy -lstdc++ + enabled libsoxr && require libsoxr soxr.h soxr_create -lsoxr + enabled libssh && require_pkg_config libssh "libssh >= 0.6.0" libssh/sftp.h sftp_init +diff --git a/libavformat/Makefile b/libavformat/Makefile +index 158a11d..6100e33 100644 +--- a/libavformat/Makefile ++++ b/libavformat/Makefile +@@ -751,6 +751,7 @@ OBJS-$(CONFIG_LIBRTMPS_PROTOCOL) += librtmp.o + OBJS-$(CONFIG_LIBRTMPT_PROTOCOL) += librtmp.o + OBJS-$(CONFIG_LIBRTMPTE_PROTOCOL) += librtmp.o + OBJS-$(CONFIG_LIBSMBCLIENT_PROTOCOL) += libsmbclient.o ++OBJS-$(CONFIG_LIBSMB2_PROTOCOL) += libsmb2.o + OBJS-$(CONFIG_LIBSRT_PROTOCOL) += libsrt.o + OBJS-$(CONFIG_LIBSSH_PROTOCOL) += libssh.o + OBJS-$(CONFIG_LIBZMQ_PROTOCOL) += libzmq.o +diff --git a/libavformat/libsmb2.c b/libavformat/libsmb2.c +new file mode 100644 +index 0000000..408f9e1 +--- /dev/null ++++ b/libavformat/libsmb2.c +@@ -0,0 +1,412 @@ ++/* ++ * Copyright (c) 2014 Lukasz Marek ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg 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. ++ * ++ * FFmpeg 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 FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++ ++#include ++#include ++#include "libavutil/avstring.h" ++#include "libavutil/opt.h" ++#include "libavutil/mem.h" ++#include "application.h" ++#include "url.h" ++#include "urldecode.h" ++//smb2.h:37:9: error: unknown type name 'time_t'; ++#include ++#include ++#include ++#include ++ ++typedef struct ++{ ++ const AVClass *class; ++ ++ struct smb2_context *ctx; ++ struct smb2_url *url; ++ struct smb2fh *fh; ++ struct smb2dir *dir; ++ ++ uint64_t filesize; ++ char *app_ctx_intptr; ++ AVApplicationContext *app_ctx; ++ int smb2_seal; ++} LIBSMB2Context; ++ ++static void destroy_smb2(LIBSMB2Context *libsmb2) ++{ ++ if (libsmb2->fh && libsmb2->ctx) { ++ smb2_close(libsmb2->ctx, libsmb2->fh); ++ } ++ ++ if (libsmb2->ctx) { ++ smb2_disconnect_share(libsmb2->ctx); ++ smb2_destroy_context(libsmb2->ctx); ++ } ++ ++ if (libsmb2->url) { ++ smb2_destroy_url(libsmb2->url); ++ } ++ libsmb2->fh = NULL; ++ libsmb2->ctx = NULL; ++ libsmb2->url = NULL; ++} ++ ++static av_cold int libsmb2_close(URLContext *h) ++{ ++ LIBSMB2Context *libsmb2 = h->priv_data; ++ destroy_smb2(libsmb2); ++ return 0; ++} ++ ++static av_cold int libsmb2_open(URLContext *h, const char *uri, int flags) ++{ ++ int ret = 0; ++ LIBSMB2Context *libsmb2 = h->priv_data; ++ libsmb2->filesize = -1; ++ libsmb2->ctx = smb2_init_context(); ++ libsmb2->app_ctx = (AVApplicationContext *)av_dict_strtoptr(libsmb2->app_ctx_intptr); ++ ++ av_application_will_http_open(libsmb2->app_ctx, (void *)h, uri); ++ ++ if (!libsmb2->ctx) { ++ av_log(h, AV_LOG_ERROR, "smb2 create context failed: %s.\n", smb2_get_error(libsmb2->ctx)); ++ ret = AVERROR(ENOMEM); ++ goto failed; ++ } ++ ++ const char *smb_url = av_strireplace(uri, "smb2", "smb"); ++ struct smb2_url *url = smb2_parse_url(libsmb2->ctx, smb_url); ++ ++ if (url == NULL) { ++ av_log(h, AV_LOG_ERROR, "smb2 parse url failed: %s\n", smb2_get_error(libsmb2->ctx)); ++ ret = AVERROR(ENOMEM); ++ goto failed; ++ } else { ++ if (url->user) { ++ char *user = strchr(url->user, ':'); ++ if (user) { ++ *user = '\0'; ++ char *password = user + 1; ++ if (strlen(password) > 0) { ++ password = ff_urldecode(password, 0); ++ smb2_set_password(libsmb2->ctx, password); ++ } ++ } ++ } ++ ++ if (url->domain) { ++ smb2_set_domain(libsmb2->ctx, url->domain); ++ } ++ ++ if (url->share) { ++ char *share = ff_urldecode(url->share, 0); ++ memset(url->share, 0, strlen(url->share)); ++ memcpy(url->share, share, strlen(share)); ++ } ++ ++ if (url->path) { ++ char *path = ff_urldecode(url->path, 0); ++ memset(url->path, 0, strlen(url->path)); ++ memcpy(url->path, path, strlen(path)); ++ } ++ ++ libsmb2->url = url; ++ } ++ ++ //https://github.com/sahlberg/libsmb2/issues/271 ++ //fix Very slow performance w/MacOS SMB server ++ //smb2_set_security_mode(libsmb2->ctx, SMB2_NEGOTIATE_SIGNING_ENABLED); ++ smb2_set_seal(libsmb2->ctx, libsmb2->smb2_seal); ++ smb2_set_authentication(libsmb2->ctx, 1);//SMB2_SEC_NTLMSSP ++ smb2_set_timeout(libsmb2->ctx, 60); ++ ++ if (smb2_connect_share(libsmb2->ctx, url->server, url->share, url->user) != 0) { ++ av_log(h, AV_LOG_ERROR, "smb2 connect share failed: %s\n", smb2_get_error(libsmb2->ctx)); ++ ret = AVERROR(ECONNREFUSED); ++ goto failed; ++ } ++ ++ int access; ++ if ((flags & AVIO_FLAG_WRITE) && (flags & AVIO_FLAG_READ)) { ++ access = O_CREAT | O_RDWR; ++ } else if (flags & AVIO_FLAG_WRITE) { ++ access = O_CREAT | O_WRONLY; ++ } else { ++ access = O_RDONLY; ++ } ++ ++ if (flags & AVIO_FLAG_DIRECT) { ++ if ((libsmb2->dir = smb2_opendir(libsmb2->ctx, url->path)) == NULL) { ++ av_log(h, AV_LOG_ERROR, "smb2 open dir failed: %s, error: %s\n", url->path, smb2_get_error(libsmb2->ctx)); ++ ret = AVERROR(ENOTDIR); ++ goto failed; ++ } ++ } else { ++ if ((libsmb2->fh = smb2_open(libsmb2->ctx, url->path, access)) == NULL) { ++ av_log(h, AV_LOG_ERROR, "smb2 open file failed: %s, error: %s\n", url->path, smb2_get_error(libsmb2->ctx)); ++ ret = AVERROR(ENOENT); ++ goto failed; ++ } ++ } ++ ++ struct smb2_stat_64 st = {0}; ++ ++ if (smb2_stat(libsmb2->ctx, url->path, &st) < 0) ++ av_log(h, AV_LOG_WARNING, "Cannot stat file: %s\n", smb2_get_error(libsmb2->ctx)); ++ else ++ libsmb2->filesize = st.smb2_size; ++ av_application_did_http_open(libsmb2->app_ctx, (void *)h, uri, 0, 200, libsmb2->filesize); ++ return 0; ++failed: ++ av_application_did_http_open(libsmb2->app_ctx, (void *)h, uri, ret, 500, 0); ++ if (libsmb2->fh && libsmb2->ctx) { ++ smb2_close(libsmb2->ctx, libsmb2->fh); ++ } ++ if (libsmb2->ctx) { ++ smb2_disconnect_share(libsmb2->ctx); ++ smb2_destroy_context(libsmb2->ctx); ++ } ++ if (libsmb2->url) { ++ smb2_destroy_url(libsmb2->url); ++ } ++ libsmb2->fh = NULL; ++ libsmb2->ctx = NULL; ++ libsmb2->url = NULL; ++ return -1; ++} ++ ++static int64_t libsmb2_seek(URLContext *h, int64_t pos, int whence) ++{ ++ LIBSMB2Context *libsmb2 = h->priv_data; ++ int64_t newpos; ++ ++ if (whence == AVSEEK_SIZE) { ++ if (libsmb2->filesize == -1) { ++ av_log(h, AV_LOG_ERROR, "smb2 seek failed,filesize is unknown.\n"); ++ return AVERROR(EIO); ++ } else { ++ return libsmb2->filesize; ++ } ++ } ++ ++ av_application_will_http_seek(libsmb2->app_ctx, (void *)h, h->filename, pos); ++ ++ if ((newpos = smb2_lseek(libsmb2->ctx, libsmb2->fh, pos, whence, NULL)) < 0) { ++ av_log(h, AV_LOG_ERROR, "smb2 seek failed: %s\n", smb2_get_error(libsmb2->ctx)); ++ av_application_did_http_seek(libsmb2->app_ctx, (void *)h, h->filename, pos, AVERROR(errno), 500); ++ return AVERROR(errno); ++ } ++ av_application_did_http_seek(libsmb2->app_ctx, (void *)h, h->filename, pos, 0, 200); ++ return newpos; ++} ++ ++static int libsmb2_read(URLContext *h, unsigned char *buf, int size) ++{ ++ LIBSMB2Context *libsmb2 = h->priv_data; ++ ++ uint8_t *buf1 = buf; ++ int buf_size1 = size; ++ int has_error = 0; ++ ++ while (buf_size1 > 0) { ++ int read = smb2_read(libsmb2->ctx, libsmb2->fh, buf1, buf_size1); ++ if (read < 0) { ++ av_log(h, AV_LOG_ERROR, "smb2 read file failed: %s\n", ++ smb2_get_error(libsmb2->ctx)); ++ has_error = 1; ++ break; ++ } ++ if (read == 0) { ++ // eof ++ break; ++ } ++ buf1 += read; ++ buf_size1 -= read; ++ } ++ ++ int bytes_read = size - buf_size1; ++ if (bytes_read > 0) ++ av_application_did_io_tcp_read(libsmb2->app_ctx, (void*)h, bytes_read); ++ ++ return bytes_read ? bytes_read : (has_error ? AVERROR(ENOTCONN) : AVERROR_EOF); ++} ++ ++static int libsmb2_write(URLContext *h, const unsigned char *buf, int size) ++{ ++ LIBSMB2Context *libsmb2 = h->priv_data; ++ int bytes_written; ++ ++ if ((bytes_written = smb2_write(libsmb2->ctx, libsmb2->fh, buf, size)) < 0) { ++ int ret = AVERROR(errno); ++ av_log(h, AV_LOG_ERROR, "smb2 write failed: %s\n", strerror(errno)); ++ return ret; ++ } ++ ++ return bytes_written; ++} ++ ++static int libsmb2_delete(URLContext *h) ++{ ++ LIBSMB2Context *libsmb2 = h->priv_data; ++ ++ struct smb2_url *url = smb2_parse_url(libsmb2->ctx, h->filename); ++ if (url == NULL) { ++ av_log(h, AV_LOG_ERROR, "smb2 parse url failed: %s\n", ++ smb2_get_error(libsmb2->ctx)); ++ return -1; ++ } else { ++ char *path = ff_urldecode(url->path, 0); ++ return smb2_unlink(libsmb2->ctx, path); ++ } ++} ++ ++static int libsmb2_move(URLContext *h_src, URLContext *h_dst) ++{ ++ LIBSMB2Context *libsmb2 = h_src->priv_data; ++ if (!libsmb2) ++ { ++ return -1; ++ } ++ ++ struct smb2_url *src_url = smb2_parse_url(libsmb2->ctx, h_src->filename); ++ struct smb2_url *dst_url = smb2_parse_url(libsmb2->ctx, h_dst->filename); ++ ++ if (src_url == NULL || dst_url == NULL) { ++ av_log(h_src, AV_LOG_ERROR, "smb2 parse url failed: %s\n", smb2_get_error(libsmb2->ctx)); ++ return -2; ++ } else { ++ char *src_path = ff_urldecode(src_url->path, 0); ++ char *dst_path = ff_urldecode(dst_url->path, 0); ++ return smb2_rename(libsmb2->ctx, src_path, dst_path); ++ } ++} ++ ++static int libsmb2_open_dir(URLContext *h) ++{ ++ LIBSMB2Context *libsmb2 = h->priv_data; ++ struct smb2_url *url = smb2_parse_url(libsmb2->ctx, h->filename); ++ if (url == NULL) { ++ av_log(h, AV_LOG_ERROR, "smb2 parse url failed: %s\n", smb2_get_error(libsmb2->ctx)); ++ return -1; ++ } else { ++ char *path = ff_urldecode(url->path, 0); ++ libsmb2->dir = smb2_opendir(libsmb2->ctx, path); ++ if (!libsmb2->dir){ ++ av_log(h, AV_LOG_ERROR, "smb2 open dir failed: %s\n", smb2_get_error(libsmb2->ctx)); ++ return 0; ++ } ++ return AVERROR(ENOTDIR); ++ } ++} ++ ++static int libsmb2_read_dir(URLContext *h, AVIODirEntry **next) ++{ ++ LIBSMB2Context *libsmb2 = h->priv_data; ++ AVIODirEntry *entry; ++ struct smb2dirent *dirent = NULL; ++ int skip_entry; ++ ++ *next = entry = ff_alloc_dir_entry(); ++ if (!entry) ++ return AVERROR(ENOMEM); ++ do { ++ skip_entry = 0; ++ dirent = smb2_readdir(libsmb2->ctx, libsmb2->dir); ++ if (!dirent) { ++ av_freep(next); ++ return 0; ++ } ++ } while (skip_entry || !strcmp(dirent->name, ".") || ++ !strcmp(dirent->name, "..")); ++ ++ entry->name = av_strdup(dirent->name); ++ if (!entry->name) { ++ av_freep(next); ++ return AVERROR(ENOMEM); ++ } ++ ++ struct smb2_stat_64 st = dirent->st; ++ switch (st.smb2_type) { ++ case SMB2_TYPE_DIRECTORY: ++ entry->type = AVIO_ENTRY_DIRECTORY; ++ break; ++ case SMB2_TYPE_FILE: ++ entry->type = AVIO_ENTRY_FILE; ++ break; ++ case SMB2_TYPE_LINK: ++ entry->type = AVIO_ENTRY_SYMBOLIC_LINK; ++ break; ++ default: ++ entry->type = AVIO_ENTRY_UNKNOWN; ++ break; ++ } ++ ++ entry->group_id = -1; ++ entry->user_id = -1; ++ entry->filemode = -1; ++ entry->size = st.smb2_size; ++ entry->modification_timestamp = INT64_C(1000000) * st.smb2_mtime; ++ entry->access_timestamp = INT64_C(1000000) * st.smb2_atime; ++ entry->status_change_timestamp = INT64_C(1000000) * st.smb2_ctime; ++ ++ return 0; ++} ++ ++static int libsmb2_close_dir(URLContext *h) ++{ ++ LIBSMB2Context *libsmb2 = h->priv_data; ++ if (libsmb2->dir) { ++ smb2_closedir(libsmb2->ctx, libsmb2->dir); ++ libsmb2->dir = NULL; ++ } ++ return 0; ++} ++ ++#define OFFSET(x) offsetof(LIBSMB2Context, x) ++#define D AV_OPT_FLAG_DECODING_PARAM ++#define E AV_OPT_FLAG_ENCODING_PARAM ++static const AVOption options[] = { ++ { "ijkapplication", "AVApplicationContext", OFFSET(app_ctx_intptr), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, .flags = D }, ++ { "smb2_seal", "enable smb3 encrypted connection", OFFSET(smb2_seal), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, .flags = D }, ++ {NULL} ++}; ++ ++static const AVClass libsmb2lient_context_class = { ++ .class_name = "libsmb2", ++ .item_name = av_default_item_name, ++ .option = options, ++ .version = LIBAVUTIL_VERSION_INT, ++}; ++ ++const URLProtocol ff_libsmb2_protocol = { ++ .name = "smb2", ++ .flags = URL_PROTOCOL_FLAG_NETWORK, ++ .priv_data_size = sizeof(LIBSMB2Context), ++ .priv_data_class = &libsmb2lient_context_class, ++ .url_open = libsmb2_open, ++ .url_read = libsmb2_read, ++ .url_write = libsmb2_write, ++ .url_seek = libsmb2_seek, ++ .url_close = libsmb2_close, ++ .url_delete = libsmb2_delete, ++ .url_move = libsmb2_move, ++ .url_open_dir = libsmb2_open_dir, ++ .url_read_dir = libsmb2_read_dir, ++ .url_close_dir = libsmb2_close_dir, ++}; +diff --git a/libavformat/protocols.c b/libavformat/protocols.c +index d1c1095..fb712a5 100644 +--- a/libavformat/protocols.c ++++ b/libavformat/protocols.c +@@ -75,6 +75,7 @@ extern const URLProtocol ff_librtmpte_protocol; + extern const URLProtocol ff_libsrt_protocol; + extern const URLProtocol ff_libssh_protocol; + extern const URLProtocol ff_libsmbclient_protocol; ++extern const URLProtocol ff_libsmb2_protocol; + extern const URLProtocol ff_libzmq_protocol; + extern const URLProtocol ff_ipfs_gateway_protocol; + extern const URLProtocol ff_ipns_gateway_protocol; +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.1/0020-URLProtocol-add-url_parse_priv-function-pointer.patch b/patches/ffmpeg-n8.1.1/0020-URLProtocol-add-url_parse_priv-function-pointer.patch new file mode 100644 index 000000000..1639144d4 --- /dev/null +++ b/patches/ffmpeg-n8.1.1/0020-URLProtocol-add-url_parse_priv-function-pointer.patch @@ -0,0 +1,55 @@ +From 861f2778fd0f40ec42a2dd1827afe0fc11786e32 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Thu, 29 May 2025 11:14:13 +0800 +Subject: URLProtocol add url_parse_priv function pointer + +--- + libavformat/demux.c | 11 +++++++++++ + libavformat/url.h | 2 ++ + 2 files changed, 13 insertions(+) + +diff --git a/libavformat/demux.c b/libavformat/demux.c +index 494e15f..524018f 100644 +--- a/libavformat/demux.c ++++ b/libavformat/demux.c +@@ -355,6 +355,17 @@ int avformat_open_input(AVFormatContext **ps, const char *filename, + ff_id3v2_free_extra_meta(&id3v2_extra_meta); + } + ++ //fill stream info ++ if (s->pb) { ++ URLContext *url_context = ffio_geturlcontext(s->pb); ++ if (url_context && url_context->prot) { ++ URLProtocol *prot = url_context->prot; ++ if (prot->url_parse_priv) { ++ prot->url_parse_priv(s, url_context); ++ } ++ } ++ } ++ + if ((ret = avformat_queue_attached_pictures(s)) < 0) + goto close; + +diff --git a/libavformat/url.h b/libavformat/url.h +index 53c6f13..f7e5bb2 100644 +--- a/libavformat/url.h ++++ b/libavformat/url.h +@@ -48,6 +48,7 @@ typedef struct URLContext { + int min_packet_size; /**< if non zero, the stream is packetized with this min packet size */ + } URLContext; + ++typedef struct AVFormatContext AVFormatContext; + typedef struct URLProtocol { + const char *name; + int (*url_open)( URLContext *h, const char *url, int flags); +@@ -93,6 +94,7 @@ typedef struct URLProtocol { + int (*url_close_dir)(URLContext *h); + int (*url_delete)(URLContext *h); + int (*url_move)(URLContext *h_src, URLContext *h_dst); ++ int (*url_parse_priv)(AVFormatContext *ic, URLContext *h); + const char *default_whitelist; + } URLProtocol; + +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.1/0021-bluray-protocol-add-dvd-fallback.patch b/patches/ffmpeg-n8.1.1/0021-bluray-protocol-add-dvd-fallback.patch new file mode 100644 index 000000000..e95664bac --- /dev/null +++ b/patches/ffmpeg-n8.1.1/0021-bluray-protocol-add-dvd-fallback.patch @@ -0,0 +1,54 @@ +From da901ac77bf2a238a947809aa1b276a13af6e055 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Thu, 29 May 2025 11:15:16 +0800 +Subject: bluray protocol add dvd fallback + +--- + libavformat/demux.c | 23 +++++++++++++++++++---- + 1 file changed, 19 insertions(+), 4 deletions(-) + +diff --git a/libavformat/demux.c b/libavformat/demux.c +index 524018f..531992f 100644 +--- a/libavformat/demux.c ++++ b/libavformat/demux.c +@@ -161,7 +161,8 @@ static int init_input(AVFormatContext *s, const char *filename, + int ret; + AVProbeData pd = { filename, NULL, 0 }; + int score = AVPROBE_SCORE_RETRY; +- ++ AVDictionary *tmp_opts = NULL; ++ + if (s->pb) { + s->flags |= AVFMT_FLAG_CUSTOM_IO; + if (!s->iformat) +@@ -176,10 +177,24 @@ static int init_input(AVFormatContext *s, const char *filename, + if ((s->iformat && s->iformat->flags & AVFMT_NOFILE) || + (!s->iformat && (s->iformat = av_probe_input_format2(&pd, 0, &score)))) + return score; ++ ++ if (options && av_stristart(filename, "bluray://", NULL)) { ++ av_dict_copy(&tmp_opts, *options, 0); ++ } + +- if ((ret = s->io_open(s, &s->pb, filename, AVIO_FLAG_READ | s->avio_flags, options)) < 0) +- return ret; +- ++ if ((ret = s->io_open(s, &s->pb, filename, AVIO_FLAG_READ | s->avio_flags, ++ options)) < 0) { ++ if (av_stristart(filename, "bluray://", NULL)) { ++ const char *a_name = av_strireplace(filename, "bluray://", ""); ++ ret = init_input(s, a_name, &tmp_opts); ++ av_dict_free(&tmp_opts); ++ return ret; ++ } else { ++ av_dict_free(&tmp_opts); ++ return ret; ++ } ++ } ++ av_dict_free(&tmp_opts); + if (s->iformat) + return 0; + return av_probe_input_buffer2(s->pb, &s->iformat, filename, +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.1/0022-custom-bluray-fs-for-network-Blu-ray-Disc-and-BDMV.patch b/patches/ffmpeg-n8.1.1/0022-custom-bluray-fs-for-network-Blu-ray-Disc-and-BDMV.patch new file mode 100644 index 000000000..283624f95 --- /dev/null +++ b/patches/ffmpeg-n8.1.1/0022-custom-bluray-fs-for-network-Blu-ray-Disc-and-BDMV.patch @@ -0,0 +1,744 @@ +From 016c42a6ca0b61fef87a703b7e71765af8abc501 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Fri, 15 May 2026 17:00:02 +0800 +Subject: custom bluray fs for network Blu-ray Disc and BDMV + +--- + libavformat/Makefile | 2 +- + libavformat/bluray.c | 128 ++++++++- + libavformat/bluray_custom_fs.c | 459 +++++++++++++++++++++++++++++++++ + libavformat/bluray_custom_fs.h | 33 +++ + 4 files changed, 610 insertions(+), 12 deletions(-) + create mode 100644 libavformat/bluray_custom_fs.c + create mode 100644 libavformat/bluray_custom_fs.h + +diff --git a/libavformat/Makefile b/libavformat/Makefile +index 6100e33..54e88cf 100644 +--- a/libavformat/Makefile ++++ b/libavformat/Makefile +@@ -694,7 +694,7 @@ OBJS-$(CONFIG_VAPOURSYNTH_DEMUXER) += vapoursynth.o + # protocols I/O + OBJS-$(CONFIG_ANDROID_CONTENT_PROTOCOL) += file.o + OBJS-$(CONFIG_ASYNC_PROTOCOL) += async.o +-OBJS-$(CONFIG_BLURAY_PROTOCOL) += bluray.o ++OBJS-$(CONFIG_BLURAY_PROTOCOL) += bluray.o bluray_custom_fs.o + OBJS-$(CONFIG_CACHE_PROTOCOL) += cache.o + OBJS-$(CONFIG_CONCAT_PROTOCOL) += concat.o + OBJS-$(CONFIG_CONCATF_PROTOCOL) += concat.o +diff --git a/libavformat/bluray.c b/libavformat/bluray.c +index 1845551..eb43340 100644 +--- a/libavformat/bluray.c ++++ b/libavformat/bluray.c +@@ -21,23 +21,27 @@ + */ + + #include +- ++#include "libavformat/urldecode.h" + #include "libavutil/avstring.h" + #include "libavformat/url.h" + #include "libavutil/opt.h" ++#include "bluray_custom_fs.h" ++#include "libavutil/dict.h" ++#include "libavformat/avformat.h" + +-#define BLURAY_PROTO_PREFIX "bluray:" ++#define BLURAY_PROTO_PREFIX "bluray://" + #define MIN_PLAYLIST_LENGTH 180 /* 3 min */ + + typedef struct { + const AVClass *class; + + BLURAY *bd; +- ++ fs_access *access; + int playlist; + int angle; + int chapter; + /*int region;*/ ++ int title_idx; + } BlurayContext; + + #define OFFSET(x) offsetof(BlurayContext, x) +@@ -106,23 +110,58 @@ static int bluray_close(URLContext *h) + if (bd->bd) { + bd_close(bd->bd); + } +- ++ destroy_bluray_custom_access(&bd->access); + return 0; + } + +-static int bluray_open(URLContext *h, const char *path, int flags) ++#ifdef DEBUG_BLURAY ++#include ++#define BLURAY_DEBUG_MASK 0xFFFFF //(0xFFFFF & ~DBG_STREAM) ++ ++static void bluray_DebugHandler(const char *psz) + { ++ size_t len = strlen(psz); ++ if(len < 1) return; ++ av_log(NULL, AV_LOG_INFO, "[bluray] %s\n",psz); ++} ++#endif ++ ++ ++static int bluray_open(URLContext *h, const char *path, int flags, AVDictionary **options) ++{ ++#ifdef DEBUG_BLURAY ++ bd_set_debug_mask(BLURAY_DEBUG_MASK); ++ bd_set_debug_handler(bluray_DebugHandler); ++#endif ++ + BlurayContext *bd = h->priv_data; + int num_title_idx; + const char *diskname = path; + + av_strstart(path, BLURAY_PROTO_PREFIX, &diskname); + +- bd->bd = bd_open(diskname, NULL); +- if (!bd->bd) { ++ diskname = ff_urldecode(diskname, 0); ++ ++ fs_access *access = NULL; ++ int is_file = av_strstart(diskname, "file://", NULL) || av_strstart(diskname, "/", NULL); ++ ++ // file protocl can't handle AVIO_FLAG_DIRECT flag,so file not create custom access ++ if (!is_file) { ++ //set read packet buffer size is important! the default packet size is 32768, when use smb2 protocol, download speed is limited to 2MB; but when set the size to 1048576, download speed is 16MB; ++ h->max_packet_size = 1048576; ++ access = create_bluray_custom_access(diskname, options, &h->interrupt_callback); ++ } ++ ++ bd->bd = bd_open_fs(diskname, NULL, access); ++ ++ if (!bd->bd || is_bluray_custom_access_cancelled(access)) { + av_log(h, AV_LOG_ERROR, "bd_open() failed\n"); ++ if (access) { ++ destroy_bluray_custom_access(&access); ++ } + return AVERROR(EIO); + } ++ bd->access = access; + + /* check if disc can be played */ + if (check_disc_info(h) < 0) { +@@ -150,6 +189,9 @@ static int bluray_open(URLContext *h, const char *path, int flags) + int i; + for (i = 0; i < num_title_idx; i++) { + BLURAY_TITLE_INFO *info = bd_get_title_info(bd->bd, i, 0); ++ if (!info) { ++ continue; ++ } + + av_log(h, AV_LOG_INFO, "playlist %05d.mpls (%d:%02d:%02d)\n", + info->playlist, +@@ -159,17 +201,19 @@ static int bluray_open(URLContext *h, const char *path, int flags) + + if (info->duration > duration) { + bd->playlist = info->playlist; ++ bd->title_idx = i; + duration = info->duration; + } + + bd_free_title_info(info); + } +- av_log(h, AV_LOG_INFO, "selected %05d.mpls\n", bd->playlist); ++ av_log(h, AV_LOG_INFO, "select longest playlist: %05d.mpls\n", bd->playlist); + } + + /* select playlist */ +- if (bd_select_playlist(bd->bd, bd->playlist) <= 0) { +- av_log(h, AV_LOG_ERROR, "bd_select_playlist(%05d.mpls) failed\n", bd->playlist); ++ if (bd->playlist >= 0 && bd_select_playlist(bd->bd, bd->playlist) <= 0) { ++ av_log(h, AV_LOG_ERROR, "bd_select_playlist(%05d.mpls) failed\n", ++ bd->playlist); + return AVERROR(EIO); + } + +@@ -222,13 +266,75 @@ static int64_t bluray_seek(URLContext *h, int64_t pos, int whence) + return AVERROR(EINVAL); + } + ++static int bluray_parse_priv(AVFormatContext *ic, URLContext *h) ++{ ++ BlurayContext *bd = h->priv_data; ++ BLURAY_TITLE_INFO *title_info = NULL; ++ BLURAY_CLIP_INFO clip_info; ++ ++ int v_idx = 0; ++ int a_idx = 0; ++ int s_idx = 0; ++ int ret = 0; ++ ++ if (!bd || !bd->bd) { ++ return AVERROR(EFAULT); ++ } ++ ++ title_info = bd_get_title_info(bd->bd, bd->title_idx, 0); ++ if (!title_info) { ++ return AVERROR(EFAULT); ++ } ++ ++ if (title_info->clip_count <= 0) { ++ ret = EFAULT; ++ goto fail; ++ } ++ clip_info = title_info->clips[0]; ++ ++ for (int i = 0; i < ic->nb_streams; i++) { ++ if (ic->streams[i] && ic->streams[i]->codecpar) { ++ switch (ic->streams[i]->codecpar->codec_type) { ++ case AVMEDIA_TYPE_VIDEO: ++ if (v_idx < clip_info.video_stream_count) { ++ av_log(h, AV_LOG_INFO, "video stream %d lang = %s\n", v_idx, clip_info.video_streams[v_idx].lang); ++ av_dict_set(&ic->streams[i]->metadata, "language", clip_info.video_streams[v_idx].lang, AV_DICT_DONT_OVERWRITE); ++ v_idx++; ++ } ++ break; ++ case AVMEDIA_TYPE_AUDIO: ++ if (a_idx < clip_info.audio_stream_count) { ++ av_log(h, AV_LOG_INFO, "audio stream %d lang = %s\n", a_idx, clip_info.audio_streams[a_idx].lang); ++ av_dict_set(&ic->streams[i]->metadata, "language", clip_info.audio_streams[a_idx].lang, AV_DICT_DONT_OVERWRITE); ++ a_idx++; ++ } ++ break; ++ case AVMEDIA_TYPE_SUBTITLE: ++ if (s_idx < clip_info.pg_stream_count) { ++ av_log(h, AV_LOG_INFO, "subtitle stream %d lang = %s\n", s_idx, clip_info.pg_streams[s_idx].lang); ++ av_dict_set(&ic->streams[i]->metadata, "language", clip_info.pg_streams[s_idx].lang, AV_DICT_DONT_OVERWRITE); ++ s_idx++; ++ } ++ break; ++ default: ++ break; ++ } ++ } ++ } ++ ++fail: ++ bd_free_title_info(title_info); ++ ++ return ret != 0 ? AVERROR(ret) : 0; ++} + + const URLProtocol ff_bluray_protocol = { + .name = "bluray", + .url_close = bluray_close, +- .url_open = bluray_open, ++ .url_open2 = bluray_open, + .url_read = bluray_read, + .url_seek = bluray_seek, ++ .url_parse_priv = bluray_parse_priv, + .priv_data_size = sizeof(BlurayContext), + .priv_data_class = &bluray_context_class, + }; +diff --git a/libavformat/bluray_custom_fs.c b/libavformat/bluray_custom_fs.c +new file mode 100644 +index 0000000..b82e0b2 +--- /dev/null ++++ b/libavformat/bluray_custom_fs.c +@@ -0,0 +1,459 @@ ++// ++// bluray_custom_fs_smb2.c ++// ++// Created by Reach Matt on 2024/9/13. ++// ++// ++// Copyright (C) 2021 Matt Reach// ++// Licensed under the Apache License, Version 2.0 (the "License"); ++// you may not use this file except in compliance with the License. ++// You may obtain a copy of the License at ++// ++// http://www.apache.org/licenses/LICENSE-2.0 ++// ++// Unless required by applicable law or agreed to in writing, software ++// distributed under the License is distributed on an "AS IS" BASIS, ++// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++// See the License for the specific language governing permissions and ++// limitations under the License. ++ ++#include "bluray_custom_fs.h" ++#include "url.h" ++#include "application.h" ++#include "libavutil/mem.h" ++#include "libavutil/error.h" ++#include "libavutil/avstring.h" ++#include ++#include ++ ++#ifndef UDF_BLOCK_SIZE ++# define UDF_BLOCK_SIZE 2048 ++#endif ++ ++typedef struct ff_builtin_io { ++ URLContext *url_context; ++ int64_t offset; ++ AVApplicationContext *app_ctx; ++ int64_t last_read; ++} ff_builtin_io; ++ ++typedef struct ff_bluray_access { ++ const char *url; ++ AVDictionary *opts; ++ ff_builtin_io *io; ++ const AVIOInterruptCB *int_cb; ++ int cancel; ++} ff_bluray_access; ++ ++static void close_builtin_io(ff_builtin_io *io) ++{ ++ if (!io) { ++ return; ++ } ++ if (io->url_context) { ++ ffurl_closep(&io->url_context); ++ } ++} ++ ++static int create_builtin_io(ff_builtin_io **io, const char *url, ++ AVDictionary **opts, int is_dir, ++ const AVIOInterruptCB *int_cb) ++{ ++ if (!io) { ++ return -1; ++ } ++ ++ ff_builtin_io *app = av_mallocz(sizeof(ff_builtin_io)); ++ if (!app) { ++ return -2; ++ } ++ ++ const char *protocol_whitelist = NULL; ++ ++ if (opts) { ++ const AVDictionary *dict = *opts; ++ ++ if (!av_strstart(url, "http", NULL) && ++ !av_strstart(url, "smb2", NULL)) { ++ AVDictionaryEntry *app_dict = ++ av_dict_get(dict, "ijkapplication", NULL, 0); ++ if (app_dict) { ++ app->app_ctx = ++ (AVApplicationContext *)av_dict_strtoptr(app_dict->value); ++ av_application_will_http_open(app->app_ctx, NULL, url); ++ } ++ } ++ ++ AVDictionaryEntry *proto_dict = ++ av_dict_get(dict, "protocol_whitelist", NULL, 0); ++ if (proto_dict) { ++ protocol_whitelist = av_strdup(proto_dict->value); ++ } ++ } ++ ++ if (protocol_whitelist == NULL || strlen(protocol_whitelist) == 0) { ++ protocol_whitelist = "ijkio,ijkhttphook,http,tcp,https,tls,file,smb2"; ++ } ++ ++ int flags = AVIO_FLAG_READ; ++ if (is_dir) { ++ flags |= AVIO_FLAG_DIRECT; ++ } ++ ++ int ret = ffurl_open_whitelist(&app->url_context, url, flags, int_cb, opts, ++ protocol_whitelist, NULL, NULL); ++ ++ av_application_did_http_open(app->app_ctx, (void *)app->url_context, url, ++ ret < 0 ? AVERROR(errno) : 0, ++ ret < 0 ? 500 : 200, 0); ++ if (ret < 0) { ++ close_builtin_io(app); ++ av_freep(&app); ++ } ++ *io = app; ++ return ret; ++} ++ ++static int64_t seek_builtin_io(ff_builtin_io *io, int64_t offset, int origin) ++{ ++ if (!io) { ++ return 0; ++ } ++ ++ if (io->offset == offset && origin == SEEK_SET) { ++ io->last_read = offset; ++ return offset; ++ } ++ ++ av_application_will_http_seek(io->app_ctx, (void *)io->url_context, ++ io->url_context->filename, offset); ++ ++ int64_t pos = ++ io->url_context->prot->url_seek(io->url_context, offset, origin); ++ if (pos < 0) { ++ av_application_did_http_seek(io->app_ctx, (void *)io->url_context, ++ io->url_context->filename, offset, ++ AVERROR(errno), 500); ++ return AVERROR(errno); ++ } ++ io->last_read = pos; ++ io->offset = pos; ++ av_application_did_http_seek(io->app_ctx, (void *)io->url_context, ++ io->url_context->filename, offset, 0, 200); ++ return pos; ++} ++ ++static int write_builtin_io(ff_builtin_io *io, uint8_t *buf, int buf_size) ++{ ++ if (!io) { ++ return 0; ++ } ++ ++ return io->url_context->prot->url_write(io->url_context, buf, buf_size); ++} ++ ++static int read_builtin_io(ff_builtin_io *io, uint8_t *buf, int buf_size, ++ int64_t pos) ++{ ++ if (!io) { ++ return 0; ++ } ++ ++ uint8_t *buf1 = buf; ++ int buf_size1 = buf_size; ++ int read = 0; ++ ++ seek_builtin_io(io, pos, SEEK_SET); ++ ++ while (buf_size1 > 0) { ++ read = ++ io->url_context->prot->url_read(io->url_context, buf1, buf_size1); ++ if (read <= 0) { ++ // maybe AVERROR_EOF ++ break; ++ } ++ io->offset += read; ++ buf1 += read; ++ buf_size1 -= read; ++ } ++ if (read == AVERROR_EXIT) { ++ return AVERROR_EXIT; ++ } ++ int bytes = buf_size - buf_size1; ++ if (bytes == 0 && read == AVERROR_EOF) { ++ return AVERROR_EOF; ++ } else { ++ av_application_did_io_tcp_read(io->app_ctx, (void *)io->url_context, ++ bytes); ++ return bytes; ++ } ++} ++ ++static int read_blocks(void *fs_handle, void *buf, int lba, int num_blocks) ++{ ++ ff_bluray_access *access = fs_handle; ++ if (access->cancel == 1) { ++ return AVERROR_EXIT; ++ } ++ ++ ff_builtin_io *io = access->io; ++ if (!io) { ++ return -1; ++ } ++ int got = -1; ++ int64_t pos = (int64_t)lba * UDF_BLOCK_SIZE; ++ int buf_size = num_blocks * UDF_BLOCK_SIZE; ++ int bytes = read_builtin_io(io, buf, buf_size, pos); ++ if (bytes == AVERROR_EXIT) { ++ access->cancel = 1; ++ return AVERROR_EXIT; ++ } ++ ++ if (bytes > 0) { ++ got = (int)(bytes / UDF_BLOCK_SIZE); ++ } ++ return got; ++} ++ ++void destroy_bluray_custom_access(fs_access **p) ++{ ++ if (p) { ++ fs_access *access = *p; ++ if (access) { ++ ff_bluray_access* ba = access->fs_handle; ++ ff_builtin_io *io = ba->io; ++ if (io) { ++ close_builtin_io(io); ++ av_freep(&io); ++ } ++ av_free(ba->url); ++ av_dict_free(&ba->opts); ++ } ++ av_freep(p); ++ } ++} ++ ++// ------------------------------------------------------------------------------------------- ++// open_file for bdmv ++ ++static void _file_close(BD_FILE_H *file) ++{ ++ if (file) { ++ ff_builtin_io *io = file->internal; ++ if (io) { ++ close_builtin_io(io); ++ av_free(io); ++ file->internal = NULL; ++ } ++ av_freep(&file); ++ } ++} ++ ++static int64_t _file_read(BD_FILE_H *file, uint8_t *buf, int64_t size) ++{ ++ if (size <= 0) { ++ return 0; ++ } ++ ff_builtin_io *io = file->internal; ++ if (!io) { ++ return -1; ++ } ++ ++ int read = read_builtin_io(io, buf, size, io->last_read); ++ ++ if (read > 0) { ++ io->last_read += read; ++ } ++ ++ return read; ++} ++ ++static int64_t _file_write(BD_FILE_H *file, uint8_t *buf, int64_t size) ++{ ++ if (size <= 0) { ++ return 0; ++ } ++ ff_builtin_io *io = file->internal; ++ if (!io) { ++ return -1; ++ } ++ return write_builtin_io(io, buf, size); ++} ++ ++// origin: SEEK_SET, SEEK_CUR or SEEK_END ++static int64_t _file_seek(BD_FILE_H *file, int64_t offset, int32_t origin) ++{ ++ ff_builtin_io *io = file->internal; ++ if (!io) { ++ return -1; ++ } ++ return seek_builtin_io(io, offset, origin); ++} ++ ++static int64_t _file_tell(BD_FILE_H *file) ++{ ++ ff_builtin_io *io = file->internal; ++ if (!io) { ++ return -1; ++ } ++ return io->last_read; ++ // return seek_builtin_io(io, 0, SEEK_CUR); ++} ++ ++static struct bd_file_s *open_file(void *fs_handle, const char *rel_path) ++{ ++ ff_bluray_access *access = fs_handle; ++ ++ if (access->cancel == 1) { ++ return NULL; ++ } ++ ++ const char *url = av_append_path_component(access->url, rel_path); ++ if (!url) { ++ return NULL; ++ } ++ AVDictionary *opts = NULL; ++ av_dict_copy(&opts, access->opts, 0); ++ ++ ff_builtin_io *io = NULL; ++ int ret = create_builtin_io(&io, url, &opts, 0, access->int_cb); ++ av_dict_free(&opts); ++ ++ if (0 != ret) { ++ av_log(NULL, AV_LOG_ERROR, "[bluray] can't open %s,error:%s", url, ++ av_err2str(ret)); ++ av_free(url); ++ return NULL; ++ } ++ av_free(url); ++ BD_FILE_H *file = av_malloc(sizeof(BD_FILE_H)); ++ if (!file) { ++ close_builtin_io(io); ++ av_free(io); ++ return NULL; ++ } ++ ++ file->internal = io; ++ file->close = _file_close; ++ file->seek = _file_seek; ++ file->read = _file_read; ++ file->write = _file_write; ++ file->tell = _file_tell; ++ ++ return file; ++} ++ ++// open_dir for bdmv ++static void _dir_close(BD_DIR_H *dir) ++{ ++ if (dir) { ++ ff_builtin_io *io = dir->internal; ++ if (!io) { ++ return; ++ } ++ close_builtin_io(io); ++ av_free(io); ++ dir->internal = NULL; ++ av_freep(&dir); ++ } ++} ++ ++static int _dir_read(BD_DIR_H *dir, BD_DIRENT *entry) ++{ ++ ff_builtin_io *io = dir->internal; ++ if (!io) { ++ return -1; ++ } ++ ++ AVIODirEntry *next = NULL; ++ ++ if (io->url_context->prot->url_read_dir(io->url_context, &next) < 0 || !next) { ++ return -2; ++ } ++ ++ strncpy(entry->d_name, next->name, sizeof(entry->d_name)); ++ entry->d_name[sizeof(entry->d_name) - 1] = 0; ++ ++ return 0; ++} ++ ++static struct bd_dir_s* open_dir (void *fs_handle, const char *rel_path) ++{ ++ ff_bluray_access *access = fs_handle; ++ ++ if (access->cancel == 1) { ++ return NULL; ++ } ++ ++ const char *url = av_append_path_component(access->url, rel_path); ++ if (!url) { ++ return NULL; ++ } ++ AVDictionary *opts = NULL; ++ av_dict_copy(&opts, access->opts, 0); ++ ++ ff_builtin_io *io = NULL; ++ int ret = create_builtin_io(&io, url, &opts, 1, access->int_cb); ++ av_dict_free(&opts); ++ ++ if (0 != ret) { ++ av_log(NULL, AV_LOG_ERROR, "[bluray] can't open dir %s,error:%s", url, ++ av_err2str(ret)); ++ av_free(url); ++ return NULL; ++ } ++ av_free(url); ++ BD_DIR_H *dir = av_malloc(sizeof(BD_DIR_H)); ++ if (!dir) { ++ close_builtin_io(io); ++ av_free(io); ++ return NULL; ++ } ++ ++ dir->internal = io; ++ dir->close = _dir_close; ++ dir->read = _dir_read; ++ ++ return dir; ++} ++ ++int is_bluray_custom_access_cancelled(fs_access *access) ++{ ++ if (!access) { ++ return 0; ++ } ++ ++ ff_bluray_access *opaque = access->fs_handle; ++ return opaque->cancel; ++} ++ ++// 构建fs_access结构体 ++fs_access * create_bluray_custom_access(const char *url, AVDictionary **options, const AVIOInterruptCB *int_cb) ++{ ++ ff_bluray_access * opaque = av_mallocz(sizeof(ff_bluray_access)); ++ if (!opaque) { ++ return NULL; ++ } ++ ++ if (opaque) { ++ opaque->url = av_strdup(url); ++ if (options) { ++ av_dict_copy(&opaque->opts, *options, 0); ++ } ++ opaque->int_cb = int_cb; ++ int ret = create_builtin_io(&opaque->io, url, options, 0, int_cb); ++ if (0 != ret) { ++ av_log(NULL, AV_LOG_ERROR, "[bluray] can't open file %s,error:%s", ++ url, av_err2str(ret)); ++ } ++ ++ fs_access *access = av_malloc(sizeof(fs_access)); ++ access->fs_handle = opaque; ++ access->read_blocks = read_blocks; ++ access->open_file = open_file; ++ access->open_dir = open_dir; ++ ++ return access; ++ } ++ return NULL; ++} +\ No newline at end of file +diff --git a/libavformat/bluray_custom_fs.h b/libavformat/bluray_custom_fs.h +new file mode 100644 +index 0000000..058d2da +--- /dev/null ++++ b/libavformat/bluray_custom_fs.h +@@ -0,0 +1,33 @@ ++// ++// bluray_custom_fs.h ++// ++// Created by Reach Matt on 2024/9/13. ++// ++// ++// Copyright (C) 2021 Matt Reach// ++// Licensed under the Apache License, Version 2.0 (the "License"); ++// you may not use this file except in compliance with the License. ++// You may obtain a copy of the License at ++// ++// http://www.apache.org/licenses/LICENSE-2.0 ++// ++// Unless required by applicable law or agreed to in writing, software ++// distributed under the License is distributed on an "AS IS" BASIS, ++// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++// See the License for the specific language governing permissions and ++// limitations under the License. ++ ++#ifndef bluray_custom_fs_h ++#define bluray_custom_fs_h ++ ++#include ++ ++typedef struct fs_access fs_access; ++typedef struct AVDictionary AVDictionary; ++typedef struct AVIOInterruptCB AVIOInterruptCB; ++ ++void destroy_bluray_custom_access(fs_access **p); ++int is_bluray_custom_access_cancelled(fs_access *access); ++fs_access *create_bluray_custom_access(const char *url, AVDictionary **options, ++ const AVIOInterruptCB *int_cb); ++#endif /* bluray_custom_fs_h */ +\ No newline at end of file +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.1/0023-bluray-open-and-find-the-right-m2ts-then-read-seek-i.patch b/patches/ffmpeg-n8.1.1/0023-bluray-open-and-find-the-right-m2ts-then-read-seek-i.patch new file mode 100644 index 000000000..28afb7341 --- /dev/null +++ b/patches/ffmpeg-n8.1.1/0023-bluray-open-and-find-the-right-m2ts-then-read-seek-i.patch @@ -0,0 +1,71 @@ +From afbd088c5f65683f1edb1383224eeddbc4bf718b Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Tue, 12 May 2026 11:45:56 +0800 +Subject: bluray open and find the right m2ts, then read/seek it + direactly instread of bluray logic. + +--- + libavformat/bluray.c | 25 ++++++++++++++++++++----- + 1 file changed, 20 insertions(+), 5 deletions(-) + +diff --git a/libavformat/bluray.c b/libavformat/bluray.c +index eb43340..254538f 100644 +--- a/libavformat/bluray.c ++++ b/libavformat/bluray.c +@@ -42,6 +42,7 @@ typedef struct { + int chapter; + /*int region;*/ + int title_idx; ++ int stream_opened; + } BlurayContext; + + #define OFFSET(x) offsetof(BlurayContext, x) +@@ -226,7 +227,8 @@ static int bluray_open(URLContext *h, const char *path, int flags, AVDictionary + if (bd->chapter > 1) { + bd_seek_chapter(bd->bd, bd->chapter - 1); + } +- ++ /* will read ts soon */ ++ bd->stream_opened = 1; + return 0; + } + +@@ -238,7 +240,13 @@ static int bluray_read(URLContext *h, unsigned char *buf, int size) + if (!bd || !bd->bd) { + return AVERROR(EFAULT); + } +- ++ if (bd->stream_opened) { ++ int read = (int)bd_file_read(bd->bd, buf, size); ++ if (read == 0) { ++ return AVERROR_EOF; ++ } ++ return read; ++ } + len = bd_read(bd->bd, buf, size); + + return len == 0 ? AVERROR_EOF : len; +@@ -256,10 +264,17 @@ static int64_t bluray_seek(URLContext *h, int64_t pos, int whence) + case SEEK_SET: + case SEEK_CUR: + case SEEK_END: +- return bd_seek(bd->bd, pos); +- ++ if (bd->stream_opened) { ++ return bd_file_seek(bd->bd, pos, whence); ++ } else { ++ return bd_seek(bd->bd, pos); ++ } + case AVSEEK_SIZE: +- return bd_get_title_size(bd->bd); ++ if (bd->stream_opened) { ++ return bd_file_size(bd->bd); ++ } else { ++ return bd_get_title_size(bd->bd); ++ } + } + + av_log(h, AV_LOG_ERROR, "Unsupported whence operation %d\n", whence); +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.1/0024-fix-android-ffmpeg7-test.c-1-10-fatal-error-libxml2-.patch b/patches/ffmpeg-n8.1.1/0024-fix-android-ffmpeg7-test.c-1-10-fatal-error-libxml2-.patch new file mode 100644 index 000000000..6095da59e --- /dev/null +++ b/patches/ffmpeg-n8.1.1/0024-fix-android-ffmpeg7-test.c-1-10-fatal-error-libxml2-.patch @@ -0,0 +1,26 @@ +From 6e2fc211aad20c73775334f040fbab8543236672 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Fri, 6 Jun 2025 14:09:50 +0800 +Subject: fix android ffmpeg7 test.c:1:10: fatal error: + 'libxml2/libxml/xmlversion.h' + +--- + configure | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/configure b/configure +index 10c688c..9761c84 100755 +--- a/configure ++++ b/configure +@@ -7448,7 +7448,7 @@ enabled libzmq && require_pkg_config libzmq "libzmq >= 4.2.1" zmq.h z + enabled libzvbi && require_pkg_config libzvbi zvbi-0.2 libzvbi.h vbi_decoder_new && + { test_cpp_condition libzvbi.h "VBI_VERSION_MAJOR > 0 || VBI_VERSION_MINOR > 2 || VBI_VERSION_MINOR == 2 && VBI_VERSION_MICRO >= 28" || + enabled gpl || die "ERROR: libzvbi requires version 0.2.28 or --enable-gpl."; } +-enabled libxml2 && require_pkg_config libxml2 libxml-2.0 libxml2/libxml/xmlversion.h xmlCheckVersion ++enabled libxml2 && require_pkg_config libxml2 libxml-2.0 libxml/xmlversion.h xmlCheckVersion + enabled mbedtls && { check_pkg_config mbedtls mbedtls mbedtls/x509_crt.h mbedtls_x509_crt_init || + check_pkg_config mbedtls mbedtls mbedtls/ssl.h mbedtls_ssl_init || + check_lib mbedtls mbedtls/ssl.h mbedtls_ssl_init -lmbedtls -lmbedx509 -lmbedcrypto || +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.1/0025-fix-dash-init-fragment-url-is-invalid-truncated-bug.patch b/patches/ffmpeg-n8.1.1/0025-fix-dash-init-fragment-url-is-invalid-truncated-bug.patch new file mode 100644 index 000000000..c57a757ed --- /dev/null +++ b/patches/ffmpeg-n8.1.1/0025-fix-dash-init-fragment-url-is-invalid-truncated-bug.patch @@ -0,0 +1,43 @@ +From 83bd2fe0fa738f659c59d28cc484501d7260913a Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Wed, 21 May 2025 18:05:52 +0800 +Subject: fix dash init fragment url is "invalid:truncated" bug + +--- + libavformat/dashdec.c | 12 +++++++++++- + 1 file changed, 11 insertions(+), 1 deletion(-) + +diff --git a/libavformat/dashdec.c b/libavformat/dashdec.c +index 9a2ea78..5927a6e 100644 +--- a/libavformat/dashdec.c ++++ b/libavformat/dashdec.c +@@ -479,6 +479,10 @@ static char *get_content_url(xmlNodePtr *baseurl_nodes, + int i; + char *text; + char *url = NULL; ++ ++ if (strlen(val) >= max_url_size) { ++ max_url_size += 256; ++ } + char *tmp_str = av_mallocz(max_url_size); + + if (!tmp_str) +@@ -497,8 +501,14 @@ static char *get_content_url(xmlNodePtr *baseurl_nodes, + } + } + +- if (val) ++ if (val) { ++ int tmp_max_url_size = strlen(tmp_str) + strlen(val) + 1; ++ if (tmp_max_url_size > max_url_size) { ++ max_url_size = tmp_max_url_size; ++ tmp_str = av_realloc(tmp_str, max_url_size); ++ } + ff_make_absolute_url(tmp_str, max_url_size, tmp_str, val); ++ } + + if (rep_id_val) { + url = av_strireplace(tmp_str, "$RepresentationID$", rep_id_val); +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.1/0026-add-webp-demuxer-and-libwebp-decoder.patch b/patches/ffmpeg-n8.1.1/0026-add-webp-demuxer-and-libwebp-decoder.patch new file mode 100644 index 000000000..2e6de76aa --- /dev/null +++ b/patches/ffmpeg-n8.1.1/0026-add-webp-demuxer-and-libwebp-decoder.patch @@ -0,0 +1,804 @@ +From 2ca732722b9799c24fd1589ab960f18b1c8108fd Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Fri, 8 May 2026 19:02:39 +0800 +Subject: add webp demuxer and libwebp decoder + +--- + configure | 7 +- + libavcodec/Makefile | 1 + + libavcodec/allcodecs.c | 1 + + libavcodec/codec_desc.c | 9 + + libavcodec/codec_id.h | 1 + + libavcodec/libwebpdec.c | 376 +++++++++++++++++++++++++++++++++++++++ + libavformat/Makefile | 1 + + libavformat/allformats.c | 1 + + libavformat/webpdec.c | 296 ++++++++++++++++++++++++++++++ + 9 files changed, 692 insertions(+), 1 deletion(-) + create mode 100644 libavcodec/libwebpdec.c + create mode 100644 libavformat/webpdec.c + +diff --git a/configure b/configure +index 9761c84..0ecc99c 100755 +--- a/configure ++++ b/configure +@@ -7427,7 +7427,12 @@ enabled libvpx && { + enabled libvvenc && require_pkg_config libvvenc "libvvenc >= 1.6.1" "vvenc/vvenc.h" vvenc_get_version + enabled libwebp && { + enabled libwebp_encoder && require_pkg_config libwebp "libwebp >= 0.2.0" webp/encode.h WebPGetEncoderVersion +- enabled libwebp_anim_encoder && check_pkg_config libwebp_anim_encoder "libwebpmux >= 0.4.0" webp/mux.h WebPAnimEncoderOptionsInit; } ++ enabled libwebp_anim_encoder && check_pkg_config libwebp_anim_encoder "libwebpmux >= 0.4.0" webp/mux.h WebPAnimEncoderOptionsInit ++ check_pkg_config libwebpdemux "libwebpdemux >= 1.0.0" webp/demux.h WebPDemux ++ if enabled libwebpdemux; then ++ enable webp_demuxer ++ enable libwebp_decoder ++ fi } + enabled libx264 && require_pkg_config libx264 x264 "stdint.h x264.h" x264_encoder_encode && + require_cpp_condition libx264 x264.h "X264_BUILD >= 155" && { + [ "$toolchain" != "msvc" ] || +diff --git a/libavcodec/Makefile b/libavcodec/Makefile +index c51a860..4ae36ed 100644 +--- a/libavcodec/Makefile ++++ b/libavcodec/Makefile +@@ -846,6 +846,7 @@ OBJS-$(CONFIG_WBMP_DECODER) += wbmpdec.o + OBJS-$(CONFIG_WBMP_ENCODER) += wbmpenc.o + OBJS-$(CONFIG_WCMV_DECODER) += wcmv.o + OBJS-$(CONFIG_WEBP_DECODER) += webp.o ++OBJS-$(CONFIG_LIBWEBP_DECODER) += libwebpdec.o + OBJS-$(CONFIG_WEBVTT_DECODER) += webvttdec.o ass.o + OBJS-$(CONFIG_WEBVTT_ENCODER) += webvttenc.o ass_split.o + OBJS-$(CONFIG_WMALOSSLESS_DECODER) += wmalosslessdec.o wma_common.o +diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c +index 695214f..ffc2494 100644 +--- a/libavcodec/allcodecs.c ++++ b/libavcodec/allcodecs.c +@@ -428,6 +428,7 @@ extern const FFCodec ff_zlib_encoder; + extern const FFCodec ff_zlib_decoder; + extern const FFCodec ff_zmbv_encoder; + extern const FFCodec ff_zmbv_decoder; ++extern const FFCodec ff_libwebp_decoder; + + /* audio codecs */ + extern const FFCodec ff_aac_encoder; +diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c +index 93877ed..46e5f8b 100644 +--- a/libavcodec/codec_desc.c ++++ b/libavcodec/codec_desc.c +@@ -3870,6 +3870,15 @@ static const AVCodecDescriptor codec_descriptors[] = { + .long_name = NULL_IF_CONFIG_SMALL("Audio Vivid"), + .props = AV_CODEC_PROP_LOSSY, + }, ++ { ++ .id = AV_CODEC_ID_LIBWEBP, ++ .type = AVMEDIA_TYPE_VIDEO, ++ .name = "libwebp", ++ .long_name = NULL_IF_CONFIG_SMALL("libWebP"), ++ .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY | ++ AV_CODEC_PROP_LOSSLESS, ++ .mime_types= MT("image/webp"), ++ }, + { + .id = AV_CODEC_ID_VNULL, + .type = AVMEDIA_TYPE_VIDEO, +diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h +index 43f7549..31bb7a6 100644 +--- a/libavcodec/codec_id.h ++++ b/libavcodec/codec_id.h +@@ -629,6 +629,7 @@ enum AVCodecID { + * Null encoder/decoder discard all input and never return any output. + */ + AV_CODEC_ID_AVS3DA, ++ AV_CODEC_ID_LIBWEBP, + AV_CODEC_ID_VNULL, + /** + * Dummy null audio codec, useful mainly for development and debugging. +diff --git a/libavcodec/libwebpdec.c b/libavcodec/libwebpdec.c +new file mode 100644 +index 0000000..17b69d0 +--- /dev/null ++++ b/libavcodec/libwebpdec.c +@@ -0,0 +1,376 @@ ++/* ++ * libwebpdec.c ++ * ++ * Copyright (c) 2025 debugly ++ * ++ * This file is part of FSPlayer. ++ * ++ * FSPlayer 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 3 of the License, or (at your option) any later version. ++ * ++ * FSPlayer 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 FSPlayer; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include "codec_internal.h" ++#include "decode.h" ++#include "internal.h" ++#include "libavutil/common.h" ++#include "libavutil/imgutils.h" ++#include "libavutil/mem.h" ++#include "libavutil/opt.h" ++#include "libavutil/pixdesc.h" ++#include ++#include ++ ++typedef struct MyImageRef { ++ int width; ++ int height; ++ uint8_t *data; ++} MyImageRef; ++ ++typedef struct MyRect { ++ int x; ++ int y; ++ int w; ++ int h; ++} MyRect; ++ ++typedef struct LibWebPDecoderContext { ++ AVClass *class; ++ MyImageRef *canvas; // The main compositing canvas ++} LibWebPDecoderContext; ++ ++static void release_image(MyImageRef *img) ++{ ++ if (img) { ++ if (img->data) ++ av_free(img->data); ++ free(img); ++ } ++} ++ ++static int alloc_image(MyImageRef **img, int width, int height) ++{ ++ if (!img || width <= 0 || height <= 0) ++ return AVERROR(EINVAL); ++ ++ *img = malloc(sizeof(MyImageRef)); ++ if (!*img) ++ return AVERROR(ENOMEM); ++ ++ (*img)->width = width; ++ (*img)->height = height; ++ (*img)->data = av_malloc(width * height * 4); // RGBA ++ if (!(*img)->data) { ++ free(*img); ++ *img = NULL; ++ return AVERROR(ENOMEM); ++ } ++ ++ memset((*img)->data, 0, width * height * 4); // 初始化为透明 ++ return 0; ++} ++ ++static void clear_rect(MyImageRef *img, MyRect rect) ++{ ++ if (!img || !img->data) ++ return; ++ ++ int img_width = img->width; ++ int img_height = img->height; ++ uint8_t *data = img->data; ++ ++ // 确保矩形在图像范围内 ++ int start_x = FFMAX(0, rect.x); ++ int start_y = FFMAX(0, rect.y); ++ int end_x = FFMIN(img_width, rect.x + rect.w); ++ int end_y = FFMIN(img_height, rect.y + rect.h); ++ ++ for (int y = start_y; y < end_y; y++) { ++ uint8_t *row = data + y * img_width * 4; ++ memset(row + start_x * 4, 0, (end_x - start_x) * 4); ++ } ++} ++ ++static void blend_pixel(uint8_t *dst, const uint8_t *src) ++{ ++ uint8_t sa = src[3]; ++ if (sa == 0) { ++ return; // 源像素完全透明,直接返回 ++ } else if (sa == 255) { ++ // 源像素完全不透明,直接覆盖 ++ memcpy(dst, src, 4); ++ return; ++ } ++ ++ uint8_t da = dst[3]; ++ uint8_t out_a = sa + ((da * (255 - sa)) / 255); ++ ++ if (out_a == 0) { ++ memset(dst, 0, 4); ++ return; ++ } ++ ++ dst[0] = (src[0] * sa + dst[0] * da * (255 - sa) / 255) / out_a; ++ dst[1] = (src[1] * sa + dst[1] * da * (255 - sa) / 255) / out_a; ++ dst[2] = (src[2] * sa + dst[2] * da * (255 - sa) / 255) / out_a; ++ dst[3] = out_a; ++} ++ ++static void draw_image(MyImageRef *canvas, MyImageRef *image, MyRect rect) ++{ ++ if (!canvas || !canvas->data || !image || !image->data) ++ return; ++ ++ int canvas_width = canvas->width; ++ int canvas_height = canvas->height; ++ uint8_t *canvas_data = canvas->data; ++ ++ int image_width = image->width; ++ int image_height = image->height; ++ uint8_t *image_data = image->data; ++ ++ // 计算绘制区域 ++ int start_x = FFMAX(0, rect.x); ++ int start_y = FFMAX(0, rect.y); ++ int end_x = FFMIN(canvas_width, rect.x + rect.w); ++ int end_y = FFMIN(canvas_height, rect.y + rect.h); ++ ++ for (int y = start_y; y < end_y; y++) { ++ uint8_t *canvas_row = canvas_data + y * canvas_width * 4; ++ uint8_t *image_row = image_data + (y - rect.y) * image_width * 4; ++ for (int x = start_x; x < end_x; x++) { ++ uint8_t *canvas_pixel = canvas_row + x * 4; ++ uint8_t *image_pixel = image_row + (x - rect.x) * 4; ++ ++ blend_pixel(canvas_pixel, image_pixel); ++ } ++ } ++} ++ ++static av_cold int libwebp_decode_init(AVCodecContext *avctx) ++{ ++ LibWebPDecoderContext *s = avctx->priv_data; ++ int canvas_size; ++ ++ if (avctx->width <= 0 || avctx->height <= 0) { ++ av_log(avctx, AV_LOG_ERROR, ++ "Invalid canvas dimensions from avctx: %dx%d\n", avctx->width, ++ avctx->height); ++ return AVERROR_INVALIDDATA; ++ } ++ ++ if (alloc_image(&s->canvas, avctx->width, avctx->height)) { ++ av_log(avctx, AV_LOG_ERROR, "Failed to allocate canvas buffer\n"); ++ return AVERROR(ENOMEM); ++ } ++ ++ avctx->pix_fmt = AV_PIX_FMT_RGBA; ++ ++ return 0; ++} ++ ++static int get_origin_from_pkt_side_data(AVPacket *pkt, int *offsetx, ++ int *offsety, int *blend, int *dispose) ++{ ++ if (!pkt || !offsetx || !offsety) { ++ return AVERROR(EINVAL); ++ } ++ ++ // 初始化输出参数 ++ *offsetx = 0; ++ *offsety = 0; ++ *blend = 0; ++ *dispose = 0; ++ ++ // 获取元数据 ++ size_t metadata_len = 0; ++ uint8_t *metadata_data = av_packet_get_side_data( ++ pkt, AV_PKT_DATA_STRINGS_METADATA, &metadata_len); ++ ++ if (!metadata_data || metadata_len == 0) { ++ return -1; // ++ } ++ ++ // 解包元数据到AVDictionary ++ AVDictionary *dict = NULL; ++ int ret = av_packet_unpack_dictionary(metadata_data, metadata_len, &dict); ++ if (ret < 0) { ++ av_log(NULL, AV_LOG_ERROR, "Failed to unpack metadata: %d\n", ret); ++ return ret; ++ } ++ ++ // 解析offsetx和offsety和blend ++ AVDictionaryEntry *entry = NULL; ++ entry = av_dict_get(dict, "offsetx", NULL, 0); ++ if (entry && entry->value) { ++ *offsetx = atoi(entry->value); // 转换字符串为整数 ++ } ++ ++ entry = av_dict_get(dict, "offsety", NULL, 0); ++ if (entry && entry->value) { ++ *offsety = atoi(entry->value); ++ } ++ ++ entry = av_dict_get(dict, "blend", NULL, 0); ++ if (entry && entry->value) { ++ *blend = atoi(entry->value); ++ } ++ ++ entry = av_dict_get(dict, "dispose", NULL, 0); ++ if (entry && entry->value) { ++ *dispose = atoi(entry->value); ++ } ++ ++ // 释放字典 ++ av_dict_free(&dict); ++ return 0; ++} ++ ++static int libwebp_do_decode_frame(AVPacket *pkt, MyImageRef **imgp) ++{ ++ WebPDecoderConfig config; ++ // 1. 解码当前帧并获取其属性 ++ if (!WebPInitDecoderConfig(&config)) ++ return AVERROR_UNKNOWN; ++ ++ if (WebPGetFeatures(pkt->data, pkt->size, &config.input) != VP8_STATUS_OK) ++ return AVERROR_INVALIDDATA; ++ ++ int hasAlpha = config.input.has_alpha; ++ int input_width = config.input.width; ++ int input_height = config.input.height; ++ if (input_width <= 0 || input_height <= 0) ++ return AVERROR_INVALIDDATA; ++ ++ config.output.colorspace = MODE_RGBA; ++ config.output.is_external_memory = 1; ++ ++ MyImageRef *img; ++ ++ if (alloc_image(&img, input_width, input_height)) { ++ return AVERROR(ENOMEM); ++ } ++ ++ int rgba_stride = img->width * 4; ++ uint8_t *rgba = img->data; ++ config.output.u.RGBA.rgba = rgba; ++ config.output.u.RGBA.stride = rgba_stride; ++ config.output.u.RGBA.size = input_height * rgba_stride; ++ ++ if (WebPDecode(pkt->data, pkt->size, &config) != VP8_STATUS_OK) { ++ release_image(img); ++ return AVERROR_INVALIDDATA; ++ } ++ *imgp = img; ++ return 0; ++} ++ ++static int libwebp_decode_frame(AVCodecContext *avctx, AVFrame *frame, ++ int *got_frame, const AVPacket *pkt) ++{ ++ LibWebPDecoderContext *s = avctx->priv_data; ++ ++ int offsetx, offsety, blend, dispose; ++ if (get_origin_from_pkt_side_data(pkt, &offsetx, &offsety, &blend, ++ &dispose) < 0) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ // 使用 libwebp 解码当前帧 ++ MyImageRef *img = NULL; ++ int ret = libwebp_do_decode_frame((AVPacket *)pkt, &img); ++ if (ret < 0) { ++ av_log(avctx, AV_LOG_ERROR, "Failed to decode WebP frame: %d\n", ret); ++ return ret; ++ } ++ ++ // 将当前帧渲染到已准备好的画布上 ++ MyRect rect = {offsetx, offsety, img->width, img->height}; ++ draw_image(s->canvas, img, rect); ++ release_image(img); ++ ++ frame->width = avctx->width; ++ frame->height = avctx->height; ++ frame->format = avctx->pix_fmt; ++ ++ // 为 frame 申请内存 ++ if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) { ++ return ret; ++ } ++ ++ // 将合成好的画布复制到输出帧 ++ int canvas_linesize = s->canvas->width * 4; ++ av_image_copy_plane(frame->data[0], frame->linesize[0], s->canvas->data, ++ s->canvas->width * 4, s->canvas->width * 4, ++ s->canvas->height); ++ ++ // av_image_copy(frame->data, frame->linesize, ++ // (const uint8_t **)&s->canvas->data, &canvas_linesize, ++ // avctx->pix_fmt, output_width, output_height); ++ ++ // 执行 dispose 操作 ++ if (dispose == WEBP_MUX_DISPOSE_BACKGROUND) { ++ clear_rect(s->canvas, rect); ++ } ++ ++ *got_frame = 1; ++ ret = pkt->size; ++ ++ return ret; ++} ++ ++static int libwebp_decode_flush(AVCodecContext *avctx) ++{ ++ LibWebPDecoderContext *s = avctx->priv_data; ++ if (s->canvas && s->canvas->data) { ++ memset(s->canvas->data, 0, s->canvas->width * s->canvas->height * 4); ++ } ++ return 0; ++} ++ ++//when seek or seek to zero for loop,clear then canvas ++static av_cold int libwebp_decode_close(AVCodecContext *avctx) ++{ ++ LibWebPDecoderContext *s = avctx->priv_data; ++ release_image(s->canvas); ++ s->canvas = NULL; ++ return 0; ++} ++ ++#define OFFSET(x) offsetof(LibWebPDecoderContext, x) ++#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM ++ ++static const AVOption options[] = {{NULL}}; ++ ++static const AVClass libwebp_decoder_class = { ++ .class_name = "libwebp decoder", ++ .item_name = av_default_item_name, ++ .option = options, ++ .version = LIBAVUTIL_VERSION_INT, ++}; ++ ++const FFCodec ff_libwebp_decoder = { ++ .p.name = "libwebp", ++ .p.long_name = NULL_IF_CONFIG_SMALL("libwebp WebP image decoder"), ++ .p.type = AVMEDIA_TYPE_VIDEO, ++ .p.id = AV_CODEC_ID_LIBWEBP, ++ .priv_data_size = sizeof(LibWebPDecoderContext), ++ .init = libwebp_decode_init, ++ .cb.decode = libwebp_decode_frame, ++ .flush = libwebp_decode_flush, ++ .close = libwebp_decode_close, ++ .p.capabilities = AV_CODEC_CAP_DR1, ++ .p.priv_class = &libwebp_decoder_class, ++ .p.wrapper_name = "libwebp", ++ .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE, ++}; +diff --git a/libavformat/Makefile b/libavformat/Makefile +index 54e88cf..3081d27 100644 +--- a/libavformat/Makefile ++++ b/libavformat/Makefile +@@ -659,6 +659,7 @@ OBJS-$(CONFIG_WEBM_MUXER) += matroskaenc.o matroska.o \ + OBJS-$(CONFIG_WEBM_DASH_MANIFEST_MUXER) += webmdashenc.o + OBJS-$(CONFIG_WEBM_CHUNK_MUXER) += webm_chunk.o + OBJS-$(CONFIG_WEBP_MUXER) += webpenc.o ++OBJS-$(CONFIG_WEBP_DEMUXER) += webpdec.o + OBJS-$(CONFIG_WEBVTT_DEMUXER) += webvttdec.o subtitles.o + OBJS-$(CONFIG_WEBVTT_MUXER) += webvttenc.o + OBJS-$(CONFIG_WHIP_MUXER) += whip.o avc.o http.o srtp.o +diff --git a/libavformat/allformats.c b/libavformat/allformats.c +index 1a05db9..f69c550 100644 +--- a/libavformat/allformats.c ++++ b/libavformat/allformats.c +@@ -571,6 +571,7 @@ extern const FFInputFormat ff_image_svg_pipe_demuxer; + extern const FFInputFormat ff_image_sunrast_pipe_demuxer; + extern const FFInputFormat ff_image_tiff_pipe_demuxer; + extern const FFInputFormat ff_image_vbn_pipe_demuxer; ++extern const FFInputFormat ff_webp_demuxer; + extern const FFInputFormat ff_image_webp_pipe_demuxer; + extern const FFInputFormat ff_image_xbm_pipe_demuxer; + extern const FFInputFormat ff_image_xpm_pipe_demuxer; +diff --git a/libavformat/webpdec.c b/libavformat/webpdec.c +new file mode 100644 +index 0000000..078ac99 +--- /dev/null ++++ b/libavformat/webpdec.c +@@ -0,0 +1,296 @@ ++/* ++ * webpdec.c ++ * ++ * Copyright (c) 2025 debugly ++ * ++ * This file is part of FSPlayer. ++ * ++ * FSPlayer 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 3 of the License, or (at your option) any later version. ++ * ++ * FSPlayer 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 FSPlayer; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include "internal.h" ++#include "demux.h" ++#include "libavutil/intreadwrite.h" ++#include "libavutil/mem.h" ++#include "libavutil/common.h" ++#include ++ ++typedef struct WebPDemuxContext { ++ const AVClass *class; ++ WebPDemuxer *demuxer; ++ WebPIterator iter; // 帧迭代器 ++ int current_frame; // 当前帧索引 ++ int frame_count; // 总帧数 ++ int64_t duration; // 总时长(ms) ++ uint8_t *data; // 文件数据缓冲区 ++ size_t data_size; // 数据大小 ++ int64_t *frame_timestamps; // 帧时间戳数组(ms) ++ int *frame_durations; // 帧时长数组(ms) ++} WebPDemuxContext; ++ ++// 释放迭代器资源 ++static void webp_release_iterator(WebPIterator *iter) { ++ if (iter->fragment.bytes) { ++ WebPDemuxReleaseIterator(iter); ++ memset(iter, 0, sizeof(*iter)); ++ } ++} ++ ++// 探测WebP格式 ++static int webp_read_probe(const AVProbeData *p) { ++ // WebP文件格式以"RIFF"开头,后面跟着4字节大小和"WEBP"标识 ++ if (p->buf_size < 12) ++ return 0; ++ ++ // 检查RIFF标识和WEBP格式标识 ++ if (AV_RL32(p->buf) == MKTAG('R', 'I', 'F', 'F') && ++ AV_RL32(p->buf + 8) == MKTAG('W', 'E', 'B', 'P')) { ++ // 确定是WebP文件,返回较高的探测分数 ++ return AVPROBE_SCORE_MAX; ++ } ++ ++ return 0; ++} ++ ++static int webp_read_header(AVFormatContext *s) { ++ WebPDemuxContext *wp = s->priv_data; ++ AVIOContext *pb = s->pb; ++ AVStream *st; ++ int ret, i; ++ ++ // 读取文件到内存 ++ wp->data_size = avio_size(pb); ++ if (wp->data_size <= 0) { ++ av_log(s, AV_LOG_ERROR, "Invalid file size\n"); ++ return AVERROR_INVALIDDATA; ++ } ++ ++ wp->data = av_malloc(wp->data_size); ++ if (!wp->data) ++ return AVERROR(ENOMEM); ++ ++ if (avio_read(pb, wp->data, wp->data_size) != wp->data_size) { ++ av_log(s, AV_LOG_ERROR, "Failed to read file\n"); ++ ret = AVERROR(EIO); ++ goto fail; ++ } ++ ++ // 初始化WebP解复用器 ++ WebPData webp_data; ++ WebPDataInit(&webp_data); ++ webp_data.bytes = wp->data; ++ webp_data.size = wp->data_size; ++ ++ wp->demuxer = WebPDemux(&webp_data); ++ if (!wp->demuxer) { ++ av_log(s, AV_LOG_ERROR, "Failed to create WebP demuxer\n"); ++ ret = AVERROR_INVALIDDATA; ++ goto fail; ++ } ++ ++ wp->frame_count = WebPDemuxGetI(wp->demuxer, WEBP_FF_FRAME_COUNT); ++ // 获取画布尺寸 ++ int canvas_width = WebPDemuxGetI(wp->demuxer, WEBP_FF_CANVAS_WIDTH); ++ int canvas_height = WebPDemuxGetI(wp->demuxer, WEBP_FF_CANVAS_HEIGHT); ++ ++ // int loop_count = WebPDemuxGetI(wp->demuxer, WEBP_FF_LOOP_COUNT); ++ // uint32_t flags = WebPDemuxGetI(wp->demuxer, WEBP_FF_FORMAT_FLAGS); ++ ++ // int has_animation = flags & ANIMATION_FLAG; ++ // int has_alpha = flags & ALPHA_FLAG; ++ ++ // 分配帧时间戳和时长数组 ++ wp->frame_timestamps = av_malloc_array(wp->frame_count, sizeof(int64_t)); ++ wp->frame_durations = av_malloc_array(wp->frame_count, sizeof(int)); ++ if (!wp->frame_timestamps || !wp->frame_durations) { ++ ret = AVERROR(ENOMEM); ++ goto fail; ++ } ++ ++ memset(&wp->iter, 0, sizeof(wp->iter)); ++ // libwebp's index start with 1 ++ if (!WebPDemuxGetFrame(wp->demuxer, 1, &wp->iter)) { ++ av_log(s, AV_LOG_ERROR, "Failed to get first frame\n"); ++ ret = AVERROR_INVALIDDATA; ++ goto fail; ++ } ++ ++ wp->duration = 0; ++ for (i = 0; i < wp->frame_count; i++) { ++ int duration = wp->iter.duration; ++ if (duration <= 10) { ++ // WebP standard says 0 duration is used for canvas updating but not showing image, but actually Chrome and other implementations set it to 100ms if duration is lower or equal than 10ms ++ // Some animated WebP images also created without duration, we should keep compatibility ++ duration = 100; ++ } ++ wp->frame_durations[i] = duration; ++ wp->frame_timestamps[i] = wp->duration; ++ wp->duration += wp->frame_durations[i]; ++ if (i < wp->frame_count - 1) ++ WebPDemuxNextFrame(&wp->iter); ++ } ++ ++ // 重置迭代器到第一帧 ++ webp_release_iterator(&wp->iter); ++ if (!WebPDemuxGetFrame(wp->demuxer, 1, &wp->iter)) { ++ av_log(s, AV_LOG_ERROR, "Failed to reset to first frame\n"); ++ ret = AVERROR_INVALIDDATA; ++ goto fail; ++ } ++ ++ // 创建视频流 ++ st = avformat_new_stream(s, NULL); ++ if (!st) { ++ ret = AVERROR(ENOMEM); ++ goto fail; ++ } ++ ++ // 设置流参数 ++ st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; ++ // 使用自定义解码器 ++ st->codecpar->codec_id = AV_CODEC_ID_LIBWEBP; ++ st->codecpar->width = canvas_width; ++ st->codecpar->height = canvas_height; ++ st->duration = wp->duration; ++ avpriv_set_pts_info(st, 64, 1, 1000); // 时基:1ms ++ ++ wp->current_frame = 0; ++ return 0; ++ ++fail: ++ webp_release_iterator(&wp->iter); ++ WebPDemuxDelete(wp->demuxer); ++ av_free(wp->data); ++ av_free(wp->frame_timestamps); ++ av_free(wp->frame_durations); ++ return ret; ++} ++ ++/** ++ * Add this frame's source path and basename to packet's sidedata ++ * as a dictionary, so it can be used by filters like 'drawtext'. ++ */ ++static int add_origin_as_pkt_side_data(int offsetx, int offsety, int blend, int dispose, AVPacket *pkt) { ++ AVDictionary *d = NULL; ++ char *packed_metadata = NULL; ++ size_t metadata_len; ++ int ret; ++ ++ av_dict_set_int(&d, "offsetx", offsetx, 0); ++ av_dict_set_int(&d, "offsety", offsety, 0); ++ av_dict_set_int(&d, "blend", blend, 0); ++ av_dict_set_int(&d, "dispose", dispose, 0); ++ ++ packed_metadata = av_packet_pack_dictionary(d, &metadata_len); ++ av_dict_free(&d); ++ if (!packed_metadata) ++ return AVERROR(ENOMEM); ++ ret = av_packet_add_side_data(pkt, AV_PKT_DATA_STRINGS_METADATA, ++ packed_metadata, metadata_len); ++ if (ret < 0) { ++ av_freep(&packed_metadata); ++ return ret; ++ } ++ return 0; ++} ++ ++static int webp_read_packet(AVFormatContext *s, AVPacket *pkt) { ++ WebPDemuxContext *wp = s->priv_data; ++ int ret; ++ ++ if (wp->current_frame >= wp->frame_count) ++ return AVERROR_EOF; ++ ++ // 分配并填充数据包 ++ if ((ret = av_new_packet(pkt, wp->iter.fragment.size)) < 0) ++ return ret; ++ memcpy(pkt->data, wp->iter.fragment.bytes, wp->iter.fragment.size); ++ ++ add_origin_as_pkt_side_data(wp->iter.x_offset, wp->iter.y_offset, wp->iter.blend_method == WEBP_MUX_BLEND, wp->iter.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND, pkt); ++ // 设置数据包参数 ++ pkt->stream_index = 0; ++ pkt->pts = wp->frame_timestamps[wp->current_frame]; ++ pkt->duration = wp->frame_durations[wp->current_frame]; ++ pkt->flags |= AV_PKT_FLAG_KEY; ++ ++ // 准备下一帧 ++ wp->current_frame++; ++ if (wp->current_frame < wp->frame_count && !WebPDemuxNextFrame(&wp->iter)) ++ av_log(s, AV_LOG_WARNING, "Unexpected end of frames\n"); ++ ++ return 0; ++} ++ ++static int webp_read_seek(AVFormatContext *s, int stream_index, ++ int64_t timestamp, int flags) { ++ WebPDemuxContext *wp = s->priv_data; ++ int target_frame; ++ ++ if (stream_index != 0) ++ return -1; ++ ++ // 计算目标帧索引 ++ target_frame = av_rescale_rnd(timestamp, wp->frame_count, ++ wp->duration, flags & AVSEEK_FLAG_BACKWARD ? AV_ROUND_DOWN : AV_ROUND_UP); ++ ++ // 手动实现范围限制(替代av_clamp) ++ if (target_frame < 0) { ++ target_frame = 0; ++ } else if (target_frame >= wp->frame_count) { ++ target_frame = wp->frame_count - 1; ++ } ++ ++ // 定位到目标帧 ++ webp_release_iterator(&wp->iter); ++ if (!WebPDemuxGetFrame(wp->demuxer, target_frame + 1, &wp->iter)) { ++ av_log(s, AV_LOG_ERROR, "Failed to seek to frame %d\n", target_frame); ++ return AVERROR_INVALIDDATA; ++ } ++ ++ wp->current_frame = target_frame; ++ return 0; ++} ++ ++static int webp_read_close(AVFormatContext *s) { ++ WebPDemuxContext *wp = s->priv_data; ++ ++ webp_release_iterator(&wp->iter); ++ WebPDemuxDelete(wp->demuxer); ++ av_free(wp->data); ++ av_free(wp->frame_timestamps); ++ av_free(wp->frame_durations); ++ return 0; ++} ++ ++static const AVClass webp_demuxer_class = { ++ .class_name = "WebP demuxer", ++ .item_name = av_default_item_name, ++ .version = LIBAVUTIL_VERSION_INT, ++}; ++ ++// FFmpeg 7.x输入解复用器标准声明 ++const FFInputFormat ff_webp_demuxer = { ++ .p.name = "webp", ++ .p.long_name = NULL_IF_CONFIG_SMALL("WebP image format (libwebp 1.5.0+)"), ++ .p.flags = AVFMT_GENERIC_INDEX, ++ .p.extensions = "webp", ++ .read_probe = webp_read_probe, ++ .read_header = webp_read_header, ++ .read_packet = webp_read_packet, ++ .read_seek = webp_read_seek, ++ .read_close = webp_read_close, ++ .priv_data_size = sizeof(WebPDemuxContext), ++ .p.priv_class = &webp_demuxer_class, ++}; +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.2/0001-restore-ijk-h264_ps-null-pointer-fault-tolerant.patch b/patches/ffmpeg-n8.1.2/0001-restore-ijk-h264_ps-null-pointer-fault-tolerant.patch new file mode 100644 index 000000000..d7deded1d --- /dev/null +++ b/patches/ffmpeg-n8.1.2/0001-restore-ijk-h264_ps-null-pointer-fault-tolerant.patch @@ -0,0 +1,52 @@ +From 21a946850b1be93508b3d2d4478e60d347d9993f Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Wed, 28 May 2025 17:28:22 +0800 +Subject: restore ijk h264_ps null pointer fault tolerant + +--- + libavcodec/h264_ps.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/libavcodec/h264_ps.c b/libavcodec/h264_ps.c +index ac20417..2c35e0d 100644 +--- a/libavcodec/h264_ps.c ++++ b/libavcodec/h264_ps.c +@@ -437,7 +437,7 @@ int ff_h264_decode_seq_parameter_set(GetBitContext *gb, AVCodecContext *avctx, + } + + sps->ref_frame_count = get_ue_golomb_31(gb); +- if (avctx->codec_tag == MKTAG('S', 'M', 'V', '2')) ++ if (avctx && avctx->codec_tag == MKTAG('S', 'M', 'V', '2')) + sps->ref_frame_count = FFMAX(2, sps->ref_frame_count); + if (sps->ref_frame_count > H264_MAX_DPB_FRAMES) { + av_log(avctx, AV_LOG_ERROR, +@@ -480,7 +480,7 @@ int ff_h264_decode_seq_parameter_set(GetBitContext *gb, AVCodecContext *avctx, + int width = 16 * sps->mb_width; + int height = 16 * sps->mb_height; + +- if (avctx->flags2 & AV_CODEC_FLAG2_IGNORE_CROP) { ++ if (avctx && avctx->flags2 & AV_CODEC_FLAG2_IGNORE_CROP) { + av_log(avctx, AV_LOG_DEBUG, "discarding sps cropping, original " + "values are l:%d r:%d t:%d b:%d\n", + crop_left, crop_right, crop_top, crop_bottom); +@@ -552,7 +552,7 @@ int ff_h264_decode_seq_parameter_set(GetBitContext *gb, AVCodecContext *avctx, + if (!sps->vui.sar.den) + sps->vui.sar.den = 1; + +- if (avctx->debug & FF_DEBUG_PICT_INFO) { ++ if (avctx && avctx->debug & FF_DEBUG_PICT_INFO) { + static const char csp[4][5] = { "Gray", "420", "422", "444" }; + av_log(avctx, AV_LOG_DEBUG, + "sps:%u profile:%d/%d poc:%d ref:%d %dx%d %s %s crop:%u/%u/%u/%u %s %s %"PRId32"/%"PRId32" b%d reo:%d\n", +@@ -819,7 +819,7 @@ int ff_h264_decode_picture_parameter_set(GetBitContext *gb, AVCodecContext *avct + if (pps->chroma_qp_index_offset[0] != pps->chroma_qp_index_offset[1]) + pps->chroma_qp_diff = 1; + +- if (avctx->debug & FF_DEBUG_PICT_INFO) { ++ if (avctx && avctx->debug & FF_DEBUG_PICT_INFO) { + av_log(avctx, AV_LOG_DEBUG, + "pps:%u sps:%u %s slice_groups:%d ref:%u/%u %s qp:%d/%d/%d/%d %s %s %s %s\n", + pps_id, pps->sps_id, +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.2/0002-restore-ijk-avformat-add-application-and-dns_cache.patch b/patches/ffmpeg-n8.1.2/0002-restore-ijk-avformat-add-application-and-dns_cache.patch new file mode 100644 index 000000000..9e2745720 --- /dev/null +++ b/patches/ffmpeg-n8.1.2/0002-restore-ijk-avformat-add-application-and-dns_cache.patch @@ -0,0 +1,671 @@ +From c4bfe93521942a600c8ba3e477b74aca1d8c745d Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Fri, 8 May 2026 15:58:55 +0800 +Subject: restore ijk avformat add application and dns_cache + +--- + libavformat/Makefile | 5 + + libavformat/application.c | 213 ++++++++++++++++++++++++++++++++++ + libavformat/application.h | 120 ++++++++++++++++++++ + libavformat/dns_cache.c | 232 ++++++++++++++++++++++++++++++++++++++ + libavformat/dns_cache.h | 39 +++++++ + 5 files changed, 609 insertions(+) + create mode 100644 libavformat/application.c + create mode 100644 libavformat/application.h + create mode 100644 libavformat/dns_cache.c + create mode 100644 libavformat/dns_cache.h + +diff --git a/libavformat/Makefile b/libavformat/Makefile +index 4786a93..3c95fc4 100644 +--- a/libavformat/Makefile ++++ b/libavformat/Makefile +@@ -5,6 +5,9 @@ HEADERS = avformat.h \ + avio.h \ + version.h \ + version_major.h \ ++ application.h \ ++ dns_cache.h \ ++ + + OBJS = allformats.o \ + av1.o \ +@@ -35,6 +38,8 @@ OBJS = allformats.o \ + utils.o \ + version.o \ + vpcc.o \ ++ application.o \ ++ dns_cache.o \ + + OBJS-$(HAVE_LIBC_MSVCRT) += file_open.o + +diff --git a/libavformat/application.c b/libavformat/application.c +new file mode 100644 +index 0000000..de093b9 +--- /dev/null ++++ b/libavformat/application.c +@@ -0,0 +1,213 @@ ++/* ++ * copyright (c) 2016 Zhang Rui ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg 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. ++ * ++ * FFmpeg 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 FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include "application.h" ++#include "libavformat/network.h" ++#include "libavutil/avstring.h" ++#include "libavutil/mem.h" ++ ++static int av_application_alloc(AVApplicationContext **ph, void *opaque) ++{ ++ AVApplicationContext *h = NULL; ++ ++ h = av_mallocz(sizeof(AVApplicationContext)); ++ if (!h) ++ return AVERROR(ENOMEM); ++ ++ h->opaque = opaque; ++ ++ *ph = h; ++ return 0; ++} ++ ++int av_application_open(AVApplicationContext **ph, void *opaque) ++{ ++ int ret = av_application_alloc(ph, opaque); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static void av_application_close(AVApplicationContext *h) ++{ ++ av_free(h); ++} ++ ++void av_application_closep(AVApplicationContext **ph) ++{ ++ if (!ph || !*ph) ++ return; ++ ++ av_application_close(*ph); ++ *ph = NULL; ++} ++ ++void av_application_on_http_event(AVApplicationContext *h, int event_type, AVAppHttpEvent *event) ++{ ++ if (h && h->func_on_app_event) ++ h->func_on_app_event(h, event_type, (void *)event, sizeof(AVAppHttpEvent)); ++} ++ ++void av_application_will_http_open(AVApplicationContext *h, void *obj, const char *url) ++{ ++ AVAppHttpEvent event = {0}; ++ ++ if (!h || !obj || !url) ++ return; ++ ++ event.obj = obj; ++ av_strlcpy(event.url, url, sizeof(event.url)); ++ ++ av_application_on_http_event(h, AVAPP_EVENT_WILL_HTTP_OPEN, &event); ++} ++ ++void av_application_did_http_open(AVApplicationContext *h, void *obj, const char *url, int error, int http_code, int64_t filesize) ++{ ++ AVAppHttpEvent event = {0}; ++ ++ if (!h || !obj || !url) ++ return; ++ ++ event.obj = obj; ++ av_strlcpy(event.url, url, sizeof(event.url)); ++ event.error = error; ++ event.http_code = http_code; ++ event.filesize = filesize; ++ ++ av_application_on_http_event(h, AVAPP_EVENT_DID_HTTP_OPEN, &event); ++} ++ ++void av_application_will_http_seek(AVApplicationContext *h, void *obj, const char *url, int64_t offset) ++{ ++ AVAppHttpEvent event = {0}; ++ ++ if (!h || !obj || !url) ++ return; ++ ++ event.obj = obj; ++ event.offset = offset; ++ av_strlcpy(event.url, url, sizeof(event.url)); ++ ++ av_application_on_http_event(h, AVAPP_EVENT_WILL_HTTP_SEEK, &event); ++} ++ ++void av_application_did_http_seek(AVApplicationContext *h, void *obj, const char *url, int64_t offset, int error, int http_code) ++{ ++ AVAppHttpEvent event = {0}; ++ ++ if (!h || !obj || !url) ++ return; ++ ++ event.obj = obj; ++ event.offset = offset; ++ av_strlcpy(event.url, url, sizeof(event.url)); ++ event.error = error; ++ event.http_code = http_code; ++ ++ av_application_on_http_event(h, AVAPP_EVENT_DID_HTTP_SEEK, &event); ++} ++ ++static void av_application_on_io_traffic(AVApplicationContext *h, AVAppIOTraffic *event) ++{ ++ if (h && h->func_on_app_event) ++ h->func_on_app_event(h, AVAPP_EVENT_IO_TRAFFIC, (void *)event, sizeof(AVAppIOTraffic)); ++} ++ ++int av_application_on_io_control(AVApplicationContext *h, int event_type, AVAppIOControl *control) ++{ ++ if (h && h->func_on_app_event) ++ return h->func_on_app_event(h, event_type, (void *)control, sizeof(AVAppIOControl)); ++ return 0; ++} ++ ++int av_application_on_tcp_will_open(AVApplicationContext *h) ++{ ++ if (h && h->func_on_app_event) { ++ AVAppTcpIOControl control = {0}; ++ return h->func_on_app_event(h, AVAPP_CTRL_WILL_TCP_OPEN, (void *)&control, sizeof(AVAppTcpIOControl)); ++ } ++ return 0; ++} ++ ++// only callback returns error ++int av_application_on_tcp_did_open(AVApplicationContext *h, int error, int fd, AVAppTcpIOControl *control) ++{ ++ struct sockaddr_storage so_stg; ++ int ret = 0; ++ socklen_t so_len = sizeof(so_stg); ++ int so_family; ++ char *so_ip_name = control->ip; ++ ++ if (!h || !h->func_on_app_event || fd <= 0) ++ return 0; ++ ++ ret = getpeername(fd, (struct sockaddr *)&so_stg, &so_len); ++ if (ret) ++ return 0; ++ control->error = error; ++ control->fd = fd; ++ ++ so_family = ((struct sockaddr*)&so_stg)->sa_family; ++ switch (so_family) { ++ case AF_INET: { ++ struct sockaddr_in* in4 = (struct sockaddr_in*)&so_stg; ++ if (inet_ntop(AF_INET, &(in4->sin_addr), so_ip_name, sizeof(control->ip))) { ++ control->family = AF_INET; ++ control->port = in4->sin_port; ++ } ++ break; ++ } ++ case AF_INET6: { ++ struct sockaddr_in6* in6 = (struct sockaddr_in6*)&so_stg; ++ if (inet_ntop(AF_INET6, &(in6->sin6_addr), so_ip_name, sizeof(control->ip))) { ++ control->family = AF_INET6; ++ control->port = in6->sin6_port; ++ } ++ break; ++ } ++ } ++ ++ return h->func_on_app_event(h, AVAPP_CTRL_DID_TCP_OPEN, (void *)control, sizeof(AVAppTcpIOControl)); ++} ++ ++void av_application_on_async_statistic(AVApplicationContext *h, AVAppAsyncStatistic *statistic) ++{ ++ if (h && h->func_on_app_event) ++ h->func_on_app_event(h, AVAPP_EVENT_ASYNC_STATISTIC, (void *)statistic, sizeof(AVAppAsyncStatistic)); ++} ++ ++void av_application_on_async_read_speed(AVApplicationContext *h, AVAppAsyncReadSpeed *speed) ++{ ++ if (h && h->func_on_app_event) ++ h->func_on_app_event(h, AVAPP_EVENT_ASYNC_READ_SPEED, (void *)speed, sizeof(AVAppAsyncReadSpeed)); ++} ++ ++void av_application_did_io_tcp_read(AVApplicationContext *h, void *obj, int bytes) ++{ ++ AVAppIOTraffic event = {0}; ++ if (!h || !obj || bytes <= 0) ++ return; ++ ++ event.obj = obj; ++ event.bytes = bytes; ++ ++ av_application_on_io_traffic(h, &event); ++} +diff --git a/libavformat/application.h b/libavformat/application.h +new file mode 100644 +index 0000000..b9e7f5b +--- /dev/null ++++ b/libavformat/application.h +@@ -0,0 +1,120 @@ ++/* ++ * copyright (c) 2016 Zhang Rui ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg 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. ++ * ++ * FFmpeg 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 FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#ifndef AVUTIL_APPLICATION_H ++#define AVUTIL_APPLICATION_H ++ ++#include ++ ++#define AVAPP_EVENT_WILL_HTTP_OPEN 1 //AVAppHttpEvent ++#define AVAPP_EVENT_DID_HTTP_OPEN 2 //AVAppHttpEvent ++#define AVAPP_EVENT_WILL_HTTP_SEEK 3 //AVAppHttpEvent ++#define AVAPP_EVENT_DID_HTTP_SEEK 4 //AVAppHttpEvent ++ ++#define AVAPP_EVENT_ASYNC_STATISTIC 0x11000 //AVAppAsyncStatistic ++#define AVAPP_EVENT_ASYNC_READ_SPEED 0x11001 //AVAppAsyncReadSpeed ++#define AVAPP_EVENT_IO_TRAFFIC 0x12204 //AVAppIOTraffic ++ ++#define AVAPP_CTRL_WILL_TCP_OPEN 0x20001 //AVAppTcpIOControl ++#define AVAPP_CTRL_DID_TCP_OPEN 0x20002 //AVAppTcpIOControl ++ ++#define AVAPP_CTRL_WILL_HTTP_OPEN 0x20003 //AVAppIOControl ++#define AVAPP_CTRL_WILL_LIVE_OPEN 0x20005 //AVAppIOControl ++ ++#define AVAPP_CTRL_WILL_CONCAT_SEGMENT_OPEN 0x20007 //AVAppIOControl ++ ++typedef struct AVAppIOControl { ++ size_t size; ++ char url[4096]; /* in, out */ ++ int segment_index; /* in, default = 0 */ ++ int retry_counter; /* in */ ++ ++ int is_handled; /* out, default = false */ ++ int is_url_changed; /* out, default = false */ ++} AVAppIOControl; ++ ++typedef struct AVAppTcpIOControl { ++ int error; ++ int family; ++ char ip[96]; ++ int port; ++ int fd; ++} AVAppTcpIOControl; ++ ++typedef struct AVAppAsyncStatistic { ++ size_t size; ++ int64_t buf_backwards; ++ int64_t buf_forwards; ++ int64_t buf_capacity; ++} AVAppAsyncStatistic; ++ ++typedef struct AVAppAsyncReadSpeed { ++ size_t size; ++ int is_full_speed; ++ int64_t io_bytes; ++ int64_t elapsed_milli; ++} AVAppAsyncReadSpeed; ++ ++typedef struct AVAppHttpEvent ++{ ++ void *obj; ++ char url[4096]; ++ int64_t offset; ++ int error; ++ int http_code; ++ int64_t filesize; ++} AVAppHttpEvent; ++ ++typedef struct AVAppIOTraffic ++{ ++ void *obj; ++ int bytes; ++} AVAppIOTraffic; ++ ++typedef struct AVApplicationContext AVApplicationContext; ++typedef struct AVClass AVClass; ++struct AVApplicationContext { ++ const AVClass *av_class; /**< information for av_log(). Set by av_application_open(). */ ++ void *opaque; /**< user data. */ ++ int (*func_on_app_event)(AVApplicationContext *h, int event_type ,void *obj, size_t size); ++}; ++ ++// open/close ++int av_application_open(AVApplicationContext **ph, void *opaque); ++void av_application_closep(AVApplicationContext **ph); ++ ++// custom protocol invoke ++void av_application_on_http_event(AVApplicationContext *h, int event_type, AVAppHttpEvent *event); ++int av_application_on_io_control(AVApplicationContext *h, int event_type, AVAppIOControl *control); ++void av_application_on_async_statistic(AVApplicationContext *h, AVAppAsyncStatistic *statistic); ++void av_application_on_async_read_speed(AVApplicationContext *h, AVAppAsyncReadSpeed *speed); ++ ++// http event ++void av_application_will_http_open(AVApplicationContext *h, void *obj, const char *url); ++void av_application_did_http_open(AVApplicationContext *h, void *obj, const char *url, int error, int http_code, int64_t filesize); ++void av_application_will_http_seek(AVApplicationContext *h, void *obj, const char *url, int64_t offset); ++void av_application_did_http_seek(AVApplicationContext *h, void *obj, const char *url, int64_t offset, int error, int http_code); ++//tcp event ++int av_application_on_tcp_will_open(AVApplicationContext *h); ++int av_application_on_tcp_did_open(AVApplicationContext *h, int error, int fd, AVAppTcpIOControl *control); ++//tcp speed ++void av_application_did_io_tcp_read(AVApplicationContext *h, void *obj, int bytes); ++ ++#endif /* AVUTIL_APPLICATION_H */ +diff --git a/libavformat/dns_cache.c b/libavformat/dns_cache.c +new file mode 100644 +index 0000000..aab2435 +--- /dev/null ++++ b/libavformat/dns_cache.c +@@ -0,0 +1,232 @@ ++/* ++ * copyright (c) 2017 Raymond Zheng ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg 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. ++ * ++ * FFmpeg 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 FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include "dns_cache.h" ++#include "libavutil/time.h" ++#include "libavutil/mem.h" ++#include "libavformat/network.h" ++#include ++#include ++ ++#if HAVE_PTHREADS ++#include ++#endif ++ ++typedef struct DnsCacheContext DnsCacheContext; ++typedef struct DnsCacheContext { ++ AVDictionary *dns_dictionary; ++ pthread_mutex_t dns_dictionary_mutex; ++ int initialized; ++} DnsCacheContext; ++ ++static DnsCacheContext *context = NULL; ++static pthread_once_t key_once = PTHREAD_ONCE_INIT; ++ ++static void inner_init(void) { ++ int ret = 0; ++ context = (DnsCacheContext *) av_mallocz(sizeof(DnsCacheContext)); ++ if (context) { ++ ret = pthread_mutex_init(&context->dns_dictionary_mutex, NULL); ++ if (!ret) { ++ context->initialized = 1; ++ } else { ++ av_freep(&context); ++ } ++ } ++} ++ ++static void free_private_addrinfo(struct addrinfo **p_ai) { ++ struct addrinfo *ai = *p_ai; ++ ++ if (ai) { ++ if (ai->ai_addr) { ++ av_freep(&ai->ai_addr); ++ } ++ av_freep(p_ai); ++ } ++} ++ ++static int inner_remove_dns_cache(const char *uri, DnsCacheEntry *dns_cache_entry) { ++ if (context && dns_cache_entry) { ++ if (dns_cache_entry->ref_count == 0) { ++ av_dict_set_int(&context->dns_dictionary, uri, 0, 0); ++ free_private_addrinfo(&dns_cache_entry->res); ++ av_freep(&dns_cache_entry); ++ } else { ++ dns_cache_entry->delete_flag = 1; ++ } ++ } ++ ++ return 0; ++} ++ ++static DnsCacheEntry *new_dns_cache_entry(const char *uri, struct addrinfo *cur_ai, int64_t timeout) { ++ DnsCacheEntry *new_entry = NULL; ++ int64_t cur_time = av_gettime_relative(); ++ ++ if (cur_time < 0) { ++ goto fail; ++ } ++ ++ new_entry = (DnsCacheEntry *) av_mallocz(sizeof(struct DnsCacheEntry)); ++ if (!new_entry) { ++ goto fail; ++ } ++ ++ new_entry->res = (struct addrinfo *) av_mallocz(sizeof(struct addrinfo)); ++ if (!new_entry->res) { ++ av_freep(&new_entry); ++ goto fail; ++ } ++ ++ memcpy(new_entry->res, cur_ai, sizeof(struct addrinfo)); ++ ++ new_entry->res->ai_addr = (struct sockaddr *) av_mallocz(sizeof(struct sockaddr)); ++ if (!new_entry->res->ai_addr) { ++ av_freep(&new_entry->res); ++ av_freep(&new_entry); ++ goto fail; ++ } ++ ++ memcpy(new_entry->res->ai_addr, cur_ai->ai_addr, sizeof(struct sockaddr)); ++ new_entry->res->ai_canonname = NULL; ++ new_entry->res->ai_next = NULL; ++ new_entry->ref_count = 0; ++ new_entry->delete_flag = 0; ++ new_entry->expired_time = cur_time + timeout * 1000; ++ ++ return new_entry; ++ ++fail: ++ return NULL; ++} ++ ++DnsCacheEntry *get_dns_cache_reference(const char *uri) { ++ AVDictionaryEntry *elem = NULL; ++ DnsCacheEntry *dns_cache_entry = NULL; ++ int64_t cur_time = av_gettime_relative(); ++ ++ if (cur_time < 0 || !uri || strlen(uri) == 0) { ++ return NULL; ++ } ++ ++ if (!context || !context->initialized) { ++#if HAVE_PTHREADS ++ pthread_once(&key_once, inner_init); ++#endif ++ } ++ ++ if (context && context->initialized) { ++ pthread_mutex_lock(&context->dns_dictionary_mutex); ++ elem = av_dict_get(context->dns_dictionary, uri, NULL, AV_DICT_MATCH_CASE); ++ if (elem) { ++ dns_cache_entry = (DnsCacheEntry *) (intptr_t) strtoll(elem->value, NULL, 10); ++ if (dns_cache_entry) { ++ if (dns_cache_entry->expired_time < cur_time) { ++ inner_remove_dns_cache(uri, dns_cache_entry); ++ dns_cache_entry = NULL; ++ } else { ++ dns_cache_entry->ref_count++; ++ } ++ } ++ } ++ pthread_mutex_unlock(&context->dns_dictionary_mutex); ++ } ++ ++ return dns_cache_entry; ++} ++ ++int release_dns_cache_reference(const char *uri, DnsCacheEntry **p_entry) { ++ DnsCacheEntry *entry = *p_entry; ++ ++ if (!uri || strlen(uri) == 0) { ++ return -1; ++ } ++ ++ if (context && context->initialized && entry) { ++ pthread_mutex_lock(&context->dns_dictionary_mutex); ++ entry->ref_count--; ++ if (entry->delete_flag && entry->ref_count == 0) { ++ inner_remove_dns_cache(uri, entry); ++ entry = NULL; ++ } ++ pthread_mutex_unlock(&context->dns_dictionary_mutex); ++ } ++ return 0; ++} ++ ++int remove_dns_cache_entry(const char *uri) { ++ AVDictionaryEntry *elem = NULL; ++ DnsCacheEntry *dns_cache_entry = NULL; ++ ++ if (!uri || strlen(uri) == 0) { ++ return -1; ++ } ++ ++ if (context && context->initialized) { ++ pthread_mutex_lock(&context->dns_dictionary_mutex); ++ elem = av_dict_get(context->dns_dictionary, uri, NULL, AV_DICT_MATCH_CASE); ++ if (elem) { ++ dns_cache_entry = (DnsCacheEntry *) (intptr_t) strtoll(elem->value, NULL, 10); ++ if (dns_cache_entry) { ++ inner_remove_dns_cache(uri, dns_cache_entry); ++ } ++ } ++ pthread_mutex_unlock(&context->dns_dictionary_mutex); ++ } ++ ++ return 0; ++} ++ ++int add_dns_cache_entry(const char *uri, struct addrinfo *cur_ai, int64_t timeout) { ++ DnsCacheEntry *new_entry = NULL; ++ DnsCacheEntry *old_entry = NULL; ++ AVDictionaryEntry *elem = NULL; ++ ++ if (!uri || strlen(uri) == 0 || timeout <= 0) { ++ goto fail; ++ } ++ ++ if (cur_ai == NULL || cur_ai->ai_addr == NULL) { ++ goto fail; ++ } ++ ++ if (context && context->initialized) { ++ pthread_mutex_lock(&context->dns_dictionary_mutex); ++ elem = av_dict_get(context->dns_dictionary, uri, NULL, AV_DICT_MATCH_CASE); ++ if (elem) { ++ old_entry = (DnsCacheEntry *) (intptr_t) strtoll(elem->value, NULL, 10); ++ if (old_entry) { ++ pthread_mutex_unlock(&context->dns_dictionary_mutex); ++ goto fail; ++ } ++ } ++ new_entry = new_dns_cache_entry(uri, cur_ai, timeout); ++ if (new_entry) { ++ av_dict_set_int(&context->dns_dictionary, uri, (int64_t) (intptr_t) new_entry, 0); ++ } ++ pthread_mutex_unlock(&context->dns_dictionary_mutex); ++ ++ return 0; ++ } ++ ++fail: ++ return -1; ++} +diff --git a/libavformat/dns_cache.h b/libavformat/dns_cache.h +new file mode 100644 +index 0000000..23c695e +--- /dev/null ++++ b/libavformat/dns_cache.h +@@ -0,0 +1,39 @@ ++/* ++ * copyright (c) 2017 Raymond Zheng ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg 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. ++ * ++ * FFmpeg 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 FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#ifndef AVUTIL_DNS_CACHE_H ++#define AVUTIL_DNS_CACHE_H ++ ++#include "libavutil/log.h" ++#include ++ ++typedef struct DnsCacheEntry { ++ volatile int ref_count; ++ volatile int delete_flag; ++ int64_t expired_time; ++ struct addrinfo *res; // construct by private function, not support ai_next and ai_canonname, can only be released using free_private_addrinfo ++} DnsCacheEntry; ++ ++DnsCacheEntry *get_dns_cache_reference(const char *uri); ++int release_dns_cache_reference(const char *uri, DnsCacheEntry **p_entry); ++int remove_dns_cache_entry(const char *uri); ++int add_dns_cache_entry(const char *uri, struct addrinfo *cur_ai, int64_t timeout); ++ ++#endif /* AVUTIL_DNS_CACHE_H */ +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.2/0003-restore-ijk-http-event-hooks.patch b/patches/ffmpeg-n8.1.2/0003-restore-ijk-http-event-hooks.patch new file mode 100644 index 000000000..de76b7df4 --- /dev/null +++ b/patches/ffmpeg-n8.1.2/0003-restore-ijk-http-event-hooks.patch @@ -0,0 +1,278 @@ +From 91b1bc44f636fd2c2a4a40fabffadfd6fa9d22e8 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Fri, 8 May 2026 16:30:34 +0800 +Subject: restore ijk http event hooks + +--- + libavformat/http.c | 32 +++++++++++++++++++++++-- + libavformat/tcp.c | 58 +++++++++++++++++++++++++++++++++++++++++----- + libavutil/error.h | 4 ++++ + 3 files changed, 86 insertions(+), 8 deletions(-) + +diff --git a/libavformat/http.c b/libavformat/http.c +index 52073ff..f4bf225 100644 +--- a/libavformat/http.c ++++ b/libavformat/http.c +@@ -48,6 +48,7 @@ + #include "os_support.h" + #include "url.h" + #include "version.h" ++#include "application.h" + + /* XXX: POST protocol is not completely implemented because ffmpeg uses + * only a subset of it. */ +@@ -158,6 +159,9 @@ typedef struct HTTPContext { + int64_t sum_latency; /* divide by nb_requests */ + int64_t max_latency; + int max_redirects; ++ char *tcp_hook; ++ char *app_ctx_intptr; ++ AVApplicationContext *app_ctx; + } HTTPContext; + + #define OFFSET(x) offsetof(HTTPContext, x) +@@ -206,6 +210,8 @@ static const AVOption options[] = { + { "reply_code", "The http status code to return to a client", OFFSET(reply_code), AV_OPT_TYPE_INT, { .i64 = 200}, INT_MIN, 599, E}, + { "short_seek_size", "Threshold to favor readahead over seek.", OFFSET(short_seek_size), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, D }, + { "max_redirects", "Maximum number of redirects", OFFSET(max_redirects), AV_OPT_TYPE_INT, { .i64 = MAX_REDIRECTS }, 0, INT_MAX, D }, ++ { "http-tcp-hook", "hook protocol on tcp", OFFSET(tcp_hook), AV_OPT_TYPE_STRING, { .str = "tcp" }, 0, 0, D | E }, ++ { "ijkapplication", "AVApplicationContext", OFFSET(app_ctx_intptr), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, .flags = D }, + { NULL } + }; + +@@ -236,6 +242,7 @@ static int http_open_cnx_internal(URLContext *h, AVDictionary **options) + char buf[1024], urlbuf[MAX_URL_SIZE]; + int port, use_proxy, err = 0; + HTTPContext *s = h->priv_data; ++ lower_proto = s->tcp_hook; + + av_url_split(proto, sizeof(proto), auth, sizeof(auth), + hostname, sizeof(hostname), &port, +@@ -313,6 +320,13 @@ static int http_open_cnx_internal(URLContext *h, AVDictionary **options) + + if (!s->hd) { + s->nb_connections++; ++ av_dict_set_intptr(options, "ijkapplication", (uintptr_t)s->app_ctx, 0); ++ ++ // AVDictionaryEntry *t = NULL; ++ // while ((t = av_dict_get(*options, "", t, AV_DICT_IGNORE_SUFFIX))) { ++ // av_log(NULL, AV_LOG_INFO, "%-*s: %-*s = %s\n", 12, "http open tcp", 28, t->key, t->value); ++ // } ++ + err = ffurl_open_whitelist(&s->hd, buf, AVIO_FLAG_READ_WRITE, + &h->interrupt_callback, options, + h->protocol_whitelist, h->protocol_blacklist, h); +@@ -771,6 +785,7 @@ static int http_open(URLContext *h, const char *uri, int flags, + { + HTTPContext *s = h->priv_data; + int ret; ++ s->app_ctx = (AVApplicationContext *)av_dict_strtoptr(s->app_ctx_intptr); + + if( s->seekable == 1 ) + h->is_streamed = 0; +@@ -808,7 +823,9 @@ static int http_open(URLContext *h, const char *uri, int flags, + if (s->listen) { + return http_listen(h, uri, flags, options); + } ++ av_application_will_http_open(s->app_ctx, (void*)h, uri); + ret = http_open_cnx(h, options); ++ av_application_did_http_open(s->app_ctx, (void*)h, uri, ret, s->http_code, s->filesize); + bail_out: + if (ret < 0) { + av_dict_free(&s->chained_options); +@@ -1763,7 +1780,14 @@ static int http_buf_read(URLContext *h, uint8_t *buf, int size) + return AVERROR_EOF; + if (s->off == target_end && target_end < file_end) + return AVERROR(EAGAIN); /* reached end of content range */ +- len = ffurl_read(s->hd, buf, size); ++ len = size; ++ if (s->filesize > 0 && s->filesize != UINT64_MAX && s->filesize != INT32_MAX) { ++ int64_t unread = s->filesize - s->off; ++ if (len > unread) ++ len = (int)unread; ++ } ++ if (len > 0) ++ len = ffurl_read(s->hd, buf, len); + if ((!len || len == AVERROR_EOF) && + (!s->willclose || s->chunksize == UINT64_MAX) && s->off < target_end) { + av_log(h, AV_LOG_ERROR, +@@ -2158,7 +2182,9 @@ static int64_t http_seek_internal(URLContext *h, int64_t off, int whence, int fo + } + + /* if it fails, continue on old connection */ ++ av_application_will_http_seek(s->app_ctx, (void*)h, s->location, off); + if ((ret = http_open_cnx(h, &options)) < 0) { ++ av_application_did_http_seek(s->app_ctx, (void*)h, s->location, off, ret, s->http_code); + av_dict_free(&options); + memcpy(s->buffer, old_buf, old_buf_size); + s->buf_ptr = s->buffer; +@@ -2167,6 +2193,7 @@ static int64_t http_seek_internal(URLContext *h, int64_t off, int whence, int fo + s->off = old_off; + return ret; + } ++ av_application_did_http_seek(s->app_ctx, (void*)h, s->location, off, ret, s->http_code); + av_dict_free(&options); + ffurl_close(old_hd); + return off; +@@ -2260,6 +2287,7 @@ static int http_proxy_open(URLContext *h, const char *uri, int flags) + HTTPAuthType cur_auth_type; + char *authstr; + ++ s->app_ctx = (AVApplicationContext *)av_dict_strtoptr(s->app_ctx_intptr); + if( s->seekable == 1 ) + h->is_streamed = 0; + else +@@ -2272,7 +2300,7 @@ static int http_proxy_open(URLContext *h, const char *uri, int flags) + if (*path == '/') + path++; + +- ff_url_join(lower_url, sizeof(lower_url), "tcp", NULL, hostname, port, ++ ff_url_join(lower_url, sizeof(lower_url), s->tcp_hook, NULL, hostname, port, + NULL); + redo: + ret = ffurl_open_whitelist(&s->hd, lower_url, AVIO_FLAG_READ_WRITE, +diff --git a/libavformat/tcp.c b/libavformat/tcp.c +index ce9f69a..69b1e12 100644 +--- a/libavformat/tcp.c ++++ b/libavformat/tcp.c +@@ -24,7 +24,8 @@ + #include "libavutil/parseutils.h" + #include "libavutil/opt.h" + #include "libavutil/time.h" +- ++#include "libavutil/avstring.h" ++#include "application.h" + #include "internal.h" + #include "network.h" + #include "os_support.h" +@@ -49,6 +50,9 @@ typedef struct TCPContext { + #if !HAVE_WINSOCK2_H + int tcp_mss; + #endif /* !HAVE_WINSOCK2_H */ ++ ++ char * app_ctx_intptr; ++ AVApplicationContext *app_ctx; + } TCPContext; + + #define OFFSET(x) offsetof(TCPContext, x) +@@ -67,6 +71,8 @@ static const AVOption options[] = { + #if !HAVE_WINSOCK2_H + { "tcp_mss", "Maximum segment size for outgoing TCP packets", OFFSET(tcp_mss), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E }, + #endif /* !HAVE_WINSOCK2_H */ ++ { "ijkapplication", "AVApplicationContext", OFFSET(app_ctx_intptr), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, .flags = D }, ++ { "connect_timeout", "set connect timeout (in microseconds) of socket", OFFSET(open_timeout), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E }, + { NULL } + }; + +@@ -155,7 +161,21 @@ static int tcp_open(URLContext *h, const char *uri, int flags) + int ret; + char hostname[1024],proto[1024],path[1024]; + char portstr[10]; +- s->open_timeout = 5000000; ++ AVAppTcpIOControl control = {0}; ++ ++ int ret2; ++ if (s->open_timeout < 0) { ++ s->open_timeout = 15000000; ++ } ++ // av_log(NULL, AV_LOG_INFO, "xql tcp_open uri %s", uri); ++ // av_log(NULL, AV_LOG_INFO, "%-*s: %-*s = %s\n", 12, "xql tcp_open verify", 28, "ijkapplication", s->app_ctx_intptr); ++ // av_log(NULL, AV_LOG_INFO, "%-*s: %-*s = %d\n", 12, "xql tcp_open verify", 28, "connect_timeout", s->open_timeout); ++ // av_log(NULL, AV_LOG_INFO, "%-*s: %-*s = %d\n", 12, "xql tcp_open verify", 28, "addrinfo_one_by_one", s->addrinfo_one_by_one); ++ // av_log(NULL, AV_LOG_INFO, "%-*s: %-*s = %d\n", 12, "xql tcp_open verify", 28, "addrinfo_timeout", s->addrinfo_timeout); ++ // av_log(NULL, AV_LOG_INFO, "%-*s: %-*s = %d\n", 12, "xql tcp_open verify", 28, "dns_cache_timeout", s->dns_cache_timeout); ++ // av_log(NULL, AV_LOG_INFO, "%-*s: %-*s = %d\n", 12, "xql tcp_open verify", 28, "dns_cache_clear", s->dns_cache_clear); ++ ++ s->app_ctx = (AVApplicationContext *)av_dict_strtoptr(s->app_ctx_intptr); + + av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname), + &port, path, sizeof(path), uri); +@@ -172,7 +192,7 @@ static int tcp_open(URLContext *h, const char *uri, int flags) + return ret; + } + if (s->rw_timeout >= 0) { +- s->open_timeout = ++ //s->open_timeout = + h->rw_timeout = s->rw_timeout; + } + hints.ai_family = AF_UNSPEC; +@@ -230,9 +250,24 @@ static int tcp_open(URLContext *h, const char *uri, int flags) + // Socket descriptor already closed here. Safe to overwrite to client one. + fd = ret; + } else { +- ret = ff_connect_parallel(ai, s->open_timeout / 1000, 3, h, &fd, customize_fd, s); ++ ret = av_application_on_tcp_will_open(s->app_ctx); ++ if (ret) { ++ av_log(NULL, AV_LOG_WARNING, "terminated by application in AVAPP_CTRL_WILL_TCP_OPEN"); ++ goto fail1; ++ } ++ ret = ff_connect_parallel(cur_ai, s->open_timeout / 1000, 3, h, &fd, customize_fd, s); ++ ++ ret2 = av_application_on_tcp_did_open(s->app_ctx, ret, fd, &control); ++ + if (ret < 0) + goto fail1; ++ ++ if (ret2) { ++ av_log(NULL, AV_LOG_WARNING, "terminated by application in AVAPP_CTRL_DID_TCP_OPEN"); ++ ret = ret2; ++ goto fail1; ++ } ++ av_log(NULL, AV_LOG_INFO, "tcp did open uri = %s, ip = %s\n", uri , control.ip); + } + + h->is_streamed = 1; +@@ -273,12 +308,18 @@ static int tcp_read(URLContext *h, uint8_t *buf, int size) + + if (!(h->flags & AVIO_FLAG_NONBLOCK)) { + ret = ff_network_wait_fd_timeout(s->fd, 0, h->rw_timeout, &h->interrupt_callback); +- if (ret) ++ if (ret) { ++ if (ret == AVERROR(ETIMEDOUT)) { ++ ret = AVERROR_TCP_READ_TIMEOUT; ++ } + return ret; ++ } + } + ret = recv(s->fd, buf, size, 0); + if (ret == 0) + return AVERROR_EOF; ++ if (ret > 0) ++ av_application_did_io_tcp_read(s->app_ctx, (void*)h, ret); + return ret < 0 ? ff_neterrno() : ret; + } + +@@ -289,9 +330,14 @@ static int tcp_write(URLContext *h, const uint8_t *buf, int size) + + if (!(h->flags & AVIO_FLAG_NONBLOCK)) { + ret = ff_network_wait_fd_timeout(s->fd, 1, h->rw_timeout, &h->interrupt_callback); +- if (ret) ++ if (ret) { ++ if (ret == AVERROR(ETIMEDOUT)) { ++ ret = AVERROR_TCP_WRITE_TIMEOUT; ++ } + return ret; ++ } + } ++ + ret = send(s->fd, buf, size, MSG_NOSIGNAL); + return ret < 0 ? ff_neterrno() : ret; + } +diff --git a/libavutil/error.h b/libavutil/error.h +index 1efa86c..9dee755 100644 +--- a/libavutil/error.h ++++ b/libavutil/error.h +@@ -85,6 +85,10 @@ + + #define AV_ERROR_MAX_STRING_SIZE 64 + ++#define AVERROR_TCP_CONNECT_TIMEOUT -1001 ++#define AVERROR_TCP_READ_TIMEOUT -1002 ++#define AVERROR_TCP_WRITE_TIMEOUT -1003 ++ + /** + * Put a description of the AVERROR code errnum in errbuf. + * In case of failure the global variable errno is set to indicate the +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.2/0004-restore-ijk-tcp-dns-cache.patch b/patches/ffmpeg-n8.1.2/0004-restore-ijk-tcp-dns-cache.patch new file mode 100644 index 000000000..29a6b9611 --- /dev/null +++ b/patches/ffmpeg-n8.1.2/0004-restore-ijk-tcp-dns-cache.patch @@ -0,0 +1,404 @@ +From acffa5bb5305c8f28419c0f476e8d123f09d9bcd Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Fri, 8 May 2026 16:39:50 +0800 +Subject: restore ijk tcp dns cache + +--- + libavformat/tcp.c | 324 ++++++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 310 insertions(+), 14 deletions(-) + +diff --git a/libavformat/tcp.c b/libavformat/tcp.c +index 69b1e12..46b48e6 100644 +--- a/libavformat/tcp.c ++++ b/libavformat/tcp.c +@@ -26,6 +26,7 @@ + #include "libavutil/time.h" + #include "libavutil/avstring.h" + #include "application.h" ++#include "dns_cache.h" + #include "internal.h" + #include "network.h" + #include "os_support.h" +@@ -33,6 +34,9 @@ + #if HAVE_POLL_H + #include + #endif ++#if HAVE_PTHREADS ++#include ++#endif + + typedef struct TCPContext { + const AVClass *class; +@@ -52,6 +56,10 @@ typedef struct TCPContext { + #endif /* !HAVE_WINSOCK2_H */ + + char * app_ctx_intptr; ++ int addrinfo_one_by_one; ++ int addrinfo_timeout; ++ int64_t dns_cache_timeout; ++ int dns_cache_clear; + AVApplicationContext *app_ctx; + } TCPContext; + +@@ -73,9 +81,261 @@ static const AVOption options[] = { + #endif /* !HAVE_WINSOCK2_H */ + { "ijkapplication", "AVApplicationContext", OFFSET(app_ctx_intptr), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, .flags = D }, + { "connect_timeout", "set connect timeout (in microseconds) of socket", OFFSET(open_timeout), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E }, ++ { "addrinfo_one_by_one", "parse addrinfo one by one in getaddrinfo()", OFFSET(addrinfo_one_by_one), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, .flags = D|E }, ++ { "addrinfo_timeout", "set timeout (in microseconds) for getaddrinfo()", OFFSET(addrinfo_timeout), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E }, ++ { "dns_cache_timeout", "dns cache TTL (in microseconds)", OFFSET(dns_cache_timeout), AV_OPT_TYPE_INT, { .i64 = 0 }, -1, INT64_MAX, .flags = D|E }, ++ { "dns_cache_clear", "clear dns cache", OFFSET(dns_cache_clear), AV_OPT_TYPE_INT, { .i64 = 0}, -1, INT_MAX, .flags = D|E }, + { NULL } + }; + ++int ijk_tcp_getaddrinfo_nonblock(const char *hostname, const char *servname, ++ const struct addrinfo *hints, struct addrinfo **res, ++ int64_t timeout, ++ const AVIOInterruptCB *int_cb, int one_by_one); ++#ifdef HAVE_PTHREADS ++ ++typedef struct TCPAddrinfoRequest ++{ ++ AVBufferRef *buffer; ++ ++ pthread_mutex_t mutex; ++ pthread_cond_t cond; ++ ++ AVIOInterruptCB interrupt_callback; ++ ++ char *hostname; ++ char *servname; ++ struct addrinfo hints; ++ struct addrinfo *res; ++ ++ volatile int finished; ++ int last_error; ++} TCPAddrinfoRequest; ++ ++static void tcp_getaddrinfo_request_free(TCPAddrinfoRequest *req) ++{ ++ av_assert0(req); ++ if (req->res) { ++ freeaddrinfo(req->res); ++ req->res = NULL; ++ } ++ ++ av_freep(&req->servname); ++ av_freep(&req->hostname); ++ pthread_cond_destroy(&req->cond); ++ pthread_mutex_destroy(&req->mutex); ++ av_freep(&req); ++} ++ ++static void tcp_getaddrinfo_request_free_buffer(void *opaque, uint8_t *data) ++{ ++ av_assert0(opaque); ++ TCPAddrinfoRequest *req = (TCPAddrinfoRequest *)opaque; ++ tcp_getaddrinfo_request_free(req); ++} ++ ++static int tcp_getaddrinfo_request_create(TCPAddrinfoRequest **request, ++ const char *hostname, ++ const char *servname, ++ const struct addrinfo *hints, ++ const AVIOInterruptCB *int_cb) ++{ ++ TCPAddrinfoRequest *req = (TCPAddrinfoRequest *) av_mallocz(sizeof(TCPAddrinfoRequest)); ++ if (!req) ++ return AVERROR(ENOMEM); ++ ++ if (pthread_mutex_init(&req->mutex, NULL)) { ++ av_freep(&req); ++ return AVERROR(ENOMEM); ++ } ++ ++ if (pthread_cond_init(&req->cond, NULL)) { ++ pthread_mutex_destroy(&req->mutex); ++ av_freep(&req); ++ return AVERROR(ENOMEM); ++ } ++ ++ if (int_cb) ++ req->interrupt_callback = *int_cb; ++ ++ if (hostname) { ++ req->hostname = av_strdup(hostname); ++ if (!req->hostname) ++ goto fail; ++ } ++ ++ if (servname) { ++ req->servname = av_strdup(servname); ++ if (!req->hostname) ++ goto fail; ++ } ++ ++ if (hints) { ++ req->hints.ai_family = hints->ai_family; ++ req->hints.ai_socktype = hints->ai_socktype; ++ req->hints.ai_protocol = hints->ai_protocol; ++ req->hints.ai_flags = hints->ai_flags; ++ } ++ ++ req->buffer = av_buffer_create(NULL, 0, tcp_getaddrinfo_request_free_buffer, req, 0); ++ if (!req->buffer) ++ goto fail; ++ ++ *request = req; ++ return 0; ++fail: ++ tcp_getaddrinfo_request_free(req); ++ return AVERROR(ENOMEM); ++} ++ ++static void *tcp_getaddrinfo_worker(void *arg) ++{ ++ TCPAddrinfoRequest *req = arg; ++ ++ getaddrinfo(req->hostname, req->servname, &req->hints, &req->res); ++ pthread_mutex_lock(&req->mutex); ++ req->finished = 1; ++ pthread_cond_signal(&req->cond); ++ pthread_mutex_unlock(&req->mutex); ++ av_buffer_unref(&req->buffer); ++ return NULL; ++} ++ ++static void *tcp_getaddrinfo_one_by_one_worker(void *arg) ++{ ++ struct addrinfo *temp_addrinfo = NULL; ++ struct addrinfo *cur = NULL; ++ int ret = EAI_FAIL; ++ int i = 0; ++ int option_length = 0; ++ ++ TCPAddrinfoRequest *req = (TCPAddrinfoRequest *)arg; ++ ++ int family_option[2] = {AF_INET, AF_INET6}; ++ ++ option_length = sizeof(family_option) / sizeof(family_option[0]); ++ ++ for (; i < option_length; ++i) { ++ struct addrinfo *hint = &req->hints; ++ hint->ai_family = family_option[i]; ++ ret = getaddrinfo(req->hostname, req->servname, hint, &temp_addrinfo); ++ if (ret) { ++ req->last_error = ret; ++ continue; ++ } ++ pthread_mutex_lock(&req->mutex); ++ if (!req->res) { ++ req->res = temp_addrinfo; ++ } else { ++ cur = req->res; ++ while (cur->ai_next) ++ cur = cur->ai_next; ++ cur->ai_next = temp_addrinfo; ++ } ++ pthread_mutex_unlock(&req->mutex); ++ } ++ pthread_mutex_lock(&req->mutex); ++ req->finished = 1; ++ pthread_cond_signal(&req->cond); ++ pthread_mutex_unlock(&req->mutex); ++ av_buffer_unref(&req->buffer); ++ return NULL; ++} ++ ++int ijk_tcp_getaddrinfo_nonblock(const char *hostname, const char *servname, ++ const struct addrinfo *hints, struct addrinfo **res, ++ int64_t timeout, ++ const AVIOInterruptCB *int_cb, int one_by_one) ++{ ++ int ret; ++ int64_t start; ++ int64_t now; ++ AVBufferRef *req_ref = NULL; ++ TCPAddrinfoRequest *req = NULL; ++ pthread_t work_thread; ++ ++ if (hostname && !hostname[0]) ++ hostname = NULL; ++ av_log(NULL, AV_LOG_DEBUG, "dns getaddrinfo uri = %s\n", hostname); ++ if (timeout <= 0) ++ return getaddrinfo(hostname, servname, hints, res); ++ av_log(NULL, AV_LOG_DEBUG, "dns tcp_getaddrinfo_request_create uri = %s\n", hostname); ++ ret = tcp_getaddrinfo_request_create(&req, hostname, servname, hints, int_cb); ++ if (ret) ++ goto fail; ++ ++ req_ref = av_buffer_ref(req->buffer); ++ if (req_ref == NULL) { ++ ret = AVERROR(ENOMEM); ++ goto fail; ++ } ++ ++ /* FIXME: using a thread pool would be better. */ ++ if (one_by_one) ++ ret = pthread_create(&work_thread, NULL, tcp_getaddrinfo_one_by_one_worker, req); ++ else ++ ret = pthread_create(&work_thread, NULL, tcp_getaddrinfo_worker, req); ++ ++ if (ret) { ++ ret = AVERROR(ret); ++ goto fail; ++ } ++ ++ pthread_detach(work_thread); ++ ++ start = av_gettime(); ++ now = start; ++ ++ pthread_mutex_lock(&req->mutex); ++ while (1) { ++ int64_t wait_time = now + 100000; ++ struct timespec tv = { .tv_sec = wait_time / 1000000, ++ .tv_nsec = (wait_time % 1000000) * 1000 }; ++ ++ if (req->finished || (start + timeout < now)) { ++ if (req->res) { ++ ret = 0; ++ *res = req->res; ++ req->res = NULL; ++ } else { ++ ret = req->last_error ? req->last_error : AVERROR_EXIT; ++ } ++ break; ++ } ++#if defined(__ANDROID__) && defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC) ++ ret = pthread_cond_timedwait_monotonic_np(&req->cond, &req->mutex, &tv); ++#else ++ ret = pthread_cond_timedwait(&req->cond, &req->mutex, &tv); ++#endif ++ if (ret != 0 && ret != ETIMEDOUT) { ++ av_log(NULL, AV_LOG_ERROR, "pthread_cond_timedwait failed: %d\n", ret); ++ ret = AVERROR_EXIT; ++ break; ++ } ++ ++ if (ff_check_interrupt(&req->interrupt_callback)) { ++ ret = AVERROR_EXIT; ++ break; ++ } ++ ++ now = av_gettime(); ++ } ++ pthread_mutex_unlock(&req->mutex); ++fail: ++ av_buffer_unref(&req_ref); ++ return ret; ++} ++ ++#else ++int ijk_tcp_getaddrinfo_nonblock(const char *hostname, const char *servname, ++ const struct addrinfo *hints, struct addrinfo **res, ++ int64_t timeout, ++ const AVIOInterruptCB *int_cb) ++{ ++ return getaddrinfo(hostname, servname, hints, res); ++} ++#endif ++ + static const AVClass tcp_class = { + .class_name = "tcp", + .item_name = av_default_item_name, +@@ -162,7 +422,7 @@ static int tcp_open(URLContext *h, const char *uri, int flags) + char hostname[1024],proto[1024],path[1024]; + char portstr[10]; + AVAppTcpIOControl control = {0}; +- ++ DnsCacheEntry *dns_entry = NULL; + int ret2; + if (s->open_timeout < 0) { + s->open_timeout = 15000000; +@@ -200,18 +460,37 @@ static int tcp_open(URLContext *h, const char *uri, int flags) + snprintf(portstr, sizeof(portstr), "%d", port); + if (s->listen) + hints.ai_flags |= AI_PASSIVE; +- if (!hostname[0]) +- ret = getaddrinfo(NULL, portstr, &hints, &ai); +- else +- ret = getaddrinfo(hostname, portstr, &hints, &ai); +- if (ret) { +- av_log(h, AV_LOG_ERROR, +- "Failed to resolve hostname %s: %s\n", +- hostname, gai_strerror(ret)); +- return AVERROR(EIO); ++ if (s->dns_cache_timeout > 0) { ++ if (s->dns_cache_clear) { ++ remove_dns_cache_entry(uri); ++ } else { ++ dns_entry = get_dns_cache_reference(uri); ++ } + } + +- cur_ai = ai; ++ if (!dns_entry) { ++#ifdef HAVE_PTHREADS ++ ret = ijk_tcp_getaddrinfo_nonblock(hostname, portstr, &hints, &ai, s->addrinfo_timeout, &h->interrupt_callback, s->addrinfo_one_by_one); ++#else ++ if (s->addrinfo_timeout > 0) ++ av_log(h, AV_LOG_WARNING, "Ignore addrinfo_timeout without pthreads support.\n"); ++ if (!hostname[0]) ++ ret = getaddrinfo(NULL, portstr, &hints, &ai); ++ else ++ ret = getaddrinfo(hostname, portstr, &hints, &ai); ++#endif ++ if (ret) { ++ av_log(h, AV_LOG_ERROR, ++ "Failed to resolve hostname %s: %s\n", ++ hostname, gai_strerror(ret)); ++ return AVERROR(EIO); ++ } ++ ++ cur_ai = ai; ++ } else { ++ av_log(NULL, AV_LOG_DEBUG, "hit dns cache uri = %s\n", uri); ++ cur_ai = dns_entry->res; ++ } + + #if HAVE_STRUCT_SOCKADDR_IN6 + // workaround for IOS9 getaddrinfo in IPv6 only network use hardcode IPv4 address can not resolve port number. +@@ -267,19 +546,36 @@ static int tcp_open(URLContext *h, const char *uri, int flags) + ret = ret2; + goto fail1; + } +- av_log(NULL, AV_LOG_INFO, "tcp did open uri = %s, ip = %s\n", uri , control.ip); ++ ++ if (!dns_entry && !strstr(uri, control.ip) && s->dns_cache_timeout > 0) { ++ add_dns_cache_entry(uri, cur_ai, s->dns_cache_timeout); ++ av_log(NULL, AV_LOG_DEBUG, "add dns cache uri = %s, ip = %s\n", uri , control.ip); ++ } ++ av_log(NULL, AV_LOG_DEBUG, "tcp did open uri = %s, ip = %s\n", uri , control.ip); + } + + h->is_streamed = 1; + s->fd = fd; + +- freeaddrinfo(ai); ++ if (dns_entry) { ++ release_dns_cache_reference(uri, &dns_entry); ++ } else { ++ freeaddrinfo(ai); ++ } + return 0; + + fail1: + if (fd >= 0) + closesocket(fd); +- freeaddrinfo(ai); ++ ++ if (dns_entry) { ++ av_log(NULL, AV_LOG_ERROR, "hit dns cache but connect fail uri = %s, ip = %s\n", uri , control.ip); ++ release_dns_cache_reference(uri, &dns_entry); ++ remove_dns_cache_entry(uri); ++ } else { ++ freeaddrinfo(cur_ai); ++ } ++ + return ret; + } + +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.2/0005-restore-ijk-custom-protocols-and-demuxers-except-lon.patch b/patches/ffmpeg-n8.1.2/0005-restore-ijk-custom-protocols-and-demuxers-except-lon.patch new file mode 100644 index 000000000..db346dd66 --- /dev/null +++ b/patches/ffmpeg-n8.1.2/0005-restore-ijk-custom-protocols-and-demuxers-except-lon.patch @@ -0,0 +1,277 @@ +From 44cd4ed4535a755207505e1dbe2c8f06091c4af7 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Thu, 25 Jun 2026 14:18:13 +0800 +Subject: [PATCH] restore ijk custom protocols and demuxers except long url and + async + +--- + libavcodec/Makefile | 1 + + libavformat/Makefile | 10 ++++ + libavformat/allformats.c | 3 ++ + libavformat/demux.c | 13 +++++ + libavformat/demux.h | 2 + + libavformat/ijkutils.c | 107 +++++++++++++++++++++++++++++++++++++++ + libavformat/protocols.c | 6 +++ + libavutil/Makefile | 3 +- + 8 files changed, 144 insertions(+), 1 deletion(-) + create mode 100644 libavformat/ijkutils.c + +diff --git a/libavcodec/Makefile b/libavcodec/Makefile +index 1410bd8..4445d9e 100644 +--- a/libavcodec/Makefile ++++ b/libavcodec/Makefile +@@ -26,6 +26,7 @@ HEADERS = ac3_parser.h \ + version_major.h \ + videotoolbox.h \ + vorbis_parser.h \ ++ packet_internal.h \ + + OBJS = ac3_parser.o \ + adts_parser.o \ +diff --git a/libavformat/Makefile b/libavformat/Makefile +index 3c95fc4..85ed05b 100644 +--- a/libavformat/Makefile ++++ b/libavformat/Makefile +@@ -7,6 +7,15 @@ HEADERS = avformat.h \ + version_major.h \ + application.h \ + dns_cache.h \ ++ demux.h \ ++ avc.h \ ++ url.h \ ++ internal.h \ ++ avio_internal.h \ ++ flv.h \ ++ id3v2.h \ ++ os_support.h \ ++ metadata.h \ + + + OBJS = allformats.o \ +@@ -40,6 +49,7 @@ OBJS = allformats.o \ + vpcc.o \ + application.o \ + dns_cache.o \ ++ ijkutils.o \ + + OBJS-$(HAVE_LIBC_MSVCRT) += file_open.o + +diff --git a/libavformat/allformats.c b/libavformat/allformats.c +index 6ec361f..0c2d5ca 100644 +--- a/libavformat/allformats.c ++++ b/libavformat/allformats.c +@@ -584,6 +584,9 @@ extern const FFInputFormat ff_libmodplug_demuxer; + extern const FFInputFormat ff_libopenmpt_demuxer; + extern const FFInputFormat ff_vapoursynth_demuxer; + ++// ijk custom demuxers ++extern FFInputFormat ff_ijklivehook_demuxer; ++ + #include "libavformat/muxer_list.c" + #include "libavformat/demuxer_list.c" + +diff --git a/libavformat/demux.c b/libavformat/demux.c +index 5553997..fdecbd9 100644 +--- a/libavformat/demux.c ++++ b/libavformat/demux.c +@@ -235,6 +235,7 @@ int avformat_open_input(AVFormatContext **ps, const char *filename, + AVFormatContext *s = *ps; + FFFormatContext *si; + AVDictionary *tmp = NULL; ++ AVDictionary *tmp2 = NULL; + ID3v2ExtraMeta *id3v2_extra_meta = NULL; + int ret = 0; + +@@ -318,6 +319,16 @@ int avformat_open_input(AVFormatContext **ps, const char *filename, + /* e.g. AVFMT_NOFILE formats will not have an AVIOContext */ + if (s->pb && is_id3v2_format(s->iformat)) + ff_id3v2_read_dict(s->pb, &si->id3v2_meta, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta); ++ ++ if (ffifmt(s->iformat)->read_header2) { ++ if (options) ++ av_dict_copy(&tmp2,*options, 0); ++ if ((ret = ffifmt(s->iformat)->read_header2(s, &tmp2)) < 0) { ++ if (ffifmt(s->iformat)->flags_internal & FF_INFMT_FLAG_INIT_CLEANUP) ++ goto close; ++ goto fail; ++ } ++ } else + + if (ffifmt(s->iformat)->read_header) + if ((ret = ffifmt(s->iformat)->read_header(s)) < 0) { +@@ -357,6 +368,7 @@ int avformat_open_input(AVFormatContext **ps, const char *filename, + if (options) { + av_dict_free(options); + *options = tmp; ++ av_dict_free(&tmp2); + } + *ps = s; + return 0; +@@ -367,6 +379,7 @@ close: + fail: + ff_id3v2_free_extra_meta(&id3v2_extra_meta); + av_dict_free(&tmp); ++ av_dict_free(&tmp2); + if (s->pb && !(s->flags & AVFMT_FLAG_CUSTOM_IO)) + ff_format_io_close(s, &s->pb); + avformat_free_context(s); +diff --git a/libavformat/demux.h b/libavformat/demux.h +index f09afc8..531c433 100644 +--- a/libavformat/demux.h ++++ b/libavformat/demux.h +@@ -98,6 +98,8 @@ typedef struct FFInputFormat { + */ + int (*read_header)(struct AVFormatContext *); + ++ int (*read_header2)(struct AVFormatContext *, AVDictionary **options); ++ + /** + * Read one packet and put it in 'pkt'. pts and flags are also + * set. 'avformat_new_stream' can be called only if the flag +diff --git a/libavformat/ijkutils.c b/libavformat/ijkutils.c +new file mode 100644 +index 0000000..0314c48 +--- /dev/null ++++ b/libavformat/ijkutils.c +@@ -0,0 +1,107 @@ ++/* ++ * utils.c ++ * ++ * Copyright (c) 2003 Fabrice Bellard ++ * Copyright (c) 2013 Zhang Rui ++ * ++ * This file is part of ijkPlayer. ++ * ++ * ijkPlayer 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. ++ * ++ * ijkPlayer 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 ijkPlayer; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include ++#include "url.h" ++#include "demux.h" ++ ++ ++#define IJK_FF_PROTOCOL(x) \ ++extern URLProtocol ff_##x##_protocol; \ ++int ijkav_register_##x##_protocol(URLProtocol *protocol, int protocol_size); \ ++int ijkav_register_##x##_protocol(URLProtocol *protocol, int protocol_size) \ ++{ \ ++ if (protocol_size != sizeof(URLProtocol)) { \ ++ av_log(NULL, AV_LOG_ERROR, "ijkav_register_##x##_protocol: ABI mismatch.\n"); \ ++ return -1; \ ++ } \ ++ memcpy(&ff_##x##_protocol, protocol, protocol_size); \ ++ return 0; \ ++} ++ ++#define IJK_DUMMY_PROTOCOL(x) \ ++IJK_FF_PROTOCOL(x); \ ++static const AVClass ijk_##x##_context_class = { \ ++ .class_name = #x, \ ++ .item_name = av_default_item_name, \ ++ .version = LIBAVUTIL_VERSION_INT, \ ++ }; \ ++ \ ++URLProtocol ff_##x##_protocol = { \ ++ .name = #x, \ ++ .url_open2 = ijkdummy_open, \ ++ .priv_data_size = 1, \ ++ .priv_data_class = &ijk_##x##_context_class, \ ++}; ++ ++static int ijkdummy_open(URLContext *h, const char *arg, int flags, AVDictionary **options) ++{ ++ return -1; ++} ++ ++static int ijkdummy_probe(const AVProbeData *p) ++{ ++ return AVERROR_INVALIDDATA; ++} ++ ++IJK_DUMMY_PROTOCOL(ijkmediadatasource); ++IJK_DUMMY_PROTOCOL(ijkhttphook); ++IJK_DUMMY_PROTOCOL(ijksegment); ++IJK_DUMMY_PROTOCOL(ijktcphook); ++IJK_DUMMY_PROTOCOL(ijkio); ++ ++#define IJK_FF_DEMUXER(x) \ ++extern FFInputFormat ff_##x##_demuxer; \ ++int ijkav_register_##x##_demuxer(FFInputFormat *demuxer, int demuxer_size); \ ++int ijkav_register_##x##_demuxer(FFInputFormat *demuxer, int demuxer_size) \ ++{ \ ++ if (demuxer_size != sizeof(FFInputFormat)) { \ ++ av_log(NULL, AV_LOG_ERROR, "ijkav_register_##x##_demuxer: ABI mismatch.\n"); \ ++ return -1; \ ++ } \ ++ memcpy(&ff_##x##_demuxer, demuxer, demuxer_size); \ ++ return 0; \ ++} ++ ++#define IJK_DUMMY_DEMUXER(x) \ ++IJK_FF_DEMUXER(x); \ ++static const AVClass ijk_##x##_demuxer_class = { \ ++ .class_name = #x, \ ++ .item_name = av_default_item_name, \ ++ .version = LIBAVUTIL_VERSION_INT, \ ++ }; \ ++ \ ++FFInputFormat ff_##x##_demuxer = { \ ++ .p.name = #x, \ ++ .p.priv_class = &ijk_##x##_demuxer_class, \ ++ .priv_data_size = 1, \ ++ .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, \ ++ .read_probe = ijkdummy_probe, \ ++}; ++ ++/* ++ libavformat/ijkutils.c:99:1: error: field designator 'priv_data_size' does not refer to any field in type 'AVInputFormat' (aka 'struct AVInputFormat') ++ 99 | IJK_DUMMY_DEMUXER(ijklivehook); ++ ++ */ ++IJK_DUMMY_DEMUXER(ijklivehook); +diff --git a/libavformat/protocols.c b/libavformat/protocols.c +index 207b6bf..de0926b 100644 +--- a/libavformat/protocols.c ++++ b/libavformat/protocols.c +@@ -79,6 +79,12 @@ extern const URLProtocol ff_libzmq_protocol; + extern const URLProtocol ff_ipfs_gateway_protocol; + extern const URLProtocol ff_ipns_gateway_protocol; + ++extern const URLProtocol ff_ijkhttphook_protocol; ++extern const URLProtocol ff_ijkmediadatasource_protocol; ++extern const URLProtocol ff_ijksegment_protocol; ++extern const URLProtocol ff_ijktcphook_protocol; ++extern const URLProtocol ff_ijkio_protocol; ++ + #include "libavformat/protocol_list.c" + + const AVClass *ff_urlcontext_child_class_iterate(void **iter) +diff --git a/libavutil/Makefile b/libavutil/Makefile +index c524189..a9fbf99 100644 +--- a/libavutil/Makefile ++++ b/libavutil/Makefile +@@ -99,7 +99,8 @@ HEADERS = adler32.h \ + xtea.h \ + tea.h \ + tx.h \ +- video_hint.h ++ video_hint.h \ ++ thread.h \ + + ARCH_HEADERS = bswap.h \ + intmath.h \ +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.2/0006-add-4-dummy-ijkplaceholder-demuxers.patch b/patches/ffmpeg-n8.1.2/0006-add-4-dummy-ijkplaceholder-demuxers.patch new file mode 100644 index 000000000..535f45598 --- /dev/null +++ b/patches/ffmpeg-n8.1.2/0006-add-4-dummy-ijkplaceholder-demuxers.patch @@ -0,0 +1,41 @@ +From 44ce37e539f1db0fce3804b192f2e48a7d72e040 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Fri, 8 May 2026 17:02:34 +0800 +Subject: add 4 dummy ijkplaceholder demuxers + +--- + libavformat/allformats.c | 4 ++++ + libavformat/ijkutils.c | 4 ++++ + 2 files changed, 8 insertions(+) + +diff --git a/libavformat/allformats.c b/libavformat/allformats.c +index 0c2d5ca..e2f37da 100644 +--- a/libavformat/allformats.c ++++ b/libavformat/allformats.c +@@ -586,6 +586,10 @@ extern const FFInputFormat ff_vapoursynth_demuxer; + + // ijk custom demuxers + extern FFInputFormat ff_ijklivehook_demuxer; ++extern FFInputFormat ff_ijkplaceholder1_demuxer; ++extern FFInputFormat ff_ijkplaceholder2_demuxer; ++extern FFInputFormat ff_ijkplaceholder3_demuxer; ++extern FFInputFormat ff_ijkplaceholder4_demuxer; + + #include "libavformat/muxer_list.c" + #include "libavformat/demuxer_list.c" +diff --git a/libavformat/ijkutils.c b/libavformat/ijkutils.c +index 0314c48..ac80e54 100644 +--- a/libavformat/ijkutils.c ++++ b/libavformat/ijkutils.c +@@ -105,3 +105,7 @@ FFInputFormat ff_##x##_demuxer = { \ + + */ + IJK_DUMMY_DEMUXER(ijklivehook); ++IJK_DUMMY_DEMUXER(ijkplaceholder1); ++IJK_DUMMY_DEMUXER(ijkplaceholder2); ++IJK_DUMMY_DEMUXER(ijkplaceholder3); ++IJK_DUMMY_DEMUXER(ijkplaceholder4); +\ No newline at end of file +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.2/0007-add-3-dummy-ijkhttp-protocols.patch b/patches/ffmpeg-n8.1.2/0007-add-3-dummy-ijkhttp-protocols.patch new file mode 100644 index 000000000..eefafb84e --- /dev/null +++ b/patches/ffmpeg-n8.1.2/0007-add-3-dummy-ijkhttp-protocols.patch @@ -0,0 +1,41 @@ +From 86da4f9eeb7824aa61cb59b690507a0772972a1d Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Fri, 8 May 2026 17:07:35 +0800 +Subject: add 3 dummy ijkhttp protocols + +--- + libavformat/ijkutils.c | 3 +++ + libavformat/protocols.c | 3 +++ + 2 files changed, 6 insertions(+) + +diff --git a/libavformat/ijkutils.c b/libavformat/ijkutils.c +index ac80e54..ba8eadd 100644 +--- a/libavformat/ijkutils.c ++++ b/libavformat/ijkutils.c +@@ -69,6 +69,9 @@ IJK_DUMMY_PROTOCOL(ijkhttphook); + IJK_DUMMY_PROTOCOL(ijksegment); + IJK_DUMMY_PROTOCOL(ijktcphook); + IJK_DUMMY_PROTOCOL(ijkio); ++IJK_DUMMY_PROTOCOL(ijkhttp1); ++IJK_DUMMY_PROTOCOL(ijkhttp2); ++IJK_DUMMY_PROTOCOL(ijkhttp3); + + #define IJK_FF_DEMUXER(x) \ + extern FFInputFormat ff_##x##_demuxer; \ +diff --git a/libavformat/protocols.c b/libavformat/protocols.c +index de0926b..d1c1095 100644 +--- a/libavformat/protocols.c ++++ b/libavformat/protocols.c +@@ -84,6 +84,9 @@ extern const URLProtocol ff_ijkmediadatasource_protocol; + extern const URLProtocol ff_ijksegment_protocol; + extern const URLProtocol ff_ijktcphook_protocol; + extern const URLProtocol ff_ijkio_protocol; ++extern const URLProtocol ff_ijkhttp1_protocol; ++extern const URLProtocol ff_ijkhttp2_protocol; ++extern const URLProtocol ff_ijkhttp3_protocol; + + #include "libavformat/protocol_list.c" + +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.2/0008-restore-ijk-av_dict_get-that-converts-the-value-to-a.patch b/patches/ffmpeg-n8.1.2/0008-restore-ijk-av_dict_get-that-converts-the-value-to-a.patch new file mode 100644 index 000000000..d4f4d3c69 --- /dev/null +++ b/patches/ffmpeg-n8.1.2/0008-restore-ijk-av_dict_get-that-converts-the-value-to-a.patch @@ -0,0 +1,91 @@ +From 3da3f038de2cd4067263b69a06ac42ceb01b56d5 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Wed, 28 May 2025 18:33:27 +0800 +Subject: restore ijk av_dict_get that converts the value to a + pointer + +--- + libavutil/dict.c | 39 ++++++++++++++++++++++++++++++++++++++- + libavutil/dict.h | 9 +++++++++ + 2 files changed, 47 insertions(+), 1 deletion(-) + +diff --git a/libavutil/dict.c b/libavutil/dict.c +index fafb454..db8ee05 100644 +--- a/libavutil/dict.c ++++ b/libavutil/dict.c +@@ -63,7 +63,7 @@ AVDictionaryEntry *av_dict_get(const AVDictionary *m, const char *key, + const AVDictionaryEntry *entry = prev; + unsigned int j; + +- if (!key) ++ if (!m || !key) + return NULL; + + while ((entry = av_dict_iterate(m, entry))) { +@@ -183,6 +183,43 @@ int av_dict_set_int(AVDictionary **pm, const char *key, int64_t value, + return av_dict_set(pm, key, valuestr, flags); + } + ++int av_dict_set_intptr(AVDictionary **pm, const char *key, uintptr_t value, ++ int flags) ++{ ++ char valuestr[22]; ++ snprintf(valuestr, sizeof(valuestr), "%p", value); ++ flags &= ~AV_DICT_DONT_STRDUP_VAL; ++ return av_dict_set(pm, key, valuestr, flags); ++} ++ ++uintptr_t av_dict_get_intptr(const AVDictionary *m, const char* key) { ++ uintptr_t ptr = NULL; ++ AVDictionaryEntry *t = NULL; ++ if ((t = av_dict_get(m, key, NULL, 0))) { ++ return av_dict_strtoptr(t->value); ++ } ++ return NULL; ++} ++ ++uintptr_t av_dict_strtoptr(char * value) { ++ uintptr_t ptr = NULL; ++ char *next = NULL; ++ if(!value || value[0] !='0' || (value[1]|0x20)!='x') { ++ return NULL; ++ } ++ ptr = strtoull(value, &next, 16); ++ if (next == value) { ++ return NULL; ++ } ++ return ptr; ++} ++ ++char * av_dict_ptrtostr(uintptr_t value) { ++ char valuestr[22] = {0}; ++ snprintf(valuestr, sizeof(valuestr), "%p", value); ++ return av_strdup(valuestr); ++} ++ + static int parse_key_value_pair(AVDictionary **pm, const char **buf, + const char *key_val_sep, const char *pairs_sep, + int flags) +diff --git a/libavutil/dict.h b/libavutil/dict.h +index 93c7cbf..d97a601 100644 +--- a/libavutil/dict.h ++++ b/libavutil/dict.h +@@ -173,6 +173,15 @@ int av_dict_set(AVDictionary **pm, const char *key, const char *value, int flags + */ + int av_dict_set_int(AVDictionary **pm, const char *key, int64_t value, int flags); + ++/** ++ * Convenience wrapper for av_dict_get that converts the value to a pointer ++ * and stores it. ++ */ ++int av_dict_set_intptr(AVDictionary **pm, const char *key, uintptr_t value, int flags); ++uintptr_t av_dict_get_intptr(const AVDictionary *m, const char* key); ++uintptr_t av_dict_strtoptr(char * value); ++char * av_dict_ptrtostr(uintptr_t value); ++ + /** + * Parse the key/value pairs list and add the parsed entries to a dictionary. + * +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.2/0009-control-which-http-impl-was-used-by-set-selected_htt.patch b/patches/ffmpeg-n8.1.2/0009-control-which-http-impl-was-used-by-set-selected_htt.patch new file mode 100644 index 000000000..f8ae3cb75 --- /dev/null +++ b/patches/ffmpeg-n8.1.2/0009-control-which-http-impl-was-used-by-set-selected_htt.patch @@ -0,0 +1,129 @@ +From 5b78a77e4274c7aa8199ad5cc0aa1aa464b381c8 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Wed, 28 May 2025 18:25:51 +0800 +Subject: control which http impl was used by set selected_http + option + +--- + libavformat/avio.c | 63 ++++++++++++++++++++++++++++++++++++++++++++-- + libavformat/url.h | 16 ++++++++++++ + 2 files changed, 77 insertions(+), 2 deletions(-) + +diff --git a/libavformat/avio.c b/libavformat/avio.c +index b146ac9..0287cc6 100644 +--- a/libavformat/avio.c ++++ b/libavformat/avio.c +@@ -360,6 +360,65 @@ int ffurl_alloc(URLContext **puc, const char *filename, int flags, + return AVERROR_PROTOCOL_NOT_FOUND; + } + ++ ++static const struct URLProtocol *url_find_the_protocol(const char *proto_str) ++{ ++ const URLProtocol **protocols = ffurl_get_protocols(NULL, NULL); ++ if (!protocols) ++ return NULL; ++ for (int i = 0; protocols[i]; i++) { ++ const URLProtocol *up = protocols[i]; ++ if (!strcmp(proto_str, up->name)) { ++ av_freep(&protocols); ++ return up; ++ } ++ } ++ av_freep(&protocols); ++ return NULL; ++} ++ ++int ffurl_alloc2(URLContext **puc, const char *filename, int flags, ++ const AVIOInterruptCB *int_cb, AVDictionary **options) ++{ ++ if (options && *options) { ++ AVDictionaryEntry *e = av_dict_get(*options, "selected_http", NULL, 0); ++ const char *selected_http; ++ if (e && (selected_http = e->value)) { ++ ++ char proto_str[128] = {0}; ++ size_t proto_len = strspn(filename, URL_SCHEME_CHARS); ++ if (filename[proto_len] != ':' && ++ (strncmp(filename, "subfile,", 8) || !strchr(filename + proto_len + 1, ':')) || ++ is_dos_path(filename)) ++ strcpy(proto_str, "file"); ++ else ++ av_strlcpy(proto_str, filename, ++ FFMIN(proto_len + 1, sizeof(proto_str))); ++ //only apply http protocol ++ if (!strcmp(proto_str, "http") || !strcmp(proto_str, "https")) { ++ if (!strcmp(selected_http, "ijkhttp1") || !strcmp(selected_http, "ijkhttp2") || !strcmp(selected_http, "ijkhttp3")) { ++ const URLProtocol *p = url_find_the_protocol(selected_http); ++ if (p) { ++ av_log(NULL, AV_LOG_DEBUG, "%s use %s send request\n",proto_str,selected_http); ++ return url_alloc_for_protocol(puc, p, filename, flags, int_cb); ++ } ++ *puc = NULL; ++ av_log(NULL, AV_LOG_ERROR, "some thing is fault,check %s protocol\n", selected_http); ++ return AVERROR_PROTOCOL_NOT_FOUND; ++ } else { ++ av_log(NULL, AV_LOG_ERROR, "invalid selected_http value: %s\n", selected_http); ++ av_assert0(0); ++ return AVERROR_PROTOCOL_NOT_FOUND; ++ } ++ } else { ++ av_log(NULL, AV_LOG_DEBUG, "%s not use %s\n",proto_str,selected_http); ++ } ++ } ++ } ++ ++ return ffurl_alloc(puc, filename, flags, int_cb); ++} ++ + int ffurl_open_whitelist(URLContext **puc, const char *filename, int flags, + const AVIOInterruptCB *int_cb, AVDictionary **options, + const char *whitelist, const char* blacklist, +@@ -367,7 +426,7 @@ int ffurl_open_whitelist(URLContext **puc, const char *filename, int flags, + { + AVDictionary *tmp_opts = NULL; + AVDictionaryEntry *e; +- int ret = ffurl_alloc(puc, filename, flags, int_cb); ++ int ret = ffurl_alloc2(puc, filename, flags, int_cb, options); + if (ret < 0) + return ret; + if (parent) { +@@ -736,7 +795,7 @@ int avio_open_dir(AVIODirContext **s, const char *url, AVDictionary **options) + goto fail; + } + +- if ((ret = ffurl_alloc(&h, url, AVIO_FLAG_READ, NULL)) < 0) ++ if ((ret = ffurl_alloc2(&h, url, AVIO_FLAG_READ, NULL, options)) < 0) + goto fail; + + if (h->prot->url_open_dir && h->prot->url_read_dir && h->prot->url_close_dir) { +diff --git a/libavformat/url.h b/libavformat/url.h +index 0784d77..53c6f13 100644 +--- a/libavformat/url.h ++++ b/libavformat/url.h +@@ -112,6 +112,22 @@ typedef struct URLProtocol { + int ffurl_alloc(URLContext **puc, const char *filename, int flags, + const AVIOInterruptCB *int_cb); + ++ /** ++ * Create a URLContext for accessing to the resource indicated by ++ * url, but do not initiate the connection yet. ++ * ++ * @param puc pointer to the location where, in case of success, the ++ * function puts the pointer to the created URLContext ++ * @param flags flags which control how the resource indicated by url ++ * is to be opened ++ * @param int_cb interrupt callback to use for the URLContext, may be ++ * NULL ++ * @param options A dictionary filled with options for replace http protocol ++ * @return >= 0 in case of success, a negative value corresponding to an ++ * AVERROR code in case of failure ++ */ ++int ffurl_alloc2(URLContext **puc, const char *filename, int flags, ++ const AVIOInterruptCB *int_cb, AVDictionary **options); + /** + * Connect an URLContext that has been allocated by ffurl_alloc + * +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.2/0010-correct-file-seekable-value-range-0-means-streamed-c.patch b/patches/ffmpeg-n8.1.2/0010-correct-file-seekable-value-range-0-means-streamed-c.patch new file mode 100644 index 000000000..f75a71451 --- /dev/null +++ b/patches/ffmpeg-n8.1.2/0010-correct-file-seekable-value-range-0-means-streamed-c.patch @@ -0,0 +1,26 @@ +From a238a10d136f21902de411f024536b43c87cae95 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Fri, 8 May 2026 17:11:02 +0800 +Subject: correct file seekable value range, 0 means streamed + can't seek, 1 means not streamed can seek. + +--- + libavformat/file.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/libavformat/file.c b/libavformat/file.c +index 3ceddc8..59a0442 100644 +--- a/libavformat/file.c ++++ b/libavformat/file.c +@@ -106,7 +106,7 @@ static const AVOption file_options[] = { + { "truncate", "truncate existing files on write", offsetof(FileContext, trunc), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM }, + { "blocksize", "set I/O operation maximum block size", offsetof(FileContext, blocksize), AV_OPT_TYPE_INT, { .i64 = INT_MAX }, 1, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, + { "follow", "Follow a file as it is being written", offsetof(FileContext, follow), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, AV_OPT_FLAG_DECODING_PARAM }, +- { "seekable", "Sets if the file is seekable", offsetof(FileContext, seekable), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 0, AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_ENCODING_PARAM }, ++ { "seekable", "Sets if the file is seekable", offsetof(FileContext, seekable), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1, AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_ENCODING_PARAM }, + { "pkt_size", "Maximum packet size", offsetof(FileContext, pkt_size), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_ENCODING_PARAM }, + { NULL } + }; +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.2/0011-fix-lrcdec-read-line-bug-on-osx.patch b/patches/ffmpeg-n8.1.2/0011-fix-lrcdec-read-line-bug-on-osx.patch new file mode 100644 index 000000000..0e34977cc --- /dev/null +++ b/patches/ffmpeg-n8.1.2/0011-fix-lrcdec-read-line-bug-on-osx.patch @@ -0,0 +1,43 @@ +From becb7439eaa9ab67f5317a58dbe183bd94d4fcce Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Wed, 28 May 2025 18:39:38 +0800 +Subject: fix lrcdec read line bug on osx + +--- + libavformat/lrcdec.c | 20 +++++++++++--------- + 1 file changed, 11 insertions(+), 9 deletions(-) + +diff --git a/libavformat/lrcdec.c b/libavformat/lrcdec.c +index 1f96305..b758662 100644 +--- a/libavformat/lrcdec.c ++++ b/libavformat/lrcdec.c +@@ -107,15 +107,17 @@ static int64_t read_line(AVBPrint *buf, AVIOContext *pb) + int64_t pos = avio_tell(pb); + + av_bprint_clear(buf); +- while(!avio_feof(pb)) { +- int c = avio_r8(pb); +- if(c != '\r') { +- av_bprint_chars(buf, c, 1); +- } +- if(c == '\n') { +- break; +- } +- } ++ ff_read_line_to_bprint_overwrite(pb, buf); ++ ++ // while(!avio_feof(pb)) { ++ // int c = avio_r8(pb); ++ // if(c != '\r') { ++ // av_bprint_chars(buf, c, 1); ++ // } ++ // if(c == '\n') { ++ // break; ++ // } ++ // } + return pos; + } + +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.2/0012-avformat-mpegts-index-only-keyframes-to-ensure-accur.patch b/patches/ffmpeg-n8.1.2/0012-avformat-mpegts-index-only-keyframes-to-ensure-accur.patch new file mode 100644 index 000000000..6ccd438cd --- /dev/null +++ b/patches/ffmpeg-n8.1.2/0012-avformat-mpegts-index-only-keyframes-to-ensure-accur.patch @@ -0,0 +1,47 @@ +From 5aef56e86381c8151a63486524fd475536f64fe4 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Wed, 28 May 2025 18:48:39 +0800 +Subject: avformat/mpegts: index only keyframes to ensure + accurate seeks by default + +--- + libavformat/mpegts.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c +index 4b326d3..47944c7 100644 +--- a/libavformat/mpegts.c ++++ b/libavformat/mpegts.c +@@ -158,6 +158,7 @@ struct MpegTSContext { + + int skip_changes; + int skip_clear; ++ int seek_flag_keyframe; + int skip_unknown_pmt; + + int scan_all_pmts; +@@ -211,6 +212,8 @@ static const AVOption options[] = { + {.i64 = 0}, 0, 1, 0 }, + {"max_packet_size", "maximum size of emitted packet", offsetof(MpegTSContext, max_packet_size), AV_OPT_TYPE_INT, + {.i64 = 204800}, 1, INT_MAX/2, AV_OPT_FLAG_DECODING_PARAM }, ++ {"seek_flag_keyframe", "seek use keyframe mode", offsetof(MpegTSContext, seek_flag_keyframe), AV_OPT_TYPE_BOOL, ++ {.i64 = 1}, 0, 1, AV_OPT_FLAG_DECODING_PARAM }, + { NULL }, + }; + +@@ -3582,9 +3585,10 @@ static int64_t mpegts_get_dts(AVFormatContext *s, int stream_index, + av_packet_free(&pkt); + return AV_NOPTS_VALUE; + } +- if (pkt->dts != AV_NOPTS_VALUE && pkt->pos >= 0) { ++ ++ if (pkt->dts != AV_NOPTS_VALUE && pkt->pos >= 0 && (!ts->seek_flag_keyframe || (pkt->flags & AV_PKT_FLAG_KEY))) { + ff_reduce_index(s, pkt->stream_index); +- av_add_index_entry(s->streams[pkt->stream_index], pkt->pos, pkt->dts, 0, 0, AVINDEX_KEYFRAME /* FIXME keyframe? */); ++ av_add_index_entry(s->streams[pkt->stream_index], pkt->pos, pkt->dts, 0, 0, AVINDEX_KEYFRAME); + if (pkt->stream_index == stream_index && pkt->pos >= *ppos) { + int64_t dts = pkt->dts; + *ppos = pkt->pos; +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.2/0013-Correct-the-wrong-codecpar-codec_id-which-read-from-.patch b/patches/ffmpeg-n8.1.2/0013-Correct-the-wrong-codecpar-codec_id-which-read-from-.patch new file mode 100644 index 000000000..609d2c426 --- /dev/null +++ b/patches/ffmpeg-n8.1.2/0013-Correct-the-wrong-codecpar-codec_id-which-read-from-.patch @@ -0,0 +1,60 @@ +From ccea039f4d65a0f46eaf8c316ed602b95b3f6b90 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Wed, 28 May 2025 18:52:01 +0800 +Subject: Correct the wrong codecpar->codec_id which read from + MIME of ID3tags, but the real data was encoded in PNG/JPEG/TIFF + +--- + libavformat/id3v2.c | 7 ++++++- + libavformat/img2dec.c | 2 +- + libavformat/mov.c | 2 +- + 3 files changed, 8 insertions(+), 3 deletions(-) + +diff --git a/libavformat/id3v2.c b/libavformat/id3v2.c +index 5fc82ad..4ae12b6 100644 +--- a/libavformat/id3v2.c ++++ b/libavformat/id3v2.c +@@ -1187,8 +1187,13 @@ int ff_id3v2_parse_apic(AVFormatContext *s, ID3v2ExtraMeta *extra_meta) + st = s->streams[s->nb_streams - 1]; + st->codecpar->codec_id = apic->id; + +- if (AV_RB64(st->attached_pic.data) == PNGSIG) ++ if (AV_RB64(st->attached_pic.data) == PNGSIG || AV_RB64(st->attached_pic.data) == MNGSIG) { + st->codecpar->codec_id = AV_CODEC_ID_PNG; ++ } else if (AV_RB24(st->attached_pic.data) == 0xffd8ff) { ++ st->codecpar->codec_id = AV_CODEC_ID_MJPEG; ++ } else if (AV_RB32(st->attached_pic.data) == 0x49492a00 || AV_RB32(st->attached_pic.data) == 0x4D4D002a) { ++ st->codecpar->codec_id = AV_CODEC_ID_TIFF; ++ } + + if (apic->description[0]) + av_dict_set(&st->metadata, "title", apic->description, 0); +diff --git a/libavformat/img2dec.c b/libavformat/img2dec.c +index ecb7ea8..0a26d5a 100644 +--- a/libavformat/img2dec.c ++++ b/libavformat/img2dec.c +@@ -892,7 +892,7 @@ static int png_probe(const AVProbeData *p) + { + const uint8_t *b = p->buf; + +- if (AV_RB64(b) == 0x89504e470d0a1a0a) ++ if (AV_RB64(b) == 0x89504e470d0a1a0a || AV_RB64(b) == 0x8a4d4e470d0a1a0a) + return AVPROBE_SCORE_MAX - 1; + return 0; + } +diff --git a/libavformat/mov.c b/libavformat/mov.c +index 222d79e..e114770 100644 +--- a/libavformat/mov.c ++++ b/libavformat/mov.c +@@ -259,7 +259,7 @@ static int mov_read_covr(MOVContext *c, AVIOContext *pb, int type, int len) + sc->refcount = 1; + + if (st->attached_pic.size >= 8 && id != AV_CODEC_ID_BMP) { +- if (AV_RB64(st->attached_pic.data) == 0x89504e470d0a1a0a) { ++ if (AV_RB64(st->attached_pic.data) == 0x89504e470d0a1a0a || AV_RB64(st->attached_pic.data) == 0x8a4d4e470d0a1a0a) { + id = AV_CODEC_ID_PNG; + } else { + id = AV_CODEC_ID_MJPEG; +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.2/0014-fix-http-chunked-transfer-get-wrong-size-cause-av_re.patch b/patches/ffmpeg-n8.1.2/0014-fix-http-chunked-transfer-get-wrong-size-cause-av_re.patch new file mode 100644 index 000000000..8e52c2f14 --- /dev/null +++ b/patches/ffmpeg-n8.1.2/0014-fix-http-chunked-transfer-get-wrong-size-cause-av_re.patch @@ -0,0 +1,32 @@ +From 74355a6170c59a122461ff0adeccaf178eeaf602 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Thu, 29 May 2025 08:56:02 +0800 +Subject: fix http chunked transfer get wrong size cause + av_read_frame can not return eof bug + +--- + libavformat/img2dec.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/libavformat/img2dec.c b/libavformat/img2dec.c +index 0a26d5a..088c219 100644 +--- a/libavformat/img2dec.c ++++ b/libavformat/img2dec.c +@@ -451,7 +451,13 @@ int ff_img_read_packet(AVFormatContext *s1, AVPacket *pkt) + if (s->frame_size > 0) { + size[0] = s->frame_size; + } else if (!ffstream(s1->streams[0])->parser) { +- size[0] = avio_size(s1->pb); ++ //http Transfer-Encoding: chunked the size is -78; ++ int64_t s = avio_size(s1->pb); ++ if (s < 0) { ++ size[0] = 4096; ++ } else { ++ size[0] = s; ++ } + } else { + size[0] = 4096; + } +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.2/0015-not-very-useful-log-use-trace-level.patch b/patches/ffmpeg-n8.1.2/0015-not-very-useful-log-use-trace-level.patch new file mode 100644 index 000000000..a729faba4 --- /dev/null +++ b/patches/ffmpeg-n8.1.2/0015-not-very-useful-log-use-trace-level.patch @@ -0,0 +1,76 @@ +From 6a0647fe06239adcca0d09afe5ac0e78cc0224aa Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Thu, 29 May 2025 09:02:16 +0800 +Subject: not very useful log use trace level + +--- + libavcodec/h2645_parse.c | 6 +++--- + libavcodec/h2645_vui.c | 2 +- + libavformat/demux.c | 4 ++-- + 3 files changed, 6 insertions(+), 6 deletions(-) + +diff --git a/libavcodec/h2645_parse.c b/libavcodec/h2645_parse.c +index 659e818..63f3460 100644 +--- a/libavcodec/h2645_parse.c ++++ b/libavcodec/h2645_parse.c +@@ -417,7 +417,7 @@ static int vvc_parse_nal_header(H2645NAL *nal, void *logctx) + if ((nal->type >= VVC_IDR_W_RADL && nal->type <= VVC_RSV_IRAP_11) && nal->temporal_id) + return AVERROR_INVALIDDATA; + +- av_log(logctx, AV_LOG_DEBUG, ++ av_log(logctx, AV_LOG_TRACE, + "nal_unit_type: %d(%s), nuh_layer_id: %d, temporal_id: %d\n", + nal->type, vvc_nal_unit_name(nal->type), nal->nuh_layer_id, nal->temporal_id); + +@@ -438,7 +438,7 @@ static int hevc_parse_nal_header(H2645NAL *nal, void *logctx) + if (nal->temporal_id < 0) + return AVERROR_INVALIDDATA; + +- av_log(logctx, AV_LOG_DEBUG, ++ av_log(logctx, AV_LOG_TRACE, + "nal_unit_type: %d(%s), nuh_layer_id: %d, temporal_id: %d\n", + nal->type, hevc_nal_unit_name(nal->type), nal->nuh_layer_id, nal->temporal_id); + +@@ -455,7 +455,7 @@ static int h264_parse_nal_header(H2645NAL *nal, void *logctx) + nal->ref_idc = get_bits(gb, 2); + nal->type = get_bits(gb, 5); + +- av_log(logctx, AV_LOG_DEBUG, ++ av_log(logctx, AV_LOG_TRACE, + "nal_unit_type: %d(%s), nal_ref_idc: %d\n", + nal->type, h264_nal_unit_name(nal->type), nal->ref_idc); + +diff --git a/libavcodec/h2645_vui.c b/libavcodec/h2645_vui.c +index 0e576c1..445e471 100644 +--- a/libavcodec/h2645_vui.c ++++ b/libavcodec/h2645_vui.c +@@ -36,7 +36,7 @@ + + void ff_h2645_decode_common_vui_params(GetBitContext *gb, H2645VUI *vui, void *logctx) + { +- av_log(logctx, AV_LOG_DEBUG, "Decoding VUI\n"); ++ av_log(logctx, AV_LOG_TRACE, "Decoding VUI\n"); + + vui->aspect_ratio_info_present_flag = get_bits1(gb); + if (vui->aspect_ratio_info_present_flag) { +diff --git a/libavformat/demux.c b/libavformat/demux.c +index 05ab1e1..494e15f 100644 +--- a/libavformat/demux.c ++++ b/libavformat/demux.c +@@ -2074,11 +2074,11 @@ static void estimate_timings(AVFormatContext *ic, int64_t old_offset) + for (unsigned i = 0; i < ic->nb_streams; i++) { + AVStream *const st = ic->streams[i]; + if (st->time_base.den) +- av_log(ic, AV_LOG_TRACE, "stream %u: start_time: %s duration: %s\n", i, ++ av_log(ic, AV_LOG_DEBUG, "stream %u: start_time: %s duration: %s\n", i, + av_ts2timestr(st->start_time, &st->time_base), + av_ts2timestr(st->duration, &st->time_base)); + } +- av_log(ic, AV_LOG_TRACE, ++ av_log(ic, AV_LOG_DEBUG, + "format: start_time: %s duration: %s (estimate from %s) bitrate=%"PRId64" kb/s\n", + av_ts2timestr(ic->start_time, &AV_TIME_BASE_Q), + av_ts2timestr(ic->duration, &AV_TIME_BASE_Q), +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.2/0016-Audio-Vivid-Parser-and-Demuxer-but-av3a-Decoder-is-a.patch b/patches/ffmpeg-n8.1.2/0016-Audio-Vivid-Parser-and-Demuxer-but-av3a-Decoder-is-a.patch new file mode 100644 index 000000000..2f94cc8b0 --- /dev/null +++ b/patches/ffmpeg-n8.1.2/0016-Audio-Vivid-Parser-and-Demuxer-but-av3a-Decoder-is-a.patch @@ -0,0 +1,1341 @@ +From 0b9dacdb9282e956e0cecb0d7d5df7f5aa41f8cc Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Fri, 8 May 2026 19:00:36 +0800 +Subject: Audio Vivid Parser and Demuxer, but av3a Decoder is + absent + +--- + libavcodec/Makefile | 1 + + libavcodec/av3a.h | 314 ++++++++++++++++++++++++++ + libavcodec/av3a_parser.c | 219 ++++++++++++++++++ + libavcodec/codec_desc.c | 7 + + libavcodec/codec_id.h | 1 + + libavcodec/parsers.c | 1 + + libavcodec/utils.c | 1 + + libavformat/Makefile | 1 + + libavformat/allformats.c | 1 + + libavformat/av3adec.c | 473 +++++++++++++++++++++++++++++++++++++++ + libavformat/isom_tags.c | 1 + + libavformat/mov.c | 124 ++++++++++ + libavformat/mpegts.c | 6 + + libavformat/mpegts.h | 1 + + 14 files changed, 1151 insertions(+) + create mode 100644 libavcodec/av3a.h + create mode 100644 libavcodec/av3a_parser.c + create mode 100644 libavformat/av3adec.c + +diff --git a/libavcodec/Makefile b/libavcodec/Makefile +index 4445d9e..c51a860 100644 +--- a/libavcodec/Makefile ++++ b/libavcodec/Makefile +@@ -1247,6 +1247,7 @@ OBJS-$(CONFIG_APV_PARSER) += apv_parser.o + OBJS-$(CONFIG_AV1_PARSER) += av1_parser.o av1_parse.o + OBJS-$(CONFIG_AVS2_PARSER) += avs2.o avs2_parser.o + OBJS-$(CONFIG_AVS3_PARSER) += avs3_parser.o ++OBJS-$(CONFIG_AV3A_PARSER) += av3a_parser.o + OBJS-$(CONFIG_BMP_PARSER) += bmp_parser.o + OBJS-$(CONFIG_CAVSVIDEO_PARSER) += cavs_parser.o + OBJS-$(CONFIG_COOK_PARSER) += cook_parser.o +diff --git a/libavcodec/av3a.h b/libavcodec/av3a.h +new file mode 100644 +index 0000000..14dc349 +--- /dev/null ++++ b/libavcodec/av3a.h +@@ -0,0 +1,314 @@ ++/* ++ * AV3A Common Header File ++ * ++ * Copyright (c) 2024 Shuai Liu ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg 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. ++ * ++ * FFmpeg 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 FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#ifndef AVCODEC_AV3A_H ++#define AVCODEC_AV3A_H ++ ++#include "libavutil/samplefmt.h" ++#include "libavutil/channel_layout.h" ++ ++/* AATF header */ ++#define AV3A_MAX_NBYTES_HEADER 9 ++#define AV3A_AUDIO_SYNC_WORD 0xFFF ++#define AV3A_AUDIO_FRAME_SIZE 1024 ++#define AV3A_CHANNEL_LAYOUT_SIZE 15 ++#define AV3A_BITRATE_TABLE_SIZE 16 ++#define AV3A_FS_TABLE_SIZE 9 ++#define AV3A_RESOLUTION_TABLE_SIZE 3 ++#define AV3A_DCA3_BOX_MIN_SIZE 5 ++ ++/* Channel Layout */ ++#define AV3A_CH_LAYOUT_MONO (AV_CH_LAYOUT_MONO) ++#define AV3A_CH_LAYOUT_STEREO (AV_CH_LAYOUT_STEREO) ++#define AV3A_CH_LAYOUT_4POINT0 (AV3A_CH_LAYOUT_STEREO|AV_CH_FRONT_CENTER|AV_CH_BACK_CENTER) ++#define AV3A_CH_LAYOUT_5POINT1 (AV_CH_LAYOUT_5POINT1) ++#define AV3A_CH_LAYOUT_7POINT1 (AV_CH_LAYOUT_5POINT1|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT) ++#define AV3A_CH_LAYOUT_5POINT1POINT2 (AV_CH_LAYOUT_5POINT1|AV_CH_TOP_SIDE_LEFT|AV_CH_TOP_SIDE_RIGHT) ++#define AV3A_CH_LAYOUT_7POINT1POINT2 (AV3A_CH_LAYOUT_7POINT1|AV_CH_TOP_SIDE_LEFT|AV_CH_TOP_SIDE_RIGHT) ++#define AV3A_CH_LAYOUT_5POINT1POINT4 (AV_CH_LAYOUT_5POINT1|AV_CH_TOP_FRONT_LEFT|AV_CH_TOP_FRONT_RIGHT|AV_CH_TOP_BACK_LEFT|AV_CH_TOP_BACK_RIGHT) ++#define AV3A_CH_LAYOUT_7POINT1POINT4 (AV3A_CH_LAYOUT_7POINT1|AV_CH_TOP_FRONT_LEFT|AV_CH_TOP_FRONT_RIGHT|AV_CH_TOP_BACK_LEFT|AV_CH_TOP_BACK_RIGHT) ++#define AV3A_CH_AUDIO_OBJECT (AV_CHAN_UNKNOWN) ++ ++/* AV3A Codec ID */ ++typedef enum { ++ AV3A_LOSSLESS_CODEC_ID = 1, ++ AV3A_LOSSY_CODEC_ID = 2 ++} Av3aCodecId; ++ ++/* Content Type */ ++typedef enum { ++ AV3A_CHANNEL_BASED_TYPE = 0, ++ AV3A_OBJECT_BASED_TYPE = 1, ++ AV3A_CHANNEL_OBJECT_TYPE = 2, ++ AV3A_AMBISONIC_TYPE = 3 ++} Av3aContentType; ++ ++/* Internal Coding Profile */ ++typedef enum { ++ AV3A_BASE_PROFILE = 0, ++ AV3A_OBJECT_METADATA_PROFILE = 1, ++ AV3A_AMBISONIC_PROFILE = 2 ++} Av3aCodingProfile; ++ ++/* NN Type */ ++typedef enum { ++ AV3A_BASELINE_NN_TYPE = 0, ++ AV3A_LC_NN_TYPE = 1 ++} Av3aNeuralNetworkType; ++ ++/* AV3A Channel Configuration */ ++typedef enum { ++ CHANNEL_CONFIG_MONO = 0, /* Mono = 0 */ ++ CHANNEL_CONFIG_STEREO = 1, /* Stereo = 1 */ ++ CHANNEL_CONFIG_MC_5_1 = 2, /* 5.1 = 2 */ ++ CHANNEL_CONFIG_MC_7_1 = 3, /* 7.1 = 3 */ ++ CHANNEL_CONFIG_MC_10_2 = 4, /* 10.2 = 4 */ ++ CHANNEL_CONFIG_MC_22_2 = 5, /* 22.2 = 5 */ ++ CHANNEL_CONFIG_MC_4_0 = 6, /* 4.0 = 6 */ ++ CHANNEL_CONFIG_MC_5_1_2 = 7, /* 5.1.2 = 7 */ ++ CHANNEL_CONFIG_MC_5_1_4 = 8, /* 5.1.4 = 8 */ ++ CHANNEL_CONFIG_MC_7_1_2 = 9, /* 7.1.2 = 9 */ ++ CHANNEL_CONFIG_MC_7_1_4 = 10, /* 7.1.4 = 10 */ ++ CHANNEL_CONFIG_HOA_ORDER1 = 11, /* HOA1 = 11 */ ++ CHANNEL_CONFIG_HOA_ORDER2 = 12, /* HOA2 = 12 */ ++ CHANNEL_CONFIG_HOA_ORDER3 = 13, /* HOA3 = 13 */ ++ CHANNEL_CONFIG_UNKNOWN = 14 /* UNKNOWN = 14 */ ++} Av3aChannelConfig; ++ ++typedef enum { ++ AV3A_AMBISONIC_FIRST_ORDER = 1, ++ AV3A_AMBISONIC_SECOND_ORDER = 2, ++ AV3A_AMBISONIC_THIRD_ORDER = 3 ++} Av3aAmbisonicOrder; ++ ++typedef struct { ++ int16_t sync_word; /* sync word */ ++ int16_t audio_codec_id; /* audio codec id */ ++ int16_t anc_data; /* anc data */ ++ int16_t nn_type; /* neural network type */ ++ int16_t coding_profile; /* coding profile */ ++ int16_t sampling_frequency_index; /* sampling frequency index */ ++ int16_t channel_number_index; /* channel number index */ ++ int16_t bitrate_index; /* bitrate index */ ++ int16_t soundbed_type; /* soundbed type */ ++ int16_t object_channel_number; /* object channel number */ ++ int16_t bitrate_index_per_channel; /* bitrate per object */ ++ int16_t order; /* ambisonics order */ ++ int16_t resolution_index; /* resolution index */ ++ ++ int32_t sampling_rate; /* sampling rate */ ++ int64_t total_bitrate; /* total bitrate */ ++ int16_t sample_format; /* sample format */ ++ int16_t resolution; /* resolution */ ++ int16_t content_type; /* internal content type */ ++ int16_t nb_channels; /* number of channels (channel configuration) */ ++ int16_t nb_objects; /* number of objects (object_channel_number + 1) */ ++ int16_t total_channels; /* total channels */ ++ int16_t hoa_order; /* ambisonic order (order + 1) */ ++ int32_t ch_layout_mask; /* channel layout mask */ ++} AATFHeaderInfo; ++ ++/* bitrate table for mono */ ++static const int64_t ff_av3a_mono_bitrate_table[AV3A_BITRATE_TABLE_SIZE] = { ++ 16000, 32000, 44000, 56000, 64000, 72000, 80000, 96000, 128000, 144000, ++ 164000, 192000, 0, 0, 0, 0 ++}; ++ ++/* bitrate table for stereo */ ++static const int64_t ff_av3a_stereo_bitrate_table[AV3A_BITRATE_TABLE_SIZE] = { ++ 24000, 32000, 48000, 64000, 80000, 96000, 128000, 144000, 192000, 256000, ++ 320000, 0, 0, 0, 0, 0 ++}; ++ ++/* bitrate table for MC 5.1 */ ++static const int64_t ff_av3a_mc5p1_bitrate_table[AV3A_BITRATE_TABLE_SIZE] = { ++ 192000, 256000, 320000, 384000, 448000, 512000, 640000, 720000, 144000, 96000, ++ 128000, 160000, 0, 0, 0, 0 ++}; ++ ++/* bitrate table for MC 7.1 */ ++static const int64_t ff_av3a_mc7p1_bitrate_table[AV3A_BITRATE_TABLE_SIZE] = { ++ 192000, 480000, 256000, 384000, 576000, 640000, 128000, 160000, 0, 0, ++ 0, 0, 0, 0, 0, 0 ++}; ++ ++/* bitrate table for MC 4.0 */ ++static const int64_t ff_av3a_mc4p0_bitrate_table[AV3A_BITRATE_TABLE_SIZE] = { ++ 48000, 96000, 128000, 192000, 256000, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0 ++}; ++ ++/* bitrate table for MC 5.1.2 */ ++static const int64_t ff_av3a_mc5p1p2_bitrate_table[AV3A_BITRATE_TABLE_SIZE] = { ++ 152000, 320000, 480000, 576000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ++}; ++ ++/* bitrate table for MC 5.1.4 */ ++static const int64_t ff_av3a_mc5p1p4_bitrate_table[AV3A_BITRATE_TABLE_SIZE] = { ++ 176000, 384000, 576000, 704000, 256000, 448000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ++}; ++ ++/* bitrate table for MC 7.1.2 */ ++static const int64_t ff_av3a_mc7p1p2_bitrate_table[AV3A_BITRATE_TABLE_SIZE] = { ++ 216000, 480000, 576000, 384000, 768000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ++}; ++ ++/* bitrate table for MC 7.1.4 */ ++static const int64_t ff_av3a_mc7p1p4_bitrate_table[AV3A_BITRATE_TABLE_SIZE] = { ++ 240000, 608000, 384000, 512000, 832000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ++}; ++ ++/* bitrate table for FOA */ ++static const int64_t ff_av3a_foa_bitrate_table[AV3A_BITRATE_TABLE_SIZE] = { ++ 48000, 96000, 128000, 192000, 256000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ++}; ++ ++/* bitrate table for HOA2 */ ++static const int64_t ff_av3a_hoa2_bitrate_table[AV3A_BITRATE_TABLE_SIZE] = { ++ 192000, 256000, 320000, 384000, 480000, 512000, 640000, 0, 0, 0, 0, 0, 0, 0, 0, 0 ++}; ++ ++/* bitrate table for HOA3 */ ++static const int64_t ff_av3a_hoa3_bitrate_table[AV3A_BITRATE_TABLE_SIZE] = { ++ 256000, 320000, 384000, 512000, 640000, 896000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; ++ ++static const int32_t ff_av3a_sampling_rate_table[AV3A_FS_TABLE_SIZE] = { ++ 192000, 96000, 48000, 44100, 32000, 24000, 22050, 16000, 8000 ++}; ++ ++typedef struct { ++ int16_t resolution; ++ enum AVSampleFormat sample_format; ++} Av3aSampleFormatMap; ++ ++static const Av3aSampleFormatMap ff_av3a_sample_format_map_table[AV3A_RESOLUTION_TABLE_SIZE] = { ++ {8, AV_SAMPLE_FMT_U8 }, /* 0: 8 bits */ ++ {16, AV_SAMPLE_FMT_S16}, /* 1: 16 bits */ ++ {24, AV_SAMPLE_FMT_S32}, /* 2: 24 bits */ ++}; ++ ++typedef struct { ++ Av3aChannelConfig channel_number_index; ++ int16_t channels; ++ const enum AVChannel* channel_layout; ++ uint64_t mask; ++} Av3aChannelConfigMap; ++ ++static const enum AVChannel ff_av3a_default_channel_layout_mono[1] = { ++ AV_CHAN_FRONT_CENTER ++}; ++ ++static const enum AVChannel ff_av3a_default_channel_layout_stereo[2] = { ++ AV_CHAN_FRONT_LEFT, AV_CHAN_FRONT_RIGHT ++}; ++ ++static const enum AVChannel ff_av3a_channel_layout_mc_4_0[4] = { ++ AV_CHAN_FRONT_LEFT, AV_CHAN_FRONT_RIGHT, ++ AV_CHAN_FRONT_CENTER, AV_CHAN_BACK_CENTER ++}; ++ ++static const enum AVChannel ff_av3a_default_channel_layout_mc_5_1[6] = { ++ AV_CHAN_FRONT_LEFT, AV_CHAN_FRONT_RIGHT, AV_CHAN_FRONT_CENTER, ++ AV_CHAN_LOW_FREQUENCY, ++ AV_CHAN_SIDE_LEFT, AV_CHAN_SIDE_RIGHT ++}; ++ ++static const enum AVChannel ff_av3a_default_channel_layout_mc_5_1_2[8] = { ++ AV_CHAN_FRONT_LEFT, AV_CHAN_FRONT_RIGHT, AV_CHAN_FRONT_CENTER, ++ AV_CHAN_LOW_FREQUENCY, ++ AV_CHAN_SIDE_LEFT, AV_CHAN_SIDE_RIGHT, ++ AV_CHAN_TOP_SIDE_LEFT, AV_CHAN_TOP_SIDE_RIGHT ++}; ++ ++static const enum AVChannel ff_av3a_default_channel_layout_mc_7_1[8] = { ++ AV_CHAN_FRONT_LEFT, AV_CHAN_FRONT_RIGHT, AV_CHAN_FRONT_CENTER, ++ AV_CHAN_LOW_FREQUENCY, ++ AV_CHAN_SIDE_LEFT, AV_CHAN_SIDE_RIGHT, ++ AV_CHAN_BACK_LEFT, AV_CHAN_BACK_RIGHT ++}; ++ ++static const enum AVChannel ff_av3a_default_channel_layout_mc_5_1_4[10] = { ++ AV_CHAN_FRONT_LEFT, AV_CHAN_FRONT_RIGHT, AV_CHAN_FRONT_CENTER, ++ AV_CHAN_LOW_FREQUENCY, ++ AV_CHAN_SIDE_LEFT, AV_CHAN_SIDE_RIGHT, ++ AV_CHAN_TOP_FRONT_LEFT, AV_CHAN_TOP_FRONT_RIGHT, ++ AV_CHAN_TOP_BACK_LEFT, AV_CHAN_TOP_BACK_RIGHT ++}; ++ ++static const enum AVChannel ff_av3a_default_channel_layout_mc_7_1_2[10] = { ++ AV_CHAN_FRONT_LEFT, AV_CHAN_FRONT_RIGHT, AV_CHAN_FRONT_CENTER, ++ AV_CHAN_LOW_FREQUENCY, ++ AV_CHAN_SIDE_LEFT, AV_CHAN_SIDE_RIGHT, ++ AV_CHAN_BACK_LEFT, AV_CHAN_BACK_RIGHT, ++ AV_CHAN_TOP_SIDE_LEFT, AV_CHAN_TOP_SIDE_RIGHT ++}; ++ ++static const enum AVChannel ff_av3a_default_channel_layout_mc_7_1_4[12] = { ++ AV_CHAN_FRONT_LEFT, AV_CHAN_FRONT_RIGHT, AV_CHAN_FRONT_CENTER, ++ AV_CHAN_LOW_FREQUENCY, ++ AV_CHAN_SIDE_LEFT, AV_CHAN_SIDE_RIGHT, ++ AV_CHAN_BACK_LEFT, AV_CHAN_BACK_RIGHT, ++ AV_CHAN_TOP_FRONT_LEFT, AV_CHAN_TOP_FRONT_RIGHT, ++ AV_CHAN_TOP_BACK_LEFT, AV_CHAN_TOP_BACK_RIGHT ++}; ++ ++static const Av3aChannelConfigMap ff_av3a_channels_map_table[AV3A_CHANNEL_LAYOUT_SIZE] = { ++ { CHANNEL_CONFIG_MONO, 1, ff_av3a_default_channel_layout_mono, AV3A_CH_LAYOUT_MONO }, ++ { CHANNEL_CONFIG_STEREO, 2, ff_av3a_default_channel_layout_stereo, AV3A_CH_LAYOUT_STEREO }, ++ { CHANNEL_CONFIG_MC_5_1, 6, ff_av3a_default_channel_layout_mc_5_1, AV3A_CH_LAYOUT_5POINT1 }, ++ { CHANNEL_CONFIG_MC_7_1, 8, ff_av3a_default_channel_layout_mc_7_1, AV3A_CH_LAYOUT_7POINT1 }, ++ { CHANNEL_CONFIG_MC_10_2, 12, NULL, 0L }, /* reserved */ ++ { CHANNEL_CONFIG_MC_22_2, 24, NULL, 0L }, /* reserved */ ++ { CHANNEL_CONFIG_MC_4_0, 4, ff_av3a_channel_layout_mc_4_0, AV3A_CH_LAYOUT_4POINT0 }, ++ { CHANNEL_CONFIG_MC_5_1_2, 8, ff_av3a_default_channel_layout_mc_5_1_2, AV3A_CH_LAYOUT_5POINT1POINT2 }, ++ { CHANNEL_CONFIG_MC_5_1_4, 10, ff_av3a_default_channel_layout_mc_5_1_4, AV3A_CH_LAYOUT_5POINT1POINT4 }, ++ { CHANNEL_CONFIG_MC_7_1_2, 10, ff_av3a_default_channel_layout_mc_7_1_2, AV3A_CH_LAYOUT_7POINT1POINT2 }, ++ { CHANNEL_CONFIG_MC_7_1_4, 12, ff_av3a_default_channel_layout_mc_7_1_4, AV3A_CH_LAYOUT_7POINT1POINT4 }, ++ { CHANNEL_CONFIG_HOA_ORDER1, 4, NULL, 0L }, ++ { CHANNEL_CONFIG_HOA_ORDER2, 9, NULL, 0L }, ++ { CHANNEL_CONFIG_HOA_ORDER3, 16, NULL, 0L }, ++ { CHANNEL_CONFIG_UNKNOWN, 0, NULL, 0L }, ++}; ++ ++typedef struct { ++ Av3aChannelConfig channel_number_index; ++ const int64_t *bitrate_table; ++} Av3aBitrateMap; ++ ++static const Av3aBitrateMap ff_av3a_bitrate_map_table[15] = { ++ {CHANNEL_CONFIG_MONO, ff_av3a_mono_bitrate_table }, ++ {CHANNEL_CONFIG_STEREO, ff_av3a_stereo_bitrate_table }, ++ {CHANNEL_CONFIG_MC_5_1, ff_av3a_mc5p1_bitrate_table }, ++ {CHANNEL_CONFIG_MC_7_1, ff_av3a_mc7p1_bitrate_table }, ++ {CHANNEL_CONFIG_MC_10_2, NULL }, /* reserved */ ++ {CHANNEL_CONFIG_MC_22_2, NULL }, /* reserved */ ++ {CHANNEL_CONFIG_MC_4_0, ff_av3a_mc4p0_bitrate_table }, ++ {CHANNEL_CONFIG_MC_5_1_2, ff_av3a_mc5p1p2_bitrate_table }, ++ {CHANNEL_CONFIG_MC_5_1_4, ff_av3a_mc5p1p4_bitrate_table }, ++ {CHANNEL_CONFIG_MC_7_1_2, ff_av3a_mc7p1p2_bitrate_table }, ++ {CHANNEL_CONFIG_MC_7_1_4, ff_av3a_mc7p1p4_bitrate_table }, ++ {CHANNEL_CONFIG_HOA_ORDER1, ff_av3a_foa_bitrate_table }, ++ {CHANNEL_CONFIG_HOA_ORDER2, ff_av3a_hoa2_bitrate_table }, ++ {CHANNEL_CONFIG_HOA_ORDER3, ff_av3a_hoa3_bitrate_table }, ++ {CHANNEL_CONFIG_UNKNOWN, NULL }, ++}; ++#endif /* AVCODEC_AV3A_H */ +diff --git a/libavcodec/av3a_parser.c b/libavcodec/av3a_parser.c +new file mode 100644 +index 0000000..66d461e +--- /dev/null ++++ b/libavcodec/av3a_parser.c +@@ -0,0 +1,219 @@ ++/* ++ * AV3A Parser ++ * ++ * Copyright (c) 2024 Shuai Liu ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg 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. ++ * ++ * FFmpeg 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 FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include ++#include ++#include "libavutil/intreadwrite.h" ++#include "parser.h" ++#include "get_bits.h" ++#include "av3a.h" ++#include "parser_internal.h" ++ ++typedef struct { ++ int16_t audio_codec_id; ++ int16_t nn_type; ++ int16_t frame_size; ++ int16_t resolution; ++ int32_t sample_rate; ++ int64_t bit_rate; ++ ++ int16_t content_type; ++ int16_t channel_number_index; ++ int16_t nb_channels; ++ int16_t nb_objects; ++ int16_t total_channels; ++} Av3aParseContext; ++ ++static int ff_read_av3a_header_parse(GetBitContext *gb, AATFHeaderInfo *hdf) ++{ ++ int64_t soundbed_bitrate = 0L; ++ int64_t object_bitrate = 0L; ++ ++ hdf->nb_channels = 0; ++ hdf->nb_objects = 0; ++ ++ hdf->sync_word = get_bits(gb, 12); ++ if (hdf->sync_word != AV3A_AUDIO_SYNC_WORD) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ hdf->audio_codec_id = get_bits(gb, 4); ++ if (hdf->audio_codec_id != AV3A_LOSSY_CODEC_ID) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ skip_bits(gb, 1); /* skip anc_data 1 bit */ ++ ++ hdf->nn_type = get_bits(gb, 3); ++ if ((hdf->nn_type > AV3A_LC_NN_TYPE) || (hdf->nn_type < AV3A_BASELINE_NN_TYPE)) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ hdf->coding_profile = get_bits(gb, 3); ++ ++ hdf->sampling_frequency_index = get_bits(gb, 4); ++ if ((hdf->sampling_frequency_index >= AV3A_FS_TABLE_SIZE) || (hdf->sampling_frequency_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->sampling_rate = ff_av3a_sampling_rate_table[hdf->sampling_frequency_index]; ++ ++ skip_bits(gb, 8); /* skip CRC 8 bits */ ++ ++ if (hdf->coding_profile == AV3A_BASE_PROFILE) { ++ hdf->content_type = AV3A_CHANNEL_BASED_TYPE; ++ hdf->channel_number_index = get_bits(gb, 7); ++ if ((hdf->channel_number_index >= CHANNEL_CONFIG_UNKNOWN) || ++ (hdf->channel_number_index == CHANNEL_CONFIG_MC_10_2) || ++ (hdf->channel_number_index == CHANNEL_CONFIG_MC_22_2) || ++ (hdf->channel_number_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->nb_channels = ff_av3a_channels_map_table[hdf->channel_number_index].channels; ++ } else if (hdf->coding_profile == AV3A_OBJECT_METADATA_PROFILE) { ++ hdf->soundbed_type = get_bits(gb, 2); ++ if (hdf->soundbed_type == 0) { ++ hdf->content_type = AV3A_OBJECT_BASED_TYPE; ++ hdf->object_channel_number = get_bits(gb, 7); ++ if (hdf->object_channel_number < 0) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->nb_objects = hdf->object_channel_number + 1; ++ ++ hdf->bitrate_index_per_channel = get_bits(gb, 4); ++ if ((hdf->bitrate_index_per_channel >= AV3A_BITRATE_TABLE_SIZE) || (hdf->bitrate_index_per_channel < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ object_bitrate = ff_av3a_bitrate_map_table[CHANNEL_CONFIG_MONO].bitrate_table[hdf->bitrate_index_per_channel]; ++ hdf->total_bitrate = object_bitrate * hdf->nb_objects; ++ } else if (hdf->soundbed_type == 1) { ++ hdf->content_type = AV3A_CHANNEL_OBJECT_TYPE; ++ hdf->channel_number_index = get_bits(gb, 7); ++ if ((hdf->channel_number_index >= CHANNEL_CONFIG_UNKNOWN) || ++ (hdf->channel_number_index == CHANNEL_CONFIG_MC_10_2) || ++ (hdf->channel_number_index == CHANNEL_CONFIG_MC_22_2) || ++ (hdf->channel_number_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ hdf->bitrate_index = get_bits(gb, 4); ++ if ((hdf->bitrate_index >= AV3A_BITRATE_TABLE_SIZE) || (hdf->bitrate_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->nb_channels = ff_av3a_channels_map_table[hdf->channel_number_index].channels; ++ soundbed_bitrate = ff_av3a_bitrate_map_table[hdf->channel_number_index].bitrate_table[hdf->bitrate_index]; ++ ++ hdf->object_channel_number = get_bits(gb, 7); ++ if (hdf->object_channel_number < 0) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->bitrate_index_per_channel = get_bits(gb, 4); ++ if ((hdf->bitrate_index_per_channel >= AV3A_BITRATE_TABLE_SIZE) || (hdf->bitrate_index_per_channel < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->nb_objects = hdf->object_channel_number + 1; ++ object_bitrate = ff_av3a_bitrate_map_table[CHANNEL_CONFIG_MONO].bitrate_table[hdf->bitrate_index_per_channel]; ++ hdf->total_bitrate = soundbed_bitrate + (object_bitrate * hdf->nb_objects); ++ } else { ++ return AVERROR_INVALIDDATA; ++ } ++ } else if (hdf->coding_profile == AV3A_AMBISONIC_PROFILE) { ++ hdf->content_type = AV3A_AMBISONIC_TYPE; ++ hdf->order = get_bits(gb, 4); ++ hdf->hoa_order = hdf->order + 1; ++ ++ switch (hdf->hoa_order) { ++ case AV3A_AMBISONIC_FIRST_ORDER: ++ hdf->channel_number_index = CHANNEL_CONFIG_HOA_ORDER1; ++ break; ++ case AV3A_AMBISONIC_SECOND_ORDER: ++ hdf->channel_number_index = CHANNEL_CONFIG_HOA_ORDER2; ++ break; ++ case AV3A_AMBISONIC_THIRD_ORDER: ++ hdf->channel_number_index = CHANNEL_CONFIG_HOA_ORDER3; ++ break; ++ default: ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->nb_channels = ff_av3a_channels_map_table[hdf->channel_number_index].channels; ++ } else { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->total_channels = hdf->nb_channels + hdf->nb_objects; ++ ++ hdf->resolution_index = get_bits(gb, 2); ++ if ((hdf->resolution_index >= AV3A_RESOLUTION_TABLE_SIZE) || (hdf->resolution_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->resolution = ff_av3a_sample_format_map_table[hdf->resolution_index].resolution; ++ hdf->sample_format = ff_av3a_sample_format_map_table[hdf->resolution_index].sample_format; ++ ++ if (hdf->coding_profile != AV3A_OBJECT_METADATA_PROFILE) { ++ hdf->bitrate_index = get_bits(gb, 4); ++ if ((hdf->bitrate_index >= AV3A_BITRATE_TABLE_SIZE) || (hdf->bitrate_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->total_bitrate = ff_av3a_bitrate_map_table[hdf->channel_number_index].bitrate_table[hdf->bitrate_index]; ++ } ++ ++ skip_bits(gb, 8); /* skip CRC 8 bits */ ++ ++ return 0; ++} ++ ++static int raw_av3a_parse(AVCodecParserContext *s, AVCodecContext *avctx, const uint8_t **poutbuf, ++ int32_t *poutbuf_size, const uint8_t *buf, int32_t buf_size) ++{ ++ int ret = 0; ++ uint8_t header[AV3A_MAX_NBYTES_HEADER]; ++ AATFHeaderInfo hdf; ++ GetBitContext gb; ++ ++ if (buf_size < AV3A_MAX_NBYTES_HEADER) { ++ return buf_size; ++ } ++ memcpy(header, buf, AV3A_MAX_NBYTES_HEADER); ++ ++ init_get_bits8(&gb, buf, AV3A_MAX_NBYTES_HEADER); ++ if ((ret = ff_read_av3a_header_parse(&gb, &hdf)) != 0) { ++ return ret; ++ } ++ ++ avctx->codec_id = AV_CODEC_ID_AVS3DA; ++ avctx->frame_size = AV3A_AUDIO_FRAME_SIZE; ++ avctx->bits_per_raw_sample = hdf.resolution; ++ avctx->sample_rate = hdf.sampling_rate; ++ avctx->bit_rate = hdf.total_bitrate; ++ ++ avctx->ch_layout.order = AV_CHANNEL_ORDER_UNSPEC; ++ avctx->ch_layout.nb_channels = hdf.total_channels; ++ ++ *poutbuf = buf; ++ *poutbuf_size = buf_size; ++ ++ return buf_size; ++} ++ ++const FFCodecParser ff_av3a_parser = { ++ PARSER_CODEC_LIST(AV_CODEC_ID_AVS3DA), ++ .priv_data_size = sizeof(Av3aParseContext), ++ .parse = raw_av3a_parse, ++}; +diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c +index a9f21f8..93877ed 100644 +--- a/libavcodec/codec_desc.c ++++ b/libavcodec/codec_desc.c +@@ -3863,6 +3863,13 @@ static const AVCodecDescriptor codec_descriptors[] = { + .long_name = NULL_IF_CONFIG_SMALL("AVFrame to AVPacket passthrough"), + .props = AV_CODEC_PROP_LOSSLESS, + }, ++ { ++ .id = AV_CODEC_ID_AVS3DA, ++ .type = AVMEDIA_TYPE_AUDIO, ++ .name = "av3a", ++ .long_name = NULL_IF_CONFIG_SMALL("Audio Vivid"), ++ .props = AV_CODEC_PROP_LOSSY, ++ }, + { + .id = AV_CODEC_ID_VNULL, + .type = AVMEDIA_TYPE_VIDEO, +diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h +index 6529f0a..43f7549 100644 +--- a/libavcodec/codec_id.h ++++ b/libavcodec/codec_id.h +@@ -628,6 +628,7 @@ enum AVCodecID { + * Dummy null video codec, useful mainly for development and debugging. + * Null encoder/decoder discard all input and never return any output. + */ ++ AV_CODEC_ID_AVS3DA, + AV_CODEC_ID_VNULL, + /** + * Dummy null audio codec, useful mainly for development and debugging. +diff --git a/libavcodec/parsers.c b/libavcodec/parsers.c +index 162b96c..415c842 100644 +--- a/libavcodec/parsers.c ++++ b/libavcodec/parsers.c +@@ -47,6 +47,7 @@ extern const FFCodecParser ff_apv_parser; + extern const FFCodecParser ff_av1_parser; + extern const FFCodecParser ff_avs2_parser; + extern const FFCodecParser ff_avs3_parser; ++extern const FFCodecParser ff_av3a_parser; + extern const FFCodecParser ff_bmp_parser; + extern const FFCodecParser ff_cavsvideo_parser; + extern const FFCodecParser ff_cook_parser; +diff --git a/libavcodec/utils.c b/libavcodec/utils.c +index 615d60c..46f2123 100644 +--- a/libavcodec/utils.c ++++ b/libavcodec/utils.c +@@ -603,6 +603,7 @@ static int get_audio_frame_duration(enum AVCodecID id, int sr, int ch, int ba, + case AV_CODEC_ID_MP2: + case AV_CODEC_ID_MUSEPACK7: return 1152; + case AV_CODEC_ID_AC3: return 1536; ++ case AV_CODEC_ID_AVS3DA: return 1024; + case AV_CODEC_ID_FTR: return 1024; + } + +diff --git a/libavformat/Makefile b/libavformat/Makefile +index 85ed05b..158a11d 100644 +--- a/libavformat/Makefile ++++ b/libavformat/Makefile +@@ -169,6 +169,7 @@ OBJS-$(CONFIG_AVS2_DEMUXER) += avs2dec.o rawdec.o + OBJS-$(CONFIG_AVS2_MUXER) += rawenc.o + OBJS-$(CONFIG_AVS3_DEMUXER) += avs3dec.o rawdec.o + OBJS-$(CONFIG_AVS3_MUXER) += rawenc.o ++OBJS-$(CONFIG_AV3A_DEMUXER) += av3adec.o + OBJS-$(CONFIG_BETHSOFTVID_DEMUXER) += bethsoftvid.o + OBJS-$(CONFIG_BFI_DEMUXER) += bfi.o + OBJS-$(CONFIG_BINK_DEMUXER) += bink.o +diff --git a/libavformat/allformats.c b/libavformat/allformats.c +index e2f37da..1a05db9 100644 +--- a/libavformat/allformats.c ++++ b/libavformat/allformats.c +@@ -101,6 +101,7 @@ extern const FFInputFormat ff_avs2_demuxer; + extern const FFOutputFormat ff_avs2_muxer; + extern const FFInputFormat ff_avs3_demuxer; + extern const FFOutputFormat ff_avs3_muxer; ++extern const FFInputFormat ff_av3a_demuxer; + extern const FFInputFormat ff_bethsoftvid_demuxer; + extern const FFInputFormat ff_bfi_demuxer; + extern const FFInputFormat ff_bintext_demuxer; +diff --git a/libavformat/av3adec.c b/libavformat/av3adec.c +new file mode 100644 +index 0000000..9bb8729 +--- /dev/null ++++ b/libavformat/av3adec.c +@@ -0,0 +1,473 @@ ++/* ++ * AV3A Demuxer ++ * ++ * Copyright (c) 2024 Shuai Liu ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg 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. ++ * ++ * FFmpeg 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 FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++#include "avformat.h" ++#include "avio_internal.h" ++#include "internal.h" ++#include "rawdec.h" ++#include "libavutil/opt.h" ++#include "libavutil/avassert.h" ++#include "libavutil/intreadwrite.h" ++#include "libavutil/channel_layout.h" ++#include "libavcodec/get_bits.h" ++#include "libavcodec/av3a.h" ++#include ++ ++typedef struct { ++ uint8_t audio_codec_id; ++ uint8_t sampling_frequency_index; ++ uint8_t nn_type; ++ uint8_t content_type; ++ uint8_t channel_number_index; ++ uint8_t number_objects; ++ uint8_t hoa_order; ++ uint8_t resolution_index; ++ uint16_t total_bitrate_kbps; ++} Av3aFormatContext; ++ ++static int av3a_read_aatf_frame_header(AATFHeaderInfo *hdf, const uint8_t *buf) ++{ ++ int16_t sync_word; ++ GetBitContext gb; ++ ++ hdf->nb_channels = 0; ++ hdf->nb_objects = 0; ++ ++ init_get_bits8(&gb, buf, AV3A_MAX_NBYTES_HEADER); ++ ++ sync_word = get_bits(&gb, 12); ++ if (sync_word != AV3A_AUDIO_SYNC_WORD) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ /* codec id */ ++ hdf->audio_codec_id = get_bits(&gb, 4); ++ if (hdf->audio_codec_id != AV3A_LOSSY_CODEC_ID) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ /* anc data */ ++ hdf->anc_data = get_bits(&gb, 1); ++ if (hdf->anc_data) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ /* neural network type */ ++ hdf->nn_type = get_bits(&gb, 3); ++ if ((hdf->nn_type > AV3A_LC_NN_TYPE) || (hdf->nn_type < AV3A_BASELINE_NN_TYPE)) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ /* coding profile */ ++ hdf->coding_profile = get_bits(&gb, 3); ++ ++ /* sampling rate */ ++ hdf->sampling_frequency_index = get_bits(&gb, 4); ++ if ((hdf->sampling_frequency_index >= AV3A_FS_TABLE_SIZE) || (hdf->sampling_frequency_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->sampling_rate = ff_av3a_sampling_rate_table[hdf->sampling_frequency_index]; ++ ++ skip_bits(&gb, 8); ++ ++ if (hdf->coding_profile == AV3A_BASE_PROFILE) { ++ hdf->content_type = AV3A_CHANNEL_BASED_TYPE; ++ hdf->channel_number_index = get_bits(&gb, 7); ++ if ((hdf->channel_number_index >= CHANNEL_CONFIG_UNKNOWN) || ++ (hdf->channel_number_index == CHANNEL_CONFIG_MC_10_2) || ++ (hdf->channel_number_index == CHANNEL_CONFIG_MC_22_2) || ++ (hdf->channel_number_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->nb_channels = ff_av3a_channels_map_table[hdf->channel_number_index].channels; ++ } else if (hdf->coding_profile == AV3A_OBJECT_METADATA_PROFILE) { ++ hdf->soundbed_type = get_bits(&gb, 2); ++ if (hdf->soundbed_type == 0) { ++ hdf->content_type = AV3A_OBJECT_BASED_TYPE; ++ hdf->object_channel_number = get_bits(&gb, 7); ++ if (hdf->object_channel_number < 0) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->bitrate_index_per_channel = get_bits(&gb, 4); ++ if ((hdf->bitrate_index_per_channel >= AV3A_BITRATE_TABLE_SIZE) || (hdf->bitrate_index_per_channel < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->nb_objects = hdf->object_channel_number + 1; ++ hdf->total_bitrate = ff_av3a_bitrate_map_table[CHANNEL_CONFIG_MONO].bitrate_table[hdf->bitrate_index_per_channel] * hdf->nb_objects; ++ } else if (hdf->soundbed_type == 1) { ++ hdf->content_type = AV3A_CHANNEL_OBJECT_TYPE; ++ hdf->channel_number_index = get_bits(&gb, 7); ++ if ((hdf->channel_number_index >= CHANNEL_CONFIG_UNKNOWN) || ++ (hdf->channel_number_index == CHANNEL_CONFIG_MC_10_2) || ++ (hdf->channel_number_index == CHANNEL_CONFIG_MC_22_2) || ++ (hdf->channel_number_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->nb_channels = ff_av3a_channels_map_table[hdf->channel_number_index].channels; ++ hdf->bitrate_index = get_bits(&gb, 4); ++ if ((hdf->bitrate_index >= AV3A_BITRATE_TABLE_SIZE) || (hdf->bitrate_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ hdf->object_channel_number = get_bits(&gb, 7); ++ if (hdf->object_channel_number < 0) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->nb_objects = hdf->object_channel_number + 1; ++ hdf->bitrate_index_per_channel = get_bits(&gb, 4); ++ if ((hdf->bitrate_index_per_channel >= AV3A_BITRATE_TABLE_SIZE) || (hdf->bitrate_index_per_channel < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ hdf->total_bitrate = ff_av3a_bitrate_map_table[hdf->channel_number_index].bitrate_table[hdf->bitrate_index] + ++ ff_av3a_bitrate_map_table[CHANNEL_CONFIG_MONO].bitrate_table[hdf->bitrate_index_per_channel] * hdf->nb_objects; ++ } else { ++ return AVERROR_INVALIDDATA; ++ } ++ } else if (hdf->coding_profile == AV3A_AMBISONIC_PROFILE) { ++ hdf->content_type = AV3A_AMBISONIC_TYPE; ++ hdf->order = get_bits(&gb, 4); ++ hdf->hoa_order = hdf->order + 1; ++ ++ switch (hdf->hoa_order) { ++ case AV3A_AMBISONIC_FIRST_ORDER: ++ hdf->channel_number_index = CHANNEL_CONFIG_HOA_ORDER1; ++ break; ++ case AV3A_AMBISONIC_SECOND_ORDER: ++ hdf->channel_number_index = CHANNEL_CONFIG_HOA_ORDER2; ++ break; ++ case AV3A_AMBISONIC_THIRD_ORDER: ++ hdf->channel_number_index = CHANNEL_CONFIG_HOA_ORDER3; ++ break; ++ default: ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->nb_channels = ff_av3a_channels_map_table[hdf->channel_number_index].channels; ++ } else { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ hdf->total_channels = hdf->nb_channels + hdf->nb_objects; ++ ++ /* resolution */ ++ hdf->resolution_index = get_bits(&gb, 2); ++ if ((hdf->resolution_index >= AV3A_RESOLUTION_TABLE_SIZE) || (hdf->resolution_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->resolution = ff_av3a_sample_format_map_table[hdf->resolution_index].resolution; ++ hdf->sample_format = ff_av3a_sample_format_map_table[hdf->resolution_index].sample_format; ++ ++ if (hdf->coding_profile != AV3A_OBJECT_METADATA_PROFILE) { ++ hdf->bitrate_index = get_bits(&gb, 4); ++ if ((hdf->bitrate_index >= AV3A_BITRATE_TABLE_SIZE) || (hdf->bitrate_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ hdf->total_bitrate = ff_av3a_bitrate_map_table[hdf->channel_number_index].bitrate_table[hdf->bitrate_index]; ++ } ++ ++ skip_bits(&gb, 8); ++ ++ return 0; ++} ++ ++static int av3a_get_packet_size(AVFormatContext *s) ++{ ++ int ret = 0; ++ int read_bytes = 0; ++ uint16_t sync_word = 0; ++ int payload_bytes = 0; ++ int payloud_bits = 0; ++ uint8_t header[AV3A_MAX_NBYTES_HEADER]; ++ GetBitContext gb; ++ int32_t sampling_rate; ++ int16_t coding_profile, sampling_frequency_index, channel_number_index; ++ int16_t bitrate_index, bitrate_index_per_channel; ++ int16_t objects, hoa_order; ++ int64_t total_bitrate; ++ ++ if (!s) { ++ return AVERROR(ENOMEM); ++ } ++ ++ if (!s->pb) { ++ return AVERROR(ENOMEM); ++ } ++ ++ read_bytes = avio_read(s->pb, header, AV3A_MAX_NBYTES_HEADER); ++ if (read_bytes != AV3A_MAX_NBYTES_HEADER) { ++ return (read_bytes < 0) ? read_bytes : AVERROR_EOF; ++ } ++ ++ init_get_bits8(&gb, header, AV3A_MAX_NBYTES_HEADER); ++ ++ sync_word = get_bits(&gb, 12); ++ if (sync_word != AV3A_AUDIO_SYNC_WORD) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ skip_bits(&gb, 8); ++ ++ coding_profile = get_bits(&gb, 3); ++ sampling_frequency_index = get_bits(&gb, 4); ++ if ((sampling_frequency_index >= AV3A_FS_TABLE_SIZE) || (sampling_frequency_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ sampling_rate = ff_av3a_sampling_rate_table[sampling_frequency_index]; ++ ++ skip_bits(&gb, 8); ++ ++ if (coding_profile == AV3A_BASE_PROFILE) { ++ channel_number_index = get_bits(&gb, 7); ++ if ((channel_number_index >= CHANNEL_CONFIG_UNKNOWN) || ++ (channel_number_index == CHANNEL_CONFIG_MC_10_2) || ++ (channel_number_index == CHANNEL_CONFIG_MC_22_2) || ++ (channel_number_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ } else if (coding_profile == AV3A_OBJECT_METADATA_PROFILE) { ++ int64_t soundbed_bitrate, objects_bitrate; ++ int16_t soundbed_type = get_bits(&gb, 2); ++ if (soundbed_type == 0) { ++ objects = get_bits(&gb, 7); ++ if (objects < 0) { ++ return AVERROR_INVALIDDATA; ++ } ++ objects += 1; ++ ++ bitrate_index_per_channel = get_bits(&gb, 4); ++ if ((bitrate_index_per_channel >= AV3A_BITRATE_TABLE_SIZE) || (bitrate_index_per_channel < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ total_bitrate = ff_av3a_bitrate_map_table[CHANNEL_CONFIG_MONO].bitrate_table[bitrate_index_per_channel] * objects; ++ } else if (soundbed_type == 1) { ++ channel_number_index = get_bits(&gb, 7); ++ if ((channel_number_index >= CHANNEL_CONFIG_UNKNOWN) || ++ (channel_number_index == CHANNEL_CONFIG_MC_10_2) || ++ (channel_number_index == CHANNEL_CONFIG_MC_22_2) || ++ (channel_number_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ bitrate_index = get_bits(&gb, 4); ++ if ((bitrate_index >= AV3A_BITRATE_TABLE_SIZE) || (bitrate_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ soundbed_bitrate = ff_av3a_bitrate_map_table[channel_number_index].bitrate_table[bitrate_index]; ++ ++ objects = get_bits(&gb, 7); ++ if (objects < 0) { ++ return AVERROR_INVALIDDATA; ++ } ++ objects += 1; ++ bitrate_index_per_channel = get_bits(&gb, 4); ++ if ((bitrate_index_per_channel >= AV3A_BITRATE_TABLE_SIZE) || (bitrate_index_per_channel < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ objects_bitrate = ff_av3a_bitrate_map_table[CHANNEL_CONFIG_MONO].bitrate_table[bitrate_index_per_channel]; ++ total_bitrate = soundbed_bitrate + (objects_bitrate * objects); ++ } else { ++ return AVERROR_INVALIDDATA; ++ } ++ } else if (coding_profile == AV3A_AMBISONIC_PROFILE) { ++ hoa_order = get_bits(&gb, 4); ++ hoa_order += 1; ++ ++ switch (hoa_order) { ++ case AV3A_AMBISONIC_FIRST_ORDER: ++ channel_number_index = CHANNEL_CONFIG_HOA_ORDER1; ++ break; ++ case AV3A_AMBISONIC_SECOND_ORDER: ++ channel_number_index = CHANNEL_CONFIG_HOA_ORDER2; ++ break; ++ case AV3A_AMBISONIC_THIRD_ORDER: ++ channel_number_index = CHANNEL_CONFIG_HOA_ORDER3; ++ break; ++ default: ++ return AVERROR_INVALIDDATA; ++ } ++ } else { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ skip_bits(&gb, 2); ++ if (coding_profile != AV3A_OBJECT_METADATA_PROFILE) { ++ bitrate_index = get_bits(&gb, 4); ++ if ((bitrate_index >= AV3A_BITRATE_TABLE_SIZE) || (bitrate_index < 0)) { ++ return AVERROR_INVALIDDATA; ++ } ++ total_bitrate = ff_av3a_bitrate_map_table[channel_number_index].bitrate_table[bitrate_index]; ++ } ++ ++ skip_bits(&gb, 8); ++ ++ if (sampling_rate == 44100) { ++ payloud_bits = (int)floor(((float) (total_bitrate) / sampling_rate) * AV3A_AUDIO_FRAME_SIZE); ++ payload_bytes = (int)ceil((float)payloud_bits / 8); ++ } else { ++ payload_bytes = (int)ceil((((float) (total_bitrate) / sampling_rate) * AV3A_AUDIO_FRAME_SIZE) / 8); ++ } ++ ++ if ((ret = avio_seek(s->pb, -read_bytes, SEEK_CUR)) < 0) { ++ return ret; ++ } ++ ++ return payload_bytes; ++} ++ ++static int av3a_probe(const AVProbeData *p) ++{ ++ uint16_t frame_sync_word; ++ uint16_t lval = ((uint16_t)(p->buf[0])); ++ uint16_t rval = ((uint16_t)(p->buf[1])); ++ frame_sync_word = ((lval << 8) | rval) >> 4; ++ ++ if (frame_sync_word == AV3A_AUDIO_SYNC_WORD && av_match_ext(p->filename, "av3a")) { ++ return AVPROBE_SCORE_MAX; ++ } ++ ++ return 0; ++} ++ ++static int av3a_read_header(AVFormatContext *s) ++{ ++ int ret = 0; ++ uint8_t header[AV3A_MAX_NBYTES_HEADER]; ++ AVStream *stream = NULL; ++ Av3aFormatContext av3afmtctx; ++ AATFHeaderInfo hdf; ++ ++ if (!s) { ++ return AVERROR(ENOMEM); ++ } ++ ++ if (!(stream = avformat_new_stream(s, NULL))) { ++ return AVERROR(ENOMEM); ++ } ++ ++ stream->start_time = 0; ++ ffstream(stream)->need_parsing = AVSTREAM_PARSE_FULL_RAW; ++ stream->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; ++ stream->codecpar->codec_id = ((const FFInputFormat*)(s->iformat))->raw_codec_id; ++ stream->codecpar->codec_tag = MKTAG('a', 'v', '3', 'a'); ++ ++ if ((ret = avio_read(s->pb, header, AV3A_MAX_NBYTES_HEADER)) != AV3A_MAX_NBYTES_HEADER) { ++ return (ret < 0) ? ret : AVERROR_EOF; ++ } ++ ++ ret = av3a_read_aatf_frame_header(&hdf, header); ++ if (ret) { ++ return ret; ++ } ++ ++ /* stream parameters */ ++ stream->codecpar->format = hdf.sample_format; ++ stream->codecpar->bits_per_raw_sample = hdf.resolution; ++ stream->codecpar->bit_rate = hdf.total_bitrate; ++ stream->codecpar->sample_rate = (int) (hdf.sampling_rate); ++ stream->codecpar->frame_size = AV3A_AUDIO_FRAME_SIZE; ++ stream->codecpar->ch_layout.order = AV_CHANNEL_ORDER_UNSPEC; ++ stream->codecpar->ch_layout.nb_channels = hdf.total_channels; ++ ++ /* extradata */ ++ av3afmtctx.audio_codec_id = hdf.audio_codec_id; ++ av3afmtctx.sampling_frequency_index = hdf.sampling_frequency_index; ++ av3afmtctx.nn_type = hdf.nn_type; ++ av3afmtctx.content_type = hdf.content_type; ++ av3afmtctx.channel_number_index = hdf.channel_number_index; ++ av3afmtctx.number_objects = hdf.nb_objects; ++ av3afmtctx.hoa_order = hdf.hoa_order; ++ av3afmtctx.resolution_index = hdf.resolution_index; ++ av3afmtctx.total_bitrate_kbps = (int) (hdf.total_bitrate / 1000); ++ ++ if ((ret = ff_alloc_extradata(stream->codecpar, sizeof(Av3aFormatContext))) < 0) { ++ return ret; ++ } ++ memcpy(stream->codecpar->extradata, &av3afmtctx, sizeof(Av3aFormatContext)); ++ ++ if ((ret = avio_seek(s->pb, -AV3A_MAX_NBYTES_HEADER, SEEK_CUR)) < 0) { ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int av3a_read_packet(AVFormatContext *s, AVPacket *pkt) ++{ ++ int64_t pos; ++ int packet_size = 0; ++ int read_bytes = 0; ++ int ret = 0; ++ ++ if (!s) { ++ return AVERROR(ENOMEM); ++ } ++ ++ if (avio_feof(s->pb)) { ++ return AVERROR_EOF; ++ } ++ pos = avio_tell(s->pb); ++ ++ if (!(packet_size = av3a_get_packet_size(s))) { ++ return AVERROR_EOF; ++ } ++ ++ if (packet_size < 0) { ++ return packet_size; ++ } ++ ++ if ((ret = av_new_packet(pkt, packet_size)) < 0) { ++ return ret; ++ } ++ ++ if (!s->streams[0]) { ++ return AVERROR(ENOMEM); ++ } ++ ++ if (!s->streams[0]->codecpar) { ++ return AVERROR(ENOMEM); ++ } ++ ++ pkt->stream_index = 0; ++ pkt->pos = pos; ++ pkt->duration = s->streams[0]->codecpar->frame_size; ++ ++ read_bytes = avio_read(s->pb, pkt->data, packet_size); ++ if (read_bytes != packet_size) { ++ return (read_bytes < 0) ? read_bytes : AVERROR_EOF; ++ } ++ ++ return 0; ++} ++ ++const FFInputFormat ff_av3a_demuxer = { ++ .p.name = "av3a", ++ .p.long_name = NULL_IF_CONFIG_SMALL("Audio Vivid"), ++ .raw_codec_id = AV_CODEC_ID_AVS3DA, ++ .p.priv_class = &ff_raw_demuxer_class, ++ .priv_data_size = sizeof(FFRawDemuxerContext), ++ .read_probe = av3a_probe, ++ .read_header = av3a_read_header, ++ .read_packet = av3a_read_packet, ++ .p.flags = AVFMT_GENERIC_INDEX, ++ .p.extensions = "av3a", ++ .p.mime_type = "audio/av3a", ++}; +\ No newline at end of file +diff --git a/libavformat/isom_tags.c b/libavformat/isom_tags.c +index 1cd655b..528c518 100644 +--- a/libavformat/isom_tags.c ++++ b/libavformat/isom_tags.c +@@ -368,6 +368,7 @@ const AVCodecTag ff_codec_movaudio_tags[] = { + { AV_CODEC_ID_TRUEHD, MKTAG('m', 'l', 'p', 'a') }, /* mp4ra.org */ + { AV_CODEC_ID_OPUS, MKTAG('O', 'p', 'u', 's') }, /* mp4ra.org */ + { AV_CODEC_ID_MPEGH_3D_AUDIO, MKTAG('m', 'h', 'm', '1') }, /* MPEG-H 3D Audio bitstream */ ++ { AV_CODEC_ID_AVS3DA, MKTAG('a', 'v', '3', 'a') }, /* AVS3 Audio */ + { AV_CODEC_ID_NONE, 0 }, + }; + +diff --git a/libavformat/mov.c b/libavformat/mov.c +index e114770..b639fe1 100644 +--- a/libavformat/mov.c ++++ b/libavformat/mov.c +@@ -71,6 +71,10 @@ + #include "mov_chan.h" + #include "replaygain.h" + ++#if CONFIG_AV3A_DEMUXER ++#include "libavcodec/av3a.h" ++#endif ++ + #if CONFIG_ZLIB + #include + #endif +@@ -88,6 +92,123 @@ static int mov_read_default(MOVContext *c, AVIOContext *pb, MOVAtom atom); + static int mov_read_mfra(MOVContext *c, AVIOContext *f); + static void mov_free_stream_context(AVFormatContext *s, AVStream *st); + ++#if CONFIG_AV3A_DEMUXER ++static int mov_read_dca3(MOVContext *c, AVIOContext *pb, MOVAtom atom) ++{ ++ int ret; ++ int nb_channels = 0; ++ int nb_objects = 0; ++ AVStream *st; ++ GetBitContext gb; ++ uint8_t buffer[7]; ++ int audio_codec_id, sampling_frequency_index; ++ int nn_type, content_type, channel_number_index = 0, number_objects; ++ int hoa_order, resolution_index; ++ int bitrate_kbps; ++ ++ if (atom.size < AV3A_DCA3_BOX_MIN_SIZE) ++ return AVERROR_INVALIDDATA; ++ ++ if (c->fc->nb_streams < 1) ++ return 0; ++ st = c->fc->streams[c->fc->nb_streams - 1]; ++ ++ ret = avio_read(pb, buffer, sizeof(buffer)); ++ if (ret < 0) ++ return ret; ++ if (ret != (int)sizeof(buffer)) ++ return AVERROR_INVALIDDATA; ++ ++ init_get_bits8(&gb, buffer, sizeof(buffer)); ++ ++ audio_codec_id = get_bits(&gb, 4); ++ if (audio_codec_id != AV3A_LOSSY_CODEC_ID) ++ return AVERROR_INVALIDDATA; ++ ++ st->codecpar->frame_size = AV3A_AUDIO_FRAME_SIZE; ++ sampling_frequency_index = get_bits(&gb, 4); ++ if (sampling_frequency_index >= AV3A_FS_TABLE_SIZE) ++ return AVERROR_INVALIDDATA; ++ st->codecpar->sample_rate = ff_av3a_sampling_rate_table[sampling_frequency_index]; ++ ++ nn_type = get_bits(&gb, 3); ++ if (nn_type > AV3A_LC_NN_TYPE || nn_type < AV3A_BASELINE_NN_TYPE) ++ return AVERROR_INVALIDDATA; ++ ++ skip_bits(&gb, 1); ++ content_type = get_bits(&gb, 4); ++ if (content_type == AV3A_CHANNEL_BASED_TYPE) { ++ channel_number_index = get_bits(&gb, 7); ++ skip_bits(&gb, 1); ++ if (channel_number_index >= CHANNEL_CONFIG_UNKNOWN || ++ channel_number_index == CHANNEL_CONFIG_MC_10_2 || ++ channel_number_index == CHANNEL_CONFIG_MC_22_2) ++ return AVERROR_INVALIDDATA; ++ nb_channels = ff_av3a_channels_map_table[channel_number_index].channels; ++ } else if (content_type == AV3A_OBJECT_BASED_TYPE) { ++ number_objects = get_bits(&gb, 7); ++ skip_bits(&gb, 1); ++ nb_objects = number_objects; ++ if (nb_objects < 1) ++ return AVERROR_INVALIDDATA; ++ } else if (content_type == AV3A_CHANNEL_OBJECT_TYPE) { ++ channel_number_index = get_bits(&gb, 7); ++ skip_bits(&gb, 1); ++ if (channel_number_index >= CHANNEL_CONFIG_UNKNOWN || ++ channel_number_index == CHANNEL_CONFIG_MC_10_2 || ++ channel_number_index == CHANNEL_CONFIG_MC_22_2) ++ return AVERROR_INVALIDDATA; ++ number_objects = get_bits(&gb, 7); ++ skip_bits(&gb, 1); ++ nb_channels = ff_av3a_channels_map_table[channel_number_index].channels; ++ nb_objects = number_objects; ++ if (nb_objects < 1) ++ return AVERROR_INVALIDDATA; ++ } else if (content_type == AV3A_AMBISONIC_TYPE) { ++ hoa_order = get_bits(&gb, 4); ++ if (hoa_order < AV3A_AMBISONIC_FIRST_ORDER || ++ hoa_order > AV3A_AMBISONIC_THIRD_ORDER) ++ return AVERROR_INVALIDDATA; ++ nb_channels = (hoa_order + 1) * (hoa_order + 1); ++ } else { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ bitrate_kbps = get_bits(&gb, 16); ++ st->codecpar->bit_rate = bitrate_kbps * 1000; ++ ++ resolution_index = get_bits(&gb, 2); ++ if (resolution_index >= AV3A_RESOLUTION_TABLE_SIZE) ++ return AVERROR_INVALIDDATA; ++ st->codecpar->format = ff_av3a_sample_format_map_table[resolution_index].sample_format; ++ st->codecpar->bits_per_raw_sample = ff_av3a_sample_format_map_table[resolution_index].resolution; ++ ++ av_channel_layout_uninit(&st->codecpar->ch_layout); ++ if (content_type != AV3A_AMBISONIC_TYPE) { ++ st->codecpar->ch_layout.order = AV_CHANNEL_ORDER_CUSTOM; ++ st->codecpar->ch_layout.nb_channels = nb_channels + nb_objects; ++ st->codecpar->ch_layout.u.map = av_calloc(st->codecpar->ch_layout.nb_channels, ++ sizeof(*st->codecpar->ch_layout.u.map)); ++ if (!st->codecpar->ch_layout.u.map) ++ return AVERROR(ENOMEM); ++ ++ if (content_type != AV3A_OBJECT_BASED_TYPE) { ++ for (int i = 0; i < nb_channels; i++) ++ st->codecpar->ch_layout.u.map[i].id = ++ ff_av3a_channels_map_table[channel_number_index].channel_layout[i]; ++ } ++ ++ for (int i = nb_channels; i < st->codecpar->ch_layout.nb_channels; i++) ++ st->codecpar->ch_layout.u.map[i].id = AV3A_CH_AUDIO_OBJECT; ++ } else { ++ st->codecpar->ch_layout.order = AV_CHANNEL_ORDER_AMBISONIC; ++ st->codecpar->ch_layout.nb_channels = nb_channels; ++ } ++ ++ return 0; ++} ++#endif ++ + static int mov_metadata_track_or_disc_number(MOVContext *c, AVIOContext *pb, + unsigned len, const char *key) + { +@@ -9640,6 +9761,9 @@ static const MOVParseTableEntry mov_default_parse_table[] = { + #if CONFIG_IAMFDEC + { MKTAG('i','a','c','b'), mov_read_iacb }, + #endif ++#if CONFIG_AV3A_DEMUXER ++{ MKTAG('d','c','a','3'), mov_read_dca3 }, ++#endif + { MKTAG('s','r','a','t'), mov_read_srat }, + { 0, NULL } + }; +diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c +index 47944c7..d682526 100644 +--- a/libavformat/mpegts.c ++++ b/libavformat/mpegts.c +@@ -825,6 +825,9 @@ static const StreamType ISO_types[] = { + { STREAM_TYPE_VIDEO_DIRAC, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_DIRAC }, + { STREAM_TYPE_VIDEO_AVS2, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_AVS2 }, + { STREAM_TYPE_VIDEO_AVS3, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_AVS3 }, ++#if CONFIG_AV3A_DEMUXER ++ { STREAM_TYPE_AUDIO_AV3A, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AVS3DA }, ++#endif + { STREAM_TYPE_VIDEO_VC1, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_VC1 }, + { 0 }, + }; +@@ -883,6 +886,9 @@ static const StreamType REGD_types[] = { + { MKTAG('I', 'D', '3', ' '), AVMEDIA_TYPE_DATA, AV_CODEC_ID_TIMED_ID3 }, + { MKTAG('V', 'C', '-', '1'), AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_VC1 }, + { MKTAG('O', 'p', 'u', 's'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_OPUS }, ++#if CONFIG_AV3A_DEMUXER ++ { MKTAG('a', 'v', '3', 'a'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AVS3DA}, ++#endif + { 0 }, + }; + +diff --git a/libavformat/mpegts.h b/libavformat/mpegts.h +index 18f326b..92c95ef 100644 +--- a/libavformat/mpegts.h ++++ b/libavformat/mpegts.h +@@ -152,6 +152,7 @@ + #define STREAM_TYPE_VIDEO_AVS3 0xd4 + #define STREAM_TYPE_VIDEO_VC1 0xea + #define STREAM_TYPE_VIDEO_DIRAC 0xd1 ++#define STREAM_TYPE_AUDIO_AV3A 0xd5 + + /* stream_type values [0x80, 0xff] are User Private */ + #define STREAM_TYPE_BLURAY_AUDIO_PCM_BLURAY 0x80 +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.2/0017-http-add-reconnect_first_delay-opt.patch b/patches/ffmpeg-n8.1.2/0017-http-add-reconnect_first_delay-opt.patch new file mode 100644 index 000000000..23c0b615c --- /dev/null +++ b/patches/ffmpeg-n8.1.2/0017-http-add-reconnect_first_delay-opt.patch @@ -0,0 +1,41 @@ +From 63de5ca190448e027cf758f548b2213095638bd6 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Thu, 29 May 2025 09:57:28 +0800 +Subject: http add reconnect_first_delay opt + +--- + libavformat/http.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/libavformat/http.c b/libavformat/http.c +index f4bf225..cd39796 100644 +--- a/libavformat/http.c ++++ b/libavformat/http.c +@@ -131,6 +131,7 @@ typedef struct HTTPContext { + int reconnect_on_network_error; + int reconnect_streamed; + int reconnect_delay_max; ++ int reconnect_first_delay; + char *reconnect_on_http_error; + int listen; + char *resource; +@@ -205,6 +206,7 @@ static const AVOption options[] = { + { "reconnect_max_retries", "the max number of times to retry a connection", OFFSET(reconnect_max_retries), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, D }, + { "reconnect_delay_total_max", "max total reconnect delay in seconds after which to give up", OFFSET(reconnect_delay_total_max), AV_OPT_TYPE_INT, { .i64 = 256 }, 0, UINT_MAX/1000/1000, D }, + { "respect_retry_after", "respect the Retry-After header when retrying connections", OFFSET(respect_retry_after), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, D }, ++ { "reconnect_first_delay", "first reconnect delay in seconds", OFFSET(reconnect_first_delay), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, UINT_MAX/1000/1000, D }, + { "listen", "listen on HTTP", OFFSET(listen), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 2, D | E }, + { "resource", "The resource requested by a client", OFFSET(resource), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E }, + { "reply_code", "The http status code to return to a client", OFFSET(reply_code), AV_OPT_TYPE_INT, { .i64 = 200}, INT_MIN, 599, E}, +@@ -419,7 +421,7 @@ static int http_open_cnx(URLContext *h, AVDictionary **options) + HTTPAuthType cur_auth_type, cur_proxy_auth_type; + HTTPContext *s = h->priv_data; + int ret, conn_attempts = 1, auth_attempts = 0, redirects = 0; +- int reconnect_delay = 0; ++ int reconnect_delay = s->reconnect_first_delay; + int reconnect_delay_total = 0; + uint64_t off; + char *cached; +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.2/0018-fix-http-open-and-http_seek-redirect-authentication-.patch b/patches/ffmpeg-n8.1.2/0018-fix-http-open-and-http_seek-redirect-authentication-.patch new file mode 100644 index 000000000..b5bfa7259 --- /dev/null +++ b/patches/ffmpeg-n8.1.2/0018-fix-http-open-and-http_seek-redirect-authentication-.patch @@ -0,0 +1,102 @@ +From 0eb8def6e29ed4193013c3f7c2a01acf7eb40b28 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Fri, 8 May 2026 17:47:08 +0800 +Subject: fix http open and http_seek (redirect) authentication + bug + +--- + libavformat/http.c | 32 +++++++++++++++++++++++++++----- + 1 file changed, 27 insertions(+), 5 deletions(-) + +diff --git a/libavformat/http.c b/libavformat/http.c +index cd39796..b648596 100644 +--- a/libavformat/http.c ++++ b/libavformat/http.c +@@ -83,6 +83,7 @@ typedef struct HTTPContext { + char *uri; + char *location; + HTTPAuthState auth_state; ++ int auth_type2; + HTTPAuthState proxy_auth_state; + char *http_proxy; + char *headers; +@@ -190,6 +191,7 @@ static const AVOption options[] = { + { "icy_metadata_packet", "return current ICY metadata packet", OFFSET(icy_metadata_packet), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, AV_OPT_FLAG_EXPORT }, + { "metadata", "metadata read from the bitstream", OFFSET(metadata), AV_OPT_TYPE_DICT, {0}, 0, 0, AV_OPT_FLAG_EXPORT }, + { "auth_type", "HTTP authentication type", OFFSET(auth_state.auth_type), AV_OPT_TYPE_INT, { .i64 = HTTP_AUTH_NONE }, HTTP_AUTH_NONE, HTTP_AUTH_BASIC, D | E, .unit = "auth_type"}, ++ { "auth_type2", "backup HTTP authentication type for seek request", OFFSET(auth_type2), AV_OPT_TYPE_INT, { .i64 = HTTP_AUTH_NONE }, HTTP_AUTH_NONE, HTTP_AUTH_BASIC, D | E, "auth_type"}, + { "none", "No auth method set, autodetect", 0, AV_OPT_TYPE_CONST, { .i64 = HTTP_AUTH_NONE }, 0, 0, D | E, .unit = "auth_type"}, + { "basic", "HTTP basic authentication", 0, AV_OPT_TYPE_CONST, { .i64 = HTTP_AUTH_BASIC }, 0, 0, D | E, .unit = "auth_type"}, + { "send_expect_100", "Force sending an Expect: 100-continue header for POST", OFFSET(send_expect_100), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, E }, +@@ -789,6 +791,11 @@ static int http_open(URLContext *h, const char *uri, int flags, + int ret; + s->app_ctx = (AVApplicationContext *)av_dict_strtoptr(s->app_ctx_intptr); + ++ if (s->auth_type2 == HTTP_AUTH_NONE) { ++ //backup the init auth_type, when not assign. ++ s->auth_type2 = s->auth_state.auth_type; ++ } ++ + if( s->seekable == 1 ) + h->is_streamed = 0; + else +@@ -1550,6 +1557,7 @@ static int http_connect(URLContext *h, const char *path, const char *local_path, + uint64_t off = s->off; + const char *method; + int send_expect_100 = 0; ++ int cur_auth_type = s->auth_state.auth_type; + + av_bprint_init_for_buffer(&request, s->buffer, sizeof(s->buffer)); + +@@ -1708,15 +1716,24 @@ static int http_connect(URLContext *h, const char *path, const char *local_path, + s->off = off; + + if (off != s->off) { +- av_log(h, AV_LOG_ERROR, +- "Unexpected offset: expected %"PRIu64", got %"PRIu64"\n", +- off, s->off); +- err = AVERROR(EIO); +- goto done; ++ if (cur_auth_type != s->auth_state.auth_type && s->http_code == 401) { ++ s->off = off; ++ av_log(h, AV_LOG_ERROR, ++ "HTTP 401 needs authentication: %s, offset=%"PRIu64"\n", ++ s->buffer, s->off); ++ } else { ++ av_log(h, AV_LOG_ERROR, ++ "Unexpected offset: expected %"PRIu64", got %"PRIu64"\n", ++ off, s->off); ++ err = AVERROR(EIO); ++ goto done; ++ } + } + + err = 0; + done: ++ if (err < 0) ++ av_log(h, AV_LOG_ERROR, "HTTP error %d: %s\n", s->http_code, s->buffer); + av_freep(&authstr); + av_freep(&proxyauthstr); + return err; +@@ -2148,6 +2165,8 @@ static int64_t http_seek_internal(URLContext *h, int64_t off, int whence, int fo + return s->off; + } + ++ // http_seek use lasest redirect location, because after redirect, reset the auth_state: `memset(&s->auth_state, 0, sizeof(s->auth_state));` ++ + /* if the location changed (redirect), revert to the original uri */ + if (strcmp(s->uri, s->location)) { + char *new_uri; +@@ -2156,6 +2175,9 @@ static int64_t http_seek_internal(URLContext *h, int64_t off, int whence, int fo + return AVERROR(ENOMEM); + av_free(s->location); + s->location = new_uri; ++ if (s->auth_type2 != HTTP_AUTH_NONE) { ++ s->auth_state.auth_type = s->auth_type2; ++ } + } + + /* we save the old context in case the seek fails */ +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.2/0019-add-built-in-smb2-protocol-via-libsmb2.patch b/patches/ffmpeg-n8.1.2/0019-add-built-in-smb2-protocol-via-libsmb2.patch new file mode 100644 index 000000000..69938968f --- /dev/null +++ b/patches/ffmpeg-n8.1.2/0019-add-built-in-smb2-protocol-via-libsmb2.patch @@ -0,0 +1,495 @@ +From 633c6546e554aba1f6219f467b1759b6f39430a8 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Fri, 8 May 2026 17:58:33 +0800 +Subject: add built-in smb2 protocol via libsmb2 + +--- + configure | 5 + + libavformat/Makefile | 1 + + libavformat/libsmb2.c | 412 ++++++++++++++++++++++++++++++++++++++++ + libavformat/protocols.c | 1 + + 4 files changed, 419 insertions(+) + create mode 100644 libavformat/libsmb2.c + +diff --git a/configure b/configure +index 1759694..10c688c 100755 +--- a/configure ++++ b/configure +@@ -276,6 +276,7 @@ External library support: + --enable-libshaderc enable runtime GLSL->SPIRV compilation via libshaderc [no] + --enable-libshine enable fixed-point MP3 encoding via libshine [no] + --enable-libsmbclient enable Samba protocol via libsmbclient [no] ++ --enable-libsmb2 enable Samba protocol via libsmb2 [no] + --enable-libsnappy enable Snappy compression, needed for hap encoding [no] + --enable-libsoxr enable Include libsoxr resampling [no] + --enable-libspeex enable Speex de/encoding via libspeex [no] +@@ -2083,6 +2084,7 @@ EXTERNAL_LIBRARY_LIST=" + libshaderc + libshine + libsmbclient ++ libsmb2 + libsnappy + libsoxr + libspeex +@@ -4073,6 +4075,7 @@ librtmps_protocol_deps="librtmp" + librtmpt_protocol_deps="librtmp" + librtmpte_protocol_deps="librtmp" + libsmbclient_protocol_deps="libsmbclient gplv3" ++libsmb2_protocol_deps="libsmb2" + libsrt_protocol_deps="libsrt" + libsrt_protocol_select="network" + libssh_protocol_deps="libssh" +@@ -7369,6 +7372,8 @@ enabled libshaderc && require_pkg_config spirv_library "shaderc >= 2019.1 + enabled libshine && require_pkg_config libshine shine shine/layer3.h shine_encode_buffer + enabled libsmbclient && { check_pkg_config libsmbclient smbclient libsmbclient.h smbc_init || + require libsmbclient libsmbclient.h smbc_init -lsmbclient; } ++enabled libsmb2 && { check_pkg_config libsmb2 libsmb2 smb2/smb2-errors.h SMB2_STATUS_SUCCESS || ++ require libsmb2 smb2/smb2-errors.h SMB2_STATUS_SUCCESS -lsmb2; } + enabled libsnappy && require libsnappy snappy-c.h snappy_compress -lsnappy -lstdc++ + enabled libsoxr && require libsoxr soxr.h soxr_create -lsoxr + enabled libssh && require_pkg_config libssh "libssh >= 0.6.0" libssh/sftp.h sftp_init +diff --git a/libavformat/Makefile b/libavformat/Makefile +index 158a11d..6100e33 100644 +--- a/libavformat/Makefile ++++ b/libavformat/Makefile +@@ -751,6 +751,7 @@ OBJS-$(CONFIG_LIBRTMPS_PROTOCOL) += librtmp.o + OBJS-$(CONFIG_LIBRTMPT_PROTOCOL) += librtmp.o + OBJS-$(CONFIG_LIBRTMPTE_PROTOCOL) += librtmp.o + OBJS-$(CONFIG_LIBSMBCLIENT_PROTOCOL) += libsmbclient.o ++OBJS-$(CONFIG_LIBSMB2_PROTOCOL) += libsmb2.o + OBJS-$(CONFIG_LIBSRT_PROTOCOL) += libsrt.o + OBJS-$(CONFIG_LIBSSH_PROTOCOL) += libssh.o + OBJS-$(CONFIG_LIBZMQ_PROTOCOL) += libzmq.o +diff --git a/libavformat/libsmb2.c b/libavformat/libsmb2.c +new file mode 100644 +index 0000000..408f9e1 +--- /dev/null ++++ b/libavformat/libsmb2.c +@@ -0,0 +1,412 @@ ++/* ++ * Copyright (c) 2014 Lukasz Marek ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg 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. ++ * ++ * FFmpeg 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 FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++ ++#include ++#include ++#include "libavutil/avstring.h" ++#include "libavutil/opt.h" ++#include "libavutil/mem.h" ++#include "application.h" ++#include "url.h" ++#include "urldecode.h" ++//smb2.h:37:9: error: unknown type name 'time_t'; ++#include ++#include ++#include ++#include ++ ++typedef struct ++{ ++ const AVClass *class; ++ ++ struct smb2_context *ctx; ++ struct smb2_url *url; ++ struct smb2fh *fh; ++ struct smb2dir *dir; ++ ++ uint64_t filesize; ++ char *app_ctx_intptr; ++ AVApplicationContext *app_ctx; ++ int smb2_seal; ++} LIBSMB2Context; ++ ++static void destroy_smb2(LIBSMB2Context *libsmb2) ++{ ++ if (libsmb2->fh && libsmb2->ctx) { ++ smb2_close(libsmb2->ctx, libsmb2->fh); ++ } ++ ++ if (libsmb2->ctx) { ++ smb2_disconnect_share(libsmb2->ctx); ++ smb2_destroy_context(libsmb2->ctx); ++ } ++ ++ if (libsmb2->url) { ++ smb2_destroy_url(libsmb2->url); ++ } ++ libsmb2->fh = NULL; ++ libsmb2->ctx = NULL; ++ libsmb2->url = NULL; ++} ++ ++static av_cold int libsmb2_close(URLContext *h) ++{ ++ LIBSMB2Context *libsmb2 = h->priv_data; ++ destroy_smb2(libsmb2); ++ return 0; ++} ++ ++static av_cold int libsmb2_open(URLContext *h, const char *uri, int flags) ++{ ++ int ret = 0; ++ LIBSMB2Context *libsmb2 = h->priv_data; ++ libsmb2->filesize = -1; ++ libsmb2->ctx = smb2_init_context(); ++ libsmb2->app_ctx = (AVApplicationContext *)av_dict_strtoptr(libsmb2->app_ctx_intptr); ++ ++ av_application_will_http_open(libsmb2->app_ctx, (void *)h, uri); ++ ++ if (!libsmb2->ctx) { ++ av_log(h, AV_LOG_ERROR, "smb2 create context failed: %s.\n", smb2_get_error(libsmb2->ctx)); ++ ret = AVERROR(ENOMEM); ++ goto failed; ++ } ++ ++ const char *smb_url = av_strireplace(uri, "smb2", "smb"); ++ struct smb2_url *url = smb2_parse_url(libsmb2->ctx, smb_url); ++ ++ if (url == NULL) { ++ av_log(h, AV_LOG_ERROR, "smb2 parse url failed: %s\n", smb2_get_error(libsmb2->ctx)); ++ ret = AVERROR(ENOMEM); ++ goto failed; ++ } else { ++ if (url->user) { ++ char *user = strchr(url->user, ':'); ++ if (user) { ++ *user = '\0'; ++ char *password = user + 1; ++ if (strlen(password) > 0) { ++ password = ff_urldecode(password, 0); ++ smb2_set_password(libsmb2->ctx, password); ++ } ++ } ++ } ++ ++ if (url->domain) { ++ smb2_set_domain(libsmb2->ctx, url->domain); ++ } ++ ++ if (url->share) { ++ char *share = ff_urldecode(url->share, 0); ++ memset(url->share, 0, strlen(url->share)); ++ memcpy(url->share, share, strlen(share)); ++ } ++ ++ if (url->path) { ++ char *path = ff_urldecode(url->path, 0); ++ memset(url->path, 0, strlen(url->path)); ++ memcpy(url->path, path, strlen(path)); ++ } ++ ++ libsmb2->url = url; ++ } ++ ++ //https://github.com/sahlberg/libsmb2/issues/271 ++ //fix Very slow performance w/MacOS SMB server ++ //smb2_set_security_mode(libsmb2->ctx, SMB2_NEGOTIATE_SIGNING_ENABLED); ++ smb2_set_seal(libsmb2->ctx, libsmb2->smb2_seal); ++ smb2_set_authentication(libsmb2->ctx, 1);//SMB2_SEC_NTLMSSP ++ smb2_set_timeout(libsmb2->ctx, 60); ++ ++ if (smb2_connect_share(libsmb2->ctx, url->server, url->share, url->user) != 0) { ++ av_log(h, AV_LOG_ERROR, "smb2 connect share failed: %s\n", smb2_get_error(libsmb2->ctx)); ++ ret = AVERROR(ECONNREFUSED); ++ goto failed; ++ } ++ ++ int access; ++ if ((flags & AVIO_FLAG_WRITE) && (flags & AVIO_FLAG_READ)) { ++ access = O_CREAT | O_RDWR; ++ } else if (flags & AVIO_FLAG_WRITE) { ++ access = O_CREAT | O_WRONLY; ++ } else { ++ access = O_RDONLY; ++ } ++ ++ if (flags & AVIO_FLAG_DIRECT) { ++ if ((libsmb2->dir = smb2_opendir(libsmb2->ctx, url->path)) == NULL) { ++ av_log(h, AV_LOG_ERROR, "smb2 open dir failed: %s, error: %s\n", url->path, smb2_get_error(libsmb2->ctx)); ++ ret = AVERROR(ENOTDIR); ++ goto failed; ++ } ++ } else { ++ if ((libsmb2->fh = smb2_open(libsmb2->ctx, url->path, access)) == NULL) { ++ av_log(h, AV_LOG_ERROR, "smb2 open file failed: %s, error: %s\n", url->path, smb2_get_error(libsmb2->ctx)); ++ ret = AVERROR(ENOENT); ++ goto failed; ++ } ++ } ++ ++ struct smb2_stat_64 st = {0}; ++ ++ if (smb2_stat(libsmb2->ctx, url->path, &st) < 0) ++ av_log(h, AV_LOG_WARNING, "Cannot stat file: %s\n", smb2_get_error(libsmb2->ctx)); ++ else ++ libsmb2->filesize = st.smb2_size; ++ av_application_did_http_open(libsmb2->app_ctx, (void *)h, uri, 0, 200, libsmb2->filesize); ++ return 0; ++failed: ++ av_application_did_http_open(libsmb2->app_ctx, (void *)h, uri, ret, 500, 0); ++ if (libsmb2->fh && libsmb2->ctx) { ++ smb2_close(libsmb2->ctx, libsmb2->fh); ++ } ++ if (libsmb2->ctx) { ++ smb2_disconnect_share(libsmb2->ctx); ++ smb2_destroy_context(libsmb2->ctx); ++ } ++ if (libsmb2->url) { ++ smb2_destroy_url(libsmb2->url); ++ } ++ libsmb2->fh = NULL; ++ libsmb2->ctx = NULL; ++ libsmb2->url = NULL; ++ return -1; ++} ++ ++static int64_t libsmb2_seek(URLContext *h, int64_t pos, int whence) ++{ ++ LIBSMB2Context *libsmb2 = h->priv_data; ++ int64_t newpos; ++ ++ if (whence == AVSEEK_SIZE) { ++ if (libsmb2->filesize == -1) { ++ av_log(h, AV_LOG_ERROR, "smb2 seek failed,filesize is unknown.\n"); ++ return AVERROR(EIO); ++ } else { ++ return libsmb2->filesize; ++ } ++ } ++ ++ av_application_will_http_seek(libsmb2->app_ctx, (void *)h, h->filename, pos); ++ ++ if ((newpos = smb2_lseek(libsmb2->ctx, libsmb2->fh, pos, whence, NULL)) < 0) { ++ av_log(h, AV_LOG_ERROR, "smb2 seek failed: %s\n", smb2_get_error(libsmb2->ctx)); ++ av_application_did_http_seek(libsmb2->app_ctx, (void *)h, h->filename, pos, AVERROR(errno), 500); ++ return AVERROR(errno); ++ } ++ av_application_did_http_seek(libsmb2->app_ctx, (void *)h, h->filename, pos, 0, 200); ++ return newpos; ++} ++ ++static int libsmb2_read(URLContext *h, unsigned char *buf, int size) ++{ ++ LIBSMB2Context *libsmb2 = h->priv_data; ++ ++ uint8_t *buf1 = buf; ++ int buf_size1 = size; ++ int has_error = 0; ++ ++ while (buf_size1 > 0) { ++ int read = smb2_read(libsmb2->ctx, libsmb2->fh, buf1, buf_size1); ++ if (read < 0) { ++ av_log(h, AV_LOG_ERROR, "smb2 read file failed: %s\n", ++ smb2_get_error(libsmb2->ctx)); ++ has_error = 1; ++ break; ++ } ++ if (read == 0) { ++ // eof ++ break; ++ } ++ buf1 += read; ++ buf_size1 -= read; ++ } ++ ++ int bytes_read = size - buf_size1; ++ if (bytes_read > 0) ++ av_application_did_io_tcp_read(libsmb2->app_ctx, (void*)h, bytes_read); ++ ++ return bytes_read ? bytes_read : (has_error ? AVERROR(ENOTCONN) : AVERROR_EOF); ++} ++ ++static int libsmb2_write(URLContext *h, const unsigned char *buf, int size) ++{ ++ LIBSMB2Context *libsmb2 = h->priv_data; ++ int bytes_written; ++ ++ if ((bytes_written = smb2_write(libsmb2->ctx, libsmb2->fh, buf, size)) < 0) { ++ int ret = AVERROR(errno); ++ av_log(h, AV_LOG_ERROR, "smb2 write failed: %s\n", strerror(errno)); ++ return ret; ++ } ++ ++ return bytes_written; ++} ++ ++static int libsmb2_delete(URLContext *h) ++{ ++ LIBSMB2Context *libsmb2 = h->priv_data; ++ ++ struct smb2_url *url = smb2_parse_url(libsmb2->ctx, h->filename); ++ if (url == NULL) { ++ av_log(h, AV_LOG_ERROR, "smb2 parse url failed: %s\n", ++ smb2_get_error(libsmb2->ctx)); ++ return -1; ++ } else { ++ char *path = ff_urldecode(url->path, 0); ++ return smb2_unlink(libsmb2->ctx, path); ++ } ++} ++ ++static int libsmb2_move(URLContext *h_src, URLContext *h_dst) ++{ ++ LIBSMB2Context *libsmb2 = h_src->priv_data; ++ if (!libsmb2) ++ { ++ return -1; ++ } ++ ++ struct smb2_url *src_url = smb2_parse_url(libsmb2->ctx, h_src->filename); ++ struct smb2_url *dst_url = smb2_parse_url(libsmb2->ctx, h_dst->filename); ++ ++ if (src_url == NULL || dst_url == NULL) { ++ av_log(h_src, AV_LOG_ERROR, "smb2 parse url failed: %s\n", smb2_get_error(libsmb2->ctx)); ++ return -2; ++ } else { ++ char *src_path = ff_urldecode(src_url->path, 0); ++ char *dst_path = ff_urldecode(dst_url->path, 0); ++ return smb2_rename(libsmb2->ctx, src_path, dst_path); ++ } ++} ++ ++static int libsmb2_open_dir(URLContext *h) ++{ ++ LIBSMB2Context *libsmb2 = h->priv_data; ++ struct smb2_url *url = smb2_parse_url(libsmb2->ctx, h->filename); ++ if (url == NULL) { ++ av_log(h, AV_LOG_ERROR, "smb2 parse url failed: %s\n", smb2_get_error(libsmb2->ctx)); ++ return -1; ++ } else { ++ char *path = ff_urldecode(url->path, 0); ++ libsmb2->dir = smb2_opendir(libsmb2->ctx, path); ++ if (!libsmb2->dir){ ++ av_log(h, AV_LOG_ERROR, "smb2 open dir failed: %s\n", smb2_get_error(libsmb2->ctx)); ++ return 0; ++ } ++ return AVERROR(ENOTDIR); ++ } ++} ++ ++static int libsmb2_read_dir(URLContext *h, AVIODirEntry **next) ++{ ++ LIBSMB2Context *libsmb2 = h->priv_data; ++ AVIODirEntry *entry; ++ struct smb2dirent *dirent = NULL; ++ int skip_entry; ++ ++ *next = entry = ff_alloc_dir_entry(); ++ if (!entry) ++ return AVERROR(ENOMEM); ++ do { ++ skip_entry = 0; ++ dirent = smb2_readdir(libsmb2->ctx, libsmb2->dir); ++ if (!dirent) { ++ av_freep(next); ++ return 0; ++ } ++ } while (skip_entry || !strcmp(dirent->name, ".") || ++ !strcmp(dirent->name, "..")); ++ ++ entry->name = av_strdup(dirent->name); ++ if (!entry->name) { ++ av_freep(next); ++ return AVERROR(ENOMEM); ++ } ++ ++ struct smb2_stat_64 st = dirent->st; ++ switch (st.smb2_type) { ++ case SMB2_TYPE_DIRECTORY: ++ entry->type = AVIO_ENTRY_DIRECTORY; ++ break; ++ case SMB2_TYPE_FILE: ++ entry->type = AVIO_ENTRY_FILE; ++ break; ++ case SMB2_TYPE_LINK: ++ entry->type = AVIO_ENTRY_SYMBOLIC_LINK; ++ break; ++ default: ++ entry->type = AVIO_ENTRY_UNKNOWN; ++ break; ++ } ++ ++ entry->group_id = -1; ++ entry->user_id = -1; ++ entry->filemode = -1; ++ entry->size = st.smb2_size; ++ entry->modification_timestamp = INT64_C(1000000) * st.smb2_mtime; ++ entry->access_timestamp = INT64_C(1000000) * st.smb2_atime; ++ entry->status_change_timestamp = INT64_C(1000000) * st.smb2_ctime; ++ ++ return 0; ++} ++ ++static int libsmb2_close_dir(URLContext *h) ++{ ++ LIBSMB2Context *libsmb2 = h->priv_data; ++ if (libsmb2->dir) { ++ smb2_closedir(libsmb2->ctx, libsmb2->dir); ++ libsmb2->dir = NULL; ++ } ++ return 0; ++} ++ ++#define OFFSET(x) offsetof(LIBSMB2Context, x) ++#define D AV_OPT_FLAG_DECODING_PARAM ++#define E AV_OPT_FLAG_ENCODING_PARAM ++static const AVOption options[] = { ++ { "ijkapplication", "AVApplicationContext", OFFSET(app_ctx_intptr), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, .flags = D }, ++ { "smb2_seal", "enable smb3 encrypted connection", OFFSET(smb2_seal), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, .flags = D }, ++ {NULL} ++}; ++ ++static const AVClass libsmb2lient_context_class = { ++ .class_name = "libsmb2", ++ .item_name = av_default_item_name, ++ .option = options, ++ .version = LIBAVUTIL_VERSION_INT, ++}; ++ ++const URLProtocol ff_libsmb2_protocol = { ++ .name = "smb2", ++ .flags = URL_PROTOCOL_FLAG_NETWORK, ++ .priv_data_size = sizeof(LIBSMB2Context), ++ .priv_data_class = &libsmb2lient_context_class, ++ .url_open = libsmb2_open, ++ .url_read = libsmb2_read, ++ .url_write = libsmb2_write, ++ .url_seek = libsmb2_seek, ++ .url_close = libsmb2_close, ++ .url_delete = libsmb2_delete, ++ .url_move = libsmb2_move, ++ .url_open_dir = libsmb2_open_dir, ++ .url_read_dir = libsmb2_read_dir, ++ .url_close_dir = libsmb2_close_dir, ++}; +diff --git a/libavformat/protocols.c b/libavformat/protocols.c +index d1c1095..fb712a5 100644 +--- a/libavformat/protocols.c ++++ b/libavformat/protocols.c +@@ -75,6 +75,7 @@ extern const URLProtocol ff_librtmpte_protocol; + extern const URLProtocol ff_libsrt_protocol; + extern const URLProtocol ff_libssh_protocol; + extern const URLProtocol ff_libsmbclient_protocol; ++extern const URLProtocol ff_libsmb2_protocol; + extern const URLProtocol ff_libzmq_protocol; + extern const URLProtocol ff_ipfs_gateway_protocol; + extern const URLProtocol ff_ipns_gateway_protocol; +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.2/0020-URLProtocol-add-url_parse_priv-function-pointer.patch b/patches/ffmpeg-n8.1.2/0020-URLProtocol-add-url_parse_priv-function-pointer.patch new file mode 100644 index 000000000..1639144d4 --- /dev/null +++ b/patches/ffmpeg-n8.1.2/0020-URLProtocol-add-url_parse_priv-function-pointer.patch @@ -0,0 +1,55 @@ +From 861f2778fd0f40ec42a2dd1827afe0fc11786e32 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Thu, 29 May 2025 11:14:13 +0800 +Subject: URLProtocol add url_parse_priv function pointer + +--- + libavformat/demux.c | 11 +++++++++++ + libavformat/url.h | 2 ++ + 2 files changed, 13 insertions(+) + +diff --git a/libavformat/demux.c b/libavformat/demux.c +index 494e15f..524018f 100644 +--- a/libavformat/demux.c ++++ b/libavformat/demux.c +@@ -355,6 +355,17 @@ int avformat_open_input(AVFormatContext **ps, const char *filename, + ff_id3v2_free_extra_meta(&id3v2_extra_meta); + } + ++ //fill stream info ++ if (s->pb) { ++ URLContext *url_context = ffio_geturlcontext(s->pb); ++ if (url_context && url_context->prot) { ++ URLProtocol *prot = url_context->prot; ++ if (prot->url_parse_priv) { ++ prot->url_parse_priv(s, url_context); ++ } ++ } ++ } ++ + if ((ret = avformat_queue_attached_pictures(s)) < 0) + goto close; + +diff --git a/libavformat/url.h b/libavformat/url.h +index 53c6f13..f7e5bb2 100644 +--- a/libavformat/url.h ++++ b/libavformat/url.h +@@ -48,6 +48,7 @@ typedef struct URLContext { + int min_packet_size; /**< if non zero, the stream is packetized with this min packet size */ + } URLContext; + ++typedef struct AVFormatContext AVFormatContext; + typedef struct URLProtocol { + const char *name; + int (*url_open)( URLContext *h, const char *url, int flags); +@@ -93,6 +94,7 @@ typedef struct URLProtocol { + int (*url_close_dir)(URLContext *h); + int (*url_delete)(URLContext *h); + int (*url_move)(URLContext *h_src, URLContext *h_dst); ++ int (*url_parse_priv)(AVFormatContext *ic, URLContext *h); + const char *default_whitelist; + } URLProtocol; + +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.2/0021-bluray-protocol-add-dvd-fallback.patch b/patches/ffmpeg-n8.1.2/0021-bluray-protocol-add-dvd-fallback.patch new file mode 100644 index 000000000..e95664bac --- /dev/null +++ b/patches/ffmpeg-n8.1.2/0021-bluray-protocol-add-dvd-fallback.patch @@ -0,0 +1,54 @@ +From da901ac77bf2a238a947809aa1b276a13af6e055 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Thu, 29 May 2025 11:15:16 +0800 +Subject: bluray protocol add dvd fallback + +--- + libavformat/demux.c | 23 +++++++++++++++++++---- + 1 file changed, 19 insertions(+), 4 deletions(-) + +diff --git a/libavformat/demux.c b/libavformat/demux.c +index 524018f..531992f 100644 +--- a/libavformat/demux.c ++++ b/libavformat/demux.c +@@ -161,7 +161,8 @@ static int init_input(AVFormatContext *s, const char *filename, + int ret; + AVProbeData pd = { filename, NULL, 0 }; + int score = AVPROBE_SCORE_RETRY; +- ++ AVDictionary *tmp_opts = NULL; ++ + if (s->pb) { + s->flags |= AVFMT_FLAG_CUSTOM_IO; + if (!s->iformat) +@@ -176,10 +177,24 @@ static int init_input(AVFormatContext *s, const char *filename, + if ((s->iformat && s->iformat->flags & AVFMT_NOFILE) || + (!s->iformat && (s->iformat = av_probe_input_format2(&pd, 0, &score)))) + return score; ++ ++ if (options && av_stristart(filename, "bluray://", NULL)) { ++ av_dict_copy(&tmp_opts, *options, 0); ++ } + +- if ((ret = s->io_open(s, &s->pb, filename, AVIO_FLAG_READ | s->avio_flags, options)) < 0) +- return ret; +- ++ if ((ret = s->io_open(s, &s->pb, filename, AVIO_FLAG_READ | s->avio_flags, ++ options)) < 0) { ++ if (av_stristart(filename, "bluray://", NULL)) { ++ const char *a_name = av_strireplace(filename, "bluray://", ""); ++ ret = init_input(s, a_name, &tmp_opts); ++ av_dict_free(&tmp_opts); ++ return ret; ++ } else { ++ av_dict_free(&tmp_opts); ++ return ret; ++ } ++ } ++ av_dict_free(&tmp_opts); + if (s->iformat) + return 0; + return av_probe_input_buffer2(s->pb, &s->iformat, filename, +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.2/0022-custom-bluray-fs-for-network-Blu-ray-Disc-and-BDMV.patch b/patches/ffmpeg-n8.1.2/0022-custom-bluray-fs-for-network-Blu-ray-Disc-and-BDMV.patch new file mode 100644 index 000000000..283624f95 --- /dev/null +++ b/patches/ffmpeg-n8.1.2/0022-custom-bluray-fs-for-network-Blu-ray-Disc-and-BDMV.patch @@ -0,0 +1,744 @@ +From 016c42a6ca0b61fef87a703b7e71765af8abc501 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Fri, 15 May 2026 17:00:02 +0800 +Subject: custom bluray fs for network Blu-ray Disc and BDMV + +--- + libavformat/Makefile | 2 +- + libavformat/bluray.c | 128 ++++++++- + libavformat/bluray_custom_fs.c | 459 +++++++++++++++++++++++++++++++++ + libavformat/bluray_custom_fs.h | 33 +++ + 4 files changed, 610 insertions(+), 12 deletions(-) + create mode 100644 libavformat/bluray_custom_fs.c + create mode 100644 libavformat/bluray_custom_fs.h + +diff --git a/libavformat/Makefile b/libavformat/Makefile +index 6100e33..54e88cf 100644 +--- a/libavformat/Makefile ++++ b/libavformat/Makefile +@@ -694,7 +694,7 @@ OBJS-$(CONFIG_VAPOURSYNTH_DEMUXER) += vapoursynth.o + # protocols I/O + OBJS-$(CONFIG_ANDROID_CONTENT_PROTOCOL) += file.o + OBJS-$(CONFIG_ASYNC_PROTOCOL) += async.o +-OBJS-$(CONFIG_BLURAY_PROTOCOL) += bluray.o ++OBJS-$(CONFIG_BLURAY_PROTOCOL) += bluray.o bluray_custom_fs.o + OBJS-$(CONFIG_CACHE_PROTOCOL) += cache.o + OBJS-$(CONFIG_CONCAT_PROTOCOL) += concat.o + OBJS-$(CONFIG_CONCATF_PROTOCOL) += concat.o +diff --git a/libavformat/bluray.c b/libavformat/bluray.c +index 1845551..eb43340 100644 +--- a/libavformat/bluray.c ++++ b/libavformat/bluray.c +@@ -21,23 +21,27 @@ + */ + + #include +- ++#include "libavformat/urldecode.h" + #include "libavutil/avstring.h" + #include "libavformat/url.h" + #include "libavutil/opt.h" ++#include "bluray_custom_fs.h" ++#include "libavutil/dict.h" ++#include "libavformat/avformat.h" + +-#define BLURAY_PROTO_PREFIX "bluray:" ++#define BLURAY_PROTO_PREFIX "bluray://" + #define MIN_PLAYLIST_LENGTH 180 /* 3 min */ + + typedef struct { + const AVClass *class; + + BLURAY *bd; +- ++ fs_access *access; + int playlist; + int angle; + int chapter; + /*int region;*/ ++ int title_idx; + } BlurayContext; + + #define OFFSET(x) offsetof(BlurayContext, x) +@@ -106,23 +110,58 @@ static int bluray_close(URLContext *h) + if (bd->bd) { + bd_close(bd->bd); + } +- ++ destroy_bluray_custom_access(&bd->access); + return 0; + } + +-static int bluray_open(URLContext *h, const char *path, int flags) ++#ifdef DEBUG_BLURAY ++#include ++#define BLURAY_DEBUG_MASK 0xFFFFF //(0xFFFFF & ~DBG_STREAM) ++ ++static void bluray_DebugHandler(const char *psz) + { ++ size_t len = strlen(psz); ++ if(len < 1) return; ++ av_log(NULL, AV_LOG_INFO, "[bluray] %s\n",psz); ++} ++#endif ++ ++ ++static int bluray_open(URLContext *h, const char *path, int flags, AVDictionary **options) ++{ ++#ifdef DEBUG_BLURAY ++ bd_set_debug_mask(BLURAY_DEBUG_MASK); ++ bd_set_debug_handler(bluray_DebugHandler); ++#endif ++ + BlurayContext *bd = h->priv_data; + int num_title_idx; + const char *diskname = path; + + av_strstart(path, BLURAY_PROTO_PREFIX, &diskname); + +- bd->bd = bd_open(diskname, NULL); +- if (!bd->bd) { ++ diskname = ff_urldecode(diskname, 0); ++ ++ fs_access *access = NULL; ++ int is_file = av_strstart(diskname, "file://", NULL) || av_strstart(diskname, "/", NULL); ++ ++ // file protocl can't handle AVIO_FLAG_DIRECT flag,so file not create custom access ++ if (!is_file) { ++ //set read packet buffer size is important! the default packet size is 32768, when use smb2 protocol, download speed is limited to 2MB; but when set the size to 1048576, download speed is 16MB; ++ h->max_packet_size = 1048576; ++ access = create_bluray_custom_access(diskname, options, &h->interrupt_callback); ++ } ++ ++ bd->bd = bd_open_fs(diskname, NULL, access); ++ ++ if (!bd->bd || is_bluray_custom_access_cancelled(access)) { + av_log(h, AV_LOG_ERROR, "bd_open() failed\n"); ++ if (access) { ++ destroy_bluray_custom_access(&access); ++ } + return AVERROR(EIO); + } ++ bd->access = access; + + /* check if disc can be played */ + if (check_disc_info(h) < 0) { +@@ -150,6 +189,9 @@ static int bluray_open(URLContext *h, const char *path, int flags) + int i; + for (i = 0; i < num_title_idx; i++) { + BLURAY_TITLE_INFO *info = bd_get_title_info(bd->bd, i, 0); ++ if (!info) { ++ continue; ++ } + + av_log(h, AV_LOG_INFO, "playlist %05d.mpls (%d:%02d:%02d)\n", + info->playlist, +@@ -159,17 +201,19 @@ static int bluray_open(URLContext *h, const char *path, int flags) + + if (info->duration > duration) { + bd->playlist = info->playlist; ++ bd->title_idx = i; + duration = info->duration; + } + + bd_free_title_info(info); + } +- av_log(h, AV_LOG_INFO, "selected %05d.mpls\n", bd->playlist); ++ av_log(h, AV_LOG_INFO, "select longest playlist: %05d.mpls\n", bd->playlist); + } + + /* select playlist */ +- if (bd_select_playlist(bd->bd, bd->playlist) <= 0) { +- av_log(h, AV_LOG_ERROR, "bd_select_playlist(%05d.mpls) failed\n", bd->playlist); ++ if (bd->playlist >= 0 && bd_select_playlist(bd->bd, bd->playlist) <= 0) { ++ av_log(h, AV_LOG_ERROR, "bd_select_playlist(%05d.mpls) failed\n", ++ bd->playlist); + return AVERROR(EIO); + } + +@@ -222,13 +266,75 @@ static int64_t bluray_seek(URLContext *h, int64_t pos, int whence) + return AVERROR(EINVAL); + } + ++static int bluray_parse_priv(AVFormatContext *ic, URLContext *h) ++{ ++ BlurayContext *bd = h->priv_data; ++ BLURAY_TITLE_INFO *title_info = NULL; ++ BLURAY_CLIP_INFO clip_info; ++ ++ int v_idx = 0; ++ int a_idx = 0; ++ int s_idx = 0; ++ int ret = 0; ++ ++ if (!bd || !bd->bd) { ++ return AVERROR(EFAULT); ++ } ++ ++ title_info = bd_get_title_info(bd->bd, bd->title_idx, 0); ++ if (!title_info) { ++ return AVERROR(EFAULT); ++ } ++ ++ if (title_info->clip_count <= 0) { ++ ret = EFAULT; ++ goto fail; ++ } ++ clip_info = title_info->clips[0]; ++ ++ for (int i = 0; i < ic->nb_streams; i++) { ++ if (ic->streams[i] && ic->streams[i]->codecpar) { ++ switch (ic->streams[i]->codecpar->codec_type) { ++ case AVMEDIA_TYPE_VIDEO: ++ if (v_idx < clip_info.video_stream_count) { ++ av_log(h, AV_LOG_INFO, "video stream %d lang = %s\n", v_idx, clip_info.video_streams[v_idx].lang); ++ av_dict_set(&ic->streams[i]->metadata, "language", clip_info.video_streams[v_idx].lang, AV_DICT_DONT_OVERWRITE); ++ v_idx++; ++ } ++ break; ++ case AVMEDIA_TYPE_AUDIO: ++ if (a_idx < clip_info.audio_stream_count) { ++ av_log(h, AV_LOG_INFO, "audio stream %d lang = %s\n", a_idx, clip_info.audio_streams[a_idx].lang); ++ av_dict_set(&ic->streams[i]->metadata, "language", clip_info.audio_streams[a_idx].lang, AV_DICT_DONT_OVERWRITE); ++ a_idx++; ++ } ++ break; ++ case AVMEDIA_TYPE_SUBTITLE: ++ if (s_idx < clip_info.pg_stream_count) { ++ av_log(h, AV_LOG_INFO, "subtitle stream %d lang = %s\n", s_idx, clip_info.pg_streams[s_idx].lang); ++ av_dict_set(&ic->streams[i]->metadata, "language", clip_info.pg_streams[s_idx].lang, AV_DICT_DONT_OVERWRITE); ++ s_idx++; ++ } ++ break; ++ default: ++ break; ++ } ++ } ++ } ++ ++fail: ++ bd_free_title_info(title_info); ++ ++ return ret != 0 ? AVERROR(ret) : 0; ++} + + const URLProtocol ff_bluray_protocol = { + .name = "bluray", + .url_close = bluray_close, +- .url_open = bluray_open, ++ .url_open2 = bluray_open, + .url_read = bluray_read, + .url_seek = bluray_seek, ++ .url_parse_priv = bluray_parse_priv, + .priv_data_size = sizeof(BlurayContext), + .priv_data_class = &bluray_context_class, + }; +diff --git a/libavformat/bluray_custom_fs.c b/libavformat/bluray_custom_fs.c +new file mode 100644 +index 0000000..b82e0b2 +--- /dev/null ++++ b/libavformat/bluray_custom_fs.c +@@ -0,0 +1,459 @@ ++// ++// bluray_custom_fs_smb2.c ++// ++// Created by Reach Matt on 2024/9/13. ++// ++// ++// Copyright (C) 2021 Matt Reach// ++// Licensed under the Apache License, Version 2.0 (the "License"); ++// you may not use this file except in compliance with the License. ++// You may obtain a copy of the License at ++// ++// http://www.apache.org/licenses/LICENSE-2.0 ++// ++// Unless required by applicable law or agreed to in writing, software ++// distributed under the License is distributed on an "AS IS" BASIS, ++// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++// See the License for the specific language governing permissions and ++// limitations under the License. ++ ++#include "bluray_custom_fs.h" ++#include "url.h" ++#include "application.h" ++#include "libavutil/mem.h" ++#include "libavutil/error.h" ++#include "libavutil/avstring.h" ++#include ++#include ++ ++#ifndef UDF_BLOCK_SIZE ++# define UDF_BLOCK_SIZE 2048 ++#endif ++ ++typedef struct ff_builtin_io { ++ URLContext *url_context; ++ int64_t offset; ++ AVApplicationContext *app_ctx; ++ int64_t last_read; ++} ff_builtin_io; ++ ++typedef struct ff_bluray_access { ++ const char *url; ++ AVDictionary *opts; ++ ff_builtin_io *io; ++ const AVIOInterruptCB *int_cb; ++ int cancel; ++} ff_bluray_access; ++ ++static void close_builtin_io(ff_builtin_io *io) ++{ ++ if (!io) { ++ return; ++ } ++ if (io->url_context) { ++ ffurl_closep(&io->url_context); ++ } ++} ++ ++static int create_builtin_io(ff_builtin_io **io, const char *url, ++ AVDictionary **opts, int is_dir, ++ const AVIOInterruptCB *int_cb) ++{ ++ if (!io) { ++ return -1; ++ } ++ ++ ff_builtin_io *app = av_mallocz(sizeof(ff_builtin_io)); ++ if (!app) { ++ return -2; ++ } ++ ++ const char *protocol_whitelist = NULL; ++ ++ if (opts) { ++ const AVDictionary *dict = *opts; ++ ++ if (!av_strstart(url, "http", NULL) && ++ !av_strstart(url, "smb2", NULL)) { ++ AVDictionaryEntry *app_dict = ++ av_dict_get(dict, "ijkapplication", NULL, 0); ++ if (app_dict) { ++ app->app_ctx = ++ (AVApplicationContext *)av_dict_strtoptr(app_dict->value); ++ av_application_will_http_open(app->app_ctx, NULL, url); ++ } ++ } ++ ++ AVDictionaryEntry *proto_dict = ++ av_dict_get(dict, "protocol_whitelist", NULL, 0); ++ if (proto_dict) { ++ protocol_whitelist = av_strdup(proto_dict->value); ++ } ++ } ++ ++ if (protocol_whitelist == NULL || strlen(protocol_whitelist) == 0) { ++ protocol_whitelist = "ijkio,ijkhttphook,http,tcp,https,tls,file,smb2"; ++ } ++ ++ int flags = AVIO_FLAG_READ; ++ if (is_dir) { ++ flags |= AVIO_FLAG_DIRECT; ++ } ++ ++ int ret = ffurl_open_whitelist(&app->url_context, url, flags, int_cb, opts, ++ protocol_whitelist, NULL, NULL); ++ ++ av_application_did_http_open(app->app_ctx, (void *)app->url_context, url, ++ ret < 0 ? AVERROR(errno) : 0, ++ ret < 0 ? 500 : 200, 0); ++ if (ret < 0) { ++ close_builtin_io(app); ++ av_freep(&app); ++ } ++ *io = app; ++ return ret; ++} ++ ++static int64_t seek_builtin_io(ff_builtin_io *io, int64_t offset, int origin) ++{ ++ if (!io) { ++ return 0; ++ } ++ ++ if (io->offset == offset && origin == SEEK_SET) { ++ io->last_read = offset; ++ return offset; ++ } ++ ++ av_application_will_http_seek(io->app_ctx, (void *)io->url_context, ++ io->url_context->filename, offset); ++ ++ int64_t pos = ++ io->url_context->prot->url_seek(io->url_context, offset, origin); ++ if (pos < 0) { ++ av_application_did_http_seek(io->app_ctx, (void *)io->url_context, ++ io->url_context->filename, offset, ++ AVERROR(errno), 500); ++ return AVERROR(errno); ++ } ++ io->last_read = pos; ++ io->offset = pos; ++ av_application_did_http_seek(io->app_ctx, (void *)io->url_context, ++ io->url_context->filename, offset, 0, 200); ++ return pos; ++} ++ ++static int write_builtin_io(ff_builtin_io *io, uint8_t *buf, int buf_size) ++{ ++ if (!io) { ++ return 0; ++ } ++ ++ return io->url_context->prot->url_write(io->url_context, buf, buf_size); ++} ++ ++static int read_builtin_io(ff_builtin_io *io, uint8_t *buf, int buf_size, ++ int64_t pos) ++{ ++ if (!io) { ++ return 0; ++ } ++ ++ uint8_t *buf1 = buf; ++ int buf_size1 = buf_size; ++ int read = 0; ++ ++ seek_builtin_io(io, pos, SEEK_SET); ++ ++ while (buf_size1 > 0) { ++ read = ++ io->url_context->prot->url_read(io->url_context, buf1, buf_size1); ++ if (read <= 0) { ++ // maybe AVERROR_EOF ++ break; ++ } ++ io->offset += read; ++ buf1 += read; ++ buf_size1 -= read; ++ } ++ if (read == AVERROR_EXIT) { ++ return AVERROR_EXIT; ++ } ++ int bytes = buf_size - buf_size1; ++ if (bytes == 0 && read == AVERROR_EOF) { ++ return AVERROR_EOF; ++ } else { ++ av_application_did_io_tcp_read(io->app_ctx, (void *)io->url_context, ++ bytes); ++ return bytes; ++ } ++} ++ ++static int read_blocks(void *fs_handle, void *buf, int lba, int num_blocks) ++{ ++ ff_bluray_access *access = fs_handle; ++ if (access->cancel == 1) { ++ return AVERROR_EXIT; ++ } ++ ++ ff_builtin_io *io = access->io; ++ if (!io) { ++ return -1; ++ } ++ int got = -1; ++ int64_t pos = (int64_t)lba * UDF_BLOCK_SIZE; ++ int buf_size = num_blocks * UDF_BLOCK_SIZE; ++ int bytes = read_builtin_io(io, buf, buf_size, pos); ++ if (bytes == AVERROR_EXIT) { ++ access->cancel = 1; ++ return AVERROR_EXIT; ++ } ++ ++ if (bytes > 0) { ++ got = (int)(bytes / UDF_BLOCK_SIZE); ++ } ++ return got; ++} ++ ++void destroy_bluray_custom_access(fs_access **p) ++{ ++ if (p) { ++ fs_access *access = *p; ++ if (access) { ++ ff_bluray_access* ba = access->fs_handle; ++ ff_builtin_io *io = ba->io; ++ if (io) { ++ close_builtin_io(io); ++ av_freep(&io); ++ } ++ av_free(ba->url); ++ av_dict_free(&ba->opts); ++ } ++ av_freep(p); ++ } ++} ++ ++// ------------------------------------------------------------------------------------------- ++// open_file for bdmv ++ ++static void _file_close(BD_FILE_H *file) ++{ ++ if (file) { ++ ff_builtin_io *io = file->internal; ++ if (io) { ++ close_builtin_io(io); ++ av_free(io); ++ file->internal = NULL; ++ } ++ av_freep(&file); ++ } ++} ++ ++static int64_t _file_read(BD_FILE_H *file, uint8_t *buf, int64_t size) ++{ ++ if (size <= 0) { ++ return 0; ++ } ++ ff_builtin_io *io = file->internal; ++ if (!io) { ++ return -1; ++ } ++ ++ int read = read_builtin_io(io, buf, size, io->last_read); ++ ++ if (read > 0) { ++ io->last_read += read; ++ } ++ ++ return read; ++} ++ ++static int64_t _file_write(BD_FILE_H *file, uint8_t *buf, int64_t size) ++{ ++ if (size <= 0) { ++ return 0; ++ } ++ ff_builtin_io *io = file->internal; ++ if (!io) { ++ return -1; ++ } ++ return write_builtin_io(io, buf, size); ++} ++ ++// origin: SEEK_SET, SEEK_CUR or SEEK_END ++static int64_t _file_seek(BD_FILE_H *file, int64_t offset, int32_t origin) ++{ ++ ff_builtin_io *io = file->internal; ++ if (!io) { ++ return -1; ++ } ++ return seek_builtin_io(io, offset, origin); ++} ++ ++static int64_t _file_tell(BD_FILE_H *file) ++{ ++ ff_builtin_io *io = file->internal; ++ if (!io) { ++ return -1; ++ } ++ return io->last_read; ++ // return seek_builtin_io(io, 0, SEEK_CUR); ++} ++ ++static struct bd_file_s *open_file(void *fs_handle, const char *rel_path) ++{ ++ ff_bluray_access *access = fs_handle; ++ ++ if (access->cancel == 1) { ++ return NULL; ++ } ++ ++ const char *url = av_append_path_component(access->url, rel_path); ++ if (!url) { ++ return NULL; ++ } ++ AVDictionary *opts = NULL; ++ av_dict_copy(&opts, access->opts, 0); ++ ++ ff_builtin_io *io = NULL; ++ int ret = create_builtin_io(&io, url, &opts, 0, access->int_cb); ++ av_dict_free(&opts); ++ ++ if (0 != ret) { ++ av_log(NULL, AV_LOG_ERROR, "[bluray] can't open %s,error:%s", url, ++ av_err2str(ret)); ++ av_free(url); ++ return NULL; ++ } ++ av_free(url); ++ BD_FILE_H *file = av_malloc(sizeof(BD_FILE_H)); ++ if (!file) { ++ close_builtin_io(io); ++ av_free(io); ++ return NULL; ++ } ++ ++ file->internal = io; ++ file->close = _file_close; ++ file->seek = _file_seek; ++ file->read = _file_read; ++ file->write = _file_write; ++ file->tell = _file_tell; ++ ++ return file; ++} ++ ++// open_dir for bdmv ++static void _dir_close(BD_DIR_H *dir) ++{ ++ if (dir) { ++ ff_builtin_io *io = dir->internal; ++ if (!io) { ++ return; ++ } ++ close_builtin_io(io); ++ av_free(io); ++ dir->internal = NULL; ++ av_freep(&dir); ++ } ++} ++ ++static int _dir_read(BD_DIR_H *dir, BD_DIRENT *entry) ++{ ++ ff_builtin_io *io = dir->internal; ++ if (!io) { ++ return -1; ++ } ++ ++ AVIODirEntry *next = NULL; ++ ++ if (io->url_context->prot->url_read_dir(io->url_context, &next) < 0 || !next) { ++ return -2; ++ } ++ ++ strncpy(entry->d_name, next->name, sizeof(entry->d_name)); ++ entry->d_name[sizeof(entry->d_name) - 1] = 0; ++ ++ return 0; ++} ++ ++static struct bd_dir_s* open_dir (void *fs_handle, const char *rel_path) ++{ ++ ff_bluray_access *access = fs_handle; ++ ++ if (access->cancel == 1) { ++ return NULL; ++ } ++ ++ const char *url = av_append_path_component(access->url, rel_path); ++ if (!url) { ++ return NULL; ++ } ++ AVDictionary *opts = NULL; ++ av_dict_copy(&opts, access->opts, 0); ++ ++ ff_builtin_io *io = NULL; ++ int ret = create_builtin_io(&io, url, &opts, 1, access->int_cb); ++ av_dict_free(&opts); ++ ++ if (0 != ret) { ++ av_log(NULL, AV_LOG_ERROR, "[bluray] can't open dir %s,error:%s", url, ++ av_err2str(ret)); ++ av_free(url); ++ return NULL; ++ } ++ av_free(url); ++ BD_DIR_H *dir = av_malloc(sizeof(BD_DIR_H)); ++ if (!dir) { ++ close_builtin_io(io); ++ av_free(io); ++ return NULL; ++ } ++ ++ dir->internal = io; ++ dir->close = _dir_close; ++ dir->read = _dir_read; ++ ++ return dir; ++} ++ ++int is_bluray_custom_access_cancelled(fs_access *access) ++{ ++ if (!access) { ++ return 0; ++ } ++ ++ ff_bluray_access *opaque = access->fs_handle; ++ return opaque->cancel; ++} ++ ++// 构建fs_access结构体 ++fs_access * create_bluray_custom_access(const char *url, AVDictionary **options, const AVIOInterruptCB *int_cb) ++{ ++ ff_bluray_access * opaque = av_mallocz(sizeof(ff_bluray_access)); ++ if (!opaque) { ++ return NULL; ++ } ++ ++ if (opaque) { ++ opaque->url = av_strdup(url); ++ if (options) { ++ av_dict_copy(&opaque->opts, *options, 0); ++ } ++ opaque->int_cb = int_cb; ++ int ret = create_builtin_io(&opaque->io, url, options, 0, int_cb); ++ if (0 != ret) { ++ av_log(NULL, AV_LOG_ERROR, "[bluray] can't open file %s,error:%s", ++ url, av_err2str(ret)); ++ } ++ ++ fs_access *access = av_malloc(sizeof(fs_access)); ++ access->fs_handle = opaque; ++ access->read_blocks = read_blocks; ++ access->open_file = open_file; ++ access->open_dir = open_dir; ++ ++ return access; ++ } ++ return NULL; ++} +\ No newline at end of file +diff --git a/libavformat/bluray_custom_fs.h b/libavformat/bluray_custom_fs.h +new file mode 100644 +index 0000000..058d2da +--- /dev/null ++++ b/libavformat/bluray_custom_fs.h +@@ -0,0 +1,33 @@ ++// ++// bluray_custom_fs.h ++// ++// Created by Reach Matt on 2024/9/13. ++// ++// ++// Copyright (C) 2021 Matt Reach// ++// Licensed under the Apache License, Version 2.0 (the "License"); ++// you may not use this file except in compliance with the License. ++// You may obtain a copy of the License at ++// ++// http://www.apache.org/licenses/LICENSE-2.0 ++// ++// Unless required by applicable law or agreed to in writing, software ++// distributed under the License is distributed on an "AS IS" BASIS, ++// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++// See the License for the specific language governing permissions and ++// limitations under the License. ++ ++#ifndef bluray_custom_fs_h ++#define bluray_custom_fs_h ++ ++#include ++ ++typedef struct fs_access fs_access; ++typedef struct AVDictionary AVDictionary; ++typedef struct AVIOInterruptCB AVIOInterruptCB; ++ ++void destroy_bluray_custom_access(fs_access **p); ++int is_bluray_custom_access_cancelled(fs_access *access); ++fs_access *create_bluray_custom_access(const char *url, AVDictionary **options, ++ const AVIOInterruptCB *int_cb); ++#endif /* bluray_custom_fs_h */ +\ No newline at end of file +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.2/0023-bluray-open-and-find-the-right-m2ts-then-read-seek-i.patch b/patches/ffmpeg-n8.1.2/0023-bluray-open-and-find-the-right-m2ts-then-read-seek-i.patch new file mode 100644 index 000000000..28afb7341 --- /dev/null +++ b/patches/ffmpeg-n8.1.2/0023-bluray-open-and-find-the-right-m2ts-then-read-seek-i.patch @@ -0,0 +1,71 @@ +From afbd088c5f65683f1edb1383224eeddbc4bf718b Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Tue, 12 May 2026 11:45:56 +0800 +Subject: bluray open and find the right m2ts, then read/seek it + direactly instread of bluray logic. + +--- + libavformat/bluray.c | 25 ++++++++++++++++++++----- + 1 file changed, 20 insertions(+), 5 deletions(-) + +diff --git a/libavformat/bluray.c b/libavformat/bluray.c +index eb43340..254538f 100644 +--- a/libavformat/bluray.c ++++ b/libavformat/bluray.c +@@ -42,6 +42,7 @@ typedef struct { + int chapter; + /*int region;*/ + int title_idx; ++ int stream_opened; + } BlurayContext; + + #define OFFSET(x) offsetof(BlurayContext, x) +@@ -226,7 +227,8 @@ static int bluray_open(URLContext *h, const char *path, int flags, AVDictionary + if (bd->chapter > 1) { + bd_seek_chapter(bd->bd, bd->chapter - 1); + } +- ++ /* will read ts soon */ ++ bd->stream_opened = 1; + return 0; + } + +@@ -238,7 +240,13 @@ static int bluray_read(URLContext *h, unsigned char *buf, int size) + if (!bd || !bd->bd) { + return AVERROR(EFAULT); + } +- ++ if (bd->stream_opened) { ++ int read = (int)bd_file_read(bd->bd, buf, size); ++ if (read == 0) { ++ return AVERROR_EOF; ++ } ++ return read; ++ } + len = bd_read(bd->bd, buf, size); + + return len == 0 ? AVERROR_EOF : len; +@@ -256,10 +264,17 @@ static int64_t bluray_seek(URLContext *h, int64_t pos, int whence) + case SEEK_SET: + case SEEK_CUR: + case SEEK_END: +- return bd_seek(bd->bd, pos); +- ++ if (bd->stream_opened) { ++ return bd_file_seek(bd->bd, pos, whence); ++ } else { ++ return bd_seek(bd->bd, pos); ++ } + case AVSEEK_SIZE: +- return bd_get_title_size(bd->bd); ++ if (bd->stream_opened) { ++ return bd_file_size(bd->bd); ++ } else { ++ return bd_get_title_size(bd->bd); ++ } + } + + av_log(h, AV_LOG_ERROR, "Unsupported whence operation %d\n", whence); +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.2/0024-fix-android-ffmpeg7-test.c-1-10-fatal-error-libxml2-.patch b/patches/ffmpeg-n8.1.2/0024-fix-android-ffmpeg7-test.c-1-10-fatal-error-libxml2-.patch new file mode 100644 index 000000000..6095da59e --- /dev/null +++ b/patches/ffmpeg-n8.1.2/0024-fix-android-ffmpeg7-test.c-1-10-fatal-error-libxml2-.patch @@ -0,0 +1,26 @@ +From 6e2fc211aad20c73775334f040fbab8543236672 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Fri, 6 Jun 2025 14:09:50 +0800 +Subject: fix android ffmpeg7 test.c:1:10: fatal error: + 'libxml2/libxml/xmlversion.h' + +--- + configure | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/configure b/configure +index 10c688c..9761c84 100755 +--- a/configure ++++ b/configure +@@ -7448,7 +7448,7 @@ enabled libzmq && require_pkg_config libzmq "libzmq >= 4.2.1" zmq.h z + enabled libzvbi && require_pkg_config libzvbi zvbi-0.2 libzvbi.h vbi_decoder_new && + { test_cpp_condition libzvbi.h "VBI_VERSION_MAJOR > 0 || VBI_VERSION_MINOR > 2 || VBI_VERSION_MINOR == 2 && VBI_VERSION_MICRO >= 28" || + enabled gpl || die "ERROR: libzvbi requires version 0.2.28 or --enable-gpl."; } +-enabled libxml2 && require_pkg_config libxml2 libxml-2.0 libxml2/libxml/xmlversion.h xmlCheckVersion ++enabled libxml2 && require_pkg_config libxml2 libxml-2.0 libxml/xmlversion.h xmlCheckVersion + enabled mbedtls && { check_pkg_config mbedtls mbedtls mbedtls/x509_crt.h mbedtls_x509_crt_init || + check_pkg_config mbedtls mbedtls mbedtls/ssl.h mbedtls_ssl_init || + check_lib mbedtls mbedtls/ssl.h mbedtls_ssl_init -lmbedtls -lmbedx509 -lmbedcrypto || +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.2/0025-fix-dash-init-fragment-url-is-invalid-truncated-bug.patch b/patches/ffmpeg-n8.1.2/0025-fix-dash-init-fragment-url-is-invalid-truncated-bug.patch new file mode 100644 index 000000000..c57a757ed --- /dev/null +++ b/patches/ffmpeg-n8.1.2/0025-fix-dash-init-fragment-url-is-invalid-truncated-bug.patch @@ -0,0 +1,43 @@ +From 83bd2fe0fa738f659c59d28cc484501d7260913a Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Wed, 21 May 2025 18:05:52 +0800 +Subject: fix dash init fragment url is "invalid:truncated" bug + +--- + libavformat/dashdec.c | 12 +++++++++++- + 1 file changed, 11 insertions(+), 1 deletion(-) + +diff --git a/libavformat/dashdec.c b/libavformat/dashdec.c +index 9a2ea78..5927a6e 100644 +--- a/libavformat/dashdec.c ++++ b/libavformat/dashdec.c +@@ -479,6 +479,10 @@ static char *get_content_url(xmlNodePtr *baseurl_nodes, + int i; + char *text; + char *url = NULL; ++ ++ if (strlen(val) >= max_url_size) { ++ max_url_size += 256; ++ } + char *tmp_str = av_mallocz(max_url_size); + + if (!tmp_str) +@@ -497,8 +501,14 @@ static char *get_content_url(xmlNodePtr *baseurl_nodes, + } + } + +- if (val) ++ if (val) { ++ int tmp_max_url_size = strlen(tmp_str) + strlen(val) + 1; ++ if (tmp_max_url_size > max_url_size) { ++ max_url_size = tmp_max_url_size; ++ tmp_str = av_realloc(tmp_str, max_url_size); ++ } + ff_make_absolute_url(tmp_str, max_url_size, tmp_str, val); ++ } + + if (rep_id_val) { + url = av_strireplace(tmp_str, "$RepresentationID$", rep_id_val); +-- +2.50.1 (Apple Git-155) + diff --git a/patches/ffmpeg-n8.1.2/0026-add-webp-demuxer-and-libwebp-decoder.patch b/patches/ffmpeg-n8.1.2/0026-add-webp-demuxer-and-libwebp-decoder.patch new file mode 100644 index 000000000..2e6de76aa --- /dev/null +++ b/patches/ffmpeg-n8.1.2/0026-add-webp-demuxer-and-libwebp-decoder.patch @@ -0,0 +1,804 @@ +From 2ca732722b9799c24fd1589ab960f18b1c8108fd Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Fri, 8 May 2026 19:02:39 +0800 +Subject: add webp demuxer and libwebp decoder + +--- + configure | 7 +- + libavcodec/Makefile | 1 + + libavcodec/allcodecs.c | 1 + + libavcodec/codec_desc.c | 9 + + libavcodec/codec_id.h | 1 + + libavcodec/libwebpdec.c | 376 +++++++++++++++++++++++++++++++++++++++ + libavformat/Makefile | 1 + + libavformat/allformats.c | 1 + + libavformat/webpdec.c | 296 ++++++++++++++++++++++++++++++ + 9 files changed, 692 insertions(+), 1 deletion(-) + create mode 100644 libavcodec/libwebpdec.c + create mode 100644 libavformat/webpdec.c + +diff --git a/configure b/configure +index 9761c84..0ecc99c 100755 +--- a/configure ++++ b/configure +@@ -7427,7 +7427,12 @@ enabled libvpx && { + enabled libvvenc && require_pkg_config libvvenc "libvvenc >= 1.6.1" "vvenc/vvenc.h" vvenc_get_version + enabled libwebp && { + enabled libwebp_encoder && require_pkg_config libwebp "libwebp >= 0.2.0" webp/encode.h WebPGetEncoderVersion +- enabled libwebp_anim_encoder && check_pkg_config libwebp_anim_encoder "libwebpmux >= 0.4.0" webp/mux.h WebPAnimEncoderOptionsInit; } ++ enabled libwebp_anim_encoder && check_pkg_config libwebp_anim_encoder "libwebpmux >= 0.4.0" webp/mux.h WebPAnimEncoderOptionsInit ++ check_pkg_config libwebpdemux "libwebpdemux >= 1.0.0" webp/demux.h WebPDemux ++ if enabled libwebpdemux; then ++ enable webp_demuxer ++ enable libwebp_decoder ++ fi } + enabled libx264 && require_pkg_config libx264 x264 "stdint.h x264.h" x264_encoder_encode && + require_cpp_condition libx264 x264.h "X264_BUILD >= 155" && { + [ "$toolchain" != "msvc" ] || +diff --git a/libavcodec/Makefile b/libavcodec/Makefile +index c51a860..4ae36ed 100644 +--- a/libavcodec/Makefile ++++ b/libavcodec/Makefile +@@ -846,6 +846,7 @@ OBJS-$(CONFIG_WBMP_DECODER) += wbmpdec.o + OBJS-$(CONFIG_WBMP_ENCODER) += wbmpenc.o + OBJS-$(CONFIG_WCMV_DECODER) += wcmv.o + OBJS-$(CONFIG_WEBP_DECODER) += webp.o ++OBJS-$(CONFIG_LIBWEBP_DECODER) += libwebpdec.o + OBJS-$(CONFIG_WEBVTT_DECODER) += webvttdec.o ass.o + OBJS-$(CONFIG_WEBVTT_ENCODER) += webvttenc.o ass_split.o + OBJS-$(CONFIG_WMALOSSLESS_DECODER) += wmalosslessdec.o wma_common.o +diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c +index 695214f..ffc2494 100644 +--- a/libavcodec/allcodecs.c ++++ b/libavcodec/allcodecs.c +@@ -428,6 +428,7 @@ extern const FFCodec ff_zlib_encoder; + extern const FFCodec ff_zlib_decoder; + extern const FFCodec ff_zmbv_encoder; + extern const FFCodec ff_zmbv_decoder; ++extern const FFCodec ff_libwebp_decoder; + + /* audio codecs */ + extern const FFCodec ff_aac_encoder; +diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c +index 93877ed..46e5f8b 100644 +--- a/libavcodec/codec_desc.c ++++ b/libavcodec/codec_desc.c +@@ -3870,6 +3870,15 @@ static const AVCodecDescriptor codec_descriptors[] = { + .long_name = NULL_IF_CONFIG_SMALL("Audio Vivid"), + .props = AV_CODEC_PROP_LOSSY, + }, ++ { ++ .id = AV_CODEC_ID_LIBWEBP, ++ .type = AVMEDIA_TYPE_VIDEO, ++ .name = "libwebp", ++ .long_name = NULL_IF_CONFIG_SMALL("libWebP"), ++ .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY | ++ AV_CODEC_PROP_LOSSLESS, ++ .mime_types= MT("image/webp"), ++ }, + { + .id = AV_CODEC_ID_VNULL, + .type = AVMEDIA_TYPE_VIDEO, +diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h +index 43f7549..31bb7a6 100644 +--- a/libavcodec/codec_id.h ++++ b/libavcodec/codec_id.h +@@ -629,6 +629,7 @@ enum AVCodecID { + * Null encoder/decoder discard all input and never return any output. + */ + AV_CODEC_ID_AVS3DA, ++ AV_CODEC_ID_LIBWEBP, + AV_CODEC_ID_VNULL, + /** + * Dummy null audio codec, useful mainly for development and debugging. +diff --git a/libavcodec/libwebpdec.c b/libavcodec/libwebpdec.c +new file mode 100644 +index 0000000..17b69d0 +--- /dev/null ++++ b/libavcodec/libwebpdec.c +@@ -0,0 +1,376 @@ ++/* ++ * libwebpdec.c ++ * ++ * Copyright (c) 2025 debugly ++ * ++ * This file is part of FSPlayer. ++ * ++ * FSPlayer 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 3 of the License, or (at your option) any later version. ++ * ++ * FSPlayer 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 FSPlayer; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include "codec_internal.h" ++#include "decode.h" ++#include "internal.h" ++#include "libavutil/common.h" ++#include "libavutil/imgutils.h" ++#include "libavutil/mem.h" ++#include "libavutil/opt.h" ++#include "libavutil/pixdesc.h" ++#include ++#include ++ ++typedef struct MyImageRef { ++ int width; ++ int height; ++ uint8_t *data; ++} MyImageRef; ++ ++typedef struct MyRect { ++ int x; ++ int y; ++ int w; ++ int h; ++} MyRect; ++ ++typedef struct LibWebPDecoderContext { ++ AVClass *class; ++ MyImageRef *canvas; // The main compositing canvas ++} LibWebPDecoderContext; ++ ++static void release_image(MyImageRef *img) ++{ ++ if (img) { ++ if (img->data) ++ av_free(img->data); ++ free(img); ++ } ++} ++ ++static int alloc_image(MyImageRef **img, int width, int height) ++{ ++ if (!img || width <= 0 || height <= 0) ++ return AVERROR(EINVAL); ++ ++ *img = malloc(sizeof(MyImageRef)); ++ if (!*img) ++ return AVERROR(ENOMEM); ++ ++ (*img)->width = width; ++ (*img)->height = height; ++ (*img)->data = av_malloc(width * height * 4); // RGBA ++ if (!(*img)->data) { ++ free(*img); ++ *img = NULL; ++ return AVERROR(ENOMEM); ++ } ++ ++ memset((*img)->data, 0, width * height * 4); // 初始化为透明 ++ return 0; ++} ++ ++static void clear_rect(MyImageRef *img, MyRect rect) ++{ ++ if (!img || !img->data) ++ return; ++ ++ int img_width = img->width; ++ int img_height = img->height; ++ uint8_t *data = img->data; ++ ++ // 确保矩形在图像范围内 ++ int start_x = FFMAX(0, rect.x); ++ int start_y = FFMAX(0, rect.y); ++ int end_x = FFMIN(img_width, rect.x + rect.w); ++ int end_y = FFMIN(img_height, rect.y + rect.h); ++ ++ for (int y = start_y; y < end_y; y++) { ++ uint8_t *row = data + y * img_width * 4; ++ memset(row + start_x * 4, 0, (end_x - start_x) * 4); ++ } ++} ++ ++static void blend_pixel(uint8_t *dst, const uint8_t *src) ++{ ++ uint8_t sa = src[3]; ++ if (sa == 0) { ++ return; // 源像素完全透明,直接返回 ++ } else if (sa == 255) { ++ // 源像素完全不透明,直接覆盖 ++ memcpy(dst, src, 4); ++ return; ++ } ++ ++ uint8_t da = dst[3]; ++ uint8_t out_a = sa + ((da * (255 - sa)) / 255); ++ ++ if (out_a == 0) { ++ memset(dst, 0, 4); ++ return; ++ } ++ ++ dst[0] = (src[0] * sa + dst[0] * da * (255 - sa) / 255) / out_a; ++ dst[1] = (src[1] * sa + dst[1] * da * (255 - sa) / 255) / out_a; ++ dst[2] = (src[2] * sa + dst[2] * da * (255 - sa) / 255) / out_a; ++ dst[3] = out_a; ++} ++ ++static void draw_image(MyImageRef *canvas, MyImageRef *image, MyRect rect) ++{ ++ if (!canvas || !canvas->data || !image || !image->data) ++ return; ++ ++ int canvas_width = canvas->width; ++ int canvas_height = canvas->height; ++ uint8_t *canvas_data = canvas->data; ++ ++ int image_width = image->width; ++ int image_height = image->height; ++ uint8_t *image_data = image->data; ++ ++ // 计算绘制区域 ++ int start_x = FFMAX(0, rect.x); ++ int start_y = FFMAX(0, rect.y); ++ int end_x = FFMIN(canvas_width, rect.x + rect.w); ++ int end_y = FFMIN(canvas_height, rect.y + rect.h); ++ ++ for (int y = start_y; y < end_y; y++) { ++ uint8_t *canvas_row = canvas_data + y * canvas_width * 4; ++ uint8_t *image_row = image_data + (y - rect.y) * image_width * 4; ++ for (int x = start_x; x < end_x; x++) { ++ uint8_t *canvas_pixel = canvas_row + x * 4; ++ uint8_t *image_pixel = image_row + (x - rect.x) * 4; ++ ++ blend_pixel(canvas_pixel, image_pixel); ++ } ++ } ++} ++ ++static av_cold int libwebp_decode_init(AVCodecContext *avctx) ++{ ++ LibWebPDecoderContext *s = avctx->priv_data; ++ int canvas_size; ++ ++ if (avctx->width <= 0 || avctx->height <= 0) { ++ av_log(avctx, AV_LOG_ERROR, ++ "Invalid canvas dimensions from avctx: %dx%d\n", avctx->width, ++ avctx->height); ++ return AVERROR_INVALIDDATA; ++ } ++ ++ if (alloc_image(&s->canvas, avctx->width, avctx->height)) { ++ av_log(avctx, AV_LOG_ERROR, "Failed to allocate canvas buffer\n"); ++ return AVERROR(ENOMEM); ++ } ++ ++ avctx->pix_fmt = AV_PIX_FMT_RGBA; ++ ++ return 0; ++} ++ ++static int get_origin_from_pkt_side_data(AVPacket *pkt, int *offsetx, ++ int *offsety, int *blend, int *dispose) ++{ ++ if (!pkt || !offsetx || !offsety) { ++ return AVERROR(EINVAL); ++ } ++ ++ // 初始化输出参数 ++ *offsetx = 0; ++ *offsety = 0; ++ *blend = 0; ++ *dispose = 0; ++ ++ // 获取元数据 ++ size_t metadata_len = 0; ++ uint8_t *metadata_data = av_packet_get_side_data( ++ pkt, AV_PKT_DATA_STRINGS_METADATA, &metadata_len); ++ ++ if (!metadata_data || metadata_len == 0) { ++ return -1; // ++ } ++ ++ // 解包元数据到AVDictionary ++ AVDictionary *dict = NULL; ++ int ret = av_packet_unpack_dictionary(metadata_data, metadata_len, &dict); ++ if (ret < 0) { ++ av_log(NULL, AV_LOG_ERROR, "Failed to unpack metadata: %d\n", ret); ++ return ret; ++ } ++ ++ // 解析offsetx和offsety和blend ++ AVDictionaryEntry *entry = NULL; ++ entry = av_dict_get(dict, "offsetx", NULL, 0); ++ if (entry && entry->value) { ++ *offsetx = atoi(entry->value); // 转换字符串为整数 ++ } ++ ++ entry = av_dict_get(dict, "offsety", NULL, 0); ++ if (entry && entry->value) { ++ *offsety = atoi(entry->value); ++ } ++ ++ entry = av_dict_get(dict, "blend", NULL, 0); ++ if (entry && entry->value) { ++ *blend = atoi(entry->value); ++ } ++ ++ entry = av_dict_get(dict, "dispose", NULL, 0); ++ if (entry && entry->value) { ++ *dispose = atoi(entry->value); ++ } ++ ++ // 释放字典 ++ av_dict_free(&dict); ++ return 0; ++} ++ ++static int libwebp_do_decode_frame(AVPacket *pkt, MyImageRef **imgp) ++{ ++ WebPDecoderConfig config; ++ // 1. 解码当前帧并获取其属性 ++ if (!WebPInitDecoderConfig(&config)) ++ return AVERROR_UNKNOWN; ++ ++ if (WebPGetFeatures(pkt->data, pkt->size, &config.input) != VP8_STATUS_OK) ++ return AVERROR_INVALIDDATA; ++ ++ int hasAlpha = config.input.has_alpha; ++ int input_width = config.input.width; ++ int input_height = config.input.height; ++ if (input_width <= 0 || input_height <= 0) ++ return AVERROR_INVALIDDATA; ++ ++ config.output.colorspace = MODE_RGBA; ++ config.output.is_external_memory = 1; ++ ++ MyImageRef *img; ++ ++ if (alloc_image(&img, input_width, input_height)) { ++ return AVERROR(ENOMEM); ++ } ++ ++ int rgba_stride = img->width * 4; ++ uint8_t *rgba = img->data; ++ config.output.u.RGBA.rgba = rgba; ++ config.output.u.RGBA.stride = rgba_stride; ++ config.output.u.RGBA.size = input_height * rgba_stride; ++ ++ if (WebPDecode(pkt->data, pkt->size, &config) != VP8_STATUS_OK) { ++ release_image(img); ++ return AVERROR_INVALIDDATA; ++ } ++ *imgp = img; ++ return 0; ++} ++ ++static int libwebp_decode_frame(AVCodecContext *avctx, AVFrame *frame, ++ int *got_frame, const AVPacket *pkt) ++{ ++ LibWebPDecoderContext *s = avctx->priv_data; ++ ++ int offsetx, offsety, blend, dispose; ++ if (get_origin_from_pkt_side_data(pkt, &offsetx, &offsety, &blend, ++ &dispose) < 0) { ++ return AVERROR_INVALIDDATA; ++ } ++ ++ // 使用 libwebp 解码当前帧 ++ MyImageRef *img = NULL; ++ int ret = libwebp_do_decode_frame((AVPacket *)pkt, &img); ++ if (ret < 0) { ++ av_log(avctx, AV_LOG_ERROR, "Failed to decode WebP frame: %d\n", ret); ++ return ret; ++ } ++ ++ // 将当前帧渲染到已准备好的画布上 ++ MyRect rect = {offsetx, offsety, img->width, img->height}; ++ draw_image(s->canvas, img, rect); ++ release_image(img); ++ ++ frame->width = avctx->width; ++ frame->height = avctx->height; ++ frame->format = avctx->pix_fmt; ++ ++ // 为 frame 申请内存 ++ if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) { ++ return ret; ++ } ++ ++ // 将合成好的画布复制到输出帧 ++ int canvas_linesize = s->canvas->width * 4; ++ av_image_copy_plane(frame->data[0], frame->linesize[0], s->canvas->data, ++ s->canvas->width * 4, s->canvas->width * 4, ++ s->canvas->height); ++ ++ // av_image_copy(frame->data, frame->linesize, ++ // (const uint8_t **)&s->canvas->data, &canvas_linesize, ++ // avctx->pix_fmt, output_width, output_height); ++ ++ // 执行 dispose 操作 ++ if (dispose == WEBP_MUX_DISPOSE_BACKGROUND) { ++ clear_rect(s->canvas, rect); ++ } ++ ++ *got_frame = 1; ++ ret = pkt->size; ++ ++ return ret; ++} ++ ++static int libwebp_decode_flush(AVCodecContext *avctx) ++{ ++ LibWebPDecoderContext *s = avctx->priv_data; ++ if (s->canvas && s->canvas->data) { ++ memset(s->canvas->data, 0, s->canvas->width * s->canvas->height * 4); ++ } ++ return 0; ++} ++ ++//when seek or seek to zero for loop,clear then canvas ++static av_cold int libwebp_decode_close(AVCodecContext *avctx) ++{ ++ LibWebPDecoderContext *s = avctx->priv_data; ++ release_image(s->canvas); ++ s->canvas = NULL; ++ return 0; ++} ++ ++#define OFFSET(x) offsetof(LibWebPDecoderContext, x) ++#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM ++ ++static const AVOption options[] = {{NULL}}; ++ ++static const AVClass libwebp_decoder_class = { ++ .class_name = "libwebp decoder", ++ .item_name = av_default_item_name, ++ .option = options, ++ .version = LIBAVUTIL_VERSION_INT, ++}; ++ ++const FFCodec ff_libwebp_decoder = { ++ .p.name = "libwebp", ++ .p.long_name = NULL_IF_CONFIG_SMALL("libwebp WebP image decoder"), ++ .p.type = AVMEDIA_TYPE_VIDEO, ++ .p.id = AV_CODEC_ID_LIBWEBP, ++ .priv_data_size = sizeof(LibWebPDecoderContext), ++ .init = libwebp_decode_init, ++ .cb.decode = libwebp_decode_frame, ++ .flush = libwebp_decode_flush, ++ .close = libwebp_decode_close, ++ .p.capabilities = AV_CODEC_CAP_DR1, ++ .p.priv_class = &libwebp_decoder_class, ++ .p.wrapper_name = "libwebp", ++ .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE, ++}; +diff --git a/libavformat/Makefile b/libavformat/Makefile +index 54e88cf..3081d27 100644 +--- a/libavformat/Makefile ++++ b/libavformat/Makefile +@@ -659,6 +659,7 @@ OBJS-$(CONFIG_WEBM_MUXER) += matroskaenc.o matroska.o \ + OBJS-$(CONFIG_WEBM_DASH_MANIFEST_MUXER) += webmdashenc.o + OBJS-$(CONFIG_WEBM_CHUNK_MUXER) += webm_chunk.o + OBJS-$(CONFIG_WEBP_MUXER) += webpenc.o ++OBJS-$(CONFIG_WEBP_DEMUXER) += webpdec.o + OBJS-$(CONFIG_WEBVTT_DEMUXER) += webvttdec.o subtitles.o + OBJS-$(CONFIG_WEBVTT_MUXER) += webvttenc.o + OBJS-$(CONFIG_WHIP_MUXER) += whip.o avc.o http.o srtp.o +diff --git a/libavformat/allformats.c b/libavformat/allformats.c +index 1a05db9..f69c550 100644 +--- a/libavformat/allformats.c ++++ b/libavformat/allformats.c +@@ -571,6 +571,7 @@ extern const FFInputFormat ff_image_svg_pipe_demuxer; + extern const FFInputFormat ff_image_sunrast_pipe_demuxer; + extern const FFInputFormat ff_image_tiff_pipe_demuxer; + extern const FFInputFormat ff_image_vbn_pipe_demuxer; ++extern const FFInputFormat ff_webp_demuxer; + extern const FFInputFormat ff_image_webp_pipe_demuxer; + extern const FFInputFormat ff_image_xbm_pipe_demuxer; + extern const FFInputFormat ff_image_xpm_pipe_demuxer; +diff --git a/libavformat/webpdec.c b/libavformat/webpdec.c +new file mode 100644 +index 0000000..078ac99 +--- /dev/null ++++ b/libavformat/webpdec.c +@@ -0,0 +1,296 @@ ++/* ++ * webpdec.c ++ * ++ * Copyright (c) 2025 debugly ++ * ++ * This file is part of FSPlayer. ++ * ++ * FSPlayer 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 3 of the License, or (at your option) any later version. ++ * ++ * FSPlayer 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 FSPlayer; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include "internal.h" ++#include "demux.h" ++#include "libavutil/intreadwrite.h" ++#include "libavutil/mem.h" ++#include "libavutil/common.h" ++#include ++ ++typedef struct WebPDemuxContext { ++ const AVClass *class; ++ WebPDemuxer *demuxer; ++ WebPIterator iter; // 帧迭代器 ++ int current_frame; // 当前帧索引 ++ int frame_count; // 总帧数 ++ int64_t duration; // 总时长(ms) ++ uint8_t *data; // 文件数据缓冲区 ++ size_t data_size; // 数据大小 ++ int64_t *frame_timestamps; // 帧时间戳数组(ms) ++ int *frame_durations; // 帧时长数组(ms) ++} WebPDemuxContext; ++ ++// 释放迭代器资源 ++static void webp_release_iterator(WebPIterator *iter) { ++ if (iter->fragment.bytes) { ++ WebPDemuxReleaseIterator(iter); ++ memset(iter, 0, sizeof(*iter)); ++ } ++} ++ ++// 探测WebP格式 ++static int webp_read_probe(const AVProbeData *p) { ++ // WebP文件格式以"RIFF"开头,后面跟着4字节大小和"WEBP"标识 ++ if (p->buf_size < 12) ++ return 0; ++ ++ // 检查RIFF标识和WEBP格式标识 ++ if (AV_RL32(p->buf) == MKTAG('R', 'I', 'F', 'F') && ++ AV_RL32(p->buf + 8) == MKTAG('W', 'E', 'B', 'P')) { ++ // 确定是WebP文件,返回较高的探测分数 ++ return AVPROBE_SCORE_MAX; ++ } ++ ++ return 0; ++} ++ ++static int webp_read_header(AVFormatContext *s) { ++ WebPDemuxContext *wp = s->priv_data; ++ AVIOContext *pb = s->pb; ++ AVStream *st; ++ int ret, i; ++ ++ // 读取文件到内存 ++ wp->data_size = avio_size(pb); ++ if (wp->data_size <= 0) { ++ av_log(s, AV_LOG_ERROR, "Invalid file size\n"); ++ return AVERROR_INVALIDDATA; ++ } ++ ++ wp->data = av_malloc(wp->data_size); ++ if (!wp->data) ++ return AVERROR(ENOMEM); ++ ++ if (avio_read(pb, wp->data, wp->data_size) != wp->data_size) { ++ av_log(s, AV_LOG_ERROR, "Failed to read file\n"); ++ ret = AVERROR(EIO); ++ goto fail; ++ } ++ ++ // 初始化WebP解复用器 ++ WebPData webp_data; ++ WebPDataInit(&webp_data); ++ webp_data.bytes = wp->data; ++ webp_data.size = wp->data_size; ++ ++ wp->demuxer = WebPDemux(&webp_data); ++ if (!wp->demuxer) { ++ av_log(s, AV_LOG_ERROR, "Failed to create WebP demuxer\n"); ++ ret = AVERROR_INVALIDDATA; ++ goto fail; ++ } ++ ++ wp->frame_count = WebPDemuxGetI(wp->demuxer, WEBP_FF_FRAME_COUNT); ++ // 获取画布尺寸 ++ int canvas_width = WebPDemuxGetI(wp->demuxer, WEBP_FF_CANVAS_WIDTH); ++ int canvas_height = WebPDemuxGetI(wp->demuxer, WEBP_FF_CANVAS_HEIGHT); ++ ++ // int loop_count = WebPDemuxGetI(wp->demuxer, WEBP_FF_LOOP_COUNT); ++ // uint32_t flags = WebPDemuxGetI(wp->demuxer, WEBP_FF_FORMAT_FLAGS); ++ ++ // int has_animation = flags & ANIMATION_FLAG; ++ // int has_alpha = flags & ALPHA_FLAG; ++ ++ // 分配帧时间戳和时长数组 ++ wp->frame_timestamps = av_malloc_array(wp->frame_count, sizeof(int64_t)); ++ wp->frame_durations = av_malloc_array(wp->frame_count, sizeof(int)); ++ if (!wp->frame_timestamps || !wp->frame_durations) { ++ ret = AVERROR(ENOMEM); ++ goto fail; ++ } ++ ++ memset(&wp->iter, 0, sizeof(wp->iter)); ++ // libwebp's index start with 1 ++ if (!WebPDemuxGetFrame(wp->demuxer, 1, &wp->iter)) { ++ av_log(s, AV_LOG_ERROR, "Failed to get first frame\n"); ++ ret = AVERROR_INVALIDDATA; ++ goto fail; ++ } ++ ++ wp->duration = 0; ++ for (i = 0; i < wp->frame_count; i++) { ++ int duration = wp->iter.duration; ++ if (duration <= 10) { ++ // WebP standard says 0 duration is used for canvas updating but not showing image, but actually Chrome and other implementations set it to 100ms if duration is lower or equal than 10ms ++ // Some animated WebP images also created without duration, we should keep compatibility ++ duration = 100; ++ } ++ wp->frame_durations[i] = duration; ++ wp->frame_timestamps[i] = wp->duration; ++ wp->duration += wp->frame_durations[i]; ++ if (i < wp->frame_count - 1) ++ WebPDemuxNextFrame(&wp->iter); ++ } ++ ++ // 重置迭代器到第一帧 ++ webp_release_iterator(&wp->iter); ++ if (!WebPDemuxGetFrame(wp->demuxer, 1, &wp->iter)) { ++ av_log(s, AV_LOG_ERROR, "Failed to reset to first frame\n"); ++ ret = AVERROR_INVALIDDATA; ++ goto fail; ++ } ++ ++ // 创建视频流 ++ st = avformat_new_stream(s, NULL); ++ if (!st) { ++ ret = AVERROR(ENOMEM); ++ goto fail; ++ } ++ ++ // 设置流参数 ++ st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; ++ // 使用自定义解码器 ++ st->codecpar->codec_id = AV_CODEC_ID_LIBWEBP; ++ st->codecpar->width = canvas_width; ++ st->codecpar->height = canvas_height; ++ st->duration = wp->duration; ++ avpriv_set_pts_info(st, 64, 1, 1000); // 时基:1ms ++ ++ wp->current_frame = 0; ++ return 0; ++ ++fail: ++ webp_release_iterator(&wp->iter); ++ WebPDemuxDelete(wp->demuxer); ++ av_free(wp->data); ++ av_free(wp->frame_timestamps); ++ av_free(wp->frame_durations); ++ return ret; ++} ++ ++/** ++ * Add this frame's source path and basename to packet's sidedata ++ * as a dictionary, so it can be used by filters like 'drawtext'. ++ */ ++static int add_origin_as_pkt_side_data(int offsetx, int offsety, int blend, int dispose, AVPacket *pkt) { ++ AVDictionary *d = NULL; ++ char *packed_metadata = NULL; ++ size_t metadata_len; ++ int ret; ++ ++ av_dict_set_int(&d, "offsetx", offsetx, 0); ++ av_dict_set_int(&d, "offsety", offsety, 0); ++ av_dict_set_int(&d, "blend", blend, 0); ++ av_dict_set_int(&d, "dispose", dispose, 0); ++ ++ packed_metadata = av_packet_pack_dictionary(d, &metadata_len); ++ av_dict_free(&d); ++ if (!packed_metadata) ++ return AVERROR(ENOMEM); ++ ret = av_packet_add_side_data(pkt, AV_PKT_DATA_STRINGS_METADATA, ++ packed_metadata, metadata_len); ++ if (ret < 0) { ++ av_freep(&packed_metadata); ++ return ret; ++ } ++ return 0; ++} ++ ++static int webp_read_packet(AVFormatContext *s, AVPacket *pkt) { ++ WebPDemuxContext *wp = s->priv_data; ++ int ret; ++ ++ if (wp->current_frame >= wp->frame_count) ++ return AVERROR_EOF; ++ ++ // 分配并填充数据包 ++ if ((ret = av_new_packet(pkt, wp->iter.fragment.size)) < 0) ++ return ret; ++ memcpy(pkt->data, wp->iter.fragment.bytes, wp->iter.fragment.size); ++ ++ add_origin_as_pkt_side_data(wp->iter.x_offset, wp->iter.y_offset, wp->iter.blend_method == WEBP_MUX_BLEND, wp->iter.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND, pkt); ++ // 设置数据包参数 ++ pkt->stream_index = 0; ++ pkt->pts = wp->frame_timestamps[wp->current_frame]; ++ pkt->duration = wp->frame_durations[wp->current_frame]; ++ pkt->flags |= AV_PKT_FLAG_KEY; ++ ++ // 准备下一帧 ++ wp->current_frame++; ++ if (wp->current_frame < wp->frame_count && !WebPDemuxNextFrame(&wp->iter)) ++ av_log(s, AV_LOG_WARNING, "Unexpected end of frames\n"); ++ ++ return 0; ++} ++ ++static int webp_read_seek(AVFormatContext *s, int stream_index, ++ int64_t timestamp, int flags) { ++ WebPDemuxContext *wp = s->priv_data; ++ int target_frame; ++ ++ if (stream_index != 0) ++ return -1; ++ ++ // 计算目标帧索引 ++ target_frame = av_rescale_rnd(timestamp, wp->frame_count, ++ wp->duration, flags & AVSEEK_FLAG_BACKWARD ? AV_ROUND_DOWN : AV_ROUND_UP); ++ ++ // 手动实现范围限制(替代av_clamp) ++ if (target_frame < 0) { ++ target_frame = 0; ++ } else if (target_frame >= wp->frame_count) { ++ target_frame = wp->frame_count - 1; ++ } ++ ++ // 定位到目标帧 ++ webp_release_iterator(&wp->iter); ++ if (!WebPDemuxGetFrame(wp->demuxer, target_frame + 1, &wp->iter)) { ++ av_log(s, AV_LOG_ERROR, "Failed to seek to frame %d\n", target_frame); ++ return AVERROR_INVALIDDATA; ++ } ++ ++ wp->current_frame = target_frame; ++ return 0; ++} ++ ++static int webp_read_close(AVFormatContext *s) { ++ WebPDemuxContext *wp = s->priv_data; ++ ++ webp_release_iterator(&wp->iter); ++ WebPDemuxDelete(wp->demuxer); ++ av_free(wp->data); ++ av_free(wp->frame_timestamps); ++ av_free(wp->frame_durations); ++ return 0; ++} ++ ++static const AVClass webp_demuxer_class = { ++ .class_name = "WebP demuxer", ++ .item_name = av_default_item_name, ++ .version = LIBAVUTIL_VERSION_INT, ++}; ++ ++// FFmpeg 7.x输入解复用器标准声明 ++const FFInputFormat ff_webp_demuxer = { ++ .p.name = "webp", ++ .p.long_name = NULL_IF_CONFIG_SMALL("WebP image format (libwebp 1.5.0+)"), ++ .p.flags = AVFMT_GENERIC_INDEX, ++ .p.extensions = "webp", ++ .read_probe = webp_read_probe, ++ .read_header = webp_read_header, ++ .read_packet = webp_read_packet, ++ .read_seek = webp_read_seek, ++ .read_close = webp_read_close, ++ .priv_data_size = sizeof(WebPDemuxContext), ++ .p.priv_class = &webp_demuxer_class, ++}; +-- +2.50.1 (Apple Git-155) + diff --git a/patches/fftutorial/0018-supportsFamily-is-only-available-on-iOS-13.0-or-newe.patch b/patches/fftutorial/0018-supportsFamily-is-only-available-on-iOS-13.0-or-newe.patch new file mode 100644 index 000000000..1e5a4a6c8 --- /dev/null +++ b/patches/fftutorial/0018-supportsFamily-is-only-available-on-iOS-13.0-or-newe.patch @@ -0,0 +1,25 @@ +From 1ebd4c062a886049f3c3255c50fe56f6621b9844 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Fri, 26 Jul 2024 16:11:14 +0800 +Subject: [PATCH 18] 'supportsFamily:' is only available on iOS 13.0 or newer + +--- + libavfilter/metal/utils.m | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/libavfilter/metal/utils.m b/libavfilter/metal/utils.m +index f365d3c..bb1825a 100644 +--- a/libavfilter/metal/utils.m ++++ b/libavfilter/metal/utils.m +@@ -31,7 +31,7 @@ void ff_metal_compute_encoder_dispatch(id device, + BOOL fallback = YES; + // MAC_OS_X_VERSION_10_15 is only defined on SDKs new enough to include its functionality (including iOS, tvOS, etc) + #ifdef MAC_OS_X_VERSION_10_15 +- if (@available(macOS 10.15, iOS 11, tvOS 14.5, *)) { ++ if (@available(macOS 10.15, iOS 13, tvOS 14.5, *)) { + if ([device supportsFamily:MTLGPUFamilyCommon3]) { + MTLSize threadsPerGrid = MTLSizeMake(width, height, 1); + [encoder dispatchThreads:threadsPerGrid threadsPerThreadgroup:threadsPerThreadgroup]; +-- +2.39.3 (Apple Git-146) + diff --git a/patches/fftutorial/0021-Adapt-to-clang-16.patch b/patches/fftutorial/0021-Adapt-to-clang-16.patch new file mode 100644 index 000000000..a1c792743 --- /dev/null +++ b/patches/fftutorial/0021-Adapt-to-clang-16.patch @@ -0,0 +1,31 @@ +From 338d6fb305a992bc2a24347e4d7793e02b54345d Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Thu, 31 Oct 2024 10:55:33 +0800 +Subject: [PATCH 21] Adapt to clang 16 + +--- + configure | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/configure b/configure +index 16d0177..edbb925 100755 +--- a/configure ++++ b/configure +@@ -5623,7 +5623,13 @@ case $target_os in + enabled x86_32 && append SHFLAGS -Wl,-read_only_relocs,suppress + strip="${strip} -x" + add_ldflags -Wl,-dynamic,-search_paths_first +- check_cflags -Werror=partial-availability ++ # https://gitlab.gnome.org/GNOME/gimp/-/issues/8649 ++ # from clang 15 int <-> pointer conversions now defaults as an error ++ check_cflags -Wno-int-conversion ++ # from clang 16 VTPixelTransferSessionCreate' has been marked as being introduced in tvOS 16.0 here, but the deployment target is tvOS 12.0.0 ++ check_cflags -Wno-unguarded-availability -Wno-unguarded-availability-new ++ ++ # check_cflags -Werror=partial-availability + SLIBSUF=".dylib" + SLIBNAME_WITH_VERSION='$(SLIBPREF)$(FULLNAME).$(LIBVERSION)$(SLIBSUF)' + SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(FULLNAME).$(LIBMAJOR)$(SLIBSUF)' +-- +2.39.5 (Apple Git-154) + diff --git a/patches/smb2 b/patches/smb2 deleted file mode 120000 index 6ec51ed2f..000000000 --- a/patches/smb2 +++ /dev/null @@ -1 +0,0 @@ -smb2-6.2 \ No newline at end of file diff --git a/patches/soundtouch/0001-android-force-use-SOUNDTOUCH_INTEGER_SAMPLES.patch b/patches/soundtouch/0001-android-force-use-SOUNDTOUCH_INTEGER_SAMPLES.patch new file mode 100644 index 000000000..647385c29 --- /dev/null +++ b/patches/soundtouch/0001-android-force-use-SOUNDTOUCH_INTEGER_SAMPLES.patch @@ -0,0 +1,25 @@ +From 3238587c53c2910877c8954433f46e59f18e6d12 Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Sat, 26 Apr 2025 15:03:15 +0800 +Subject: [PATCH] android force use SOUNDTOUCH_INTEGER_SAMPLES + +--- + include/STTypes.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/include/STTypes.h b/include/STTypes.h +index 03dea9e..78ac3e6 100644 +--- a/include/STTypes.h ++++ b/include/STTypes.h +@@ -71,7 +71,7 @@ namespace soundtouch + /// runtime performance so recommendation is to keep this off. + // #define USE_MULTICH_ALWAYS + +- #if (defined(__SOFTFP__) && defined(ANDROID)) ++ #if (defined(ANDROID)) + // For Android compilation: Force use of Integer samples in case that + // compilation uses soft-floating point emulation - soft-fp is way too slow + #undef SOUNDTOUCH_FLOAT_SAMPLES +-- +2.39.5 (Apple Git-154) + diff --git a/patches/uavs3d/0003-Compatibility-with-CMake-3.5-has-been-removed-from-C.patch b/patches/uavs3d/0003-Compatibility-with-CMake-3.5-has-been-removed-from-C.patch new file mode 100644 index 000000000..89b902ca6 --- /dev/null +++ b/patches/uavs3d/0003-Compatibility-with-CMake-3.5-has-been-removed-from-C.patch @@ -0,0 +1,22 @@ +From 0ac257f96d3f48f1decd2c8e211bc9fef7fc760c Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Tue, 16 Dec 2025 09:28:45 +0800 +Subject: [PATCH] Compatibility with CMake < 3.5 has been removed from CMake + +--- + CMakeLists.txt | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 46458c7..da6c82e 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -1,4 +1,4 @@ +-cmake_minimum_required(VERSION 3.1) ++cmake_minimum_required(VERSION 3.10) + + project(uavs3d) + +-- +2.50.1 (Apple Git-155) + diff --git a/patches/yuv/0001-skip-binary-and-shared-lib-and-jpeg.patch b/patches/yuv/0001-fix-build-error.patch similarity index 51% rename from patches/yuv/0001-skip-binary-and-shared-lib-and-jpeg.patch rename to patches/yuv/0001-fix-build-error.patch index 89f419861..2e28e8201 100644 --- a/patches/yuv/0001-skip-binary-and-shared-lib-and-jpeg.patch +++ b/patches/yuv/0001-fix-build-error.patch @@ -1,71 +1,71 @@ -From adaf7690b34ade92e3cfb0af812ec8c534381b12 Mon Sep 17 00:00:00 2001 +From 55f90bbd61881edaef80f0751264b8ca4d0cec43 Mon Sep 17 00:00:00 2001 From: qianlongxu -Date: Tue, 14 Jan 2025 17:53:31 +0800 -Subject: [PATCH] skip binary and shared lib and jpeg +Date: Mon, 8 Dec 2025 12:48:09 +0800 +Subject: [PATCH] fix build error --- - CMakeLists.txt | 17 +++++++++++++++-- - 1 file changed, 15 insertions(+), 2 deletions(-) + CMakeLists.txt | 13 +++++++++++-- + 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt -index 636531e..47b9759 100644 +index 9abfa74..b0feb35 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt -@@ -5,6 +5,7 @@ +@@ -3,8 +3,9 @@ + # Run with -DTEST=ON to build unit tests + PROJECT ( YUV C CXX ) # "C" is required even for C++ projects - CMAKE_MINIMUM_REQUIRED( VERSION 2.8 ) - OPTION( TEST "Built unit tests" OFF ) +-CMAKE_MINIMUM_REQUIRED( VERSION 2.8.12 ) ++CMAKE_MINIMUM_REQUIRED( VERSION 3.10) + OPTION( UNIT_TEST "Built unit tests" OFF ) +OPTION( BINARY "Built binary" OFF ) SET ( ly_base_dir ${PROJECT_SOURCE_DIR} ) SET ( ly_src_dir ${ly_base_dir}/source ) -@@ -25,22 +26,26 @@ INCLUDE_DIRECTORIES( BEFORE ${ly_inc_dir} ) +@@ -29,6 +30,7 @@ endif() # this creates the static library (.a) ADD_LIBRARY ( ${ly_lib_static} STATIC ${ly_source_files} ) +if(BUILD_SHARED_LIBS) # this creates the shared library (.so) ADD_LIBRARY ( ${ly_lib_shared} SHARED ${ly_source_files} ) -+ SET_TARGET_PROPERTIES ( ${ly_lib_shared} PROPERTIES OUTPUT_NAME "${ly_lib_name}" ) - SET_TARGET_PROPERTIES ( ${ly_lib_shared} PROPERTIES PREFIX "lib" ) +@@ -36,7 +38,9 @@ SET_TARGET_PROPERTIES ( ${ly_lib_shared} PROPERTIES PREFIX "lib" ) + if(WIN32) + SET_TARGET_PROPERTIES ( ${ly_lib_shared} PROPERTIES IMPORT_PREFIX "lib" ) + endif() +endif() - # this creates the conversion tool +if(BINARY) - ADD_EXECUTABLE ( yuvconvert ${ly_base_dir}/util/yuvconvert.cc ) - TARGET_LINK_LIBRARIES ( yuvconvert ${ly_lib_static} ) - -- - INCLUDE ( FindJPEG ) - if (JPEG_FOUND) - include_directories( ${JPEG_INCLUDE_DIR} ) - target_link_libraries( yuvconvert ${JPEG_LIBRARY} ) + # this creates the cpuid tool + ADD_EXECUTABLE ( cpuid ${ly_base_dir}/util/cpuid.c ) + TARGET_LINK_LIBRARIES ( cpuid ${ly_lib_static} ) +@@ -55,6 +59,7 @@ if (JPEG_FOUND) + target_link_libraries( ${ly_lib_shared} ${JPEG_LIBRARY} ) add_definitions( -DHAVE_JPEG ) endif() +endif() - if(TEST) + if(UNIT_TEST) find_library(GTEST_LIBRARY gtest) -@@ -75,9 +80,17 @@ endif() +@@ -97,12 +102,16 @@ if(UNIT_TEST) + endif() - # install the conversion tool, .so, .a, and all the header files +if(BINARY) -+message("The BINARY option is enabled.") + # install the conversion tool, .so, .a, and all the header files INSTALL ( PROGRAMS ${CMAKE_BINARY_DIR}/yuvconvert DESTINATION bin ) --INSTALL ( TARGETS ${ly_lib_static} DESTINATION lib ) +endif() -+ + INSTALL ( TARGETS ${ly_lib_static} DESTINATION lib ) +if(BUILD_SHARED_LIBS) -+message("The BUILD_SHARED_LIBS option is enabled.") INSTALL ( TARGETS ${ly_lib_shared} LIBRARY DESTINATION lib RUNTIME DESTINATION bin ) +endif() -+ -+INSTALL ( TARGETS ${ly_lib_static} DESTINATION lib ) INSTALL ( DIRECTORY ${PROJECT_SOURCE_DIR}/include/ DESTINATION include ) # create the .deb and .rpm packages using cpack +-INCLUDE ( CM_linux_packages.cmake ) ++# INCLUDE ( CM_linux_packages.cmake ) + -- -2.39.5 (Apple Git-154) +2.50.1 (Apple Git-155) diff --git a/patches/yuv/0002-fix-convert-odd-height-cvpixelbuffer-to-bgra-crash.patch b/patches/yuv/0002-fix-convert-odd-height-cvpixelbuffer-to-bgra-crash.patch new file mode 100644 index 000000000..c1d63eff3 --- /dev/null +++ b/patches/yuv/0002-fix-convert-odd-height-cvpixelbuffer-to-bgra-crash.patch @@ -0,0 +1,219 @@ +From c253f0b58f86597d7e020076454fbf8c63847f8d Mon Sep 17 00:00:00 2001 +From: qianlongxu +Date: Fri, 5 Dec 2025 16:07:06 +0800 +Subject: [PATCH] fix convert odd height cvpixelbuffer to bgra crash + +--- + include/libyuv/convert_argb.h | 44 +++++++++++ + source/convert_argb.cc | 140 ++++++++++++++++++++++++++++++++++ + 2 files changed, 184 insertions(+) + +diff --git a/include/libyuv/convert_argb.h b/include/libyuv/convert_argb.h +index 5b50567..2bcfda8 100644 +--- a/include/libyuv/convert_argb.h ++++ b/include/libyuv/convert_argb.h +@@ -1856,6 +1856,50 @@ int NV12ToARGBMatrix(const uint8_t* src_y, + int width, + int height); + ++ /* ++Convert NV12 to ARGB with matrix safely ++itu_matrix 1: BT.709 ++ 2: BT.601 ++ 3: BT.2020 ++full_range 0: limited range ++ 1: full range ++*/ ++ ++LIBYUV_API ++int NV12ToARGBMatrixRangeSafely(const uint8_t* src_y, ++ int src_stride_y, ++ const uint8_t* src_uv, ++ int src_stride_uv, ++ uint8_t* dst_argb, ++ int dst_stride_argb, ++ int width, ++ int height, ++ int uv_height, ++ int itu_matrix, ++ int full_range); ++ ++ ++ /* ++convert nv12 to argb with matrix,uv height safely ++because cvpixelbuffer uv height is y_height/2 ++libyuv need uv height is y_height/2 + 1 ++for example: ++y height is 615 ++uv height is 307 ++libyuv need uv height is 308 ++*/ ++LIBYUV_API ++int NV12ToARGBMatrixSafely(const uint8_t* src_y, ++ int src_stride_y, ++ const uint8_t* src_uv, ++ int src_stride_uv, ++ uint8_t* dst_argb, ++ int dst_stride_argb, ++ const struct YuvConstants* yuvconstants, ++ int width, ++ int height, ++ int uv_height); ++ + // Convert NV21 to ARGB with matrix. + LIBYUV_API + int NV21ToARGBMatrix(const uint8_t* src_y, +diff --git a/source/convert_argb.cc b/source/convert_argb.cc +index 3655e30..eaff1e9 100644 +--- a/source/convert_argb.cc ++++ b/source/convert_argb.cc +@@ -4060,6 +4060,146 @@ int NV12ToARGBMatrix(const uint8_t* src_y, + return 0; + } + ++/* ++convert nv12 to argb with matrix,uv height safe ++because cvpixelbuffer uv height is y_height/2 ++libyuv need uv height is y_height/2 + 1 ++for example: ++y height is 615 ++uv height is 307 ++libyuv need uv height is 308 ++*/ ++LIBYUV_API ++int NV12ToARGBMatrixSafely(const uint8_t* src_y, ++ int src_stride_y, ++ const uint8_t* src_uv, ++ int src_stride_uv, ++ uint8_t* dst_argb, ++ int dst_stride_argb, ++ const struct YuvConstants* yuvconstants, ++ int width, ++ int height, ++ int uv_height) { ++ int y; ++ void (*NV12ToARGBRow)( ++ const uint8_t* y_buf, const uint8_t* uv_buf, uint8_t* rgb_buf, ++ const struct YuvConstants* yuvconstants, int width) = NV12ToARGBRow_C; ++ assert(yuvconstants); ++ if (!src_y || !src_uv || !dst_argb || width <= 0 || height == 0) { ++ return -1; ++ } ++ // Negative height means invert the image. ++ if (height < 0) { ++ height = -height; ++ dst_argb = dst_argb + (height - 1) * dst_stride_argb; ++ dst_stride_argb = -dst_stride_argb; ++ } ++#if defined(HAS_NV12TOARGBROW_SSSE3) ++ if (TestCpuFlag(kCpuHasSSSE3)) { ++ NV12ToARGBRow = NV12ToARGBRow_Any_SSSE3; ++ if (IS_ALIGNED(width, 8)) { ++ NV12ToARGBRow = NV12ToARGBRow_SSSE3; ++ } ++ } ++#endif ++#if defined(HAS_NV12TOARGBROW_AVX2) ++ if (TestCpuFlag(kCpuHasAVX2)) { ++ NV12ToARGBRow = NV12ToARGBRow_Any_AVX2; ++ if (IS_ALIGNED(width, 16)) { ++ NV12ToARGBRow = NV12ToARGBRow_AVX2; ++ } ++ } ++#endif ++#if defined(HAS_NV12TOARGBROW_NEON) ++ if (TestCpuFlag(kCpuHasNEON)) { ++ NV12ToARGBRow = NV12ToARGBRow_Any_NEON; ++ if (IS_ALIGNED(width, 8)) { ++ NV12ToARGBRow = NV12ToARGBRow_NEON; ++ } ++ } ++#endif ++#if defined(HAS_NV12TOARGBROW_MSA) ++ if (TestCpuFlag(kCpuHasMSA)) { ++ NV12ToARGBRow = NV12ToARGBRow_Any_MSA; ++ if (IS_ALIGNED(width, 8)) { ++ NV12ToARGBRow = NV12ToARGBRow_MSA; ++ } ++ } ++#endif ++#if defined(HAS_NV12TOARGBROW_LSX) ++ if (TestCpuFlag(kCpuHasLSX)) { ++ NV12ToARGBRow = NV12ToARGBRow_Any_LSX; ++ if (IS_ALIGNED(width, 8)) { ++ NV12ToARGBRow = NV12ToARGBRow_LSX; ++ } ++ } ++#endif ++#if defined(HAS_NV12TOARGBROW_LASX) ++ if (TestCpuFlag(kCpuHasLASX)) { ++ NV12ToARGBRow = NV12ToARGBRow_Any_LASX; ++ if (IS_ALIGNED(width, 16)) { ++ NV12ToARGBRow = NV12ToARGBRow_LASX; ++ } ++ } ++#endif ++#if defined(HAS_NV12TOARGBROW_RVV) ++ if (TestCpuFlag(kCpuHasRVV)) { ++ NV12ToARGBRow = NV12ToARGBRow_RVV; ++ } ++#endif ++ ++ //because src_uv will add src_stride_uv,so after added src_uv should not exceed uv_height ++ uv_height--; ++ ++ for (y = 0; y < height; ++y) { ++ NV12ToARGBRow(src_y, src_uv, dst_argb, yuvconstants, width); ++ dst_argb += dst_stride_argb; ++ src_y += src_stride_y; ++ //cvpixelbuffer may have different uv height: height >> 1 - 1 ++ if (y & 1 && (y >> 1) < uv_height) { ++ src_uv += src_stride_uv; ++ } ++ } ++ return 0; ++} ++ ++/* ++Convert NV12 to ARGB with matrix. ++itu_matrix 1: BT.709 ++ 2: BT.601 ++ 3: BT.2020 ++full_range 0: limited range ++ 1: full range ++*/ ++ ++LIBYUV_API ++int NV12ToARGBMatrixRangeSafely(const uint8_t* src_y, ++ int src_stride_y, ++ const uint8_t* src_uv, ++ int src_stride_uv, ++ uint8_t* dst_argb, ++ int dst_stride_argb, ++ int width, ++ int height, ++ int uv_height, ++ int itu_matrix, ++ int full_range) ++ { ++const struct YuvConstants* yuvconstants; ++ ++ if (itu_matrix == 2) { ++ yuvconstants = full_range ? &kYuvJPEGConstants : &kYuvI601Constants; ++ } else if (itu_matrix == 3) { ++ yuvconstants = full_range ? &kYuvV2020Constants : &kYuv2020Constants; ++ } else { ++ yuvconstants = full_range ? &kYuvF709Constants : &kYuvH709Constants; ++ } ++ return NV12ToARGBMatrixSafely(src_y, src_stride_y, src_uv, src_stride_uv, ++ dst_argb, dst_stride_argb, ++ yuvconstants, ++ width, height,uv_height); ++} ++ + // Convert NV21 to ARGB with matrix. + LIBYUV_API + int NV21ToARGBMatrix(const uint8_t* src_y, +-- +2.50.1 (Apple Git-155) + diff --git a/tools/GitHub-2025.css b/tools/GitHub-2025.css new file mode 100644 index 000000000..ca470a6d5 --- /dev/null +++ b/tools/GitHub-2025.css @@ -0,0 +1,1372 @@ +/* github-markdown-css --light=light */ +body { + color-scheme: light; + /* Ideal for movement that starts on the page and ends off the page. */ + /* Ideal for movement that starts and ends on the page. */ + /* Ideal for movement that starts off the page and ends on the page. */ + /* Ideal for non-movement properties, like opacity or background color. */ + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100%; + margin: 0; + color: #1f2328; + background-color: #ffffff; + font-family: -apple-system,BlinkMacSystemFont,"Segoe UI","Noto Sans",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji"; + font-size: 16px; + line-height: 1.5; + word-wrap: break-word; + padding: 30px; +} + +body .octicon { + display: inline-block; + fill: currentColor; + vertical-align: text-bottom; +} + +body h1:hover .anchor .octicon-link:before, +body h2:hover .anchor .octicon-link:before, +body h3:hover .anchor .octicon-link:before, +body h4:hover .anchor .octicon-link:before, +body h5:hover .anchor .octicon-link:before, +body h6:hover .anchor .octicon-link:before { + width: 16px; + height: 16px; + content: ' '; + display: inline-block; + background-color: currentColor; + -webkit-mask-image: url("data:image/svg+xml,"); + mask-image: url("data:image/svg+xml,"); +} + +body details, +body figcaption, +body figure { + display: block; +} + +body summary { + display: list-item; +} + +body [hidden] { + display: none !important; +} + +body a { + background-color: transparent; + color: #0969da; + text-decoration: none; +} + +body abbr[title] { + border-bottom: none; + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; +} + +body b, +body strong { + font-weight: 600; +} + +body dfn { + font-style: italic; +} + +body h1 { + margin: .67em 0; + font-weight: 600; + padding-bottom: .3em; + font-size: 2em; + border-bottom: 1px solid #d1d9e0b3; +} + +body mark { + background-color: #fff8c5; + color: #1f2328; +} + +body small { + font-size: 90%; +} + +body sub, +body sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +body sub { + bottom: -0.25em; +} + +body sup { + top: -0.5em; +} + +body img { + border-style: none; + max-width: 100%; + box-sizing: content-box; +} + +body code, +body kbd, +body pre, +body samp { + font-family: monospace; + font-size: 1em; +} + +body figure { + margin: 1em 2.5rem; +} + +body hr { + box-sizing: content-box; + overflow: hidden; + background: transparent; + border-bottom: 1px solid #d1d9e0b3; + height: .25em; + padding: 0; + margin: 1.5rem 0; + background-color: #d1d9e0; + border: 0; +} + +body input { + font: inherit; + margin: 0; + overflow: visible; + font-family: inherit; + font-size: inherit; + line-height: inherit; +} + +body [type=button], +body [type=reset], +body [type=submit] { + -webkit-appearance: button; + appearance: button; +} + +body [type=checkbox], +body [type=radio] { + box-sizing: border-box; + padding: 0; +} + +body [type=number]::-webkit-inner-spin-button, +body [type=number]::-webkit-outer-spin-button { + height: auto; +} + +body [type=search]::-webkit-search-cancel-button, +body [type=search]::-webkit-search-decoration { + -webkit-appearance: none; + appearance: none; +} + +body ::-webkit-input-placeholder { + color: inherit; + opacity: .54; +} + +body ::-webkit-file-upload-button { + -webkit-appearance: button; + appearance: button; + font: inherit; +} + +body a:hover { + text-decoration: underline; +} + +body ::placeholder { + color: #59636e; + opacity: 1; +} + +body hr::before { + display: table; + content: ""; +} + +body hr::after { + display: table; + clear: both; + content: ""; +} + +body table { + border-spacing: 0; + border-collapse: collapse; + display: block; + width: max-content; + max-width: 100%; + overflow: auto; + font-variant: tabular-nums; +} + +body td, +body th { + padding: 0; +} + +body details summary { + cursor: pointer; +} + +body a:focus, +body [role=button]:focus, +body input[type=radio]:focus, +body input[type=checkbox]:focus { + outline: 2px solid var(--borderColor-accent-emphasis); + outline-offset: -2px; + box-shadow: none; +} + +body a:focus:not(:focus-visible), +body [role=button]:focus:not(:focus-visible), +body input[type=radio]:focus:not(:focus-visible), +body input[type=checkbox]:focus:not(:focus-visible) { + outline: solid 1px transparent; +} + +body a:focus-visible, +body [role=button]:focus-visible, +body input[type=radio]:focus-visible, +body input[type=checkbox]:focus-visible { + outline: 2px solid var(--borderColor-accent-emphasis); + outline-offset: -2px; + box-shadow: none; +} + +body a:not([class]):focus, +body a:not([class]):focus-visible, +body input[type=radio]:focus, +body input[type=radio]:focus-visible, +body input[type=checkbox]:focus, +body input[type=checkbox]:focus-visible { + outline-offset: 0; +} + +body kbd { + display: inline-block; + padding: 0.25rem; + font: 11px ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace; + line-height: 10px; + color: #1f2328; + vertical-align: middle; + background-color: #f6f8fa; + border: solid 1px var(--borderColor-muted); + border-bottom-color: var(--borderColor-muted); + border-radius: 6px; + box-shadow: inset 0 -1px 0 var(--borderColor-muted); +} + +body h1, +body h2, +body h3, +body h4, +body h5, +body h6 { + margin-top: 1.5rem; + margin-bottom: 1rem; + font-weight: 600; + line-height: 1.25; +} + +body h2 { + font-weight: 600; + padding-bottom: .3em; + font-size: 1.5em; + border-bottom: 1px solid #d1d9e0b3; +} + +body h3 { + font-weight: 600; + font-size: 1.25em; +} + +body h4 { + font-weight: 600; + font-size: 1em; +} + +body h5 { + font-weight: 600; + font-size: .875em; +} + +body h6 { + font-weight: 600; + font-size: .85em; + color: #59636e; +} + +body p { + margin-top: 0; + margin-bottom: 10px; +} + +body blockquote { + margin: 0; + padding: 0 1em; + color: #59636e; + border-left: .25em solid #d1d9e0; +} + +body ul, +body ol { + margin-top: 0; + margin-bottom: 0; + padding-left: 2em; +} + +body ol ol, +body ul ol { + list-style-type: lower-roman; +} + +body ul ul ol, +body ul ol ol, +body ol ul ol, +body ol ol ol { + list-style-type: lower-alpha; +} + +body dd { + margin-left: 0; +} + +body tt, +body code, +body samp { + font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace; + font-size: 12px; +} + +body pre { + margin-top: 0; + margin-bottom: 0; + font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace; + font-size: 12px; + word-wrap: normal; +} + +body .octicon { + display: inline-block; + overflow: visible !important; + vertical-align: text-bottom; + fill: currentColor; +} + +body .Box-body.scrollable-overlay { + max-height: 400px; + overflow-y: scroll; +} + +body .Box-body .help { + padding-top: 0.5rem; + margin: 0; + color: #59636e; + text-align: center; +} + +body input::-webkit-outer-spin-button, +body input::-webkit-inner-spin-button { + margin: 0; + appearance: none; +} + +body .border-0 { + border: 0 !important; +} + +body .color-fg-muted { + color: #59636e !important; +} + +body .color-bg-default { + background-color: #ffffff !important; +} + +body .mb-0 { + margin-bottom: 0 !important; +} + +body .mr-2 { + margin-right: 0.5rem !important; +} + +body .my-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; +} + +body .p-0 { + padding: 0 !important; +} + +body .py-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; +} + +body .px-3 { + padding-right: 1rem !important; + padding-left: 1rem !important; +} + +body .f6 { + font-size: 0.75rem !important; +} + +body .text-bold { + font-weight: 600 !important; +} + +body .sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + overflow: hidden; + clip-path: rect(0 0 0 0); + overflow-wrap: normal; + border: 0; +} + +body::before { + display: table; + content: ""; +} + +body::after { + display: table; + clear: both; + content: ""; +} + +body>*:first-child { + margin-top: 0 !important; +} + +body>*:last-child { + margin-bottom: 0 !important; +} + +body a:not([href]) { + color: inherit; + text-decoration: none; +} + +body .absent { + color: #d1242f; +} + +body .anchor { + float: left; + padding-right: 0.25rem; + margin-left: -20px; + line-height: 1; +} + +body .anchor:focus { + outline: none; +} + +body p, +body blockquote, +body ul, +body ol, +body dl, +body table, +body pre, +body details { + margin-top: 0; + margin-bottom: 1rem; +} + +body blockquote>:first-child { + margin-top: 0; +} + +body blockquote>:last-child { + margin-bottom: 0; +} + +body h1 .octicon-link, +body h2 .octicon-link, +body h3 .octicon-link, +body h4 .octicon-link, +body h5 .octicon-link, +body h6 .octicon-link { + color: #1f2328; + vertical-align: middle; + visibility: hidden; +} + +body h1:hover .anchor, +body h2:hover .anchor, +body h3:hover .anchor, +body h4:hover .anchor, +body h5:hover .anchor, +body h6:hover .anchor { + text-decoration: none; +} + +body h1:hover .anchor .octicon-link, +body h2:hover .anchor .octicon-link, +body h3:hover .anchor .octicon-link, +body h4:hover .anchor .octicon-link, +body h5:hover .anchor .octicon-link, +body h6:hover .anchor .octicon-link { + visibility: visible; +} + +body h1 tt, +body h1 code, +body h2 tt, +body h2 code, +body h3 tt, +body h3 code, +body h4 tt, +body h4 code, +body h5 tt, +body h5 code, +body h6 tt, +body h6 code { + padding: 0 .2em; + font-size: inherit; +} + +body summary h1, +body summary h2, +body summary h3, +body summary h4, +body summary h5, +body summary h6 { + display: inline-block; +} + +body summary h1 .anchor, +body summary h2 .anchor, +body summary h3 .anchor, +body summary h4 .anchor, +body summary h5 .anchor, +body summary h6 .anchor { + margin-left: -40px; +} + +body summary h1, +body summary h2 { + padding-bottom: 0; + border-bottom: 0; +} + +body ul.no-list, +body ol.no-list { + padding: 0; + list-style-type: none; +} + +body ol[type="a s"] { + list-style-type: lower-alpha; +} + +body ol[type="A s"] { + list-style-type: upper-alpha; +} + +body ol[type="i s"] { + list-style-type: lower-roman; +} + +body ol[type="I s"] { + list-style-type: upper-roman; +} + +body ol[type="1"] { + list-style-type: decimal; +} + +body div>ol:not([type]) { + list-style-type: decimal; +} + +body ul ul, +body ul ol, +body ol ol, +body ol ul { + margin-top: 0; + margin-bottom: 0; +} + +body li>p { + margin-top: 1rem; +} + +body li+li { + margin-top: .25em; +} + +body dl { + padding: 0; +} + +body dl dt { + padding: 0; + margin-top: 1rem; + font-size: 1em; + font-style: italic; + font-weight: 600; +} + +body dl dd { + padding: 0 1rem; + margin-bottom: 1rem; +} + +body table th { + font-weight: 600; +} + +body table th, +body table td { + padding: 6px 13px; + border: 1px solid #d1d9e0; +} + +body table td>:last-child { + margin-bottom: 0; +} + +body table tr { + background-color: #ffffff; + border-top: 1px solid #d1d9e0b3; +} + +body table tr:nth-child(2n) { + background-color: #f6f8fa; +} + +body table img { + background-color: transparent; +} + +body img[align=right] { + padding-left: 20px; +} + +body img[align=left] { + padding-right: 20px; +} + +body .emoji { + max-width: none; + vertical-align: text-top; + background-color: transparent; +} + +body span.frame { + display: block; + overflow: hidden; +} + +body span.frame>span { + display: block; + float: left; + width: auto; + padding: 7px; + margin: 13px 0 0; + overflow: hidden; + border: 1px solid #d1d9e0; +} + +body span.frame span img { + display: block; + float: left; +} + +body span.frame span span { + display: block; + padding: 5px 0 0; + clear: both; + color: #1f2328; +} + +body span.align-center { + display: block; + overflow: hidden; + clear: both; +} + +body span.align-center>span { + display: block; + margin: 13px auto 0; + overflow: hidden; + text-align: center; +} + +body span.align-center span img { + margin: 0 auto; + text-align: center; +} + +body span.align-right { + display: block; + overflow: hidden; + clear: both; +} + +body span.align-right>span { + display: block; + margin: 13px 0 0; + overflow: hidden; + text-align: right; +} + +body span.align-right span img { + margin: 0; + text-align: right; +} + +body span.float-left { + display: block; + float: left; + margin-right: 13px; + overflow: hidden; +} + +body span.float-left span { + margin: 13px 0 0; +} + +body span.float-right { + display: block; + float: right; + margin-left: 13px; + overflow: hidden; +} + +body span.float-right>span { + display: block; + margin: 13px auto 0; + overflow: hidden; + text-align: right; +} + +body code, +body tt { + padding: .2em .4em; + margin: 0; + font-size: 85%; + white-space: break-spaces; + background-color: #818b981f; + border-radius: 6px; +} + +body code br, +body tt br { + display: none; +} + +body del code { + text-decoration: inherit; +} + +body samp { + font-size: 85%; +} + +body pre code { + font-size: 100%; +} + +body pre>code { + padding: 0; + margin: 0; + word-break: normal; + white-space: pre; + background: transparent; + border: 0; +} + +body .highlight { + margin-bottom: 1rem; +} + +body .highlight pre { + margin-bottom: 0; + word-break: normal; +} + +body .highlight pre, +body pre { + padding: 1rem; + overflow: auto; + font-size: 85%; + line-height: 1.45; + color: #1f2328; + background-color: #f6f8fa; + border-radius: 6px; +} + +body pre code, +body pre tt { + display: inline; + max-width: auto; + padding: 0; + margin: 0; + overflow: visible; + line-height: inherit; + word-wrap: normal; + background-color: transparent; + border: 0; +} + +body .csv-data td, +body .csv-data th { + padding: 5px; + overflow: hidden; + font-size: 12px; + line-height: 1; + text-align: left; + white-space: nowrap; +} + +body .csv-data .blob-num { + padding: 10px 0.5rem 9px; + text-align: right; + background: #ffffff; + border: 0; +} + +body .csv-data tr { + border-top: 0; +} + +body .csv-data th { + font-weight: 600; + background: #f6f8fa; + border-top: 0; +} + +body [data-footnote-ref]::before { + content: "["; +} + +body [data-footnote-ref]::after { + content: "]"; +} + +body .footnotes { + font-size: 12px; + color: #59636e; + border-top: 1px solid #d1d9e0; +} + +body .footnotes ol { + padding-left: 1rem; +} + +body .footnotes ol ul { + display: inline-block; + padding-left: 1rem; + margin-top: 1rem; +} + +body .footnotes li { + position: relative; +} + +body .footnotes li:target::before { + position: absolute; + top: calc(0.5rem*-1); + right: calc(0.5rem*-1); + bottom: calc(0.5rem*-1); + left: calc(1.5rem*-1); + pointer-events: none; + content: ""; + border: 2px solid #0969da; + border-radius: 6px; +} + +body .footnotes li:target { + color: #1f2328; +} + +body .footnotes .data-footnote-backref g-emoji { + font-family: monospace; +} + +body .Box { + background-color: #ffffff; + border-color: #d1d9e0; + border-radius: 0.375rem; + border-style: solid; + border-width: 0.0625rem; +} + +body .Box--condensed { + line-height: 1.25; +} + +body .Box--condensed .Box-body, +body .Box--condensed .Box-footer, +body .Box--condensed .Box-header { + padding: 0.5rem 1rem; +} + +body .Box--condensed .Box-row { + padding: 0.5rem 1rem; +} + +body .Box-header { + background-color: #f6f8fa; + border-color: #d1d9e0; + border-style: solid; + border-top-left-radius: 0.375rem; + border-top-right-radius: 0.375rem; + border-width: 0.0625rem; + margin: calc(0.0625rem*-1) calc(0.0625rem*-1) 0; + padding: 1rem; +} + +body .Box-body { + border-bottom: 0.0625rem solid #d1d9e0; + padding: 1rem; +} + +body .Box-body:last-of-type { + border-bottom-left-radius: 0.375rem; + border-bottom-right-radius: 0.375rem; + margin-bottom: calc(0.0625rem*-1); +} + +body .pl-c { + color: #59636e; +} + +body .pl-c1, +body .pl-s .pl-v { + color: #0550ae; +} + +body .pl-e, +body .pl-en { + color: #6639ba; +} + +body .pl-smi, +body .pl-s .pl-s1 { + color: #1f2328; +} + +body .pl-ent { + color: #0550ae; +} + +body .pl-k { + color: #cf222e; +} + +body .pl-s, +body .pl-pds, +body .pl-s .pl-pse .pl-s1, +body .pl-sr, +body .pl-sr .pl-cce, +body .pl-sr .pl-sre, +body .pl-sr .pl-sra { + color: #0a3069; +} + +body .pl-v, +body .pl-smw { + color: #953800; +} + +body .pl-bu { + color: #82071e; +} + +body .pl-ii { + color: #f6f8fa; + background-color: #82071e; +} + +body .pl-c2 { + color: #f6f8fa; + background-color: #cf222e; +} + +body .pl-sr .pl-cce { + font-weight: bold; + color: #116329; +} + +body .pl-ml { + color: #3b2300; +} + +body .pl-mh, +body .pl-mh .pl-en, +body .pl-ms { + font-weight: bold; + color: #0550ae; +} + +body .pl-mi { + font-style: italic; + color: #1f2328; +} + +body .pl-mb { + font-weight: bold; + color: #1f2328; +} + +body .pl-md { + color: #82071e; + background-color: #ffebe9; +} + +body .pl-mi1 { + color: #116329; + background-color: #dafbe1; +} + +body .pl-mc { + color: #953800; + background-color: #ffd8b5; +} + +body .pl-mi2 { + color: #d1d9e0; + background-color: #0550ae; +} + +body .pl-mdr { + font-weight: bold; + color: #8250df; +} + +body .pl-ba { + color: #59636e; +} + +body .pl-sg { + color: #818b98; +} + +body .pl-corl { + text-decoration: underline; + color: #0a3069; +} + +body [role=button]:focus:not(:focus-visible), +body [role=tabpanel][tabindex="0"]:focus:not(:focus-visible), +body button:focus:not(:focus-visible), +body summary:focus:not(:focus-visible), +body a:focus:not(:focus-visible) { + outline: none; + box-shadow: none; +} + +body [tabindex="0"]:focus:not(:focus-visible), +body details-dialog:focus:not(:focus-visible) { + outline: none; +} + +body g-emoji { + display: inline-block; + min-width: 1ch; + font-family: "Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"; + font-size: 1em; + font-style: normal !important; + font-weight: 400; + line-height: 1; + vertical-align: -0.075em; +} + +body g-emoji img { + width: 1em; + height: 1em; +} + +body .commit-tease-sha { + display: inline-block; + font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace; + font-size: 90%; + color: #1f2328; +} + +body .blob-wrapper { + overflow-x: auto; + overflow-y: hidden; +} + +body .blob-wrapper table tr:nth-child(2n) { + background-color: transparent; +} + +body .blob-wrapper-embedded { + max-height: 240px; + overflow-y: auto; +} + +body .blob-num { + position: relative; + width: 1%; + min-width: 50px; + padding-right: 10px; + padding-left: 10px; + font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace; + font-size: 12px; + line-height: 20px; + color: #59636e; + text-align: right; + white-space: nowrap; + vertical-align: top; + cursor: pointer; + -webkit-user-select: none; + user-select: none; +} + +body .blob-num:hover { + color: #1f2328; +} + +body .blob-num::before { + content: attr(data-line-number); +} + +body .blob-num.non-expandable { + cursor: default; +} + +body .blob-num.non-expandable:hover { + color: #59636e; +} + +body .blob-code { + position: relative; + padding-right: 10px; + padding-left: 10px; + line-height: 20px; + vertical-align: top; +} + +body .blob-code-inner { + display: table-cell; + overflow: visible; + font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace; + font-size: 12px; + color: #1f2328; + word-wrap: anywhere; + white-space: pre; +} + +body .blob-code-inner .x-first { + border-top-left-radius: .2em; + border-bottom-left-radius: .2em; +} + +body .blob-code-inner .x-last { + border-top-right-radius: .2em; + border-bottom-right-radius: .2em; +} + +body .blob-code-inner.highlighted, +body .blob-code-inner .highlighted { + background-color: #fff8c5; + box-shadow: inset 2px 0 0 #d4a72c66; +} + +body .blob-code-inner::selection, +body .blob-code-inner *::selection { + background-color: #0969da33; +} + +body .blob-code-inner.blob-code-addition, +body .blob-code-inner.blob-code-deletion { + position: relative; + padding-left: 22px !important; +} + +body .task-list-item { + list-style-type: none; +} + +body .task-list-item label { + font-weight: 400; +} + +body .task-list-item.enabled label { + cursor: pointer; +} + +body .task-list-item+.task-list-item { + margin-top: 0.25rem; +} + +body .task-list-item .handle { + display: none; +} + +/* body .task-list-item-checkbox { */ +.task-list-item > input { + margin: 0 .2em .25em -1.4em; + vertical-align: middle; +} + +body ul:dir(rtl) .task-list-item-checkbox { + margin: 0 -1.6em .25em .2em; +} + +body ol:dir(rtl) .task-list-item-checkbox { + margin: 0 -1.6em .25em .2em; +} + +body .contains-task-list:hover .task-list-item-convert-container, +body .contains-task-list:focus-within .task-list-item-convert-container { + display: block; + width: auto; + height: 24px; + overflow: visible; + clip: auto; +} + +body ::-webkit-calendar-picker-indicator { + filter: invert(50%); +} + +body .markdown-alert { + padding: 0.5rem 1rem; + margin-bottom: 1rem; + color: inherit; + border-left: .25em solid #d1d9e0; +} + +body .markdown-alert>:first-child { + margin-top: 0; +} + +body .markdown-alert>:last-child { + margin-bottom: 0; +} + +body .markdown-alert .markdown-alert-title { + display: flex; + font-weight: 500; + align-items: center; + line-height: 1; +} + +body .markdown-alert.markdown-alert-note { + border-left-color: #0969da; +} + +body .markdown-alert.markdown-alert-note .markdown-alert-title { + color: #0969da; +} + +body .markdown-alert.markdown-alert-important { + border-left-color: #8250df; +} + +body .markdown-alert.markdown-alert-important .markdown-alert-title { + color: #8250df; +} + +body .markdown-alert.markdown-alert-warning { + border-left-color: #9a6700; +} + +body .markdown-alert.markdown-alert-warning .markdown-alert-title { + color: #9a6700; +} + +body .markdown-alert.markdown-alert-tip { + border-left-color: #1a7f37; +} + +body .markdown-alert.markdown-alert-tip .markdown-alert-title { + color: #1a7f37; +} + +body .markdown-alert.markdown-alert-caution { + border-left-color: #cf222e; +} + +body .markdown-alert.markdown-alert-caution .markdown-alert-title { + color: #d1242f; +} + +body>*:first-child>.heading-element:first-child { + margin-top: 0 !important; +} + +body .tab-size[data-tab-size="1"] { + tab-size: 1; +} + +body .tab-size[data-tab-size="2"] { + tab-size: 2; +} + +body .tab-size[data-tab-size="3"] { + tab-size: 3; +} + +body .tab-size[data-tab-size="4"] { + tab-size: 4; +} + +body .tab-size[data-tab-size="5"] { + tab-size: 5; +} + +body .tab-size[data-tab-size="6"] { + tab-size: 6; +} + +body .tab-size[data-tab-size="7"] { + tab-size: 7; +} + +body .tab-size[data-tab-size="8"] { + tab-size: 8; +} + +body .tab-size[data-tab-size="9"] { + tab-size: 9; +} + +body .tab-size[data-tab-size="10"] { + tab-size: 10; +} + +body .tab-size[data-tab-size="11"] { + tab-size: 11; +} + +body .tab-size[data-tab-size="12"] { + tab-size: 12; +} + +body .Box .section-focus .preview-section { + display: none; +} + +body .Box .section-focus .edit-section { + display: block; +} + +body .highlight pre:has(+.zeroclipboard-container) { + min-height: 52px; +} \ No newline at end of file diff --git a/tools/export-android-build-env.sh b/tools/export-android-build-env.sh index 2e12a633a..f9a667fc3 100755 --- a/tools/export-android-build-env.sh +++ b/tools/export-android-build-env.sh @@ -32,11 +32,20 @@ case $_MR_ARCH in export MR_ANDROID_ABI=x86 ;; x86_64) + # -lt 代表 less than(小于) + if [[ $MR_ANDROID_API -lt 21 ]]; then + export MR_ANDROID_API=21 + fi export MR_TRIPLE=x86_64-linux-android$MR_ANDROID_API export MR_FF_ARCH=x86_64 export MR_ANDROID_ABI=x86_64 ;; arm64*) + # 64 位 ARM 架构是从 Android 5.0(API 21)才开始诞生的 + # -lt 代表 less than(小于) + if [[ $MR_ANDROID_API -lt 21 ]]; then + export MR_ANDROID_API=21 + fi export MR_TRIPLE=aarch64-linux-android$MR_ANDROID_API export MR_FF_ARCH=aarch64 export MR_ANDROID_ABI=arm64-v8a @@ -49,25 +58,6 @@ esac # x86_64 export MR_ARCH="$_MR_ARCH" -# openssl-armv7a -export MR_BUILD_NAME="${LIB_NAME}-${_MR_ARCH}" -# android/ffmpeg-x86_64 -export MR_BUILD_SOURCE="${MR_SRC_ROOT}/${MR_BUILD_NAME}" -# android/ffmpeg-x86_64 -export MR_BUILD_PREFIX="${MR_PRODUCT_ROOT}/${MR_BUILD_NAME}" - -if [ -z "$ANDROID_NDK_HOME" ]; then - echo "You must define ANDROID_NDK_HOME before starting." - echo "They must point to your NDK directories.\n" - exit 1 -else - export MR_NDK_REL=$(grep -m 1 -o '^## r[0-9]*.*' $ANDROID_NDK_HOME/CHANGELOG.md | awk '{print $2}') -fi - -export MR_ANDROID_NDK_HOME="$ANDROID_NDK_HOME" -export MR_TOOLCHAIN_ROOT="$MR_ANDROID_NDK_HOME/toolchains/llvm/prebuilt/${MR_HOST_TAG}" -export PATH="${MR_TOOLCHAIN_ROOT}/bin:$PATH" -export MR_SYS_ROOT="${MR_TOOLCHAIN_ROOT}/sysroot" # Common prefix for ld, as, etc. CROSS_PREFIX_WITH_PATH=${MR_TOOLCHAIN_ROOT}/bin/llvm- @@ -99,12 +89,16 @@ export MR_YASM=${MR_TOOLCHAIN_ROOT}/bin/yasm export MR_DEFAULT_CFLAGS="$MR_INIT_CFLAGS -D__ANDROID__" +# openssl-armv7a +# android/ffmpeg-x86_64 +export MR_BUILD_SOURCE="${MR_SRC_ROOT}/${REPO_DIR}-${_MR_ARCH}" +# android/fftutorial-x86_64 +export MR_BUILD_PREFIX="${MR_PRODUCT_ROOT}/${LIB_NAME}-${_MR_ARCH}" echo "MR_ARCH : [$MR_ARCH]" echo "MR_TRIPLE : [$MR_TRIPLE]" echo "MR_ANDROID_API : [$MR_ANDROID_API]" echo "MR_ANDROID_NDK : [$MR_NDK_REL]" -echo "MR_BUILD_NAME : [$MR_BUILD_NAME]" echo "MR_BUILD_SOURCE : [$MR_BUILD_SOURCE]" echo "MR_BUILD_PREFIX : [$MR_BUILD_PREFIX]" echo "MR_DEFAULT_CFLAGS : [$MR_DEFAULT_CFLAGS]" diff --git a/tools/export-android-host-env.sh b/tools/export-android-host-env.sh index afc7a6b3b..e6fcd35a6 100644 --- a/tools/export-android-host-env.sh +++ b/tools/export-android-host-env.sh @@ -19,14 +19,36 @@ function install_depends() { local name="$1" - local r=$(brew list | grep "$name") - if [[ -z $r ]]; then - echo "will use brew install ${name}." - brew install "$name" + if command -v "$name" &> /dev/null; then + echo "[✅] ${name}: $(eval $name --version | head -n 1)" + return 0 + else + if [[ "$name" == "rustup" || "$name" == "cargo" ]]; then + echo "will install rustup-init." + brew install rustup-init + rustup-init -y + return 0 + else + echo "will use brew install ${name}." + brew install "$name" + fi fi echo "[✅] ${name}: $(eval $name --version)" } +# 定义跨平台sed函数 +my_sed_i() { + if [[ "$(uname)" == "Darwin" ]]; then + # macOS系统 + sed -i '' "$@" + else + # Linux系统及其他系统 + sed -i "$@" + fi +} + +export -f my_sed_i + case "$OSTYPE" in darwin*) HOST_TAG="darwin-x86_64"; export -f install_depends ;; linux*) HOST_TAG="linux-x86_64" ;; @@ -53,7 +75,25 @@ export MR_HOST_NPROC="$HOST_NPROC" export MR_TAGET_OS="android" # export MR_PLAT="android" +if [[ -n "$ANDROID_NDK_HOME" ]];then + export MR_ANDROID_NDK_HOME="$ANDROID_NDK_HOME" +elif [[ -n "$ANDROID_NDK_ROOT" ]]; then + export MR_ANDROID_NDK_HOME="$ANDROID_NDK_ROOT" +elif [[ -n "$ANDROID_NDK" ]]; then + export MR_ANDROID_NDK_HOME="$ANDROID_NDK" +else + echo "You must define ANDROID_NDK_HOME or ANDROID_NDK_ROOT or ANDROID_NDK before starting." + echo "They must point to your NDK directories.\n" + exit 1 +fi + +export MR_NDK_REL=$(grep -m 1 -o '^## r[0-9]*.*' $MR_ANDROID_NDK_HOME/CHANGELOG.md | awk '{print $2}') + +export MR_TOOLCHAIN_ROOT="$MR_ANDROID_NDK_HOME/toolchains/llvm/prebuilt/${MR_HOST_TAG}" +export PATH="${MR_TOOLCHAIN_ROOT}/bin:$PATH" +export MR_SYS_ROOT="${MR_TOOLCHAIN_ROOT}/sysroot" + # Using Make from the Android SDK -export MR_MAKE_EXECUTABLE=${ANDROID_NDK_HOME}/prebuilt/${MR_HOST_TAG}/bin/make +export MR_MAKE_EXECUTABLE=${MR_ANDROID_NDK_HOME}/prebuilt/${MR_HOST_TAG}/bin/make # Init Android plat env export MR_DEFAULT_ARCHS="armv7a arm64 x86 x86_64" \ No newline at end of file diff --git a/tools/export-android-pkg-config-dir.sh b/tools/export-android-pkg-config-dir.sh index 64d76a98f..74c428606 100644 --- a/tools/export-android-pkg-config-dir.sh +++ b/tools/export-android-pkg-config-dir.sh @@ -27,7 +27,7 @@ else uni_dir="${MR_UNI_PROD_DIR}" fi -for dir in `[ -d ${uni_dir} ] && find "${uni_dir}" -type f -name "*.pc" | grep "$_MR_ARCH\/" | xargs dirname | uniq` ; +for dir in `[ -d ${uni_dir} ] && find "${uni_dir}" -type f -name "*.pc" | grep "$_MR_ARCH\/" | xargs -n 1 dirname | uniq` ; do if [[ $pkg_cfg_dir ]];then pkg_cfg_dir="${pkg_cfg_dir}:${dir}" diff --git a/tools/export-apple-build-env.sh b/tools/export-apple-build-env.sh index 3a929bc20..eac44b2e0 100755 --- a/tools/export-apple-build-env.sh +++ b/tools/export-apple-build-env.sh @@ -16,9 +16,9 @@ # #https://stackoverflow.com/questions/59895/how-do-i-get-the-directory-where-a-bash-script-is-located-from-within-the-script -XCRUN_DEVELOPER=`xcode-select -print-path` -if [ ! -d "$XCRUN_DEVELOPER" ]; then - echo "xcode path is not set correctly $XCRUN_DEVELOPER does not exist (most likely because of xcode > 4.3)" +XCODE_DEVELOPER=`xcode-select -print-path` +if [ ! -d "$XCODE_DEVELOPER" ]; then + echo "xcode path is not set correctly $XCODE_DEVELOPER does not exist (most likely because of xcode > 4.3)" echo "run" echo "sudo xcode-select -switch " echo "for default installation:" @@ -26,25 +26,30 @@ if [ ! -d "$XCRUN_DEVELOPER" ]; then exit 1 fi -case $XCRUN_DEVELOPER in +case $XCODE_DEVELOPER in *\ * ) echo "Your Xcode path contains whitespaces, which is not supported." exit 1 ;; esac +# /Applications/Xcode.app/Contents/Developer +export MR_XCODE_DEVELOPER="$XCODE_DEVELOPER" + echo $(xcodebuild -version) +DEPLOYMENT_TARGET_KEY= if [[ "$MR_PLAT" == 'ios' ]]; then + export MR_DEPLOYMENT_TARGET_VER=12.0 case $_MR_ARCH in *_simulator) export XCRUN_PLATFORM='iPhoneSimulator' - DEPLOYMENT_TARGET='-mios-simulator-version-min=11.0' + DEPLOYMENT_TARGET_KEY='-mios-simulator-version-min' export MR_IS_SIMULATOR=1 ;; 'arm64') export XCRUN_PLATFORM='iPhoneOS' - DEPLOYMENT_TARGET='-miphoneos-version-min=11.0' + DEPLOYMENT_TARGET_KEY='-miphoneos-version-min' export MR_IS_SIMULATOR=0 ;; *) @@ -52,21 +57,23 @@ if [[ "$MR_PLAT" == 'ios' ]]; then exit 1 ;; esac - elif [[ "$MR_PLAT" == 'macos' ]]; then - export XCRUN_PLATFORM='MacOSX' - export MACOSX_DEPLOYMENT_TARGET=10.11 - DEPLOYMENT_TARGET="-mmacosx-version-min=$MACOSX_DEPLOYMENT_TARGET" - export MR_IS_SIMULATOR=0 - elif [[ "$MR_PLAT" == 'tvos' ]]; then +elif [[ "$MR_PLAT" == 'macos' ]]; then + export XCRUN_PLATFORM='MacOSX' + export MACOSX_DEPLOYMENT_TARGET=10.14 + export MR_DEPLOYMENT_TARGET_VER=10.14 + DEPLOYMENT_TARGET_KEY="-mmacosx-version-min" + export MR_IS_SIMULATOR=0 +elif [[ "$MR_PLAT" == 'tvos' ]]; then + export MR_DEPLOYMENT_TARGET_VER=12.0 case $_MR_ARCH in *_simulator) export XCRUN_PLATFORM='AppleTVSimulator' - DEPLOYMENT_TARGET="-mtvos-simulator-version-min=12.0" + DEPLOYMENT_TARGET_KEY="-mtvos-simulator-version-min" export MR_IS_SIMULATOR=1 ;; 'arm64') export XCRUN_PLATFORM='AppleTVOS' - DEPLOYMENT_TARGET="-mtvos-version-min=12.0" + DEPLOYMENT_TARGET_KEY="-mtvos-version-min" export MR_IS_SIMULATOR=0 ;; *) @@ -92,17 +99,15 @@ export MR_SYS_ROOT=`xcrun -sdk $XCRUN_SDK --show-sdk-path` export MR_ARCH="${_MR_ARCH/_simulator/}" export MR_FF_ARCH="${MR_ARCH}" -# ffmpeg-x86_64 -export MR_BUILD_NAME="${LIB_NAME}-${_MR_ARCH}" -# ios/ffmpeg-x86_64 -export MR_BUILD_SOURCE="${MR_SRC_ROOT}/${MR_BUILD_NAME}" # ios/ffmpeg-x86_64 -export MR_BUILD_PREFIX="${MR_PRODUCT_ROOT}/${MR_BUILD_NAME}" +export MR_BUILD_SOURCE="${MR_SRC_ROOT}/${REPO_DIR}-${_MR_ARCH}" +# ios/fftutorial-x86_64 +export MR_BUILD_PREFIX="${MR_PRODUCT_ROOT}/${LIB_NAME}-${_MR_ARCH}" +export MR_DEPLOYMENT_TARGET="${DEPLOYMENT_TARGET_KEY}=${MR_DEPLOYMENT_TARGET_VER}" # -arch x86_64 -mios-simulator-version-min=11.0 -export MR_DEFAULT_CFLAGS="-arch $MR_ARCH $MR_INIT_CFLAGS $DEPLOYMENT_TARGET -D__APPLE__" +export MR_DEFAULT_CFLAGS="-arch $MR_ARCH $MR_INIT_CFLAGS $MR_DEPLOYMENT_TARGET -D__APPLE__" echo "MR_ARCH : [$MR_ARCH]" -echo "MR_BUILD_NAME : [$MR_BUILD_NAME]" echo "MR_BUILD_SOURCE : [$MR_BUILD_SOURCE]" echo "MR_BUILD_PREFIX : [$MR_BUILD_PREFIX]" echo "MR_DEFAULT_CFLAGS: [$MR_DEFAULT_CFLAGS]" diff --git a/tools/export-apple-host-env.sh b/tools/export-apple-host-env.sh index a5dbedfd2..35e1eb0b6 100644 --- a/tools/export-apple-host-env.sh +++ b/tools/export-apple-host-env.sh @@ -19,12 +19,20 @@ if [[ "$MR_PLAT" != 'macos' ]]; then export MR_FORCE_CROSS=true fi -if [[ "$MR_PLAT" == 'ios' ]]; then - export MR_DEFAULT_ARCHS="arm64 arm64_simulator x86_64_simulator" +if [[ "$MR_VENDOR_LIBS" == *"moltenvk"* || "$MR_VENDOR_LIBS" == "moltenvk" ]]; then + if [[ "$MR_PLAT" == 'macos' ]]; then + export MR_DEFAULT_ARCHS="arm64" + elif [[ "$MR_PLAT" == 'ios' || "$MR_PLAT" == 'tvos' ]]; then + export MR_DEFAULT_ARCHS="arm64 arm64_simulator" + fi +else + if [[ "$MR_PLAT" == 'ios' ]]; then + export MR_DEFAULT_ARCHS="arm64 arm64_simulator x86_64_simulator" elif [[ "$MR_PLAT" == 'macos' ]]; then - export MR_DEFAULT_ARCHS="x86_64 arm64" + export MR_DEFAULT_ARCHS="x86_64 arm64" elif [[ "$MR_PLAT" == 'tvos' ]]; then - export MR_DEFAULT_ARCHS="arm64 arm64_simulator x86_64_simulator" + export MR_DEFAULT_ARCHS="arm64 arm64_simulator x86_64_simulator" + fi fi # Number of physical cores in the system to facilitate parallel assembling @@ -35,12 +43,38 @@ export DEBUG_INFORMATION_FORMAT=dwarf-with-dsym function install_depends() { local name="$1" - local r=$(brew list | grep "$name") - if [[ -z $r ]]; then - echo "will use brew install ${name}." - brew install "$name" + local check_name="$name" + # On macOS, GNU libtool is installed as glibtool to avoid conflict with Apple's libtool + if [[ "$(uname)" == "Darwin" && "$name" == "libtool" ]]; then + check_name="glibtool" + fi + + if command -v "$check_name" &> /dev/null; then + echo "[✅] ${name}: $(eval $check_name --version | head -n 1)" + return 0 + else + if [[ "$name" == "rustup" || "$name" == "cargo" ]]; then + echo "will install rustup-init." + brew install rustup-init + rustup-init -y + return 0 + else + echo "will use brew install ${name}." + brew install "$name" + fi + fi +} + +# 定义跨平台sed函数 +my_sed_i() { + if [[ "$(uname)" == "Darwin" ]]; then + # macOS系统 + sed -i '' "$@" + else + # Linux系统及其他系统 + sed -i "$@" fi - echo "[✅] ${name}: $(eval $name --version)" } -export -f install_depends \ No newline at end of file +export -f install_depends +export -f my_sed_i \ No newline at end of file diff --git a/tools/ios.toolchain.cmake b/tools/ios.toolchain.cmake index 45c563663..1e447e7f6 100644 --- a/tools/ios.toolchain.cmake +++ b/tools/ios.toolchain.cmake @@ -74,6 +74,7 @@ # WATCHOSCOMBINED = Build for armv7k arm64_32 x86_64 watchOS + watchOS Simulator. Combined into FAT STATIC lib (only supported on 3.14+ of CMake with "-G Xcode" argument in combination with the "cmake --install" CMake build step) # SIMULATOR_WATCHOS = Build for x86_64 for watchOS Simulator. # SIMULATORARM64_WATCHOS = Build for arm64 for watchOS Simulator. +# SIMULATOR_WATCHOSCOMBINED = Build for arm64 x86_64 for watchOS Simulator. Combined into FAT STATIC lib (supported on 3.14+ of CMakewith "-G Xcode" argument ONLY) # MAC = Build for x86_64 macOS. # MAC_ARM64 = Build for Apple Silicon macOS. # MAC_UNIVERSAL = Combined build for x86_64 and Apple Silicon on macOS. @@ -98,6 +99,7 @@ # ON (default) = Will require "enable_language(OBJC) and/or enable_language(OBJCXX)" for full OBJC|OBJCXX support # OFF = Will embed the OBJC and OBJCXX flags into the CMAKE_C_FLAGS and CMAKE_CXX_FLAGS (legacy behavior, CMake version < 3.16) # +# ENABLE_BITCODE: (ON|OFF) Enables or disables bitcode support. Default OFF # # ENABLE_ARC: (ON|OFF) Enables or disables ARC support. Default ON (ARC enabled by default) # @@ -152,7 +154,7 @@ # command. # -cmake_minimum_required(VERSION 3.8.0) +cmake_minimum_required(VERSION 3.10) # CMake invokes the toolchain file twice during the first build, but only once during subsequent rebuilds. # NOTE: To improve single-library build-times, provide the flag "OS_SINGLE_BUILD" as a build argument. @@ -165,9 +167,9 @@ set(ENV{_IOS_TOOLCHAIN_HAS_RUN} true) list(APPEND _supported_platforms "OS" "OS64" "OS64COMBINED" "SIMULATOR" "SIMULATOR64" "SIMULATORARM64" "SIMULATOR64COMBINED" "TVOS" "TVOSCOMBINED" "SIMULATOR_TVOS" "SIMULATORARM64_TVOS" - "WATCHOS" "WATCHOSCOMBINED" "SIMULATOR_WATCHOS" "SIMULATORARM64_WATCHOS" + "WATCHOS" "WATCHOSCOMBINED" "SIMULATOR_WATCHOS" "SIMULATORARM64_WATCHOS" "SIMULATOR_WATCHOSCOMBINED" "MAC" "MAC_ARM64" "MAC_UNIVERSAL" - "VISIONOS" "SIMULATOR_VISIONOS" "VISIONOSCOMBINED" + "VISIONOS" "SIMULATOR_VISIONOS" "VISIONOSCOMBINED" "MAC_CATALYST" "MAC_CATALYST_ARM64" "MAC_CATALYST_UNIVERSAL") # Cache what generator is used @@ -263,28 +265,28 @@ set(NAMED_LANGUAGE_SUPPORT_INT ${NAMED_LANGUAGE_SUPPORT} CACHE BOOL # Specify the minimum version of the deployment target. if(NOT DEFINED DEPLOYMENT_TARGET) if (PLATFORM MATCHES "WATCHOS") - # Unless specified, SDK version 4.0 is used by default as minimum target version (watchOS). + # Unless specified, SDK version 6.0 is used by default as minimum target version (watchOS). set(DEPLOYMENT_TARGET "6.0") elseif(PLATFORM STREQUAL "MAC") # Unless specified, SDK version 10.13 (High Sierra) is used by default as the minimum target version (macos). - set(DEPLOYMENT_TARGET "10.11") + set(DEPLOYMENT_TARGET "10.13") elseif(PLATFORM STREQUAL "VISIONOS" OR PLATFORM STREQUAL "SIMULATOR_VISIONOS" OR PLATFORM STREQUAL "VISIONOSCOMBINED") # Unless specified, SDK version 1.0 is used by default as minimum target version (visionOS). set(DEPLOYMENT_TARGET "1.0") elseif(PLATFORM STREQUAL "MAC_ARM64") # Unless specified, SDK version 11.0 (Big Sur) is used by default as the minimum target version (macOS on arm). - set(DEPLOYMENT_TARGET "10.11") + set(DEPLOYMENT_TARGET "11.0") elseif(PLATFORM STREQUAL "MAC_UNIVERSAL") - # Unless specified, SDK version 11.0 (Big Sur) is used by default as minimum target version for universal builds. - set(DEPLOYMENT_TARGET "10.11") + # Unless specified, SDK version 10.13 (High Sierra) is used by default as minimum target version for universal builds. + set(DEPLOYMENT_TARGET "10.13") elseif(PLATFORM STREQUAL "MAC_CATALYST" OR PLATFORM STREQUAL "MAC_CATALYST_ARM64" OR PLATFORM STREQUAL "MAC_CATALYST_UNIVERSAL") - # Unless specified, SDK version 13.0 is used by default as the minimum target version (mac catalyst minimum requirement). + # Unless specified, SDK version 13.1 is used by default as the minimum target version (mac catalyst minimum requirement). set(DEPLOYMENT_TARGET "13.1") else() - # Unless specified, SDK version 11.0 is used by default as the minimum target version (iOS, tvOS). - set(DEPLOYMENT_TARGET "11.0") + # Unless specified, SDK version 13.0 is used by default as the minimum target version (iOS, tvOS). + set(DEPLOYMENT_TARGET "13.0") endif() - message(STATUS "[DEFAULTS] Using the default min-version (${DEPLOYMENT_TARGET}) since DEPLOYMENT_TARGET not provided!") + message(STATUS "[DEFAULTS] Using the default min-version since DEPLOYMENT_TARGET not provided!") elseif(DEFINED DEPLOYMENT_TARGET AND PLATFORM MATCHES "^MAC_CATALYST" AND ${DEPLOYMENT_TARGET} VERSION_LESS "13.1") message(FATAL_ERROR "Mac Catalyst builds requires a minimum deployment target of 13.1!") endif() @@ -315,13 +317,13 @@ if(PLATFORM_INT STREQUAL "OS") set(ARCHS armv7 armv7s arm64) set(APPLE_TARGET_TRIPLE_INT arm-apple-ios${DEPLOYMENT_TARGET}) else() - set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-ios${DEPLOYMENT_TARGET}) + set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-ios${DEPLOYMENT_TARGET}) endif() elseif(PLATFORM_INT STREQUAL "OS64") set(SDK_NAME iphoneos) if(NOT ARCHS) if (XCODE_VERSION_INT VERSION_GREATER 10.0) - set(ARCHS arm64) # FIXME: Add arm64e when Apple has fixed the integration issues with it + set(ARCHS arm64) # FIXME: Add arm64e when Apple has fixed the integration issues with it, libarclite_iphoneos.a is currently missing bitcode markers for example else() set(ARCHS arm64) endif() @@ -358,7 +360,7 @@ elseif(PLATFORM_INT STREQUAL "SIMULATOR64COMBINED") if(MODERN_CMAKE) if(NOT ARCHS) if (XCODE_VERSION_INT VERSION_GREATER 12.0) - set(ARCHS arm64 x86_64) # FIXME: Add arm64e when Apple have fixed the integration issues with it + set(ARCHS arm64 x86_64) # FIXME: Add arm64e when Apple have fixed the integration issues with it, libarclite_iphoneos.a is currently missing bitcode markers for example set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=iphoneos*] "") set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=iphonesimulator*] "x86_64 arm64") set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=iphoneos*] "") @@ -460,12 +462,12 @@ elseif(PLATFORM_INT STREQUAL "WATCHOSCOMBINED") if(MODERN_CMAKE) if(NOT ARCHS) if (XCODE_VERSION_INT VERSION_GREATER 10.0) - set(ARCHS armv7k arm64_32 i386) - set(APPLE_TARGET_TRIPLE_INT arm64_32-i386-apple-watchos${DEPLOYMENT_TARGET}) + set(ARCHS armv7k arm64_32 x86_64) + set(APPLE_TARGET_TRIPLE_INT arm64_32-x86_64-apple-watchos${DEPLOYMENT_TARGET}) set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=watchos*] "armv7k arm64_32") - set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=watchsimulator*] "i386") + set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=watchsimulator*] "x86_64") set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=watchos*] "armv7k arm64_32") - set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=watchsimulator*] "i386") + set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=watchsimulator*] "x86_64") else() set(ARCHS armv7k i386) set(APPLE_TARGET_TRIPLE_INT arm-i386-apple-watchos${DEPLOYMENT_TARGET}) @@ -483,8 +485,13 @@ elseif(PLATFORM_INT STREQUAL "WATCHOSCOMBINED") elseif(PLATFORM_INT STREQUAL "SIMULATOR_WATCHOS") set(SDK_NAME watchsimulator) if(NOT ARCHS) - set(ARCHS i386) - set(APPLE_TARGET_TRIPLE_INT i386-apple-watchos${DEPLOYMENT_TARGET}-simulator) + if (XCODE_VERSION_INT VERSION_GREATER 10.0) + set(ARCHS x86_64) + set(APPLE_TARGET_TRIPLE_INT x86_64-apple-watchos${DEPLOYMENT_TARGET}-simulator) + else() + set(ARCHS i386) + set(APPLE_TARGET_TRIPLE_INT i386-apple-watchos${DEPLOYMENT_TARGET}-simulator) + endif() else() set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-watchos${DEPLOYMENT_TARGET}-simulator) endif() @@ -496,6 +503,31 @@ elseif(PLATFORM_INT STREQUAL "SIMULATORARM64_WATCHOS") else() set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-watchos${DEPLOYMENT_TARGET}-simulator) endif() +elseif(PLATFORM_INT STREQUAL "SIMULATOR_WATCHOSCOMBINED") + set(SDK_NAME watchsimulator) + if(MODERN_CMAKE) + if(NOT ARCHS) + if (XCODE_VERSION_INT VERSION_GREATER 12.0) + set(ARCHS arm64 x86_64) + set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=watchos*] "") + set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=watchsimulator*] "arm64 x86_64") + set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=watchos*] "") + set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=watchsimulator*] "arm64 x86_64") + set(APPLE_TARGET_TRIPLE_INT arm64_x86_64-apple-watchos${DEPLOYMENT_TARGET}-simulator) + else() + set(ARCHS arm64 i386) + set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=watchos*] "") + set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=watchsimulator*] "i386") + set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=watchos*] "") + set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=watchsimulator*] "i386") + set(APPLE_TARGET_TRIPLE_INT arm64_i386-apple-watchos${DEPLOYMENT_TARGET}-simulator) + endif() + else() + set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-watchos${DEPLOYMENT_TARGET}-simulator) + endif() + else() + message(FATAL_ERROR "Please make sure that you are running CMake 3.14+ to make the SIMULATOR_WATCHOSCOMBINED setting work") + endif() elseif(PLATFORM_INT STREQUAL "SIMULATOR_VISIONOS") set(SDK_NAME xrsimulator) if(NOT ARCHS) @@ -553,15 +585,16 @@ elseif(PLATFORM_INT STREQUAL "MAC_UNIVERSAL") if(NOT ARCHS) set(ARCHS "x86_64;arm64") endif() - string(REPLACE ";" "-" ARCHS_SPLIT "${ARCHS}") - set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-macosx${DEPLOYMENT_TARGET}) + # For universal builds, don't set target triple - let CMake handle it + # string(REPLACE ";" "-" ARCHS_SPLIT "${ARCHS}") + # set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-macosx${DEPLOYMENT_TARGET}) elseif(PLATFORM_INT STREQUAL "MAC_CATALYST_UNIVERSAL") set(SDK_NAME macosx) if(NOT ARCHS) set(ARCHS "x86_64;arm64") endif() string(REPLACE ";" "-" ARCHS_SPLIT "${ARCHS}") - set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-ios${DEPLOYMENT_TARGET}-macabi) + set(APPLE_TARGET_TRIPLE_INT apple-ios${DEPLOYMENT_TARGET}-macabi) else() message(FATAL_ERROR "Invalid PLATFORM: ${PLATFORM_INT}") endif() @@ -616,6 +649,13 @@ elseif(DEFINED CMAKE_OSX_SYSROOT_INT) set(CMAKE_OSX_SYSROOT "${CMAKE_OSX_SYSROOT_INT}" CACHE INTERNAL "") endif() +# Use bitcode or not +if(NOT DEFINED ENABLE_BITCODE) + message(STATUS "[DEFAULTS] Disabling bitcode support by default. ENABLE_BITCODE not provided for override!") + set(ENABLE_BITCODE OFF) +endif() +set(ENABLE_BITCODE_INT ${ENABLE_BITCODE} CACHE BOOL + "Whether or not to enable bitcode" FORCE) # Use ARC or not if(NOT DEFINED ENABLE_ARC) # Unless specified, enable ARC support by default @@ -869,6 +909,15 @@ if(PLATFORM_INT MATCHES "^MAC_CATALYST") set(C_TARGET_FLAGS "-isystem ${CMAKE_OSX_SYSROOT_INT}/System/iOSSupport/usr/include -iframework ${CMAKE_OSX_SYSROOT_INT}/System/iOSSupport/System/Library/Frameworks") endif() +if(ENABLE_BITCODE_INT) + set(BITCODE "-fembed-bitcode") + set(CMAKE_XCODE_ATTRIBUTE_BITCODE_GENERATION_MODE "bitcode") + set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "YES") +else() + set(BITCODE "") + set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "NO") +endif() + if(ENABLE_ARC_INT) set(FOBJC_ARC "-fobjc-arc") set(CMAKE_XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC "YES") @@ -907,26 +956,26 @@ endif() if(CMAKE_GENERATOR MATCHES "Xcode") message(STATUS "Not setting any manual command-line buildflags, since Xcode is selected as the generator. Modifying the Xcode build-settings directly instead.") else() - set(CMAKE_C_FLAGS "${C_TARGET_FLAGS} ${APPLE_TARGET_TRIPLE_FLAG} ${SDK_NAME_VERSION_FLAGS} ${OBJC_LEGACY_VARS} ${VISIBILITY} ${CMAKE_C_FLAGS}" CACHE INTERNAL + set(CMAKE_C_FLAGS "${C_TARGET_FLAGS} ${APPLE_TARGET_TRIPLE_FLAG} ${SDK_NAME_VERSION_FLAGS} ${OBJC_LEGACY_VARS} ${BITCODE} ${VISIBILITY} ${CMAKE_C_FLAGS}" CACHE INTERNAL "Flags used by the compiler during all C build types.") set(CMAKE_C_FLAGS_DEBUG "-O0 -g ${CMAKE_C_FLAGS_DEBUG}") set(CMAKE_C_FLAGS_MINSIZEREL "-DNDEBUG -Os ${CMAKE_C_FLAGS_MINSIZEREL}") set(CMAKE_C_FLAGS_RELWITHDEBINFO "-DNDEBUG -O2 -g ${CMAKE_C_FLAGS_RELWITHDEBINFO}") set(CMAKE_C_FLAGS_RELEASE "-DNDEBUG -O3 ${CMAKE_C_FLAGS_RELEASE}") - set(CMAKE_CXX_FLAGS "${C_TARGET_FLAGS} ${APPLE_TARGET_TRIPLE_FLAG} ${SDK_NAME_VERSION_FLAGS} ${OBJC_LEGACY_VARS} ${VISIBILITY} ${CMAKE_CXX_FLAGS}" CACHE INTERNAL + set(CMAKE_CXX_FLAGS "${C_TARGET_FLAGS} ${APPLE_TARGET_TRIPLE_FLAG} ${SDK_NAME_VERSION_FLAGS} ${OBJC_LEGACY_VARS} ${BITCODE} ${VISIBILITY} ${CMAKE_CXX_FLAGS}" CACHE INTERNAL "Flags used by the compiler during all CXX build types.") set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g ${CMAKE_CXX_FLAGS_DEBUG}") set(CMAKE_CXX_FLAGS_MINSIZEREL "-DNDEBUG -Os ${CMAKE_CXX_FLAGS_MINSIZEREL}") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-DNDEBUG -O2 -g ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") set(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG -O3 ${CMAKE_CXX_FLAGS_RELEASE}") if(NAMED_LANGUAGE_SUPPORT_INT) - set(CMAKE_OBJC_FLAGS "${C_TARGET_FLAGS} ${APPLE_TARGET_TRIPLE_FLAG} ${SDK_NAME_VERSION_FLAGS} ${VISIBILITY} ${FOBJC_ARC} ${OBJC_VARS} ${CMAKE_OBJC_FLAGS}" CACHE INTERNAL + set(CMAKE_OBJC_FLAGS "${C_TARGET_FLAGS} ${APPLE_TARGET_TRIPLE_FLAG} ${SDK_NAME_VERSION_FLAGS} ${BITCODE} ${VISIBILITY} ${FOBJC_ARC} ${OBJC_VARS} ${CMAKE_OBJC_FLAGS}" CACHE INTERNAL "Flags used by the compiler during all OBJC build types.") set(CMAKE_OBJC_FLAGS_DEBUG "-O0 -g ${CMAKE_OBJC_FLAGS_DEBUG}") set(CMAKE_OBJC_FLAGS_MINSIZEREL "-DNDEBUG -Os ${CMAKE_OBJC_FLAGS_MINSIZEREL}") set(CMAKE_OBJC_FLAGS_RELWITHDEBINFO "-DNDEBUG -O2 -g ${CMAKE_OBJC_FLAGS_RELWITHDEBINFO}") set(CMAKE_OBJC_FLAGS_RELEASE "-DNDEBUG -O3 ${CMAKE_OBJC_FLAGS_RELEASE}") - set(CMAKE_OBJCXX_FLAGS "${C_TARGET_FLAGS} ${APPLE_TARGET_TRIPLE_FLAG} ${SDK_NAME_VERSION_FLAGS} ${VISIBILITY} ${FOBJC_ARC} ${OBJC_VARS} ${CMAKE_OBJCXX_FLAGS}" CACHE INTERNAL + set(CMAKE_OBJCXX_FLAGS "${C_TARGET_FLAGS} ${APPLE_TARGET_TRIPLE_FLAG} ${SDK_NAME_VERSION_FLAGS} ${BITCODE} ${VISIBILITY} ${FOBJC_ARC} ${OBJC_VARS} ${CMAKE_OBJCXX_FLAGS}" CACHE INTERNAL "Flags used by the compiler during all OBJCXX build types.") set(CMAKE_OBJCXX_FLAGS_DEBUG "-O0 -g ${CMAKE_OBJCXX_FLAGS_DEBUG}") set(CMAKE_OBJCXX_FLAGS_MINSIZEREL "-DNDEBUG -Os ${CMAKE_OBJCXX_FLAGS_MINSIZEREL}") @@ -943,7 +992,7 @@ else() set(CMAKE_OBJCXX_LINK_FLAGS "${C_TARGET_FLAGS} ${SDK_NAME_VERSION_FLAGS} -Wl,-search_paths_first ${CMAKE_OBJCXX_LINK_FLAGS}" CACHE INTERNAL "Flags used by the compiler for all OBJCXX link types.") endif() - set(CMAKE_ASM_FLAGS "${CMAKE_C_FLAGS} -x assembler-with-cpp -arch ${CMAKE_OSX_ARCHITECTURES} ${APPLE_TARGET_TRIPLE_FLAG}" CACHE INTERNAL + set(CMAKE_ASM_FLAGS "${CMAKE_C_FLAGS} -x assembler-with-cpp" CACHE INTERNAL "Flags used by the compiler for all ASM build types.") endif() @@ -973,6 +1022,11 @@ if(DEFINED SDK_NAME_VERSION_FLAGS) message(STATUS "Using version flags: ${SDK_NAME_VERSION_FLAGS}") endif() message(STATUS "Using a data_ptr size of: ${CMAKE_CXX_SIZEOF_DATA_PTR}") +if(ENABLE_BITCODE_INT) + message(STATUS "Bitcode: Enabled") +else() + message(STATUS "Bitcode: Disabled") +endif() if(ENABLE_ARC_INT) message(STATUS "ARC: Enabled") @@ -1002,6 +1056,7 @@ set(CMAKE_TRY_COMPILE_PLATFORM_VARIABLES DEPLOYMENT_TARGET CMAKE_DEVELOPER_ROOT CMAKE_OSX_SYSROOT_INT + ENABLE_BITCODE ENABLE_ARC CMAKE_ASM_COMPILER CMAKE_C_COMPILER @@ -1026,7 +1081,7 @@ set(CMAKE_TRY_COMPILE_PLATFORM_VARIABLES ) if(NAMED_LANGUAGE_SUPPORT_INT) - list(APPEND CMAKE_TRY_COMPILE_PLATFORM_VARIABLES + list(APPEND CMAKE_TRY_COMPILE_PLATFORM_VARIABLES CMAKE_OBJC_FLAGS CMAKE_OBJC_DEBUG CMAKE_OBJC_MINSIZEREL @@ -1064,7 +1119,7 @@ IF(NOT DEFINED CMAKE_FIND_FRAMEWORK) ENDIF(NOT DEFINED CMAKE_FIND_FRAMEWORK) # Set up the default search directories for frameworks. -if(PLATFORM_INT MATCHES "^MAC_CATALYST") +if(PLATFORM_INT MATCHES "^MAC_CATALYST") set(CMAKE_FRAMEWORK_PATH ${CMAKE_DEVELOPER_ROOT}/Library/PrivateFrameworks ${CMAKE_OSX_SYSROOT_INT}/System/Library/Frameworks @@ -1119,4 +1174,4 @@ macro(find_host_package) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY BOTH) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH) set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE BOTH) -endmacro(find_host_package) +endmacro(find_host_package) \ No newline at end of file diff --git a/tools/list-all-feature.sh b/tools/list-all-feature.sh new file mode 100755 index 000000000..dac1f5c43 --- /dev/null +++ b/tools/list-all-feature.sh @@ -0,0 +1,162 @@ +#!/bin/bash + +# 获取当前脚本所在目录并切换 +SHELL_ROOT=$(cd "$(dirname "$0")" && pwd) +cd "$SHELL_ROOT" +cd ../build/src/macos + +# 定义 FFmpeg 版本及其对应的配置路径 +# 顺序必须从新到旧:左边最新,右边最老 +VERSIONS=( + "8.1.2|./ffmpeg8-arm64/configure" + "7.1.3|./ffmpeg7-arm64/configure" + "6.1.1|./ffmpeg6-arm64/configure" + "5.1.6|./ffmpeg5-arm64/configure" + "4.0.5|./ffmpeg4-arm64/configure" +) + +# 创建临时文件夹 +TMP_DIR=$(mktemp -d /tmp/ffmpeg_perfect.XXXXXX) +trap 'rm -rf "$TMP_DIR"' EXIT + +# ========================================== +# 特征处理主函数(采用全局大列表对齐算法) +# ========================================== +process_feature() { + local list_cmd="$1" + local title_name="$2" + + local headers=() + local raw_files=() + local total_versions=${#VERSIONS[@]} + + # 1. 收集所有版本的数据(单行、排序、去重) + for i in "${!VERSIONS[@]}"; do + local version="${VERSIONS[i]%%|*}" + local config_path="${VERSIONS[i]##*|}" + local raw_file="$TMP_DIR/raw_$i" + + if [ ! -x "$config_path" ]; then + touch "$raw_file" + headers+=("$version (N/A)") + else + local raw_output + raw_output=$($config_path "$list_cmd" 2>/dev/null) + echo "$raw_output" | tr -s '[:space:]' '\n' | grep -v '^$' | sort -u > "$raw_file" + + local count + count=$(wc -l < "$raw_file" | tr -d ' ') + headers+=("$version ($count)") + fi + raw_files+=("$raw_file") + done + + # 2. 构建完美的全局统一基准列表 (Master List) + local master_list="$TMP_DIR/master_list" + local left_items="$TMP_DIR/left_items" + local deleted_items="$TMP_DIR/deleted_items" + + # 2.1 优先以最左边最新版 (raw_0) 的内容作为上半部分基准 + cat "${raw_files[0]}" > "$left_items" + + # 2.2 把其他所有老版本里有、但新版里没有的遗留/废弃项收集起来 + > "$deleted_items" + for (( i=1; i> "$deleted_items" + done + + # 2.3 对废弃项进行去重和排序,确保表格下半部分也整齐美观 + local cleaned_deleted="$TMP_DIR/cleaned_deleted" + sort -u "$deleted_items" | while IFS= read -r old_item; do + if ! grep -Fqx "$old_item" "$left_items"; then + echo "$old_item" + fi + done | sort > "$cleaned_deleted" + + # 2.4 合体:上半部分是新版有序列表,下半部分是旧版废弃有序列表 + cat "$left_items" "$cleaned_deleted" > "$master_list" + + # 3. 打印 Markdown 表格头部 + echo "## $title_name" + echo "" + + local header_line="|" + local divider_line="|" + for h in "${headers[@]}"; do + header_line="$header_line $h |" + divider_line="$divider_line --- |" + done + echo "$header_line" + echo "$divider_line" + + # 4. 遍历全局基准大列表,进行多版本精准查岗并输出 + while IFS= read -r feature_name || [[ -n "$feature_name" ]]; do + # 确保不处理空白行 + [ -z "$feature_name" ] && continue + + local row_output="|" + # 检查每个版本中是否存在这个特征 + for i in "${!VERSIONS[@]}"; do + if grep -Fqx "$feature_name" "${raw_files[$i]}"; then + row_output="$row_output $feature_name |" + else + row_output="$row_output |" + fi + done + echo "$row_output" + done < "$master_list" + + echo "" +} + +# ========================================== +# 各功能模块的独立路由函数 +# ========================================== +show_protocols() { process_feature "--list-protocols" "Protocols"; } +show_encoders() { process_feature "--list-encoders" "Encoders"; } +show_decoders() { process_feature "--list-decoders" "Decoders"; } +show_demuxers() { process_feature "--list-demuxers" "Demuxers"; } +show_muxers() { process_feature "--list-muxers" "Muxers"; } +show_filters() { process_feature "--list-filters" "Filters"; } +show_bsfs() { process_feature "--list-bsfs" "Bitstream Filters"; } +show_hwaccels() { process_feature "--list-hwaccels" "Hardware Accelerators"; } +show_indevs() { process_feature "--list-indevs" "Input Devices"; } +show_outdevs() { process_feature "--list-outdevs" "Output Devices"; } +show_parsers() { process_feature "--list-parsers" "Parsers"; } + +show_usage() { + echo "Usage: $0 [feature]" + echo "Available features:" + echo " protocols, encoders, decoders, demuxers, muxers, filters," + echo " bsfs, hwaccels, indevs, outdevs, parsers, all" +} + +# ========================================== +# Main 主控入口 +# ========================================== +main() { + local action="${1:-all}" + case "$action" in + protocols) show_protocols ;; + encoders) show_encoders ;; + decoders) show_decoders ;; + demuxers) show_demuxers ;; + muxers) show_muxers ;; + filters) show_filters ;; + bsfs) show_bsfs ;; + hwaccels) show_hwaccels ;; + indevs) show_indevs ;; + outdevs) show_outdevs ;; + parsers) show_parsers ;; + all) + echo "# FFmpeg Evolution Comparison" + show_protocols; show_encoders; show_decoders; show_demuxers + show_muxers; show_filters; show_bsfs; show_hwaccels + show_indevs; show_outdevs; show_parsers + ;; + -h|--help) show_usage; exit 0 ;; + *) echo "Error: Unknown feature '$action'" >&2; show_usage; exit 1 ;; + esac +} + +main "$@" \ No newline at end of file diff --git a/tools/parse-arguments.sh b/tools/parse-arguments.sh index a018a69a5..3394e8974 100644 --- a/tools/parse-arguments.sh +++ b/tools/parse-arguments.sh @@ -22,7 +22,7 @@ function main_usage() cat << EOF usage: ./main.sh [options] -compile ijkplayer using libs for iOS、macOS、tvOS、Android platform, such as ass、ffmpeg... +compile fsplayer using libs for iOS、macOS、tvOS、Android platform, such as ass、ffmpeg... Commands: +help Show help banner of specified command @@ -42,11 +42,12 @@ Clone vendor library git repository,Checkout specify commit,Apply patches OPTIONS: -p Specify platform (ios,macos,tvos,android), can't be nil -a Specify archs (x86_64,arm64,x86_64_simulator,arm64_simulator,all) all="x86_64,arm64,x86_64_simulator,arm64_simulator" - -l Specify which libs need init (all|libyuv|openssl|opus|bluray|dav1d|dvdread|freetype|fribidi|harfbuzz|unibreak|ass|ffmpeg), can't be nil + -l Specify which libs need init (libyuv|openssl|openssl3|opus|bluray|dav1d|dvdread|freetype|fribidi|harfbuzz|unibreak|ass|ijkffmpeg|fftutorial|ffmpeg4|ffmpeg5|ffmpeg6|ffmpeg7), can't be nil -s Specify workspace dir --help Show help banner of init command --skip-pull-base Skip pull base repo - --skip-patches Skip apply FFmpeg patches + --smart-apply Apply patches with git apply --reject instead of git am + -lib-config Read library config from specified path,eg: -lib-path ~/matt/lib/ffmpeg.sh EOF } @@ -60,12 +61,13 @@ Compile libs, such as ass、ffmpeg... OPTIONS: -c Specify sub command (build,clean,rebuild) rebuild=clean+build, default is build -a Specify archs (x86_64,arm64,x86_64_simulator,arm64_simulator,all) all="x86_64,arm64,x86_64_simulator,arm64_simulator" - -l Specify which libs need 'cmd' (all|openssl|opus|bluray|dav1d|dvdread|freetype|fribidi|harfbuzz|unibreak|ass|ffmpeg), can't be nil + -l Specify which libs need 'cmd' (openssl|opus|bluray|dav1d|dvdread|freetype|fribidi|harfbuzz|unibreak|ass|ffmpeg), can't be nil -s Specify workspace dir -j Force number of cores to be used + -lib-config Read library config from specified path,eg: -lib-path ~/matt/lib/ffmpeg.sh --help Show help banner of compile command --debug Enable debug mode (disable by default) - --skip-fmwk Skip make xcframework(apple platform only) + --fmwk Make xcframework(apple platform only) EOF } @@ -78,40 +80,15 @@ Download and Install Pre-compile library to product dir OPTIONS: -p Specify platform (ios,macos,tvos), can't be nil - -l Specify which libs need 'cmd' (all|libyuv|openssl|opus|bluray|dav1d|dvdread|freetype|fribidi|harfbuzz|unibreak|ass|ffmpeg), can't be nil + -l Specify which libs need 'cmd' (libyuv|openssl|opus|bluray|dav1d|dvdread|freetype|fribidi|harfbuzz|unibreak|ass|ffmpeg), can't be nil -s Specify workspace dir - -correct-pc Specify a path for correct the pc file prefix recursion --help Show intall help --fmwk Install xcframework bundle instead of .a + -lib-config Read library config from specified path,eg: -lib-path ~/matt/lib/ffmpeg.sh + -correct-pc Specify a path for correct the pc file prefix recursion EOF } -function correct_pc_file(){ - local fix_path="$1" - local dir=${PWD} - - echo "fix pc files in folder: $fix_path" - cd "$fix_path" - - for pc in `find . -type f -name "*.pc"` ; - do - local pkgconfig=$(cd $(dirname "$pc"); pwd) - local lib_dir=$(cd $(dirname "$pkgconfig"); pwd) - local base_dir=$(cd $(dirname "$lib_dir"); pwd) - local include_dir="${base_dir}/include" - local bin_dir="${base_dir}/bin" - - sed -i "" "s|^prefix=.*|prefix=$base_dir|" "$pc" - sed -i "" "s|^exec_prefix=[^$].*|exec_prefix=$bin_dir|" $pc - sed -i "" "s|^libdir=[^$].*|libdir=$lib_dir|" "$pc" - sed -i "" "s|^includedir=[^$].*include|includedir=$include_dir|" "$pc" - sed -i "" "s|-L/[^ ]*lib|-L$lib_dir|" "$pc" - sed -i "" "s|-I/[^ ]*include|-I$include_dir|" "$pc" - done - - cd "$dir" -} - function parse_path() { local p="$1" @@ -135,7 +112,28 @@ function env_assert() fi } +function echo_env() +{ + name="$1" + value=$(eval echo "\$$name") + if [[ -n "$value" ]]; then + echo "$name : [${value}]" >&2 + fi +} + +function make_absolute_path() +{ + local p="$1" + if [[ $p == /* ]]; then + echo "$(cd "$(dirname "$p")" && pwd)/$(basename "$p")" + else + echo "$(cd "$(dirname "$MR_SHELL_ROOT_DIR/$p")" && pwd)/$(basename "$p")" + fi +} + export -f env_assert +export -f echo_env +export -f make_absolute_path function help() { @@ -149,6 +147,8 @@ arch= libs= workspace= debug= +has_lib_config= +MR_UNKNOWN_OPTIONS=() case $1 in init | install) @@ -166,7 +166,6 @@ case $1 in ;; esac -export -f correct_pc_file export MR_ACTION=$action while [[ $# -gt 0 ]]; do @@ -202,28 +201,26 @@ while [[ $# -gt 0 ]]; do --debug) export MR_DEBUG='debug' ;; - --skip-pull-base) - export SKIP_PULL_BASE=1 + --fmwk) + export MR_MAKE_XCFRAMEWORK=1 ;; - --skip-ff-patches) - export SKIP_FFMPEG_PATHCHES=1 - ;; - --skip-fmwk) - export MR_SKIP_MAKE_XCFRAMEWORK=1 + -lib-config) + MR_UNKNOWN_OPTIONS+=("$1") + has_lib_config=1 ;; -correct-pc) - shift - correct_pc_file "$1" - exit 0 + MR_UNKNOWN_OPTIONS+=("$1") + has_correct_pc=1 ;; - **) - echo "unkonwn option:$1" + *) + MR_UNKNOWN_OPTIONS+=("$1") ;; esac shift done if [[ -z "$platform" ]];then + echo "platform can't empty" help exit 1 fi @@ -233,7 +230,7 @@ if [[ "$platform" != 'ios' && "$platform" != 'macos' && "$platform" != 'tvos' && exit 1 fi -if [[ -z "$libs" ]];then +if [[ -z "$libs" && "$has_lib_config" != "1" && "$has_correct_pc" != "1" ]];then echo "libs can't be nil, use -l specify libs" exit 1 fi @@ -293,13 +290,6 @@ else done fi -if [[ "$MR_VENDOR_LIBS" == "all" ]]; then - cfg_dir=$(DIRNAME=$(dirname "${BASH_SOURCE[0]}"); cd "${DIRNAME}/../configs"; pwd) - source "${cfg_dir}/default.sh" - - eval libs='$'"${MR_PLAT}_default_libs" - export MR_VENDOR_LIBS="$libs" -fi echo "MR_ACTION : [$MR_ACTION]" echo "MR_PLAT : [$MR_PLAT]" @@ -309,8 +299,7 @@ echo "MR_ACTIVE_ARCHS : [$MR_ACTIVE_ARCHS]" echo "MR_HOST_NPROC : [$MR_HOST_NPROC]" echo "MR_DEBUG : [$MR_DEBUG]" echo "MR_INIT_CFLAGS : [$MR_INIT_CFLAGS]" -echo "SKIP_PULL_BASE : [$SKIP_PULL_BASE]" -echo "SKIP_FFMPEG_PATHCHES : [$SKIP_FFMPEG_PATHCHES]" -echo "MR_SKIP_MAKE_XCFRAMEWORK" : [$MR_SKIP_MAKE_XCFRAMEWORK] +echo "MR_MAKE_XCFRAMEWORK" : [$MR_MAKE_XCFRAMEWORK] +[[ ${#MR_UNKNOWN_OPTIONS[@]} -gt 0 ]] && echo "MR_UNKNOWN_OPTIONS : [${MR_UNKNOWN_OPTIONS[*]}]" unset platform cmd arch libs workspace debug action cflags diff --git a/tools/prepare-build-workspace.sh b/tools/prepare-build-workspace.sh index ae1c757d3..3ff84077c 100644 --- a/tools/prepare-build-workspace.sh +++ b/tools/prepare-build-workspace.sh @@ -39,13 +39,11 @@ export MR_XCFRMK_DIR="${MR_WORKSPACE}/product/xcframework" export MR_IOS_PRODUCT_ROOT="${MR_WORKSPACE}/product/ios" export MR_MACOS_PRODUCT_ROOT="${MR_WORKSPACE}/product/macos" export MR_TVOS_PRODUCT_ROOT="${MR_WORKSPACE}/product/tvos" -export MR_PRE_ROOT="${MR_WORKSPACE}/pre" export MR_UNI_PROD_DIR="${MR_PRODUCT_ROOT}/universal" export MR_UNI_SIM_PROD_DIR="${MR_PRODUCT_ROOT}/universal-simulator" echo "MR_SRC_ROOT : [$MR_SRC_ROOT]" -echo "MR_PRE_ROOT : [$MR_PRE_ROOT]" echo "MR_PRODUCT_ROOT: [$MR_PRODUCT_ROOT]" echo "MR_UNI_PROD_DIR: [$MR_UNI_PROD_DIR]" echo "MR_UNI_SIM_PROD_DIR: [$MR_UNI_SIM_PROD_DIR]" diff --git a/tools/publish-feature-doc.sh b/tools/publish-feature-doc.sh new file mode 100755 index 000000000..99ab47c97 --- /dev/null +++ b/tools/publish-feature-doc.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# 确保脚本遇到错误时立即停止 +set -e + +echo "=== 1. 初始化并编译/配置各个 FFmpeg 版本 ===" +./main.sh init -p macos -l 'ffmpeg4 ffmpeg5 ffmpeg6 ffmpeg7 ffmpeg8' -a arm64 + +echo "=== 2. 创建发布目录 ===" +OUTPUT_DIR="docs" +mkdir -p "$OUTPUT_DIR" + +echo "=== 3. 解决自定义域名与路由冲突 (关键修复) ===" +# 1. 彻底禁用 Jekyll 编译,防止它乱动 permalink 路由 +touch "$OUTPUT_DIR/.nojekyll" + +# 2. 【核心】如果你的当前仓库在 Settings 绑定了自定义域名,请把下面这行的注释解开,并换成你的域名: +# echo "debugly.github.io" > "$OUTPUT_DIR/CNAME" + + +echo "=== 4. 生成 index.md (去掉了干扰路由的 Front Matter) ===" +# 回归最纯粹的 Markdown 头部,靠 GitHub 原生渲染,不再加任何 permalink +cat << 'EOF' > "$OUTPUT_DIR/index.md" +# FFmpeg Feature Evolution Matrix (macOS arm64) + +> [!NOTE] +> This page is automatically generated. It compares the availability of protocols, codecs, filters, and other features across different FFmpeg versions. +> **Columns from left to right:** Newest (8.1.2) to Oldest (4.0.5). Blanks on the right indicate newly added features in that version. + +EOF + +# 追加原有的矩阵表格生成脚本的输出 +./tools/list-all-feature.sh >> "$OUTPUT_DIR/index.md" + +echo "🎉 针对自定义域名优化成功!内容已生成至 ./$OUTPUT_DIR/index.md" \ No newline at end of file diff --git a/tools/publish-feature-html.sh b/tools/publish-feature-html.sh new file mode 100644 index 000000000..79ebce2c2 --- /dev/null +++ b/tools/publish-feature-html.sh @@ -0,0 +1,76 @@ +#!/bin/bash +set -e + +echo "=== 1. 初始化并配置各个 FFmpeg 版本 ===" +./main.sh init -p macos -l 'ffmpeg4 ffmpeg5 ffmpeg6 ffmpeg7 ffmpeg8' -a arm64 + +echo "=== 2. 创建发布目录 ===" +OUTPUT_DIR="docs" +mkdir -p "$OUTPUT_DIR" +touch "$OUTPUT_DIR/.nojekyll" + +echo "=== 3. 生成临时 Markdown 矩阵数据 ===" +TMP_MD_DATA=$(mktemp /tmp/ffmpeg_md.XXXXXX) + +cat << 'EOF' > "$TMP_MD_DATA" +# FFmpeg Feature Evolution Matrix (macOS arm64) + +> [!NOTE] +> This page is automatically generated. It compares the availability of protocols, codecs, filters, and other features across different FFmpeg versions. +> **Columns from left to right:** Newest (8.1.2) to Oldest (4.0.5). Blanks on the right indicate newly added features in that version. +EOF + +# 追加原本的对齐矩阵 +./tools/list-all-feature.sh >> "$TMP_MD_DATA" + +echo "=== 4. 完美融入 GitHub-2025.css 生成原生 index.html ===" +MD_CONTENT=$(cat "$TMP_MD_DATA") + +# 直接利用本地的 GitHub-2025.css 拼接出最终网页 +cat << EOF > "$OUTPUT_DIR/index.html" + + + + + FFmpeg Evolution Matrix + + + + + +
+
+ + + + + + +EOF + +rm -f "$TMP_MD_DATA" +echo "🎉 融合 GitHub-2025.css 成功!完美的原生体验网页已生成至 ./$OUTPUT_DIR/index.html" \ No newline at end of file