Skip to content

Bump FFmpegKit to 8.1.9 (FFmpeg n8.1.3) and fix the SIGXCPU freeze - #23

Merged
sbokatuk merged 5 commits into
mainfrom
bump-ffmpegkit-8.1.9
Oct 30, 2026
Merged

sbokatuk merged 5 commits into
mainfrom
bump-ffmpegkit-8.1.9

Conversation

@sbokatuk

@sbokatuk sbokatuk commented Oct 30, 2026 •

Copy link
Copy Markdown
Owner

Moves FFmpegKit from 8.1.7 to 8.1.9, which is the first release on FFmpeg n8.1.3, and fixes an app freeze that also affects the published 8.1.2.5. Package 8.1.3.1, tag v8.1.3.1.

Closes #21

Don't merge until you've approved it: merging docs/release-notes/8.1.3.1.md is the release.

Please review first

1. A freeze fix that isn't part of the version bump (commit 73232f5). G4 could not pass without it. The example app froze with an ANR on its first conversion, and it does the same on the published 8.1.2.5, so this is not a regression from 8.1.9.

  • Cause: Mono on Android restarts threads after a garbage collection with SIGXCPU, and FFmpeg's term_init installs its own handler for that signal. The next collection after an FFmpeg command then leaves every managed thread suspended for good.
  • Why nothing caught it: CI never deploys the sample, and the device checks allocated too little to trigger a collection.
  • Fix: FFmpegKitConfig.IgnoreSignal(Signal.Sigxcpu) from static constructors on FFmpegKit, FFmpegKitConfig and AsyncFFmpegExecuteTask. No public API changes. A module initializer was rejected because it would load the native libraries with the assembly, which breaks apps that gate FFmpegKit at runtime.
  • Proof: with the one-line opt-out added to the sample, conversions complete. The new device check forces collections after FFmpeg has run. It hangs on 8.1.2.5 and on unfixed 8.1.3.1, and passes in all 10 G3 runs here.

2. Two device check fixes (commit b49d886). Running all eight variants exposed them, because CI's emulator job only runs Video:

  • ExternalLibraries had to be non-empty, but min has none. The published 8.1.2.5 fails the same way.
  • The progress check stopped waiting at the first sample, which is at position zero, and could then fail with "Position never advanced". It now waits for a sample that advanced, so it is just as strict.

The progress race failed 2 of 9 cold starts on 8.1.3.1 and 0 of 24 on 8.1.2.5, so I can't show that it predates this bump. After the fix, 60 of 60 cold starts pass, each receiving both samples, so no callback is being lost.

What changed upstream

  • FFmpeg n8.1.2 → n8.1.3, read from libavcodec.so in all 8 variants on both ABIs. Upstream reports that this closes CVE-2026-64830 and CVE-2026-64835. FFmpeg's own history has the matching VobSub and ADX fixes; the CVE numbers are upstream's claim.
  • drawtext returns to Full, FullGpl and Video: --enable-libharfbuzz and the filter are in the shipped libraries. This was checked in the binaries, not by running the filter on a device.
  • Upstream reports that a -filter_complex parse error no longer kills the process. Not verified here.
  • Min and Https now ship libc++_shared.so, about 1 MB larger .aar.
  • The 8.1.9 .pom now declares smart-exception. The jars stay embedded, because no .NET build reads a .pom.

Everything outside jni/ is identical to 8.1.7 in all 8 variants, licences included. All 160 .so files pass a static GNU_RELRO check and are 16 KB aligned.

API changes: none. classes.jar is byte-identical to 8.1.7 in all 8 variants, and generated api.xml matches in all 24 variant and framework pairs, so Metadata.xml is unchanged. Target frameworks, ABIs, minSdk 24 and licensing are unchanged.

