mpv_audio_kit 0.0.1+6
mpv_audio_kit: ^0.0.1+6 copied to clipboard
Flutter audio player built on top of libmpv. Supports audio filters, pitch control, equalizer, and all mpv audio features. Targets macOS, Windows, Linux, iOS and Android.
mpv_audio_kit #
Audio engine for Flutter & Dart.
mpv_audio_kit is an audio library built on the latest version of libmpv (v0.41.0). It provides a dedicated background event loop, extensive DSP capabilities, and granular control over the audio pipeline, making it ideal for audio applications.
Why did I build this? #
Many existing Flutter audio libraries are either outdated or too restrictive, often hiding the native engine's power behind oversimplified abstractions.
This project was born out of two main needs:
- Unlocking Jellyfin's full potential: For audio streaming, supporting
.m3u8(HLS) is essential. Jellyfin uses HLS for transcoding, and having a modernlibmpvcore (v0.41.0+) ensures that precise seeking works flawlessly during transcoded streams. - Total control for technical users: This library doesn't gatekeep features; it exposes the native engine so technical users can tune buffers, network timeouts, and DSP filters exactly how they need, without any limitations.
Installation #
Add mpv_audio_kit to your pubspec.yaml:
dependencies:
mpv_audio_kit: ^0.0.1
Platform Requirements #
- Android: SDK 24 (Android 7.0) or above.
- iOS: iOS 13.0 or above.
- macOS: 10.14 or above (Apple Silicon).
- Windows: Windows 10 or above.
- Linux: Ubuntu 22.04 or above.
Platforms #
| Platform | Architecture | Device | Emulator | mpv version |
|---|---|---|---|---|
| Android | arm64-v8a, x86_64 | ✅ | ✅ | v0.41.0 |
| iOS | arm64, x86_64 | ✅ | ✅ | v0.41.0 |
| macOS | arm64 | ✅ | — | v0.41.0 |
| Windows | x86_64 | ✅ | — | v0.41.0 |
| Linux | x86_64 | ✅ | — | v0.41.0 |
Reference #
Visuals #
The following images demonstrate the example app included in the example/ directory. This application serves as a reference music player for testing the various features and capabilities of mpv.
| Screen | Description |
|---|---|
![]() |
Playback Interface UI with real-time metadata extraction and cover art processing. |
![]() |
Queue Management Live playlist control with support for adding, removing tracks. |
![]() |
Audio Engine (DSP & Filters) Feature-rich 10-band graphic equalizer, EBU R128 industry-standard loudness normalization, and real-time audio compression. |
![]() |
Routing & Hardware Tuning Audio mode selection (WASAPI/ALSA/CoreAudio), device selection, and output format configuration. |
![]() |
Stream Lab Specialized testing environment for network streams, radio protocols, and custom HTTP headers. |
![]() |
Demuxer & Cache Control Granular control over network buffering, demuxer thread filling, and memory pool management. |
![]() |
System Infrastructure Exclusive audio, Low-level timing adjustments, audio buffer sizes, and native engine configuration. |
Features #
- ⚡ Async Event Loop:
libmpvevents are processed in a background isolate. - 🎵 Gapless Playback: Seamless audio transitions between tracks.
- ⚖️ ReplayGain: Industry-standard track & album normalization.
- 🎛️ High-Fidelity Filters: 10-band EQ, EBU R128 Normalization, Compression, Crossfeed.
- 📜 Dynamic Playlist: Add, remove, move, and replace tracks in real-time.
- ⚙️ Audiophile Hardware: Exclusive mode (WASAPI/ALSA/CoreAudio) and device switching.
- 🔍 Metadata & Cover Art: Native extraction of embedded covers and metadata tags.
- 🌐 HTTP Headers: Support for authenticated streams and custom User-Agents.
- 📦 Caching & Buffering: Fine-tuned control over network cache and thread-filling.
Quick Start #
import 'package:flutter/material.dart';
import 'package:mpv_audio_kit/mpv_audio_kit.dart';
void main() => runApp(const MaterialApp(home: AudioPlayerScreen()));
class AudioPlayerScreen extends StatefulWidget {
const AudioPlayerScreen({super.key});
@override
State<AudioPlayerScreen> createState() => _AudioPlayerScreenState();
}
class _AudioPlayerScreenState extends State<AudioPlayerScreen> {
// 1. Initialize the Player
late final Player player = Player();
@override
void initState() {
super.initState();
// 2. Load a track immediately
player.open(Media('https://example.com/audio.mp3'));
}
@override
void dispose() {
// 3. Clean up native resources
player.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: StreamBuilder<Duration>(
stream: player.stream.position,
builder: (context, snap) => Text('Position: ${snap.data}'),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => player.playOrPause(),
child: const Icon(Icons.play_arrow),
),
);
}
}
Guide #
1. Initialization & Lifecycle #
The Player class coordinates with libmpv through a dedicated background isolate.
final player = Player(
configuration: PlayerConfiguration(
logLevel: 'info', // Debugging log level
initialVolume: 100.0, // Pre-set startup volume
autoPlay: true, // Automatically start playing on 'open'
audioClientName: 'my_app', // Identifier for the system mixer
),
);
// Always dispose to avoid native memory leaks
await player.dispose();
2. Media & Playlists #
A Media object represents an audio source (URL, Local Path, Asset, or Content URI).
final media = Media(
'https://cdn.example.com/stream.mp3',
httpHeaders: { 'Authorization': 'token' }, // For restricted streams
extras: { 'title': 'my_track', 'artist': 'my_artist' }, // Store custom metadata for the UI
);
await player.open(media); // Single track
await player.openPlaylist([media1, media2]); // Multiple tracks
Playlist Management
await player.next(); // Move to the next track
await player.previous(); // Move to the previous track
await player.jump(2); // Jump to the 3rd track (0-indexed)
await player.remove(0); // Remove the 1st track
await player.move(5, 0); // Move track at index 5 to index 0
await player.setShuffle(true); // Enable random playback order
await player.setPlaylistMode(PlaylistMode.loop); // Modes: single, loop, none
3. Advanced Playback Control #
Beyond play/pause, mpv_audio_kit offers precise control over the audio pipeline.
await player.seek(Duration(seconds: 30)); // Seek to absolute position
await player.seek(Duration(seconds: 10), relative: true); // Seek forward by 10s
await player.setRate(1.2); // Set playback speed (e.g., 1.2x)
await player.setPitch(1.1); // Change pitch without affecting speed
await player.setPitchCorrection(true); // Maintain pitch during speed changes
await player.setAudioDelay(0.05); // Offset audio by 50ms (e.g., for BT sync)
4. Audio Quality & DSP #
Leverage built-in high-fidelity audio processing.
ReplayGain & Gapless
await player.setGaplessPlayback('yes'); // Enable seamless track transitions
await player.setReplayGain('track'); // Use track-based volume normalization
await player.setAudioExclusive(true); // Direct hardware access (WASAPI/ALSA)
Audio Filters (FFmpeg/lavfi)
Replace or append to the filter chain at runtime.
await player.setAudioFilters([
AudioFilter.equalizer([0, 0, 2, 4, 2, 0, -2, -4, -4, 0]), // Custom 10-band EQ
AudioFilter.loudnorm(), // Professional loudness normalization
AudioFilter.crossfeed(), // Reduced listening fatigue for headphones
AudioFilter.compressor(threshold: -20, ratio: 4), // Dynamic range compression
]);
5. Network & Caching #
Critical for radio apps and high-latency mobile networks.
await player.setCache('yes'); // Enable network caching
await player.setCacheSecs(60.0); // Buffer 60 seconds of audio data
await player.setCachePause(true); // Automatically pause on buffer depletion
await player.setCacheOnDisk(true); // Allow overflow cache to spill to disk
await player.setNetworkTimeout(15.0); // Set TCP connection timeout limit
6. Primitive API & Raw Access #
Direct control over the native engine via primitive values.
await player.setAudioSampleRate(192000); // Set specific sample rate
player.setRawProperty('audio-samplerate', '96000'); // Direct property injection
print(player.state.audioSampleRate); // Read current engine value
7. States & Streams #
Reactive property observation and synchronous state access.
player.stream.position.listen((pos) => print(pos)); // Observe real-time updates
player.stream.metadata.listen((meta) => print(meta)); // Track metadata changes
print(player.state.volume); // Synchronous property access
Permissions & Background Playback #
Android #
Update AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
iOS #
Enable Audio, AirPlay, and Picture in Picture in Signing & Capabilities.
Add to Info.plist:
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
</array>
Project Background #
All the native bindings, isolate logic, and architectural patterns were implemented through the use of Claude Opus 4.6 and Antigravity in general, with Gemini models for the UI part.
The goal was to build a low-level audio engine through organization and orchestration, without necessarily being a low-level bindings specialist.
Credits #
This project architecture is inspired by and includes native bridging logic from media-kit (by alexmercerind and cillyvms), specifically:
- NativeReferenceHolder: Native memory management.
- AndroidHelper: URI to file-descriptor mapping.
Funding #
If you find this library useful and want to support its development, consider becoming a supporter on Patreon:
Developed by Alessandro Di Ronza






