JavaFx media Player onEndOfMedia is not working - javafx

I'm currently trying to play a resource video on the mediaPlayer and when the video finishes I want to change scene. For this, I need to detect when the video finished.
I have the following code, but it doesn't work:
Media media = new Media(FireDarts.class.getResource("/videos/MicroPhoton_Intro.mp4").toString());
MediaPlayer mediaPlayer = new MediaPlayer(media);
mediaPlayer.setAutoPlay(true);
MediaView mediaView = new MediaView(mediaPlayer);
mediaPlayer.setOnEndOfMedia(new Runnable() {
#Override
public void run()
{
System.out.println("test");
}
});
With this code, after the video finished, it should print "Test" on the output console, but it doesn't. Now here goes the really weird part: If I upload the same video to my webserver and call it directly from there, it works! Assuming this, I came (maybe wrong) conclusion that the way I get the video from the resource is wrong.

Related

How to wait for JavaFx MediaPlayer to finish

I have a MediaPlayer object which plays a video. When the video is finished playing, I want the program to hide the MediaView and display different JavaFx entities. However, when I call player.play() the program immediately interprets the lines of code which follow, resulting in the MediaView not being visible at all.
public class Level1Controller implements Initializable {
File file = new File("ngnl.mp4");
Media media = new Media(file.toURI().toString());
MediaPlayer player = new MediaPlayer(media);
#FXML
MediaView view = new MediaView();
/**
* Initializes the controller class.
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
view.setMediaPlayer(player);
player.play();
view.setVisible(false);
}
}
More buttons and such would be "under" the MediaView in the JavaFx hierarchy, so when it is hidden, the underlying stuff would be seen.
Can't find any official documents that mention this but I'm pretty sure the media file is played on a separate thread under the hood by JavaFX so that it doesn't block the UI thread. This is why you need to use MediaPlayer.setOnEndOfMedia:
view.setMediaPlayer(player);
player.setOnEndOfMedia(() -> {
view.setVisible(false);
});
player.play();

JavaFX MediaPlayer - seek() method makes the player hang

I've been looking for ages for direction on this matter and I finally post here.
I have a JavaFX application with MediaPlayer. One day, seeking at a later position in video (that had not been accessed previously) started hanging the player. No status change before it gets to PLAYING, the buffer is loaded, status at READY before I call seek().
First I thought it is because I went out of Application thread, tried to put the MediaPlayer back on the root to be sure, and the seek method worked as before, fast enough for me.
But then for a reason I can't get, it started hanging again all the time, with same symptoms.
Now, even with the most simple code, it hangs too.
I'm desperate, the waiting time can be 30 seconds to reach a position 2 minutes later in the video. Looks like the Media Player is scanning again all video until it finds the good position it's seeking, thus taking more time for a later position. If the position has been accessed before though, seek() won't hang...
Am I the only one with this problem?
I'm on Mac os EL Capitan, but tried on Windows VM too and I get the same behaviour.
Here is a standalone code, but I don't see how it will help, I don't even hope for ppl to reproduce:
public class VideoPlayerExample extends Application {
public static void main(String[] args) throws Exception {
launch(args);
}
#Override
public void start(final Stage stage) throws Exception {
File file = new FileChooser().showOpenDialog(stage);
Media media = new Media(file.toURI().toString());
MediaPlayer mp = new MediaPlayer(media);
mp.statusProperty().addListener(new ChangeListener<MediaPlayer.Status>() {
#Override
public void changed(ObservableValue<? extends Status> observable, Status oldValue, Status newValue) {
System.out.println(newValue);
}
});
Group gp = new Group(new MediaView(mp));
Button buttonTest = new Button("It's gonna hang...");
buttonTest.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
mp.pause();
System.out.println(mp.getCurrentTime().toMillis());
mp.seek(new Duration(mp.getCurrentTime().toMillis() +10000));
mp.play();
}
});
gp.getChildren().add(buttonTest);
stage.setScene(new Scene(gp, 540, 208));
stage.show();
}
}
Any help will be so greatly appreciated!
You're right - I can't reproduce your problem. I have macOS Sierra 10.12.6. All I can say is check the type of movie you're trying to play - not all encodings are supported. Also, according to the documentation, if the movie's duration is Duration.INDEFINITE, seek() will have no effect.
Place the seek method in a new thread not on your JavaFX thread.
new Thread(() -> mp.seek(new Duration(mp.getCurrentTime().toMillis() +10000)))).start();

Java FX showing a LoadingScreen while code and other objects are running/drawn

So I want to display a Loading Screen(simple Stackpane with an ProgressIndicator on top of a blur etc..) over my Desktop Application while another Screen is loading data from the database and drawing itself. Up until now I used a Task in a new Thread to "split" my loading animation running in the FX thread from the logic in the background, problem is this time logic and drawing objects on the screen in the bakckground are heavily intervowen and I cannot split them without stupenduous amount of work.
So I found out about AnimationTimer, which apparently is called on every frame and sends a pulse which causes an equivalent to a repaint() in Swing. And so I tried to use it thus:
public void showApplicationScreen(){
public void addAndShowTransporterStatus() {
AnimationTimer at = new AnimationTimer() {
int i;
#Override
public void handle(long now) {
i++;
LOG.info(i); //testing to see frames per second
}
};
at.start();
showLoadingIndicator(true);
loadDataFromDBandDrawObjects();
showLoadingIndicator(false);
at.stop();
}
Is there some kind of trick to it that I am missing? Or some other (simple) way?
I cant believe something so simple is so complicated to do. Gotta say I wish every Node had a repaintAtIntervall(double timespan) method that would suspend everything else the application is doing atm and repaint itself before continuing with the normal flow. Might not be pretty but it sure as hell would be useful.
You really shouldn't need an AnimationTimer for something like this. If you are doing the initial loading in a background thread, use a Task. Show a loading screen and hide it in the task's onSucceeded handler. You can create node instances in a background thread as long as they are not part of the scene graph, so while it's not a particularly good design, you can do something like:
Task<Parent> createMainScene = new Task<Parent>() {
#Override
public Parent call() {
Parent root = ... ;
// load data etc., create structure below root
// call updateMessage(...) to update a status message if needed
// call updateProgress(...) to update the progress if needed
// ...
return root ;
}
};
ProgressBar pBar = new ProgressBar();
pBar.progressProperty().bind(createMainScene.progressProperty());
Label statusLabel = new Label();
statusLabel.textProperty().bind(createMainScene.messageProperty());
VBox root = new VBox(5, statusLabel, pBar);
Stage loadingStage = new Stage(new Scene(root));
loadingStage.show();
createMainScene.setOnSucceeded(e -> {
primaryStage.setScene(new Scene(createMainScene.getValue()));
primaryStage.show();
loadingStage.hide();
});
new Thread(createMainScene).start();
A better (more properly-separated) design would be to have the task just load the application data and process it, and return an object encapsulating the data. Then in the onSucceeded handler you would create the UI from the data (which should not take a long time). However, it sounds like you cannot do that with the current code with which you're working.

JxBrowser HEAVYWEIGHT Dispose on WINDOW_CLOSE_REQUEST

I'm currently playing around with the Evaluation License for JxBrowser 6.2.
I'm creating the BrowserView as follows:
Browser browser = new Browser(BrowserType.HEAVYWEIGHT);
BrowserView browser_view = new BrowserView(browser);
I'm attaching the BrowserView component as follows:
stage.setScene(new Scene(browser_view));
If the Browser is configured to operate in LIGHTWEIGHT mode, I'm able to execute:
browser_view.getBrowser().dispose();
Platform.exit();
However, if the Browser is configured to operate in HEAVYWEIGHT mode, then the application hangs when executing:
browser_view.getBrowser().dispose();
I can see in the logs that the Dispose message was written, but it appears as though the JxBrowser Chromium processes never receive/process the message.
Any ideas?
As answered before me the solution to this is to dispose the browser after the stage has been hidden (closed).
A good approach would be to put those commands on the stop() method of JavaFX Application.
So that either way you close the window (by clicking the close button or programmatically by calling Platform.exit()), the browser will dispose (and the whole application will finish and exit).
Something like that:
#Override
public void stop() throws Exception {
stage.hide();
browser.dispose();
}
As a reference, I used configuration described here link (Section: 9. Pop-up Windows).
Platform.runLater(() -> {
browser.dispose();
});
Platform.runLater(() -> {
stage.close();
});
It looks like you need to ensure that the stage has been closed before calling dispose.
stage.close();
browser_view.getBrowser().dispose();
Platform.exit();
Please try calling this code asynchronously. Maybe it's just a deadlock:
Platform.runLater(new Runnable() {
#Override
public void run() {
browser_view.getBrowser().dispose();
Platform.exit();
}
});

How to register a Java Object in the JxBrowser before first JavaScript

I am using JxBrowser 6.1.1 and succeed in registering a Java object in the JsContext in a onFinishLoadingFrame handler. The problem is that the property is not yet available when the first JavaScript is run in the page loaded by the JxBrowser.
The working call looks like this:
browser.executeJavaScriptAndReturnValue("window").asObject().setProperty("api", api);
When I do the same in the new onScriptContextCreated handler, the program halts executing:
JSValue jsWindow = browser.executeJavaScriptAndReturnValue("window");
The full code is and fails at line JSValue jsWindow = ...:
client.browser.addScriptContextListener(new ScriptContextAdapter() {
#Override
public void onScriptContextCreated(ScriptContextEvent event) {
Browser browser = event.getBrowser();
JSValue jsWindow = browser.executeJavaScriptAndReturnValue("window");
JSObject windowObject = jsWindow.asObject();
windowObject.setProperty("api", client.getApi());
}
});
I guess the window property is simply not there yet.
Is there a way to achieve this with the onScriptContextCreated event or is there a different way to achieve this?
The code also works fine when started in a separate thread as proposed below. But the problem remains that this code runs later than the first JavaScript in the browser.
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
JSValue jsWindow = browser.executeJavaScriptAndReturnValue("window");
JSObject windowObject = jsWindow.asObject();
windowObject.setProperty("api", client.getApi());
}
});
thread.start();

Resources