Skip to content
Taner Sener edited this page Jul 28, 2026 · 2 revisions

1. Use argument arrays when command values can contain spaces

FFmpegKit.execute(), FFmpegKit.executeAsync(), FFprobeKit.execute() and FFprobeKit.executeAsync() accept a single command string. FFmpegKitNext parses that string with FFmpegKitConfig.parseArguments(): spaces split arguments, and single or double quotes keep a value together.

This works when you quote paths or filter expressions correctly.

FFmpegKit.execute("-i '/storage/emulated/0/My Videos/input file.mp4' -c:v mpeg4 '/storage/emulated/0/My Videos/output file.mp4'");

For generated commands, user-selected file names, filter graphs, JSON values or any value that may contain quotes or spaces, prefer the argument-array APIs. They avoid command-string quoting mistakes.

FFmpegKit.executeWithArguments(new String[] {
    "-i", inputPath,
    "-c:v", "mpeg4",
    outputPath
});
await FFmpegKit.executeWithArguments([
  '-i', inputPath,
  '-c:v', 'mpeg4',
  outputPath,
]);

Do not put shell syntax such as pipes, redirects, command substitution or environment assignments into an execute() command string. FFmpegKitNext passes arguments to FFmpeg/FFprobe; it does not run the command through a shell.


2. Build and integrate local artifacts

FFmpegKitNext does not publish ready-to-use packages to Maven Central, CocoaPods, pub.dev or npm. Build the native artifacts locally, then integrate the generated files from prebuilt.

For a native Android app, consume the local Maven repository produced by the Android build. Match the path to the API level you built.

repositories {
    maven { url "<ffmpeg-kit-next>/prebuilt/bundle-android-aar-24-maven" }
    // Resolves transitive dependencies declared by the local POM.
    mavenCentral()
}

dependencies {
    implementation "com.arthenica:ffmpeg-kit-next:<version>"
}

For Apple platforms, use the generated frameworks, xcframeworks or local Swift package from prebuilt. There are no hosted CocoaPods packages to install.


3. Flutter and React Native packages are local packages

ffmpeg_kit_next_flutter is not published to pub.dev. Use a local path dependency after building and copying the native binaries into the plugin.

dependencies:
  ffmpeg_kit_next_flutter:
    path: ../ffmpeg-kit-next/flutter/flutter

Flutter Android also needs the plugin's local Maven repository in your Flutter app's project-level android/build.gradle.

allprojects {
    repositories {
        google()
        mavenCentral()
        def ffmpegKitProject = rootProject.findProject(":ffmpeg_kit_next_flutter")
        if (ffmpegKitProject != null) {
            maven {
                url "${ffmpegKitProject.projectDir}/libs-maven"
            }
        }
    }
}

ffmpeg-kit-next-react-native is not published to npm. Use a local file dependency.

yarn add file:../ffmpeg-kit-next/react-native

or:

npm install ../ffmpeg-kit-next/react-native

React Native Android also needs the plugin's local Maven repository in your app's android/build.gradle.

allprojects {
    repositories {
        def ffmpegKitProject = rootProject.findProject(":ffmpeg-kit-next-react-native")
        if (ffmpegKitProject != null) {
            maven {
                url "${ffmpegKitProject.projectDir}/libs-maven"
            }
        }
    }
}

On iOS, iPadOS and macOS, Flutter and React Native use the local plugin podspecs and copied local frameworks. They do not need an extra Maven-style repository step.


4. Enable FFmpeg features in the build

Filters, encoders, decoders, protocols and external-library-backed formats are decided when FFmpegKitNext is built. Application code cannot enable a missing FFmpeg feature at runtime.

Use --enable-<library> options or --full while building. Use --enable-gpl when you intentionally enable GPL-licensed libraries.

Nix wrappers are the recommended entry points because they provide the build environment:

./nix-android.sh -p android-r27d --enable-libass --enable-freetype --enable-fontconfig
./nix-ios.sh -p xcode26 --enable-libass --enable-freetype --enable-fontconfig

Calling the direct platform scripts is still supported when you provide the required SDK, toolchain and host tools yourself:

./scripts/start-android.sh --enable-libass --enable-freetype --enable-fontconfig
./scripts/start-ios.sh --enable-libass --enable-freetype --enable-fontconfig

Linux built-in feature options use linux- names, for example:

./nix-linux.sh -p linux-x86_64-glibc-2_40 --enable-linux-libass --enable-linux-freetype --enable-linux-fontconfig

At runtime, check what your binary actually contains with Packages.getExternalLibraries() rather than assuming a package variant.

