播放音频的 JavaFX 问题:不支持的协议“文件”

JavaFX problem with playing audio: Unsupported protocol "file"

提问人:kofiy 提问时间:7/21/2023 最后编辑:kofiy 更新时间:7/21/2023 访问量:103

问:

我正在尝试使用javafx.scene.media.AudioClip播放.jar文件中包含的音频,但是我经常遇到此错误:

Exception in thread "main" java.lang.UnsupportedOperationException: Unsupported protocol "file"
        at com.sun.media.jfxmedia.locator.Locator.<init>(Locator.java:241)
        at com.sun.media.jfxmediaimpl.NativeMediaAudioClip.<init>(NativeMediaAudioClip.java:53)
        at com.sun.media.jfxmediaimpl.NativeMediaAudioClip.load(NativeMediaAudioClip.java:63)
        at com.sun.media.jfxmediaimpl.AudioClipProvider.load(AudioClipProvider.java:66)
        at com.sun.media.jfxmedia.AudioClip.load(AudioClip.java:135)
        at javafx.scene.media.AudioClip.<init>(AudioClip.java:83)
        at main.Main.main(Main.java:77)

我尽可能地简化了我的代码,省略了抽象和其他方法,以确保错误实际上存在于 AudioClip 中:

package main;

import javafx.scene.media.AudioClip;

public class Main {
    public static void main(String[] args) {
        AudioClip clip = new AudioClip(Main.class.getResource("/sound/ambience/testSound.mp3").toExternalForm());
        clip.play();
    }
}

(我也尝试使用.toString()而不是toExternalForm())

我确保声音文件实际上包含在正确路径的 .jar 中。例如,这种通过 JavaX AudioInputStream 和 Clip 播放音频的方式就可以正常工作:

    public static void main(String[] args) throws LineUnavailableException, IOException, UnsupportedAudioFileException, InterruptedException {
        AudioInputStream ais = AudioSystem.getAudioInputStream(Main.class.getResource("/sound/ambience/randomScare.wav"));
        Clip clip = AudioSystem.getClip();
        clip.open(ais);
        ais.close();

        clip.start();
        Thread.sleep()
    }

(但我不能在最终产品中使用它,因为它仅支持未压缩的 .wav 格式)

我不知道是什么原因导致了这个问题,因为即使是官方的 javadoc 也指出:

source- 要从中加载音频剪辑的 URL 字符串。这可以是 HTTP、文件或 jar 源

一些可能有助于找到解决方案的更多信息: 我正在使用 maven 中央存储库中的版本(我也尝试切换回版本):org.openjfx:javafx-media21-ea+2420.0.1

        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-media</artifactId>
            <version>21-ea+24</version>
        </dependency>

Maven 项目是使用 java 版本 1.17 设置的:

    <properties>
        <maven.compiler.source>1.17</maven.compiler.source>
        <maven.compiler.target>1.17</maven.compiler.target>
        <exec.mainClass>main.Main</exec.mainClass>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>

我在 Windows 18 上使用 Zulu 10 () 运行它 (Zulu18.32+13-CA (build 18.0.2.1+1)Build 19044)

我将不胜感激任何帮助(并希望我已经提供了所有需要的信息)。

更新:我尝试使用 github 工作区运行 .jar 构建(该 .jar 对我的朋友来说效果很好),并出现不同的错误:

