JavaFX MediaPlayer - seek() method makes the player hang - javafx

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();

Related

Is it possible to launch a JavaFX application through another JavaFX application?

Can I know why there is an error when I say.
Stage s = new Stage();
new CaeserCipherFX().start(s);
This is my code below. I need to launch another JavaFX Application from this one. Please help. Thank you.
public class Main extends Application
{
String args[];
#Override
public void start(Stage stage) throws Exception
{
// creating types of encryptions (Button)
Button caeserCipher = new Button("1. Caeser Cipher");
Button runningKeyCipher = new Button("2. Running Key Cipher");
Button trithemiusCipher = new Button("3. Trithemius Cipher");
Button vignereCipher = new Button("4. Vignere Cipher");
//setting styles
caeserCipher.setTextFill(Color.BLUE);
runningKeyCipher.setTextFill(Color.BLUE);
trithemiusCipher.setTextFill(Color.BLUE);
vignereCipher.setTextFill(Color.BLUE);
/*need to add more!*/
//setting action listeners
String arr [] = {"CaeserCipher","RunningKeyCipher","TrithemiusCipher","VignereCipher"};
caeserCipher.setOnAction((ActionEvent event)->{
//open caeser cipher
Stage s = new Stage();
new CaeserCipherFX().start(s);
});
runningKeyCipher.setOnAction((ActionEvent event)->{
//open running key cipher
stage.hide();
});
trithemiusCipher.setOnAction((ActionEvent event)->{
//open trithemius cipher
stage.hide();
});
vignereCipher.setOnAction((ActionEvent event)->{
//open vignere cipher
stage.hide();
});
// creating flowpane(FlowPane)
FlowPane menu = new FlowPane();
menu.setHgap(25);
menu.setVgap(25);
menu.setMargin(caeserCipher, new Insets(20, 0, 20, 20));
//list for Flowpane(ObservableList)
ObservableList list = menu.getChildren();
//adding list to flowpane
list.addAll(caeserCipher,runningKeyCipher,trithemiusCipher,vignereCipher);
//scene for stage
Scene scene = new Scene(menu);
stage.setTitle("Main Menu");
stage.setScene(scene);
// stage.initStyle(StageStyle.UTILITY);
stage.setHeight(100);
stage.setWidth(600);
stage.setResizable(false);
// Show the Stage (window)
stage.show();
}
}
And I want to launch the code below:
public class CaeserCipherFX extends Application
{
#Override
public void start(Stage stage) throws Exception
{//some other code
//some other code
}
}
There is a ubiquitous JavaFX main application thread which takes a while to get used to.
Think of it like the front-end thread. Theoretically, you should use that thread to handle UI updates and complex cpu tasks such as looking up something in a BD or figuring out the 100000th decimal of PI should be done in a background thread. If you don't do this, the UI will become unresponsive until the DB data is returned, or that decimal is found.
public class TestClass extends Application {
public static void main(String[] args) {
System.out.println("here");
Application.launch(TestClass.class, args);
System.out.println("this is called once application launch is terminated.");
}
#Override
public void init() throws Exception {
super.init(); //To change body of generated methods, choose Tools | Templates.
System.out.println("message from init");
}
#Override
public void start(Stage primaryStage) throws Exception { // this is abstract.
System.out.println("message from start");
Platform.exit(); // if you remove this line, the application won't exit.
}
}
Since JavaFX comes with some prerequisites, you need to start you rapplication using a front-end. You can work around this, but technically,
public void start(Stage primaryStage)
is what , for all intensive purposes, starts your program.
From here, you can use the primaryStage to control most of your application. It's a good idea to put a .onCloseRequest() on it in which you call Platform.exit();
If you want to have multiple windows in your application, you could use something like
public class TestClass extends Application {
public static void main(String[] args) {
System.out.println("here");
Application.launch(TestClass.class, args);
System.out.println("this is called once application launch is terminated.");
}
#Override
public void init() throws Exception {
super.init(); //To change body of generated methods, choose Tools | Templates.
System.out.println("message from init");
}
#Override
public void start(Stage primaryStage) throws Exception { // this is abstract.
primaryStage.setScene(new Scene(new TextArea("this is the first stage (window)")));
primaryStage.setTitle("stage 1");
primaryStage.show();
primaryStage.setOnCloseRequest((event) -> {
Platform.exit();
});
Stage secondaryStage = new Stage();
secondaryStage.setTitle("stage 2");
TextArea ta2 = new TextArea("this is a different stage.");
Scene scene = new Scene(ta2);
secondaryStage.setScene(scene);
secondaryStage.show();
primaryStage.setX(200);
secondaryStage.setX(200 + primaryStage.getWidth() + 50);
}
}
This is what I assume you want to do. Basically create a new window whenever you press a button. You can create stages like this.
The reason for which you can't do it your way is because you are attempting to start another javafx thread by invoking new CaeserCipherFX which is an application object, not a Stage.
new CaeserCipherFX().start(s); // this can only be called once.
IF you absolutely want to have 2 distinct applications (note: not application windows), then you need to have 2 distinct processes.
Lastly, the primaryStage parameter used in either examples is in the beginning basically a placeholder (as in it's constructed, but there's nothing really in it... like a new String()). You can use different stage objects as your "primary" UI.
Lastly, if depending on the stuff you want to decrypt, you may need to use background threads if you want to keep the UI responsiveness. For this you will need to check out the concurrency part of the javafx tutorial.
Is it possible to launch a JavaFX application through another JavaFX application? Not really.
Alternatively, you can use java.lang.ProcessBuilder
This class essentially sends command lines to your operating system shell.
You can use it to run something like "java -jar XXX\YYY\CaeserCipherFX.jar" whenever you click a button. (you'll have to build a CaeserCypherFX project into a jar file)
This will create a new JVM. This means no memory state sharing. You can handle this through IPC.

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();
}
});

