rlm@3: package com.aurellem.capture; rlm@3: rlm@3: import java.io.File; rlm@3: import java.io.IOException; rlm@12: import java.util.concurrent.Callable; rlm@3: rlm@12: import com.aurellem.capture.audio.MultiListener; rlm@12: import com.aurellem.capture.audio.WaveFileWriter; rlm@9: import com.aurellem.capture.video.AVIVideoRecorder; rlm@10: import com.aurellem.capture.video.AbstractVideoRecorder; rlm@39: import com.aurellem.capture.video.FileVideoRecorder; rlm@10: import com.aurellem.capture.video.XuggleVideoRecorder; rlm@3: import com.jme3.app.Application; rlm@12: import com.jme3.audio.AudioRenderer; rlm@12: import com.jme3.renderer.ViewPort; rlm@12: import com.jme3.scene.Spatial; rlm@49: import com.jme3.system.AppSettings; rlm@44: import com.jme3.system.JmeSystem; rlm@3: rlm@53: /** rlm@56: * Use the methods in this class for capturing consistent, high quality video rlm@56: * and audio from a jMonkeyEngine3 application. To capture audio or video do rlm@56: * the following: rlm@53: * rlm@56: * 1.) Set your application's timer to an IsoTimer. Create the IsoTimer with the rlm@56: * desired video frames-per-second. rlm@56: * rlm@56: * 2.) Call captureAudio and/or captureVideo on the Application as desired rlm@56: * before starting the Application. rlm@55: * rlm@56: * See the Basic and Advanced demos in the examples section for more rlm@56: * information. If you have any trouble, please PM me on the jMonkeyEngine rlm@56: * forums. My username is bortreb. rlm@55: * rlm@53: * @author Robert McIntyre rlm@53: */ rlm@53: rlm@3: public class Capture { rlm@3: rlm@56: /** rlm@56: * Use this function to capture video from your application. You rlm@56: * specify the framerate at which the video will be recording by setting rlm@56: * the Application's timer to an IsoTimer created with the desired rlm@56: * frames-per-second. rlm@56: * rlm@56: * There are three ways to record and they are selected by the rlm@56: * properties of the file that you specify. rlm@56: * rlm@56: * 1.) (Preferred) If you supply an empty directory as the file, then rlm@56: * the video will be saved as a sequence of .png files, one file per rlm@56: * frame. The files start at 0000000.png and increment from there. rlm@56: * You can then combine the frames into your preferred rlm@56: * container/codec. If the directory is not empty, then writing rlm@56: * video frames to it will fail, and nothing will be written. rlm@56: * rlm@56: * 2.) If the filename ends in ".avi" then the frames will be encoded as rlm@56: * a RAW stream inside an AVI 1.0 container. The resulting file rlm@56: * will be quite large and you will probably want to re-encode it to rlm@56: * your preferred container/codec format. Be advised that some rlm@56: * video payers cannot process AVI with a RAW stream, and that AVI rlm@56: * 1.0 files generated by this method that exceed 2.0GB are invalid rlm@56: * according to the AVI 1.0 spec (but many programs can still deal rlm@56: * with them.) Thanks to Werner Randelshofer for his excellent work rlm@56: * which made AVI file writer option possible. rlm@56: * rlm@56: * 3.) Any non-directory file ending in anything other than ".avi" will rlm@56: * be processed through Xuggle. Xuggle provides the option to use rlm@56: * many codecs/containers, but you will have to install it on your rlm@56: * system yourself in order to use this option. Please visit rlm@56: * http://www.xuggle.com/ to learn how to do this. rlm@56: * rlm@56: * @param app The Application from which you wish to record Video. rlm@56: * @param file The file to which the video will be captured. rlm@56: * @throws IOException rlm@56: */ rlm@54: rlm@56: public static void captureVideo(final Application app, final File file) throws IOException{ rlm@56: final AbstractVideoRecorder videoRecorder; rlm@12: rlm@56: if (file.getCanonicalPath().endsWith(".avi")){ rlm@56: videoRecorder = new AVIVideoRecorder(file);} rlm@56: else if (file.isDirectory()){ rlm@56: videoRecorder = new FileVideoRecorder(file);} rlm@56: else { videoRecorder = new XuggleVideoRecorder(file);} rlm@12: rlm@56: Callable thunk = new Callable(){ rlm@56: public Object call(){ rlm@12: rlm@56: ViewPort viewPort = rlm@56: app.getRenderManager() rlm@56: .createPostView("aurellem video record", app.getCamera()); rlm@12: rlm@56: viewPort.setClearFlags(false, false, false); rlm@12: rlm@56: // get GUI node stuff rlm@56: for (Spatial s : app.getGuiViewPort().getScenes()){ rlm@56: viewPort.attachScene(s); rlm@56: } rlm@12: rlm@56: app.getStateManager().attach(videoRecorder); rlm@56: viewPort.addProcessor(videoRecorder); rlm@56: return null; rlm@56: } rlm@56: }; rlm@56: app.enqueue(thunk); rlm@56: } rlm@12: rlm@54: rlm@56: /** rlm@56: * Use this function to capture audio from your application. Audio data rlm@56: * will be saved in linear PCM format at 44,100 hertz in the wav container rlm@56: * format to the file that you specify. rlm@56: * rlm@56: * Note that you *have* to use an IsoTimer for your Application's timer rlm@56: * while recording audio or the recording will fail. Also ensure that your rlm@56: * IsoTimer obeys the following constraints: rlm@56: * rlm@56: * 1.) The frames-per-second value of the IsoTimer cannot be lower than 10 rlm@56: * frames-per-second. rlm@56: * rlm@56: * 2.) The frames-per-second value of the IsoTimer must evenly divide rlm@56: * 44,100. rlm@56: * rlm@56: * @param app The Application from which you wish to record Audio. rlm@56: * @param file the target file to which you want to record audio data. rlm@56: * @throws IOException rlm@56: */ rlm@12: rlm@56: public static void captureAudio(final Application app, final File file) throws IOException{ rlm@56: AppSettings settings = null; rlm@56: if (app.getContext() != null){settings = app.getContext().getSettings();} rlm@56: if (settings == null){settings = new AppSettings(true);} rlm@56: settings.setAudioRenderer("Send"); rlm@56: app.setSettings(settings); rlm@49: rlm@56: JmeSystem.setSystemDelegate(new AurellemSystemDelegate()); rlm@49: rlm@56: final WaveFileWriter writer = new WaveFileWriter(file); rlm@12: rlm@56: Callable thunk = new Callable(){ rlm@56: public Object call(){ rlm@56: AudioRenderer ar = app.getAudioRenderer(); rlm@56: if (ar instanceof MultiListener){ rlm@56: MultiListener ml = (MultiListener)ar; rlm@56: ml.registerSoundProcessor(writer); rlm@56: } rlm@56: return null; rlm@56: } rlm@56: }; rlm@56: app.enqueue(thunk); rlm@56: } rlm@3: }