Exception in thread "AWT-EventQueue-0" java.lang.UnsatisfiedLinkError: no glib-lite in java.library.path: C:\Program Files\Zulu\zulu-18\bin;C:\Windows\Sun\Java\bin;C:\Windows\system32;C:\Windows;C:\Program Files\Zulu\zulu-18\bin\;C:\Program Files\Zulu\zulu-19\bin\;C:\ProgramData\Oracle\Java\javapath;C:\Program Files\Common Files\Oracle\Java\javapath;C:\ProgramData\Oracle\Java\jdk-16.0.1;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;C:\Program Files\Common Files\Autodesk Shared\;C:\Program Files\dotnet\;C:\Program Files\spwn\;C:\Program Files\Kotlin\kotlinc_1_6_10;C:\Program Files\Kotlin\kotlinc_1_6_10\bin;C:\Program Files\Kotlin\konaco_1_6_10;C:\Program Files\Kotlin\konaco_1_6_10\bin;C:\Program Files\Microsoft SQL Server\130\Tools\Binn\;C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\170\Tools\Binn\;C:\Program Files\ffmpeg\bin;C:\Program Files\Git\cmd;C:\Program Files\PuTTY\;C:\Program Files\NVIDIA Corporation\NVIDIA NvDLISR;C:\Users\Kofiy\AppData\Local\Programs\Python\Python310\Scripts\;C:\Users\Kofiy\AppData\Local\Programs\Python\Python310\;C:\Users\Kofiy\AppData\Local\Microsoft\WindowsApps;C:\jdk-16.0.1\bin;C:\jdk-16.0.1\bin\;C:\jdk-16.0.1\bin\javac.exe;C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2021.1.2\bin;C:\Users\Kofiy\AppData\Local\GitHubDesktop\bin;C:\Users\Kofiy\AppData\Local\Programs\Microsoft VS Code\bin;C:\Program Files\JetBrains\PyCharm Community Edition 2021.3.2\bin;C:\Users\Kofiy\AppData\Local\Programs\Python\Python310;C:\Users\Kofiy\AppData\Local\Programs\Python\Python310\Scripts;C:\Users\Kofiy\.dotnet\tools;.
        at java.base/java.lang.ClassLoader.loadLibrary(ClassLoader.java:2434)
        at java.base/java.lang.Runtime.loadLibrary0(Runtime.java:848)
        at java.base/java.lang.System.loadLibrary(System.java:2015)
        at com.sun.glass.utils.NativeLibLoader.loadLibraryInternal(NativeLibLoader.java:168)
        at com.sun.glass.utils.NativeLibLoader.loadLibrary(NativeLibLoader.java:56)
        at com.sun.media.jfxmediaimpl.NativeMediaManager.lambda$new$0(NativeMediaManager.java:111)
        at java.base/java.security.AccessController.doPrivileged(AccessController.java:569)
        at com.sun.media.jfxmediaimpl.NativeMediaManager.<init>(NativeMediaManager.java:108)
        at com.sun.media.jfxmediaimpl.NativeMediaManager$NativeMediaManagerInitializer.<clinit>(NativeMediaManager.java:78)
        at com.sun.media.jfxmediaimpl.NativeMediaManager.getDefaultInstance(NativeMediaManager.java:90)
        at com.sun.media.jfxmedia.MediaManager.canPlayProtocol(MediaManager.java:78)
        at com.sun.media.jfxmedia.locator.Locator.<init>(Locator.java:240)
        at com.sun.media.jfxmediaimpl.NativeMediaAudioClip.<init>(NativeMediaAudioClip.java:53)
        at com.sun.media.jfxmediaimpl.NativeMediaAudioClip.load(NativeMediaAudioClip.java:63)
        at com.sun.media.jfxmediaimpl.AudioClipProvider.load(AudioClipProvider.java:66)
        at com.sun.media.jfxmedia.AudioClip.load(AudioClip.java:135)
        at javafx.scene.media.AudioClip.<init>(AudioClip.java:83)
        at main.Sound.playMP3(Sound.java:222)
        at main.GamePanel.startGame(GamePanel.java:1624)
        at main.KeyHandler.keyPressed(KeyHandler.java:126)
        at java.desktop/java.awt.Component.processKeyEvent(Component.java:6574)
        at java.desktop/javax.swing.JComponent.processKeyEvent(JComponent.java:2905)
        at java.desktop/java.awt.Component.processEvent(Component.java:6393)
        at java.desktop/java.awt.Container.processEvent(Container.java:2266)
        at java.desktop/java.awt.Component.dispatchEventImpl(Component.java:4991)
        at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2324)
        at java.desktop/java.awt.Component.dispatchEvent(Component.java:4823)
        at java.desktop/java.awt.KeyboardFocusManager.redispatchEvent(KeyboardFocusManager.java:1952)
        at java.desktop/java.awt.DefaultKeyboardFocusManager.dispatchKeyEvent(DefaultKeyboardFocusManager.java:883)
        at java.desktop/java.awt.DefaultKeyboardFocusManager.preDispatchKeyEvent(DefaultKeyboardFocusManager.java:1146)
        at java.desktop/java.awt.DefaultKeyboardFocusManager.typeAheadAssertions(DefaultKeyboardFocusManager.java:1020)
        at java.desktop/java.awt.DefaultKeyboardFocusManager.dispatchEvent(DefaultKeyboardFocusManager.java:848)
        at java.desktop/java.awt.Component.dispatchEventImpl(Component.java:4872)
        at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2324)
        at java.desktop/java.awt.Window.dispatchEventImpl(Window.java:2780)
        at java.desktop/java.awt.Component.dispatchEvent(Component.java:4823)
        at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:775)
        at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:720)
        at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:714)
        at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
        at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
        at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:97)
        at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:747)
        at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:745)
        at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
        at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
        at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:744)
        at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
        at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
        at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
        at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
        at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
        at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)

(((现在在 AWT-EventQueue-0 中出现错误,因为它是项目的完整版本,而不是我之前编写的简化代码)))

我还尝试通过双击运行 .jar,通过 IntelJ IDEA 在我的系统上构建的 .jar 和在 GitHub 工作区上构建的 .jar 都显示此错误:Error: A JNI error has occurred, please check your installation and try again

附言:每当我双击任何 .jar 时,实际上都会出现这个 JNI 视觉错误,这也很奇怪 PS 2:JNI 视觉错误是因为我搞砸了,并且将 Java 1.8 作为默认 JRE

所以它实际上看起来像是我的安装问题,所以我将尝试重新安装JDK,验证系统文件等。

