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