Skip to content
All work

library

mpv_audio_kit

A full audio stack for Flutter: libmpv driven over FFI from a background isolate, an ordered chain of typed ffmpeg effects, real-time spectrum and waveform, native media sessions, and its own cross-platform build of libmpv. Ships on Android, iOS, macOS, Windows and Linux.

GitHub stars
34
pub.dev likes
52
pub points
160/160
Downloads /30d
2.2K

Why I built it

Many Flutter audio libraries are built on an old version of mpv, or are too restrictive and hide the audio processing features. mpv_audio_kit exposes the whole engine through a typed Dart API, without blocking the UI thread.

The project is split across three repositories:

  • mpv_audio_kit: the Dart/Flutter library, published on pub.dev.
  • libmpv-scripts: an audio-only build of libmpv and FFmpeg with custom patches, cross-compiled for five operating systems and driven by a Go TUI.
  • MPV Studio: the reference app built on the library.

Architecture

The Player lives on your main isolate and exposes a synchronous state snapshot plus more than 100 streams. Commands go to libmpv through its async API; a dedicated isolate blocks on mpv_wait_event and posts state back, so slow network streams and heavy filter graphs don’t block the UI.

Architecture: UI and Player on the main isolate, event isolate and libmpv in the background, platform plugin bridging to OS media controls

Media sessions (Media3 on Android, Now Playing on Apple, MPRIS on Linux, SMTC on Windows) are driven from Dart, so lock-screen controls and notifications work the same way on every platform.

A typed effects chain

Audio effects are an ordered list of typed stages (18-band graphic EQ, compressor, loudness normalisation, pitch and tempo, bass/treble, stereo width, headphone crossfeed, silence trim) compiled into a single ffmpeg filter graph. Anything not covered by a typed stage can still be dropped in as a raw lavfi filter. Dragging a slider that only changes a stage’s parameters is applied to the running graph in place, with no audible rebuild.

Effects chain: typed effect stages compiled into an ffmpeg lavfi graph
Tweaking the EQ and effects live in MPV Studio

Seeing the sound

Spectrum, PCM, waveform overviews and loudness scans come out of patched FFmpeg filters and reach Dart as streams.

Spectrum pipeline from ffmpeg filter tap to Dart stream

Getting libmpv into the app

A Dart build hook downloads the libmpv build for each target from the project’s releases, verifies it against a pinned SHA-256 and bundles it with the app. The builds themselves come from libmpv-scripts: Docker for Linux, Windows and Android, Xcode for Apple, with a verify stage that checks every artifact before release.

dependencies:
  mpv_audio_kit: ^0.5.0
final player = Player();
await player.open(Media('https://example.com/audio.mp3'));

await player.setAudioEffects([
  // +3 dB around 100 Hz, then compress and limit
  EqualizerEffect(frequency: 100, width: 50, widthType: EqualizerWidthType.h, gain: 3),
  AcompressorEffect(threshold: 0.1, ratio: 4),
  AlimiterEffect(limit: 0.9),
]);

player.stream.audioEffects.listen((fx) => print('${fx.length} stages'));

Streams and protocols

HTTP(S), HLS, DASH and SMB2/3 are supported. For media servers, Jellyfin transcodes use seekable HLS, and Plex’s per-stream /decision call is made from an on_load hook instead of a local proxy.

Network streams
Raw mpv console

Next project

Vocem Overlay

Discord voice overlay drawn inside the game, for Linux.