Skip to content

diag(ffmpeg): name the libavcodec that answered, not "this build" - #404

Merged
superuser404notfound merged 1 commit into
mainfrom
diag/ffmpeg-runtime-abi-check
Aug 22, 2026
Merged

diag(ffmpeg): name the libavcodec that answered, not "this build"#404
superuser404notfound merged 1 commit into
mainfrom
diag/ffmpeg-runtime-abi-check

Conversation

@superuser404notfound

Copy link
Copy Markdown
Owner

Closes nothing; AE#396 turned out to be host-side, and the reporter fixed it themselves. What is left is the part that was ours: the engine had no way to notice it was running against a different FFmpeg, and the one line it did emit described someone else’s build as if it were ours.

What AE#396 was

The reporting app linked a second FFmpeg: an older internal module shipped libavcodec 61 as a static archive, -force_loaded, so its avcodec_* became ordinary definitions in the executable and beat the engine’s dylibs for every other object in the same link. AetherEngine expects 62 and executed against 61. Every bridged source on that build emitted nothing, on five fixtures across three codecs and two containers, and on their own tvOS app (same engine, same file, no second FFmpeg) the same file played.

The trace the engine left was:

[HLSVideoEngine] flac bridge encoder absent from this FFmpeg build, cascading to eac3

True, and about a build that was not ours. avcodec_version and avcodec_configuration appeared zero times in Sources/.

What this adds

FFmpegRuntimeCheck compares the compile-time major of all four linked libraries against what the loaded binaries answer, and emits the verdict once from AetherEngine.init:

[FFmpeg] libavcodec 62.28.102, libavformat 62.6.100, libavutil 60.13.100, libswresample 6.0.100

On a mismatch that becomes an ERROR: naming the mismatched libraries with both majors, the two shapes that cause it (a force-loaded static archive; a dependency exporting the same symbols, libVLC among them), the nm -m / otool -L probes that show which binary wins, and the configure line of the libavcodec that actually answered. libavutil matters more than libavcodec here: a major shift there moves struct layouts, which is a crash rather than a silence.

The encoder-absence message now carries that identity, so a claim about "this FFmpeg build" always says which one.

Docs

A linking contract in docs/api.md, and the README’s static-linking and diagnostics sections, because being dynamically embedded is not the same as being reached. libVLC beside AetherEngine is an ordinary shape for the fallback ladder this API invites, and CocoaPods sorts a pod ahead of a vendored framework by default.

Not done

Namespacing the vendored FFmpeg symbols, the reporter’s second suggestion. It would need a post-build symbol rewrite across eight slices plus rewritten headers, and it breaks every consumer that uses the FFmpeg headers directly. The cause is link order in the host, which only the host can set; what was missing was a witness, not a rename.

Tests

11 new, TDD, and the sharp one is testLoadedLibrariesMatchTheHeadersThisBuildCompiledAgainst: it fails in exactly the situation AE#396 was. Mutation-checked (compiled major +1 fails it, and names the reason). Full suite 1955 tests green.

Reported and diagnosed by @kskchaitanya1993.

🤖 Generated with Claude Code

https://claude.ai/code/session_01E5UztmCZtNYPQpzdmSdmWG

AE#396 was reported as an audio-bridge defect and was a second FFmpeg. The
host linked a static libavcodec 61 with -force_load, so its symbols became
ordinary definitions in the executable and beat the engine's dylibs; the
engine ran against headers it was never compiled against. Its only trace
was "flac bridge encoder absent from this FFmpeg build", a true sentence
about a build that was not ours, and it reads as a claim about ours. Five
fixtures and two devices before anyone looked at the link.

The engine could not notice. avcodec_version and avcodec_configuration
appeared zero times in Sources/.

FFmpegRuntimeCheck compares the compile-time major of all four linked
libraries against what the loaded binaries report, and emits the verdict
once from init: the four versions when they match, and otherwise an ERROR
naming the mismatched libraries, the two shapes that cause it (a
force-loaded static archive, a dependency exporting the same symbols such
as libVLC), the nm/otool probes that show which binary wins, and the
configure line of the libavcodec that answered. The encoder-absence
message carries that identity too, so a statement about "this FFmpeg
build" always says which one.

Docs: a linking contract in docs/api.md, and the README's static-linking
and diagnostics sections, because being dynamically embedded is not the
same as being reached.

Reported and diagnosed by @kskchaitanya1993, who also supplied the
host-side reproduction.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E5UztmCZtNYPQpzdmSdmWG
@superuser404notfound
superuser404notfound merged commit f76739a into main Aug 22, 2026
4 checks passed
@superuser404notfound
superuser404notfound deleted the diag/ffmpeg-runtime-abi-check branch August 22, 2026 04:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant