# HG changeset patch # User Robert McIntyre # Date 1320361274 25200 # Node ID ecafe87ffddc7ed0c0fa71db1fae2b9aab7944e5 # Parent cce471a4108afe873720718024f78c9a93b289a2 updating capture video with new examples diff -r cce471a4108a -r ecafe87ffddc org/capture-video.org --- a/org/capture-video.org Thu Nov 03 10:42:28 2011 -0700 +++ b/org/capture-video.org Thu Nov 03 16:01:14 2011 -0700 @@ -143,53 +143,147 @@ called. -=./jme3/src/core/com/jme3/system/IsoTimer.java= -#+include ./jme3/src/core/com/jme3/system/IsoTimer.java src java +=./src/com/aurellem/capture/IsoTimer.java= +#+include ../../jmeCapture/src/com/aurellem/capture/IsoTimer.java src java 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 $(\frac{1}{fps})$ seconds of game-time. -In order to facilitate setting the =Timer= in user code, I added -getter and setter methods to =Application.java=. - -In =./jme3/src/core/com/jme3/app/Application.java= I added: -#+include ./jme3/src/core/com/jme3/app/Application.java src java :lines "340-356" - * Encoding to Video Now that the issue of time is solved, we just need a function that writes each frame to a video. We can put this function somewhere where it will be called exactly one per frame. +The basic functions that a =VideoRecorder= should support are +recording, starting, stopping, and possibly a final finishing step +there it finilizes the recording (such as writing headers for a video +file). + +An appropiate interface describing this behaviour could look like +this: + +=./src/com/aurellem/capture/video/VideoRecorder.java= +#+include ../../jmeCapture/src/com/aurellem/capture/video/VideoRecorder.java src java + + JME3 already provides exactly the class we need: the =SceneProcessor= class can be attached to any viewport and the methods defined therein will be called at the appropriate points in the rendering process. +However, it is also important to properly close the video stream and +write headers and such, and even though =SceneProcessor= has a +=.cleanup()= method, it is only called when the =SceneProcessor= is +removed from the =RenderManager=, not when the game is shutting down +when the user pressed ESC, for example. To obtain reliable shutdown +behaviour, we also have to implement =AppState=, which provides a +=.cleanup()= method that /is/ called on shutdown. + +Here is an AbstractVideoRecorder class that takes care of the details +of setup and teardown. + +=./src/com/aurellem/capture/video/AbstractVideoRecorder.java= +#+include ../../jmeCapture/src/com/aurellem/capture/video/AbstractVideoRecorder.java src java + If you want to generate video from Java, a great option is [[http://www.xuggle.com/][Xuggle]]. It takes care of everything related to video encoding and decoding and runs on Windows, Linux and Mac. Out of all the video frameworks for Java I personally like this one the best. -Here is a =SceneProcessor= that uses [[http://www.xuggle.com/][Xuggle]] to write each frame to a +Here is a =VideoRecorder= that uses [[http://www.xuggle.com/][Xuggle]] to write each frame to a video file. -=./jme3/src/core/com/jme3/app/VideoProcessor.java= -#+include ./jme3/src/core/com/jme3/app/VideoProcessor.java src java +=./src/com/aurellem/capture/video/XuggleVideoRecorder.java= +#+include ../../jmeCapture/src/com/aurellem/capture/video/XuggleVideoRecorder.java src java With this, we are able to record video! +However, it can be hard to properly install Xuggle. For those of you +who would rather not use Xuggle, here is an alternate class that uses +[[http://www.randelshofer.ch/blog/2008/08/writing-avi-videos-in-pure-java/][Werner Randelshofer's]] excellent pure Java AVI file writer. + +=./src/com/aurellem/capture/video/AVIVideoRecorder.java= +#+include ../../jmeCapture/src/com/aurellem/capture/video/AVIVideoRecorder.java src java + +This =AVIVideoRecorder= is more limited than the +=XuggleVideoRecorder=, but requires less external dependencies. + +Finally, for those of you who prefer to create the final video from a +sequence of images, there is the =FileVideoRecorder=, which records +each frame to a folder as a sequentially numbered image file. Note +that you have to remember the FPS at which you recorded the video, as +this information is lost when saving each frame to a file. + +=./src/com/aurellem/capture/video/FileVideoRecorder.java= +#+include ../../jmeCapture/src/com/aurellem/capture/video/FileVideoRecorder.java src java + + +* /Really/ Simple Video Recording + +The most common case for recording a video is probably to just capture +whatever is on your screen exactly as you see it. In this case, this +method will do. + +#+begin_src java +public static void captureVideo(final Application app, + final File file) throws IOException{ + final AbstractVideoRecorder videoRecorder; + if (file.getCanonicalPath().endsWith(".avi")){ + videoRecorder = new AVIVideoRecorder(file);} + else if (file.isDirectory()){ + videoRecorder = new FileVideoRecorder(file);} + else { videoRecorder = new XuggleVideoRecorder(file);} + + Callable thunk = new Callable(){ + public Object call(){ + ViewPort viewPort = + app.getRenderManager() + .createPostView("aurellem record", app.getCamera()); + viewPort.setClearFlags(false, false, false); + // get GUI node stuff + for (Spatial s : app.getGuiViewPort().getScenes()){ + viewPort.attachScene(s); + } + app.getStateManager().attach(videoRecorder); + viewPort.addProcessor(videoRecorder); + return null; + } + }; + app.enqueue(thunk); +} +#+end_src + +This will select the appropiate backend =VideoRecorder= class +depending on the file name you specify, and insturment your +application to record video to the file. You should still set the +game's timer to an =IsoTimer= with the desired fps. + +This example will record video from the ocean scene from the +jMonkeyEngine test suite. +#+begin_src java +File video = File.createTempFile("JME-water-video", ".avi"); +captureVideo(app, video); +app.start(); +System.out.println(video.getCanonicalPath()); +#+end_src + + +I've added support for this under a class called +=com.aurellem.capture.Capture=. You can get it [[http://hg.bortreb.com/jmeCapture/][here]]. + + * Hello Video! I've taken [[http://code.google.com/p/jmonkeyengine/source/browse/trunk/engine/src/test/jme3test/helloworld/HelloLoop.java][=./jme3/src/test/jme3test/helloworld/HelloLoop.java=]] and augmented it with video output as follows: -=./jme3/src/test/jme3test/helloworld/HelloVideo.java= -#+include ./jme3/src/test/jme3test/helloworld/HelloVideo.java src java +=./src/com/aurellem/capture/examples/HelloVideo.java= +#+include ../../src/com/aurellem/capture/examples/HelloVideo.java src java The videos are created in the =hello-video= directory -#+begin_src sh :results verbatim +#+begin_src sh :results verbatim :exports both du -h hello-video/* #+end_src @@ -216,7 +310,6 @@ #+END_HTML - * Summary It's quite easy to augment your own application to record video, almost regardless of how complicated the actual application is. You @@ -231,26 +324,19 @@ this.setTimer(new IsoTimer(30)); #+end_src -Somewhere in the initialization of your Application. Right now, you -will have to add the =setTimer= method to =Application=, but hopefully -this method will be included soon by the JMonkeyEngine3 team. - -Then, you create a =VideoProcessor= object and attach it to the -=ViewPort= from which you want to record. +Somewhere in the initialization of your Application. If you want to record from the game's main =ViewPort= to a file called =/home/r/record.flv=, then add: #+begin_src java :exports code -viewPort.addProcessor(new VideoProcessor(new File("/home/r/record.flv"))); +Capture.captureVideo(app, new File("/home/r/record.flv")); #+end_src -Do this for each =ViewPort= from which you want to record. The more -ViewPorts from which you record, the slower the game will run, but -this slowness will not affect the final video output. +Before you call =app.start()=; * More Examples -** Hello Physics +** COMMENT Hello Physics =HelloVideo= is boring. Let's add some video capturing to =HelloPhysics= and create something fun! @@ -341,7 +427,7 @@ Thats a terribly large size! Let's compress it: -** Compressing the HelloPhysics Video +** COMMENT Compressing the HelloPhysics Video First, we'll scale the video, then, we'll decrease it's bitrate. The end result will be perfect for upload to YouTube. @@ -451,7 +537,7 @@ * Sample Videos I encoded most of the original JME3 Hello demos for your viewing -pleasure, all using the =VideoProcessor= and =IsoTimer= classes. +pleasure, all using the =Capture= and =IsoTimer= classes. ** HelloTerrain [[http://youtu.be/5_4wyDFwrVQ][HelloTerrain.avi]] @@ -651,3 +737,4 @@ +