Panama FFM (jextract) bindings for FFmpeg with bundled, from-source LGPL natives. One Maven coordinate, batteries included — no user-installed FFmpeg, no JNI.
| Artifact | Contents | License |
|---|---|---|
io.github.ghosthack:ffmpeg-ffm | jextract-generated stubs + runtime loader (pure Java, JDK ≥ 22) | MIT |
io.github.ghosthack:ffmpeg-ffm-natives (classifier macos-arm64, windows-x64, linux-x64) | FFmpeg 8.1.2 shared libraries, built from unmodified source, LGPL-only configuration | LGPL v2.1+ (see THIRD-PARTY.md) |
Status: decode-oriented surface (demux + decode + swscale; no encoders,
muxers, or network), curated to the API listed in jextract/gen-bindings.sh.
Platforms: macos-arm64, windows-x64, and linux-x64. AV1 decodes in software
via a statically linked dav1d (BSD-2) — FFmpeg has no native AV1 software
decoder, only a hwaccel shim. JPEG XL decodes via a statically linked libjxl
0.11 (BSD-3, with highway/brotli/skcms; since 0.3.0) — FFmpeg has only a JXL
parser natively.
Software decode by design. All decoding runs on the CPU (with full SIMD:
dav1d and FFmpeg ship hand-written NEON/AVX kernels selected at runtime). The
natives do compile the hardware-accel hooks (VideoToolbox on macOS,
D3D11VA/D3D12VA/DXVA2 on Windows), but the curated binding surface omits the
hw-device API (av_hwdevice_ctx_create, get_format, av_hwframe_transfer_data),
so nothing routes through them. Hardware decode would be a purely additive
binding release — regenerated stubs, byte-identical natives.
<dependency>
<groupId>io.github.ghosthack</groupId>
<artifactId>ffmpeg-ffm</artifactId>
<version>8.1.2-0.3.5</version>
</dependency>
<dependency>
<groupId>io.github.ghosthack</groupId>
<artifactId>ffmpeg-ffm-natives</artifactId>
<version>8.1.2-0.3.5</version>
<classifier>macos-arm64</classifier> <!-- or windows-x64 / linux-x64 -->
<scope>runtime</scope>
</dependency>When using the module path, declare requires ffmpeg.ffm; and run with
--enable-native-access=ffmpeg.ffm. When using the class path, run with
--enable-native-access=ALL-UNNAMED. Then:
importio.github.ghosthack.ffmpegffm.ffmpeg.FFmpeg;
intversion = FFmpeg.avformat_version(); // natives extract + load on first usecore/src/test/java/.../SmokeTest.java is a complete open→decode→scale example.
Demuxers may return zero-sized packets (Ogg/Theora does this for duplicate
frames). Do not pass those packets to avcodec_send_packet: libavcodec treats
an empty packet like a decoder drain, so a later data packet can fail with
EINVAL. DecoderSupport.hasPayload(packet) distinguishes packets that are
safe to send.
The send/receive API also requires callers to retain and resend the same packet
when avcodec_send_packet returns EAGAIN; reading a replacement packet loses
input. Use DecoderSupport.averrorEagain() for the platform-specific native
error value. ThreadedTheoraSmokeTest is the complete buffered example,
including empty-packet filtering, packet retention, and final draining.
-Dffmpegffm.libdir=<dir>orFFMPEG_FFM_LIBDIR— use an existing FFmpeg install (also the LGPL relinking hook);- the bundled natives jar for the current platform, extracted once to
~/.cache/ffmpeg-ffm/<version>-<platform>/; - libraries already loaded by the host application.
jextract 22 generates indexed array-field accessors (e.g.
AVFrame.linesize(frame, 0)) whose VarHandle is built from the field's own
layout, so they read relative to the struct start, not the field offset —
wrong for every array field not at offset 0. Use the field-slice form instead:
intlinesize0 = AVFrame.linesize(frame).getAtIndex(ValueLayout.JAVA_INT, 0);Each platform's natives are built on that platform; stub generation runs once, on macOS (layouts are portable across platforms for FFmpeg's public API).
macOS (arm64):
mkdir -p build/ffmpeg-8.1.2
curl -sfL https://github.com/FFmpeg/FFmpeg/archive/refs/tags/n8.1.2.tar.gz \
| tar xz --strip-components=1 -C build/ffmpeg-8.1.2
build-natives/macos-arm64.sh # configure/make, stage dylibs + manifest
jextract/gen-bindings.sh # regenerate stubs (needs tools/jextract-22, see tools/README.md)
mvn install # installs ffmpeg-ffm + natives:macos-arm64, runs smoke testsWindows (x64), from an MSYS2 MINGW64 shell: see build-natives/windows-x64.sh
header for prerequisites, then the same flow with windows-x64.sh.
Staged natives (natives/src/main/resources/natives/) are build outputs and
not committed; the scripts verify every shipped library resolves its
dependencies only against the staged set and the OS.