FFmpegKit.Net.HttpsGpl.iOS
8.1.2.3
dotnet add package FFmpegKit.Net.HttpsGpl.iOS --version 8.1.2.3
NuGet\Install-Package FFmpegKit.Net.HttpsGpl.iOS -Version 8.1.2.3
<PackageReference Include="FFmpegKit.Net.HttpsGpl.iOS" Version="8.1.2.3" />
<PackageVersion Include="FFmpegKit.Net.HttpsGpl.iOS" Version="8.1.2.3" />
<PackageReference Include="FFmpegKit.Net.HttpsGpl.iOS" />
paket add FFmpegKit.Net.HttpsGpl.iOS --version 8.1.2.3
#r "nuget: FFmpegKit.Net.HttpsGpl.iOS, 8.1.2.3"
#:package FFmpegKit.Net.HttpsGpl.iOS@8.1.2.3
#addin nuget:?package=FFmpegKit.Net.HttpsGpl.iOS&version=8.1.2.3
#tool nuget:?package=FFmpegKit.Net.HttpsGpl.iOS&version=8.1.2.3
FFmpegKit.iOS
.NET for iOS and .NET MAUI bindings for the native FFmpegKit library.
GitHub reports this repository as MIT because that is what it contains: binding source only, no native binaries. The published packages are not MIT — they embed native FFmpeg builds and are additionally covered by LGPL-3.0, or GPL-3.0 for the
-gplvariants. See License.
Built against the prebuilt Apple binaries from sk3llo/ffmpeg_kit_flutter — see Where the native binaries come from for why that fork and not the original.
About
This repository contains .NET bindings on top of the FFmpegKit .xcframework build for iOS. One project, src/FFmpegKit.iOS, produces all eight package variants — the variant is selected with the FFmpegKitBuildType MSBuild property.
Packages target net8.0-ios18.0, net9.0-ios18.0 and net10.0-ios26.0.
Each .NET SDK's iOS workload supports only two target frameworks — the .NET 9 band ships net8 and net9, the .NET 10 band ships net9 and net10 — so no single dotnet pack can produce all three. BuildNugets.sh packs once per band and build/merge-packages.py merges the lib/ trees and nuspec dependency groups into one package per variant.
Where the native binaries come from
FFmpegKit has several relevant repositories, and only one of them still ships usable iOS binaries:
| Repository | State | Prebuilt iOS .xcframework |
|---|---|---|
arthenica/ffmpeg-kit |
archived | none — every release now carries zero assets |
arthenica/ffmpeg-kit-next |
active, the official continuation | none — source only |
ffmpegkit-maintained/ffmpeg |
active community fork | none — Android .aar only, nothing for Apple platforms |
ffmpegkit-maintained/ffmpeg-kit-ios-full |
stale | yes, but FFmpeg 6.0, full-gpl only, no releases or tags to pin |
sk3llo/ffmpeg_kit_flutter |
active | yes — all eight variants, currently 8.1.2 |
Note that this is not the same source as the Android bindings use: FFmpegKit.Android takes its .aar files from ffmpegkit-maintained/ffmpeg via Maven Central, and that fork does not build for Apple platforms.
Releases there are tagged <version>-<variant> and carry each xcframework as a separate zip plus a checksums.json. FetchXcFrameworks.sh downloads all eight and verifies every one against that manifest — these are tens of megabytes of native code that gets linked into your app, so a truncated or substituted archive fails the build rather than shipping.
The version is set by FFmpegKitNativeVersion in Directory.Build.props, which FetchXcFrameworks.sh reads, so the download and the frameworks the project expects cannot drift apart.
The fork currently publishes four FFmpeg lines, each with all eight variants:
| FFmpeg |
|---|
7.1.1 |
8.0.0 |
8.1.1 |
8.1.2 |
Each carries a device slice and a simulator slice. The exact architectures are upstream's to decide and have changed within a version: 8.1.2 shipped a device slice of arm64 + arm64e and was later rebuilt as arm64 alone. The package tests therefore assert the shape — one device slice, one simulator slice, both iOS, no macOS — rather than specific names.
Upstream also ships a macOS slice in each xcframework. It is stripped on download: it cannot be reached from a net*-ios binding, but it would still be embedded in the package once per target framework. Keeping it would push the FullGpl package past nuget.org's 250 MB limit. If you need macOS or Mac Catalyst, note that no Mac Catalyst slice is published at all, so that would need a different source.
Versioning
Package versions are <ffmpeg version>.<binding revision>:
8.1.2.1
└───┬─┘ └┬┘
│ └─ binding revision — this repository
└────── FFmpeg version — the native build inside the package
The first three components name the FFmpeg build the package contains, which is also the version FetchXcFrameworks.sh downloads and what upstream tags its releases with. The fourth belongs to this repository and increments whenever the bindings or packaging change while the native binaries stay put — 8.1.2.1 and 8.1.2.2 are the same FFmpeg with different bindings.
A floating range such as 8.1.2.* therefore always resolves to the newest bindings for that exact FFmpeg build and never crosses onto another one. Pin an exact version instead if you would rather approve every binding update yourself.
The Android bindings use the same scheme, and currently track the same FFmpeg line —
8.1.2on both. The binding revisions advance independently, so the fourth component will differ between the two. They also wrap different upstream FFmpegKit builds, so the APIs are not identical.
Releasing an older line
The tag selects the FFmpeg line: the first three components of v7.1.1.1 are the FFmpeg version to build against, so that tag binds FFmpeg 7.1.1 and publishes 7.1.1.1 packages. The fourth component is the binding revision and does not affect which native build is fetched (v8.1.2.6 → FFmpeg 8.1.2), and a prerelease suffix is ignored too (v8.1.2.1-beta.1 → FFmpeg 8.1.2). No branch or Directory.Build.props edit is needed.
Locally, pass the native version as the second argument:
./build/FetchXcFrameworks.sh 7.1.1 # fetch that line's xcframeworks
./build/BuildNugets.sh 7.1.1 7.1.1 # package version, native version
License
This section describes what the upstream project states. It is not legal advice — if the distinction matters for your product, get it reviewed.
The C# binding code in this repository is MIT. The published NuGet packages are not, because each one embeds native FFmpeg binaries that carry their own copyleft terms. Each package therefore declares MIT AND <native license>:
| Package | Native license | SPDX expression |
|---|---|---|
FFmpegKit.Net.Audio.iOS |
LGPL-3.0 | MIT AND LGPL-3.0-only |
FFmpegKit.Net.Full.iOS |
LGPL-3.0 | MIT AND LGPL-3.0-only |
FFmpegKit.Net.Https.iOS |
LGPL-3.0 | MIT AND LGPL-3.0-only |
FFmpegKit.Net.Min.iOS |
LGPL-3.0 | MIT AND LGPL-3.0-only |
FFmpegKit.Net.Video.iOS |
LGPL-3.0 | MIT AND LGPL-3.0-only |
FFmpegKit.Net.FullGpl.iOS |
GPL-3.0 | MIT AND GPL-3.0-only |
FFmpegKit.Net.HttpsGpl.iOS |
GPL-3.0 | MIT AND GPL-3.0-only |
FFmpegKit.Net.MinGpl.iOS |
GPL-3.0 | MIT AND GPL-3.0-only |
The -gpl variants enable x264, x265, xvid and vidstab, which are GPL — upstream keeps them as separate artifacts specifically so they never contaminate the LGPL ones. Upstream's guidance is direct: if your app is closed-source, use a non-GPL variant.
Upstream states version 3.0 with no "or later" wording, hence the -only SPDX identifiers.
Every package ships the texts it is covered by under licenses/ — LICENSE (MIT, the bindings) and LGPL-3.0.txt or GPL-3.0.txt (the native binaries). The same texts are in this repository under licenses/.
The package tests assert this rather than trusting it: every variant is checked for the presence or absence of x264 in its libavcodec, so a GPL build packed under an LGPL licence expression fails the build.
Installation
Install the package via NuGet. There are various packages depending on what you plan to use and if you require a GPL compatible package or not. These package variants match the different variants built in the FFmpegKit repository. The -gpl variants are GPL-3.0 — see License before choosing one.
A package version is its FFmpeg version plus a binding revision — see Versioning. 8.1.2.* floats to the newest bindings for FFmpeg 8.1.2 without ever crossing onto another FFmpeg build.
Migrating from FFmpegKit.FullGpl.iOS / FFmpegKit.Video.iOS
These packages replace the older FFmpegKit.<Variant>.iOS ones. Change the package id:
-<PackageReference Include="FFmpegKit.FullGpl.iOS" Version="4.5.1-beta2" />
+<PackageReference Include="FFmpegKit.Net.FullGpl.iOS" Version="8.1.2" />
The Ffmpegkit.Ios namespace is unchanged, so your using directives and calls stay as they are. It deliberately does not follow the package name: a namespace rooted at FFmpegKit containing a type also called FFmpegKit makes FFmpegKit.Execute(...) resolve the namespace instead of the class and fail to compile.
The assembly is now FFmpegKit.Net.<Variant>.iOS, which matters only if you reference it by assembly name or use reflection.
Two things to be aware of when upgrading:
- The old packages declared the version as
4.5.1but actually contained an FFmpeg 6.0 build, and declared their licence asMITwhile shipping GPL binaries. Both are corrected here — see License. Your obligations have not changed; the metadata was simply wrong. - The bound API surface is much larger now. The previous binding was generated from
FFmpegKit.halone, which does not transitively include most of the API, soFFmpegKitConfig,FFprobeKit,MediaInformationand friends were missing entirely.
Usage
Include the Ffmpegkit.Ios namespace:
using Ffmpegkit.Ios;
Execute your FFmpeg command:
var session = await FFmpegKit.ExecuteAsync("-i input.mov -c:v libx264 output.mp4");
if (session.Succeeded())
Console.WriteLine("done");
ExecuteAsync wraps FFmpegKit's own asynchronous path, so nothing blocks the calling thread. Pass a CancellationToken to stop a running command — the session then completes with a cancelled return code rather than throwing. A synchronous FFmpegKit.Execute is also bound, but it blocks for the whole transcode, which on the UI thread means a frozen app.
Probing works the same way:
var probe = await FFprobeKit.GetMediaInformationAsync(path);
Console.WriteLine(probe.MediaInformation?.Format);
Global log and statistics hooks take lambdas, and are cleared by name:
FFmpegKitConfig.EnableLogCallback(log => Console.WriteLine(log.Message));
FFmpegKitConfig.DisableLogCallback(); // likewise DisableStatisticsCallback()
Both callbacks arrive on an FFmpegKit worker thread — marshal before touching UI — and are held by FFmpegKit until cleared, so anything they capture stays alive too.
More examples and usage can be found in the original FFmpegKit wiki. That repository is archived, but the Objective-C API it documents is the one these bindings expose, so it remains the reference.
Troubleshooting
Crash on a real device: Could not find the type 'ObjCRuntime.__Registrar__'. From 8.1.2.3 the package handles this for you — you should not see it. It happens because .NET 9 made the managed static registrar the device default, and that registrar does not emit the per-assembly __Registrar__ lookup type for this binding, so the first call returning a bound Objective-C object (for example FFprobeKit.GetMediaInformationAsync, which returns a MediaInformationSession) crashes at load. It is the .NET SDK issue dotnet/macios#22071, not a fault in the binding, and the simulator never hits it because the simulator/interpreter path uses a different registrar — which is why it can pass every simulator test and still crash on a phone.
The package ships a buildTransitive target that defaults consuming apps (including apps that reference it only through FFmpegKit.Net.Full.Maui) to Registrar=dynamic, which resolves classes by reflection and sidesteps the gap. To override it, set <Registrar> yourself in the app project:
- NativeAOT (
PublishAot=true) requires the managed static registrar, so a NativeAOT app must setRegistrarexplicitly; this particular crash remains until the upstream issue is fixed. Registrar=static(the classic static registrar) also avoids the crash and is lighter at runtime thandynamic; it is a reasonable override once verified on a device.
partial-static, which the macOS binding defaults to for the equivalent failure, is not usable on iOS — it produces duplicate-symbol link errors against Microsoft.iOS.registrar.a.
Building
Prerequisites
macOS with Xcode, and the .NET 9 and 10 SDKs each with the iOS workload installed — every band supplies a different reference pack. The SDK is chosen by the global.json in the working directory, so install each band from a directory pinned to it:
for major in 9 10; do
dir=$(mktemp -d) && cd "$dir"
dotnet new globaljson --sdk-version "$(dotnet --list-sdks | grep "^${major}\." | tail -1 | cut -d' ' -f1)" --force
dotnet workload install ios
done
Python 3 is also needed, for the xcframework slice stripping and the package merge step.
All variants
./build/FetchXcFrameworks.sh # downloads the xcframeworks (~1 GB for all eight)
./build/BuildNugets.sh # packs all 8 variants into ./artifacts
./build/BuildNugets.sh 8.1.2-rc.1 # ...or with an explicit package version
FetchXcFrameworks.sh reads the FFmpegKit version from FFmpegKitNativeVersion in Directory.Build.props, the same property the .csproj uses to locate the frameworks, so the two cannot drift apart. Pass a version to override it, and a variant to fetch just one:
./build/FetchXcFrameworks.sh 8.1.2 Video
A single variant
# net8 + net9 assets (.NET 9 SDK, per global.json)
dotnet pack src/FFmpegKit.iOS/FFmpegKit.iOS.csproj \
-c Release -p:FFmpegKitBuildType=Video -p:FFmpegKitSdkBand=net9 -o artifacts
FFmpegKitBuildType is one of Audio, Full, FullGpl, Https, HttpsGpl, Min, MinGpl, Video. FFmpegKitSdkBand is net9 or net10 and must match the SDK actually running the build. Each variant builds into its own obj/ and bin/ subdirectory, so they can be built in sequence without interfering with each other.
Regenerating the binding
Only needed when bumping to a newer native FFmpegKit version. The binding is generated with Objective Sharpie from the vendored frameworks' public headers:
# Stage the public headers from the device slice
mkdir -p Headers
cp -R src/FFmpegKit.iOS/libs/Video/ffmpegkit.xcframework/ios-arm64_arm64e/ffmpegkit.framework/Headers/* Headers/
# Sharpie must be pointed at an umbrella header. FFmpegKit.h alone only pulls in a fraction of
# the API - binding just that is how the previous binding ended up missing FFmpegKitConfig,
# FFprobeKit and the MediaInformation types.
ls Headers/*.h | grep -v fftools | grep -v ffmpegkit_exception \
| sed 's|Headers/|#import "|; s|$|"|' > Headers/FFmpegKitUmbrella.h
sharpie bind -output Binding -sdk iphoneos26.5 -scope Headers Headers/FFmpegKitUmbrella.h -c -I Headers
Then reconcile Binding/ApiDefinitions.cs and Binding/StructsAndEnums.cs into src/FFmpegKit.iOS/ApiDefinition.cs and src/FFmpegKit.iOS/Structs.cs. Every [Verify] attribute sharpie emits must be reviewed and removed — they intentionally cause build failures. Note that sharpie emits the Level enum as ulong despite its negative members; it has to be long.
Tests
Package tests run anywhere and inspect the packed .nupkg files — assembly present for every target framework, all eight xcframeworks with iOS device and simulator slices only, manifests consistent with the slices actually shipped, the GPL/LGPL split matching what the binaries contain, and nuspec metadata:
./build/BuildNugets.sh # produce ./artifacts first
dotnet test tests/FFmpegKit.iOS.PackageTests
FFMPEGKIT_VARIANTS=Video dotnet test tests/FFmpegKit.iOS.PackageTests # ...or just one variant
Simulator smoke tests build an app against the packed package and run real FFmpeg commands on a booted simulator, which is the only way to prove the native frameworks actually link and load:
./.github/scripts/run-simulator-tests.sh Video 8.1.2 net10.0-ios26.0
CI
| Workflow | Trigger | What it does |
|---|---|---|
pr.yml |
pull request | Builds and packs all 8 variants as <version>-beta.<pr>.<run>, runs package tests and the simulator smoke tests (net8 and net10 legs in parallel), then publishes the betas to nuget.org. Forked PRs build and test but skip publishing, since they cannot read secrets. |
release.yml |
tag v* |
Same build and tests at the tag's version, publishes to nuget.org, then creates a GitHub release with the changelog since the previous tag and links to every package. |
Both call the reusable build.yml, which runs on macOS — iOS builds have no cross-platform path, unlike the Android bindings.
Note that prereleases pushed to nuget.org cannot be deleted, only unlisted — every pull request push publishes eight packages.
Package size
The -gpl variants are large: FullGpl packs to roughly 230 MB against nuget.org's 250 MB limit, because the native payload is embedded once per target framework. If a future FFmpegKit release grows the binaries enough to cross that line, the options are to drop the (end-of-life) net8.0-ios target, or to thin the simulator slice to arm64 only — which costs iOS Simulator support for anyone still on an Intel Mac.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0-ios18.0 is compatible. net9.0-ios was computed. net9.0-ios18.0 is compatible. net10.0-ios was computed. net10.0-ios26.0 is compatible. |
-
net10.0-ios26.0
- No dependencies.
-
net8.0-ios18.0
- No dependencies.
-
net9.0-ios18.0
- No dependencies.
NuGet packages (1)
Showing the top 1 NuGet packages that depend on FFmpegKit.Net.HttpsGpl.iOS:
| Package | Downloads |
|---|---|
|
FFmpegKit.Net.HttpsGpl
One API over the FFmpegKit bindings for Android, iOS and macOS: run FFmpeg/FFprobe commands with awaitable calls, typed media information, progress reporting, failure output on the result and an injectable IFFmpegKit, instead of writing a per-platform adapter over Ffmpegkit.Droid, Ffmpegkit.Ios and Ffmpegkit.Mac by hand. Pulls in the platform binding that matches whichever target framework you build. For .NET MAUI apps, FFmpegKit.Net.Maui adds the app-builder wiring. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 8.1.2.3 | 46 | 7/28/2026 |
| 8.1.2.3-beta.12.28 | 30 | 7/30/2026 |
| 8.1.2.3-beta.11.25 | 35 | 7/30/2026 |
| 8.1.2.3-beta.10.24 | 43 | 7/29/2026 |
| 8.1.2.3-beta.9.23 | 34 | 7/28/2026 |
| 8.1.2.3-beta.8.22 | 37 | 7/28/2026 |
| 8.1.2.2 | 103 | 7/24/2026 |
| 8.1.2.2-beta.7.21 | 40 | 7/26/2026 |
| 8.1.2.2-beta.6.19 | 49 | 7/24/2026 |
| 8.1.2.2-beta.6.17 | 46 | 7/24/2026 |
| 8.1.2.1 | 103 | 7/22/2026 |
| 8.1.2.1-beta.6.16 | 52 | 7/24/2026 |
| 8.1.2.1-beta.5.15 | 49 | 7/23/2026 |
| 8.1.2.1-beta.5.14 | 42 | 7/23/2026 |
| 8.1.2.1-beta.4.13 | 52 | 7/22/2026 |
| 8.1.2.1-beta.3.12 | 50 | 7/22/2026 |