Log.d(TAG, Packages.getExternalLibraries().toString());
final libraries = await Packages.getExternalLibraries();

5. Burning subtitles requires both the right build and fonts

The subtitles filter depends on libass. Build with --enable-libass when you want to burn subtitle files into video. In the build scripts, enabling libass also enables the font-related dependencies it needs, including fontconfig, freetype, fribidi and harfbuzz.

Android does not provide a default fontconfig setup for applications. If fonts are not registered, subtitle rendering may fail or may complete without drawing the expected text. Register system fonts and any custom font directory before running the command.

FFmpegKitConfig.setFontDirectoryList(
    context,
    Arrays.asList("/system/fonts", customFontDirectory),
    Collections.emptyMap()
);

If a subtitle file requests a font family that is not present on the device, either include that font with your app or provide a font-name mapping in the third argument of setFontDirectory() or setFontDirectoryList().


6. drawtext needs freetype and libharfbuzz; family lookup needs fontconfig

If a command fails with:

No such filter: 'drawtext'

your FFmpeg binary was built without the libraries required by the drawtext filter. Rebuild with freetype and harfbuzz enabled.

./nix-android.sh -p android-r27d --enable-freetype --enable-harfbuzz
./scripts/start-android.sh --enable-freetype --enable-harfbuzz

If the filter exists but font family lookup fails with an error like:

Cannot find a valid font for the family Sans
Error initializing filter 'drawtext'

then enable fontconfig in the build and register a font directory with FFmpegKitConfig.setFontDirectory() or FFmpegKitConfig.setFontDirectoryList().

You can also use an explicit font file when that is simpler. This still requires freetype and harfbuzz, but it avoids font-family lookup.

-vf "drawtext=fontfile=/system/fonts/Roboto-Regular.ttf:text='Hello world':x=10:y=10"

7. Common system font directories

On Android, system fonts are usually under:

/system/fonts

On Apple platforms, system fonts are commonly under:

/System/Library/Fonts
/System/Library/Fonts/Cache

On macOS, application sandboxing and file permissions may affect access to additional locations such as /Library/Fonts or ~/Library/Fonts. For bundled app fonts, register the directory inside your application bundle instead of relying on user font folders.

Examples:

[FFmpegKitConfig setFontDirectoryList:@[
    @"/System/Library/Fonts",
    @"/System/Library/Fonts/Cache"
] with:@{}];
FFmpegKitConfig::setFontDirectoryList(
    std::list<std::string>{"/usr/share/fonts"},
    std::map<std::string, std::string>()
);

The font directory helpers require a build with fontconfig support. They generate a temporary fonts.conf file, include a cachedir entry for Fontconfig cache files, and set FONTCONFIG_PATH for the native FFmpeg process.


8. Use SAF URLs for Android content:// Uris

Android document picker Uris are content:// Uris, not normal file paths. Convert them to ffkitsaf: URLs before passing them to FFmpegKit or FFprobeKit.

String safUrl = FFmpegKitConfig.getSafParameterForRead(context, uri, true);
FFmpegKit.executeAsync(String.format("-i %s -c:v mpeg4 file2.mp4", safUrl), session -> {
    FFmpegKitConfig.unregisterSafProtocolUrl(safUrl);
});

For output files created with ACTION_CREATE_DOCUMENT, use getSafParameterForWrite().

String safUrl = FFmpegKitConfig.getSafParameterForWrite(context, uri);
FFmpegKit.executeAsync(String.format("-i file1.mp4 -c:v mpeg4 %s", safUrl));

By default, a generated SAF URL is single-use and is released automatically when the file is closed. Pass reusable=true only when you need to use the same URL in more than one command, and then release it manually with unregisterSafProtocolUrl().

Flutter and React Native expose the same SAF helpers, but they are Android-only. Calling them on other platforms fails.


9. Do not disable FFmpegKit protocols if you need them

FFmpegKitNext adds custom protocols on top of FFmpeg:

  • Android: ffkitsaf:, ffkitmem: and ffkitstream:
  • Apple, Linux and Web: ffkitmem: and ffkitstream:

The build option --no-ffmpeg-kit-protocols disables these protocols. If a command fails with a protocol error for ffkitsaf, ffkitmem or ffkitstream, make sure your binary was not built with --no-ffmpeg-kit-protocols.


10. Android libc++_shared.so duplicate errors

The Android AAR contains native libraries that may include libc++_shared.so. If another dependency also packages it, Gradle can fail with an error similar to:

More than one file was found with OS independent path 'lib/x86/libc++_shared.so'

