JavaFX MediaPlayer不播放M4A文件

时间:2016-12-19 21:35:01

标签: java audio javafx javasound

给出以下代码:

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。但是,这并不能解释为什么只有MP3个文件而不是WAVMediaPlayer文件才会出现这种情况。由于某种原因,M4A可能会将require_once('TwitterAPIExchange.php'); $connection = array('oauth_access_token' => 'xxx', 'oauth_access_token_secret' => 'xxx', 'consumer_key' => 'xxx', 'consumer_secret' => 'xxx'); try { $twitter = new TwitterAPIExchange($connection); echo 'Connection success <br /> '; $response = $twitter->buildOauth('https://api.twitter.com/1.1/statuses/update.json', 'POST')->setPostfields(array('status' => 'Test Tweet'))->performRequest(); $json = json_decode($response, true); if (!empty($json['id_str'])) echo 'Post success: '.$json['id_str']; } catch (Exception $ex) { echo $ex->getMessage(); } 文件视为视频文件而不是音频文件。然而,这是所有猜测,我不确定为什么会发生这种情况。

1 个答案:

答案 0 :(得分:2)

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

来自How Garbage Collection Works

garbage collector

添加代码时:

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

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

另请注意MediaPlayer javadoc

  

MediaPlayer的操作本质上是异步的。玩家不准备立即响应命令,直到其状态转换为MediaPlayer.Status.READY,这实际上通常发生在媒体预滚动完成时。

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

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