Mapping: added 8.1.3 8.1.9 and kept 8.1.2 8.1.7, so 8.1.7 stays rebuildable. The brief's replace-the-row step and its "8.1.7 can no longer be rebuilt" line don't apply, because this is a new FFmpeg.

Test report — FFmpegKit.Net.*.Android 8.1.3.1-local.20261030121512

Toolchain: .NET SDK 8.0.427 / 9.0.320 / 10.0.403, android workloads 34.0.154 / 35.0.105 / 36.1.118, JDK 17.0.21, emulator ffmpegkit_e2e (API 35 google_apis, arm64-v8a), physical Pixel 10 (Android 17, API 37, arm64-v8a). No Xcode involved.

Gate Target TFM Result Evidence
G1 pack all 8 variants, both bands net8, net9, net10 PASS 8 nupkgs, 8 snupkgs; correct .aar, licence and n8.1.3 on both ABIs in each
G2 package tests all 8 variants PASS 190/190; suite also shown to fail on an empty folder and on a relabelled licence
G3 Audio emulator net10.0-android36.0 PASS 15/15, ffmpeg=n8.1.3, package=audio
G3 Full emulator net10.0-android36.0 PASS 15/15, ffmpeg=n8.1.3, package=custom (see note)
G3 FullGpl emulator net10.0-android36.0 PASS 15/15, ffmpeg=n8.1.3, package=custom (see note)
G3 Https emulator net10.0-android36.0 PASS 15/15, ffmpeg=n8.1.3, package=https
G3 HttpsGpl emulator net10.0-android36.0 PASS 15/15, ffmpeg=n8.1.3, package=https-gpl
G3 Min emulator net10.0-android36.0 PASS 15/15, ffmpeg=n8.1.3, package=min
G3 MinGpl emulator net10.0-android36.0 PASS 15/15, ffmpeg=n8.1.3, package=min-gpl
G3 Video emulator net10.0-android36.0 PASS 15/15, ffmpeg=n8.1.3, package=video
G3 Full emulator net8.0-android34.0 PASS 15/15, ffmpeg=n8.1.3
G3 Video emulator net8.0-android34.0 PASS 15/15, ffmpeg=n8.1.3
G4 Example emulator net10.0-android36.0 PASS "Source: 320x240, 3s, mpeg4."; Resize 107,659, Grayscale 99,770, AAC 27,731 bytes; all three played
G4 Example emulator net9.0-android35.0 PASS same results
G4 Example emulator net8.0-android34.0 PASS same results; not required by the brief, added because CI builds this band
G3 all 8 variants Pixel 10, Android 17 (API 37) net10.0-android36.0 PASS 15/15 each, ffmpeg=n8.1.3, GC check passed; not required, added on the maintainer's request
G3 Full, Video Pixel 10, Android 17 (API 37) net8.0-android34.0 PASS 15/15 each, ffmpeg=n8.1.3
G4 Example Pixel 10, Android 17 (API 37) net10, net9 and net8 PASS probe correct; Resize 109,957, Grayscale 102,323, AAC 27,731 bytes; all three played on every band
G5 umbrella samples N/A belongs to F2, after this is published
G6 PR CI ubuntu, emulator API 35 x86_64, Video net8.0-android34.0, net10.0-android36.0 PASS all 7 checks green on d218ca7; both emulator legs FFMPEGKIT_E2E_DONE PASS passed=15, ffmpeg=n8.1.3, GC check passed
G7 consumer path N/A packaging and .targets are unchanged

No check is SKIPPED.

