0.3.0 - 2026-05-20
Initial public release. Native Elixir bindings for FFmpeg 8 via the
rsmpeg Rust crate, packaged as a Rustler NIF - in-process calls
instead of shelling out to the ffmpeg / ffprobe CLIs.
Operations
Exmpeg.version/0- linked FFmpeg sub-library versions and configure flags.Exmpeg.probe/1- container + per-stream metadata (ffprobe).Exmpeg.remux/3- stream copy between containers with optionalstart_s/duration_swindow,:drop_audio/:drop_video/:drop_subtitles, and a:tagskeyword/map for container metadata.Exmpeg.extract_frame/3- single-frame thumbnail at a timestamp, written as.jpg/.png/.bmp/.webp, with optional aspect-preserving resize.Exmpeg.extract_audio/3- decoded audio to.wav,.mp3,.m4a/.aac,.opus/.ogg, or.flac, with optional sample-rate / channel-count / bitrate selection. Sample rate snaps to the encoder's supported list when needed (e.g. libopus 8/12/16/24/48 kHz).Exmpeg.concat/3- join multiple inputs sharing the same stream layout into a single output without re-encoding.Exmpeg.transcode/3- per-stream re-encode with codec / bitrate / scale / fps / sample-rate selection driven by anAVFilterGraph, plus a raw:video_filterfilter-graph spec. Streams marked"copy"are stream-copied.
Inputs and progress
- Memory inputs - every read-side operation accepts
{:memory, binary}in place of a filesystem path, backed by a customAVIOContextCustomwith read + seek callbacks so demuxers that seek (mp4moov,matroskacues) work without a temp file. - Progress callbacks -
remux/3,extract_audio/3,concat/3, andtranscode/3acceptprogress: pid()and send throttled{:exmpeg_progress, %{...}}messages plus a final tick afterwrite_trailer.
Packaging
- Precompiled NIFs for
aarch64-apple-darwin,x86_64-unknown-linux-gnu, andaarch64-unknown-linux-gnu; other targets build from source withEXMPEG_BUILD=1. - The precompiled tarballs bundle an LGPL-only FFmpeg 8 (libmp3lame,
libopus, libvpx; no
--enable-gpl), so they redistribute cleanly under this package's MIT license. H.264 / H.265 software encoding (libx264/libx265, GPL) is not in the precompiled binaries and returns:unsupported; build from source against a GPL-enabled FFmpeg 8 to use them. H.264/H.265 decoding is unaffected.
Safety
- The Rust crate is built on rsmpeg's safe wrappers with
#![deny(unsafe_code)]at the root. Allunsafeis confined toffi_helpers.rs, each block carrying aSAFETY:comment. - Every NIF entry point is wrapped in
run_with_panic_protection, so a Rust panic surfaces as{:error, %{type: "nif_panic"}}instead of crashing the BEAM. - Size options (
:width/:height/:sample_rate) are bounded at the API boundary so an absurd value cannot trigger an out-of-memory allocation inside the NIF. - Disk writes are atomic: operations write a
<stem>.partial.<ext>sibling and rename onto the destination only after the muxer trailer is written; a mid-encode failure removes the partial.