ffmpeg: upgrade to 7.1.1 (binaries + Stride.Video 7.x API migration)

This commit is contained in:
Virgile Bello
2026-05-24 14:40:31 +09:00
parent c9584bb97e
commit c62e56fa8b
65 changed files with 300 additions and 462 deletions
+5
View File
@@ -23,9 +23,14 @@
*.exp filter=lfs diff=lfs merge=lfs -text
*.a filter=lfs diff=lfs merge=lfs -text
*.so filter=lfs diff=lfs merge=lfs -text
*.so.* filter=lfs diff=lfs merge=lfs -text
*.dylib filter=lfs diff=lfs merge=lfs -text
*.pdb filter=lfs diff=lfs merge=lfs -text
# FFmpeg per-platform CLI binaries (no extension)
deps/FFmpeg/ffmpeg.linux-x64 filter=lfs diff=lfs merge=lfs -text
deps/FFmpeg/ffmpeg.osx-arm64 filter=lfs diff=lfs merge=lfs -text
# setup
*.msi filter=lfs diff=lfs merge=lfs -text
*.cab filter=lfs diff=lfs merge=lfs -text
+1 -1
View File
@@ -57,7 +57,7 @@ jobs:
- name: Install runtime dependencies
run: |
sudo apt-get update
sudo apt-get install -y libopenal-dev libvulkan1 xvfb lld vulkan-validationlayers
sudo apt-get install -y libopenal-dev libvulkan1 xvfb lld vulkan-validationlayers libva2 libva-drm2 libva-x11-2
- name: Start virtual display
run: |
# Lavapipe's x11 WSI needs a display server to expose VK_KHR_swapchain,
-8
View File
@@ -1,8 +0,0 @@
CALL ..\find_git.cmd
IF NOT ERRORLEVEL 0 (
ECHO "Could not find git.exe"
EXIT /B %ERRORLEVEL%
)
%GIT_CMD% clone https://git.ffmpeg.org/ffmpeg.git ../../externals/ffmpeg
rem checkout n3.3.3 tag
IF NOT ERRORLEVEL 0 PAUSE
-242
View File
@@ -1,242 +0,0 @@
#!/bin/sh
# References/help
# https://trac.ffmpeg.org/wiki/CompilationGuide/MSVC
# https://pracucci.com/compile-ffmpeg-on-windows-with-visual-studio-compiler.html
# https://stackoverflow.com/questions/41358478/is-it-possible-to-build-ffmpeg-x64-on-windows
# Pre-requisites:
# Install MSYS2 and YASM as indicated in https://pracucci.com/compile-ffmpeg-on-windows-with-visual-studio-compiler.html
# Prior to running one of the command the following steps must be performed:
# 1. Start a Visual Studio prompt (should be located under C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\VC\Auxiliary\Build\)
# a. for X86 start vcvars32.bat
# b. for x64 start vcvars64.bat
# 2. Then run MSYS2 command prompt (C:\workspace\windows\msys64\msys2_shell.cmd)
# 3. Set the current working directory to where ffmpeg sources are installed (e.g. /c/Projects/stride/externals/ffmpeg)
# cmds
# C:\Windows\system32\cmd.exe
# "C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\VC\Auxiliary\Build\vcvars64.bat"
# C:\workspace\msys64\msys2_shell.cmd -use-full-path
# cd C:\\Dev\\stride\\externals\\ffmpeg
# C:\\Dev\\stride\\deps\\FFmpeg\\Windows\\build.sh
clean() {
# We could also do a distclean to also remove previously built binaries
make clean
}
configure_windows_x64_shared() {
"${BASEDIR}/configure" \
--toolchain=msvc \
--arch=amd64 \
--enable-asm \
--enable-shared \
--enable-version3 \
--enable-w32threads \
--enable-yasm \
--disable-debug \
--disable-doc \
--disable-programs \
--disable-static \
--target-os=win64 \
--prefix="${BASEDIR}/build/windows/x64"
}
configure_windows_arm64_shared() {
"${BASEDIR}/configure" \
--toolchain=msvc \
--arch=arm64 \
--enable-asm \
--enable-shared \
--enable-version3 \
--enable-w32threads \
--enable-yasm \
--disable-debug \
--disable-doc \
--disable-programs \
--disable-static \
--disable-x86asm \
--target-os=win64 \
--prefix="${BASEDIR}/build/windows/arm64"
}
configure_windows_x86_shared() {
"${BASEDIR}/configure" \
--toolchain=msvc \
--arch=x86 \
--enable-asm \
--enable-shared \
--enable-version3 \
--enable-w32threads \
--enable-x86asm \
--disable-debug \
--disable-doc \
--disable-programs \
--disable-static \
--target-os=win32 \
--prefix="${BASEDIR}/build/windows/x86"
}
configure_windows_x86_exe() {
"${BASEDIR}/configure" \
--toolchain=msvc \
--arch=x86 \
--enable-asm \
--enable-static \
--enable-version3 \
--enable-w32threads \
--enable-yasm \
--disable-debug \
--disable-doc \
--disable-ffplay \
--disable-ffprobe \
--disable-ffserver \
--disable-shared \
--target-os=win32 \
--prefix="${BASEDIR}/build/windows/x86"
}
configure_windows_x64_exe() {
"${BASEDIR}/configure" \
--toolchain=msvc \
--arch=amd64 \
--enable-asm \
--enable-static \
--enable-version3 \
--enable-w32threads \
--enable-yasm \
--disable-debug \
--disable-doc \
--disable-ffplay \
--disable-ffprobe \
--disable-ffserver \
--disable-shared \
--target-os=win64 \
--prefix="${BASEDIR}/build/windows/x64"
}
configure_windows_arm64_exe() {
"${BASEDIR}/configure" \
--toolchain=msvc \
--arch=arm64 \
--enable-asm \
--enable-static \
--enable-version3 \
--enable-w32threads \
--enable-yasm \
--disable-debug \
--disable-doc \
--disable-ffplay \
--disable-ffprobe \
--disable-ffserver \
--disable-shared \
--disable-x86asm \
--target-os=win64 \
--prefix="${BASEDIR}/build/windows/arm64"
}
install() {
make -j${NUMBER_OF_CORES} && make install
}
build_windows_x64_shared() {
oot="${BASEDIR}/build/windows/x64/oot/ffmpeg"
mkdir -p "$oot"
pushd "$oot"
# Configure ffmpeg (can be skipped if already done)
configure_windows_x64_shared
# Make sure any previous build doesn't pollute the current one (can be skipped if configure didn't change since last time)
clean
# Build FFmpeg and copy binaries+include files into the installation folder (defined by --prefix in the configure command)
install
popd
}
build_windows_arm64_shared() {
oot="${BASEDIR}/build/windows/arm64/oot/ffmpeg"
mkdir -p "$oot"
pushd "$oot"
# Configure ffmpeg (can be skipped if already done)
configure_windows_arm64_shared
# Make sure any previous build doesn't pollute the current one (can be skipped if configure didn't change since last time)
clean
# Build FFmpeg and copy binaries+include files into the installation folder (defined by --prefix in the configure command)
install
popd
}
build_windows_x86_shared() {
oot="${BASEDIR}/build/windows/x86/oot/ffmpeg"
mkdir -p "$oot"
pushd "$oot"
# Configure ffmpeg (can be skipped if already done)
configure_windows_x86_shared
# Make sure any previous build doesn't pollute the current one (can be skipped if configure didn't change since last time)
clean
# Build FFmpeg and copy binaries+include files into the installation folder (defined by --prefix in the configure command)
install
popd
}
build_windows_x64_exe() {
oot="${BASEDIR}/build/windows/x64/oot/ffmpeg"
mkdir -p "$oot"
pushd "$oot"
# Configure ffmpeg (can be skipped if already done)
configure_windows_x64_exe
# Make sure any previous build doesn't pollute the current one (can be skipped if configure didn't change since last time)
clean
# Build FFmpeg and copy binaries+include files into the installation folder (defined by --prefix in the configure command)
install
popd
}
build_windows_arm64_exe() {
oot="${BASEDIR}/build/windows/arm64/oot/ffmpeg"
mkdir -p "$oot"
pushd "$oot"
# Configure ffmpeg (can be skipped if already done)
configure_windows_arm64_exe
# Make sure any previous build doesn't pollute the current one (can be skipped if configure didn't change since last time)
clean
# Build FFmpeg and copy binaries+include files into the installation folder (defined by --prefix in the configure command)
install
popd
}
build_windows_x86_exe() {
oot="${BASEDIR}/build/windows/x86/oot/ffmpeg"
mkdir -p "$oot"
pushd "$oot"
# Configure ffmpeg (can be skipped if already done)
configure_windows_x86_exe
# Make sure any previous build doesn't pollute the current one (can be skipped if configure didn't change since last time)
clean
# Build FFmpeg and copy binaries+include files into the installation folder (defined by --prefix in the configure command)
install
popd
}
BASEDIR=$(pwd)
NUMBER_OF_CORES=$(nproc)
# Uncomment a line to run that configuration
#build_windows_x64_shared
build_windows_x86_shared
#build_windows_x64_exe
#build_windows_x86_exe
#build_windows_arm64_exe
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4ef9225d5cf5bc9d704846a28c85c17990b5064aaee950571770c5ae501d9f68
size 4241360
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d693b7c3c0ba18c3ce57ea51d8ab485450a69dea3d5253059bd5f5e5fb1720a0
size 14592
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8df7b76ea5ae5cb5a7dc81a9efb7f2f7c5ba8cfc2258180513d9cd97e0e051e8
size 154968
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ca8f1acd1b303c20bc82bdbb3fa31ec7f341d0b103e3ec7b8a3e8c0d2a4e0769
size 758600
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:30d6348a55ade3b06a70b277584dbbbd50cd978e5a93f57a17f1edd2016815dc
size 732232
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1bfbbe4f997e3ee34ef2536413944ee04fe3196b763a499f87d7417aa73c2b25
size 108976
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2d9f12b01668d4af8903147bdfe5b7d4b81030a5bae0fedeae8311458f0a9c2c
size 535008
-62
View File
@@ -1,62 +0,0 @@
== x64 DLL version ==
This is a FFmpeg win64 shared build.
FFMpeg version: 3.3.3 (9079c70d2095643af6954001d0627445650b85a6)
This FFmpeg build was configured with:
--toolchain=msvc
--arch=x86_64
--enable-version3
--enable-yasm
--enable-asm
--enable-shared
--enable-w32threads
--enable-avresample
--disable-ffserver
--disable-programs
--disable-doc
--disable-static
--target-os=win64
== x86 DLL version ==
This is a FFmpeg x86 generic shared build.
FFMpeg version: 3.3.3 (9079c70d2095643af6954001d0627445650b85a6)
This FFmpeg build was configured with:
--toolchain=msvc
--arch=x86
--enable-version3
--enable-yasm
--enable-asm
--enable-shared
--enable-w32threads
--enable-avresample
--disable-ffserver
--disable-programs
--disable-doc
--disable-static
// Instructions for building FFmpeg:
# ref: https://pracucci.com/compile-ffmpeg-on-windows-with-visual-studio-compiler.html
1. checkout ffmpeg
2. download MSYS (​http://msys2.github.io)
3. install it C:\workspace\windows\msys64
4. run MSYS command prompt (C:\workspace\windows\msys64\msys2_shell.cmd)
5. install package with "pacman -Su make gcc diffutils autoconf automake gperf yasm python"
6. go to C:\workspace\windows\msys64
7. edit msys2_shell.cmd file
8. uncomment the "set MSYS2_PATH_TYPE=inherit" line so that environment variables are available
9. download yasm http://www.tortall.net/projects/yasm/releases/yasm-1.3.0-win64.exe
10. rename it to yasm.exe and move it to C:\workspace\windows
11. Add C:\workspace\windows to PATH environment variable
12. in C:\ProgramData\Microsoft\AndroidNDK64\android-ndk-r13b\build\tools\make_standalone_toolchain.py
* in get_host_tag_or_die method add support for MSYS
elif (platform.system() == 'MSYS_NT-10.0' or
platform.system() == 'MINGW64_NT-10.0'):
host_tag = 'windows-x86_64'
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6766f50729325f4c20e880b56ff8f869b474bd900fe35471a64d8fc8cc08c935
size 2661400
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d5280eebd4d37ffa2600d614dd8bfef70422db05194ffd5f97fb5f34d1593d06
size 35688
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:202cc65322b0ccc64df742d647662cae4f5c0e9139d67a2a12a123fa9b31ecfe
size 136232
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b880d7608976a1676fc763ebf4290873874902074ff7cadd30789f14ce1d8b28
size 709688
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:acdcd7a72be171b862587aa1053e5c4ab2fdf1c3b7b12154f5cede912caafa50
size 542904
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1b26e573f915e211046672419909a6796ebdb69baeae0b05e54732ef9cdd005c
size 87000
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e09921db6251805ae0bb51a39a679cde29e5859ddf0d0335f5921b7e5b9f3ec7
size 369544
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7f69cd8022a13a8be34f01506e10b9bbf92f0bb04d6eb71a662267b47c54351d
size 9722880
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3f11af5322848d625649c7d176870b441d70b2468bb9f1e50877636007876ad2
size 2697216
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9e8c57dcd4b5e3cb81b035597424c800fb44f52607af295ac487728888f6c040
size 204800
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:492f103bd1e3665de8c39a8d9317aec7f23f7af553d6a5de814c8419299416be
size 15360
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:092b69c5fc586c8493428a902556702474ac3e1b909adc129eb5597f830e9df8
size 142848
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a7f4842a8224747bbaff9a5e0d5b2e191f4c7d2085099cd6d5b56ace545ad7ea
size 1856512
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3725c5ed89c43e6cf1060a413773644eaf78a5c2efacf7e8f9413f994c62e33d
size 2210816
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3a9ee4dc5cfa80d225b759e2dcc6bdc84d2e8d4724e6e585a82389989d0cb15e
size 707072
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b434850920c56ed34c9047f0f904d7dc8673ba580de40bc601f7df28b88b1255
size 623616
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:bf50fb717053718eb0e17c7251d1a1c9c1b2526fdd2a3bd61741265a103df3c5
size 530432
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fcf2ea93ba2a06818955cf2cd7880c5771f147f885a10b64aa72410e98b58525
size 186880
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9ceb23d7ad38f9d1adf4bb7bb65898fbdf0eb8fd989ccc24bf2f8a3225d77f12
size 70144
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:eefb4dc92d198b7bd7013f3a68afc3f1a3b131327db7bbdd2148ca59305a13e1
size 452608
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f22ba431a7da148a1f24a5da3e257613b915f105e111541dc79e4b898820e162
size 338944
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a9665ac0382166204b3fe4a05e5c7a2cd6edcb4d2656aa54adf2d2d0753e33ba
size 10908672
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:222c8d3099acd8eacf47b40338c3d8fd99517e48ffe68f942fb7473559ec9e8f
size 4144128
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e9c74436ef4dce4fe8970fbba514c67e99b0e7ebb8a868e2f375e73c7736ec2f
size 185856
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:95b25bbea098bae01ac10cb2fc39dc971865ab12d57ae973c4b99fa4221cf500
size 16896
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:846579b73ae443969de04bda0f73c970c0928c25c8264065cd0c913a4295ac1c
size 156160
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a98f4954516e648c287da18f4a7cffdcd85b5ffc314dbc9af1413386efc78a1b
size 2219008
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e27749d84a2410cf2f42937a9afbe8aefb908dd53fad4ef8a18f4883d53c5226
size 2144768
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1cf332068580bc6b5f2be437194be51f974264bfc63557ea3231424f8d592bae
size 742912
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8fee906cce37070992e8a8915933b0a465aa92c1c76dc6da0c21d0fcb4b76a3b
size 620544
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ed9b00c0858ef13668842646fb4ead9e881053fea0636a5bda3f537f6fe09520
size 765440
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fad3e26b39029ed019798f59b814679aea5e0b1b1e84f9d3b30b7a00f8066029
size 209408
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3a6bfe69a3b2ac02db76c53334be439fac971a5b9e9e3be164c255d45e5bcd3c
size 105984
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a3c1e512dc830041d156d1694e430a5cae8d2d4a152ccc938b115e36fd96c348
size 590336
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b7ad48d4bb3ace0a7dc8f1a15d3f421013ebc7bfd3e609e2f0a0bccf3b5eb39c
size 559616
+2 -2
View File
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7def5065a350b40cd20f9c2fd551b250948b886cb5f31cb4b9d9aa39566dd16b
size 48005120
oid sha256:dafa8e6b99289f3eede788389a273ecde382816635782cfcec0b6cc5d9c9fa30
size 8864768
Vendored Executable
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:dae8b48cbcc9777df31d3d553739dca0e0add1098c7144ced4d6ea6bc732e888
size 9311984
Vendored Executable
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:164eb5f2121bb6342b67d72fbe6338c14cbf536982aa5781f0297fa7a1a0386c
size 6157320
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3b06b6bb403051e857c5db40db09be5615530d3387d093d6ef3262fe4249d6b8
size 6062592
-1
View File
@@ -1 +0,0 @@
For win-arm64: Use https://github.com/azeno/FFmpeg/tree/win-arm64, it's based on release/3.4
+10 -1
View File
@@ -1 +1,10 @@
Extracted from ffmpeg-20160530-git-d74cc61-win64-static.zip
FFmpeg 7.1.1 (LGPL libs, GPL CLI with libx264 + platform HW encoders)
Built by .github/workflows/dep-ffmpeg.yml — re-run that workflow and copy
the artifacts to refresh.
Layout:
ffmpeg.exe Windows x64 CLI (libx264 + MF + NVENC)
ffmpeg.win-arm64.exe Windows arm64 CLI (libx264 + MF)
ffmpeg.linux-x64 Linux x64 CLI (libx264 + VA-API)
ffmpeg.osx-arm64 macOS arm64 CLI (libx264 + VideoToolbox)
dotnet/<rid>/ Shared libav* libraries shipped with games
+1 -1
View File
@@ -10,7 +10,7 @@
<PackageVersion Include="DotRecast.Detour" Version="2025.2.1" />
<PackageVersion Include="DotRecast.Recast" Version="2025.2.1" />
<PackageVersion Include="DotRecast.Recast.Toolset" Version="2025.2.1" />
<PackageVersion Include="FFmpeg.AutoGen" Version="3.4.0.2" />
<PackageVersion Include="FFmpeg.AutoGen" Version="7.1.1" />
<PackageVersion Include="K4os.Compression.LZ4.Legacy" Version="1.3.6" />
<PackageVersion Include="Microsoft.Direct3D.WARP" Version="1.0.18" />
<PackageVersion Include="Microsoft.ML.OnnxRuntime" Version="1.20.1" />
@@ -34,7 +34,7 @@ namespace Stride.Assets.Media
public DecodeSoundFileCommand(string url, SoundAsset parameters, IAssetFinder assetFinder)
: base(url, parameters, assetFinder)
{
Version = 3;
Version = 4;
}
/// <inheritdoc />
@@ -66,7 +66,7 @@ namespace Stride.Assets.Media
public EncodeVideoFileCommand(string url, VideoConvertParameters description, IAssetFinder assetFinder, AVCodecID[] listSupportedCodecNames)
: base(url, description, assetFinder)
{
Version = 4;
Version = 5;
ListSupportedCodecNames = listSupportedCodecNames;
}
@@ -69,7 +69,22 @@
<StrideNativeLib Include="..\..\..\deps\msdfgen\Release\**\*.dll;..\..\..\deps\msdfgen\Release\**\*.so;..\..\..\deps\msdfgen\Release\**\*.dylib">
<Link>runtimes\%(RecursiveDir)native\%(Filename)%(Extension)</Link>
</StrideNativeLib>
<!-- Asset compiler ffmpeg CLI — ship every RID under runtimes/<rid>/native/ so the package
is portable across hosts. ToolLocator resolves the right one at runtime via the current RID. -->
<Content Include="..\..\..\deps\FFmpeg\ffmpeg.exe">
<Link>runtimes\win-x64\native\ffmpeg.exe</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\..\..\deps\FFmpeg\ffmpeg.win-arm64.exe">
<Link>runtimes\win-arm64\native\ffmpeg.exe</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\..\..\deps\FFmpeg\ffmpeg.linux-x64">
<Link>runtimes\linux-x64\native\ffmpeg</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\..\..\deps\FFmpeg\ffmpeg.osx-arm64">
<Link>runtimes\osx-arm64\native\ffmpeg</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
+31 -4
View File
@@ -1,10 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using Stride.Core.IO;
namespace Stride.Assets;
@@ -32,6 +29,18 @@ static class ToolLocator
}
var asmDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
// RID-keyed bundled binary: runtimes/<rid>/native/<tool>{.exe}. This is where
// Stride.Assets.csproj copies the per-platform ffmpeg CLIs.
var rid = GetCurrentRid();
if (rid != null)
{
var ridTool = UPath.Combine(asmDir, new UFile($"runtimes/{rid}/native/{toolName}{exeExt}"));
if (File.Exists(ridTool))
return ridTool;
}
// Legacy locations kept for backward compatibility with older deps/ layouts.
var tool = UPath.Combine(asmDir, new UFile($"{toolName}{exeExt}"));
if (File.Exists(tool))
return tool;
@@ -42,4 +51,22 @@ static class ToolLocator
return null;
}
private static string GetCurrentRid()
{
string os;
if (OperatingSystem.IsWindows()) os = "win";
else if (OperatingSystem.IsLinux()) os = "linux";
else if (OperatingSystem.IsMacOS()) os = "osx";
else return null;
var arch = RuntimeInformation.OSArchitecture switch
{
Architecture.X64 => "x64",
Architecture.Arm64 => "arm64",
Architecture.X86 => "x86",
_ => null,
};
return arch == null ? null : $"{os}-{arch}";
}
}
@@ -15,9 +15,10 @@ namespace Stride.Video.FFmpeg
public AudioStream([NotNull] AVStream* pStream, [NotNull] FFmpegMedia media)
: base(pStream, media)
{
var pCodec = pStream->codec;
ChannelCount = pCodec->channels;
SampleRate = pCodec->sample_rate;
var pCodecpar = pStream->codecpar;
// FFmpeg 5.1+: channels/channel_layout replaced by AVChannelLayout ch_layout.
ChannelCount = pCodecpar->ch_layout.nb_channels;
SampleRate = pCodecpar->sample_rate;
}
/// <summary>
@@ -67,9 +67,9 @@ namespace Stride.Video.FFmpeg
/// <summary>
/// Initializes a new instance of the <see cref="FFmpegCodec"/> class.
/// </summary>
public FFmpegCodec(GraphicsDevice graphcsDevice, AVCodecContext* originalContext)
public FFmpegCodec(GraphicsDevice graphcsDevice, AVCodecParameters* originalCodecpar)
{
var codecId = originalContext->codec_id;
var codecId = originalCodecpar->codec_id;
var pCodec = ffmpeg.avcodec_find_decoder(codecId);
if (pCodec == null)
// TODO: log?
@@ -77,13 +77,17 @@ namespace Stride.Video.FFmpeg
int ret;
var pCodecContext = ffmpeg.avcodec_alloc_context3(pCodec);
var pCodecParam = ffmpeg.avcodec_parameters_alloc();
ret = ffmpeg.avcodec_parameters_from_context(pCodecParam, originalContext);
if (ret < 0)
// TODO: log?
throw new ApplicationException($"Could not retrieve codec parameters. Error code={ret.ToString("X8")}");
// Set the context get_format function
ret = ffmpeg.avcodec_parameters_to_context(pCodecContext, originalCodecpar);
if (ret < 0)
// TODO: log?
throw new ApplicationException($"Could not fill codec parameters. Error code={ret.ToString("X8")}");
#if STRIDE_GRAPHICS_API_DIRECT3D11
// HW decode is wired up for D3D11VA only. On other backends, fall through to FFmpeg's
// software path (default get_format + no hw_device_ctx) — codec picks a SW pix_fmt
// and sws_scale converts to the Target texture's RGBA later.
getFormat = (context, formats) =>
{
AVPixelFormat* pixelFormat;
@@ -98,11 +102,6 @@ namespace Stride.Video.FFmpeg
};
pCodecContext->get_format = getFormat;
ret = ffmpeg.avcodec_parameters_to_context(pCodecContext, pCodecParam);
if (ret < 0)
// TODO: log?
throw new ApplicationException($"Could not fill codec parameters. Error code={ret.ToString("X8")}");
// create the hardware device context.
AVBufferRef* pHWDeviceContextLocal;
if (ffmpeg.av_hwdevice_ctx_create(&pHWDeviceContextLocal, HardwareDeviceType, null, null, 0) >= 0)
@@ -111,13 +110,13 @@ namespace Stride.Video.FFmpeg
pHWDeviceContext = pHWDeviceContextLocal;
pCodecContext->hw_device_ctx = ffmpeg.av_buffer_ref(pHWDeviceContext);
}
#endif
// Setup hardware acceleration context
//if (IsHardwareAccelerated)
// CreateHarwareAccelerationContext(graphcsDevice, pCodecContext, pCodec);
if ((pCodec->capabilities & ffmpeg.AV_CODEC_CAP_TRUNCATED) == ffmpeg.AV_CODEC_CAP_TRUNCATED)
pCodecContext->flags |= ffmpeg.AV_CODEC_FLAG_TRUNCATED;
// AV_CODEC_CAP_TRUNCATED / AV_CODEC_FLAG_TRUNCATED removed in FFmpeg 5.0 — modern parsers handle framing.
if (ffmpeg.avcodec_is_open(pCodecContext) == 0)
{
@@ -126,7 +125,6 @@ namespace Stride.Video.FFmpeg
// TODO: log?
throw new ApplicationException($"Could not open codec. Error code={ret.ToString("X8")}");
}
ffmpeg.avcodec_parameters_free(&pCodecParam);
pAVCodecContext = pCodecContext;
}
@@ -297,7 +295,7 @@ namespace Stride.Video.FFmpeg
ffmpeg.av_buffer_unref(&pHWDeviceContextLocal);
var pAVCodecContextLocal = pAVCodecContext;
ffmpeg.avcodec_close(pAVCodecContextLocal);
// avcodec_close removed in FFmpeg 7.0 — avcodec_free_context handles closing.
ffmpeg.avcodec_free_context(&pAVCodecContextLocal);
}
@@ -172,34 +172,25 @@ namespace Stride.Video.FFmpeg
{
FFmpegUtils.EnsurePlatformSupport();
var skip_to_keyframe = stream.AVStream->skip_to_keyframe;
try
if (currentStreams.TryGetValue(stream, out var streamInfo))
{
if (currentStreams.TryGetValue(stream, out var streamInfo))
{
// flush the codec buffered images
streamInfo.Codec.Flush(pDecodedFrame);
}
// flush the format buffered images
ffmpeg.avformat_flush(AVFormatContext);
// perform the actual seek
stream.AVStream->skip_to_keyframe = 1;
var ret = ffmpeg.av_seek_frame(AVFormatContext, stream.Index, timestamp, ffmpeg.AVSEEK_FLAG_BACKWARD);
if (ret < 0)
{
Logger.Error($"Could not seek frame. Error code={ret.ToString("X8")}");
Logger.Error(GetErrorMessage(ret));
return false;
}
return true;
// flush the codec buffered images
streamInfo.Codec.Flush(pDecodedFrame);
}
finally
// flush the format buffered images
ffmpeg.avformat_flush(AVFormatContext);
// AVSEEK_FLAG_BACKWARD already lands on the nearest preceding keyframe.
var ret = ffmpeg.av_seek_frame(AVFormatContext, stream.Index, timestamp, ffmpeg.AVSEEK_FLAG_BACKWARD);
if (ret < 0)
{
stream.AVStream->skip_to_keyframe = skip_to_keyframe;
Logger.Error($"Could not seek frame. Error code={ret.ToString("X8")}");
Logger.Error(GetErrorMessage(ret));
return false;
}
return true;
}
[ItemNotNull, NotNull]
@@ -212,29 +203,34 @@ namespace Stride.Video.FFmpeg
// TODO: log?
throw new InvalidOperationException(@"Media isn't open.");
var codecContext = *stream.AVStream->codec;
var pCodecpar = stream.AVStream->codecpar;
var streamInfo = GetStreamInfo(stream);
var dstData = new byte_ptrArray4();
var dstLinesize = new int_array4();
ffmpeg.av_image_fill_arrays(ref dstData, ref dstLinesize, (byte*)streamInfo.Image.Buffer, DestinationPixelFormat, codecContext.width, codecContext.height, 1);
ffmpeg.av_image_fill_arrays(ref dstData, ref dstLinesize, (byte*)streamInfo.Image.Buffer, DestinationPixelFormat, pCodecpar->width, pCodecpar->height, 1);
streamInfo.Image.Linesize = dstLinesize[0];
var extractedFrameCount = 0;
var packet = new AVPacket();
var pPacket = &packet;
ffmpeg.av_init_packet(pPacket);
for (int i = 0; i < count; i++)
// FFmpeg 6.0+: av_init_packet removed; packets must be heap-allocated via av_packet_alloc.
var pPacket = ffmpeg.av_packet_alloc();
try
{
var extractionStatus = ExtractNextImage(streamInfo, pPacket, stream.AVStream, dstData, dstLinesize);
streamInfo.ReachedEnd = extractionStatus == FrameExtractionStatus.ReachEOF;
if (extractionStatus == FrameExtractionStatus.Succeeded)
++extractedFrameCount;
}
for (int i = 0; i < count; i++)
{
var extractionStatus = ExtractNextImage(streamInfo, pPacket, stream.AVStream, dstData, dstLinesize);
streamInfo.ReachedEnd = extractionStatus == FrameExtractionStatus.ReachEOF;
if (extractionStatus == FrameExtractionStatus.Succeeded)
++extractedFrameCount;
}
return extractedFrameCount;
return extractedFrameCount;
}
finally
{
ffmpeg.av_packet_free(&pPacket);
}
}
/// <summary>
@@ -252,9 +248,9 @@ namespace Stride.Video.FFmpeg
// Unfortunately the side data was not present in the stream
// -> we need to decode and look in the first packet and frame.
var streamInfo = GetStreamInfo(stream);
var packet = new AVPacket();
var pPacket = &packet;
ffmpeg.av_init_packet(pPacket);
// FFmpeg 6.0+: av_init_packet removed; av_packet_alloc returns a heap packet
// and av_packet_free in the finally below replaces av_packet_unref + struct dtor.
var pPacket = ffmpeg.av_packet_alloc();
var pCodecContext = streamInfo.Codec.pAVCodecContext;
try
@@ -313,7 +309,7 @@ namespace Stride.Video.FFmpeg
}
finally
{
ffmpeg.av_packet_unref(pPacket);
ffmpeg.av_packet_free(&pPacket);
ffmpeg.av_frame_unref(pDecodedFrame);
// return to the beginning of the media file (just in case)
@@ -325,16 +321,16 @@ namespace Stride.Video.FFmpeg
{
if (!currentStreams.TryGetValue(stream, out var streamInfo))
{
var codecContext = *(stream.AVStream->codec);
var width = codecContext.width;
var height = codecContext.height;
var pCodecpar = stream.AVStream->codecpar;
var width = pCodecpar->width;
var height = pCodecpar->height;
var convertedFrameBufferSize = ffmpeg.av_image_get_buffer_size(DestinationPixelFormat, width, height, 1);
convertedFrameBufferSize = (convertedFrameBufferSize + 3) & ~0x03; // align on a boundary of 4 (32-bits)
currentStreams[stream] = streamInfo = new StreamInfo
{
Codec = new FFmpegCodec(graphicsDevice, &codecContext),
Codec = new FFmpegCodec(graphicsDevice, pCodecpar),
Image = new VideoImage(width, height, convertedFrameBufferSize),
};
}
@@ -379,8 +375,13 @@ namespace Stride.Video.FFmpeg
}
ret = ffmpeg.avcodec_receive_frame(pCodecContext, pDecodedFrame);
//if (ret == ffmpeg.AVERROR(ffmpeg.EAGAIN)) // don't want to block the execution thread
// continue;
if (ret == ffmpeg.AVERROR(ffmpeg.EAGAIN))
{
// Decoder needs more packets before producing a frame — normal flow
// control with the modern send_packet / receive_frame API. Loop back
// and feed it the next packet.
continue;
}
if (ret < 0)
{
@@ -32,7 +32,7 @@ namespace Stride.Video.FFmpeg
{
if (pStream == null) throw new ArgumentNullException(nameof(pStream));
AVStream = pStream;
Codec = pStream->codec->codec_id;
Codec = pStream->codecpar->codec_id;
Media = media;
Index = pStream->index;
Metadata = FFmpegUtils.ToDictionary(pStream->metadata);
@@ -81,8 +81,7 @@ namespace Stride.Video.FFmpeg
if (media == null) throw new ArgumentNullException(nameof(media));
FFmpegStream result;
var pCodec = pStream->codec;
switch (pCodec->codec_type)
switch (pStream->codecpar->codec_type)
{
case AVMediaType.AVMEDIA_TYPE_AUDIO:
result = new AudioStream(pStream, media);
@@ -4,6 +4,8 @@
#if STRIDE_VIDEO_FFMPEG
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using FFmpeg.AutoGen;
@@ -22,7 +24,10 @@ namespace Stride.Video.FFmpeg
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool CheckPlatformSupport()
{
return (Platform.Type == PlatformType.Windows || Platform.Type == PlatformType.Android);
return Platform.Type == PlatformType.Windows
|| Platform.Type == PlatformType.Linux
|| Platform.Type == PlatformType.macOS
|| Platform.Type == PlatformType.Android;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -41,8 +46,7 @@ namespace Stride.Video.FFmpeg
return;
initialized = true;
ffmpeg.av_register_all();
ffmpeg.avcodec_register_all();
// av_register_all / avcodec_register_all removed in FFmpeg 4.0 / 5.0 — registration is automatic now.
ffmpeg.avformat_network_init();
}
@@ -73,20 +77,40 @@ namespace Stride.Video.FFmpeg
// |---- avformat
// |---- avcodec
// |---- avutil
// FFmpeg.AutoGen 7.x resolves libav* through its own LibraryLoader which probes
// ffmpeg.RootPath. Default ("") only searches the OS standard paths and never finds
// SONAME-versioned libavutil.so.59 / libavutil.59.dylib we ship.
// Two layouts to handle: tests flatten libs into the assembly dir, regular asm builds
// keep them in runtimes/<rid>/native/. Check both.
var asmDir = Path.GetDirectoryName(typeof(FFmpegUtils).Assembly.Location);
if (asmDir != null)
{
var rid = GetCurrentRid();
var runtimesDir = rid != null ? Path.Combine(asmDir, "runtimes", rid, "native") : null;
// RID builds flatten DLLs to asmDir; portable builds keep them under runtimes/<rid>/native/.
var probe = Platform.Type switch
{
PlatformType.Windows => "avutil-59.dll",
PlatformType.macOS => "libavutil.59.dylib",
_ => "libavutil.so.59",
};
ffmpeg.RootPath = runtimesDir != null && File.Exists(Path.Combine(runtimesDir, probe)) ? runtimesDir : asmDir;
}
if (Platform.Type == PlatformType.Windows)
{
var type = typeof(FFmpegUtils);
NativeLibraryHelper.PreloadLibrary("avutil-55", type);
NativeLibraryHelper.PreloadLibrary("swresample-2", type);
NativeLibraryHelper.PreloadLibrary("avcodec-57", type);
NativeLibraryHelper.PreloadLibrary("avformat-57", type);
NativeLibraryHelper.PreloadLibrary("swscale-4", type);
NativeLibraryHelper.PreloadLibrary("avfilter-6", type);
NativeLibraryHelper.PreloadLibrary("avdevice-57", type);
NativeLibraryHelper.PreloadLibrary("avutil-59", type);
NativeLibraryHelper.PreloadLibrary("swresample-5", type);
NativeLibraryHelper.PreloadLibrary("avcodec-61", type);
NativeLibraryHelper.PreloadLibrary("avformat-61", type);
NativeLibraryHelper.PreloadLibrary("swscale-8", type);
NativeLibraryHelper.PreloadLibrary("avfilter-10", type);
NativeLibraryHelper.PreloadLibrary("avdevice-61", type);
}
else
{
// This is likely there for forcing dll loading (on Android?), it will need some review.
// Force-load via a no-op API call so dlopen happens here (clearer stacks on failure).
uint version;
version = ffmpeg.avutil_version();
version = ffmpeg.swresample_version();
@@ -98,6 +122,23 @@ namespace Stride.Video.FFmpeg
}
}
private static string GetCurrentRid()
{
string os;
if (OperatingSystem.IsWindows()) os = "win";
else if (OperatingSystem.IsLinux()) os = "linux";
else if (OperatingSystem.IsMacOS()) os = "osx";
else return null;
var arch = RuntimeInformation.OSArchitecture switch
{
Architecture.X64 => "x64",
Architecture.Arm64 => "arm64",
_ => null,
};
return arch == null ? null : $"{os}-{arch}";
}
/// <summary>
/// Converts a <see cref="AVDictionary"/>* to a Dictionary&lt;string,string&gt;.
/// </summary>
@@ -16,8 +16,8 @@ namespace Stride.Video.FFmpeg
public VideoStream([NotNull] AVStream* pStream, [NotNull] FFmpegMedia media)
: base(pStream, media)
{
var pCodec = pStream->codec;
var framerateRatio = pCodec->framerate;
var pCodecpar = pStream->codecpar;
var framerateRatio = pCodecpar->framerate;
// HOTFIX (#95)
if (framerateRatio.den == 0)
@@ -30,9 +30,9 @@ namespace Stride.Video.FFmpeg
FPS = Convert.ToDouble(framerateRatio.num) / Convert.ToDouble(framerateRatio.den);
FrameDuration = TimeSpan.FromTicks(TimeSpan.TicksPerSecond / Convert.ToInt64(FPS));
}
PixelFormat = pCodec->pix_fmt;
Height = pCodec->height;
Width = pCodec->width;
PixelFormat = (AVPixelFormat)pCodecpar->format;
Height = pCodecpar->height;
Width = pCodecpar->width;
}
/// <summary>
@@ -132,17 +132,20 @@ namespace Stride.Video
if (videoComponent.Target != null)
{
videoTexture.SetTargetContentToVideoStream(videoComponent.Target);
// Now update the video texture with data of the new video frame:
// Upload the decoded RGBA frame directly into the user-supplied Target.
// We bypass VideoTexture's Swap-based machinery: it ping-pongs the user's
// Target with an internal renderTargetTexture and undoes the swap on Stop()
// — so once the video ends, the last decoded frame is stashed in the
// internal texture and the user-facing Target reverts to its uninitialized
// contents. Writing straight to the Target means the decoded data persists.
// Cost: skips mip generation, which the previous path supported via
// VideoTexture.GenerateMipMaps but defaults to single-mip anyway.
var graphicsContext = services.GetSafeServiceAs<GraphicsContext>();
if (streamInfo.Codec.IsHardwareAccelerated && streamInfo.Image == null)
videoTexture.CopyDecoderOutputToTopLevelMipmap(graphicsContext, streamInfo.Codec.DecoderOutputTexture);
else
videoTexture.UpdateTopLevelMipmapFromData(graphicsContext, streamInfo.Image);
videoTexture.GenerateMipMaps(graphicsContext);
unsafe
{
var dataPointer = new ReadOnlySpan<byte>((void*)streamInfo.Image.Buffer, streamInfo.Image.BufferSize);
videoComponent.Target.SetData(graphicsContext.CommandList, dataPointer, arrayIndex: 0, mipLevel: 0);
}
}
}
@@ -179,8 +182,9 @@ namespace Stride.Video
Duration = stream.Duration;
AllocateVideoTexture(stream.Width, stream.Height);
}
catch
catch (Exception ex)
{
Logger.Error($"InitializeMediaImpl failed: {ex}");
ReleaseMedia();
return;
}