This is where KMP shines. You define a generic interface in your commonMain source set, and implement it specifically for Android using the VLC engine.
actual class MediaEngine(private val context: Context) private var libVLC: LibVLC? = null private var mediaPlayer: MediaPlayer? = null
The engine, via libvlcjni.so – its Android JNI bridge – provides a solution by bundling ffmpeg with hundreds of built-in external codecs, decoupled from the OS. kmp external codec libvlcjni.so
However, merely having the .so file isn't enough; you need the JNI Java glue code to call it. This is usually why developers stick to the libvlc-all package—it includes both the native library and the necessary Java class wrappers.
// androidMain/kotlin/PlatformMediaPlayer.kt import org.videolan.libvlc.LibVLC import org.videolan.libvlc.MediaPlayer import org.videolan.libvlc.Media This is where KMP shines
In your androidApp or shared module's build.gradle , you typically add the dependency:
actual fun setDataSource(path: String) val options = arrayOf("--codec=all", "--no-audio-time-stretch") libVLC = LibVLC(ApplicationProvider.getApplicationContext(), options) mediaPlayer = MediaPlayer(libVLC) val media = Media(libVLC, path) media.addOption(":no-audio-filter") // optional mediaPlayer.media = media = null private var mediaPlayer: MediaPlayer
libvlc: using decoder module "avcodec" avcodec: using FFmpeg decoder for codec 'eac3'
Add dependency in build.gradle.kts (Android target):
Android supports a specific set of media formats out of the box. However, the ecosystem is plagued by inconsistencies: