我正在Java程序中实现视频捕获,方法是在用户定义的时间间隔(对于我的测试,100ms)创建BufferedImage
,然后使用这些图像制作电影文件。我尝试记录的JFrame
包含JLayeredPane
包含的类似仪表板的界面。 JFrame
也有两个Canvas3D
。我告诉render
或paint
这3件事中的每一件事都是他们自己的LinkedBlockingDeque<BufferedImage>
,我稍后再将它们合并。仪表板设置为仅渲染每个dashboardFrameRepaintFrequency
帧。
Thread captureThread = new Thread(new Runnable() {
public void run() {
final Object LOCK = new Object();
final Thread captureDashboard = new Thread(new Runnable() {
public void run() {
while (m_isCapturingMovie) {
synchronized (LOCK) {
try {
LOCK.wait();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.err.println("Calling getDashboardImage");
m_unsavedDash.add(getDashboardImage(m_dashboard.getWidth(), m_dashboard.getHeight(), BufferedImage.TYPE_INT_ARGB));
System.err.println("captureDashboard returned from calling m_unsavedDash.add...");
}
}
}
}
});
captureDashboard.start();
while (m_isCapturingMovie) {
startTime = System.currentTimeMillis();
captureCanvases();
if (++frameCount > dashboardFrameRepaintFrequency) {
frameCount = 0;
synchronized (LOCK) {
LOCK.notify();
}
}
endTime = System.currentTimeMillis();
millisToSleep = captureDelayInMillis - (endTime - startTime);
if (millisToSleep > 0) {
try {
Thread.sleep(millisToSleep);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
synchronized (captureDashboard) {
captureDashboard.notify();
}
}
});
我发现在15-20 notify()
之后,程序锁定了 - 它停止记录画布,停止响应键盘输入..我仍然可以改变哪个窗口有焦点,以及按钮(例如关闭窗口的X按钮)仍然会改变鼠标翻转或点击的视觉状态,但它们不会执行命令。
从控制台输出看来,在那些15-20次迭代之后,captureDashboard线程似乎没有从getDashboardImage
方法返回:
private BufferedImage getDashboardImage(int width, int height, int type) {
BufferedImage dashImg = new BufferedImage(m_dashboard.getWidth(), m_dashboard.getHeight(), type);
Graphics2D g = dashImg.createGraphics();
m_dashboard.paintAll(g);
g.dispose();
return getScaledImage(width, height, dashImg);
}
private BufferedImage getScaledImage(int width, int height, BufferedImage original) {
BufferedImage scaled = new BufferedImage(width, height, original.getType());
AffineTransform at = new AffineTransform();
at.scale((double) scaled.getWidth() / original.getWidth(), (double) scaled.getHeight() / original.getHeight());
AffineTransformOp scaleOp;
if (at.getScaleX() + at.getScaleY() > 2.0) {
scaleOp = new AffineTransformOp(at, AffineTransformOp.TYPE_BICUBIC); // Better quality for enlargement
} else {
scaleOp = new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR); // Better quality for ensmallment
}
scaled = scaleOp.filter(original, scaled);
original.flush();
return scaled;
}
有什么想法吗?我已经在这工作了几天而且我很难过。
答案 0 :(得分:1)
问题是我需要从AWT调度线程中调用paintAll
。
所以而不是:
m_dashboard.paintAll(g);
我需要:
final Graphics2D g = dashImg.createGraphics();
EventQueue.invokeLater(new Runnable() {
public void run () {
m_dashboard.paintAll(g);
}
});
然而,这导致我的程序“超越自己”并在它被绘制之前返回BufferedImage
,但仅在程序负载很重的情况下。为了解释这一点,我刚刚添加了以下内容:
final Graphics2D g = dashImg.createGraphics();
final SyncObj LOCK = new SyncObj();
EventQueue.invokeLater(new Runnable() {
public void run () {
m_dashboard.paintAll(g);
LOCK.doNotify();
}
});
LOCK.doWait();
g.dispose();
SyncObj只是一个简单的地方:
class SyncObj {
private boolean condition = false;
private Object obj = new Object();
public void doWait() {
synchronized (obj) {
while (!condition) {
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
condition = false;
}
}
public void doNotify() {
synchronized (obj) {
condition = true;
obj.notify();
}
}
}