# HG changeset patch # User Robert McIntyre # Date 1322961343 21600 # Node ID 6484a820e27db16c924c4493bce22a0fbb825eaa # Parent 3dc1f15e1e132fbbe9bc505b37f7ef7a63af9093 wrote main documentation. diff -r 3dc1f15e1e13 -r 6484a820e27d src/com/aurellem/capture/Capture.java --- a/src/com/aurellem/capture/Capture.java Sat Dec 03 13:54:47 2011 -0600 +++ b/src/com/aurellem/capture/Capture.java Sat Dec 03 19:15:43 2011 -0600 @@ -18,20 +18,53 @@ import com.jme3.system.JmeSystem; /** + * Use the methods in this class for capturing consistent, high quality video and audio from a + * jMonkeyEngine3 application. To capture audio or video do the following: * + * 1.) Set your application's timer to an IsoTimer. Create the IsoTimer with the desired video + * frames-per-second. + * 2.) Call captureAudio and/or captureVideo on the Application as desired. + * * @author Robert McIntyre - * */ public class Capture { + /** + * Use this function to capture video from your application. You specify the framerate at which + * the video will be recording by setting the Application's timer to an IsoTimer created with the + * desired frames-per-second. + * + * There are three ways to record and they are selected by the properties of the file that you + * specify. + * + * 1.) (Preferred) + * If you supply an empty directory as the file, then the video will + * be saved as a sequence of .png files, one file per frame. The files start at + * 0000000.png and increment from there. You can then combine the frames into your + * preferred container/codec. If the directory is not empty, then writing video frames + * to it will fail, and nothing will be written. + * + * 2.) If the filename ends in ".avi" then the frames will be encoded as a RAW stream + * inside an AVI 1.0 container. The resulting file will be quite large and you will + * probably want to re-encode it to your preferred container/codec format. Be advised + * that some video payers cannot process AVI with a RAW stream, and that AVI 1.0 files + * generated by this method that exceed 2.0GB are invalid according to the AVI 1.0 spec + * (but many programs can still deal with them.) Thanks to Werner Randelshofer for his + * excellent work which made AVI file writer option possible. + * + * 3.) Any non-directory file ending in anything other than ".avi" will be processed through + * Xuggle. Xuggle provides the option to use many codecs/containers, but you will have to + * install it on your system yourself in order to use this option. Please visit + * http://www.xuggle.com/ to learn how to do this. + * + * @param app The Application from which you wish to record Video. + * @param file The file to which the video will be captured. + * @throws IOException + */ + public static void captureVideo(final Application app, final File file) throws IOException{ - final AbstractVideoRecorder videoRecorder; - // The XuggleVideoRecorder is better than the AVIVideoRecorder in every way - // except for ease of installation. The excellent work by Werner Randelshofer - // is used as a fallback option. Please visit http://www.xuggle.com/ to learn - // how to set up the XuggleVideoRecorder. if (file.getCanonicalPath().endsWith(".avi")){ videoRecorder = new AVIVideoRecorder(file);} @@ -61,6 +94,21 @@ app.enqueue(thunk); } + + /** + * Use this function to capture audio from your application. Audio data will be saved in linear + * PCM format at 44,100 hertz in the wav container format to the file that you specify. + * + * Note that you *have* to use an IsoTimer for your Application's timer while recording audio or + * the recording will fail. Also ensure that your IsoTimer obeys the following constraints: + * + * - The frames-per-second value of the IsoTimer cannot be lower than 10 frames-per-second. + * - The frames-per-second value of the IsoTimer must evenly divide 44,100. + * + * @param app The Application from which you wish to record Audio. + * @param file the target file to which you want to record audio data. + * @throws IOException + */ public static void captureAudio(final Application app, final File file) throws IOException{ AppSettings settings = null; @@ -70,7 +118,6 @@ app.setSettings(settings); JmeSystem.setSystemDelegate(new AurellemSystemDelegate()); - final WaveFileWriter writer = new WaveFileWriter(file); @@ -84,7 +131,6 @@ return null; } }; - app.enqueue(thunk); } } diff -r 3dc1f15e1e13 -r 6484a820e27d src/com/aurellem/capture/IsoTimer.java --- a/src/com/aurellem/capture/IsoTimer.java Sat Dec 03 13:54:47 2011 -0600 +++ b/src/com/aurellem/capture/IsoTimer.java Sat Dec 03 19:15:43 2011 -0600 @@ -2,6 +2,38 @@ import com.jme3.system.Timer; +/** + * A standard JME3 application that extends SimpleApplication or + * Application tries as hard as it can to keep in sync with + * user-time. If a ball is rolling at 1 game-mile per game-hour in the + * game, and you wait for one user-hour as measured by the clock on + * your wall, then the ball should have traveled exactly one + * game-mile. In order to keep sync with the real world, the game + * throttles its physics engine and graphics display. If the + * computations involved in running the game are too intense, then the + * game will first skip frames, then sacrifice physics accuracy. If + * there are particularly demanding computations, then you may only get + * 1 fps, and the ball may tunnel through the floor or obstacles due + * to inaccurate physics simulation, but after the end of one + * user-hour, that ball will have traveled one game-mile. + * + * When we're recording video or audio, we don't care if the game-time + * syncs with user-time, but instead whether the time in the recorded + * video (video-time) syncs with user-time. To continue the analogy, + * if we recorded the ball rolling at 1 game-mile per game-hour and + * watched the video later, we would want to see 30 fps video of the + * ball rolling at 1 video-mile per user-hour. It doesn't matter how + * much user-time it took to simulate that hour of game-time to make + * the high-quality recording. If an Application uses this IsoTimer + * instead of the normal one, we can be sure that every call to + * simpleUpdate, for example, corresponds to exactly (1 / fps) seconds + * of game-time. This let's us record perfect video and audio even on + * a slow computer. + * + * @author Robert McIntyre + * + */ + public class IsoTimer extends Timer { private float framerate; @@ -9,19 +41,24 @@ public IsoTimer(float framerate){ this.framerate = framerate; - this.ticks = 0;} + this.ticks = 0; + } public long getTime() { - return (long) (this.ticks / this.framerate);} + return (long) (this.ticks / this.framerate); + } public long getResolution() { - return 1000000000L;} + return 1000000000L; + } public float getFrameRate() { - return this.framerate;} + return this.framerate; + } public float getTimePerFrame() { - return (float) (1.0f / this.framerate);} + return (float) (1.0f / this.framerate); + } public void update() {this.ticks++;}