Skip to main content

mpv_audio_kit 0.7.0 copy "mpv_audio_kit: ^0.7.0" to clipboard
mpv_audio_kit: ^0.7.0 copied to clipboard

Dart audio player built on mpv v0.41.0, with audio filters, equalizer, visualizer and waveform. For Android, iOS, macOS, Windows, Linux, web, Android TV, tvOS, Tizen and webOS.

mpv_audio_kit, audio engine for Flutter built on mpv and FFmpeg, shown in the mpv_studio player

mpv_audio_kit #

Audio engine for Flutter.


Made for your media library #

The package is an audio player for Flutter apps that play a media library, from a server or from the device. It runs mpv and FFmpeg behind a typed Dart API, on Android, iOS, macOS, Windows, Linux and the web.

MPV Studio Try MPV Studio in your browser
The demo app runs the package with libmpv as WebAssembly. Its source is in the mpv_studio repository.
Jellyfin Jellyfin
Stream your library directly or transcoded. Transcoded streams arrive as HLS, and you can still seek in them.
Plex Plex
Play tracks and playlists over HLS or DASH. Each entry asks the server for its stream only when it opens, so a long queue starts at once.
Samba Samba
Play straight from a NAS or any network share over SMB2 and SMB3.
Tuning and analysis
Tune the cache and the network, shape the sound with a chain of FFmpeg effects, and watch it with a live spectrum, waveform and loudness meter.

mpv_studio playing a track, with its waveform and spectrum


Getting started #

dependencies:
  mpv_audio_kit: ^0.7.0

The build downloads the libmpv binary for each native target and checks its SHA-256, so the first build needs network access. On the web the module ships with the package.

Platform Minimum Architectures Size
Android 7.0 (SDK 24) arm64-v8a, armeabi-v7a, x86_64 7.7 to 8.3 MB per ABI
iOS 15.0 arm64, simulator arm64 and x86_64 7.3 MB
macOS 12.0 arm64, x86_64 15.1 MB universal
tvOS 15.0 arm64, simulator arm64 and x86_64 7.3 MB
Tizen 6.0 armv7l, emulator x86_64 6.0 MB, 8.6 MB on the emulator
webOS 26 armv7l 5.8 MB
Windows 10 arm64, x86_64 7.5 to 7.8 MB
Linux glibc 2.31 aarch64, x86_64 8.4 to 8.7 MB
Web a cross-origin isolated page wasm32 4.3 MB, 2.0 MB zipped

The package needs Flutter 3.38.10 or later, and Flutter 3.47.4 to build for iOS. Linux needs ALSA, and uses PipeWire or PulseAudio when they are installed.

Play a file #

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  MpvAudioKit.ensureInitialized();
  runApp(const MyApp());
}

final player = Player();
await player.open(Media('https://example.com/audio.flac'));
await player.play();

player.stream.position.listen((position) => print(position));

await player.dispose(); // when you are done

player.state holds the current values, and player.stream sends each change.

Player lifecycle: ensureInitialized once, then for each player create, open, listen and control, and dispose

How it works #

The player lives on your main isolate and calls libmpv without blocking it. A background isolate waits for libmpv's events and brings state back. On the web the module wakes the player on the main thread.

Architecture: the Player on the main isolate calls libmpv, the event isolate forwards events and state, and the platform plugin connects the media session

Working with the player #

A queue #

await player.openAll([track1, track2, track3]);
await player.insert(player.state.playback.playlist.index + 1, track4); // play next
await player.next();
await player.setLoop(Loop.playlist);
await player.setPrefetchPlaylist(true); // gapless over the network

See Playback for sources, headers, chapters, resume points and live PCM.

Effects #

await player.setAudioEffects(const [
  EqualizerEffect(frequency: 100, width: 50, widthType: EqualizerWidthType.h, gain: 3),
  LoudnormEffect(i: -16),
  AlimiterEffect(limit: 0.9),
]);

Each of the 86 effects is an FFmpeg filter with typed options. See Effects for the whole chain.

Errors #

player.stream.error.listen((error) {
  switch (error) {
    case EndFileError():
      print('The file stopped: ${error.message}');
    case LogError():
      print('mpv reported: ${error.message}');
  }
});

See Errors and logs for every error type and the log stream.

A URL that expires #

A source resolver gives each entry its stream URL when it opens, so the tokens in a queue never go stale.

await player.setSourceResolver((request) async {
  final id = request.media.extras?['trackId'] as String?;
  return id == null ? null : api.streamUrl(id); // null keeps the URL
});

await player.openAll([
  Media('resolve://1', extras: {'trackId': '1'}),
  Media('resolve://2', extras: {'trackId': '2'}),
]);

See Hooks and raw access for the resolver, mpv hooks and raw properties.

Lock screen and notification #

await player.setMediaSession(const MediaSession());

The player then shows in the system media controls on every platform. On Android this also keeps it playing in the background. See Media session for the metadata, the buttons and each platform.