With recent Android Gradle Plugin versions, use the packaging.jniLibs.pickFirsts DSL:

android {
    packaging {
        jniLibs {
            pickFirsts += [
                "lib/armeabi-v7a/libc++_shared.so",
                "lib/arm64-v8a/libc++_shared.so",
                "lib/x86/libc++_shared.so",
                "lib/x86_64/libc++_shared.so"
            ]
        }
    }
}

Older projects may still use:

android {
    packagingOptions {
        pickFirst "lib/armeabi-v7a/libc++_shared.so"
        pickFirst "lib/arm64-v8a/libc++_shared.so"
        pickFirst "lib/x86/libc++_shared.so"
        pickFirst "lib/x86_64/libc++_shared.so"
    }
}

This resolves the packaging conflict only. It does not prove that all native dependencies were built against compatible C++ runtime expectations, so test every ABI you ship.


11. Match Apple deployment targets

Current FFmpegKitNext Apple artifacts support these minimum deployment targets:

  • iOS and iPadOS: 12.1
  • macOS: 10.15
  • tvOS: 11.0
  • visionOS: 1.0

If an application target is lower than the local artifact or plugin podspec, CocoaPods, Swift Package Manager or Xcode can fail with a higher-minimum-deployment-target error. Raise the app target to at least the supported value.

For Flutter or CocoaPods-based iOS apps:

platform :ios, '12.1'

For macOS CocoaPods targets:

platform :osx, '10.15'

When building your own Apple artifacts, you can override deployment target values with script options such as --target and --mac-catalyst-target, but do not go below the supported platform floor.

./nix-ios.sh -p xcode26 --target=12.1 --mac-catalyst-target=12.1
./nix-macos.sh -p xcode26 --target=10.15
./nix-tvos.sh -p xcode26 --target=11.0
./nix-visionos.sh -p xcode26 --target=1.0

12. PNG files in Apple application bundles

Xcode can optimize PNG files that are included in an application bundle. In some cases, the optimized resource cannot be decoded by FFmpeg's libpng support and the command fails with:

Error while decoding stream #0:0: Generic error in an external library

If this happens for PNG files bundled with the app, open the app target's Build Settings in Xcode and disable these Packaging settings:

  • Compress PNG Files
  • Remove Text Metadata From PNG Files

These settings may be hidden until the target contains at least one .png file. This tip applies to resources processed by Xcode in the app bundle, not to arbitrary PNG files downloaded or created at runtime.


13. Ignore SIGXCPU when using Mono-based frameworks

Mono-based frameworks such as Unity and Xamarin may need SIGXCPU handling disabled inside FFmpegKitNext.

Android:

FFmpegKitConfig.ignoreSignal(Signal.SIGXCPU);

Apple:

[FFmpegKitConfig ignoreSignal:SIGXCPU];

Flutter:

await FFmpegKitConfig.ignoreSignal(Signal.sigXCpu);

React Native:

await FFmpegKitConfig.ignoreSignal(Signal.SIGXCPU);

14. UDP and RTSP transport warnings

Current Android build scripts enable pthreads for FFmpeg. Therefore, this old message should not appear with a current Android binary built by the provided scripts:

'circular_buffer_size' option was set but it is not supported on this build (pthread support is required)

If you see it, you are likely running an old or custom FFmpeg binary built without pthread support. Rebuild from current FFmpegKitNext sources. For RTSP streams, you can also force TCP transport when UDP is unreliable or blocked by the network:

-rtsp_transport tcp

15. Do not disable redirection just to hide logs

Log and statistics redirection is enabled by default. With redirection enabled, sessions collect logs/statistics and callbacks work.

FFmpegKitConfig.disableRedirection() has broader side effects: logs are printed to stderr, log/statistics callbacks are disabled, and FFprobe media information helpers do not work. Use it only when you intentionally want those behavior changes.

If you only want quieter output, keep redirection enabled and either lower FFmpeg's log level:

-loglevel error

or change the log redirection strategy. For example, on Android:

FFmpegKitConfig.setLogRedirectionStrategy(LogRedirectionStrategy.NEVER_PRINT_LOGS);

The equivalent enum names are LogRedirectionStrategyNeverPrintLogs on Apple, LogRedirectionStrategy.neverPrintLogs on Flutter and LogRedirectionStrategy.NEVER_PRINT_LOGS on React Native.


16. Check build.log for local build failures

The console often shows only the high-level failing step. The detailed configure, compiler, linker and pkg-config output is written to build.log in the project root. When an external library fails to build or FFmpeg says a library was not found, inspect build.log before changing build options.

Clone this wiki locally