Notes on the evidence:

  • Commits: G3 ran on the final test code (d218ca7). The package, G1, G2 and G4 are from before that commit, which only changes SmokeTests.cs, a file that isn't packed.
  • Version check: SmokeTests only asserts that FFmpegVersion is non-empty, so the local G3 runner compared the logged value against n8.1.3 itself. It's still not enforced in CI.
  • package=custom: published 8.1.2.5 Full reports the same, with an identical library list, so it isn't a change.
  • Playback: paused frames of the After player were identical and frames during playback differed. Position advanced, and the resize clip's on-screen counter moved. The AAC result has no video track, so its picture is black by design and only the position moves.
  • Progress in the sample: the in-progress state was seen ("Starting…", bar, "Converting…", Cancel enabled), but no percentage was caught, because these conversions finish in under a second. Numeric progress is covered by G3's reports progress check.
  • x86_64: runs on a device only through this PR's CI, on the Video variant. Locally everything ran on arm64.
  • Android levels: API 35 (emulator) and API 37 (Pixel 10, 4 KB pages). Nothing below API 35 was run, and no device with 16 KB pages was available.
  • Output sizes: video files differ between the Pixel and the emulator, which have 8 and 2 cores. Each file has the expected 75 frames and resolution, and the AAC output is identical. The likely cause is the encoder's thread count, which I didn't test separately.
  • 16 KB warning on net8: Android 17 shows a compatibility dialog on the debuggable net8 sample. All 10 FFmpegKit libraries are 16 KB aligned in the net8, net9 and net10 APKs. The unaligned ones in net8 belong to .NET 8 itself (libmonosgen, libmonodroid, libSystem.*), and net9 and net10 have none. This isn't new and this binding can't fix it. net8 apps will depend on that .NET 8 runtime on 16 KB devices.

Corrections to the brief and setup

  • The SDK 8 and 9 manifests on the build Mac want net8 mono packs at 8.0.33, but 8.0.23 is installed in the root-owned /usr/local/share/dotnet. That breaks every net8 Android build there, including clean origin/main. For this run the eight verified Microsoft packs came from a private DOTNETSDK_WORKLOAD_PACK_ROOTS folder, with no machine or repo change. The real fix needs sudo, probably dotnet workload repair, which I haven't tested.
  • -t:Install on the sample needs -f <tfm>, because it uses plural TargetFrameworks. The README is corrected.
  • docs/release-notes/README.md doesn't exist, so the house style came from the existing notes.
  • Issue Upstream drift: FFmpegKit #21's text still says 8.1.8; the checker now reports 8.1.9.
  • CI's emulator job only runs Video, and the sample is never deployed there, which is how the freeze stayed hidden. Worth a separate change.

🤖 Generated with Claude Code

sbokatuk and others added 4 commits October 30, 2026 12:32
FFmpegKit 8.1.9 is the newest upstream release. Upstream reports that its
FFmpeg n8.1.3 closes two CVEs reachable in this build, and it restores
drawtext, which the earlier builds silently left out.

The version comes from the shipped libavcodec.so: n8.1.3 in all eight
variants on both ABIs, so this starts a new FFmpeg line at 8.1.3.1 rather
than continuing 8.1.2.x. The 8.1.2 row stays in the mapping, so FFmpegKit
8.1.7 remains rebuildable and the file stays append-only.

classes.jar is byte-identical to 8.1.7 and the generated api.xml matches in
all 24 variant and framework pairs, so Metadata.xml needs no change. The
8.1.9 .pom now declares smart-exception, which the comments denied; the jars
stay embedded because no .NET build reads a .pom.

Fixes two inaccuracies found on the way: the README said the sample builds
on release, which passes verify: false, and `-t:Install` needs a framework
because the sample uses plural TargetFrameworks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Running all eight variants locally failed two checks without any FFmpeg
fault, so neither says anything about the package under test:

ExternalLibraries must be non-empty, but min is built with no external
libraries. The published 8.1.2.5 Min fails identically.

The progress check stopped waiting at the first sample, which is the one at
position zero, and then asserted that position had advanced. Progress<T>
posts asynchronously, so the sample that advanced could still be queued.
That failed 2 of 9 cold starts on 8.1.3.1 and 0 of 24 on 8.1.2.5. Waiting for
an advanced sample keeps the assertion just as strict: 60 of 60 cold starts
pass, each receiving both samples, so no callback is being lost.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mono on Android restarts the threads it stopped for a garbage collection
with SIGXCPU, and FFmpeg's term_init installs its own handler for that
signal when a command runs. The restart then never reaches Mono: the next
collection leaves every managed thread suspended for good, and the app ends
in an ANR.