Visualizer #

player.stream.fft.listen((frame) => paintSpectrum(frame));

The player gives a live spectrum, the raw PCM, a waveform of the whole file and a loudness scan. See Visualizer and analysis for each of them.

The browser #

The page needs two headers and a user gesture before it plays. The players of a page share at most 512 MiB of memory, 256 MiB on an iPhone or an iPad. Test on a real iPhone: a desktop browser does not show the limits of iOS. See Web for the setup and the browser's limits.


Permissions #

Android #

Add this permission to your app's manifest for network sources:

<uses-permission android:name="android.permission.INTERNET" />

The package already declares FOREGROUND_SERVICE, FOREGROUND_SERVICE_MEDIA_PLAYBACK and POST_NOTIFICATIONS. You do not need to request POST_NOTIFICATIONS at runtime. Media notifications are exempt.

Background playback needs the media session. setMediaSession runs a foreground service of type mediaPlayback, which keeps the app alive after the user leaves it. Without a session, Android may freeze or stop the app in the background.

  • After 10 minutes paused, the service leaves the foreground and Android may stop the app. A pause caused by a phone call under pauseAndResume does not count.
  • If Android refuses the foreground service to a play started in the background, the player pauses.
  • The player takes no wake lock. If a network stall pauses the output with the screen off, the device may sleep until something wakes it.
  • Pressing Back on the root route, or swiping the app away from recent apps, can destroy the Flutter engine and stop its players. To keep playing after the activity closes, keep the engine alive, cached or run by a service.

iOS #

For background playback, add the Background Modes capability in Signing & Capabilities and check Audio, AirPlay, and Picture in Picture. This writes to Info.plist:

<key>UIBackgroundModes</key>
<array>
  <string>audio</string>
</array>

Android TV #

Android TV, Fire TV and Google TV run the Android build. The app needs a leanback entry and a banner in its manifest, and the media session for the keys of the remote. See Android TV for the manifest and HDMI passthrough.

tvOS #

Build with flutter-tvos. It installs no code asset, so on tvOS the plugin's tvos/Package.swift links libmpv instead of the build hook: Swift Package Manager downloads libmpv_tvos.xcframework.zip of the release and checks its checksum. A libmpv of your own goes to tvos/libmpv.xcframework in a checkout of the package: the hook's libmpv user define does not reach tvOS.

Tizen #

Build with flutter-tizen. The build hook bundles the Tizen libmpv for the TVs, and for the TV emulator when the app sets the user define tizen: true. See Tizen.

webOS #

Build with flutter-webos, for webOS 26 and later. It runs no build hook, so the package's webos/CMakeLists.txt bundles libmpv instead. See webOS.

macOS #

For network sources, add to DebugProfile.entitlements and Release.entitlements:

<key>com.apple.security.network.client</key>
<true/>

Troubleshooting #

Building and testing in containers #

These steps apply to WSL, Docker and Distrobox. libmpv comes prebuilt, so building needs only the Flutter desktop tools. At runtime libmpv needs libasound2, and a PipeWire (0.3.57 or later) or PulseAudio client to reach the host's sound server. Desktop distributions ship them. A minimal container does not, so install them:

sudo apt update

# Flutter desktop build tools
sudo apt install clang cmake ninja-build pkg-config libgtk-3-dev liblzma-dev

# Runtime libraries to play sound through the host
sudo apt install libasound2 libpipewire-0.3-0 libpulse0

Inside a container the alsa driver cannot reach the sound card. Use pipewire or pulse. On WSL only pulse works.


Documentation #

Licenses #

The package is BSD-3-Clause. The libmpv it bundles is LGPL, version 3 or later, and your app keeps its own license. showLicensePage lists the libmpv texts with nothing to set up. Your users must be able to replace libmpv and get its source, which the release's licenses.zip names. LICENSES.md has the details and the GPL build with Rubber Band.

Project background #

I wrote the native bindings, the isolate code and most of the architecture with Claude Code.

Jellyfin, Plex and Samba are trademarks of their respective owners. mpv_audio_kit is not affiliated with them.


Developed by Alessandro Di Ronza

56
likes
160
points
4.67k
downloads
screenshot

Documentation

API reference

Publisher

verified publisherales-drnz.com

Weekly Downloads

Dart audio player built on mpv v0.41.0, with audio filters, equalizer, visualizer and waveform. For Android, iOS, macOS, Windows, Linux, web, Android TV, tvOS, Tizen and webOS.

Repository (GitHub)
View/report issues
Contributing

Topics

#audio #audio-player #music #ffmpeg #tv

Funding

Consider supporting this project:

www.patreon.com
www.buymeacoffee.com

License

BSD-3-Clause (license)

Dependencies

code_assets, crypto, ffi, fftea, flutter, flutter_web_plugins, hooks, meta

More

Packages that depend on mpv_audio_kit

Packages that implement mpv_audio_kit