Issue
I have a highschool capstone where I must create a music player which plays music. However, whenever I connect my bluetooth headphones(airpods or musicozy) and then disconnect them, the MediaPlayer halts and an error is produced. I've searched on the internet for an answer but am unable to find one. If someone could help me that would be great! I'm using Javafx 17.0.2 and JDK 11.
Here's a mini reproducible example below.
JavaFxMediaPlayer
package apprunner;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.stage.Stage;
public class JavaFxMediaPlayer extends Application {
public static void main(String[] args) throws MalformedURLException, IOException {
launch(args);
}
@Override
public void start(Stage stage) throws Exception {
stage.setTitle("My");
Button button = new Button("Play Song");
Scene scene = new Scene(button, 200, 100);
stage.setScene(scene);
stage.show();
File file = new File("C:\\Users\\John Doe\\OneDrive\\Desktop\\YourLieInAprilTest\\Mp3Test.mp3");
String path = file.toURI().toASCIIString();
Media media = new Media(path);
MediaPlayer mediaPlayer = new MediaPlayer(media);
button.setOnAction(new EventHandler() {
@Override
public void handle(Event arg0) {
mediaPlayer.stop();
mediaPlayer.play();
}
});
Runnable printStackTrace = new Runnable() {
public void run() {
mediaPlayer.getError().getMessage();
mediaPlayer.getError().printStackTrace();
}
};
mediaPlayer.setOnError(printStackTrace);
}
}
module-info
module MotisHarmony {
requires javafx.swt;
requires javafx.base;
requires javafx.controls;
requires javafx.fxml;
requires javafx.graphics;
requires javafx.media;
requires javafx.swing;
requires javafx.web;
exports mediaplayerjavafx;
opens mediaplayerjavafx to javafx.graphics;
}
Error Produced
MediaException: PLAYBACK_HALTED : IDirectSoundBuffer_GetStatus The operation completed successfully.
, IDirectSoundBuffer_GetCurrentPosition: The operation completed successfully.
, dwStatus: 0
at javafx.media/javafx.scene.media.MediaException.haltException(MediaException.java:150)
at javafx.media/javafx.scene.media.MediaPlayer$_PlayerStateListener.lambda$onHalt$7(MediaPlayer.java:2566)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:457)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:456)
at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:184)
at java.base/java.lang.Thread.run(Thread.java:834)
New JavaFxMediaPlayer Fix Attempt (Seek not working)
package apprunner;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.stage.Stage;
import javafx.util.Duration;
public class AppRunner extends Application {
public Duration durationBackup = null;
MediaPlayer mediaPlayer;
public static void main(String[] args) throws MalformedURLException, IOException {
launch(args);
}
@Override
public void start(Stage stage) throws Exception {
stage.setTitle("My");
Button button = new Button("Play Song");
Scene scene = new Scene(button, 200, 100);
stage.setScene(scene);
stage.show();
File file = new File("C:\\Users\\John Doe\\OneDrive\\Desktop\\YourLieInAprilTest\\Mp3Test.mp3");
String path = file.toURI().toASCIIString();
Media media = new Media(path);
mediaPlayer = new MediaPlayer(media);
button.setOnAction(new EventHandler() {
@Override
public void handle(Event arg0) {
mediaPlayer.stop();
mediaPlayer.play();
}
});
mediaPlayer.currentTimeProperty().addListener(new InvalidationListener() {
public void invalidated(Observable ov) {
//Here we keep a backup of the current duration of the song just incase the mediaPlayer crashes, which it does everytime you disconnect a bluetooth headset for some reason
durationBackup = mediaPlayer.getCurrentTime();
}
});
//Here I try to create a new MediaPlayer and go to the last position we were at before the mediaPlayer halted
Runnable attemptToResetMediaPlayer = new Runnable() {
public void run() {
mediaPlayer = new MediaPlayer(media);
mediaPlayer.play();
System.out.println(durationBackup.toMillis());
mediaPlayer.seek(durationBackup);
}
};
mediaPlayer.setOnError(attemptToResetMediaPlayer);
}
}
How to recreate bug
- First you must connect your bluetooth headphones to your laptop/computer
- Then you must run the mini reproducible example above
- Then you must click on the "Play Song" button which appears
- Then when the song is playing, disconnect your bluetooth headphones
- The mediaPlayer will halt and the error message and its stack trace will be printed
Extra Information
Link to the Mp3 file I used to test. https://drive.google.com/file/d/1CvAafbMviQ7nvKyojnem9GK73LJsD6MJ/view?usp=sharing
I am using JDK 11 and Javafx 17.0.2
System Type: 64-bit operating system, x64-based processor
Processor: Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz 2.81 GHz
Windows Edition: Windows 10 Home
Solution
The problem with your recovery is that you are seeking too soon. The docs for seek state that it does nothing while the media player is stopped, but you aren't waiting for the state to change after calling play().
Try this:
mediaPlayer.setOnError( () -> {
mediaPlayer = new MediaPlayer(media);
System.out.println(durationBackup.toMillis());
mediaPlayer.setOnPlaying(() ->{
mediaPlayer.seek(durationBackup);
mediaPlayer.setOnPlaying(null);
});
mediaPlayer.play();
});
Answered By - swpalmer
Answer Checked By - Senaida (JavaFixing Volunteer)