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
This commit is contained in:
parent
732955a56c
commit
904d835742
@ -11,11 +11,15 @@ import java.nio.channels.FileChannel;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import javax.imageio.IIOImage;
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
|
import javax.imageio.ImageWriteParam;
|
||||||
|
import javax.imageio.ImageWriter;
|
||||||
|
import javax.imageio.stream.ImageOutputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Released under BSD License
|
* Released under BSD License
|
||||||
* @author monceaux, normenhansen
|
* @author monceaux, normenhansen, entrusC
|
||||||
*/
|
*/
|
||||||
public class MjpegFileWriter {
|
public class MjpegFileWriter {
|
||||||
|
|
||||||
@ -56,7 +60,11 @@ public class MjpegFileWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void addImage(Image image) throws Exception {
|
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 {
|
public void addImage(byte[] imagedata) throws Exception {
|
||||||
@ -79,18 +87,29 @@ public class MjpegFileWriter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
imagedata = null;
|
imagedata = null;
|
||||||
|
|
||||||
|
numFrames++; //add a frame
|
||||||
}
|
}
|
||||||
|
|
||||||
public void finishAVI() throws Exception {
|
public void finishAVI() throws Exception {
|
||||||
byte[] indexlistBytes = indexlist.toBytes();
|
byte[] indexlistBytes = indexlist.toBytes();
|
||||||
aviOutput.write(indexlistBytes);
|
aviOutput.write(indexlistBytes);
|
||||||
aviOutput.close();
|
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");
|
RandomAccessFile raf = new RandomAccessFile(aviFile, "rw");
|
||||||
raf.seek(4);
|
|
||||||
raf.write(intBytes(swapInt((int) size - 8)));
|
//add header and length by writing the headers again
|
||||||
raf.seek(aviMovieOffset + 4);
|
//with the now available information
|
||||||
raf.write(intBytes(swapInt((int) (size - 8 - aviMovieOffset - indexlistBytes.length))));
|
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();
|
raf.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,6 +162,10 @@ public class MjpegFileWriter {
|
|||||||
public RIFFHeader() {
|
public RIFFHeader() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RIFFHeader(int fileSize) {
|
||||||
|
this.fileSize = fileSize;
|
||||||
|
}
|
||||||
|
|
||||||
public byte[] toBytes() throws Exception {
|
public byte[] toBytes() throws Exception {
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
baos.write(fcc);
|
baos.write(fcc);
|
||||||
@ -382,6 +405,10 @@ public class MjpegFileWriter {
|
|||||||
public AVIMovieList() {
|
public AVIMovieList() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AVIMovieList(int listSize) {
|
||||||
|
this.listSize = listSize;
|
||||||
|
}
|
||||||
|
|
||||||
public byte[] toBytes() throws Exception {
|
public byte[] toBytes() throws Exception {
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
baos.write(fcc);
|
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;
|
BufferedImage bi;
|
||||||
if (image instanceof BufferedImage && ((BufferedImage) image).getType() == BufferedImage.TYPE_INT_RGB) {
|
if (image instanceof BufferedImage && ((BufferedImage) image).getType() == BufferedImage.TYPE_INT_RGB) {
|
||||||
bi = (BufferedImage) image;
|
bi = (BufferedImage) image;
|
||||||
@ -483,7 +510,17 @@ public class MjpegFileWriter {
|
|||||||
g.drawImage(image, 0, 0, width, height, null);
|
g.drawImage(image, 0, 0, width, height, null);
|
||||||
}
|
}
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
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();
|
baos.close();
|
||||||
return baos.toByteArray();
|
return baos.toByteArray();
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package com.jme3.app.state;
|
package com.jme3.app.state;
|
||||||
|
|
||||||
import com.jme3.app.Application;
|
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.post.SceneProcessor;
|
||||||
import com.jme3.renderer.Camera;
|
import com.jme3.renderer.Camera;
|
||||||
import com.jme3.renderer.RenderManager;
|
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
|
* 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
|
* the AppState will attempt to write a file into the user home directory, made unique
|
||||||
* by a timestamp.
|
* by a timestamp.
|
||||||
* @author normenhansen, Robert McIntyre
|
* @author normenhansen, Robert McIntyre, entrusC
|
||||||
*/
|
*/
|
||||||
public class VideoRecorderAppState extends AbstractAppState {
|
public class VideoRecorderAppState extends AbstractAppState {
|
||||||
|
|
||||||
@ -46,13 +48,41 @@ public class VideoRecorderAppState extends AbstractAppState {
|
|||||||
});
|
});
|
||||||
private int numCpus = Runtime.getRuntime().availableProcessors();
|
private int numCpus = Runtime.getRuntime().availableProcessors();
|
||||||
private ViewPort lastViewPort;
|
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() {
|
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) {
|
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.file = file;
|
||||||
|
this.quality = quality;
|
||||||
Logger.getLogger(this.getClass().getName()).log(Level.INFO, "JME3 VideoRecorder running on {0} CPU's", numCpus);
|
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;
|
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
|
@Override
|
||||||
public void initialize(AppStateManager stateManager, Application app) {
|
public void initialize(AppStateManager stateManager, Application app) {
|
||||||
super.initialize(stateManager, app);
|
super.initialize(stateManager, app);
|
||||||
@ -128,7 +174,7 @@ public class VideoRecorderAppState extends AbstractAppState {
|
|||||||
|
|
||||||
public Void call() throws Exception {
|
public Void call() throws Exception {
|
||||||
Screenshots.convertScreenShot(item.buffer, item.image);
|
Screenshots.convertScreenShot(item.buffer, item.image);
|
||||||
item.data = writer.writeImageToBytes(item.image);
|
item.data = writer.writeImageToBytes(item.image, quality);
|
||||||
while (usedItems.peek() != item) {
|
while (usedItems.peek() != item) {
|
||||||
Thread.sleep(1);
|
Thread.sleep(1);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user