The example app hit this on its first conversion, on this build and on the
published 8.1.2.5 alike. Nothing caught it before because CI never deploys
the sample and the device checks allocated too little to trigger a
collection, so the new check forces collections after FFmpeg has run. It
hangs on 8.1.2.5 and on 8.1.3.1 without this change.

FFmpegKitConfig.ignoreSignal is upstream's opt-out and has to come before
the first command, so the types that can start one call it from their static
constructors. A module initializer would load the native libraries as soon
as the assembly loads, breaking apps that gate FFmpegKit at runtime, and a
failed native load is swallowed so those apps see the same error as before
instead of a TypeInitializationException.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merging this file is the release: auto-release.yml tags v8.1.3.1, and
release.yml maps that tag to FFmpegKit 8.1.9 through native-versions.tsv.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 30, 2026 09:33

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The regression test can pass without exercising the second managed thread’s stop/restart because garbage collection and shutdown may complete before the allocator task starts.

1 open issue
What changed in this PR

Updates the Android bindings to FFmpegKit 8.1.9 (FFmpeg n8.1.3) for package 8.1.3.1 and adds protection against the Mono garbage-collection freeze caused by FFmpeg taking over SIGXCPU.

Changes:

  • Adds the native version mapping and checksum baseline while preserving older mappings.
  • Introduces lazy signal protection, a garbage-collection regression check, and corrections to progress and Min-variant checks.
  • Updates release notes, version examples, and dependency documentation without changing the public API.
File Description
tests/​FFmpegKit.Android.PackageTests/​PackageLayoutTests.cs Updates comments about native filenames and runtime dependencies.
tests/​FFmpegKit.Android.DeviceTests/​SmokeTests.cs Adds the freeze regression check and adjusts progress and library assertions.
src/​FFmpegKit.Android/​Jars/​FetchJars.sh Clarifies smart-exception dependency comments.
src/​FFmpegKit.Android/​FFmpegKit.Android.csproj Updates comments explaining embedded jars.
src/​FFmpegKit.Android/​BuildNugets.sh Revises usage examples and documents local version naming.
src/​FFmpegKit.Android/​Additions/​FFmpegKit.Signals.cs Adds static initialisation to protect Mono's restart signal.
samples/​FFmpegKit.Android.Example/​FFmpegKit.Android.Example.csproj Updates version examples and explanatory comments.
README.md Documents the upgrade, freeze protection, and corrected build instructions.
docs/​release-notes/​8.1.3.1.md Records fixes, native changes, and validation results.
Directory.Build.props Sets FFmpeg 8.1.3 and FFmpegKit 8.1.9 defaults.
build/​native-versions.tsv Appends the new version pairing.
build/​checksums/​8.1.9.sha256 Adds checksums for eight native archives and two jars.
.github/​copilot-instructions.md Updates version guidance and signal-protection requirements.

Review effort: Balanced

Task.Run only queues the work, so all three collections could finish before
the allocator started. The check would then pass without any running managed
thread to stop and restart, which is the condition it exists to cover.

It still hangs on the published 8.1.2.5, which has no SIGXCPU opt-out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sbokatuk
sbokatuk merged commit 9026f9a into main Oct 30, 2026
7 checks passed
@sbokatuk
sbokatuk deleted the bump-ffmpegkit-8.1.9 branch October 30, 2026 11:38

This branch was successfully deployed

1 active deployment
nuget.org — d218ca79 Deployed Oct 30, 2026 by sbokatuk via publish beta to nuget.org #32
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.

Upstream drift: FFmpegKit

2 participants