Given the following code:
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);
}
}
The audio file (audio.ext
) is supposed to play when I click on the screen. I can get MP3
files and WAV
files to play audio. However when I try the same code using an M4A
file, the audio does not play.
I noticed some interesting cases when modifying the code slightly to fix the problem.
Case 1: Making the MediaPlayer an instance variable instead of a local variable.
If I make the MediaPlayer
object (mp
) an instance variable and initialize it in the setOnMouseClicked
block, the audio plays as it should and I have no issues.
Case 2: Adding the following code to the end of the setOnMouseClicked
block:
MediaView mv = new MediaView(mp);
stackPane.getChildren().add(mv);
If I do this, then the audio plays as it should, and the screen does not visually change (i.e., adding the MediaView
object to the StackPane
does not visually alter it).
My question is: Why does this happen, and is there any way to get the audio to play without having to use these workarounds?
One suspicion I have is that an external reference to the object is necessary for the MediaPlayer
to work. In Case 1, the instance variable served as the external reference, and in Case 2, the StackPane
held a reference to the MediaView
which in turn had a reference to the MediaPlayer
. However, this does not explain why this only occurs with M4A
files and not MP3
or WAV
files. Perhaps MediaPlayer
treats M4A
files as video files instead of audio files for some reason. However, this is all speculation and I do not know for sure why this is occurring.
When you don't store a reference to an object, the Java garbage collector can clean it up when the reference goes out of scope.
From How Garbage Collection Works:
When you add the code:
A reference to the media player is retained in the MediaView and the StackPane, so the media player is not garbage collected. However, when you don't have the code that retains the reference, then the MediaPlayer can be garbage collected at any time.
Also note the MediaPlayer javadoc:
Because the operation is asynchronous, if you don't store a reference to the MediaPlayer, then the code can be subject to a race condition, where the garbage collector cleans up the MediaPlayer before the MediaPlayer completes playing your media. By the nature of race conditions, the behavior of code that is subject to them is unpredictable, which is usually an undesirable feature.
As to why MP3 and WAV files are playing without you retaining a reference to the MediaPlayer, that could be just luck. The garbage collection can occur at any time that you no longer have a reference to the MediaPlayer. So playing Media in a MediaPlayer that you don't retain a reference to isn't a behavior I would rely on. Instead, it would be best to retain a reference to the MediaPlayer at least until it has completed playing its associated media.