Mercurial > cortex
changeset 42:ecafe87ffddc
updating capture video with new examples
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Thu, 03 Nov 2011 16:01:14 -0700 |
parents | cce471a4108a |
children | 117eb477d0a7 |
files | org/capture-video.org |
diffstat | 1 files changed, 115 insertions(+), 28 deletions(-) [+] |
line wrap: on
line diff
1.1 --- a/org/capture-video.org Thu Nov 03 10:42:28 2011 -0700 1.2 +++ b/org/capture-video.org Thu Nov 03 16:01:14 2011 -0700 1.3 @@ -143,53 +143,147 @@ 1.4 called. 1.5 1.6 1.7 -=./jme3/src/core/com/jme3/system/IsoTimer.java= 1.8 -#+include ./jme3/src/core/com/jme3/system/IsoTimer.java src java 1.9 +=./src/com/aurellem/capture/IsoTimer.java= 1.10 +#+include ../../jmeCapture/src/com/aurellem/capture/IsoTimer.java src java 1.11 1.12 If an Application uses this =IsoTimer= instead of the normal one, we 1.13 can be sure that every call to =simpleUpdate=, for example, corresponds 1.14 to exactly $(\frac{1}{fps})$ seconds of game-time. 1.15 1.16 -In order to facilitate setting the =Timer= in user code, I added 1.17 -getter and setter methods to =Application.java=. 1.18 - 1.19 -In =./jme3/src/core/com/jme3/app/Application.java= I added: 1.20 -#+include ./jme3/src/core/com/jme3/app/Application.java src java :lines "340-356" 1.21 - 1.22 * Encoding to Video 1.23 1.24 Now that the issue of time is solved, we just need a function that 1.25 writes each frame to a video. We can put this function somewhere 1.26 where it will be called exactly one per frame. 1.27 1.28 +The basic functions that a =VideoRecorder= should support are 1.29 +recording, starting, stopping, and possibly a final finishing step 1.30 +there it finilizes the recording (such as writing headers for a video 1.31 +file). 1.32 + 1.33 +An appropiate interface describing this behaviour could look like 1.34 +this: 1.35 + 1.36 +=./src/com/aurellem/capture/video/VideoRecorder.java= 1.37 +#+include ../../jmeCapture/src/com/aurellem/capture/video/VideoRecorder.java src java 1.38 + 1.39 + 1.40 JME3 already provides exactly the class we need: the =SceneProcessor= 1.41 class can be attached to any viewport and the methods defined therein 1.42 will be called at the appropriate points in the rendering process. 1.43 1.44 +However, it is also important to properly close the video stream and 1.45 +write headers and such, and even though =SceneProcessor= has a 1.46 +=.cleanup()= method, it is only called when the =SceneProcessor= is 1.47 +removed from the =RenderManager=, not when the game is shutting down 1.48 +when the user pressed ESC, for example. To obtain reliable shutdown 1.49 +behaviour, we also have to implement =AppState=, which provides a 1.50 +=.cleanup()= method that /is/ called on shutdown. 1.51 + 1.52 +Here is an AbstractVideoRecorder class that takes care of the details 1.53 +of setup and teardown. 1.54 + 1.55 +=./src/com/aurellem/capture/video/AbstractVideoRecorder.java= 1.56 +#+include ../../jmeCapture/src/com/aurellem/capture/video/AbstractVideoRecorder.java src java 1.57 + 1.58 If you want to generate video from Java, a great option is [[http://www.xuggle.com/][Xuggle]]. It 1.59 takes care of everything related to video encoding and decoding and 1.60 runs on Windows, Linux and Mac. Out of all the video frameworks for 1.61 Java I personally like this one the best. 1.62 1.63 -Here is a =SceneProcessor= that uses [[http://www.xuggle.com/][Xuggle]] to write each frame to a 1.64 +Here is a =VideoRecorder= that uses [[http://www.xuggle.com/][Xuggle]] to write each frame to a 1.65 video file. 1.66 1.67 -=./jme3/src/core/com/jme3/app/VideoProcessor.java= 1.68 -#+include ./jme3/src/core/com/jme3/app/VideoProcessor.java src java 1.69 +=./src/com/aurellem/capture/video/XuggleVideoRecorder.java= 1.70 +#+include ../../jmeCapture/src/com/aurellem/capture/video/XuggleVideoRecorder.java src java 1.71 1.72 With this, we are able to record video! 1.73 1.74 +However, it can be hard to properly install Xuggle. For those of you 1.75 +who would rather not use Xuggle, here is an alternate class that uses 1.76 +[[http://www.randelshofer.ch/blog/2008/08/writing-avi-videos-in-pure-java/][Werner Randelshofer's]] excellent pure Java AVI file writer. 1.77 + 1.78 +=./src/com/aurellem/capture/video/AVIVideoRecorder.java= 1.79 +#+include ../../jmeCapture/src/com/aurellem/capture/video/AVIVideoRecorder.java src java 1.80 + 1.81 +This =AVIVideoRecorder= is more limited than the 1.82 +=XuggleVideoRecorder=, but requires less external dependencies. 1.83 + 1.84 +Finally, for those of you who prefer to create the final video from a 1.85 +sequence of images, there is the =FileVideoRecorder=, which records 1.86 +each frame to a folder as a sequentially numbered image file. Note 1.87 +that you have to remember the FPS at which you recorded the video, as 1.88 +this information is lost when saving each frame to a file. 1.89 + 1.90 +=./src/com/aurellem/capture/video/FileVideoRecorder.java= 1.91 +#+include ../../jmeCapture/src/com/aurellem/capture/video/FileVideoRecorder.java src java 1.92 + 1.93 + 1.94 +* /Really/ Simple Video Recording 1.95 + 1.96 +The most common case for recording a video is probably to just capture 1.97 +whatever is on your screen exactly as you see it. In this case, this 1.98 +method will do. 1.99 + 1.100 +#+begin_src java 1.101 +public static void captureVideo(final Application app, 1.102 + final File file) throws IOException{ 1.103 + final AbstractVideoRecorder videoRecorder; 1.104 + if (file.getCanonicalPath().endsWith(".avi")){ 1.105 + videoRecorder = new AVIVideoRecorder(file);} 1.106 + else if (file.isDirectory()){ 1.107 + videoRecorder = new FileVideoRecorder(file);} 1.108 + else { videoRecorder = new XuggleVideoRecorder(file);} 1.109 + 1.110 + Callable<Object> thunk = new Callable<Object>(){ 1.111 + public Object call(){ 1.112 + ViewPort viewPort = 1.113 + app.getRenderManager() 1.114 + .createPostView("aurellem record", app.getCamera()); 1.115 + viewPort.setClearFlags(false, false, false); 1.116 + // get GUI node stuff 1.117 + for (Spatial s : app.getGuiViewPort().getScenes()){ 1.118 + viewPort.attachScene(s); 1.119 + } 1.120 + app.getStateManager().attach(videoRecorder); 1.121 + viewPort.addProcessor(videoRecorder); 1.122 + return null; 1.123 + } 1.124 + }; 1.125 + app.enqueue(thunk); 1.126 +} 1.127 +#+end_src 1.128 + 1.129 +This will select the appropiate backend =VideoRecorder= class 1.130 +depending on the file name you specify, and insturment your 1.131 +application to record video to the file. You should still set the 1.132 +game's timer to an =IsoTimer= with the desired fps. 1.133 + 1.134 +This example will record video from the ocean scene from the 1.135 +jMonkeyEngine test suite. 1.136 +#+begin_src java 1.137 +File video = File.createTempFile("JME-water-video", ".avi"); 1.138 +captureVideo(app, video); 1.139 +app.start(); 1.140 +System.out.println(video.getCanonicalPath()); 1.141 +#+end_src 1.142 + 1.143 + 1.144 +I've added support for this under a class called 1.145 +=com.aurellem.capture.Capture=. You can get it [[http://hg.bortreb.com/jmeCapture/][here]]. 1.146 + 1.147 + 1.148 * Hello Video! 1.149 1.150 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 1.151 augmented it with video output as follows: 1.152 1.153 -=./jme3/src/test/jme3test/helloworld/HelloVideo.java= 1.154 -#+include ./jme3/src/test/jme3test/helloworld/HelloVideo.java src java 1.155 +=./src/com/aurellem/capture/examples/HelloVideo.java= 1.156 +#+include ../../src/com/aurellem/capture/examples/HelloVideo.java src java 1.157 1.158 The videos are created in the =hello-video= directory 1.159 1.160 -#+begin_src sh :results verbatim 1.161 +#+begin_src sh :results verbatim :exports both 1.162 du -h hello-video/* 1.163 #+end_src 1.164 1.165 @@ -216,7 +310,6 @@ 1.166 #+END_HTML 1.167 1.168 1.169 - 1.170 * Summary 1.171 It's quite easy to augment your own application to record video, 1.172 almost regardless of how complicated the actual application is. You 1.173 @@ -231,26 +324,19 @@ 1.174 this.setTimer(new IsoTimer(30)); 1.175 #+end_src 1.176 1.177 -Somewhere in the initialization of your Application. Right now, you 1.178 -will have to add the =setTimer= method to =Application=, but hopefully 1.179 -this method will be included soon by the JMonkeyEngine3 team. 1.180 - 1.181 -Then, you create a =VideoProcessor= object and attach it to the 1.182 -=ViewPort= from which you want to record. 1.183 +Somewhere in the initialization of your Application. 1.184 1.185 If you want to record from the game's main =ViewPort= to a file called 1.186 =/home/r/record.flv=, then add: 1.187 1.188 #+begin_src java :exports code 1.189 -viewPort.addProcessor(new VideoProcessor(new File("/home/r/record.flv"))); 1.190 +Capture.captureVideo(app, new File("/home/r/record.flv")); 1.191 #+end_src 1.192 1.193 -Do this for each =ViewPort= from which you want to record. The more 1.194 -ViewPorts from which you record, the slower the game will run, but 1.195 -this slowness will not affect the final video output. 1.196 +Before you call =app.start()=; 1.197 1.198 * More Examples 1.199 -** Hello Physics 1.200 +** COMMENT Hello Physics 1.201 =HelloVideo= is boring. Let's add some video capturing to =HelloPhysics= 1.202 and create something fun! 1.203 1.204 @@ -341,7 +427,7 @@ 1.205 Thats a terribly large size! 1.206 Let's compress it: 1.207 1.208 -** Compressing the HelloPhysics Video 1.209 +** COMMENT Compressing the HelloPhysics Video 1.210 First, we'll scale the video, then, we'll decrease it's bitrate. The 1.211 end result will be perfect for upload to YouTube. 1.212 1.213 @@ -451,7 +537,7 @@ 1.214 1.215 * Sample Videos 1.216 I encoded most of the original JME3 Hello demos for your viewing 1.217 -pleasure, all using the =VideoProcessor= and =IsoTimer= classes. 1.218 +pleasure, all using the =Capture= and =IsoTimer= classes. 1.219 1.220 ** HelloTerrain 1.221 [[http://youtu.be/5_4wyDFwrVQ][HelloTerrain.avi]] 1.222 @@ -651,3 +737,4 @@ 1.223 1.224 1.225 1.226 +