comparison org/capture-video.org @ 42:ecafe87ffddc

updating capture video with new examples
author Robert McIntyre <rlm@mit.edu>
date Thu, 03 Nov 2011 16:01:14 -0700
parents 97703c7f020e
children da4de661c5d9
comparison
equal deleted inserted replaced
41:cce471a4108a 42:ecafe87ffddc
141 The easiest way to achieve this special timing is to create a new 141 The easiest way to achieve this special timing is to create a new
142 timer that always reports the same framerate to JME3 every time it is 142 timer that always reports the same framerate to JME3 every time it is
143 called. 143 called.
144 144
145 145
146 =./jme3/src/core/com/jme3/system/IsoTimer.java= 146 =./src/com/aurellem/capture/IsoTimer.java=
147 #+include ./jme3/src/core/com/jme3/system/IsoTimer.java src java 147 #+include ../../jmeCapture/src/com/aurellem/capture/IsoTimer.java src java
148 148
149 If an Application uses this =IsoTimer= instead of the normal one, we 149 If an Application uses this =IsoTimer= instead of the normal one, we
150 can be sure that every call to =simpleUpdate=, for example, corresponds 150 can be sure that every call to =simpleUpdate=, for example, corresponds
151 to exactly $(\frac{1}{fps})$ seconds of game-time. 151 to exactly $(\frac{1}{fps})$ seconds of game-time.
152 152
153 In order to facilitate setting the =Timer= in user code, I added
154 getter and setter methods to =Application.java=.
155
156 In =./jme3/src/core/com/jme3/app/Application.java= I added:
157 #+include ./jme3/src/core/com/jme3/app/Application.java src java :lines "340-356"
158
159 * Encoding to Video 153 * Encoding to Video
160 154
161 Now that the issue of time is solved, we just need a function that 155 Now that the issue of time is solved, we just need a function that
162 writes each frame to a video. We can put this function somewhere 156 writes each frame to a video. We can put this function somewhere
163 where it will be called exactly one per frame. 157 where it will be called exactly one per frame.
164 158
159 The basic functions that a =VideoRecorder= should support are
160 recording, starting, stopping, and possibly a final finishing step
161 there it finilizes the recording (such as writing headers for a video
162 file).
163
164 An appropiate interface describing this behaviour could look like
165 this:
166
167 =./src/com/aurellem/capture/video/VideoRecorder.java=
168 #+include ../../jmeCapture/src/com/aurellem/capture/video/VideoRecorder.java src java
169
170
165 JME3 already provides exactly the class we need: the =SceneProcessor= 171 JME3 already provides exactly the class we need: the =SceneProcessor=
166 class can be attached to any viewport and the methods defined therein 172 class can be attached to any viewport and the methods defined therein
167 will be called at the appropriate points in the rendering process. 173 will be called at the appropriate points in the rendering process.
174
175 However, it is also important to properly close the video stream and
176 write headers and such, and even though =SceneProcessor= has a
177 =.cleanup()= method, it is only called when the =SceneProcessor= is
178 removed from the =RenderManager=, not when the game is shutting down
179 when the user pressed ESC, for example. To obtain reliable shutdown
180 behaviour, we also have to implement =AppState=, which provides a
181 =.cleanup()= method that /is/ called on shutdown.
182
183 Here is an AbstractVideoRecorder class that takes care of the details
184 of setup and teardown.
185
186 =./src/com/aurellem/capture/video/AbstractVideoRecorder.java=
187 #+include ../../jmeCapture/src/com/aurellem/capture/video/AbstractVideoRecorder.java src java
168 188
169 If you want to generate video from Java, a great option is [[http://www.xuggle.com/][Xuggle]]. It 189 If you want to generate video from Java, a great option is [[http://www.xuggle.com/][Xuggle]]. It
170 takes care of everything related to video encoding and decoding and 190 takes care of everything related to video encoding and decoding and
171 runs on Windows, Linux and Mac. Out of all the video frameworks for 191 runs on Windows, Linux and Mac. Out of all the video frameworks for
172 Java I personally like this one the best. 192 Java I personally like this one the best.
173 193
174 Here is a =SceneProcessor= that uses [[http://www.xuggle.com/][Xuggle]] to write each frame to a 194 Here is a =VideoRecorder= that uses [[http://www.xuggle.com/][Xuggle]] to write each frame to a
175 video file. 195 video file.
176 196
177 =./jme3/src/core/com/jme3/app/VideoProcessor.java= 197 =./src/com/aurellem/capture/video/XuggleVideoRecorder.java=
178 #+include ./jme3/src/core/com/jme3/app/VideoProcessor.java src java 198 #+include ../../jmeCapture/src/com/aurellem/capture/video/XuggleVideoRecorder.java src java
179 199
180 With this, we are able to record video! 200 With this, we are able to record video!
201
202 However, it can be hard to properly install Xuggle. For those of you
203 who would rather not use Xuggle, here is an alternate class that uses
204 [[http://www.randelshofer.ch/blog/2008/08/writing-avi-videos-in-pure-java/][Werner Randelshofer's]] excellent pure Java AVI file writer.
205
206 =./src/com/aurellem/capture/video/AVIVideoRecorder.java=
207 #+include ../../jmeCapture/src/com/aurellem/capture/video/AVIVideoRecorder.java src java
208
209 This =AVIVideoRecorder= is more limited than the
210 =XuggleVideoRecorder=, but requires less external dependencies.
211
212 Finally, for those of you who prefer to create the final video from a
213 sequence of images, there is the =FileVideoRecorder=, which records
214 each frame to a folder as a sequentially numbered image file. Note
215 that you have to remember the FPS at which you recorded the video, as
216 this information is lost when saving each frame to a file.
217
218 =./src/com/aurellem/capture/video/FileVideoRecorder.java=
219 #+include ../../jmeCapture/src/com/aurellem/capture/video/FileVideoRecorder.java src java
220
221
222 * /Really/ Simple Video Recording
223
224 The most common case for recording a video is probably to just capture
225 whatever is on your screen exactly as you see it. In this case, this
226 method will do.
227
228 #+begin_src java
229 public static void captureVideo(final Application app,
230 final File file) throws IOException{
231 final AbstractVideoRecorder videoRecorder;
232 if (file.getCanonicalPath().endsWith(".avi")){
233 videoRecorder = new AVIVideoRecorder(file);}
234 else if (file.isDirectory()){
235 videoRecorder = new FileVideoRecorder(file);}
236 else { videoRecorder = new XuggleVideoRecorder(file);}
237
238 Callable<Object> thunk = new Callable<Object>(){
239 public Object call(){
240 ViewPort viewPort =
241 app.getRenderManager()
242 .createPostView("aurellem record", app.getCamera());
243 viewPort.setClearFlags(false, false, false);
244 // get GUI node stuff
245 for (Spatial s : app.getGuiViewPort().getScenes()){
246 viewPort.attachScene(s);
247 }
248 app.getStateManager().attach(videoRecorder);
249 viewPort.addProcessor(videoRecorder);
250 return null;
251 }
252 };
253 app.enqueue(thunk);
254 }
255 #+end_src
256
257 This will select the appropiate backend =VideoRecorder= class
258 depending on the file name you specify, and insturment your
259 application to record video to the file. You should still set the
260 game's timer to an =IsoTimer= with the desired fps.
261
262 This example will record video from the ocean scene from the
263 jMonkeyEngine test suite.
264 #+begin_src java
265 File video = File.createTempFile("JME-water-video", ".avi");
266 captureVideo(app, video);
267 app.start();
268 System.out.println(video.getCanonicalPath());
269 #+end_src
270
271
272 I've added support for this under a class called
273 =com.aurellem.capture.Capture=. You can get it [[http://hg.bortreb.com/jmeCapture/][here]].
274
181 275
182 * Hello Video! 276 * Hello Video!
183 277
184 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 278 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
185 augmented it with video output as follows: 279 augmented it with video output as follows:
186 280
187 =./jme3/src/test/jme3test/helloworld/HelloVideo.java= 281 =./src/com/aurellem/capture/examples/HelloVideo.java=
188 #+include ./jme3/src/test/jme3test/helloworld/HelloVideo.java src java 282 #+include ../../src/com/aurellem/capture/examples/HelloVideo.java src java
189 283
190 The videos are created in the =hello-video= directory 284 The videos are created in the =hello-video= directory
191 285
192 #+begin_src sh :results verbatim 286 #+begin_src sh :results verbatim :exports both
193 du -h hello-video/* 287 du -h hello-video/*
194 #+end_src 288 #+end_src
195 289
196 #+results: 290 #+results:
197 : 932K hello-video/hello-video-moving.flv 291 : 932K hello-video/hello-video-moving.flv
214 </iframe> 308 </iframe>
215 309
216 #+END_HTML 310 #+END_HTML
217 311
218 312
219
220 * Summary 313 * Summary
221 It's quite easy to augment your own application to record video, 314 It's quite easy to augment your own application to record video,
222 almost regardless of how complicated the actual application is. You 315 almost regardless of how complicated the actual application is. You
223 can also record from multiple ViewPorts as the above example shows. 316 can also record from multiple ViewPorts as the above example shows.
224 317
229 322
230 #+begin_src java :exports code 323 #+begin_src java :exports code
231 this.setTimer(new IsoTimer(30)); 324 this.setTimer(new IsoTimer(30));
232 #+end_src 325 #+end_src
233 326
234 Somewhere in the initialization of your Application. Right now, you 327 Somewhere in the initialization of your Application.
235 will have to add the =setTimer= method to =Application=, but hopefully
236 this method will be included soon by the JMonkeyEngine3 team.
237
238 Then, you create a =VideoProcessor= object and attach it to the
239 =ViewPort= from which you want to record.
240 328
241 If you want to record from the game's main =ViewPort= to a file called 329 If you want to record from the game's main =ViewPort= to a file called
242 =/home/r/record.flv=, then add: 330 =/home/r/record.flv=, then add:
243 331
244 #+begin_src java :exports code 332 #+begin_src java :exports code
245 viewPort.addProcessor(new VideoProcessor(new File("/home/r/record.flv"))); 333 Capture.captureVideo(app, new File("/home/r/record.flv"));
246 #+end_src 334 #+end_src
247 335
248 Do this for each =ViewPort= from which you want to record. The more 336 Before you call =app.start()=;
249 ViewPorts from which you record, the slower the game will run, but
250 this slowness will not affect the final video output.
251 337
252 * More Examples 338 * More Examples
253 ** Hello Physics 339 ** COMMENT Hello Physics
254 =HelloVideo= is boring. Let's add some video capturing to =HelloPhysics= 340 =HelloVideo= is boring. Let's add some video capturing to =HelloPhysics=
255 and create something fun! 341 and create something fun!
256 342
257 This example is a modified version of =HelloPhysics= that creates four 343 This example is a modified version of =HelloPhysics= that creates four
258 simultaneous views of the same scene of cannonballs careening into a 344 simultaneous views of the same scene of cannonballs careening into a
339 425
340 426
341 Thats a terribly large size! 427 Thats a terribly large size!
342 Let's compress it: 428 Let's compress it:
343 429
344 ** Compressing the HelloPhysics Video 430 ** COMMENT Compressing the HelloPhysics Video
345 First, we'll scale the video, then, we'll decrease it's bitrate. The 431 First, we'll scale the video, then, we'll decrease it's bitrate. The
346 end result will be perfect for upload to YouTube. 432 end result will be perfect for upload to YouTube.
347 433
348 #+begin_src sh :results silent 434 #+begin_src sh :results silent
349 cd youtube 435 cd youtube
449 JME3 Xuggle Aurellem video capture 535 JME3 Xuggle Aurellem video capture
450 536
451 537
452 * Sample Videos 538 * Sample Videos
453 I encoded most of the original JME3 Hello demos for your viewing 539 I encoded most of the original JME3 Hello demos for your viewing
454 pleasure, all using the =VideoProcessor= and =IsoTimer= classes. 540 pleasure, all using the =Capture= and =IsoTimer= classes.
455 541
456 ** HelloTerrain 542 ** HelloTerrain
457 [[http://youtu.be/5_4wyDFwrVQ][HelloTerrain.avi]] 543 [[http://youtu.be/5_4wyDFwrVQ][HelloTerrain.avi]]
458 544
459 #+begin_html 545 #+begin_html
649 735
650 736
651 737
652 738
653 739
740