Reputation: 59
I am writing a program that is to do quite a lot of work in real time, its to process Images from a video and display the Images on a JavaFx ImageView
, the issue is I cannot update main thread components from another thread, java is not thread safe so I though of a way of using a Timer
instead, a thread keeps lagging behind, it mostly hangs, so below is how I have implemented my code
TimerTask frame_grabber = new TimerTask()
{
@Override
public void run()
{
processVideo();
Platform.runLater(new Runnable() {
@Override public void run() {
imageView.setImage(tmp);
}
});
}
};
timer = new Timer();
Double period = 1000 / getFPS() * 2;
this.timer.schedule(frame_grabber, 0, period.longValue());
This seems to work better but the my entire GUI
is laggying, can someone suggest me a better way of processing the video and updating my UI
without causing any lags?
Upvotes: 0
Views: 311
Reputation: 209225
If I understand your question correctly, you basically want to display a video via an ImageView
, making sure that you don't flood the FX Application Thread by sending it images faster than it can display them. Is that right?
If so, you can do something like:
AtomicReference<Image> latestImage = new AtomicReference<>();
TimerTask frameGrabber = new TimerTask() {
@Override
public void run() {
if (latestImage.setAndGet(processVideo()) == null) {
Platform.runLater(() -> imageView.setImage(latestImage.setAndGet(null)));
}
}
};
// rate at which you want to sample video:
double sampleRate = ... ;
long sampleMillis = (long) 1000 / sampleRate ;
this.timer.schedule(frameGrabber, 0, sampleMillis);
In this code, you ensure you don't flood the FX Application thread with too many requests. The timer task sets latestImage
to the latest image it has grabbed. The Platform.runLater()
runnable gets an image and sets latestImage
to null
, indicating it is ready for a new image. The timer only schedules a new runnable to Platform.runLater()
if the last one has been consumed. The result is that the FX Application Thread will consume as many images as it can, but if images are produced more quickly than it can consume then, intermediate images will be skipped.
Using the AtomicReference
ensures that retrieving a value and setting a new value in latestImage
are managed atomically, making sure there are no race conditions.
I'm assuming a modification to your processVideo()
method here, so that it returns the image it grabs:
private Image processVideo() {
Image image = ... // grab image
return image ;
}
Upvotes: 1