X-Git-Url: http://git.dolda2000.com/gitweb/?a=blobdiff_plain;f=src%2Fkaka%2Fcakelight%2FFrameGrabber.java;h=5354ea69a8ab1a13dbb5bbf132fc491beee55b4b;hb=e147b561b263ad878201f68ba0f76b9a1d76c59c;hp=92fd50a4679331cbeb98615030bbe53dadaa4a56;hpb=03670958a5eab132a4f1a6ae5ae58b88f55bfe2f;p=kaka%2Fcakelight.git
diff --git a/src/kaka/cakelight/FrameGrabber.java b/src/kaka/cakelight/FrameGrabber.java
index 92fd50a..5354ea6 100644
--- a/src/kaka/cakelight/FrameGrabber.java
+++ b/src/kaka/cakelight/FrameGrabber.java
@@ -1,5 +1,7 @@
package kaka.cakelight;
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
import java.io.*;
import java.util.Optional;
@@ -10,6 +12,7 @@ public class FrameGrabber implements Closeable {
private File file;
private int bytesPerFrame;
private InputStream fileStream;
+ private final ByteArrayOutputStream bufferedBytes = new ByteArrayOutputStream();
private FrameGrabber() {
}
@@ -28,6 +31,7 @@ public class FrameGrabber implements Closeable {
fileStream = new FileInputStream(file);
return true;
} catch (FileNotFoundException e) {
+ // TODO: handle java.io.FileNotFoundException: /dev/video1 (Permission denied)
e.printStackTrace();
return false;
}
@@ -36,12 +40,29 @@ public class FrameGrabber implements Closeable {
/**
* Must be run in the same thread as {@link #prepare}.
*/
- public Optional grabFrame() {
+ public Optional grabFrame() {
try {
- byte[] data = new byte[bytesPerFrame];
- int count = fileStream.read(data);
- log("# of bytes read = " + count);
- return Optional.of(Frame.of(data, config));
+ byte[] data;
+ if (config.video.mjpg) {
+ byte[] jpgData = readStreamingJpgData();
+ if (jpgData == null) {
+ return Optional.empty();
+ }
+ saveTemporaryJpgFile(jpgData);
+ byte[] bmpData = convertJpgFileToByteArray();
+ if (bmpData == null) {
+ return Optional.empty();
+ }
+ data = bmpData;
+ } else {
+ data = new byte[bytesPerFrame];
+ int count = fileStream.read(data);
+ if (count != bytesPerFrame) {
+ log("Expected to read " + bytesPerFrame + " bytes per frame but read " + count);
+ }
+ }
+
+ return Optional.of(VideoFrame.of(data, config));
} catch (IOException e) {
e.printStackTrace();
}
@@ -49,6 +70,62 @@ public class FrameGrabber implements Closeable {
return Optional.empty();
}
+ private byte[] readStreamingJpgData() throws IOException {
+ byte[] data;
+ byte[] batch = new byte[1024];
+ boolean lastByteIsXX = false;
+ loop:
+ while (true) {
+ int batchCount = fileStream.read(batch);
+ if (batchCount == -1) {
+ return null;
+ }
+ if (lastByteIsXX) {
+ if (batch[0] == (byte) 0xd8) {
+ data = bufferedBytes.toByteArray();
+ bufferedBytes.reset();
+ bufferedBytes.write(0xff);
+ bufferedBytes.write(batch, 0, batchCount);
+ break;
+ }
+ bufferedBytes.write(0xff);
+ }
+ for (int i = 0; i < batchCount - 1; i++) {
+ if (batch[i] == (byte) 0xff && batch[i + 1] == (byte) 0xd8) { // start of jpeg
+ if (i > 0) {
+ bufferedBytes.write(batch, 0, i);
+ }
+ data = bufferedBytes.toByteArray();
+ bufferedBytes.reset();
+ bufferedBytes.write(batch, i, batchCount - i);
+ break loop;
+ }
+ }
+ lastByteIsXX = batch[batchCount - 1] == (byte) 0xff;
+ bufferedBytes.write(batch, 0, batchCount - (lastByteIsXX ? 1 : 0));
+ }
+ return data;
+ }
+
+ private void saveTemporaryJpgFile(byte[] data) throws IOException {
+ try (FileOutputStream fos = new FileOutputStream("/tmp/cakelight-video-stream.jpg")) {
+ fos.write(data);
+ }
+ }
+
+ private byte[] convertJpgFileToByteArray() throws IOException {
+ BufferedImage image = ImageIO.read(new File("/tmp/cakelight-video-stream.jpg"));
+ if (image != null) { // will almost always be null the first time
+ try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+ ImageIO.write(image, "bmp", baos);
+ baos.flush();
+ return baos.toByteArray();
+ }
+ } else {
+ return null;
+ }
+ }
+
@Override
public void close() throws IOException {
fileStream.close();