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@71: //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@68: * Use the methods in this class for capturing consistent, rlm@68: * high quality video and audio from a jMonkeyEngine3 rlm@68: * application. To capture audio or video do the following: rlm@53: * rlm@68: * 1.) Set your application's timer to an IsoTimer. Create rlm@68: * the IsoTimer with the desired video rlm@68: * frames-per-second. rlm@56: * rlm@68: * 2.) Call captureAudio and/or captureVideo on the rlm@68: * Application as desired before starting the rlm@68: * Application. rlm@55: * rlm@68: * See the Basic and Advanced demos in the examples section rlm@68: * for more information. If you have any trouble, please PM rlm@68: * me on the jMonkeyEngine 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@68: * Use this function to capture video from your rlm@68: * application. You specify the framerate at which the rlm@68: * video will be recording by setting the Application's rlm@68: * timer to an IsoTimer created with the desired rlm@56: * frames-per-second. rlm@56: * rlm@68: * There are three ways to record and they are selected rlm@68: * by the properties of the file that you specify. rlm@56: * rlm@68: * 1.) (Preferred) If you supply an empty directory as rlm@68: * the file, then the video will be saved as a rlm@68: * sequence of .png files, one file per frame. The rlm@68: * files start at 0000000.png and increment from rlm@68: * there. You can then combine the frames into your rlm@68: * preferred container/codec. If the directory is rlm@68: * not empty, then writing video frames to it will rlm@68: * fail, and nothing will be written. rlm@56: * rlm@68: * 2.) If the filename ends in ".avi" then the frames rlm@68: * will be encoded as a RAW stream inside an AVI 1.0 rlm@68: * container. The resulting file will be quite rlm@68: * large and you will probably want to re-encode it rlm@68: * to your preferred container/codec format. Be rlm@68: * advised that some video payers cannot process AVI rlm@68: * with a RAW stream, and that AVI 1.0 files rlm@68: * generated by this method that exceed 2.0GB are rlm@68: * invalid according to the AVI 1.0 spec (but many rlm@68: * programs can still deal with them.) Thanks to rlm@68: * Werner Randelshofer for his excellent work which rlm@68: * made AVI file writer option possible. rlm@56: * rlm@68: * 3.) Any non-directory file ending in anything other rlm@68: * than ".avi" will be processed through Xuggle. rlm@68: * Xuggle provides the option to use many rlm@68: * codecs/containers, but you will have to install rlm@68: * it on your system yourself in order to use this rlm@68: * option. Please visit http://www.xuggle.com/ to rlm@68: * 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@68: public static void captureVideo rlm@68: (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@72: else { rlm@56: videoRecorder = new FileVideoRecorder(file);} rlm@72: //else { rlm@71: //videoRecorder = new XuggleVideoRecorder(file); rlm@72: //} rlm@12: rlm@56: Callable thunk = new Callable(){ rlm@56: public Object call(){ rlm@12: rlm@56: ViewPort viewPort = rlm@56: app.getRenderManager() rlm@68: .createPostView("aurellem video record", rlm@68: 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@68: * Use this function to capture audio from your rlm@68: * application. Audio data will be saved in linear PCM rlm@68: * format at 44,100 hertz in the wav container format to rlm@68: * the file that you specify. rlm@56: * rlm@68: * Note that you *have* to use an IsoTimer for your rlm@68: * Application's timer while recording audio or the rlm@68: * recording will fail. Also ensure that your IsoTimer rlm@68: * obeys the following constraints: rlm@56: * rlm@68: * 1.) The frames-per-second value of the IsoTimer rlm@68: * cannot be lower than 10 frames-per-second. rlm@56: * rlm@68: * 2.) The frames-per-second value of the IsoTimer must rlm@68: * evenly divide 44,100. rlm@56: * rlm@68: * @param app The Application from which you wish to rlm@68: * record Audio. rlm@68: * @param file the target file to which you want to rlm@68: * record audio data. rlm@56: * @throws IOException rlm@56: */ rlm@12: rlm@68: public static void captureAudio rlm@68: (final Application app, final File file) throws IOException{ rlm@56: AppSettings settings = null; rlm@68: if (app.getContext() != null){ rlm@68: 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: }