JavaFX MediaPlayer 不播放 M4A 文件

JavaFX MediaPlayer not playing M4A files

给定以下代码:

import java.io.File;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.stage.Stage;

public class Main extends Application {

    @Override
    public void start(Stage stage) throws Exception {
        StackPane stackPane = new StackPane();
        stackPane.setOnMouseClicked((event) -> {
            String path = "audio.ext";
            Media media = new Media(new File(path).toURI().toString());
            MediaPlayer mp = new MediaPlayer(media);
            mp.setAutoPlay(true);
        });
        stage.setScene(new Scene(stackPane));
        stage.show();
    }

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

音频文件 (audio.ext) 应该在我点击屏幕时播放。我可以获得 MP3 个文件和 WAV 个文件来播放音频。但是,当我使用 M4A 文件尝试相同的代码时,音频无法播放。

我在稍微修改代码以解决问题时注意到一些有趣的情况。

案例 1:将 MediaPlayer 设为实例变量而不是局部变量。

如果我将 MediaPlayer 对象 (mp) 设为实例变量并在 setOnMouseClicked 块中对其进行初始化,音频将正常播放,我没有任何问题。

案例 2:在 setOnMouseClicked 块的末尾添加以下代码:

MediaView mv = new MediaView(mp);
stackPane.getChildren().add(mv);

如果我这样做,那么音频会正常播放,并且屏幕不会在视觉上发生变化(即,将 MediaView 对象添加到 StackPane 不会在视觉上改变它)。


我的问题是:为什么会发生这种情况,是否有任何方法可以在不使用这些解决方法的情况下播放音频?

我怀疑 MediaPlayer 需要对对象的外部引用。在案例 1 中,实例变量用作外部引用,在案例 2 中,StackPane 持有对 MediaView 的引用,后者又引用 MediaPlayer。但是,这并不能解释为什么这只发生在 M4A 文件而不是 MP3WAV 文件上。也许 MediaPlayer 出于某种原因将 M4A 文件视为视频文件而不是音频文件。然而,这都是猜测,我不确定为什么会这样。

当您不存储对对象的引用时,Java 垃圾收集器可以在引用超出范围时清理它。

来自How Garbage Collection Works

添加代码时:

MediaPlayer mp = new MediaPlayer(media);
MediaView mv = new MediaView(mp);
stackPane.getChildren().add(mv);

对媒体播放器的引用保留在 MediaView 和 StackPane 中,因此媒体播放器不会被垃圾回收。但是,当您没有保留引用的代码时,MediaPlayer 可以随时被垃圾回收。

还要注意 MediaPlayer javadoc:

The operation of a MediaPlayer is inherently asynchronous. A player is not prepared to respond to commands quasi-immediately until its status has transitioned to MediaPlayer.Status.READY, which in effect generally occurs when media pre-roll completes.

因为操作是异步的,如果你不存储对 MediaPlayer 的引用,那么代码可能会受到 race condition,垃圾收集器在 MediaPlayer 完成播放之前清理 MediaPlayer你的媒体。根据竞争条件的性质,受其影响的代码的行为是不可预测的,这通常是不受欢迎的特征。

至于为什么在没有保留对 MediaPlayer 的引用的情况下播放 MP3 和 WAV 文件,这可能只是运气。垃圾收集可以在您不再具有对 MediaPlayer 的引用的任何时候发生。因此,在您不保留引用的 MediaPlayer 中播放媒体不是我所依赖的行为。相反,最好至少保留对 MediaPlayer 的引用,直到它完成关联媒体的播放。