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)]
+
+
**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 供大家使用。
-
-如果您想要为开源社区贡献一份力量,请买杯咖啡给我提提神儿。
-
-
-
-感谢以下朋友对 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 构建脚本
+
+
+
+
+**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