Add Linux x64 FFmpeg native bundle
This commit is contained in:
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -2,3 +2,4 @@ docs/opcode-reference.md whitespace=trailing-space,space-before-tab,cr-at-eol
|
|||||||
docs/global-reference.md whitespace=trailing-space,space-before-tab,cr-at-eol
|
docs/global-reference.md whitespace=trailing-space,space-before-tab,cr-at-eol
|
||||||
docs/engine-ctx-reference.md whitespace=trailing-space,space-before-tab,cr-at-eol
|
docs/engine-ctx-reference.md whitespace=trailing-space,space-before-tab,cr-at-eol
|
||||||
tools/age_opcodes_himegari.py whitespace=trailing-space,space-before-tab,cr-at-eol
|
tools/age_opcodes_himegari.py whitespace=trailing-space,space-before-tab,cr-at-eol
|
||||||
|
*.sh text eol=lf
|
||||||
|
|||||||
@@ -113,8 +113,8 @@ S:\Game Hacking\Eushully\Himegari\ ← workspace root (three siblings)
|
|||||||
│ └── Age.Engine.Text.Windows/ optional exact Windows GDI gray-4 reference backend; kept
|
│ └── Age.Engine.Text.Windows/ optional exact Windows GDI gray-4 reference backend; kept
|
||||||
│ outside the platform-neutral VM/runtime core
|
│ outside the platform-neutral VM/runtime core
|
||||||
├── native/ authored native runtime boundaries
|
├── native/ authored native runtime boundaries
|
||||||
│ └── age_movie_ffmpeg/ project-owned FFmpeg C ABI, immutable Windows dependency manifest,
|
│ └── age_movie_ffmpeg/ project-owned FFmpeg C ABI, immutable Windows/Linux dependency manifests,
|
||||||
│ and bootstrap/build scripts (outputs stay under disposable build/)
|
│ and target bootstrap/build scripts (outputs stay under disposable build/)
|
||||||
├── tools/frida/ runtime-capture + engine-dump scripts (see tools/frida/README.md)
|
├── tools/frida/ runtime-capture + engine-dump scripts (see tools/frida/README.md)
|
||||||
└── godot/ DELIVERABLE — the Godot/C# ADV front-end (references Age.Engine
|
└── godot/ DELIVERABLE — the Godot/C# ADV front-end (references Age.Engine
|
||||||
plus the optional exact Windows text adapter). Its assembly embeds
|
plus the optional exact Windows text adapter). Its assembly embeds
|
||||||
|
|||||||
@@ -27,10 +27,11 @@ The VM and content pipeline are already mostly platform-neutral:
|
|||||||
The managed FFmpeg resolver derives reserved Windows, Linux, and macOS x64/arm64 RIDs from the actual
|
The managed FFmpeg resolver derives reserved Windows, Linux, and macOS x64/arm64 RIDs from the actual
|
||||||
process and rejects unreserved architectures instead of silently looking in the wrong directory.
|
process and rejects unreserved architectures instead of silently looking in the wrong directory.
|
||||||
|
|
||||||
The sole movie path now uses the project-owned FFmpeg C ABI rather than a Windows multimedia API, but only a
|
The sole movie path now uses the project-owned FFmpeg C ABI rather than a Windows multimedia API. Reproducible
|
||||||
Windows-x64 native bundle is built and staged today. The accepted DirectShow fallback was deleted after the
|
Windows-x64 and Linux-x64 native bundles are built and staged; the Linux ABI and complete installed corpus pass
|
||||||
corpus and audible LOGO/OP/CHAPTER gates passed. There are also softer Windows assumptions that should be tested
|
under WSL, but an exported Linux Godot executable has not yet received window/audio/font/filesystem smoke tests.
|
||||||
or replaced before claiming portable exports.
|
The accepted DirectShow fallback was deleted after the corpus and audible LOGO/OP/CHAPTER gates passed. There
|
||||||
|
are also softer Windows assumptions that should be tested or replaced before claiming portable exports.
|
||||||
|
|
||||||
## Dependency inventory
|
## Dependency inventory
|
||||||
|
|
||||||
@@ -38,8 +39,8 @@ or replaced before claiming portable exports.
|
|||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| SYS4INI per-game startup profile | `Sys4AssetCatalog` parses and retains the bounded ordered startup trailer; `Sys4LogicalCanvas` applies `SCREENX`/`SCREENY` with AGE's independent `640x480` fallbacks. Native SYS4INI also carries text, ADV input/skip, save ABI/path, audio, legacy renderer, and Windows registration settings | Godot uses the selected canvas for content scaling, backbuffers/compositor bounds, primary surfaces, layout/input fallbacks, and the default windowed client. `--window-width`/`--window-height` vary only the physical client while preserving the canvas and letterbox policy. The port still does not source `CancelMesSkipOnClick`, `CoexistMesSkip`, cursor/redraw policy, or wheel action ids from the trailer | Validate the existing presentation policy on Linux/macOS window managers. Apply further semantic keys explicitly, translate save roots through the host, and classify DirectDraw/fullscreen-bit and registration/key settings as native compatibility metadata. Canonical inventory and consumers: `sys4-format-notes.md` and `engine-re.md` |
|
| SYS4INI per-game startup profile | `Sys4AssetCatalog` parses and retains the bounded ordered startup trailer; `Sys4LogicalCanvas` applies `SCREENX`/`SCREENY` with AGE's independent `640x480` fallbacks. Native SYS4INI also carries text, ADV input/skip, save ABI/path, audio, legacy renderer, and Windows registration settings | Godot uses the selected canvas for content scaling, backbuffers/compositor bounds, primary surfaces, layout/input fallbacks, and the default windowed client. `--window-width`/`--window-height` vary only the physical client while preserving the canvas and letterbox policy. The port still does not source `CancelMesSkipOnClick`, `CoexistMesSkip`, cursor/redraw policy, or wheel action ids from the trailer | Validate the existing presentation policy on Linux/macOS window managers. Apply further semantic keys explicitly, translate save roots through the host, and classify DirectDraw/fullscreen-bit and registration/key settings as native compatibility metadata. Canonical inventory and consumers: `sys4-format-notes.md` and `engine-re.md` |
|
||||||
| Retained graphics presentation | Backend-neutral `GfxState`; accepted default Godot `Sprite2D` GPU stage plus the retained software pixel oracle, using runtime `ImageTexture`, canvas transforms/materials, and no native graphics API | GPU backend caches static/color-key variants, updates dynamic surfaces, handles retained range transitions, and falls back whole-frame for the legacy host screen-transition path | Godot owns D3D/Vulkan/Metal/OpenGL selection; validate shader/blend/filter behavior per target rather than adding a platform renderer |
|
| Retained graphics presentation | Backend-neutral `GfxState`; accepted default Godot `Sprite2D` GPU stage plus the retained software pixel oracle, using runtime `ImageTexture`, canvas transforms/materials, and no native graphics API | GPU backend caches static/color-key variants, updates dynamic surfaces, handles retained range transitions, and falls back whole-frame for the legacy host screen-transition path | Godot owns D3D/Vulkan/Metal/OpenGL selection; validate shader/blend/filter behavior per target rather than adding a platform renderer |
|
||||||
| AGE movie decode (`0x236` scene movies; `0x20f` modal LOGO/OP/ED; `0x24d` movie masks) | `FfmpegMovieDecoder` is the sole factory over the project-owned `native/age_movie_ffmpeg` ABI | Windows-x64 passes the complete 213-payload installed video/audio corpus gate plus audible LOGO/OP/CHAPTER playback and real TEST.AGF green-mask decode | Add target-specific native builds and export packaging |
|
| AGE movie decode (`0x236` scene movies; `0x20f` modal LOGO/OP/ED; `0x24d` movie masks) | `FfmpegMovieDecoder` is the sole factory over the project-owned `native/age_movie_ffmpeg` ABI | Windows-x64 and Linux-x64 both pass the complete 213-payload installed video/audio corpus gate; Windows also passes audible LOGO/OP/CHAPTER playback and real TEST.AGF green-mask decode | Add macOS native builds, distributable source-offer packaging, and Linux/macOS Godot runtime smoke gates |
|
||||||
| Movie integration | Each surface owns a unique playback-instance id; `MovieRuntime` owns `IMovieDecoder` from an injected factory; video-only streams use monotonic pacing while audio-bearing streams use the Godot output clock. Opcode `0x24d` redirects decoded green bytes through the platform-neutral retained rasterizer and exact managed packed-alpha mask helper | Concurrent/restarted uses of one asset have independent frame/audio/completion/teardown state. Ordinary and mask movies share VFS/FFmpeg ownership; the mask result is a backend-neutral dynamic RGBA surface consumed by either Godot renderer. Managed code is no longer Windows-annotated, while only the win-x64 native bundle exists today | Add Linux/macOS native builds and smoke gates |
|
| Movie integration | Each surface owns a unique playback-instance id; `MovieRuntime` owns `IMovieDecoder` from an injected factory; video-only streams use monotonic pacing while audio-bearing streams use the Godot output clock. Opcode `0x24d` redirects decoded green bytes through the platform-neutral retained rasterizer and exact managed packed-alpha mask helper | Concurrent/restarted uses of one asset have independent frame/audio/completion/teardown state. Ordinary and mask movies share VFS/FFmpeg ownership; the mask result is a backend-neutral dynamic RGBA surface consumed by either Godot renderer. Managed code is no longer Windows-annotated, and target-aware builds stage either the win-x64 or Linux-x64 native bundle | Add macOS native builds and Linux/macOS in-engine smoke gates |
|
||||||
| Movie audio | ABI v2 returns timestamped stereo float PCM; bounded managed buffering feeds a per-playback Godot `AudioStreamGenerator` and routes native movie flags to engine buses | All 29 installed audio-bearing streams decode with signal; synchronized LOGO/OP/CHAPTER playback is audibly accepted | Treat absent, distorted, or unsynchronized audio from an audio-bearing movie as a runtime bug |
|
| Movie audio | ABI v2 returns timestamped stereo float PCM; bounded managed buffering feeds a per-playback Godot `AudioStreamGenerator` and routes native movie flags to engine buses | All 29 installed audio-bearing streams decode with signal; synchronized LOGO/OP/CHAPTER playback is audibly accepted | Treat absent, distorted, or unsynchronized audio from an audio-bearing movie as a runtime bug |
|
||||||
| ADV font discovery/raster fidelity | `Age.Engine.Text` remains OS-neutral. On compatible ACP-932 Windows, `auto` selects the separate exact `Age.Engine.Text.Windows` GDI gray-4 backend. Otherwise Godot TextServer supplies Unicode masks and metrics from ordered system-font substitutions in `godot/config/himegari-text-rendering.json`; this policy is explicitly not GDI pixel-exact. Immediate `0x204`/`0x205`, live ADV, retained History, and the wait atlas all enter ordinary RGBA surfaces/retained handles | Three Mincho/Gothic regular/bold samples byte-match an independent Unicode GDI oracle. Forced exact and forced portable self-tests cover immediate, live, and History rasterization, bounded caching, complete-line materialization, retained ordering, alpha/tint/fade, transforms, clipping, capture, HIDEWIN suspension, partial erase/republication, reset, and teardown. Gameplay `Label` pools and overlay projection are gone | Produce and smoke-test Linux/macOS exports, confirm installed family availability/fallback selection there, and keep the GDI project out of non-Windows deliverables |
|
| ADV font discovery/raster fidelity | `Age.Engine.Text` remains OS-neutral. On compatible ACP-932 Windows, `auto` selects the separate exact `Age.Engine.Text.Windows` GDI gray-4 backend. Otherwise Godot TextServer supplies Unicode masks and metrics from ordered system-font substitutions in `godot/config/himegari-text-rendering.json`; this policy is explicitly not GDI pixel-exact. Immediate `0x204`/`0x205`, live ADV, retained History, and the wait atlas all enter ordinary RGBA surfaces/retained handles | Three Mincho/Gothic regular/bold samples byte-match an independent Unicode GDI oracle. Forced exact and forced portable self-tests cover immediate, live, and History rasterization, bounded caching, complete-line materialization, retained ordering, alpha/tint/fade, transforms, clipping, capture, HIDEWIN suspension, partial erase/republication, reset, and teardown. Gameplay `Label` pools and overlay projection are gone | Produce and smoke-test Linux/macOS exports, confirm installed family availability/fallback selection there, and keep the GDI project out of non-Windows deliverables |
|
||||||
| Filesystem semantics | Several filename and containment comparisons use `OrdinalIgnoreCase`; installed assets are conventionally uppercase | Needs validation on case-sensitive filesystems; may hide casing or containment mistakes | Add Linux/macOS tests with mixed-case synthetic roots and use filesystem-appropriate containment rules |
|
| Filesystem semantics | Several filename and containment comparisons use `OrdinalIgnoreCase`; installed assets are conventionally uppercase | Needs validation on case-sensitive filesystems; may hide casing or containment mistakes | Add Linux/macOS tests with mixed-case synthetic roots and use filesystem-appropriate containment rules |
|
||||||
@@ -72,9 +73,9 @@ VM op 0x236 (non-modal) / op 0x20f (modal) / op 0x24d (green-mask transition)
|
|||||||
|
|
||||||
Everything before and after the selected decoder is portable. The replacement decision is an in-process
|
Everything before and after the selected decoder is portable. The replacement decision is an in-process
|
||||||
FFmpeg backend behind a project-owned C ABI, not raw FFmpeg structs in Godot/C# and not a subprocess. FFmpeg
|
FFmpeg backend behind a project-owned C ABI, not raw FFmpeg structs in Godot/C# and not a subprocess. FFmpeg
|
||||||
`n8.1.2-29-g703dcc25b9` is pinned by immutable release URL and SHA-256 in
|
`n8.1.2-29-g703dcc25b9` is pinned by immutable release URL and SHA-256 in each target manifest under
|
||||||
`native/age_movie_ffmpeg/dependency-win64.json`; changing that pin requires rerunning the full installed-movie
|
`native/age_movie_ffmpeg/`; changing either pin requires rerunning the full installed-movie gate. The shim
|
||||||
gate. The shim dynamically links an LGPL build made without GPL or nonfree components and uses
|
dynamically links an LGPL build made without GPL or nonfree components and uses
|
||||||
`libavformat`, `libavcodec`, `libavutil`, `libswscale`, and `libswresample`. Release artifacts must carry
|
`libavformat`, `libavcodec`, `libavutil`, `libswscale`, and `libswresample`. Release artifacts must carry
|
||||||
the matching FFmpeg source/configuration and notices required by FFmpeg's
|
the matching FFmpeg source/configuration and notices required by FFmpeg's
|
||||||
[license checklist](https://ffmpeg.org/legal.html).
|
[license checklist](https://ffmpeg.org/legal.html).
|
||||||
@@ -167,11 +168,10 @@ DirectShow renderer to the existing D3D target without assigning an RGB key. The
|
|||||||
DirectShow preroll/stream-origin inference are recorded in `docs/engine-re.md`.
|
DirectShow preroll/stream-origin inference are recorded in `docs/engine-re.md`.
|
||||||
|
|
||||||
Native deliverables are RID-specific and bundled with the Godot export; the runtime must not discover an
|
Native deliverables are RID-specific and bundled with the Godot export; the runtime must not discover an
|
||||||
arbitrary system FFmpeg. The first implementation gate is Windows x64 because that is the current runnable
|
arbitrary system FFmpeg. Windows x64 and Linux x64 now have pinned builds; the C ABI and loader paths also reserve
|
||||||
target, but the C ABI and loader paths must reserve Windows x64, Linux x64, macOS x64, and macOS arm64 from the
|
macOS x64 and macOS arm64. Builds use shared libraries, `$ORIGIN`/`@loader_path`-style local lookup on Unix
|
||||||
start. Builds use shared libraries, `$ORIGIN`/`@loader_path`-style local lookup on Unix targets, recorded source
|
targets, recorded source hashes and configure arguments, and no committed original-game data. Packaging
|
||||||
hashes and configure arguments, and no committed original-game data. Packaging automation is part of completing
|
automation is part of completing the backend, including the corresponding-source/configuration offer.
|
||||||
the backend, not a prerequisite for the first native decode spike.
|
|
||||||
|
|
||||||
The current movie ABI is version 3. Its position-seek entry point resets both independent demux/decoder
|
The current movie ABI is version 3. Its position-seek entry point resets both independent demux/decoder
|
||||||
pipelines before managed keyframe/audio preroll, so opcode `0x241` has the same bounded, synchronized behavior
|
pipelines before managed keyframe/audio preroll, so opcode `0x241` has the same bounded, synchronized behavior
|
||||||
@@ -188,8 +188,19 @@ packaging. `FfmpegMovieDecoder` now adds cancellable timestamp pacing and is the
|
|||||||
copies the shim, five required shared libraries, and FFmpeg license beside `Himegari.dll`; a natural SYSTEM4
|
copies the shim, five required shared libraries, and FFmpeg license beside `Himegari.dll`; a natural SYSTEM4
|
||||||
smoke completed 7288 ms `LOGO.AGF`, opened 106919 ms `OP.AGF`, and published frames from both without loading
|
smoke completed 7288 ms `LOGO.AGF`, opened 106919 ms `OP.AGF`, and published frames from both without loading
|
||||||
DirectShow; the user subsequently confirmed both opening movies work in normal windowed playback. DirectShow
|
DirectShow; the user subsequently confirmed both opening movies work in normal windowed playback. DirectShow
|
||||||
was deleted after the later synchronized-audio gate. Full
|
was deleted after the later synchronized-audio gate. Full export/source-offer packaging, Linux Godot runtime
|
||||||
export/source-offer packaging and non-Windows builds remain outstanding.
|
smoke coverage, and macOS native builds remain outstanding.
|
||||||
|
|
||||||
|
The Linux-x64 bundle landed on 2026-07-31 from the matching pinned BtbN LGPL shared build. Its bootstrap verifies
|
||||||
|
the immutable archive hash and reported FFmpeg commit; its build emits an ELF64 x86-64 ABI v3 shim plus only the
|
||||||
|
five required SONAME libraries and license under disposable `build/native/linux-x64`. The shim records
|
||||||
|
`RUNPATH=$ORIGIN`, every FFmpeg dependency resolves beside it, and the bundle's newest required glibc symbol is
|
||||||
|
2.28, matching the provider baseline enforced by the build script. A self-contained Linux corpus-gate executable
|
||||||
|
under Ubuntu 20.04 decoded all 213 installed payloads with 0 failures in 10.696 seconds. A zero-warning
|
||||||
|
`ExportRelease` Linux-target assembly build staged the shim, five libraries, and license beside `Himegari.dll`
|
||||||
|
without the Windows GDI assembly or DLLs. These gates establish Linux loader/ABI/decoder and managed staging
|
||||||
|
compatibility; they do not yet establish an exported Godot window, live audio timing, font realization, or
|
||||||
|
case-sensitive install/profile behavior.
|
||||||
|
|
||||||
The Windows-x64 installed-corpus gate passed on 2026-07-22 with the pinned dependency: all 213 signature-selected
|
The Windows-x64 installed-corpus gate passed on 2026-07-22 with the pinned dependency: all 213 signature-selected
|
||||||
MPEG payloads opened and reached EOF under a 30-second per-item bound. It decoded all 15,788 frames across twelve
|
MPEG payloads opened and reached EOF under a 30-second per-item bound. It decoded all 15,788 frames across twelve
|
||||||
|
|||||||
@@ -221,22 +221,25 @@ script/offset/opcode, that frame chain, the hottest sites in the bounded final 1
|
|||||||
final 16-instruction sequence. This makes the last ADV locator unnecessary for identifying a post-dialogue loop;
|
final 16-instruction sequence. This makes the last ADV locator unnecessary for identifying a post-dialogue loop;
|
||||||
send either the `step-limit` console block or the generated JSON.
|
send either the `step-limit` console block or the generated JSON.
|
||||||
|
|
||||||
## Native FFmpeg movie shim (Windows x64)
|
## Native FFmpeg movie shim (Windows and Linux x64)
|
||||||
|
|
||||||
These PowerShell tools build the selected Windows-x64 live movie backend. The dependency manifest pins an
|
The target-specific dependency manifests pin immutable LGPL shared FFmpeg archives and SHA-256 hashes. The
|
||||||
immutable LGPL shared FFmpeg archive and SHA-256.
|
Windows tools use PowerShell/MSVC; the Linux tools run in Bash with Python 3, curl, a C compiler, binutils, and
|
||||||
|
the ordinary archive/core utilities available.
|
||||||
|
|
||||||
| Tool | Purpose | Run | Reads → Writes |
|
| Tool | Purpose | Run | Reads → Writes |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| `native/age_movie_ffmpeg/bootstrap-win64.ps1` | Download, hash-check, extract, and version-check the pinned FFmpeg SDK. Returns the resolved SDK root. | `.\\native\\age_movie_ffmpeg\\bootstrap-win64.ps1 [-Destination <dir>]` | `dependency-win64.json`, network/archive cache → disposable `build/downloads/`, `build/ffmpeg-sdk/` |
|
| `native/age_movie_ffmpeg/bootstrap-win64.ps1` | Download, hash-check, extract, and version-check the pinned FFmpeg SDK. Returns the resolved SDK root. | `.\\native\\age_movie_ffmpeg\\bootstrap-win64.ps1 [-Destination <dir>]` | `dependency-win64.json`, network/archive cache → disposable `build/downloads/`, `build/ffmpeg-sdk/` |
|
||||||
| `native/age_movie_ffmpeg/build-win64.ps1` | Discover the MSVC x64 toolchain, build `age_movie_ffmpeg.dll`, and stage its exact shared-library/license dependencies. | `.\\native\\age_movie_ffmpeg\\build-win64.ps1 -SdkRoot <bootstrap-output> [-OutputDirectory <dir>]` | C ABI source + FFmpeg SDK → disposable `build/native/win-x64/` by default |
|
| `native/age_movie_ffmpeg/build-win64.ps1` | Discover the MSVC x64 toolchain, build `age_movie_ffmpeg.dll`, and stage its exact shared-library/license dependencies. | `.\\native\\age_movie_ffmpeg\\build-win64.ps1 -SdkRoot <bootstrap-output> [-OutputDirectory <dir>]` | C ABI source + FFmpeg SDK → disposable `build/native/win-x64/` by default |
|
||||||
|
| `native/age_movie_ffmpeg/bootstrap-linux-x64.sh` | Download, SHA-256-check, extract, and version-check the pinned Linux x64 FFmpeg SDK. Prints the resolved SDK root. | `native/age_movie_ffmpeg/bootstrap-linux-x64.sh [destination]` | `dependency-linux-x64.json`, network/archive cache → disposable `build/downloads/`, `build/ffmpeg-sdk/` |
|
||||||
|
| `native/age_movie_ffmpeg/build-linux-x64.sh` | Build the ELF64 x86-64 shim; stage the five exact FFmpeg SONAME libraries and license; reject stale `.so` files, unresolved/nonlocal FFmpeg dependencies, a missing `$ORIGIN` runpath, or a glibc requirement newer than the pinned 2.28 baseline. | `sdk_root="$(native/age_movie_ffmpeg/bootstrap-linux-x64.sh)"` then `native/age_movie_ffmpeg/build-linux-x64.sh "$sdk_root" [output-directory]` | C ABI source + FFmpeg SDK → disposable `build/native/linux-x64/` by default |
|
||||||
| `tools/movie-corpus-gate` | Discover every MPEG program stream stored under an `.AGF` catalog entry; decode every video frame and, when present, every audio block through the unpaced FFmpeg session; validate independent sequence dimensions, metadata, RGBA size, stereo finite PCM, monotonic video/audio timestamps, EOF, timeout, and teardown; then emit a per-asset JSON report. Audio fields include sample rate/channels, block and PCM-frame counts, first/last PTS, signal presence, and decode time. Exit 0 means the expected corpus count and every asset passed; exit 1 is a gate failure; exit 2 means the native shim is absent. | `dotnet run --project tools/movie-corpus-gate -- --output build/movie-corpus-ffmpeg.json --expected-count 213 --max-item-ms 30000` · optional `--native-dir <dir>` | `SYS4INI.BIN` + loose/ALF VFS assets + staged FFmpeg shim → stdout progress + disposable `build/movie-corpus-ffmpeg.json` |
|
| `tools/movie-corpus-gate` | Discover every MPEG program stream stored under an `.AGF` catalog entry; decode every video frame and, when present, every audio block through the unpaced FFmpeg session; validate independent sequence dimensions, metadata, RGBA size, stereo finite PCM, monotonic video/audio timestamps, EOF, timeout, and teardown; then emit a per-asset JSON report. Audio fields include sample rate/channels, block and PCM-frame counts, first/last PTS, signal presence, and decode time. Exit 0 means the expected corpus count and every asset passed; exit 1 is a gate failure; exit 2 means the native shim is absent. | `dotnet run --project tools/movie-corpus-gate -- --output build/movie-corpus-ffmpeg.json --expected-count 213 --max-item-ms 30000` · optional `--native-dir <dir>` | `SYS4INI.BIN` + loose/ALF VFS assets + staged FFmpeg shim → stdout progress + disposable `build/movie-corpus-ffmpeg.json` |
|
||||||
|
|
||||||
The managed isolated probes load from `AGE_FFMPEG_NATIVE_DIR` when set, then application-local and
|
The managed isolated probes load from `AGE_FFMPEG_NATIVE_DIR` when set, then application-local and
|
||||||
`runtimes/win-x64/native` locations. Build the shim before running the `FfmpegShim*` tests; no original-game
|
`runtimes/<rid>/native` locations. Build the target shim before running the `FfmpegShim*` tests; no original-game
|
||||||
movie is copied into the repository or native output. When `build/native/win-x64/age_movie_ffmpeg.dll` exists,
|
movie is copied into the repository or native output. A target-aware `dotnet build godot/Himegari.csproj`
|
||||||
`dotnet build godot/Himegari.csproj` also stages the shim, its five DLL dependencies, and `FFmpeg-LICENSE.txt`
|
stages the matching shim, five shared-library dependencies, and `FFmpeg-LICENSE.txt` beside `Himegari.dll`
|
||||||
beside `Himegari.dll` for development playback.
|
when the corresponding `build/native/win-x64/` or `build/native/linux-x64/` bundle exists.
|
||||||
|
|
||||||
The current native movie ABI is version 3. In addition to sequential video/audio decode, it exposes a
|
The current native movie ABI is version 3. In addition to sequential video/audio decode, it exposes a
|
||||||
synchronous position seek for both independent demuxers; exact video-frame selection and audio trimming are
|
synchronous position seek for both independent demuxers; exact video-frame selection and audio trimming are
|
||||||
|
|||||||
65
native/age_movie_ffmpeg/bootstrap-linux-x64.sh
Executable file
65
native/age_movie_ffmpeg/bootstrap-linux-x64.sh
Executable file
@@ -0,0 +1,65 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
repo_root="$(cd -- "$script_dir/../.." && pwd)"
|
||||||
|
destination="${1:-$repo_root/build/ffmpeg-sdk}"
|
||||||
|
manifest="$script_dir/dependency-linux-x64.json"
|
||||||
|
|
||||||
|
manifest_value() {
|
||||||
|
python3 -c 'import json, sys; print(json.load(open(sys.argv[1], encoding="utf-8"))[sys.argv[2]])' \
|
||||||
|
"$manifest" "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
for command in python3 curl sha256sum tar awk; do
|
||||||
|
command -v "$command" >/dev/null 2>&1 || {
|
||||||
|
echo "required command was not found: $command" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
done
|
||||||
|
|
||||||
|
archive="$(manifest_value archive)"
|
||||||
|
url="$(manifest_value url)"
|
||||||
|
expected_hash="$(manifest_value sha256)"
|
||||||
|
expected_version="$(manifest_value ffmpeg_version)"
|
||||||
|
archive_root="${archive%.tar.xz}"
|
||||||
|
download_dir="$repo_root/build/downloads"
|
||||||
|
archive_path="$download_dir/$archive"
|
||||||
|
sdk_root="$destination/$archive_root"
|
||||||
|
|
||||||
|
mkdir -p -- "$download_dir" "$destination"
|
||||||
|
if [[ ! -f "$archive_path" ]]; then
|
||||||
|
curl --fail --location --retry 3 --output "$archive_path" "$url"
|
||||||
|
fi
|
||||||
|
|
||||||
|
actual_hash="$(sha256sum "$archive_path" | awk '{ print $1 }')"
|
||||||
|
if [[ "$actual_hash" != "$expected_hash" ]]; then
|
||||||
|
echo "FFmpeg archive SHA-256 mismatch: expected $expected_hash, got $actual_hash" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -d "$sdk_root" ]]; then
|
||||||
|
tar -xJf "$archive_path" -C "$destination"
|
||||||
|
fi
|
||||||
|
|
||||||
|
for required in \
|
||||||
|
"$sdk_root/include/libavformat/avformat.h" \
|
||||||
|
"$sdk_root/lib/libavformat.so" \
|
||||||
|
"$sdk_root/bin/ffmpeg" \
|
||||||
|
"$sdk_root/LICENSE.txt"; do
|
||||||
|
if [[ ! -e "$required" ]]; then
|
||||||
|
echo "FFmpeg SDK file was not found after extraction: $required" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
reported="$(LD_LIBRARY_PATH="$sdk_root/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" \
|
||||||
|
"$sdk_root/bin/ffmpeg" -version)"
|
||||||
|
reported="${reported%%$'\n'*}"
|
||||||
|
version_without_date="${expected_version%-????????}"
|
||||||
|
if [[ "$reported" != *"$version_without_date"* ]]; then
|
||||||
|
echo "unexpected FFmpeg build: $reported" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf '%s\n' "$sdk_root"
|
||||||
131
native/age_movie_ffmpeg/build-linux-x64.sh
Executable file
131
native/age_movie_ffmpeg/build-linux-x64.sh
Executable file
@@ -0,0 +1,131 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
if [[ $# -lt 1 || $# -gt 2 ]]; then
|
||||||
|
echo "usage: $0 <ffmpeg-sdk-root> [output-directory]" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
repo_root="$(cd -- "$script_dir/../.." && pwd)"
|
||||||
|
sdk_root="$(cd -- "$1" && pwd)"
|
||||||
|
output_directory_input="${2:-$repo_root/build/native/linux-x64}"
|
||||||
|
mkdir -p -- "$output_directory_input"
|
||||||
|
output_directory="$(cd -- "$output_directory_input" && pwd)"
|
||||||
|
include_directory="$sdk_root/include"
|
||||||
|
library_directory="$sdk_root/lib"
|
||||||
|
shim="$output_directory/libage_movie_ffmpeg.so"
|
||||||
|
manifest="$script_dir/dependency-linux-x64.json"
|
||||||
|
|
||||||
|
for command in cc readelf ldd python3 sort; do
|
||||||
|
command -v "$command" >/dev/null 2>&1 || {
|
||||||
|
echo "required command was not found: $command" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
done
|
||||||
|
|
||||||
|
for required in \
|
||||||
|
"$include_directory/libavformat/avformat.h" \
|
||||||
|
"$library_directory/libavformat.so" \
|
||||||
|
"$library_directory/libavcodec.so" \
|
||||||
|
"$library_directory/libavutil.so" \
|
||||||
|
"$library_directory/libswscale.so" \
|
||||||
|
"$library_directory/libswresample.so" \
|
||||||
|
"$sdk_root/LICENSE.txt"; do
|
||||||
|
if [[ ! -e "$required" ]]; then
|
||||||
|
echo "missing FFmpeg SDK file: $required" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
cc \
|
||||||
|
-std=c11 -fPIC -fvisibility=hidden -O2 -Wall -Wextra \
|
||||||
|
-I "$include_directory" \
|
||||||
|
-shared -Wl,-z,defs -Wl,-z,origin -Wl,--enable-new-dtags -Wl,-rpath,'$ORIGIN' \
|
||||||
|
-o "$shim" "$script_dir/age_movie.c" \
|
||||||
|
-L "$library_directory" \
|
||||||
|
-lavformat -lavcodec -lavutil -lswscale -lswresample
|
||||||
|
|
||||||
|
runtime_libraries=(
|
||||||
|
libavformat.so.62
|
||||||
|
libavcodec.so.62
|
||||||
|
libavutil.so.60
|
||||||
|
libswscale.so.9
|
||||||
|
libswresample.so.6
|
||||||
|
)
|
||||||
|
for runtime in "${runtime_libraries[@]}"; do
|
||||||
|
source_library="$library_directory/$runtime"
|
||||||
|
if [[ ! -e "$source_library" ]]; then
|
||||||
|
echo "missing FFmpeg runtime library: $source_library" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
cp --dereference -- "$source_library" "$output_directory/$runtime"
|
||||||
|
done
|
||||||
|
cp -- "$sdk_root/LICENSE.txt" "$output_directory/FFmpeg-LICENSE.txt"
|
||||||
|
|
||||||
|
expected_outputs=(libage_movie_ffmpeg.so "${runtime_libraries[@]}")
|
||||||
|
shopt -s nullglob
|
||||||
|
for existing in "$output_directory"/*.so*; do
|
||||||
|
existing_name="$(basename -- "$existing")"
|
||||||
|
expected=false
|
||||||
|
for expected_name in "${expected_outputs[@]}"; do
|
||||||
|
if [[ "$existing_name" == "$expected_name" ]]; then
|
||||||
|
expected=true
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [[ "$expected" != true ]]; then
|
||||||
|
echo "unexpected stale shared library in output directory: $existing" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if ! readelf -h "$shim" | grep -Eq 'Class:[[:space:]]+ELF64'; then
|
||||||
|
echo "native movie shim is not an ELF64 binary: $shim" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! readelf -h "$shim" | grep -Eq 'Machine:[[:space:]]+Advanced Micro Devices X86-64'; then
|
||||||
|
echo "native movie shim is not an x86-64 binary: $shim" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! readelf -d "$shim" | grep -Fq '$ORIGIN'; then
|
||||||
|
echo "native movie shim does not have an \$ORIGIN runtime search path: $shim" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
ldd_output="$(ldd "$shim")"
|
||||||
|
if [[ "$ldd_output" == *"not found"* ]]; then
|
||||||
|
printf '%s\n' "$ldd_output" >&2
|
||||||
|
echo "native movie bundle has unresolved shared-library dependencies" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
for runtime in "${runtime_libraries[@]}"; do
|
||||||
|
resolution="$(printf '%s\n' "$ldd_output" | grep -F "$runtime =>" || true)"
|
||||||
|
if [[ "$resolution" != *"=> $output_directory/$runtime "* ]]; then
|
||||||
|
printf '%s\n' "$ldd_output" >&2
|
||||||
|
echo "$runtime did not resolve from the bundle directory" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
minimum_glibc="$(python3 -c \
|
||||||
|
'import json, sys; print(json.load(open(sys.argv[1], encoding="utf-8"))["minimum_glibc"])' \
|
||||||
|
"$manifest")"
|
||||||
|
versioned_outputs=("$shim")
|
||||||
|
for runtime in "${runtime_libraries[@]}"; do
|
||||||
|
versioned_outputs+=("$output_directory/$runtime")
|
||||||
|
done
|
||||||
|
highest_glibc="$(readelf --version-info "${versioned_outputs[@]}" \
|
||||||
|
| grep -Eo 'GLIBC_[0-9]+(\.[0-9]+)*' | sort -Vu | tail -n 1 || true)"
|
||||||
|
if [[ -z "$highest_glibc" ]]; then
|
||||||
|
echo "could not determine the bundle's glibc symbol-version requirement" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
highest_glibc="${highest_glibc#GLIBC_}"
|
||||||
|
newest_glibc="$(printf '%s\n%s\n' "$minimum_glibc" "$highest_glibc" | sort -Vu | tail -n 1)"
|
||||||
|
if [[ "$newest_glibc" != "$minimum_glibc" ]]; then
|
||||||
|
echo "bundle requires glibc $highest_glibc, newer than the pinned $minimum_glibc baseline" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf '%s\n' "$shim"
|
||||||
12
native/age_movie_ffmpeg/dependency-linux-x64.json
Normal file
12
native/age_movie_ffmpeg/dependency-linux-x64.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"provider": "BtbN/FFmpeg-Builds",
|
||||||
|
"release_tag": "autobuild-2026-07-21-13-38",
|
||||||
|
"archive": "ffmpeg-n8.1.2-29-g703dcc25b9-linux64-lgpl-shared-8.1.tar.xz",
|
||||||
|
"url": "https://github.com/BtbN/FFmpeg-Builds/releases/download/autobuild-2026-07-21-13-38/ffmpeg-n8.1.2-29-g703dcc25b9-linux64-lgpl-shared-8.1.tar.xz",
|
||||||
|
"sha256": "d0026954f10d303d1fc6517ed6b2290c42323b3958f11baafb1b330dfc564b36",
|
||||||
|
"ffmpeg_version": "n8.1.2-29-g703dcc25b9-20260721",
|
||||||
|
"ffmpeg_commit": "703dcc25b9",
|
||||||
|
"variant": "linux64 LGPL shared 8.1",
|
||||||
|
"minimum_glibc": "2.28",
|
||||||
|
"minimum_linux_kernel": "4.18"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user