macosmedia-playerkotlin-multiplatformvlcj

VLCJ on macOS in Kotlin Multiplatform project: Audio plays but no video rendering


I built a cross-platform project using Kotlin Multiplatform targeting Android, Windows, and macOS. Now, I need to implement a media player that must support HLS. Because of this, I ruled out the JavaFX approach and opted for the following solution:

The Android version works perfectly. However, when running with VLCJ on macOS, the video just won’t render — there’s audio, and I’ve tried controlling playback via keyboard (play, pause, seek, volume, fullscreen, exit fullscreen), and all those functions work normally.

Here is the Gradle configuration and the relevant source code under /jvmMain/.

[versions]
vlcj = "4.11.0"

[libraries]
vlcj = { module = "uk.co.caprica:vlcj", version.ref = "vlcj" }
    sourceSets {
        // ...
        jvmMain.dependencies {
            implementation(compose.desktop.currentOs)
            implementation(libs.kotlinx.coroutinesSwing)
            implementation(libs.vlcj)
        }
    }
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application

fun main() = application {
    Window(
        onCloseRequest = ::exitApplication,
        title = "Media"
    ) {
        App()
    }
}
class JVMPlatform: Platform {
    override val name: String = "Java ${System.getProperty("java.version")}"
}

actual fun getPlatform(): Platform = JVMPlatform()
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.awt.SwingPanel
import uk.co.caprica.vlcj.factory.MediaPlayerFactory
import uk.co.caprica.vlcj.player.embedded.EmbeddedMediaPlayer
import java.awt.Canvas

@Composable
actual fun Player(modifier: Modifier, url: String) {
    val factory = MediaPlayerFactory()
    val mediaPlayer: EmbeddedMediaPlayer = factory.mediaPlayers().newEmbeddedMediaPlayer()

    SwingPanel(
        factory = { Canvas() },
        modifier = modifier.fillMaxSize(),
        update = { canvas ->
            mediaPlayer.videoSurface().set(factory.videoSurfaces().newVideoSurface(canvas))
            mediaPlayer.media().play(url)
        }
    )
}
  1. Could I have misunderstood VLC’s platform support? Does supporting multiple platforms not mean it’s fully adapted for cross-platform use?
  2. Even if the video did render correctly, would a separate VLC player installation still be required on PC?
  3. This is my first KMP project, and I’m developing on macOS. Does that limit my ability to test and package the app for Windows?

Solution

  • 1. Support for OSX could be limited

    From V4LC github readme:

    vlcj is primarily developed and therefore extensively tested on Linux - it does also work just fine on Windows and OSX, although there may be some limitations on OSX.

    so it seems the OSX experience could be lacking in support. Notably,

    On macOS, there are potentially critical limitations if you use any version of Java after the 1.6 version. This is because in Java 1.7 on macOS there is no longer any "heavyweight" window toolkit, everything is lightweight. This is a problem because VLC normally requires the window handle of a heavyweight window so it can be told where to render the video into.

    2. Yes, a separate instance of VLC is needed.

    From their version 4 tutorial:

    It is possible to bundle up the VLC native libraries and the VLC plugins that you need and include them with your own application, but doing this is not as straightforward as you might think. If you're just starting out with vlcj, then just install VLC first.

    3. Yes, you should test code you want on working on Windows on Windows

    Since you state you want multiplatform support, especially since you doing something like video you definitely are touching platform specific things like video/audio so it is wise to do verify.