Mercurial > jmeCapture
changeset 54:6484a820e27d
wrote main documentation.
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sat, 03 Dec 2011 19:15:43 -0600 |
parents | 3dc1f15e1e13 |
children | b05f629fc296 |
files | src/com/aurellem/capture/Capture.java src/com/aurellem/capture/IsoTimer.java |
diffstat | 2 files changed, 96 insertions(+), 13 deletions(-) [+] |
line wrap: on
line diff
1.1 --- a/src/com/aurellem/capture/Capture.java Sat Dec 03 13:54:47 2011 -0600 1.2 +++ b/src/com/aurellem/capture/Capture.java Sat Dec 03 19:15:43 2011 -0600 1.3 @@ -18,20 +18,53 @@ 1.4 import com.jme3.system.JmeSystem; 1.5 1.6 /** 1.7 + * Use the methods in this class for capturing consistent, high quality video and audio from a 1.8 + * jMonkeyEngine3 application. To capture audio or video do the following: 1.9 * 1.10 + * 1.) Set your application's timer to an IsoTimer. Create the IsoTimer with the desired video 1.11 + * frames-per-second. 1.12 + * 2.) Call captureAudio and/or captureVideo on the Application as desired. 1.13 + * 1.14 * @author Robert McIntyre 1.15 - * 1.16 */ 1.17 1.18 public class Capture { 1.19 1.20 + /** 1.21 + * Use this function to capture video from your application. You specify the framerate at which 1.22 + * the video will be recording by setting the Application's timer to an IsoTimer created with the 1.23 + * desired frames-per-second. 1.24 + * 1.25 + * There are three ways to record and they are selected by the properties of the file that you 1.26 + * specify. 1.27 + * 1.28 + * 1.) (Preferred) 1.29 + * If you supply an empty directory as the file, then the video will 1.30 + * be saved as a sequence of .png files, one file per frame. The files start at 1.31 + * 0000000.png and increment from there. You can then combine the frames into your 1.32 + * preferred container/codec. If the directory is not empty, then writing video frames 1.33 + * to it will fail, and nothing will be written. 1.34 + * 1.35 + * 2.) If the filename ends in ".avi" then the frames will be encoded as a RAW stream 1.36 + * inside an AVI 1.0 container. The resulting file will be quite large and you will 1.37 + * probably want to re-encode it to your preferred container/codec format. Be advised 1.38 + * that some video payers cannot process AVI with a RAW stream, and that AVI 1.0 files 1.39 + * generated by this method that exceed 2.0GB are invalid according to the AVI 1.0 spec 1.40 + * (but many programs can still deal with them.) Thanks to Werner Randelshofer for his 1.41 + * excellent work which made AVI file writer option possible. 1.42 + * 1.43 + * 3.) Any non-directory file ending in anything other than ".avi" will be processed through 1.44 + * Xuggle. Xuggle provides the option to use many codecs/containers, but you will have to 1.45 + * install it on your system yourself in order to use this option. Please visit 1.46 + * http://www.xuggle.com/ to learn how to do this. 1.47 + * 1.48 + * @param app The Application from which you wish to record Video. 1.49 + * @param file The file to which the video will be captured. 1.50 + * @throws IOException 1.51 + */ 1.52 + 1.53 public static void captureVideo(final Application app, final File file) throws IOException{ 1.54 - 1.55 final AbstractVideoRecorder videoRecorder; 1.56 - // The XuggleVideoRecorder is better than the AVIVideoRecorder in every way 1.57 - // except for ease of installation. The excellent work by Werner Randelshofer 1.58 - // is used as a fallback option. Please visit http://www.xuggle.com/ to learn 1.59 - // how to set up the XuggleVideoRecorder. 1.60 1.61 if (file.getCanonicalPath().endsWith(".avi")){ 1.62 videoRecorder = new AVIVideoRecorder(file);} 1.63 @@ -61,6 +94,21 @@ 1.64 app.enqueue(thunk); 1.65 } 1.66 1.67 + 1.68 + /** 1.69 + * Use this function to capture audio from your application. Audio data will be saved in linear 1.70 + * PCM format at 44,100 hertz in the wav container format to the file that you specify. 1.71 + * 1.72 + * Note that you *have* to use an IsoTimer for your Application's timer while recording audio or 1.73 + * the recording will fail. Also ensure that your IsoTimer obeys the following constraints: 1.74 + * 1.75 + * - The frames-per-second value of the IsoTimer cannot be lower than 10 frames-per-second. 1.76 + * - The frames-per-second value of the IsoTimer must evenly divide 44,100. 1.77 + * 1.78 + * @param app The Application from which you wish to record Audio. 1.79 + * @param file the target file to which you want to record audio data. 1.80 + * @throws IOException 1.81 + */ 1.82 1.83 public static void captureAudio(final Application app, final File file) throws IOException{ 1.84 AppSettings settings = null; 1.85 @@ -70,7 +118,6 @@ 1.86 app.setSettings(settings); 1.87 1.88 JmeSystem.setSystemDelegate(new AurellemSystemDelegate()); 1.89 - 1.90 1.91 final WaveFileWriter writer = new WaveFileWriter(file); 1.92 1.93 @@ -84,7 +131,6 @@ 1.94 return null; 1.95 } 1.96 }; 1.97 - 1.98 app.enqueue(thunk); 1.99 } 1.100 }
2.1 --- a/src/com/aurellem/capture/IsoTimer.java Sat Dec 03 13:54:47 2011 -0600 2.2 +++ b/src/com/aurellem/capture/IsoTimer.java Sat Dec 03 19:15:43 2011 -0600 2.3 @@ -2,6 +2,38 @@ 2.4 2.5 import com.jme3.system.Timer; 2.6 2.7 +/** 2.8 + * A standard JME3 application that extends SimpleApplication or 2.9 + * Application tries as hard as it can to keep in sync with 2.10 + * user-time. If a ball is rolling at 1 game-mile per game-hour in the 2.11 + * game, and you wait for one user-hour as measured by the clock on 2.12 + * your wall, then the ball should have traveled exactly one 2.13 + * game-mile. In order to keep sync with the real world, the game 2.14 + * throttles its physics engine and graphics display. If the 2.15 + * computations involved in running the game are too intense, then the 2.16 + * game will first skip frames, then sacrifice physics accuracy. If 2.17 + * there are particularly demanding computations, then you may only get 2.18 + * 1 fps, and the ball may tunnel through the floor or obstacles due 2.19 + * to inaccurate physics simulation, but after the end of one 2.20 + * user-hour, that ball will have traveled one game-mile. 2.21 + * 2.22 + * When we're recording video or audio, we don't care if the game-time 2.23 + * syncs with user-time, but instead whether the time in the recorded 2.24 + * video (video-time) syncs with user-time. To continue the analogy, 2.25 + * if we recorded the ball rolling at 1 game-mile per game-hour and 2.26 + * watched the video later, we would want to see 30 fps video of the 2.27 + * ball rolling at 1 video-mile per user-hour. It doesn't matter how 2.28 + * much user-time it took to simulate that hour of game-time to make 2.29 + * the high-quality recording. If an Application uses this IsoTimer 2.30 + * instead of the normal one, we can be sure that every call to 2.31 + * simpleUpdate, for example, corresponds to exactly (1 / fps) seconds 2.32 + * of game-time. This let's us record perfect video and audio even on 2.33 + * a slow computer. 2.34 + * 2.35 + * @author Robert McIntyre 2.36 + * 2.37 + */ 2.38 + 2.39 public class IsoTimer extends Timer { 2.40 2.41 private float framerate; 2.42 @@ -9,19 +41,24 @@ 2.43 2.44 public IsoTimer(float framerate){ 2.45 this.framerate = framerate; 2.46 - this.ticks = 0;} 2.47 + this.ticks = 0; 2.48 + } 2.49 2.50 public long getTime() { 2.51 - return (long) (this.ticks / this.framerate);} 2.52 + return (long) (this.ticks / this.framerate); 2.53 + } 2.54 2.55 public long getResolution() { 2.56 - return 1000000000L;} 2.57 + return 1000000000L; 2.58 + } 2.59 2.60 public float getFrameRate() { 2.61 - return this.framerate;} 2.62 + return this.framerate; 2.63 + } 2.64 2.65 public float getTimePerFrame() { 2.66 - return (float) (1.0f / this.framerate);} 2.67 + return (float) (1.0f / this.framerate); 2.68 + } 2.69 2.70 public void update() {this.ticks++;} 2.71