Asynctask read xml parallely in viewpager get crashed in android

I use xml parser to read asynctask parallel (multiple thread) in viewpager.
Here my problem is while I swipe my viewpager fastly app get crashed.
Here is my code
private void doTheAutoRefresh(final int n) {
final ExecutorService es = Executors.newFixedThreadPool(7);
//final CountDownLatch latch = new CountDownLatch(n);
t=new Thread(){
#Override
public void run() {
this.setPriority(10);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
load =new LoadWebPageASYNC(n);
load.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}else{
load=new LoadWebPageASYNC(n);
load.execute();}
}
};
t.setPriority(10);
es.execute(t);
}
Could you please help me what I am gets wrong.
I think the problem is with update current fragment.
I am new to android so please guide me for right direction.
I do not understand why you are creating a thread pool and then start single new Thread. At first sight it seems you do not want to call final ExecutorService es = Executors.newFixedThreadPool(7); every time you call doTheAutoRefresh
do you start a new Thread in LoadWebPageASYNC(n) as well ?
Maybe you can post the error message and a bit more code.
kind regards
Anton

JavaFX auto-scroll auto-update text

Newbie question about JavaFX that I haven't been able to answer, despite knowing it must be pretty simple to do and not finding any resources on it anywhere I've looked (tutorials, many of the Oracle online docs, articles, the well-known JavaFX bloggers, etc.)
I'm developing a command line (script) running application and I have successfully gotten output (via ProcessBuilder) from the script that I can display in an ongoing manner, as things happen on the command line. That is, I can do System.out.println(line); all day long, showing the output in the console, which simply returns output from an input stream returned by the 'myProcess' that's running, created like this:
BufferedReader bri = new BufferedReader(new InputStreamReader(myProcess.getInputStream()))
So I am able to see all the output coming back from the script.
I'd like to set-up a JavaFX TextArea or ScrollPane or, not sure what, to display this output text (there's a lot of it, like several thousand lines) as an ongoing 'progress' of what's taking place in the script, as it happens. I have a Scene, I have buttons and get input from this scene to start the script running, but now I'd like to show the result of clicking the button "RUN THIS SCRIPT", so to speak.
I assume I need to create a TextArea as described here or perhaps a TextBuilder would be useful to begin making it. Not sure.
I need a bit of help in how to setup the binding or auto-scroll/auto-update part of this.
Can someone provide me a place to start, to do this with JavaFX? I'd rather not use Swing.
(I'm using JavaFX 2.2, JDK 1.7u7, all the latest stuff, and yes, this is an FXML app--so doing it that way would be preferred.)
UPDATE: Sergey Grinev's answer was very helpful in the binding part. But here is some more detail on what I mean when I ask for "a bit of help in how to setup" -- basically, I need to return control to the main Scene to allow the user to Cancel the script, or to otherwise monitor what's going on. So I'd like to "spawn" the process that runs that script (that is, have some kind of 'free running process'), but still get the output from it. (I wasn't very clear on that in my initial question.)
The technique I'm using here (see below) is to do a waitFor on the process, but of course this means the dialog/Scene is 'hung' while the script executes. I'd like to gain control back, but how do I pass the 'p' (Process) to some other controller piece (or alternatively, simply kick off that other process passing in the parameters to start the script and have it start the script) that will then do the auto-update, via the binding Sergey Grinev mentions--without 'hanging' the Scene/window? Also: Can I then 'stop' this other process if the user requests it?
Here is my current code ('waits' while script--which takes 20-40 min to run!--completes; this is not what I want, I'd like control returned to the user):
public class MyController implements Initializable {
#FXML
private void handleRunScript(ActionEvent event) throws IOException {
ProcessBuilder pb = new ProcessBuilder("myscript.sh", "arg1", "arg2", ...);
Process p = pb.start();
try {
BufferedReader bri = new BufferedReader
(new InputStreamReader(p.getInputStream()));
String line;
while ((line = bri.readLine()) != null) {
System.out.println(line);
textAreaRight.setText(line);
}
bri.close();
p.waitFor();
}
catch (Exception err) {
err.printStackTrace();
}
}
#FXML
private void handleCancel(ActionEvent event) {
doSomethingDifferent();
}
}
To log strings you can use TextArea
To make it asynchronious you need to make a separate thread for output reader.
public class DoTextAreaLog extends Application {
TextArea log = new TextArea();
Process p;
#Override
public void start(Stage stage) {
try {
ProcessBuilder pb = new ProcessBuilder("ping", "stackoverflow.com", "-n", "100");
p = pb.start();
// this thread will read from process without blocking an application
new Thread(new Runnable() {
#Override
public void run() {
try {
//try-with-resources from jdk7, change it back if you use older jdk
try (BufferedReader bri = new BufferedReader(new InputStreamReader(p.getInputStream()))) {
String line;
while ((line = bri.readLine()) != null) {
log(line);
}
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}).start();
stage.setScene(new Scene(new Group(log), 400, 300));
stage.show();
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
public void stop() throws Exception {
super.stop();
// this called on fx app close, you may call it in user action handler
if (p!=null ) {
p.destroy();
}
}
private void log(final String st) {
// we can access fx objects only from fx thread
// so we need to wrap log access into Platform#runLater
Platform.runLater(new Runnable() {
#Override
public void run() {
log.setText(st + "\n" + log.getText());
}
});
}
public static void main(String[] args) {
launch();
}
}

Resources