changeset 279:d8ff1a293b8c

merged video changes
author Robert McIntyre <rlm@mit.edu>
date Wed, 15 Feb 2012 10:26:05 -0700
parents 4c07724c4f0a (current diff) 54ec231dec4c (diff)
children 301e91a6c2d1
files
diffstat 1 files changed, 120 insertions(+), 73 deletions(-) [+]
line wrap: on
line diff
     1.1 --- a/org/capture-video.org	Wed Feb 15 10:25:54 2012 -0700
     1.2 +++ b/org/capture-video.org	Wed Feb 15 10:26:05 2012 -0700
     1.3 @@ -24,35 +24,70 @@
     1.4  smooth video output at a constant framerate.
     1.5  
     1.6  
     1.7 -* Game-time vs. User-time vs. Video-time
     1.8 +* Video recording requires a steady framerate
     1.9 +** The built-in =Timer= rushes to keep up.
    1.10 +#* Game-time vs. User-time vs. Video-time
    1.11  
    1.12 -A standard JME3 application that extends =SimpleApplication= or
    1.13 -=Application= tries as hard as it can to keep in sync with
    1.14 -/user-time/.  If a ball is rolling at 1 game-mile per game-hour in the
    1.15 -game, and you wait for one user-hour as measured by the clock on your
    1.16 -wall, then the ball should have traveled exactly one game-mile. In
    1.17 -order to keep sync with the real world, the game throttles its physics
    1.18 -engine and graphics display.  If the computations involved in running
    1.19 -the game are too intense, then the game will first skip frames, then
    1.20 -sacrifice physics accuracy.  If there are particuraly demanding
    1.21 -computations, then you may only get 1 fps, and the ball may tunnel
    1.22 -through the floor or obstacles due to inaccurate physics simulation,
    1.23 -but after the end of one user-hour, that ball will have traveled one
    1.24 -game-mile.
    1.25 +Standard JME3 applications use a =Timer= object to manage time
    1.26 +in the simulated world. Because most JME3 applications (e.g. games)
    1.27 +are supposed to happen \ldquo{}live\rdquo{}, the built-in =Timer=
    1.28 +requires simulated time to match real time. This means that the
    1.29 +application must rush to finish all of its calculations on
    1.30 +schedule: the more complicated the calculations, the more the
    1.31 +application is obligated to rush. And if the workload becomes too
    1.32 +much to handle on schedule, =Timer= forces the application to cut
    1.33 +corners: it demands fast, approximate answers instead of careful,
    1.34 +accurate ones.  Although this policy sometimes causes physically impossible
    1.35 +glitches and choppy framerates, it ensures that the user will never be
    1.36 +kept waiting while the computer stops to make a complicated
    1.37 +calculation.
    1.38  
    1.39 -When we're recording video, we don't care if the game-time syncs with
    1.40 -user-time, but instead whether the time in the recorded video
    1.41 -(video-time) syncs with user-time. To continue the analogy, if we
    1.42 -recorded the ball rolling at 1 game-mile per game-hour and watched the
    1.43 -video later, we would want to see 30 fps video of the ball rolling at
    1.44 -1 video-mile per /user-hour/. It doesn't matter how much user-time it
    1.45 -took to simulate that hour of game-time to make the high-quality
    1.46 -recording.
    1.47 +Now, the built-in =Timer= values speed over accuracy because real-time
    1.48 +applications require it. On the other hand, if your goal is to record a
    1.49 +glitch-free video, you need a =Timer= that will take its time to
    1.50 +ensure that all calculations are accurate, even if they take a long time. In the next section, we
    1.51 +will create a new kind of =Timer=\mdash{}called =IsoTimer=\mdash{}which
    1.52 +slows down to let the computer finish all its calculations. The result
    1.53 +is a perfectly steady framerate and a flawless physical simulation.
    1.54  
    1.55 -* COMMENT Two examples to clarify the point:
    1.56 -** Recording from a Simple Simulation
    1.57 +# are supposed to happen \ldquo live \rdquo, this =Timer= requires the
    1.58 +# application to update in real-time. In order to keep up with the real world, JME applications cannot
    1.59 +# afford to take too much time on expensive computations. Whenever the
    1.60 +# workload becomes too much for the computer to handle on schedule,
    1.61 +# =Timer= forces the computer to cut corners, giving fast, approximate
    1.62 +# answers instead of careful, accurate ones. Although physical accuracy sometimes
    1.63 +# suffers as a result, this policy ensures that the user will never be
    1.64 +# kept waiting while the computer stops to make a complicated
    1.65 +# calculation.
    1.66  
    1.67 -*** Without a Special Timer
    1.68 +#fast answers are more important than accurate ones. 
    1.69 +
    1.70 +# A standard JME3 application that extends =SimpleApplication= or
    1.71 +# =Application= tries as hard as it can to keep in sync with
    1.72 +# /user-time/.  If a ball is rolling at 1 game-mile per game-hour in the
    1.73 +# game, and you wait for one user-hour as measured by the clock on your
    1.74 +# wall, then the ball should have traveled exactly one game-mile. In
    1.75 +# order to keep sync with the real world, the game throttles its physics
    1.76 +# engine and graphics display.  If the computations involved in running
    1.77 +# the game are too intense, then the game will first skip frames, then
    1.78 +# sacrifice physics accuracy.  If there are particuraly demanding
    1.79 +# computations, then you may only get 1 fps, and the ball may tunnel
    1.80 +# through the floor or obstacles due to inaccurate physics simulation,
    1.81 +# but after the end of one user-hour, that ball will have traveled one
    1.82 +# game-mile.
    1.83 +
    1.84 +# When we're recording video, we don't care if the game-time syncs with
    1.85 +# user-time, but instead whether the time in the recorded video
    1.86 +# (video-time) syncs with user-time. To continue the analogy, if we
    1.87 +# recorded the ball rolling at 1 game-mile per game-hour and watched the
    1.88 +# video later, we would want to see 30 fps video of the ball rolling at
    1.89 +# 1 video-mile per /user-hour/. It doesn't matter how much user-time it
    1.90 +# took to simulate that hour of game-time to make the high-quality
    1.91 +# recording.
    1.92 +** COMMENT Two examples to clarify the point:
    1.93 +*** Recording from a Simple Simulation
    1.94 +
    1.95 +**** Without a Special Timer
    1.96  You have a simulation of a ball rolling on an infinite empty plane at
    1.97  one game-mile per game-hour, and a really good computer. Normally,
    1.98  JME3 will throttle the physics engine and graphics display to sync the
    1.99 @@ -66,7 +101,7 @@
   1.100  frames per user-second.
   1.101  
   1.102  
   1.103 -*** With a Special Timer
   1.104 +**** With a Special Timer
   1.105  Then, you change the game's timer so that user-time will be synced to
   1.106  video-time. Assume that encoding a single frame takes 0 seconds
   1.107  user-time to complete.
   1.108 @@ -85,7 +120,7 @@
   1.109  will take exactly one hour user-time (and one hour video-time) for the
   1.110  ball in the video to travel one video-mile.
   1.111  
   1.112 -** Recording from a Complex Simulation 
   1.113 +*** Recording from a Complex Simulation 
   1.114  
   1.115  *** Without a Special Timer
   1.116  You have a simulation of a ball rolling on an infinite empty plane at
   1.117 @@ -122,7 +157,7 @@
   1.118  will have an hour long video at 60 fps.
   1.119  
   1.120  
   1.121 -* COMMENT proposed names for the new timer
   1.122 +** COMMENT proposed names for the new timer
   1.123  # METRONOME
   1.124  # IsoTimer
   1.125  # EvenTimer
   1.126 @@ -136,7 +171,7 @@
   1.127  # SteadyTimer
   1.128  
   1.129  
   1.130 -* =IsoTimer= records time like a metronome
   1.131 +** =IsoTimer= records time like a metronome
   1.132  
   1.133  The easiest way to achieve this special timing is to create a new
   1.134  timer that always reports the same framerate to JME3 every time it is
   1.135 @@ -150,15 +185,18 @@
   1.136  can be sure that every call to =simpleUpdate=, for example, corresponds
   1.137  to exactly $(\frac{1}{fps})$ seconds of game-time.
   1.138  
   1.139 -* Encoding to Video
   1.140 +* =VideoRecorder= manages video feeds in JMonkeyEngine.
   1.141 +
   1.142 +
   1.143 +** =AbstractVideoRecorder= provides a general framework for managing videos.
   1.144  
   1.145  Now that the issue of time is solved, we just need a function that
   1.146  writes each frame to a video.  We can put this function somewhere
   1.147 -where it will be called exactly one per frame.
   1.148 +where it will be called exactly once per frame.
   1.149  
   1.150  The basic functions that a =VideoRecorder= should support are
   1.151 -recording, starting, stopping, and possibly a final finishing step
   1.152 -there it finilizes the recording (such as writing headers for a video
   1.153 +recording, starting, stopping, and possibly a cleanup step
   1.154 +where it finalizes the recording (e.g. by writing headers for a video
   1.155  file).
   1.156  
   1.157  An appropiate interface describing this behaviour could look like
   1.158 @@ -186,10 +224,13 @@
   1.159  =./src/com/aurellem/capture/video/AbstractVideoRecorder.java=
   1.160  #+include ../../jmeCapture/src/com/aurellem/capture/video/AbstractVideoRecorder.java src java
   1.161  
   1.162 +
   1.163 +** There are many options for handling video files in Java
   1.164 +
   1.165  If you want to generate video from Java, a great option is [[http://www.xuggle.com/][Xuggle]]. It
   1.166  takes care of everything related to video encoding and decoding and
   1.167  runs on Windows, Linux and Mac.  Out of all the video frameworks for
   1.168 -Java I personally like this one the best.
   1.169 +Java, I personally like this one the best.
   1.170  
   1.171  Here is a =VideoRecorder= that uses [[http://www.xuggle.com/][Xuggle]] to write each frame to a
   1.172  video file.
   1.173 @@ -199,8 +240,7 @@
   1.174  
   1.175  With this, we are able to record video!
   1.176  
   1.177 -However, it can be hard to properly install Xuggle. For those of you
   1.178 -who would rather not use Xuggle, here is an alternate class that uses
   1.179 +However, it can be hard to properly install Xuggle. If you would rather not use Xuggle, here is an alternate class that uses
   1.180  [[http://www.randelshofer.ch/blog/2008/08/writing-avi-videos-in-pure-java/][Werner Randelshofer's]] excellent pure Java AVI file writer.
   1.181  
   1.182  =./src/com/aurellem/capture/video/AVIVideoRecorder.java=
   1.183 @@ -219,11 +259,19 @@
   1.184  #+include ../../jmeCapture/src/com/aurellem/capture/video/FileVideoRecorder.java src java
   1.185  
   1.186  
   1.187 -* /Really/ Simple Video Recording
   1.188  
   1.189 -The most common case for recording a video is probably to just capture
   1.190 -whatever is on your screen exactly as you see it.  In this case, this
   1.191 -method will do.
   1.192 +
   1.193 +* How to record videos yourself
   1.194 +
   1.195 +** Include this code.
   1.196 +
   1.197 +  No matter how complicated your application is, it's easy to add
   1.198 +  support for video output with just a few lines of code.
   1.199 +#  You can also record from multiple ViewPorts as the above example shows.
   1.200 +
   1.201 +And although you can use =VideoRecorder= to record advanced split-screen videos with multiple views, in the simplest case, you want to capture a single view\mdash{}
   1.202 +exactly what's on screen. In this case, the following simple =captureVideo=
   1.203 +method will do the job:
   1.204  
   1.205  #+begin_src java
   1.206  public static void captureVideo(final Application app, 
   1.207 @@ -254,13 +302,37 @@
   1.208  }
   1.209  #+end_src
   1.210  
   1.211 -This will select the appropiate backend =VideoRecorder= class
   1.212 -depending on the file name you specify, and insturment your
   1.213 -application to record video to the file.  You should still set the
   1.214 -game's timer to an =IsoTimer= with the desired fps.
   1.215 +This method selects the appropriate =VideoRecorder= class
   1.216 +for the file type you specify, and instructs your
   1.217 +application to record video to the file.
   1.218 +
   1.219 +
   1.220 +
   1.221 +
   1.222 +Now that you have a =captureVideo= method, you use it like this:
   1.223 +
   1.224 +  - Establish an =Isotimer= and set its framerate :: For example, if
   1.225 +       you want to record video with a framerate of 30 fps, include
   1.226 +       the following line of code somewhere in the initializtion of
   1.227 +       your application: 
   1.228 +#+begin_src java :exports code
   1.229 +this.setTimer(new IsoTimer(30));
   1.230 +#+end_src
   1.231 +
   1.232 +  - Choose the output file :: If you want to record from the game's
   1.233 +       main =ViewPort= to a file called =/home/r/record.flv=, then
   1.234 +       include the following line of code somewhere before you call =app.start()=;
   1.235 +
   1.236 +  #+begin_src java :exports code
   1.237 +Capture.captureVideo(app, new File("/home/r/record.flv"));
   1.238 +  #+end_src
   1.239 +
   1.240 +  
   1.241 +** Simple example
   1.242 +
   1.243  
   1.244  This example will record video from the ocean scene from the
   1.245 -jMonkeyEngine test suite.
   1.246 +JMonkeyEngine test suite.
   1.247  #+begin_src java
   1.248  File video = File.createTempFile("JME-water-video", ".avi");
   1.249  captureVideo(app, video);
   1.250 @@ -273,7 +345,7 @@
   1.251  =com.aurellem.capture.Capture=. You can get it [[http://hg.bortreb.com/jmeCapture/][here]].
   1.252  
   1.253  
   1.254 -* Hello Video!
   1.255 +** Hello Video! example
   1.256  
   1.257  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.258  augmented it with video output as follows:
   1.259 @@ -310,32 +382,7 @@
   1.260  #+END_HTML
   1.261  
   1.262    
   1.263 -* Summary 
   1.264 -It's quite easy to augment your own application to record video,
   1.265 -almost regardless of how complicated the actual application is.  You
   1.266 -can also record from multiple ViewPorts as the above example shows.
   1.267 -
   1.268 -The process for adding video recording to your application is as
   1.269 -follows:
   1.270 -
   1.271 -Assuming you want to record at 30 fps, add:
   1.272 -
   1.273 -#+begin_src java :exports code
   1.274 -this.setTimer(new IsoTimer(30));
   1.275 -#+end_src
   1.276 -
   1.277 -Somewhere in the initialization of your Application.
   1.278 -
   1.279 -If you want to record from the game's main =ViewPort= to a file called
   1.280 -=/home/r/record.flv=, then add:
   1.281 -
   1.282 -#+begin_src java :exports code
   1.283 -Capture.captureVideo(app, new File("/home/r/record.flv"));
   1.284 -#+end_src
   1.285 -
   1.286 -Before you call =app.start()=;
   1.287 -
   1.288 -* More Examples
   1.289 +* COMMENT More Examples
   1.290  ** COMMENT Hello Physics
   1.291  =HelloVideo= is boring. Let's add some video capturing to =HelloPhysics=
   1.292  and create something fun!
   1.293 @@ -535,7 +582,7 @@
   1.294   JME3 Xuggle Aurellem video capture
   1.295   
   1.296   
   1.297 -* Sample Videos
   1.298 +* Showcase of recorded videos
   1.299  I encoded most of the original JME3 Hello demos for your viewing
   1.300  pleasure, all using the =Capture= and =IsoTimer= classes.
   1.301