KMPPlayer is a Kotlin Multiplatform media playback library that delivers a unified, reactive interface to control media playback across Android and iOS platforms. Internally, it leverages ExoPlayer on Android and AVPlayer on iOS, while exposing a clean Kotlin API usable from shared code and Jetpack Compose.
- 🧩 Unified playback interface (
KMPPlayerState) - 📁 Supports local files & remote media URLs
- 🎮 Built-in media controls: play, pause, stop, loop, seek
- 📡 Reactive
StateFlow-based event stream (KMPPlayerEvent) - 🎚 Volume & playback speed control
- 📍 Position & duration tracking
- 🎨 Jetpack Compose support (
KMPPlayer) - ⚙️ Access native players: ExoPlayer (Android), AVPlayer (iOS)
- 🧼 Proper resource cleanup with
destroy()
Published to Maven Central:
repositories {
mavenCentral()
}For Kotlin Multiplatform (shared code):
kotlin {
sourceSets {
commonMain.dependencies {
implementation("io.github.rufenkhokhar:KMP-Player:+")
}
}
}Or for Android-only usage:
dependencies {
implementation("io.github.rufenkhokhar:KMP-Player:+")
}val playerState = rememberKMPPlayerState()
KMPPlayer(
state = playerState,
showControls = true,
modifier = Modifier
.fillMaxSize()
.background(Color.Black)
)
LaunchedEffect(Unit) {
// Load media
playerState.setFileUrl("https://www.sample-videos.com/video321/mp4/360/big_buck_bunny_360p_20mb.mp4")
// Configure playback
playerState.setVolume(0.8f) // 80% volume
playerState.setPlaybackSpeed(1.25f) // 1.25x speed
playerState.setVideoLoop(true) // enable looping
// Start playback
playerState.play()
// Observe playback events
playerState.observePlayerEvent().collect { event ->
when (event) {
is KMPPlayerEvent.Playing -> {
println("Playing: ${event.currentPosition} / ${event.duration}")
}
KMPPlayerEvent.Paused -> println("Paused")
KMPPlayerEvent.Stop -> println("Stopped")
KMPPlayerEvent.Buffering -> println("Buffering…")
KMPPlayerEvent.Ideal -> println("Idle")
KMPPlayerEvent.Ended -> println("Playback ended")
is KMPPlayerEvent.Error -> println("Error: ${event.message}")
}
}
}@ExperimentalMultiplatform
interface KMPPlayerState {
fun play()
fun stop()
fun pause()
fun isPlaying(): Boolean
fun setLocalFile(absolutePath: String)
fun setFileUrl(url: String)
fun setVideoLoop(loop: Boolean)
fun observePlayerEvent(): StateFlow<KMPPlayerEvent>
fun getPlatformPlayer(): Any?
fun destroy()
fun setVolume(volume: Float)
fun getVolume(): Float
fun setPlaybackSpeed(speed: Float)
fun getCurrentPosition(): Long
fun getDuration(): Long
fun seekTo(position: Long)
}sealed interface KMPPlayerEvent {
data class Playing(
val currentPosition: Long = 0L,
val duration: Long = 0L
) : KMPPlayerEvent
data object Paused : KMPPlayerEvent
data object Stop : KMPPlayerEvent
data object Buffering : KMPPlayerEvent
data object Ideal : KMPPlayerEvent
data object Ended : KMPPlayerEvent
data class Error(val message: String) : KMPPlayerEvent
}- Use
setVideoLoop(true)for infinite looping - Use
setVolume(0.0f–1.0f)to control playback volume - Use
setPlaybackSpeed(speed)to adjust playback rate - Use
getCurrentPosition()+getDuration()for progress tracking - Use
seekTo(ms)to scrub or skip - Always call
destroy()to release player resources when not in use - For advanced control, use
getPlatformPlayer()and cast to native types
MIT License
© 2025 Rufen Khokhar
Pull requests and feature ideas are welcome!
Open an issue to start the discussion.
Rufen Khokhar
📧 rufankhokhar@gmail.com
🔗 github.com/rufenkhokhar