我通过创建 BufferedImage
在 Java 程序中实现视频捕获s 以用户定义的间隔(对于我的测试,100 毫秒),然后使用这些图像制作电影文件。 JFrame
我试图记录的内容包括一个类似仪表板的界面,由 JLayeredPane
包含. JFrame
还有两个Canvas3D
秒。我把这三件事都告诉了 render
或 paint
融入自己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()
之后s,程序锁定 - 它停止记录 Canvas ,停止响应键盘输入。我仍然可以更改哪个窗口具有焦点,并且按钮(例如关闭窗口的 X 按钮)仍然会因鼠标悬停而改变视觉状态或点击,但他们不执行他们的命令。
从控制台输出来看,captureDashboard 线程似乎在 15-20 次迭代之后没有从 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;
}
有什么想法吗?我已经为此工作了几天,但我很困惑。
最佳答案
问题是我需要从 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();
}
}
}
关于Java:重复调用 paintAll 挂起程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17750753/