Fix JavaFX media: extract native libs from jar to temp dir
The 'media type not supported' error occurs because the JavaFX native codec .dylib files aren't extracted when loading from classpath. extractJavafxNativeLibs() finds the javafx-media jar, extracts the .dylib files to a temp dir, and adds it to java.library.path. Also remove --add-modules JVM args (caused boot layer init failure).
This commit is contained in:
@@ -44,6 +44,9 @@ fun LivePhotoPlayer(
|
||||
|
||||
log.info("LivePhotoPlayer: loading ${file.name} (${file.length() / 1024}KB)")
|
||||
|
||||
// Extract JavaFX native media libs from jar if needed
|
||||
extractJavafxNativeLibs()
|
||||
|
||||
val media = javafx.scene.media.Media(file.toURI().toString())
|
||||
val player = javafx.scene.media.MediaPlayer(media)
|
||||
|
||||
@@ -64,11 +67,10 @@ fun LivePhotoPlayer(
|
||||
|
||||
val root = javafx.scene.layout.StackPane(mediaView)
|
||||
root.style = "-fx-background-color: black;"
|
||||
|
||||
jfxPanel.scene = javafx.scene.Scene(root)
|
||||
|
||||
} catch (e: Exception) {
|
||||
log.warn("LivePhotoPlayer: JavaFX Platform.runLater failed: ${e.message}")
|
||||
log.warn("LivePhotoPlayer: Platform.runLater failed: ${e.message}")
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
@@ -92,3 +94,58 @@ fun isJavaFxMediaAvailable(): Boolean {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract JavaFX native libraries from the classpath jars to a temp directory
|
||||
* and add that directory to java.library.path.
|
||||
*
|
||||
* This is needed when OpenJFX is loaded from classpath (not module path) —
|
||||
* the native .dylib/.so files inside the jar need to be extracted for the
|
||||
* media codecs (AVFoundation on macOS) to load correctly.
|
||||
*/
|
||||
private var nativeLibsExtracted = false
|
||||
|
||||
private fun extractJavafxNativeLibs() {
|
||||
if (nativeLibsExtracted) return
|
||||
nativeLibsExtracted = true
|
||||
|
||||
try {
|
||||
val tmpDir = File(System.getProperty("java.io.tmpdir"), "photophetch-javafx-natives")
|
||||
tmpDir.mkdirs()
|
||||
|
||||
// Find the javafx-media jar on the classpath
|
||||
val classLoader = Thread.currentThread().contextClassLoader
|
||||
val mediaClass = classLoader.loadClass("javafx.scene.media.MediaPlayer")
|
||||
val codeSource = mediaClass.protectionDomain?.codeSource?.location
|
||||
|
||||
if (codeSource != null) {
|
||||
val jarUrl = codeSource.toURI()
|
||||
val jarFile = java.util.jar.JarFile(File(jarUrl))
|
||||
|
||||
// Extract .dylib files (macOS native media codecs)
|
||||
jarFile.entries().asSequence()
|
||||
.filter { it.name.endsWith(".dylib") || it.name.endsWith(".so") }
|
||||
.forEach { entry ->
|
||||
val outFile = File(tmpDir, File(entry.name).name)
|
||||
if (!outFile.exists()) {
|
||||
jarFile.getInputStream(entry).use { input ->
|
||||
outFile.outputStream().use { output -> input.copyTo(output) }
|
||||
}
|
||||
log.info("Extracted native lib: ${outFile.name}")
|
||||
}
|
||||
}
|
||||
jarFile.close()
|
||||
}
|
||||
|
||||
// Add tmpDir to java.library.path via reflection (standard trick)
|
||||
val usrPathsField = ClassLoader::class.java.getDeclaredField("usr_paths")
|
||||
usrPathsField.isAccessible = true
|
||||
val paths = usrPathsField.get(null) as Array<String>
|
||||
if (tmpDir.absolutePath !in paths) {
|
||||
usrPathsField.set(null, paths + tmpDir.absolutePath)
|
||||
log.info("Added ${tmpDir.absolutePath} to java.library.path")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
log.warn("extractJavafxNativeLibs failed (may still work): ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user