I'm implementing video capture in a Java program by creating BufferedImage
s at a user-defined interval (for my testing, 100ms), and then using those images to make a movie file. The JFrame
that I am trying to record from includes a dashboard-like interface contained by a JLayeredPane
. The JFrame
also has two Canvas3D
s. I'm telling each of these 3 things to render
or paint
into their own LinkedBlockingDeque<BufferedImage>
, and I combine them later. The dashboard is set to only render every dashboardFrameRepaintFrequency
frames.
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();
}
}
});
I've found that after 15-20 notify()
s, the program locks up - it stops recording the Canvases, stops responding to keyboard input.. I can still change which window has focus, and the buttons (such as the X button to close a window) still change visual state from mouse rollover or clicking, but they don't execute their commands.
From the console output, it seems that the captureDashboard thread, after those 15-20 iterations, does not return from the getDashboardImage
method:
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;
}
Any ideas? I've been working at this for a few days and I'm stumped.