package com.aicontest.visualizer;
import java.awt.Frame;
import java.awt.Image;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriter;
import javax.imageio.stream.FileImageOutputStream;
import javax.swing.SwingUtilities;
public class VideoCapture {
private final WebWrapper visualizer;
private final Frame frame;
private int num = 0;
private VideoCompressorPool videoCompressorPool;
private long start;
public VideoCapture(WebWrapper visualizer, Frame frame, String format)
throws IOException {
this.visualizer = visualizer;
this.frame = frame;
String fileNameFormat = String.format("%%0%dd." + format, 8);
int processors = Runtime.getRuntime().availableProcessors();
videoCompressorPool = new VideoCompressorPool(processors, format,
fileNameFormat);
}
public void captureFrame(double time, int duration) {
System.out.print("Compressing image " + num);
long now = new Date().getTime();
if (time == 0) {
start = now;
} else {
System.out.print(" - ETA: ");
double percent = time / duration;
double msPerPercent = (now - start) / percent;
long remainingTime = (long) Math.ceil((1 - percent) * msPerPercent
/ 1000);
if (remainingTime > 3600) {
System.out.print((remainingTime / 3600) + "h, ");
remainingTime %= 3600;
}
if (remainingTime > 60) {
System.out.print((remainingTime / 60) + "m, ");
remainingTime %= 60;
}
System.out.print(remainingTime + "s");
}
System.out.println();
BufferedImage image = visualizer.canvas.getPixmap();
videoCompressorPool.compress(image, num++);
if (time == duration) {
quit();
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
frame.dispatchEvent(new WindowEvent(frame,
WindowEvent.WINDOW_CLOSING));
}
});
}
}
public void quit() {
videoCompressorPool.quit();
}
}
class VideoCompressorPool {
private final PoolWorker[] threads;
private final LinkedList<NumberedImage> queue;
private final String fileNameFormat;
private volatile boolean running = true;
public VideoCompressorPool(int nThreads, String format,
String fileNameFormat) {
this.fileNameFormat = fileNameFormat;
queue = new LinkedList<NumberedImage>();
threads = new PoolWorker[nThreads];
for (int i = 0; i < nThreads; i++) {
threads[i] = new PoolWorker(format);
threads[i].start();
}
}
public void quit() {
running = false;
for (int i = 0; i < threads.length; i++) {
PoolWorker thread = threads[i];
thread.interrupt();
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
System.exit(1);
}
}
}
public void compress(BufferedImage image, int num) {
synchronized (queue) {
while (!queue.isEmpty()) {
try {
queue.wait();
} catch (InterruptedException ignored) {
}
}
queue.addLast(new NumberedImage(image, num));
queue.notify();
}
}
private class NumberedImage {
final BufferedImage image;
final int num;
public NumberedImage(BufferedImage image, int num) {
this.image = image;
this.num = num;
}
}
private class PoolWorker extends Thread implements ImageObserver {
private ImageWriter writer;
public PoolWorker(String format) {
Iterator<ImageWriter> writers = ImageIO
.getImageWritersBySuffix(format);
if (writers.hasNext()) {
writer = writers.next();
}
}
public void run() {
NumberedImage image;
while (true) {
synchronized (queue) {
while (queue.isEmpty()) {
if (!running) return;
try {
queue.wait();
} catch (InterruptedException ignored) {
}
}
image = queue.removeFirst();
queue.notifyAll();
}
try {
File file = new File(String.format(fileNameFormat,
image.num));
FileImageOutputStream os = new FileImageOutputStream(file);
try {
BufferedImage normalized = new BufferedImage(
image.image.getWidth(),
image.image.getHeight(),
BufferedImage.TYPE_INT_RGB);
normalized.getGraphics().drawImage(image.image, 0, 0,
this);
writer.setOutput(os);
writer.write(normalized);
} finally {
os.close();
}
} catch (Exception e) {
System.err.println("Couldn't store video frame "
+ image.num + ": " + e.getMessage());
}
}
}
@Override
public boolean imageUpdate(Image img, int infoflags, int x, int y,
int width, int height) {
return true;
}
}
}