Wendie Lamero
Wendie Lamero

Reputation: 59

Multi threading issues in Java JavaFX application

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

Answers (1)

James_D
James_D

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

Related Questions