Mercurial > cortex
view org/capture-video.org @ 2:46b2d4c9522d
moved common options from hgignore to a central file
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sun, 16 Oct 2011 06:49:58 -0700 |
parents | 92f8d83b5d0b |
children | 50c92af2018e |
line wrap: on
line source
1 #+title: Capture Live Video Feeds from JMonkeyEngine2 #+author: Robert McIntyre3 #+email: rlm@mit.edu4 #+MATHJAX: align:"left" mathml:t path:"../aurellem/src/MathJax/MathJax.js"5 #+STYLE: <link rel="stylesheet" type="text/css" href="../aurellem/src/css/aurellem.css"/>6 #+OPTIONS: H:3 num:t toc:t \n:nil @:t ::t |:t ^:t -:t f:t *:t <:t7 #+BABEL: :exports both :noweb yes :cache no :mkdirp yes8 #+description: Capture video from a JMonkeyEngine3 Application with Xuggle, and use gstreamer to compress the video to upload to YouTube.9 #+keywords: JME3, video, Xuggle, JMonkeyEngine, youtube, capture video, Java10 #+INCLUDE: ../aurellem/src/templates/level-0.org11 :PROPERTIES:12 :EXPORT_FILE_NAME: ../whatever.html13 :END:16 * The Problem17 So you've made your cool new JMonkeyEngine3 game and you want to18 create a demo video to show off your hard work. Screen capturing is19 the most straightforward way to do this, but it can slow down your20 game and produce low-quality video as a result. A better way is to21 record a video feed directly from the game while it is22 running.24 In this post, I'll explain how you can alter your JMonkeyEngine3 game25 to output video while it is running. The main trick is to alter the26 pace of JMonkeyEngine3's in-game time: we allow the engine as much27 time as it needs to compute complicated in-game events and to encode28 video frames. As a result, the game appears to speed up and slow down29 as the computational demands shift, but the end result is perfectly30 smooth video output at a constant framerate.33 * Game-time vs. User-time vs. Video-time35 A standard JME3 application that extends =SimpleApplication= or36 =Application= tries as hard as it can to keep in sync with37 /user-time/. If a ball is rolling at 1 game-mile per game-hour in the38 game, and you wait for one user-hour as measured by the clock on your39 wall, then the ball should have traveled exactly one game-mile. In40 order to keep sync with the real world, the game throttles its physics41 engine and graphics display. If the computations involved in running42 the game are too intense, then the game will first skip frames, then43 sacrifice physics accuracy. If there are particuraly demanding44 computations, then you may only get 1 fps, and the ball may tunnel45 through the floor or obstacles due to inaccurate physics simulation,46 but after the end of one user-hour, that ball will have traveled one47 game-mile.49 When we're recording video, we don't care if the game-time syncs with50 user-time, but instead whether the time in the recorded video51 (video-time) syncs with user-time. To continue the analogy, if we52 recorded the ball rolling at 1 game-mile per game-hour and watched the53 video later, we would want to see 30 fps video of the ball rolling at54 1 video-mile per /user-hour/. It doesn't matter how much user-time it55 took to simulate that hour of game-time to make the high-quality56 recording.58 * COMMENT Two examples to clarify the point:59 ** Recording from a Simple Simulation61 *** Without a Special Timer62 You have a simulation of a ball rolling on an infinite empty plane at63 one game-mile per game-hour, and a really good computer. Normally,64 JME3 will throttle the physics engine and graphics display to sync the65 game-time with user-time. If it takes one-thousandth of a second66 user-time to simulate one-sixtieth of a second game time and another67 one-thousandth of a second to draw to the screen, then JME3 will just68 sit around for the remainder of $\frac{1}{60} - \frac{2}{1000}$69 user-seconds, then calculate the next frame in $\frac{2}{1000}$70 user-seconds, then wait, and so on. For every second of user time that71 passes, one second of game-time passes, and the game will run at 6072 frames per user-second.75 *** With a Special Timer76 Then, you change the game's timer so that user-time will be synced to77 video-time. Assume that encoding a single frame takes 0 seconds78 user-time to complete.80 Now, JME3 takes advantage of all available resources. It still takes81 one-thousandth of a second to calculate a physics tick, and another82 one-thousandth to render to the screen. Then it takes 0 seconds to83 write the video frame to disk and encode the video. In only one second84 of user time, JME3 will complete 500 physics-tick/render/encode-video85 cycles, and $\frac{500}{60}=8\frac{1}{3}$ seconds of game-time will86 have passed. Game-time appears to dilate $8\frac{1}{3}\times$ with87 respect to user-time, and in only 7.2 minutes user-time, one hour of88 video will have been recorded. The game itself will run at 500 fps.89 When someone watches the video, they will see 60 frames per90 user-second, and $\frac{1}{60}$ video-seconds will pass each frame. It91 will take exactly one hour user-time (and one hour video-time) for the92 ball in the video to travel one video-mile.94 ** Recording from a Complex Simulation96 *** Without a Special Timer97 You have a simulation of a ball rolling on an infinite empty plane at98 one game-mile per game-hour accompanied by multiple explosions99 involving thousands of nodes, particle effects, and complicated shadow100 shaders to create realistic shadows. You also have a slow101 laptop. Normally, JME3 must sacrifice rendering and physics simulation102 to try to keep up. If it takes $\frac{1}{120}$ of a user-second to103 calculate $\frac{1}{60}$ game-seconds, and an additional104 $\frac{1}{60}$ of a user-second to render to screen, then JME3 has105 it's work cut out for it. In order to render to the screen, it will106 first step the game forward by up to four physics ticks before107 rendering to the screen. If it still isn't fast enough then it will108 decrease the accuracy of the physics engine until game-time and user109 time are synched or a certain threshold is reached, at which point the110 game visibly slows down. In this case, JME3 continuously repeat a111 cycle of two physics ticks, and one screen render. For every112 user-second that passes, one game-second will pass, but the game will113 run at 30 fps instead of 60 fps like before.115 *** With a Special Timer116 Then, you change the game's timer so that user-time will be synced to117 video-time. Once again, assume video encoding takes $\frac{1}{60}$ of118 a user-second.120 Now, JME3 will spend $\frac{1}{120}$ of a user-second to step the121 physics tick $\frac{1}{60}$ game-seconds, $\frac{1}{60}$ to draw to122 the screen, and an additional $\frac{1}{60}$ to encode the video and123 write the frame to disk. This is a total of $\frac{1}{24}$124 user-seconds for each $\frac{1}{60}$ game-seconds. It will take125 $(\frac{60}{24} = 2.5)$ user-hours to record one game-hour and game-time126 will appear to flow two-fifths as fast as user time while the game is127 running. However, just as in example one, when all is said and done we128 will have an hour long video at 60 fps.131 * COMMENT proposed names for the new timer132 # METRONOME133 # IsoTimer134 # EvenTimer135 # PulseTimer136 # FixedTimer137 # RigidTimer138 # FixedTempo139 # RegularTimer140 # MetronomeTimer141 # ConstantTimer142 # SteadyTimer145 * =IsoTimer= records time like a metronome147 The easiest way to achieve this special timing is to create a new148 timer that always reports the same framerate to JME3 every time it is149 called.152 =./jme3/src/core/com/jme3/system/IsoTimer.java=153 #+include ./jme3/src/core/com/jme3/system/IsoTimer.java src java155 If an Application uses this =IsoTimer= instead of the normal one, we156 can be sure that every call to =simpleUpdate=, for example, corresponds157 to exactly $(\frac{1}{fps})$ seconds of game-time.159 In order to facilitate setting the =Timer= in user code, I added160 getter and setter methods to =Application.java=.162 In =./jme3/src/core/com/jme3/app/Application.java= I added:163 #+include ./jme3/src/core/com/jme3/app/Application.java src java :lines "340-356"165 * Encoding to Video167 Now that the issue of time is solved, we just need a function that168 writes each frame to a video. We can put this function somewhere169 where it will be called exactly one per frame.171 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 If you want to generate video from Java, a great option is [[http://www.xuggle.com/][Xuggle]]. It176 takes care of everything related to video encoding and decoding and177 runs on Windows, Linux and Mac. Out of all the video frameworks for178 Java I personally like this one the best.180 Here is a =SceneProcessor= that uses [[http://www.xuggle.com/][Xuggle]] to write each frame to a181 video file.183 =./jme3/src/core/com/jme3/app/VideoProcessor.java=184 #+include ./jme3/src/core/com/jme3/app/VideoProcessor.java src java186 With this, we are able to record video!188 * Hello Video!190 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=]] and191 augmented it with video output as follows:193 =./jme3/src/test/jme3test/helloworld/HelloVideo.java=194 #+include ./jme3/src/test/jme3test/helloworld/HelloVideo.java src java196 The videos are created in the =hello-video= directory198 #+begin_src sh :results verbatim199 du -h hello-video/*200 #+end_src202 #+results:203 : 932K hello-video/hello-video-moving.flv204 : 640K hello-video/hello-video-static.flv206 And can be immediately uploaded to youtube208 - [[http://www.youtube.com/watch?v=C8gxVAySaPg][hello-video-moving.flv]]209 #+BEGIN_HTML210 <iframe width="425" height="349"211 src="http://www.youtube.com/embed/C8gxVAySaPg"212 frameborder="0" allowfullscreen>213 </iframe>214 #+END_HTML215 - [[http://www.youtube.com/watch?v=pHcFOtIS07Q][hello-video-static.flv]]216 #+BEGIN_HTML217 <iframe width="425" height="349"218 src="http://www.youtube.com/embed/pHcFOtIS07Q"219 frameborder="0" allowfullscreen>220 </iframe>222 #+END_HTML226 * Summary227 It's quite easy to augment your own application to record video,228 almost regardless of how complicated the actual application is. You229 can also record from multiple ViewPorts as the above example shows.231 The process for adding video recording to your application is as232 follows:234 Assuming you want to record at 30 fps, add:236 #+begin_src java :exports code237 this.setTimer(new IsoTimer(30));238 #+end_src240 Somewhere in the initialization of your Application. Right now, you241 will have to add the =setTimer= method to =Application=, but hopefully242 this method will be included soon by the JMonkeyEngine3 team.244 Then, you create a =VideoProcessor= object and attach it to the245 =ViewPort= from which you want to record.247 If you want to record from the game's main =ViewPort= to a file called248 =/home/r/record.flv=, then add:250 #+begin_src java :exports code251 viewPort.addProcessor(new VideoProcessor(new File("/home/r/record.flv")));252 #+end_src254 Do this for each =ViewPort= from which you want to record. The more255 ViewPorts from which you record, the slower the game will run, but256 this slowness will not affect the final video output.258 * More Examples259 ** Hello Physics260 =HelloVideo= is boring. Let's add some video capturing to =HelloPhysics=261 and create something fun!263 This example is a modified version of =HelloPhysics= that creates four264 simultaneous views of the same scene of cannonballs careening into a265 brick wall.267 =./jme3/src/test/jme3test/helloworld/HelloPhysicsWithVideo.java=268 #+include ./jme3/src/test/jme3test/helloworld/HelloPhysicsWithVideo.java src java270 Running the program outputs four videos into the =./physics-videos=271 directory.273 #+begin_src sh :exports both :results verbatim274 ls ./physics-videos | grep -275 #+end_src277 #+results:278 : lower-left.flv279 : lower-right.flv280 : upper-left.flv281 : upper-right.flv283 The videos are fused together with the following =gstreamer= commands:285 #+begin_src sh :results silent286 cd physics-videos288 gst-launch-0.10 \289 filesrc location=./upper-right.flv ! decodebin ! \290 videoscale ! ffmpegcolorspace ! \291 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \292 videobox border-alpha=0 left=-640 ! \293 videomixer name=mix ! ffmpegcolorspace ! videorate ! \294 video/x-raw-yuv, width=1280, height=480, framerate=25/1 ! \295 jpegenc ! avimux ! filesink location=upper.flv \296 \297 filesrc location=./upper-left.flv ! decodebin ! \298 videoscale ! ffmpegcolorspace ! \299 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \300 videobox right=-640 ! mix.301 #+end_src303 #+begin_src sh :results silent304 cd physics-videos306 gst-launch-0.10 \307 filesrc location=./lower-left.flv ! decodebin ! \308 videoscale ! ffmpegcolorspace ! \309 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \310 videobox border-alpha=0 left=-640 ! \311 videomixer name=mix ! ffmpegcolorspace ! videorate ! \312 video/x-raw-yuv, width=1280, height=480, framerate=25/1 ! \313 jpegenc ! avimux ! filesink location=lower.flv \314 \315 filesrc location=./lower-right.flv ! decodebin ! \316 videoscale ! ffmpegcolorspace ! \317 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \318 videobox right=-640 ! mix.319 #+end_src321 #+begin_src sh :results silent322 cd physics-videos324 gst-launch-0.10 \325 filesrc location=./upper.flv ! decodebin ! \326 videoscale ! ffmpegcolorspace ! \327 video/x-raw-yuv, width=1280, height=480, framerate=25/1 ! \328 videobox border-alpha=0 bottom=-480 ! \329 videomixer name=mix ! ffmpegcolorspace ! videorate ! \330 video/x-raw-yuv, width=1280, height=960, framerate=25/1 ! \331 jpegenc ! avimux ! filesink location=../youtube/helloPhysics.flv \332 \333 filesrc location=./lower.flv ! decodebin ! \334 videoscale ! ffmpegcolorspace ! \335 video/x-raw-yuv, width=1280, height=480, framerate=25/1 ! \336 videobox top=-480 ! mix.337 #+end_src339 #+begin_src sh :results verbatim340 du -h youtube/helloPhysics.flv341 #+end_src343 #+results:344 : 180M physics-videos/helloPhysics.flv347 Thats a terribly large size!348 Let's compress it:350 ** Compressing the HelloPhysics Video351 First, we'll scale the video, then, we'll decrease it's bitrate. The352 end result will be perfect for upload to YouTube.354 #+begin_src sh :results silent355 cd youtube357 gst-launch-0.10 \358 filesrc location=./helloPhysics.flv ! decodebin ! \359 videoscale ! ffmpegcolorspace ! \360 `: # the original size is 1280 by 960` \361 video/x-raw-yuv, width=1280, height=960, framerate=25/1 ! \362 videoscale ! \363 `: # here we scale the video down` \364 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \365 `: # and here we limit the bitrate` \366 theoraenc bitrate=1024 quality=30 ! \367 oggmux ! progressreport update-freq=1 ! \368 filesink location=./helloPhysics.ogg369 #+end_src371 #+begin_src sh :results verbatim372 du -h youtube/helloPhysics.ogg373 #+end_src375 #+results:376 : 13M youtube/helloPhysics.ogg378 [[http://www.youtube.com/watch?v=WIJt9aRGusc][helloPhysics.ogg]]380 #+begin_html381 <iframe width="425" height="349"382 src="http://www.youtube.com/embed/WIJt9aRGusc?hl=en&fs=1"383 frameborder="0" allowfullscreen>384 </iframe>385 #+end_html388 ** COMMENT failed attempts389 Let's try the [[http://diracvideo.org/][Dirac]] video encoder.391 #+begin_src sh :results verbatim392 cd youtube393 START=$(date +%s)394 gst-launch-0.10 \395 filesrc location=./helloPhysics.flv ! decodebin ! \396 videoscale ! ffmpegcolorspace ! \397 video/x-raw-yuv, width=1280, height=960, framerate=25/1 ! \398 schroenc ! filesink location=./helloPhysics.drc > /dev/null399 echo `expr $(( $(date +%s) - $START))`400 #+end_src403 #+results:404 : 142406 That took 142 seconds. Let's see how it does compression-wise:408 #+begin_src sh :results verbatim409 du -h ./youtube/helloPhysics.drc410 #+end_src412 #+results:413 : 22M ./physics-videos/helloPhysics.drc416 #+begin_src sh :results verbatim417 cd youtube418 START=$(date +%s)419 gst-launch-0.10 \420 filesrc location=./helloPhysics.flv ! decodebin ! \421 videoscale ! ffmpegcolorspace ! \422 video/x-raw-yuv, width=1280, height=960, framerate=25/1 ! \423 theoraenc ! oggmux ! filesink location=./helloPhysics.ogg \424 > /dev/null425 echo `expr $(( $(date +%s) - $START))`426 #+end_src428 #+results:429 : 123431 #+begin_src sh :results verbatim432 du -h youtube/helloPhysics.ogg433 #+end_src435 #+results:436 : 59M physics-videos/helloPhysics.ogg439 =*.drc= files can not be uploaded to YouTube, so I'll go for the440 avi file.443 ** COMMENT text for videos444 Video output from JMonkeyEngine3 (www.jmonkeyengine.org/) using Xuggle445 (www.xuggle.com/). Everything is explained at446 http://aurellem.org/cortex/capture-video.html.449 Video output from JMonkeyEngine3 (www.jmonkeyengine.org/) HelloPhysics450 demo application using Xuggle (www.xuggle.com/). Everything is451 explained at http://aurellem.org/cortex/capture-video.html. Here,452 four points of view are simultaneously recorded and then glued453 together later.455 JME3 Xuggle Aurellem video capture458 * Sample Videos459 I encoded most of the original JME3 Hello demos for your viewing460 pleasure, all using the =VideoProcessor= and =IsoTimer= classes.462 ** HelloTerrain463 [[http://youtu.be/5_4wyDFwrVQ][HelloTerrain.avi]]465 #+begin_html466 <iframe width="425" height="349"467 src="http://www.youtube.com/embed/5_4wyDFwrVQ"468 frameborder="0" allowfullscreen>469 </iframe>470 #+end_html472 ** HelloAssets473 [[http://www.youtube.com/watch?v=oGg-Q6k1BM4][HelloAssets.avi]]475 #+begin_html476 <iframe width="425" height="349"477 src="http://www.youtube.com/embed/oGg-Q6k1BM4?hl=en&fs=1"478 frameborder="0" allowfullscreen>479 </iframe>480 #+end_html482 ** HelloEffects483 [[http://www.youtube.com/watch?v=TuxlLMe53hA][HelloEffects]]485 #+begin_html486 <iframe width="425" height="349"487 src="http://www.youtube.com/embed/TuxlLMe53hA?hl=en&fs=1"488 frameborder="0" allowfullscreen>489 </iframe>490 #+end_html492 ** HelloCollision493 [[http://www.youtube.com/watch?v=GPlvJkiZfFw][HelloCollision.avi]]495 #+begin_html496 <iframe width="425" height="349"497 src="http://www.youtube.com/embed/GPlvJkiZfFw?hl=en&fs=1"498 frameborder="0" allowfullscreen>499 </iframe>500 #+end_html502 ** HelloAnimation503 [[http://www.youtube.com/watch?v=SDCfOSPYUkg][HelloAnimation.avi]]505 #+begin_html506 <iframe width="425" height="349"507 src="http://www.youtube.com/embed/SDCfOSPYUkg?hl=en&fs=1"508 frameborder="0" allowfullscreen>509 </iframe>510 #+end_html512 ** HelloNode513 [[http://www.youtube.com/watch?v=pL-0fR0-ilQ][HelloNode.avi]]515 #+begin_html516 <iframe width="425" height="349"517 src="http://www.youtube.com/embed/pL-0fR0-ilQ?hl=en&fs=1"518 frameborder="0" allowfullscreen>519 </iframe>520 #+end_html522 ** HelloLoop523 [[http://www.youtube.com/watch?v=mosZzzcdE5w][HelloLoop.avi]]525 #+begin_html526 <iframe width="425" height="349"527 src="http://www.youtube.com/embed/mosZzzcdE5w?hl=en&fs=1"528 frameborder="0" allowfullscreen>529 </iframe>530 #+end_html533 *** COMMENT x-form the other stupid534 progressreport update-freq=1536 gst-launch-0.10 \537 filesrc location=./helloPhy ! decodebin ! \538 videoscale ! ffmpegcolorspace ! \539 video/x-raw-yuv, width=1280, height=960, framerate=25/1 ! \540 x264enc ! avimux ! filesink location=helloPhysics.avi \543 gst-launch-0.10 \544 filesrc location=./HelloAnimationStatic.flv ! decodebin ! \545 videoscale ! ffmpegcolorspace ! \546 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \547 videobox border-alpha=0 left=-640 ! \548 videomixer name=mix ! ffmpegcolorspace ! videorate ! \549 video/x-raw-yuv, width=1280, height=480, framerate=25/1 ! \550 x264enc ! avimux ! progressreport update-freq=1 ! \551 filesink location=../youtube/HelloAnimation.avi \552 \553 filesrc location=./HelloAnimationMotion.flv ! decodebin ! \554 videoscale ! ffmpegcolorspace ! \555 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \556 videobox right=-640 ! mix.558 gst-launch-0.10 \559 filesrc location=./HelloCollisionMotion.flv ! decodebin ! \560 videoscale ! ffmpegcolorspace ! \561 video/x-raw-yuv, width=800, height=600, framerate=25/1 ! \562 x264enc bitrate=1024 ! avimux ! \563 filesink location=../youtube/HelloCollision.avi565 gst-launch-0.10 \566 filesrc location=./HelloEffectsStatic.flv ! decodebin ! \567 videoscale ! ffmpegcolorspace ! \568 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \569 videobox border-alpha=0 left=-640 ! \570 videomixer name=mix ! ffmpegcolorspace ! videorate ! \571 video/x-raw-yuv, width=1280, height=480, framerate=25/1 ! \572 x264enc bitrate=1024 ! avimux ! progressreport update-freq=1 ! \573 filesink location=../youtube/HelloEffects.avi \574 \575 filesrc location=./HelloEffectsMotion.flv ! decodebin ! \576 videoscale ! ffmpegcolorspace ! \577 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \578 videobox right=-640 ! mix.580 gst-launch-0.10 \581 filesrc location=./HelloTerrainMotion.flv ! decodebin ! \582 videoscale ! ffmpegcolorspace ! \583 video/x-raw-yuv, width=800, height=600, framerate=25/1 ! \584 x264enc bitrate=1024 ! avimux ! \585 filesink location=../youtube/HelloTerrain.avi588 gst-launch-0.10 \589 filesrc location=./HelloAssetsStatic.flv ! decodebin ! \590 videoscale ! ffmpegcolorspace ! \591 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \592 videobox border-alpha=0 left=-640 ! \593 videomixer name=mix ! ffmpegcolorspace ! videorate ! \594 video/x-raw-yuv, width=1280, height=480, framerate=25/1 ! \595 x264enc bitrate=1024 ! avimux ! progressreport update-freq=1 ! \596 filesink location=../youtube/HelloAssets.avi \597 \598 filesrc location=./HelloAssetsMotion.flv ! decodebin ! \599 videoscale ! ffmpegcolorspace ! \600 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \601 videobox right=-640 ! mix.604 gst-launch-0.10 \605 filesrc location=./HelloNodeStatic.flv ! decodebin ! \606 videoscale ! ffmpegcolorspace ! \607 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \608 videobox border-alpha=0 left=-640 ! \609 videomixer name=mix ! ffmpegcolorspace ! videorate ! \610 video/x-raw-yuv, width=1280, height=480, framerate=25/1 ! \611 x264enc bitrate=1024 ! avimux ! progressreport update-freq=1 ! \612 filesink location=../youtube/HelloNode.avi \613 \614 filesrc location=./HelloNodeMotion.flv ! decodebin ! \615 videoscale ! ffmpegcolorspace ! \616 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \617 videobox right=-640 ! mix.619 gst-launch-0.10 \620 filesrc location=./HelloLoopStatic.flv ! decodebin ! \621 videoscale ! ffmpegcolorspace ! \622 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \623 videobox border-alpha=0 left=-640 ! \624 videomixer name=mix ! ffmpegcolorspace ! videorate ! \625 video/x-raw-yuv, width=1280, height=480, framerate=25/1 ! \626 x264enc bitrate=1024 ! avimux ! progressreport update-freq=1 ! \627 filesink location=../youtube/HelloLoop.avi \628 \629 filesrc location=./HelloLoopMotion.flv ! decodebin ! \630 videoscale ! ffmpegcolorspace ! \631 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \632 videobox right=-640 ! mix.