diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 9862598..86016f1 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -6,7 +6,7 @@ - One project, `src/FFmpegKit.Android`, produces all eight packages `FFmpegKit.Net..Android` — `Audio|Full|FullGpl|Https|HttpsGpl|Min|MinGpl|Video`, selected by `FFmpegKitBuildType`. - Native binaries come from the community fork **[ffmpegkit-maintained/ffmpeg](https://github.com/ffmpegkit-maintained/ffmpeg)** via Maven Central (`dev.ffmpegkit-maintained`). `arthenica/ffmpeg-kit` is archived with its assets deleted and `ffmpeg-kit-next` ships no binaries — do not point anything back at them. -- **Two version schemes, never interchangeable.** `FFmpegVersion` (8.1.3) is the FFmpeg inside and what package versions are based on; `FFmpegKitNativeVersion` (8.1.9) is the fork release that names the `.aar`. Both live in `Directory.Build.props`; `build/native-versions.tsv` is the single source of truth for the pairing (`6.1.6↔6.0.3`, `7.1.5↔7.1.6`, `8.1.2↔8.1.7`, `8.1.3↔8.1.9`). Convert with `./build/resolve-versions.sh --ffmpeg 8.1.3` or `--ffmpegkit 8.1.9`. +- **Two version schemes, never interchangeable.** `FFmpegVersion` (8.1.3) is the FFmpeg inside and what package versions are based on; `FFmpegKitNativeVersion` (8.1.9) is the fork release that names the `.aar`. Both live in `Directory.Build.props`; `build/native-versions.tsv` is the single source of truth for the pairing (`6.1.6↔6.0.4`, `7.1.5↔7.1.7`, `8.1.2↔8.1.7`, `8.1.3↔8.1.9`). Convert with `./build/resolve-versions.sh --ffmpeg 8.1.3` or `--ffmpegkit 8.1.9`. - Package version = `.`, e.g. `8.1.3.1`. Three tracks publish in parallel; all ship `arm64-v8a` + `x86_64` at minSdk 24, never 32-bit. - Prerequisites: .NET 8, 9 and 10 SDKs each with the Android workload (the SDK is chosen by the *working directory's* `global.json`), plus Python 3 for the merge step. @@ -44,7 +44,7 @@ Everything runs on **ubuntu-latest**. `pr.yml` and `release.yml` both call the r - Pull request → pack all eight variants as `-beta..`, package tests, MAUI sample builds (net8 + net10), emulator smoke tests, then publish the betas to nuget.org. Forked PRs build and test but skip publishing. Betas cannot be deleted, only unlisted. - Merging `docs/release-notes/.md` to `main` is the release: `auto-release.yml` tags the merge `v` and dispatches `release.yml`, whose `guard` job proves the commit is an ancestor of the default branch before anything publishes. -- Tags name the **FFmpeg** version: `v6.1.6.4` publishes 6.1.6.4 and binds FFmpegKit 6.0.3 from the mapping; a version outside `build/native-versions.tsv` fails the release with the list of known builds. +- Tags name the **FFmpeg** version: `v6.1.6.5` publishes 6.1.6.5 and binds FFmpegKit 6.0.4 from the mapping; a version outside `build/native-versions.tsv` fails the release with the list of known builds. - `upstream-drift.yml` (daily) runs `build/check-upstream.sh` over `build/upstream.tsv` and files one issue per group. ## Testing diff --git a/.github/instructions/native-versions.instructions.md b/.github/instructions/native-versions.instructions.md index dd71327..b2d64b5 100644 --- a/.github/instructions/native-versions.instructions.md +++ b/.github/instructions/native-versions.instructions.md @@ -8,8 +8,8 @@ The `.aar` files come from Maven Central (`dev.ffmpegkit-maintained`) and are fe 1. **Establish the FFmpeg version the release actually contains.** Read it out of the shipped `libavcodec.so` (`jni/arm64-v8a/` inside the `.aar`), the way `.github/scripts/native-build-info.sh` does. Upstream release prose mentions other FFmpeg versions in passing and must not be used as the source. 2. **Record the checksum baseline:** `./build/update-checksums.sh ` writes `build/checksums/.sha256` (Maven Central `.sha256` sidecars where published, download-and-hash otherwise). Review the diff and commit it. This is deliberately never run in CI — CI verifies against a baseline a human recorded. -3. **Add the mapping row** to `build/native-versions.tsv`: `\t`, tab-separated, keeping the existing comment header. The file is append-only — rows that have shipped stay so their tags remain releasable. -4. **Then** bump `FFmpegVersion` and `FFmpegKitNativeVersion` in `Directory.Build.props` (both, together — they are different schemes) and fetch: `./src/FFmpegKit.Android/Jars/FetchJars.sh `. +3. **Add the mapping row** to `build/native-versions.tsv`: `\t`, tab-separated, keeping the existing comment header. There is one row per FFmpeg version, because the lookup stops at the first match and a test requires both columns to be unique. A new FFmpeg gets a new row. A respin of the same FFmpeg replaces that row: note the old pair in the header comment, keep its checksum baseline, and say in the release note that the old FFmpegKit release can no longer be built from `main`. Existing tags are unaffected, because a release reads the file as it stood at its tag. Never drop a track. +4. **Then**, if this is the default line, bump `FFmpegVersion` and `FFmpegKitNativeVersion` in `Directory.Build.props` (both, together — they are different schemes). Older lines leave the props alone. Either way, fetch: `./src/FFmpegKit.Android/Jars/FetchJars.sh `. 5. Verify and pack: `./src/FFmpegKit.Android/Jars/FetchJars.sh --verify`, then `./src/FFmpegKit.Android/BuildNugets.sh`, then `dotnet test tests/FFmpegKit.Android.PackageTests` (it asserts the mapping and the props agree). ## Rules diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4eff18d..f46c7c4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,7 +24,7 @@ on: native-version: description: > FFmpegKit native version to bind against. Defaults to FFmpegKitNativeVersion in - Directory.Build.props; set it to build a package for an older LTS line, e.g. 6.0.3. + Directory.Build.props; set it to build a package for an older LTS line, e.g. 6.0.4. required: false default: '' type: string diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 77458d6..b94818d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -55,6 +55,7 @@ jobs: version: ${{ steps.resolve.outputs.version }} native-version: ${{ steps.resolve.outputs.native-version }} ffmpeg-version: ${{ steps.resolve.outputs.ffmpeg-version }} + default-track: ${{ steps.resolve.outputs.default-track }} prerelease: ${{ steps.resolve.outputs.prerelease }} steps: - uses: actions/checkout@v4 @@ -83,6 +84,15 @@ jobs: echo "ffmpeg-version=${ffmpeg}" >> "$GITHUB_OUTPUT" echo "FFmpeg ${ffmpeg} (FFmpegKit ${ffmpegkit})" >> "$GITHUB_STEP_SUMMARY" + # FFmpegVersion in this tag's Directory.Build.props is the default track, so this + # follows the default without a second list of versions to keep up to date. + default_ffmpeg=$(sed -n 's:.*\(.*\).*:\1:p' Directory.Build.props | head -1) + if [ "${ffmpeg}" = "${default_ffmpeg}" ]; then + echo "default-track=true" >> "$GITHUB_OUTPUT" + else + echo "default-track=false" >> "$GITHUB_OUTPUT" + fi + case "${version}" in *-*) prerelease=true ;; *) prerelease=false ;; @@ -238,8 +248,14 @@ jobs: # The parent repositories pin these packages by exact version; tell them what just shipped # rather than leaving it to their daily drift check. Who cares, and about which packages, # lives in build/parents.tsv. Skips itself quietly while PARENT_ISSUE_PAT does not exist. + # + # Parents pin the default track, and notify-parents.sh keeps one open issue per package: an + # older track's release would comment on that issue and retitle it to the older version, telling + # the parent to move down. The check is here because notify-parents.* are kept byte-identical + # across the repositories, and this is the only one publishing parallel tracks on one tag prefix. notify-parents: - needs: publish + needs: [version, publish] + if: needs.version.outputs.default-track == 'true' uses: ./.github/workflows/notify-parents.yml with: tag: ${{ github.ref_name }} diff --git a/README.md b/README.md index 18c498a..077ee7e 100644 --- a/README.md +++ b/README.md @@ -40,8 +40,8 @@ The fork currently publishes three lines, each with all eight variants: | FFmpegKit | Bundled FFmpeg | ABIs | minSdk | | --- | --- | --- | --- | -| `6.0.3` | `n6.1.6` | `arm64-v8a`, `x86_64` | 24 | -| `7.1.6` | `n7.1.5` | `arm64-v8a`, `x86_64` | 24 | +| `6.0.4` | `n6.1.6` | `arm64-v8a`, `x86_64` | 24 | +| `7.1.7` | `n7.1.5` | `arm64-v8a`, `x86_64` | 24 | | `8.1.9` | `n8.1.3` | `arm64-v8a`, `x86_64` | 24 | **FFmpegKit's version is not FFmpeg's.** FFmpegKit `8.1.9` packages FFmpeg `n8.1.3`; the fork versions its own releases independently of the FFmpeg it pins. The bundled version above is read out of the shipped `libavcodec.so`, and every generated release note states it, because upstream's own notes mention other FFmpeg versions in passing and are easy to misread. @@ -52,18 +52,18 @@ Both constraints are surfaced at build time in consuming apps by a small `.targe ### Releasing an older line -Tags name the FFmpeg version: **`v6.1.6.4` publishes `6.1.6.4`** and builds against the FFmpegKit release that packages FFmpeg 6.1.6, looked up in [`build/native-versions.tsv`](build/native-versions.tsv). The fourth component is the binding revision and any prerelease suffix is ignored when resolving (`v8.1.2.4-rc.1` → FFmpeg `8.1.2`). A tag naming a version that is not in the mapping fails the release with the list of known builds, so an old-style tag like `v8.1.7.4` cannot be published by mistake. No branch or `Directory.Build.props` edit is needed. +Tags name the FFmpeg version: **`v6.1.6.5` publishes `6.1.6.5`** and builds against the FFmpegKit release that packages FFmpeg 6.1.6, looked up in [`build/native-versions.tsv`](build/native-versions.tsv). The fourth component is the binding revision and any prerelease suffix is ignored when resolving (`v8.1.2.4-rc.1` → FFmpeg `8.1.2`). A tag naming a version that is not in the mapping fails the release with the list of known builds, so an old-style tag like `v8.1.7.4` cannot be published by mistake. No branch or `Directory.Build.props` edit is needed. Locally, pass the native version as the second argument: ```sh -./src/FFmpegKit.Android/Jars/FetchJars.sh 6.0.3 # .aar files are named after the FFmpegKit release -./src/FFmpegKit.Android/BuildNugets.sh 6.1.6.4 6.0.3 # package version, FFmpegKit release +./src/FFmpegKit.Android/Jars/FetchJars.sh 6.0.4 # .aar files are named after the FFmpegKit release +./src/FFmpegKit.Android/BuildNugets.sh 6.1.6.5 6.0.4 # package version, FFmpegKit release ``` NuGet orders `8.1.3` above `6.1.6`, so publishing an older line later does not change what `dotnet add package` resolves by default. -`FFmpegKitConfig` also needs the two `smart-exception` jars at runtime. They are not bundled in the `.aar`, and its `.pom` declares them only from FFmpegKit `8.1.9` on, which does nothing for a NuGet consumer, so they are fetched separately and embedded into the binding. +`FFmpegKitConfig` also needs the two `smart-exception` jars at runtime. They are not bundled in the `.aar`, and its `.pom` declares them only from FFmpegKit `6.0.4`, `7.1.7` and `8.1.9` on, which does nothing for a NuGet consumer, so they are fetched separately and embedded into the binding. ## License @@ -111,8 +111,8 @@ Three FFmpegKit lines are published in parallel, so you can pin to the FFmpeg ge | Track | FFmpeg | From FFmpegKit | Install | | --- | --- | --- | --- | | **`8.*`** (recommended) | `n8.1.3` | 8.1.9 | `dotnet add package FFmpegKit.Net.Video.Android --version "8.*"` | -| `7.*` | `n7.1.5` | 7.1.6 | `dotnet add package FFmpegKit.Net.Video.Android --version "7.*"` | -| `6.*` | `n6.1.6` | 6.0.3 | `dotnet add package FFmpegKit.Net.Video.Android --version "6.*"` | +| `7.*` | `n7.1.5` | 7.1.7 | `dotnet add package FFmpegKit.Net.Video.Android --version "7.*"` | +| `6.*` | `n6.1.6` | 6.0.4 | `dotnet add package FFmpegKit.Net.Video.Android --version "6.*"` | A package version is **`.`** — `8.1.3.1` is FFmpeg `8.1.3`, binding revision `1`. It deliberately names FFmpeg rather than FFmpegKit, because FFmpegKit's own release numbers do not track FFmpeg: FFmpegKit `8.1.9` packages FFmpeg `n8.1.3`. [`build/native-versions.tsv`](build/native-versions.tsv) maps between the two and is what the release workflow uses to turn a tag into the `.aar` to download. diff --git a/build/checksums/6.0.4.sha256 b/build/checksums/6.0.4.sha256 new file mode 100644 index 0000000..8b394aa --- /dev/null +++ b/build/checksums/6.0.4.sha256 @@ -0,0 +1,10 @@ +54bbc7fca3f27811a9289eaceac5b94837f87bd1d97d0f98148d8cbedcfa93c1 ffmpeg-kit-audio-6.0.4.aar +e4ca28c45b8d6a17b3c002313e3d112cd5d03ede6648fbcc4f1b508bc19f27b2 ffmpeg-kit-full-6.0.4.aar +7453e9c43749cd44689d6da49dbb24b5061f74d1c8fe0858e3866a7c5daa4d2c ffmpeg-kit-full-gpl-6.0.4.aar +6b6d70012861ca22553f4114f2387e07fccb0ea54b85d9c488986be9328ee1f2 ffmpeg-kit-https-6.0.4.aar +fc9f93989188cdce5b0a6d87014c39a44bb68989e4148edfb5c02368a3f6ecce ffmpeg-kit-https-gpl-6.0.4.aar +8447360873e1d875a0d187a0abf6d26de1950e39692abdc608b141abd404e181 ffmpeg-kit-min-6.0.4.aar +619ae8c59010d1aeaf82b15bc481602ca08b6a21adc07857b875140c8b1b9bc5 ffmpeg-kit-min-gpl-6.0.4.aar +782379a68c78e5c9481dbb1db15f2d97760bc279ad2d020295e5626295dd5538 ffmpeg-kit-video-6.0.4.aar +1cad0fb4dfa01755a014331b5ed199281d2c3fab5aca5c9d7abd0b41d0ec3f7b smart-exception-common-0.2.1.jar +5b96aaa5f191dedbef72fb0c38f1a2b01807920afc0d92a75a2acd6e0cc7703c smart-exception-java-0.2.1.jar diff --git a/build/checksums/7.1.7.sha256 b/build/checksums/7.1.7.sha256 new file mode 100644 index 0000000..4ee8300 --- /dev/null +++ b/build/checksums/7.1.7.sha256 @@ -0,0 +1,10 @@ +a6cdcaf60020f1365919199c84cd5a67b65f00c1a5ffdf376b6bae1e23deaa4b ffmpeg-kit-audio-7.1.7.aar +5235d56ba510bc7302427968c720941bf6de29620f4837ee7f60ad5b8c07053d ffmpeg-kit-full-7.1.7.aar +07ebecf09e6adf1bec3b6ba589220b641919617a14be171be0e628f4b7aef88c ffmpeg-kit-full-gpl-7.1.7.aar +10e52c267079df3dbcfcb1017c56d132fd6e7f35e3e38bf7e033d258160da0ac ffmpeg-kit-https-7.1.7.aar +a14664a98b788219bde2219903f762538bdf68a3df0667b4f3ce6ffc9d89ece5 ffmpeg-kit-https-gpl-7.1.7.aar +3b7dd48c2ea980be4bc5f01c6cdfeb54121c73e814aa2d648759d1c1170a0c81 ffmpeg-kit-min-7.1.7.aar +ecd83b4b674c55bda16df2527ddf1f98d62691f5ae8b41d7fe4b2faa1364bc2f ffmpeg-kit-min-gpl-7.1.7.aar +358a5e7dc08038cd9a626a8f363dba8409f43d0e6850fe124abb2bb06d024910 ffmpeg-kit-video-7.1.7.aar +1cad0fb4dfa01755a014331b5ed199281d2c3fab5aca5c9d7abd0b41d0ec3f7b smart-exception-common-0.2.1.jar +5b96aaa5f191dedbef72fb0c38f1a2b01807920afc0d92a75a2acd6e0cc7703c smart-exception-java-0.2.1.jar diff --git a/build/native-versions.tsv b/build/native-versions.tsv index e4bc596..5456396 100644 --- a/build/native-versions.tsv +++ b/build/native-versions.tsv @@ -1,15 +1,19 @@ # Maps the FFmpeg version a build actually contains to the FFmpegKit release that packages it. # # These are two different numbering schemes and they do not track each other: FFmpegKit 8.1.7 -# packages FFmpeg n8.1.2, FFmpegKit 7.1.6 packages FFmpeg n7.1.5. Package versions here are based +# packages FFmpeg n8.1.2, FFmpegKit 7.1.7 packages FFmpeg n7.1.5. Package versions here are based # on the FFmpeg version, because that is what determines behaviour; the FFmpegKit version is only # needed to name the .aar to download. # # Every value below was read from the shipped libavcodec.so, not from upstream release prose, # which mentions other FFmpeg versions in passing and is easy to misread. # +# There is one row per FFmpeg version. When FFmpegKit respins the same FFmpeg, its row is replaced, +# so the newest respin is what gets built. Tags are unaffected: a release reads this file as it +# stood at its tag. Replaced so far: 6.1.6 was 6.0.3, 7.1.5 was 7.1.6. +# # ffmpeg ffmpegkit -6.1.6 6.0.3 -7.1.5 7.1.6 +6.1.6 6.0.4 +7.1.5 7.1.7 8.1.2 8.1.7 8.1.3 8.1.9 diff --git a/build/parents.tsv b/build/parents.tsv index 24fd550..268d345 100644 --- a/build/parents.tsv +++ b/build/parents.tsv @@ -4,7 +4,8 @@ # tag-prefix the release tag's prefix - the version follows it (v3.12.1.4 -> prefix "v") # parent owner/repo of the repository holding the pin # packages space-separated package ids the parent pins from this repository's release -# A released tag whose prefix matches no row notifies nobody. +# A released tag whose prefix matches no row notifies nobody. Neither does a release on an older +# track: the parent pins the default one, see the notify-parents job in release.yml. # # tag-prefix parent packages v sbokatuk/FFMpegKit.Net FFmpegKit.Net.Full.Android diff --git a/build/update-checksums.sh b/build/update-checksums.sh index 0dbc561..40eb124 100755 --- a/build/update-checksums.sh +++ b/build/update-checksums.sh @@ -18,7 +18,7 @@ set -e # # Usage: # build/update-checksums.sh # version from Directory.Build.props -# build/update-checksums.sh 7.1.6 # an older line, e.g. when adding a native-versions.tsv row +# build/update-checksums.sh 7.1.7 # an older line, e.g. when adding a native-versions.tsv row cd "$(dirname "$0")/.." diff --git a/docs/release-notes/6.1.6.5.md b/docs/release-notes/6.1.6.5.md new file mode 100644 index 0000000..b9f59d5 --- /dev/null +++ b/docs/release-notes/6.1.6.5.md @@ -0,0 +1,69 @@ +## What's changed + +FFmpeg stays on `n6.1.6`, now from FFmpegKit release [`6.0.4`](https://github.com/ffmpegkit-maintained/ffmpeg/releases/tag/v6.0.4-lts-android) (previously `6.0.3`). The FFmpeg version hasn't moved, so this is the next binding revision, `6.1.6.5`. If you use the `6.*` range, your next restore moves you onto it. + +Update if you use `6.1.6.4`: it has two bugs that can stop FFmpeg working in your app. + +### Fixed: FFmpeg fails to load on some Android versions + +On an arm64 Android 12 (API 31) emulator, `Min`, `MinGpl`, `Https`, `HttpsGpl` and `Video` from `6.1.6.4` could not run any FFmpeg command. The first one threw: + +``` +java.lang.UnsatisfiedLinkError: dlopen failed: can't enable GNU RELRO protection for ".../libswresample.so": Out of memory +``` + +This has nothing to do with available memory. In those builds the library's RELRO segment ends past its last loaded segment, so Android's linker tries to protect memory that isn't mapped. Upstream traces this to NDK r26c and now builds with r27c. + +All five variants now pass every device check on that emulator. `Video` from `6.1.6.4` did load on an API 35 emulator and on a Pixel 10 with Android 17, so whether you hit this depends on the Android version. Other versions were not tried. Nor was `x86_64`, where `Audio` carries the same defect as these five. + +### Fixed: the app freezes after an FFmpeg command + +An app on the default Mono runtime could freeze for good at the first garbage collection after it ran an FFmpeg command, usually ending in Android's "isn't responding" dialog. How soon that happened depended on how much the app allocated, so a small test app could run for a long time without showing it. A device check that forces collections after FFmpeg has run never finishes on `6.1.6.4`, and passes on this release. + +Mono on Android restarts the threads it stopped for a collection with `SIGXCPU`. FFmpeg installs its own `SIGXCPU` handler when a command runs, so the restart never reached Mono and those threads stayed suspended. FFmpeg also treats that signal as a request to stop. + +The packages now call `FFmpegKitConfig.IgnoreSignal(Signal.Sigxcpu)` the first time you use `FFmpegKit`, `FFmpegKitConfig` or `AsyncFFmpegExecuteTask`, so you don't need to change any code. If you already make that call yourself, it is harmless and you can remove it. Your own Java code still has to make the call if it starts FFmpeg without going through those types. + +### Security + +Upstream reports that FFmpegKit `6.0.4` backports fixes for two issues onto `n6.1.6`: + +- **CVE-2026-64830**: VobSub demuxer, unbounded writes past `vobsub->q[]`. +- **CVE-2026-64835**: ADX decoder, channel state desync. + +`FFmpegKitConfig.FFmpegVersion` reports `n6.1.6` with or without them, so check the package version instead. + +### Native changes + +- **`drawtext` is now in `Full`, `FullGpl` and `Video`.** FFmpegKit built HarfBuzz but never enabled it, so FFmpeg silently left the filter out. A command using `drawtext` failed on `6.1.6.4` with `No such filter: 'drawtext'`, and now renders text. +- **A `-filter_complex` parse error no longer kills the process**, according to upstream. This could not be confirmed here: a graph naming an unknown filter returned an error on both `6.1.6.4` and this release. +- **`Min` and `Https` now carry `libc++_shared.so`**, as the other variants already did. That adds about 1.7 MB on `arm64-v8a` and 1.5 MB on `x86_64` before compression. +- **`libc++_shared.so` is now 16 KB-aligned**, like every other library in the packages. Upstream notes that Google Play stops accepting updates without 16 KB support on 1 February 2027. + +### .NET API + +The FFmpegKit update changes nothing here: `classes.jar` is byte-identical between FFmpegKit `6.0.3` and `6.0.4` in all eight variants. + +`6.1.6.4` came out before `8.1.2.5`, so this release also picks up that release's binding changes: + +- **`FFmpegKitConfig.DisableLogCallback()`** and **`DisableStatisticsCallback()`** clear the global callbacks. +- The hand-written additions now have **nullable reference annotations**, which can produce new compiler warnings in projects with nullable checks on. +- Every package now warns at build time: **`FFMPEGKIT001`** when `SupportedOSPlatformVersion` is below 24, and **`FFMPEGKIT002`** when a build includes 32-bit runtime identifiers. You can suppress either with `NoWarn`. + +Target frameworks, `arm64-v8a` and `x86_64`, `minSdkVersion` 24 and licensing are unchanged. + +### FFmpegKit `6.0.3` can no longer be built from `main` + +FFmpegKit `6.0.4` packages the same FFmpeg, and [`build/native-versions.tsv`](https://github.com/sbokatuk/FFmpegKit.Android/blob/main/build/native-versions.tsv) has one row per FFmpeg version, so it replaces `6.0.3` there. `6.1.6.4` stays on nuget.org, but it has none of the fixes above. + +### What was tested + +Against the packed packages: + +- An arm64 API 35 emulator and a Pixel 10 with Android 17: all eight variants on `net10.0-android36.0`, plus `Full` and `Video` on `net8.0-android34.0`. Every device check passes, including the garbage collection check, and `FFmpegKitConfig.FFmpegVersion` reports `n6.1.6`. +- An arm64 API 31 emulator: `Min`, `MinGpl`, `Https`, `HttpsGpl` and `Video` on `net10.0-android36.0`. +- The example app on both of the first two devices, on `net10.0-android36.0`, `net9.0-android35.0` and `net8.0-android34.0`: media probe, resize, grayscale and audio extraction, then playback of the result. +- `drawtext` in `Video`, `Full` and `FullGpl`. +- All 190 package tests. + +`x86_64` was not run on a device. diff --git a/docs/release-notes/7.1.5.5.md b/docs/release-notes/7.1.5.5.md new file mode 100644 index 0000000..ad583cd --- /dev/null +++ b/docs/release-notes/7.1.5.5.md @@ -0,0 +1,69 @@ +## What's changed + +FFmpeg stays on `n7.1.5`, now from FFmpegKit release [`7.1.7`](https://github.com/ffmpegkit-maintained/ffmpeg/releases/tag/v7.1.7-lts-android) (previously `7.1.6`). The FFmpeg version hasn't moved, so this is the next binding revision, `7.1.5.5`. If you use the `7.*` range, your next restore moves you onto it. + +Update if you use `7.1.5.4`: it has two bugs that can stop FFmpeg working in your app. + +### Fixed: FFmpeg fails to load on some Android versions + +On an arm64 Android 12 (API 31) emulator, `Min`, `MinGpl`, `Https`, `HttpsGpl` and `Video` from `7.1.5.4` could not run any FFmpeg command. The first one threw: + +``` +java.lang.UnsatisfiedLinkError: dlopen failed: can't enable GNU RELRO protection for ".../libswresample.so": Out of memory +``` + +This has nothing to do with available memory. In those builds the library's RELRO segment ends past its last loaded segment, so Android's linker tries to protect memory that isn't mapped. Upstream traces this to NDK r26c and now builds with r27c. + +All five variants now pass every device check on that emulator. `Video` from `7.1.5.4` did load on an API 35 emulator and on a Pixel 10 with Android 17, so whether you hit this depends on the Android version. Other versions were not tried. Nor was `x86_64`, where `Audio` carries the same defect as these five. + +### Fixed: the app freezes after an FFmpeg command + +An app on the default Mono runtime could freeze for good at the first garbage collection after it ran an FFmpeg command, usually ending in Android's "isn't responding" dialog. How soon that happened depended on how much the app allocated, so a small test app could run for a long time without showing it. A device check that forces collections after FFmpeg has run never finishes on `7.1.5.4`, and passes on this release. + +Mono on Android restarts the threads it stopped for a collection with `SIGXCPU`. FFmpeg installs its own `SIGXCPU` handler when a command runs, so the restart never reached Mono and those threads stayed suspended. FFmpeg also treats that signal as a request to stop. + +The packages now call `FFmpegKitConfig.IgnoreSignal(Signal.Sigxcpu)` the first time you use `FFmpegKit`, `FFmpegKitConfig` or `AsyncFFmpegExecuteTask`, so you don't need to change any code. If you already make that call yourself, it is harmless and you can remove it. Your own Java code still has to make the call if it starts FFmpeg without going through those types. + +### Security + +Upstream reports that FFmpegKit `7.1.7` backports fixes for two issues onto `n7.1.5`: + +- **CVE-2026-64830**: VobSub demuxer, unbounded writes past `vobsub->q[]`. +- **CVE-2026-64835**: ADX decoder, channel state desync. + +`FFmpegKitConfig.FFmpegVersion` reports `n7.1.5` with or without them, so check the package version instead. + +### Native changes + +- **`drawtext` is now in `Full`, `FullGpl` and `Video`.** FFmpegKit built HarfBuzz but never enabled it, so FFmpeg silently left the filter out. A command using `drawtext` failed on `7.1.5.4` with `No such filter: 'drawtext'`, and now renders text. +- **A `-filter_complex` parse error no longer kills the process**, according to upstream. This could not be confirmed here: a graph naming an unknown filter returned an error on both `7.1.5.4` and this release. +- **`Min` and `Https` now carry `libc++_shared.so`**, as the other variants already did. That adds about 1.7 MB on `arm64-v8a` and 1.5 MB on `x86_64` before compression. +- **`libc++_shared.so` is now 16 KB-aligned**, like every other library in the packages. Upstream notes that Google Play stops accepting updates without 16 KB support on 1 February 2027. + +### .NET API + +The FFmpegKit update changes nothing here: `classes.jar` is byte-identical between FFmpegKit `7.1.6` and `7.1.7` in all eight variants. + +`7.1.5.4` came out before `8.1.2.5`, so this release also picks up that release's binding changes: + +- **`FFmpegKitConfig.DisableLogCallback()`** and **`DisableStatisticsCallback()`** clear the global callbacks. +- The hand-written additions now have **nullable reference annotations**, which can produce new compiler warnings in projects with nullable checks on. +- Every package now warns at build time: **`FFMPEGKIT001`** when `SupportedOSPlatformVersion` is below 24, and **`FFMPEGKIT002`** when a build includes 32-bit runtime identifiers. You can suppress either with `NoWarn`. + +Target frameworks, `arm64-v8a` and `x86_64`, `minSdkVersion` 24 and licensing are unchanged. + +### FFmpegKit `7.1.6` can no longer be built from `main` + +FFmpegKit `7.1.7` packages the same FFmpeg, and [`build/native-versions.tsv`](https://github.com/sbokatuk/FFmpegKit.Android/blob/main/build/native-versions.tsv) has one row per FFmpeg version, so it replaces `7.1.6` there. `7.1.5.4` stays on nuget.org, but it has none of the fixes above. + +### What was tested + +Against the packed packages: + +- An arm64 API 35 emulator and a Pixel 10 with Android 17: all eight variants on `net10.0-android36.0`, plus `Full` and `Video` on `net8.0-android34.0`. Every device check passes, including the garbage collection check, and `FFmpegKitConfig.FFmpegVersion` reports `n7.1.5`. +- An arm64 API 31 emulator: `Min`, `MinGpl`, `Https`, `HttpsGpl` and `Video` on `net10.0-android36.0`. +- The example app on both of the first two devices, on `net10.0-android36.0`, `net9.0-android35.0` and `net8.0-android34.0`: media probe, resize, grayscale and audio extraction, then playback of the result. +- `drawtext` in `Video`, `Full` and `FullGpl`. +- All 190 package tests. + +`x86_64` was not run on a device. diff --git a/src/FFmpegKit.Android/BuildNugets.sh b/src/FFmpegKit.Android/BuildNugets.sh index 2209ceb..75412c4 100755 --- a/src/FFmpegKit.Android/BuildNugets.sh +++ b/src/FFmpegKit.Android/BuildNugets.sh @@ -7,7 +7,7 @@ set -e # Usage: # ./BuildNugets.sh # version from Directory.Build.props # ./BuildNugets.sh 8.1.3.1-local.1 # explicit package version -# ./BuildNugets.sh 6.1.6.5-local.1 6.0.3 # ...and bind an older FFmpegKit LTS line +# ./BuildNugets.sh 6.1.6.5-local.1 6.0.4 # ...and bind an older FFmpegKit LTS line # # Use a -local version for anything built on a workstation. NuGet caches by id and version, so a # real version number packed here would shadow the published package in the global cache. diff --git a/src/FFmpegKit.Android/FFmpegKit.Android.csproj b/src/FFmpegKit.Android/FFmpegKit.Android.csproj index acba88c..35d9ea2 100644 --- a/src/FFmpegKit.Android/FFmpegKit.Android.csproj +++ b/src/FFmpegKit.Android/FFmpegKit.Android.csproj @@ -89,8 +89,8 @@ diff --git a/src/FFmpegKit.Android/Jars/FetchJars.sh b/src/FFmpegKit.Android/Jars/FetchJars.sh index 3cd859e..5ba6256 100755 --- a/src/FFmpegKit.Android/Jars/FetchJars.sh +++ b/src/FFmpegKit.Android/Jars/FetchJars.sh @@ -134,9 +134,9 @@ STAGING=$(mktemp -d) trap 'rm -rf "$STAGING"' EXIT # FFmpegKitConfig's static initialiser calls com.arthenica.smartexception.java.Exceptions, which -# is not bundled in the .aar. Its .pom declares it only from FFmpegKit 8.1.9 on, and no .NET -# build reads that .pom. Without these two jars every FFmpeg call fails at runtime with -# NoClassDefFoundError. +# is not bundled in the .aar. Its .pom declares it only from FFmpegKit 6.0.4, 7.1.7 and 8.1.9 on, +# and no .NET build reads that .pom. Without these two jars every FFmpeg call fails at runtime +# with NoClassDefFoundError. for artifact in common java; do curl -fL --retry 3 --retry-delay 2 --output-dir "$STAGING" -O \ "https://github.com/tanersener/smart-exception/releases/download/v$SMART_EXCEPTION_VERSION/smart-exception-$artifact-$SMART_EXCEPTION_VERSION.jar" diff --git a/tests/FFmpegKit.Android.PackageTests/NativeVersionMapTests.cs b/tests/FFmpegKit.Android.PackageTests/NativeVersionMapTests.cs index dabd788..62b66bc 100644 --- a/tests/FFmpegKit.Android.PackageTests/NativeVersionMapTests.cs +++ b/tests/FFmpegKit.Android.PackageTests/NativeVersionMapTests.cs @@ -46,8 +46,9 @@ private static string ReadProperty(string name) [Fact] public void Mapping_lists_every_supported_build() { - // Append-only: lines that have shipped stay in the mapping so their tags remain - // releasable. New rows are expected over time, so this is a floor, not an exact count. + // One row per FFmpeg version: a respin of the same FFmpeg replaces its row, but a track + // is never dropped. New rows are expected over time, so this is a floor, not an exact + // count. Assert.True(Mapping.Count >= 3, $"native-versions.tsv lists {Mapping.Count} rows; the 6.x/7.x/8.x lines must not be removed."); }