Add variable quality and video file frame count to VideoRecorderAppState, thanks to @entrusc

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9283 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
nor..67 13 years ago
parent 732955a56c
commit 904d835742
  1. 55
      engine/src/desktop/com/jme3/app/state/MjpegFileWriter.java
  2. 52
      engine/src/desktop/com/jme3/app/state/VideoRecorderAppState.java

@ -11,11 +11,15 @@ import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
/**
* Released under BSD License
* @author monceaux, normenhansen
* @author monceaux, normenhansen, entrusC
*/
public class MjpegFileWriter {
@ -56,7 +60,11 @@ public class MjpegFileWriter {
}
public void addImage(Image image) throws Exception {
addImage(writeImageToBytes(image));
addImage(image, 0.8f);
}
public void addImage(Image image, float quality) throws Exception {
addImage(writeImageToBytes(image, quality));
}
public void addImage(byte[] imagedata) throws Exception {
@ -79,18 +87,29 @@ public class MjpegFileWriter {
}
}
imagedata = null;
numFrames++; //add a frame
}
public void finishAVI() throws Exception {
byte[] indexlistBytes = indexlist.toBytes();
aviOutput.write(indexlistBytes);
aviOutput.close();
long size = aviFile.length();
int fileSize = (int)aviFile.length();
int listSize = (int) (fileSize - 8 - aviMovieOffset - indexlistBytes.length);
RandomAccessFile raf = new RandomAccessFile(aviFile, "rw");
raf.seek(4);
raf.write(intBytes(swapInt((int) size - 8)));
raf.seek(aviMovieOffset + 4);
raf.write(intBytes(swapInt((int) (size - 8 - aviMovieOffset - indexlistBytes.length))));
//add header and length by writing the headers again
//with the now available information
raf.write(new RIFFHeader(fileSize).toBytes());
raf.write(new AVIMainHeader().toBytes());
raf.write(new AVIStreamList().toBytes());
raf.write(new AVIStreamHeader().toBytes());
raf.write(new AVIStreamFormat().toBytes());
raf.write(new AVIJunk().toBytes());
raf.write(new AVIMovieList(listSize).toBytes());
raf.close();
}
@ -142,6 +161,10 @@ public class MjpegFileWriter {
public RIFFHeader() {
}
public RIFFHeader(int fileSize) {
this.fileSize = fileSize;
}
public byte[] toBytes() throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
@ -382,6 +405,10 @@ public class MjpegFileWriter {
public AVIMovieList() {
}
public AVIMovieList(int listSize) {
this.listSize = listSize;
}
public byte[] toBytes() throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write(fcc);
@ -473,7 +500,7 @@ public class MjpegFileWriter {
}
}
public byte[] writeImageToBytes(Image image) throws Exception {
public byte[] writeImageToBytes(Image image, float quality) throws Exception {
BufferedImage bi;
if (image instanceof BufferedImage && ((BufferedImage) image).getType() == BufferedImage.TYPE_INT_RGB) {
bi = (BufferedImage) image;
@ -483,7 +510,17 @@ public class MjpegFileWriter {
g.drawImage(image, 0, 0, width, height, null);
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(bi, "jpg", baos);
ImageWriter imgWrtr = ImageIO.getImageWritersByFormatName("jpg").next();
ImageOutputStream imgOutStrm = ImageIO.createImageOutputStream(baos);
imgWrtr.setOutput(imgOutStrm);
ImageWriteParam jpgWrtPrm = imgWrtr.getDefaultWriteParam();
jpgWrtPrm.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
jpgWrtPrm.setCompressionQuality(quality);
imgWrtr.write(null, new IIOImage(bi, null, null), jpgWrtPrm);
imgOutStrm.close();
baos.close();
return baos.toByteArray();
}

@ -1,6 +1,8 @@
package com.jme3.app.state;
import com.jme3.app.Application;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.post.SceneProcessor;
import com.jme3.renderer.Camera;
import com.jme3.renderer.RenderManager;
@ -27,7 +29,7 @@ import java.util.logging.Logger;
* state is detached, else the old file will be overwritten. If you specify no file
* the AppState will attempt to write a file into the user home directory, made unique
* by a timestamp.
* @author normenhansen, Robert McIntyre
* @author normenhansen, Robert McIntyre, entrusC
*/
public class VideoRecorderAppState extends AbstractAppState {
@ -46,13 +48,41 @@ public class VideoRecorderAppState extends AbstractAppState {
});
private int numCpus = Runtime.getRuntime().availableProcessors();
private ViewPort lastViewPort;
private float quality;
/**
* Using this constructor the video files will be written sequentially to the user's home directory with
* a quality of 0.8
*/
public VideoRecorderAppState() {
Logger.getLogger(this.getClass().getName()).log(Level.INFO, "JME3 VideoRecorder running on {0} CPU's", numCpus);
this(null, 0.8f);
}
/**
* Using this constructor the video files will be written sequentially to the user's home directory.
* @param quality the quality of the jpegs in the video stream (0.0 smallest file - 1.0 largest file)
*/
public VideoRecorderAppState(float quality) {
this(null, quality);
}
/**
* This constructor allows you to specify the output file of the video. The quality is set
* to 0.8
* @param file the video file
*/
public VideoRecorderAppState(File file) {
this(file, 0.8f);
}
/**
* This constructor allows you to specify the output file of the video as well as the quality
* @param file the video file
* @param quality the quality of the jpegs in the video stream (0.0 smallest file - 1.0 largest file)
*/
public VideoRecorderAppState(File file, float quality) {
this.file = file;
this.quality = quality;
Logger.getLogger(this.getClass().getName()).log(Level.INFO, "JME3 VideoRecorder running on {0} CPU's", numCpus);
}
@ -67,6 +97,22 @@ public class VideoRecorderAppState extends AbstractAppState {
this.file = file;
}
/**
* Get the quality used to compress the video images.
* @return the quality of the jpegs in the video stream (0.0 smallest file - 1.0 largest file)
*/
public float getQuality() {
return quality;
}
/**
* Set the video image quality from 0(worst/smallest) to 1(best/largest).
* @param quality the quality of the jpegs in the video stream (0.0 smallest file - 1.0 largest file)
*/
public void setQuality(float quality) {
this.quality = quality;
}
@Override
public void initialize(AppStateManager stateManager, Application app) {
super.initialize(stateManager, app);
@ -128,7 +174,7 @@ public class VideoRecorderAppState extends AbstractAppState {
public Void call() throws Exception {
Screenshots.convertScreenShot(item.buffer, item.image);
item.data = writer.writeImageToBytes(item.image);
item.data = writer.writeImageToBytes(item.image, quality);
while (usedItems.peek() != item) {
Thread.sleep(1);
}

Loading…
Cancel
Save