0

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?

1
  • You could try to decouple the the scheduled rate of the timer from the UI frame rate. JavaFX frame rate is caped at about 60 fps, and you might want to call the TimerTask more often. To be more precise in any advice you probably should post the code for processVideo as well. Commented Jan 20, 2016 at 6:47

1 Answer 1

1

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 ;
}
Sign up to request clarification or add additional context in comments.

1 Comment

beautiful answer, you saved my life.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.