我正在编写一个实时做大量工作的程序,它从视频处理图像并在JavaFx ImageView
上显示图像,问题是我无法更新主线程组件从另一个线程来看,java不是线程安全的,所以我使用Timer
的方式代替,一个线程一直滞后,它主要是挂起,所以下面是我如何实现我的代码
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());
这似乎工作得更好,但我的整个GUI
都很懒散,有人可以建议我更好的方式来处理视频和更新我的UI
而不会造成任何滞后吗?
答案 0 :(得分:1)
如果我正确理解了您的问题,您基本上希望通过ImageView
显示视频,确保您不会通过快速发送图像来淹没FX应用程序线程,而不是显示它们。是吗?
如果是这样,您可以执行以下操作:
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);
在此代码中,您确保不要使用太多请求来泛滥FX应用程序线程。计时器任务将latestImage
设置为它抓取的最新图像。 Platform.runLater()
runnable获取图片并将latestImage
设置为null
,表示已为新图片做好准备。如果最后一个已经被消耗,则计时器仅将新的runnable调度到Platform.runLater()
。结果是FX应用程序线程将消耗尽可能多的图像,但如果图像的生成速度比它可以消耗的速度快,那么将跳过中间图像。
使用AtomicReference
可确保检索值并在latestImage
中设置新值以原子方式进行管理,确保没有竞争条件。
我假设您在这里修改了processVideo()
方法,以便它返回它抓取的图像:
private Image processVideo() {
Image image = ... // grab image
return image ;
}