# HG changeset patch # User Dylan Holmes # Date 1329290115 21600 # Node ID da4de661c5d9b8dfd1dadf27f85d67c3554da50b # Parent 12e6231eae8e68ec26f6bf9a14a52719571f77f3 I changed Capture Video\! diff -r 12e6231eae8e -r da4de661c5d9 org/capture-video.org --- a/org/capture-video.org Tue Feb 14 23:12:56 2012 -0600 +++ b/org/capture-video.org Wed Feb 15 01:15:15 2012 -0600 @@ -24,35 +24,70 @@ smooth video output at a constant framerate. -* Game-time vs. User-time vs. Video-time +* Video recording requires a steady framerate +** The built-in =Timer= rushes to keep up. +#* Game-time vs. User-time vs. Video-time -A standard JME3 application that extends =SimpleApplication= or -=Application= tries as hard as it can to keep in sync with -/user-time/. If a ball is rolling at 1 game-mile per game-hour in the -game, and you wait for one user-hour as measured by the clock on your -wall, then the ball should have traveled exactly one game-mile. In -order to keep sync with the real world, the game throttles its physics -engine and graphics display. If the computations involved in running -the game are too intense, then the game will first skip frames, then -sacrifice physics accuracy. If there are particuraly demanding -computations, then you may only get 1 fps, and the ball may tunnel -through the floor or obstacles due to inaccurate physics simulation, -but after the end of one user-hour, that ball will have traveled one -game-mile. +Standard JME3 applications use a =Timer= object to manage time +in the simulated world. Because most JME3 applications (e.g. games) +are supposed to happen \ldquo{}live\rdquo{}, the built-in =Timer= +requires simulated time to match real time. This means that the +application must rush to finish all of its calculations on +schedule: the more complicated the calculations, the more the +application is obligated to rush. And if the workload becomes too +much to handle on schedule, =Timer= forces the application to cut +corners: it demands fast, approximate answers instead of careful, +accurate ones. Although this policy sometimes causes physically impossible +glitches and choppy framerates, it ensures that the user will never be +kept waiting while the computer stops to make a complicated +calculation. -When we're recording video, we don't care if the game-time syncs with -user-time, but instead whether the time in the recorded video -(video-time) syncs with user-time. To continue the analogy, if we -recorded the ball rolling at 1 game-mile per game-hour and watched the -video later, we would want to see 30 fps video of the ball rolling at -1 video-mile per /user-hour/. It doesn't matter how much user-time it -took to simulate that hour of game-time to make the high-quality -recording. +Now, the built-in =Timer= values speed over accuracy because real-time +applications require it. On the other hand, if your goal is to record a +glitch-free video, you need a =Timer= that will take its time to +ensure that all calculations are accurate, even if they take a long time. In the next section, we +will create a new kind of =Timer=\mdash{}called =IsoTimer=\mdash{}which +slows down to let the computer finish all its calculations. The result +is a perfectly steady framerate and a flawless physical simulation. -* COMMENT Two examples to clarify the point: -** Recording from a Simple Simulation +# are supposed to happen \ldquo live \rdquo, this =Timer= requires the +# application to update in real-time. In order to keep up with the real world, JME applications cannot +# afford to take too much time on expensive computations. Whenever the +# workload becomes too much for the computer to handle on schedule, +# =Timer= forces the computer to cut corners, giving fast, approximate +# answers instead of careful, accurate ones. Although physical accuracy sometimes +# suffers as a result, this policy ensures that the user will never be +# kept waiting while the computer stops to make a complicated +# calculation. -*** Without a Special Timer +#fast answers are more important than accurate ones. + +# A standard JME3 application that extends =SimpleApplication= or +# =Application= tries as hard as it can to keep in sync with +# /user-time/. If a ball is rolling at 1 game-mile per game-hour in the +# game, and you wait for one user-hour as measured by the clock on your +# wall, then the ball should have traveled exactly one game-mile. In +# order to keep sync with the real world, the game throttles its physics +# engine and graphics display. If the computations involved in running +# the game are too intense, then the game will first skip frames, then +# sacrifice physics accuracy. If there are particuraly demanding +# computations, then you may only get 1 fps, and the ball may tunnel +# through the floor or obstacles due to inaccurate physics simulation, +# but after the end of one user-hour, that ball will have traveled one +# game-mile. + +# When we're recording video, we don't care if the game-time syncs with +# user-time, but instead whether the time in the recorded video +# (video-time) syncs with user-time. To continue the analogy, if we +# recorded the ball rolling at 1 game-mile per game-hour and watched the +# video later, we would want to see 30 fps video of the ball rolling at +# 1 video-mile per /user-hour/. It doesn't matter how much user-time it +# took to simulate that hour of game-time to make the high-quality +# recording. +** COMMENT Two examples to clarify the point: +*** Recording from a Simple Simulation + +**** Without a Special Timer You have a simulation of a ball rolling on an infinite empty plane at one game-mile per game-hour, and a really good computer. Normally, JME3 will throttle the physics engine and graphics display to sync the @@ -66,7 +101,7 @@ frames per user-second. -*** With a Special Timer +**** With a Special Timer Then, you change the game's timer so that user-time will be synced to video-time. Assume that encoding a single frame takes 0 seconds user-time to complete. @@ -85,7 +120,7 @@ will take exactly one hour user-time (and one hour video-time) for the ball in the video to travel one video-mile. -** Recording from a Complex Simulation +*** Recording from a Complex Simulation *** Without a Special Timer You have a simulation of a ball rolling on an infinite empty plane at @@ -122,7 +157,7 @@ will have an hour long video at 60 fps. -* COMMENT proposed names for the new timer +** COMMENT proposed names for the new timer # METRONOME # IsoTimer # EvenTimer @@ -136,7 +171,7 @@ # SteadyTimer -* =IsoTimer= records time like a metronome +** =IsoTimer= records time like a metronome The easiest way to achieve this special timing is to create a new timer that always reports the same framerate to JME3 every time it is @@ -150,15 +185,18 @@ can be sure that every call to =simpleUpdate=, for example, corresponds to exactly $(\frac{1}{fps})$ seconds of game-time. -* Encoding to Video +* =VideoRecorder= manages video feeds in JMonkeyEngine. + + +** =AbstractVideoRecorder= provides a general framework for managing videos. 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. +where it will be called exactly once 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 +recording, starting, stopping, and possibly a cleanup step +where it finalizes the recording (e.g. by writing headers for a video file). An appropiate interface describing this behaviour could look like @@ -186,10 +224,13 @@ =./src/com/aurellem/capture/video/AbstractVideoRecorder.java= #+include ../../jmeCapture/src/com/aurellem/capture/video/AbstractVideoRecorder.java src java + +** There are many options for handling video files in 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. +Java, I personally like this one the best. Here is a =VideoRecorder= that uses [[http://www.xuggle.com/][Xuggle]] to write each frame to a video file. @@ -199,8 +240,7 @@ 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 +However, it can be hard to properly install Xuggle. If you 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= @@ -219,11 +259,19 @@ #+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. + +* How to record videos yourself + +** Include this code. + + No matter how complicated your application is, it's easy to add + support for video output with just a few lines of code. +# You can also record from multiple ViewPorts as the above example shows. + +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{} +exactly what's on screen. In this case, the following simple =captureVideo= +method will do the job: #+begin_src java public static void captureVideo(final Application app, @@ -254,13 +302,37 @@ } #+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 method selects the appropriate =VideoRecorder= class +for the file type you specify, and instructs your +application to record video to the file. + + + + +Now that you have a =captureVideo= method, you use it like this: + + - Establish an =Isotimer= and set its framerate :: For example, if + you want to record video with a framerate of 30 fps, include + the following line of code somewhere in the initializtion of + your application: +#+begin_src java :exports code +this.setTimer(new IsoTimer(30)); +#+end_src + + - Choose the output file :: If you want to record from the game's + main =ViewPort= to a file called =/home/r/record.flv=, then + include the following line of code somewhere before you call =app.start()=; + + #+begin_src java :exports code +Capture.captureVideo(app, new File("/home/r/record.flv")); + #+end_src + + +** Simple example + This example will record video from the ocean scene from the -jMonkeyEngine test suite. +JMonkeyEngine test suite. #+begin_src java File video = File.createTempFile("JME-water-video", ".avi"); captureVideo(app, video); @@ -273,7 +345,7 @@ =com.aurellem.capture.Capture=. You can get it [[http://hg.bortreb.com/jmeCapture/][here]]. -* Hello Video! +** Hello Video! example 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: @@ -310,32 +382,7 @@ #+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 -can also record from multiple ViewPorts as the above example shows. - -The process for adding video recording to your application is as -follows: - -Assuming you want to record at 30 fps, add: - -#+begin_src java :exports code -this.setTimer(new IsoTimer(30)); -#+end_src - -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 -Capture.captureVideo(app, new File("/home/r/record.flv")); -#+end_src - -Before you call =app.start()=; - -* More Examples +* COMMENT More Examples ** COMMENT Hello Physics =HelloVideo= is boring. Let's add some video capturing to =HelloPhysics= and create something fun! @@ -535,7 +582,7 @@ JME3 Xuggle Aurellem video capture -* Sample Videos +* Showcase of recorded videos I encoded most of the original JME3 Hello demos for your viewing pleasure, all using the =Capture= and =IsoTimer= classes.