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 JMonkeyEngine
2 #+author: Robert McIntyre
3 #+email: rlm@mit.edu
4 #+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 <:t
7 #+BABEL: :exports both :noweb yes :cache no :mkdirp yes
8 #+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, Java
10 #+INCLUDE: ../aurellem/src/templates/level-0.org
11 :PROPERTIES:
12 :EXPORT_FILE_NAME: ../whatever.html
13 :END:
16 * The Problem
17 So you've made your cool new JMonkeyEngine3 game and you want to
18 create a demo video to show off your hard work. Screen capturing is
19 the most straightforward way to do this, but it can slow down your
20 game and produce low-quality video as a result. A better way is to
21 record a video feed directly from the game while it is
22 running.
24 In this post, I'll explain how you can alter your JMonkeyEngine3 game
25 to output video while it is running. The main trick is to alter the
26 pace of JMonkeyEngine3's in-game time: we allow the engine as much
27 time as it needs to compute complicated in-game events and to encode
28 video frames. As a result, the game appears to speed up and slow down
29 as the computational demands shift, but the end result is perfectly
30 smooth video output at a constant framerate.
33 * Game-time vs. User-time vs. Video-time
35 A standard JME3 application that extends =SimpleApplication= or
36 =Application= tries as hard as it can to keep in sync with
37 /user-time/. If a ball is rolling at 1 game-mile per game-hour in the
38 game, and you wait for one user-hour as measured by the clock on your
39 wall, then the ball should have traveled exactly one game-mile. In
40 order to keep sync with the real world, the game throttles its physics
41 engine and graphics display. If the computations involved in running
42 the game are too intense, then the game will first skip frames, then
43 sacrifice physics accuracy. If there are particuraly demanding
44 computations, then you may only get 1 fps, and the ball may tunnel
45 through the floor or obstacles due to inaccurate physics simulation,
46 but after the end of one user-hour, that ball will have traveled one
47 game-mile.
49 When we're recording video, we don't care if the game-time syncs with
50 user-time, but instead whether the time in the recorded video
51 (video-time) syncs with user-time. To continue the analogy, if we
52 recorded the ball rolling at 1 game-mile per game-hour and watched the
53 video later, we would want to see 30 fps video of the ball rolling at
54 1 video-mile per /user-hour/. It doesn't matter how much user-time it
55 took to simulate that hour of game-time to make the high-quality
56 recording.
58 * COMMENT Two examples to clarify the point:
59 ** Recording from a Simple Simulation
61 *** Without a Special Timer
62 You have a simulation of a ball rolling on an infinite empty plane at
63 one game-mile per game-hour, and a really good computer. Normally,
64 JME3 will throttle the physics engine and graphics display to sync the
65 game-time with user-time. If it takes one-thousandth of a second
66 user-time to simulate one-sixtieth of a second game time and another
67 one-thousandth of a second to draw to the screen, then JME3 will just
68 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 that
71 passes, one second of game-time passes, and the game will run at 60
72 frames per user-second.
75 *** With a Special Timer
76 Then, you change the game's timer so that user-time will be synced to
77 video-time. Assume that encoding a single frame takes 0 seconds
78 user-time to complete.
80 Now, JME3 takes advantage of all available resources. It still takes
81 one-thousandth of a second to calculate a physics tick, and another
82 one-thousandth to render to the screen. Then it takes 0 seconds to
83 write the video frame to disk and encode the video. In only one second
84 of user time, JME3 will complete 500 physics-tick/render/encode-video
85 cycles, and $\frac{500}{60}=8\frac{1}{3}$ seconds of game-time will
86 have passed. Game-time appears to dilate $8\frac{1}{3}\times$ with
87 respect to user-time, and in only 7.2 minutes user-time, one hour of
88 video will have been recorded. The game itself will run at 500 fps.
89 When someone watches the video, they will see 60 frames per
90 user-second, and $\frac{1}{60}$ video-seconds will pass each frame. It
91 will take exactly one hour user-time (and one hour video-time) for the
92 ball in the video to travel one video-mile.
94 ** Recording from a Complex Simulation
96 *** Without a Special Timer
97 You have a simulation of a ball rolling on an infinite empty plane at
98 one game-mile per game-hour accompanied by multiple explosions
99 involving thousands of nodes, particle effects, and complicated shadow
100 shaders to create realistic shadows. You also have a slow
101 laptop. Normally, JME3 must sacrifice rendering and physics simulation
102 to try to keep up. If it takes $\frac{1}{120}$ of a user-second to
103 calculate $\frac{1}{60}$ game-seconds, and an additional
104 $\frac{1}{60}$ of a user-second to render to screen, then JME3 has
105 it's work cut out for it. In order to render to the screen, it will
106 first step the game forward by up to four physics ticks before
107 rendering to the screen. If it still isn't fast enough then it will
108 decrease the accuracy of the physics engine until game-time and user
109 time are synched or a certain threshold is reached, at which point the
110 game visibly slows down. In this case, JME3 continuously repeat a
111 cycle of two physics ticks, and one screen render. For every
112 user-second that passes, one game-second will pass, but the game will
113 run at 30 fps instead of 60 fps like before.
115 *** With a Special Timer
116 Then, you change the game's timer so that user-time will be synced to
117 video-time. Once again, assume video encoding takes $\frac{1}{60}$ of
118 a user-second.
120 Now, JME3 will spend $\frac{1}{120}$ of a user-second to step the
121 physics tick $\frac{1}{60}$ game-seconds, $\frac{1}{60}$ to draw to
122 the screen, and an additional $\frac{1}{60}$ to encode the video and
123 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 take
125 $(\frac{60}{24} = 2.5)$ user-hours to record one game-hour and game-time
126 will appear to flow two-fifths as fast as user time while the game is
127 running. However, just as in example one, when all is said and done we
128 will have an hour long video at 60 fps.
131 * COMMENT proposed names for the new timer
132 # METRONOME
133 # IsoTimer
134 # EvenTimer
135 # PulseTimer
136 # FixedTimer
137 # RigidTimer
138 # FixedTempo
139 # RegularTimer
140 # MetronomeTimer
141 # ConstantTimer
142 # SteadyTimer
145 * =IsoTimer= records time like a metronome
147 The easiest way to achieve this special timing is to create a new
148 timer that always reports the same framerate to JME3 every time it is
149 called.
152 =./jme3/src/core/com/jme3/system/IsoTimer.java=
153 #+include ./jme3/src/core/com/jme3/system/IsoTimer.java src java
155 If an Application uses this =IsoTimer= instead of the normal one, we
156 can be sure that every call to =simpleUpdate=, for example, corresponds
157 to exactly $(\frac{1}{fps})$ seconds of game-time.
159 In order to facilitate setting the =Timer= in user code, I added
160 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 Video
167 Now that the issue of time is solved, we just need a function that
168 writes each frame to a video. We can put this function somewhere
169 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 therein
173 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]]. It
176 takes care of everything related to video encoding and decoding and
177 runs on Windows, Linux and Mac. Out of all the video frameworks for
178 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 a
181 video file.
183 =./jme3/src/core/com/jme3/app/VideoProcessor.java=
184 #+include ./jme3/src/core/com/jme3/app/VideoProcessor.java src java
186 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=]] and
191 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 java
196 The videos are created in the =hello-video= directory
198 #+begin_src sh :results verbatim
199 du -h hello-video/*
200 #+end_src
202 #+results:
203 : 932K hello-video/hello-video-moving.flv
204 : 640K hello-video/hello-video-static.flv
206 And can be immediately uploaded to youtube
208 - [[http://www.youtube.com/watch?v=C8gxVAySaPg][hello-video-moving.flv]]
209 #+BEGIN_HTML
210 <iframe width="425" height="349"
211 src="http://www.youtube.com/embed/C8gxVAySaPg"
212 frameborder="0" allowfullscreen>
213 </iframe>
214 #+END_HTML
215 - [[http://www.youtube.com/watch?v=pHcFOtIS07Q][hello-video-static.flv]]
216 #+BEGIN_HTML
217 <iframe width="425" height="349"
218 src="http://www.youtube.com/embed/pHcFOtIS07Q"
219 frameborder="0" allowfullscreen>
220 </iframe>
222 #+END_HTML
226 * Summary
227 It's quite easy to augment your own application to record video,
228 almost regardless of how complicated the actual application is. You
229 can also record from multiple ViewPorts as the above example shows.
231 The process for adding video recording to your application is as
232 follows:
234 Assuming you want to record at 30 fps, add:
236 #+begin_src java :exports code
237 this.setTimer(new IsoTimer(30));
238 #+end_src
240 Somewhere in the initialization of your Application. Right now, you
241 will have to add the =setTimer= method to =Application=, but hopefully
242 this method will be included soon by the JMonkeyEngine3 team.
244 Then, you create a =VideoProcessor= object and attach it to the
245 =ViewPort= from which you want to record.
247 If you want to record from the game's main =ViewPort= to a file called
248 =/home/r/record.flv=, then add:
250 #+begin_src java :exports code
251 viewPort.addProcessor(new VideoProcessor(new File("/home/r/record.flv")));
252 #+end_src
254 Do this for each =ViewPort= from which you want to record. The more
255 ViewPorts from which you record, the slower the game will run, but
256 this slowness will not affect the final video output.
258 * More Examples
259 ** Hello Physics
260 =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 four
264 simultaneous views of the same scene of cannonballs careening into a
265 brick wall.
267 =./jme3/src/test/jme3test/helloworld/HelloPhysicsWithVideo.java=
268 #+include ./jme3/src/test/jme3test/helloworld/HelloPhysicsWithVideo.java src java
270 Running the program outputs four videos into the =./physics-videos=
271 directory.
273 #+begin_src sh :exports both :results verbatim
274 ls ./physics-videos | grep -
275 #+end_src
277 #+results:
278 : lower-left.flv
279 : lower-right.flv
280 : upper-left.flv
281 : upper-right.flv
283 The videos are fused together with the following =gstreamer= commands:
285 #+begin_src sh :results silent
286 cd physics-videos
288 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_src
303 #+begin_src sh :results silent
304 cd physics-videos
306 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_src
321 #+begin_src sh :results silent
322 cd physics-videos
324 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_src
339 #+begin_src sh :results verbatim
340 du -h youtube/helloPhysics.flv
341 #+end_src
343 #+results:
344 : 180M physics-videos/helloPhysics.flv
347 Thats a terribly large size!
348 Let's compress it:
350 ** Compressing the HelloPhysics Video
351 First, we'll scale the video, then, we'll decrease it's bitrate. The
352 end result will be perfect for upload to YouTube.
354 #+begin_src sh :results silent
355 cd youtube
357 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.ogg
369 #+end_src
371 #+begin_src sh :results verbatim
372 du -h youtube/helloPhysics.ogg
373 #+end_src
375 #+results:
376 : 13M youtube/helloPhysics.ogg
378 [[http://www.youtube.com/watch?v=WIJt9aRGusc][helloPhysics.ogg]]
380 #+begin_html
381 <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_html
388 ** COMMENT failed attempts
389 Let's try the [[http://diracvideo.org/][Dirac]] video encoder.
391 #+begin_src sh :results verbatim
392 cd youtube
393 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/null
399 echo `expr $(( $(date +%s) - $START))`
400 #+end_src
403 #+results:
404 : 142
406 That took 142 seconds. Let's see how it does compression-wise:
408 #+begin_src sh :results verbatim
409 du -h ./youtube/helloPhysics.drc
410 #+end_src
412 #+results:
413 : 22M ./physics-videos/helloPhysics.drc
416 #+begin_src sh :results verbatim
417 cd youtube
418 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/null
425 echo `expr $(( $(date +%s) - $START))`
426 #+end_src
428 #+results:
429 : 123
431 #+begin_src sh :results verbatim
432 du -h youtube/helloPhysics.ogg
433 #+end_src
435 #+results:
436 : 59M physics-videos/helloPhysics.ogg
439 =*.drc= files can not be uploaded to YouTube, so I'll go for the
440 avi file.
443 ** COMMENT text for videos
444 Video output from JMonkeyEngine3 (www.jmonkeyengine.org/) using Xuggle
445 (www.xuggle.com/). Everything is explained at
446 http://aurellem.org/cortex/capture-video.html.
449 Video output from JMonkeyEngine3 (www.jmonkeyengine.org/) HelloPhysics
450 demo application using Xuggle (www.xuggle.com/). Everything is
451 explained at http://aurellem.org/cortex/capture-video.html. Here,
452 four points of view are simultaneously recorded and then glued
453 together later.
455 JME3 Xuggle Aurellem video capture
458 * Sample Videos
459 I encoded most of the original JME3 Hello demos for your viewing
460 pleasure, all using the =VideoProcessor= and =IsoTimer= classes.
462 ** HelloTerrain
463 [[http://youtu.be/5_4wyDFwrVQ][HelloTerrain.avi]]
465 #+begin_html
466 <iframe width="425" height="349"
467 src="http://www.youtube.com/embed/5_4wyDFwrVQ"
468 frameborder="0" allowfullscreen>
469 </iframe>
470 #+end_html
472 ** HelloAssets
473 [[http://www.youtube.com/watch?v=oGg-Q6k1BM4][HelloAssets.avi]]
475 #+begin_html
476 <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_html
482 ** HelloEffects
483 [[http://www.youtube.com/watch?v=TuxlLMe53hA][HelloEffects]]
485 #+begin_html
486 <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_html
492 ** HelloCollision
493 [[http://www.youtube.com/watch?v=GPlvJkiZfFw][HelloCollision.avi]]
495 #+begin_html
496 <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_html
502 ** HelloAnimation
503 [[http://www.youtube.com/watch?v=SDCfOSPYUkg][HelloAnimation.avi]]
505 #+begin_html
506 <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_html
512 ** HelloNode
513 [[http://www.youtube.com/watch?v=pL-0fR0-ilQ][HelloNode.avi]]
515 #+begin_html
516 <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_html
522 ** HelloLoop
523 [[http://www.youtube.com/watch?v=mosZzzcdE5w][HelloLoop.avi]]
525 #+begin_html
526 <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_html
533 *** COMMENT x-form the other stupid
534 progressreport update-freq=1
536 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.avi
565 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.avi
588 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.