Mercurial > cortex
view org/capture-video.org @ 69:39e4e1542e4a
updated test-suite
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Fri, 09 Dec 2011 23:11:28 -0600 |
parents | ecafe87ffddc |
children | da4de661c5d9 |
line wrap: on
line source
1 #+title: Capture Live Video Feeds from JMonkeyEngine2 #+author: Robert McIntyre3 #+email: rlm@mit.edu4 #+description: Capture video from a JMonkeyEngine3 Application with Xuggle, and use gstreamer to compress the video to upload to YouTube.5 #+keywords: JME3, video, Xuggle, JMonkeyEngine, youtube, capture video, Java6 #+SETUPFILE: ../../aurellem/org/setup.org7 #+INCLUDE: ../../aurellem/org/level-0.org10 * The Problem11 So you've made your cool new JMonkeyEngine3 game and you want to12 create a demo video to show off your hard work. Screen capturing is13 the most straightforward way to do this, but it can slow down your14 game and produce low-quality video as a result. A better way is to15 record a video feed directly from the game while it is16 running.18 In this post, I'll explain how you can alter your JMonkeyEngine3 game19 to output video while it is running. The main trick is to alter the20 pace of JMonkeyEngine3's in-game time: we allow the engine as much21 time as it needs to compute complicated in-game events and to encode22 video frames. As a result, the game appears to speed up and slow down23 as the computational demands shift, but the end result is perfectly24 smooth video output at a constant framerate.27 * Game-time vs. User-time vs. Video-time29 A standard JME3 application that extends =SimpleApplication= or30 =Application= tries as hard as it can to keep in sync with31 /user-time/. If a ball is rolling at 1 game-mile per game-hour in the32 game, and you wait for one user-hour as measured by the clock on your33 wall, then the ball should have traveled exactly one game-mile. In34 order to keep sync with the real world, the game throttles its physics35 engine and graphics display. If the computations involved in running36 the game are too intense, then the game will first skip frames, then37 sacrifice physics accuracy. If there are particuraly demanding38 computations, then you may only get 1 fps, and the ball may tunnel39 through the floor or obstacles due to inaccurate physics simulation,40 but after the end of one user-hour, that ball will have traveled one41 game-mile.43 When we're recording video, we don't care if the game-time syncs with44 user-time, but instead whether the time in the recorded video45 (video-time) syncs with user-time. To continue the analogy, if we46 recorded the ball rolling at 1 game-mile per game-hour and watched the47 video later, we would want to see 30 fps video of the ball rolling at48 1 video-mile per /user-hour/. It doesn't matter how much user-time it49 took to simulate that hour of game-time to make the high-quality50 recording.52 * COMMENT Two examples to clarify the point:53 ** Recording from a Simple Simulation55 *** Without a Special Timer56 You have a simulation of a ball rolling on an infinite empty plane at57 one game-mile per game-hour, and a really good computer. Normally,58 JME3 will throttle the physics engine and graphics display to sync the59 game-time with user-time. If it takes one-thousandth of a second60 user-time to simulate one-sixtieth of a second game time and another61 one-thousandth of a second to draw to the screen, then JME3 will just62 sit around for the remainder of $\frac{1}{60} - \frac{2}{1000}$63 user-seconds, then calculate the next frame in $\frac{2}{1000}$64 user-seconds, then wait, and so on. For every second of user time that65 passes, one second of game-time passes, and the game will run at 6066 frames per user-second.69 *** With a Special Timer70 Then, you change the game's timer so that user-time will be synced to71 video-time. Assume that encoding a single frame takes 0 seconds72 user-time to complete.74 Now, JME3 takes advantage of all available resources. It still takes75 one-thousandth of a second to calculate a physics tick, and another76 one-thousandth to render to the screen. Then it takes 0 seconds to77 write the video frame to disk and encode the video. In only one second78 of user time, JME3 will complete 500 physics-tick/render/encode-video79 cycles, and $\frac{500}{60}=8\frac{1}{3}$ seconds of game-time will80 have passed. Game-time appears to dilate $8\frac{1}{3}\times$ with81 respect to user-time, and in only 7.2 minutes user-time, one hour of82 video will have been recorded. The game itself will run at 500 fps.83 When someone watches the video, they will see 60 frames per84 user-second, and $\frac{1}{60}$ video-seconds will pass each frame. It85 will take exactly one hour user-time (and one hour video-time) for the86 ball in the video to travel one video-mile.88 ** Recording from a Complex Simulation90 *** Without a Special Timer91 You have a simulation of a ball rolling on an infinite empty plane at92 one game-mile per game-hour accompanied by multiple explosions93 involving thousands of nodes, particle effects, and complicated shadow94 shaders to create realistic shadows. You also have a slow95 laptop. Normally, JME3 must sacrifice rendering and physics simulation96 to try to keep up. If it takes $\frac{1}{120}$ of a user-second to97 calculate $\frac{1}{60}$ game-seconds, and an additional98 $\frac{1}{60}$ of a user-second to render to screen, then JME3 has99 it's work cut out for it. In order to render to the screen, it will100 first step the game forward by up to four physics ticks before101 rendering to the screen. If it still isn't fast enough then it will102 decrease the accuracy of the physics engine until game-time and user103 time are synched or a certain threshold is reached, at which point the104 game visibly slows down. In this case, JME3 continuously repeat a105 cycle of two physics ticks, and one screen render. For every106 user-second that passes, one game-second will pass, but the game will107 run at 30 fps instead of 60 fps like before.109 *** With a Special Timer110 Then, you change the game's timer so that user-time will be synced to111 video-time. Once again, assume video encoding takes $\frac{1}{60}$ of112 a user-second.114 Now, JME3 will spend $\frac{1}{120}$ of a user-second to step the115 physics tick $\frac{1}{60}$ game-seconds, $\frac{1}{60}$ to draw to116 the screen, and an additional $\frac{1}{60}$ to encode the video and117 write the frame to disk. This is a total of $\frac{1}{24}$118 user-seconds for each $\frac{1}{60}$ game-seconds. It will take119 $(\frac{60}{24} = 2.5)$ user-hours to record one game-hour and game-time120 will appear to flow two-fifths as fast as user time while the game is121 running. However, just as in example one, when all is said and done we122 will have an hour long video at 60 fps.125 * COMMENT proposed names for the new timer126 # METRONOME127 # IsoTimer128 # EvenTimer129 # PulseTimer130 # FixedTimer131 # RigidTimer132 # FixedTempo133 # RegularTimer134 # MetronomeTimer135 # ConstantTimer136 # SteadyTimer139 * =IsoTimer= records time like a metronome141 The easiest way to achieve this special timing is to create a new142 timer that always reports the same framerate to JME3 every time it is143 called.146 =./src/com/aurellem/capture/IsoTimer.java=147 #+include ../../jmeCapture/src/com/aurellem/capture/IsoTimer.java src java149 If an Application uses this =IsoTimer= instead of the normal one, we150 can be sure that every call to =simpleUpdate=, for example, corresponds151 to exactly $(\frac{1}{fps})$ seconds of game-time.153 * Encoding to Video155 Now that the issue of time is solved, we just need a function that156 writes each frame to a video. We can put this function somewhere157 where it will be called exactly one per frame.159 The basic functions that a =VideoRecorder= should support are160 recording, starting, stopping, and possibly a final finishing step161 there it finilizes the recording (such as writing headers for a video162 file).164 An appropiate interface describing this behaviour could look like165 this:167 =./src/com/aurellem/capture/video/VideoRecorder.java=168 #+include ../../jmeCapture/src/com/aurellem/capture/video/VideoRecorder.java src java171 JME3 already provides exactly the class we need: the =SceneProcessor=172 class can be attached to any viewport and the methods defined therein173 will be called at the appropriate points in the rendering process.175 However, it is also important to properly close the video stream and176 write headers and such, and even though =SceneProcessor= has a177 =.cleanup()= method, it is only called when the =SceneProcessor= is178 removed from the =RenderManager=, not when the game is shutting down179 when the user pressed ESC, for example. To obtain reliable shutdown180 behaviour, we also have to implement =AppState=, which provides a181 =.cleanup()= method that /is/ called on shutdown.183 Here is an AbstractVideoRecorder class that takes care of the details184 of setup and teardown.186 =./src/com/aurellem/capture/video/AbstractVideoRecorder.java=187 #+include ../../jmeCapture/src/com/aurellem/capture/video/AbstractVideoRecorder.java src java189 If you want to generate video from Java, a great option is [[http://www.xuggle.com/][Xuggle]]. It190 takes care of everything related to video encoding and decoding and191 runs on Windows, Linux and Mac. Out of all the video frameworks for192 Java I personally like this one the best.194 Here is a =VideoRecorder= that uses [[http://www.xuggle.com/][Xuggle]] to write each frame to a195 video file.197 =./src/com/aurellem/capture/video/XuggleVideoRecorder.java=198 #+include ../../jmeCapture/src/com/aurellem/capture/video/XuggleVideoRecorder.java src java200 With this, we are able to record video!202 However, it can be hard to properly install Xuggle. For those of you203 who would rather not use Xuggle, here is an alternate class that uses204 [[http://www.randelshofer.ch/blog/2008/08/writing-avi-videos-in-pure-java/][Werner Randelshofer's]] excellent pure Java AVI file writer.206 =./src/com/aurellem/capture/video/AVIVideoRecorder.java=207 #+include ../../jmeCapture/src/com/aurellem/capture/video/AVIVideoRecorder.java src java209 This =AVIVideoRecorder= is more limited than the210 =XuggleVideoRecorder=, but requires less external dependencies.212 Finally, for those of you who prefer to create the final video from a213 sequence of images, there is the =FileVideoRecorder=, which records214 each frame to a folder as a sequentially numbered image file. Note215 that you have to remember the FPS at which you recorded the video, as216 this information is lost when saving each frame to a file.218 =./src/com/aurellem/capture/video/FileVideoRecorder.java=219 #+include ../../jmeCapture/src/com/aurellem/capture/video/FileVideoRecorder.java src java222 * /Really/ Simple Video Recording224 The most common case for recording a video is probably to just capture225 whatever is on your screen exactly as you see it. In this case, this226 method will do.228 #+begin_src java229 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);}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 stuff245 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_src257 This will select the appropiate backend =VideoRecorder= class258 depending on the file name you specify, and insturment your259 application to record video to the file. You should still set the260 game's timer to an =IsoTimer= with the desired fps.262 This example will record video from the ocean scene from the263 jMonkeyEngine test suite.264 #+begin_src java265 File video = File.createTempFile("JME-water-video", ".avi");266 captureVideo(app, video);267 app.start();268 System.out.println(video.getCanonicalPath());269 #+end_src272 I've added support for this under a class called273 =com.aurellem.capture.Capture=. You can get it [[http://hg.bortreb.com/jmeCapture/][here]].276 * Hello Video!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=]] and279 augmented it with video output as follows:281 =./src/com/aurellem/capture/examples/HelloVideo.java=282 #+include ../../src/com/aurellem/capture/examples/HelloVideo.java src java284 The videos are created in the =hello-video= directory286 #+begin_src sh :results verbatim :exports both287 du -h hello-video/*288 #+end_src290 #+results:291 : 932K hello-video/hello-video-moving.flv292 : 640K hello-video/hello-video-static.flv294 And can be immediately uploaded to youtube296 - [[http://www.youtube.com/watch?v=C8gxVAySaPg][hello-video-moving.flv]]297 #+BEGIN_HTML298 <iframe width="425" height="349"299 src="http://www.youtube.com/embed/C8gxVAySaPg"300 frameborder="0" allowfullscreen>301 </iframe>302 #+END_HTML303 - [[http://www.youtube.com/watch?v=pHcFOtIS07Q][hello-video-static.flv]]304 #+BEGIN_HTML305 <iframe width="425" height="349"306 src="http://www.youtube.com/embed/pHcFOtIS07Q"307 frameborder="0" allowfullscreen>308 </iframe>310 #+END_HTML313 * Summary314 It's quite easy to augment your own application to record video,315 almost regardless of how complicated the actual application is. You316 can also record from multiple ViewPorts as the above example shows.318 The process for adding video recording to your application is as319 follows:321 Assuming you want to record at 30 fps, add:323 #+begin_src java :exports code324 this.setTimer(new IsoTimer(30));325 #+end_src327 Somewhere in the initialization of your Application.329 If you want to record from the game's main =ViewPort= to a file called330 =/home/r/record.flv=, then add:332 #+begin_src java :exports code333 Capture.captureVideo(app, new File("/home/r/record.flv"));334 #+end_src336 Before you call =app.start()=;338 * More Examples339 ** COMMENT Hello Physics340 =HelloVideo= is boring. Let's add some video capturing to =HelloPhysics=341 and create something fun!343 This example is a modified version of =HelloPhysics= that creates four344 simultaneous views of the same scene of cannonballs careening into a345 brick wall.347 =./jme3/src/test/jme3test/helloworld/HelloPhysicsWithVideo.java=348 #+include ./jme3/src/test/jme3test/helloworld/HelloPhysicsWithVideo.java src java350 Running the program outputs four videos into the =./physics-videos=351 directory.353 #+begin_src sh :exports both :results verbatim354 ls ./physics-videos | grep -355 #+end_src357 #+results:358 : lower-left.flv359 : lower-right.flv360 : upper-left.flv361 : upper-right.flv363 The videos are fused together with the following =gstreamer= commands:365 #+begin_src sh :results silent366 cd physics-videos368 gst-launch-0.10 \369 filesrc location=./upper-right.flv ! decodebin ! \370 videoscale ! ffmpegcolorspace ! \371 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \372 videobox border-alpha=0 left=-640 ! \373 videomixer name=mix ! ffmpegcolorspace ! videorate ! \374 video/x-raw-yuv, width=1280, height=480, framerate=25/1 ! \375 jpegenc ! avimux ! filesink location=upper.flv \376 \377 filesrc location=./upper-left.flv ! decodebin ! \378 videoscale ! ffmpegcolorspace ! \379 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \380 videobox right=-640 ! mix.381 #+end_src383 #+begin_src sh :results silent384 cd physics-videos386 gst-launch-0.10 \387 filesrc location=./lower-left.flv ! decodebin ! \388 videoscale ! ffmpegcolorspace ! \389 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \390 videobox border-alpha=0 left=-640 ! \391 videomixer name=mix ! ffmpegcolorspace ! videorate ! \392 video/x-raw-yuv, width=1280, height=480, framerate=25/1 ! \393 jpegenc ! avimux ! filesink location=lower.flv \394 \395 filesrc location=./lower-right.flv ! decodebin ! \396 videoscale ! ffmpegcolorspace ! \397 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \398 videobox right=-640 ! mix.399 #+end_src401 #+begin_src sh :results silent402 cd physics-videos404 gst-launch-0.10 \405 filesrc location=./upper.flv ! decodebin ! \406 videoscale ! ffmpegcolorspace ! \407 video/x-raw-yuv, width=1280, height=480, framerate=25/1 ! \408 videobox border-alpha=0 bottom=-480 ! \409 videomixer name=mix ! ffmpegcolorspace ! videorate ! \410 video/x-raw-yuv, width=1280, height=960, framerate=25/1 ! \411 jpegenc ! avimux ! filesink location=../youtube/helloPhysics.flv \412 \413 filesrc location=./lower.flv ! decodebin ! \414 videoscale ! ffmpegcolorspace ! \415 video/x-raw-yuv, width=1280, height=480, framerate=25/1 ! \416 videobox top=-480 ! mix.417 #+end_src419 #+begin_src sh :results verbatim420 du -h youtube/helloPhysics.flv421 #+end_src423 #+results:424 : 180M physics-videos/helloPhysics.flv427 Thats a terribly large size!428 Let's compress it:430 ** COMMENT Compressing the HelloPhysics Video431 First, we'll scale the video, then, we'll decrease it's bitrate. The432 end result will be perfect for upload to YouTube.434 #+begin_src sh :results silent435 cd youtube437 gst-launch-0.10 \438 filesrc location=./helloPhysics.flv ! decodebin ! \439 videoscale ! ffmpegcolorspace ! \440 `: # the original size is 1280 by 960` \441 video/x-raw-yuv, width=1280, height=960, framerate=25/1 ! \442 videoscale ! \443 `: # here we scale the video down` \444 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \445 `: # and here we limit the bitrate` \446 theoraenc bitrate=1024 quality=30 ! \447 oggmux ! progressreport update-freq=1 ! \448 filesink location=./helloPhysics.ogg449 #+end_src451 #+begin_src sh :results verbatim452 du -h youtube/helloPhysics.ogg453 #+end_src455 #+results:456 : 13M youtube/helloPhysics.ogg458 [[http://www.youtube.com/watch?v=WIJt9aRGusc][helloPhysics.ogg]]460 #+begin_html461 <iframe width="425" height="349"462 src="http://www.youtube.com/embed/WIJt9aRGusc?hl=en&fs=1"463 frameborder="0" allowfullscreen>464 </iframe>465 #+end_html468 ** COMMENT failed attempts469 Let's try the [[http://diracvideo.org/][Dirac]] video encoder.471 #+begin_src sh :results verbatim472 cd youtube473 START=$(date +%s)474 gst-launch-0.10 \475 filesrc location=./helloPhysics.flv ! decodebin ! \476 videoscale ! ffmpegcolorspace ! \477 video/x-raw-yuv, width=1280, height=960, framerate=25/1 ! \478 schroenc ! filesink location=./helloPhysics.drc > /dev/null479 echo `expr $(( $(date +%s) - $START))`480 #+end_src483 #+results:484 : 142486 That took 142 seconds. Let's see how it does compression-wise:488 #+begin_src sh :results verbatim489 du -h ./youtube/helloPhysics.drc490 #+end_src492 #+results:493 : 22M ./physics-videos/helloPhysics.drc496 #+begin_src sh :results verbatim497 cd youtube498 START=$(date +%s)499 gst-launch-0.10 \500 filesrc location=./helloPhysics.flv ! decodebin ! \501 videoscale ! ffmpegcolorspace ! \502 video/x-raw-yuv, width=1280, height=960, framerate=25/1 ! \503 theoraenc ! oggmux ! filesink location=./helloPhysics.ogg \504 > /dev/null505 echo `expr $(( $(date +%s) - $START))`506 #+end_src508 #+results:509 : 123511 #+begin_src sh :results verbatim512 du -h youtube/helloPhysics.ogg513 #+end_src515 #+results:516 : 59M physics-videos/helloPhysics.ogg519 =*.drc= files can not be uploaded to YouTube, so I'll go for the520 avi file.523 ** COMMENT text for videos524 Video output from JMonkeyEngine3 (www.jmonkeyengine.org/) using Xuggle525 (www.xuggle.com/). Everything is explained at526 http://aurellem.org/cortex/capture-video.html.529 Video output from JMonkeyEngine3 (www.jmonkeyengine.org/) HelloPhysics530 demo application using Xuggle (www.xuggle.com/). Everything is531 explained at http://aurellem.org/cortex/capture-video.html. Here,532 four points of view are simultaneously recorded and then glued533 together later.535 JME3 Xuggle Aurellem video capture538 * Sample Videos539 I encoded most of the original JME3 Hello demos for your viewing540 pleasure, all using the =Capture= and =IsoTimer= classes.542 ** HelloTerrain543 [[http://youtu.be/5_4wyDFwrVQ][HelloTerrain.avi]]545 #+begin_html546 <iframe width="425" height="349"547 src="http://www.youtube.com/embed/5_4wyDFwrVQ"548 frameborder="0" allowfullscreen>549 </iframe>550 #+end_html552 ** HelloAssets553 [[http://www.youtube.com/watch?v=oGg-Q6k1BM4][HelloAssets.avi]]555 #+begin_html556 <iframe width="425" height="349"557 src="http://www.youtube.com/embed/oGg-Q6k1BM4?hl=en&fs=1"558 frameborder="0" allowfullscreen>559 </iframe>560 #+end_html562 ** HelloEffects563 [[http://www.youtube.com/watch?v=TuxlLMe53hA][HelloEffects]]565 #+begin_html566 <iframe width="425" height="349"567 src="http://www.youtube.com/embed/TuxlLMe53hA?hl=en&fs=1"568 frameborder="0" allowfullscreen>569 </iframe>570 #+end_html572 ** HelloCollision573 [[http://www.youtube.com/watch?v=GPlvJkiZfFw][HelloCollision.avi]]575 #+begin_html576 <iframe width="425" height="349"577 src="http://www.youtube.com/embed/GPlvJkiZfFw?hl=en&fs=1"578 frameborder="0" allowfullscreen>579 </iframe>580 #+end_html582 ** HelloAnimation583 [[http://www.youtube.com/watch?v=SDCfOSPYUkg][HelloAnimation.avi]]585 #+begin_html586 <iframe width="425" height="349"587 src="http://www.youtube.com/embed/SDCfOSPYUkg?hl=en&fs=1"588 frameborder="0" allowfullscreen>589 </iframe>590 #+end_html592 ** HelloNode593 [[http://www.youtube.com/watch?v=pL-0fR0-ilQ][HelloNode.avi]]595 #+begin_html596 <iframe width="425" height="349"597 src="http://www.youtube.com/embed/pL-0fR0-ilQ?hl=en&fs=1"598 frameborder="0" allowfullscreen>599 </iframe>600 #+end_html602 ** HelloLoop603 [[http://www.youtube.com/watch?v=mosZzzcdE5w][HelloLoop.avi]]605 #+begin_html606 <iframe width="425" height="349"607 src="http://www.youtube.com/embed/mosZzzcdE5w?hl=en&fs=1"608 frameborder="0" allowfullscreen>609 </iframe>610 #+end_html613 *** COMMENT x-form the other stupid614 progressreport update-freq=1616 gst-launch-0.10 \617 filesrc location=./helloPhy ! decodebin ! \618 videoscale ! ffmpegcolorspace ! \619 video/x-raw-yuv, width=1280, height=960, framerate=25/1 ! \620 x264enc ! avimux ! filesink location=helloPhysics.avi \623 gst-launch-0.10 \624 filesrc location=./HelloAnimationStatic.flv ! decodebin ! \625 videoscale ! ffmpegcolorspace ! \626 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \627 videobox border-alpha=0 left=-640 ! \628 videomixer name=mix ! ffmpegcolorspace ! videorate ! \629 video/x-raw-yuv, width=1280, height=480, framerate=25/1 ! \630 x264enc ! avimux ! progressreport update-freq=1 ! \631 filesink location=../youtube/HelloAnimation.avi \632 \633 filesrc location=./HelloAnimationMotion.flv ! decodebin ! \634 videoscale ! ffmpegcolorspace ! \635 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \636 videobox right=-640 ! mix.638 gst-launch-0.10 \639 filesrc location=./HelloCollisionMotion.flv ! decodebin ! \640 videoscale ! ffmpegcolorspace ! \641 video/x-raw-yuv, width=800, height=600, framerate=25/1 ! \642 x264enc bitrate=1024 ! avimux ! \643 filesink location=../youtube/HelloCollision.avi645 gst-launch-0.10 \646 filesrc location=./HelloEffectsStatic.flv ! decodebin ! \647 videoscale ! ffmpegcolorspace ! \648 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \649 videobox border-alpha=0 left=-640 ! \650 videomixer name=mix ! ffmpegcolorspace ! videorate ! \651 video/x-raw-yuv, width=1280, height=480, framerate=25/1 ! \652 x264enc bitrate=1024 ! avimux ! progressreport update-freq=1 ! \653 filesink location=../youtube/HelloEffects.avi \654 \655 filesrc location=./HelloEffectsMotion.flv ! decodebin ! \656 videoscale ! ffmpegcolorspace ! \657 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \658 videobox right=-640 ! mix.660 gst-launch-0.10 \661 filesrc location=./HelloTerrainMotion.flv ! decodebin ! \662 videoscale ! ffmpegcolorspace ! \663 video/x-raw-yuv, width=800, height=600, framerate=25/1 ! \664 x264enc bitrate=1024 ! avimux ! \665 filesink location=../youtube/HelloTerrain.avi668 gst-launch-0.10 \669 filesrc location=./HelloAssetsStatic.flv ! decodebin ! \670 videoscale ! ffmpegcolorspace ! \671 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \672 videobox border-alpha=0 left=-640 ! \673 videomixer name=mix ! ffmpegcolorspace ! videorate ! \674 video/x-raw-yuv, width=1280, height=480, framerate=25/1 ! \675 x264enc bitrate=1024 ! avimux ! progressreport update-freq=1 ! \676 filesink location=../youtube/HelloAssets.avi \677 \678 filesrc location=./HelloAssetsMotion.flv ! decodebin ! \679 videoscale ! ffmpegcolorspace ! \680 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \681 videobox right=-640 ! mix.684 gst-launch-0.10 \685 filesrc location=./HelloNodeStatic.flv ! decodebin ! \686 videoscale ! ffmpegcolorspace ! \687 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \688 videobox border-alpha=0 left=-640 ! \689 videomixer name=mix ! ffmpegcolorspace ! videorate ! \690 video/x-raw-yuv, width=1280, height=480, framerate=25/1 ! \691 x264enc bitrate=1024 ! avimux ! progressreport update-freq=1 ! \692 filesink location=../youtube/HelloNode.avi \693 \694 filesrc location=./HelloNodeMotion.flv ! decodebin ! \695 videoscale ! ffmpegcolorspace ! \696 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \697 videobox right=-640 ! mix.699 gst-launch-0.10 \700 filesrc location=./HelloLoopStatic.flv ! decodebin ! \701 videoscale ! ffmpegcolorspace ! \702 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \703 videobox border-alpha=0 left=-640 ! \704 videomixer name=mix ! ffmpegcolorspace ! videorate ! \705 video/x-raw-yuv, width=1280, height=480, framerate=25/1 ! \706 x264enc bitrate=1024 ! avimux ! progressreport update-freq=1 ! \707 filesink location=../youtube/HelloLoop.avi \708 \709 filesrc location=./HelloLoopMotion.flv ! decodebin ! \710 videoscale ! ffmpegcolorspace ! \711 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \712 videobox right=-640 ! mix.