Java JavaFX 音频 URL 方案 不支持操作

评论

2赞 jewelsea 7/21/2023
您的示例应用程序正在播放音频应用程序,而没有显式启动 JavaFX 运行时或启动 JavaFX 应用程序,这将隐式启动 JavaFX 运行时。如果 JavaFX 运行时未运行,我不知道是否支持使用 JavaFX 媒体播放音频(我不会尝试)。
2赞 Slaw 7/21/2023
@jewelsea “如果 JavaFX 运行时未运行,我不知道是否支持使用 JavaFX 媒体播放音频”——它对我有用,但是是的,我不会依赖它。
2赞 g00se 7/21/2023
它肯定会对资源类型感到困惑,但正如@jewelsea所说,您正在以错误的方式初始化 FX 环境,因此这无济于事。如果有任何用处,我可以发布指向工作版本的链接。我说工作,但实际上主线程对我来说没有完成,这表明我有一点问题
1赞 Slaw 7/21/2023
@g00se 有趣。以与 OP 相同的方式玩,我不得不添加一个调用来阻止主线程过早退出(这导致 JVM 过早退出)。显然,尝试使用外部 JavaFX 应用程序会导致意外行为,老实说,这并不奇怪。AudioClipThread.sleepAudioClip
2赞 Slaw 7/21/2023
@kofiy 堆栈跟踪和线程的存在表明您正在使用 AWT 或 Swing 作为 UI 框架,但随后尝试使用 JavaFX 播放音频。不建议混合使用这样的 UI 框架。但是,如果必须这样做,则必须管理哪个 UI 线程正在适当地执行某些代码。首先,初始化 JavaFX 平台(有几种方法可以做到这一点)。然后,确保与 的任何交互都发生在 JavaFX 应用程序线程上,而不是 EDT 上。AWT-EventQueue-0AudioClip

答:

3赞 g00se 7/21/2023 #1

这不是一个答案,但我把它放在那里是为了向你展示我会发生什么。在这两种情况下,都会播放声音,我使用 your 打印到 stdout。从 jar 运行它时:toExternalForm

goose@t410:/tmp/sound$ java -cp /tmp/sound/target/sound-1.0-SNAPSHOT.jar fx.sound.App
jar:file:/tmp/sound/target/sound-1.0-SNAPSHOT.jar!/sound/ambience/testSound.mp3

然后使用 Maven:

goose@t410:/tmp/sound$ mvnt javafx:run
[INFO] Scanning for projects...
[INFO] 
[INFO] -----------------------< com.technojeeves:sound >-----------------------
[INFO] Building sound 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] >>> javafx-maven-plugin:0.0.6:run (default-cli) > process-classes @ sound >>>
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ sound ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 1 resource
[INFO] 
[INFO] --- maven-compiler-plugin:3.8.0:compile (default-compile) @ sound ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] <<< javafx-maven-plugin:0.0.6:run (default-cli) < process-classes @ sound <<<
[INFO] 
[INFO] 
[INFO] --- javafx-maven-plugin:0.0.6:run (default-cli) @ sound ---
[INFO] Toolchain in javafx-maven-plugin null
[WARNING] Module name not found in <mainClass>. Module name will be assumed from module-info.java
file:/tmp/sound/target/classes/sound/ambience/testSound.mp3
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  7.320 s
[INFO] Finished at: 2023-07-20T23:13:32+01:00
[INFO] ------------------------------------------------------------------------
goose@t410:/tmp/sound$ 

用于运行剪辑的代码:

package main;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.stage.Stage;
import javafx.scene.media.AudioClip;

public class Main extends Application {
    @Override
    public void start(Stage stage) {
        AudioClip clip = new AudioClip(Main.class.getResource("/sound/ambience/testSound.mp3").toExternalForm());
        System.out.println(Main.class.getResource("/sound/ambience/testSound.mp3").toExternalForm());
        clip.play();
        //Platform.exit();
    }


    public static void main(String[] args) {
        launch();
    }

}

评论

0赞 trashgod 7/21/2023
有趣的是,在 之后,该方案是jlinkjrt:/…
2赞 jewelsea 7/21/2023
@trashgod jrt:/ 方案在“JEP 220:模块化运行时映像”下的“用于命名存储模块、类和资源的新 URI 方案”一节中进行了讨论。
0赞 kofiy 7/21/2023
我明白了,除了 Windows 和 Linux 之间的差异之外,看起来几乎一样。jar:file:/C:/Users/Kofiy/test.jar!/sound/ambience/testSound.mp3
2赞 g00se 7/21/2023
如果从 jar 运行,这就是您想要的
1赞 kofiy 7/21/2023 #2

最终,问题实际上是 JavaFX 没有被初始化。

作为一个简单的测试解决方法,我创建了非常简单的 JavaFX 应用程序,它只启动实际的 JavaX 应用程序。

(尽管将来我可能会重写一大段代码以避免这种非常糟糕的解决方法。(但至少它现在有效......