view org/capture-video.org @ 4:50c92af2018e

fixed headers, make compat symlink
author Robert McIntyre <rlm@mit.edu>
date Thu, 20 Oct 2011 15:10:38 -0700
parents 92f8d83b5d0b
children 97703c7f020e
line wrap: on
line source
1 #+title: Capture Live Video Feeds from JMonkeyEngine
2 #+author: Robert McIntyre
3 #+email: rlm@mit.edu
4 #+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, Java
6 #+SETUPFILE: ../../aurellem/org/setup.org
7 #+INCLUDE: ../../aurellem/org/level-0.org
10 * The Problem
11 So you've made your cool new JMonkeyEngine3 game and you want to
12 create a demo video to show off your hard work. Screen capturing is
13 the most straightforward way to do this, but it can slow down your
14 game and produce low-quality video as a result. A better way is to
15 record a video feed directly from the game while it is
16 running.
18 In this post, I'll explain how you can alter your JMonkeyEngine3 game
19 to output video while it is running. The main trick is to alter the
20 pace of JMonkeyEngine3's in-game time: we allow the engine as much
21 time as it needs to compute complicated in-game events and to encode
22 video frames. As a result, the game appears to speed up and slow down
23 as the computational demands shift, but the end result is perfectly
24 smooth video output at a constant framerate.
27 * Game-time vs. User-time vs. Video-time
29 A standard JME3 application that extends =SimpleApplication= or
30 =Application= tries as hard as it can to keep in sync with
31 /user-time/. If a ball is rolling at 1 game-mile per game-hour in the
32 game, and you wait for one user-hour as measured by the clock on your
33 wall, then the ball should have traveled exactly one game-mile. In
34 order to keep sync with the real world, the game throttles its physics
35 engine and graphics display. If the computations involved in running
36 the game are too intense, then the game will first skip frames, then
37 sacrifice physics accuracy. If there are particuraly demanding
38 computations, then you may only get 1 fps, and the ball may tunnel
39 through the floor or obstacles due to inaccurate physics simulation,
40 but after the end of one user-hour, that ball will have traveled one
41 game-mile.
43 When we're recording video, we don't care if the game-time syncs with
44 user-time, but instead whether the time in the recorded video
45 (video-time) syncs with user-time. To continue the analogy, if we
46 recorded the ball rolling at 1 game-mile per game-hour and watched the
47 video later, we would want to see 30 fps video of the ball rolling at
48 1 video-mile per /user-hour/. It doesn't matter how much user-time it
49 took to simulate that hour of game-time to make the high-quality
50 recording.
52 * COMMENT Two examples to clarify the point:
53 ** Recording from a Simple Simulation
55 *** Without a Special Timer
56 You have a simulation of a ball rolling on an infinite empty plane at
57 one game-mile per game-hour, and a really good computer. Normally,
58 JME3 will throttle the physics engine and graphics display to sync the
59 game-time with user-time. If it takes one-thousandth of a second
60 user-time to simulate one-sixtieth of a second game time and another
61 one-thousandth of a second to draw to the screen, then JME3 will just
62 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 that
65 passes, one second of game-time passes, and the game will run at 60
66 frames per user-second.
69 *** With a Special Timer
70 Then, you change the game's timer so that user-time will be synced to
71 video-time. Assume that encoding a single frame takes 0 seconds
72 user-time to complete.
74 Now, JME3 takes advantage of all available resources. It still takes
75 one-thousandth of a second to calculate a physics tick, and another
76 one-thousandth to render to the screen. Then it takes 0 seconds to
77 write the video frame to disk and encode the video. In only one second
78 of user time, JME3 will complete 500 physics-tick/render/encode-video
79 cycles, and $\frac{500}{60}=8\frac{1}{3}$ seconds of game-time will
80 have passed. Game-time appears to dilate $8\frac{1}{3}\times$ with
81 respect to user-time, and in only 7.2 minutes user-time, one hour of
82 video will have been recorded. The game itself will run at 500 fps.
83 When someone watches the video, they will see 60 frames per
84 user-second, and $\frac{1}{60}$ video-seconds will pass each frame. It
85 will take exactly one hour user-time (and one hour video-time) for the
86 ball in the video to travel one video-mile.
88 ** Recording from a Complex Simulation
90 *** Without a Special Timer
91 You have a simulation of a ball rolling on an infinite empty plane at
92 one game-mile per game-hour accompanied by multiple explosions
93 involving thousands of nodes, particle effects, and complicated shadow
94 shaders to create realistic shadows. You also have a slow
95 laptop. Normally, JME3 must sacrifice rendering and physics simulation
96 to try to keep up. If it takes $\frac{1}{120}$ of a user-second to
97 calculate $\frac{1}{60}$ game-seconds, and an additional
98 $\frac{1}{60}$ of a user-second to render to screen, then JME3 has
99 it's work cut out for it. In order to render to the screen, it will
100 first step the game forward by up to four physics ticks before
101 rendering to the screen. If it still isn't fast enough then it will
102 decrease the accuracy of the physics engine until game-time and user
103 time are synched or a certain threshold is reached, at which point the
104 game visibly slows down. In this case, JME3 continuously repeat a
105 cycle of two physics ticks, and one screen render. For every
106 user-second that passes, one game-second will pass, but the game will
107 run at 30 fps instead of 60 fps like before.
109 *** With a Special Timer
110 Then, you change the game's timer so that user-time will be synced to
111 video-time. Once again, assume video encoding takes $\frac{1}{60}$ of
112 a user-second.
114 Now, JME3 will spend $\frac{1}{120}$ of a user-second to step the
115 physics tick $\frac{1}{60}$ game-seconds, $\frac{1}{60}$ to draw to
116 the screen, and an additional $\frac{1}{60}$ to encode the video and
117 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 take
119 $(\frac{60}{24} = 2.5)$ user-hours to record one game-hour and game-time
120 will appear to flow two-fifths as fast as user time while the game is
121 running. However, just as in example one, when all is said and done we
122 will have an hour long video at 60 fps.
125 * COMMENT proposed names for the new timer
126 # METRONOME
127 # IsoTimer
128 # EvenTimer
129 # PulseTimer
130 # FixedTimer
131 # RigidTimer
132 # FixedTempo
133 # RegularTimer
134 # MetronomeTimer
135 # ConstantTimer
136 # SteadyTimer
139 * =IsoTimer= records time like a metronome
141 The easiest way to achieve this special timing is to create a new
142 timer that always reports the same framerate to JME3 every time it is
143 called.
146 =./jme3/src/core/com/jme3/system/IsoTimer.java=
147 #+include ./jme3/src/core/com/jme3/system/IsoTimer.java src java
149 If an Application uses this =IsoTimer= instead of the normal one, we
150 can be sure that every call to =simpleUpdate=, for example, corresponds
151 to exactly $(\frac{1}{fps})$ seconds of game-time.
153 In order to facilitate setting the =Timer= in user code, I added
154 getter and setter methods to =Application.java=.
156 In =./jme3/src/core/com/jme3/app/Application.java= I added:
157 #+include ./jme3/src/core/com/jme3/app/Application.java src java :lines "340-356"
159 * Encoding to Video
161 Now that the issue of time is solved, we just need a function that
162 writes each frame to a video. We can put this function somewhere
163 where it will be called exactly one per frame.
165 JME3 already provides exactly the class we need: the =SceneProcessor=
166 class can be attached to any viewport and the methods defined therein
167 will be called at the appropriate points in the rendering process.
169 If you want to generate video from Java, a great option is [[http://www.xuggle.com/][Xuggle]]. It
170 takes care of everything related to video encoding and decoding and
171 runs on Windows, Linux and Mac. Out of all the video frameworks for
172 Java I personally like this one the best.
174 Here is a =SceneProcessor= that uses [[http://www.xuggle.com/][Xuggle]] to write each frame to a
175 video file.
177 =./jme3/src/core/com/jme3/app/VideoProcessor.java=
178 #+include ./jme3/src/core/com/jme3/app/VideoProcessor.java src java
180 With this, we are able to record video!
182 * Hello Video!
184 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
185 augmented it with video output as follows:
187 =./jme3/src/test/jme3test/helloworld/HelloVideo.java=
188 #+include ./jme3/src/test/jme3test/helloworld/HelloVideo.java src java
190 The videos are created in the =hello-video= directory
192 #+begin_src sh :results verbatim
193 du -h hello-video/*
194 #+end_src
196 #+results:
197 : 932K hello-video/hello-video-moving.flv
198 : 640K hello-video/hello-video-static.flv
200 And can be immediately uploaded to youtube
202 - [[http://www.youtube.com/watch?v=C8gxVAySaPg][hello-video-moving.flv]]
203 #+BEGIN_HTML
204 <iframe width="425" height="349"
205 src="http://www.youtube.com/embed/C8gxVAySaPg"
206 frameborder="0" allowfullscreen>
207 </iframe>
208 #+END_HTML
209 - [[http://www.youtube.com/watch?v=pHcFOtIS07Q][hello-video-static.flv]]
210 #+BEGIN_HTML
211 <iframe width="425" height="349"
212 src="http://www.youtube.com/embed/pHcFOtIS07Q"
213 frameborder="0" allowfullscreen>
214 </iframe>
216 #+END_HTML
220 * Summary
221 It's quite easy to augment your own application to record video,
222 almost regardless of how complicated the actual application is. You
223 can also record from multiple ViewPorts as the above example shows.
225 The process for adding video recording to your application is as
226 follows:
228 Assuming you want to record at 30 fps, add:
230 #+begin_src java :exports code
231 this.setTimer(new IsoTimer(30));
232 #+end_src
234 Somewhere in the initialization of your Application. Right now, you
235 will have to add the =setTimer= method to =Application=, but hopefully
236 this method will be included soon by the JMonkeyEngine3 team.
238 Then, you create a =VideoProcessor= object and attach it to the
239 =ViewPort= from which you want to record.
241 If you want to record from the game's main =ViewPort= to a file called
242 =/home/r/record.flv=, then add:
244 #+begin_src java :exports code
245 viewPort.addProcessor(new VideoProcessor(new File("/home/r/record.flv")));
246 #+end_src
248 Do this for each =ViewPort= from which you want to record. The more
249 ViewPorts from which you record, the slower the game will run, but
250 this slowness will not affect the final video output.
252 * More Examples
253 ** Hello Physics
254 =HelloVideo= is boring. Let's add some video capturing to =HelloPhysics=
255 and create something fun!
257 This example is a modified version of =HelloPhysics= that creates four
258 simultaneous views of the same scene of cannonballs careening into a
259 brick wall.
261 =./jme3/src/test/jme3test/helloworld/HelloPhysicsWithVideo.java=
262 #+include ./jme3/src/test/jme3test/helloworld/HelloPhysicsWithVideo.java src java
264 Running the program outputs four videos into the =./physics-videos=
265 directory.
267 #+begin_src sh :exports both :results verbatim
268 ls ./physics-videos | grep -
269 #+end_src
271 #+results:
272 : lower-left.flv
273 : lower-right.flv
274 : upper-left.flv
275 : upper-right.flv
277 The videos are fused together with the following =gstreamer= commands:
279 #+begin_src sh :results silent
280 cd physics-videos
282 gst-launch-0.10 \
283 filesrc location=./upper-right.flv ! decodebin ! \
284 videoscale ! ffmpegcolorspace ! \
285 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \
286 videobox border-alpha=0 left=-640 ! \
287 videomixer name=mix ! ffmpegcolorspace ! videorate ! \
288 video/x-raw-yuv, width=1280, height=480, framerate=25/1 ! \
289 jpegenc ! avimux ! filesink location=upper.flv \
290 \
291 filesrc location=./upper-left.flv ! decodebin ! \
292 videoscale ! ffmpegcolorspace ! \
293 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \
294 videobox right=-640 ! mix.
295 #+end_src
297 #+begin_src sh :results silent
298 cd physics-videos
300 gst-launch-0.10 \
301 filesrc location=./lower-left.flv ! decodebin ! \
302 videoscale ! ffmpegcolorspace ! \
303 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \
304 videobox border-alpha=0 left=-640 ! \
305 videomixer name=mix ! ffmpegcolorspace ! videorate ! \
306 video/x-raw-yuv, width=1280, height=480, framerate=25/1 ! \
307 jpegenc ! avimux ! filesink location=lower.flv \
308 \
309 filesrc location=./lower-right.flv ! decodebin ! \
310 videoscale ! ffmpegcolorspace ! \
311 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \
312 videobox right=-640 ! mix.
313 #+end_src
315 #+begin_src sh :results silent
316 cd physics-videos
318 gst-launch-0.10 \
319 filesrc location=./upper.flv ! decodebin ! \
320 videoscale ! ffmpegcolorspace ! \
321 video/x-raw-yuv, width=1280, height=480, framerate=25/1 ! \
322 videobox border-alpha=0 bottom=-480 ! \
323 videomixer name=mix ! ffmpegcolorspace ! videorate ! \
324 video/x-raw-yuv, width=1280, height=960, framerate=25/1 ! \
325 jpegenc ! avimux ! filesink location=../youtube/helloPhysics.flv \
326 \
327 filesrc location=./lower.flv ! decodebin ! \
328 videoscale ! ffmpegcolorspace ! \
329 video/x-raw-yuv, width=1280, height=480, framerate=25/1 ! \
330 videobox top=-480 ! mix.
331 #+end_src
333 #+begin_src sh :results verbatim
334 du -h youtube/helloPhysics.flv
335 #+end_src
337 #+results:
338 : 180M physics-videos/helloPhysics.flv
341 Thats a terribly large size!
342 Let's compress it:
344 ** Compressing the HelloPhysics Video
345 First, we'll scale the video, then, we'll decrease it's bitrate. The
346 end result will be perfect for upload to YouTube.
348 #+begin_src sh :results silent
349 cd youtube
351 gst-launch-0.10 \
352 filesrc location=./helloPhysics.flv ! decodebin ! \
353 videoscale ! ffmpegcolorspace ! \
354 `: # the original size is 1280 by 960` \
355 video/x-raw-yuv, width=1280, height=960, framerate=25/1 ! \
356 videoscale ! \
357 `: # here we scale the video down` \
358 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \
359 `: # and here we limit the bitrate` \
360 theoraenc bitrate=1024 quality=30 ! \
361 oggmux ! progressreport update-freq=1 ! \
362 filesink location=./helloPhysics.ogg
363 #+end_src
365 #+begin_src sh :results verbatim
366 du -h youtube/helloPhysics.ogg
367 #+end_src
369 #+results:
370 : 13M youtube/helloPhysics.ogg
372 [[http://www.youtube.com/watch?v=WIJt9aRGusc][helloPhysics.ogg]]
374 #+begin_html
375 <iframe width="425" height="349"
376 src="http://www.youtube.com/embed/WIJt9aRGusc?hl=en&fs=1"
377 frameborder="0" allowfullscreen>
378 </iframe>
379 #+end_html
382 ** COMMENT failed attempts
383 Let's try the [[http://diracvideo.org/][Dirac]] video encoder.
385 #+begin_src sh :results verbatim
386 cd youtube
387 START=$(date +%s)
388 gst-launch-0.10 \
389 filesrc location=./helloPhysics.flv ! decodebin ! \
390 videoscale ! ffmpegcolorspace ! \
391 video/x-raw-yuv, width=1280, height=960, framerate=25/1 ! \
392 schroenc ! filesink location=./helloPhysics.drc > /dev/null
393 echo `expr $(( $(date +%s) - $START))`
394 #+end_src
397 #+results:
398 : 142
400 That took 142 seconds. Let's see how it does compression-wise:
402 #+begin_src sh :results verbatim
403 du -h ./youtube/helloPhysics.drc
404 #+end_src
406 #+results:
407 : 22M ./physics-videos/helloPhysics.drc
410 #+begin_src sh :results verbatim
411 cd youtube
412 START=$(date +%s)
413 gst-launch-0.10 \
414 filesrc location=./helloPhysics.flv ! decodebin ! \
415 videoscale ! ffmpegcolorspace ! \
416 video/x-raw-yuv, width=1280, height=960, framerate=25/1 ! \
417 theoraenc ! oggmux ! filesink location=./helloPhysics.ogg \
418 > /dev/null
419 echo `expr $(( $(date +%s) - $START))`
420 #+end_src
422 #+results:
423 : 123
425 #+begin_src sh :results verbatim
426 du -h youtube/helloPhysics.ogg
427 #+end_src
429 #+results:
430 : 59M physics-videos/helloPhysics.ogg
433 =*.drc= files can not be uploaded to YouTube, so I'll go for the
434 avi file.
437 ** COMMENT text for videos
438 Video output from JMonkeyEngine3 (www.jmonkeyengine.org/) using Xuggle
439 (www.xuggle.com/). Everything is explained at
440 http://aurellem.org/cortex/capture-video.html.
443 Video output from JMonkeyEngine3 (www.jmonkeyengine.org/) HelloPhysics
444 demo application using Xuggle (www.xuggle.com/). Everything is
445 explained at http://aurellem.org/cortex/capture-video.html. Here,
446 four points of view are simultaneously recorded and then glued
447 together later.
449 JME3 Xuggle Aurellem video capture
452 * Sample Videos
453 I encoded most of the original JME3 Hello demos for your viewing
454 pleasure, all using the =VideoProcessor= and =IsoTimer= classes.
456 ** HelloTerrain
457 [[http://youtu.be/5_4wyDFwrVQ][HelloTerrain.avi]]
459 #+begin_html
460 <iframe width="425" height="349"
461 src="http://www.youtube.com/embed/5_4wyDFwrVQ"
462 frameborder="0" allowfullscreen>
463 </iframe>
464 #+end_html
466 ** HelloAssets
467 [[http://www.youtube.com/watch?v=oGg-Q6k1BM4][HelloAssets.avi]]
469 #+begin_html
470 <iframe width="425" height="349"
471 src="http://www.youtube.com/embed/oGg-Q6k1BM4?hl=en&fs=1"
472 frameborder="0" allowfullscreen>
473 </iframe>
474 #+end_html
476 ** HelloEffects
477 [[http://www.youtube.com/watch?v=TuxlLMe53hA][HelloEffects]]
479 #+begin_html
480 <iframe width="425" height="349"
481 src="http://www.youtube.com/embed/TuxlLMe53hA?hl=en&fs=1"
482 frameborder="0" allowfullscreen>
483 </iframe>
484 #+end_html
486 ** HelloCollision
487 [[http://www.youtube.com/watch?v=GPlvJkiZfFw][HelloCollision.avi]]
489 #+begin_html
490 <iframe width="425" height="349"
491 src="http://www.youtube.com/embed/GPlvJkiZfFw?hl=en&fs=1"
492 frameborder="0" allowfullscreen>
493 </iframe>
494 #+end_html
496 ** HelloAnimation
497 [[http://www.youtube.com/watch?v=SDCfOSPYUkg][HelloAnimation.avi]]
499 #+begin_html
500 <iframe width="425" height="349"
501 src="http://www.youtube.com/embed/SDCfOSPYUkg?hl=en&fs=1"
502 frameborder="0" allowfullscreen>
503 </iframe>
504 #+end_html
506 ** HelloNode
507 [[http://www.youtube.com/watch?v=pL-0fR0-ilQ][HelloNode.avi]]
509 #+begin_html
510 <iframe width="425" height="349"
511 src="http://www.youtube.com/embed/pL-0fR0-ilQ?hl=en&fs=1"
512 frameborder="0" allowfullscreen>
513 </iframe>
514 #+end_html
516 ** HelloLoop
517 [[http://www.youtube.com/watch?v=mosZzzcdE5w][HelloLoop.avi]]
519 #+begin_html
520 <iframe width="425" height="349"
521 src="http://www.youtube.com/embed/mosZzzcdE5w?hl=en&fs=1"
522 frameborder="0" allowfullscreen>
523 </iframe>
524 #+end_html
527 *** COMMENT x-form the other stupid
528 progressreport update-freq=1
530 gst-launch-0.10 \
531 filesrc location=./helloPhy ! decodebin ! \
532 videoscale ! ffmpegcolorspace ! \
533 video/x-raw-yuv, width=1280, height=960, framerate=25/1 ! \
534 x264enc ! avimux ! filesink location=helloPhysics.avi \
537 gst-launch-0.10 \
538 filesrc location=./HelloAnimationStatic.flv ! decodebin ! \
539 videoscale ! ffmpegcolorspace ! \
540 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \
541 videobox border-alpha=0 left=-640 ! \
542 videomixer name=mix ! ffmpegcolorspace ! videorate ! \
543 video/x-raw-yuv, width=1280, height=480, framerate=25/1 ! \
544 x264enc ! avimux ! progressreport update-freq=1 ! \
545 filesink location=../youtube/HelloAnimation.avi \
546 \
547 filesrc location=./HelloAnimationMotion.flv ! decodebin ! \
548 videoscale ! ffmpegcolorspace ! \
549 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \
550 videobox right=-640 ! mix.
552 gst-launch-0.10 \
553 filesrc location=./HelloCollisionMotion.flv ! decodebin ! \
554 videoscale ! ffmpegcolorspace ! \
555 video/x-raw-yuv, width=800, height=600, framerate=25/1 ! \
556 x264enc bitrate=1024 ! avimux ! \
557 filesink location=../youtube/HelloCollision.avi
559 gst-launch-0.10 \
560 filesrc location=./HelloEffectsStatic.flv ! decodebin ! \
561 videoscale ! ffmpegcolorspace ! \
562 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \
563 videobox border-alpha=0 left=-640 ! \
564 videomixer name=mix ! ffmpegcolorspace ! videorate ! \
565 video/x-raw-yuv, width=1280, height=480, framerate=25/1 ! \
566 x264enc bitrate=1024 ! avimux ! progressreport update-freq=1 ! \
567 filesink location=../youtube/HelloEffects.avi \
568 \
569 filesrc location=./HelloEffectsMotion.flv ! decodebin ! \
570 videoscale ! ffmpegcolorspace ! \
571 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \
572 videobox right=-640 ! mix.
574 gst-launch-0.10 \
575 filesrc location=./HelloTerrainMotion.flv ! decodebin ! \
576 videoscale ! ffmpegcolorspace ! \
577 video/x-raw-yuv, width=800, height=600, framerate=25/1 ! \
578 x264enc bitrate=1024 ! avimux ! \
579 filesink location=../youtube/HelloTerrain.avi
582 gst-launch-0.10 \
583 filesrc location=./HelloAssetsStatic.flv ! decodebin ! \
584 videoscale ! ffmpegcolorspace ! \
585 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \
586 videobox border-alpha=0 left=-640 ! \
587 videomixer name=mix ! ffmpegcolorspace ! videorate ! \
588 video/x-raw-yuv, width=1280, height=480, framerate=25/1 ! \
589 x264enc bitrate=1024 ! avimux ! progressreport update-freq=1 ! \
590 filesink location=../youtube/HelloAssets.avi \
591 \
592 filesrc location=./HelloAssetsMotion.flv ! decodebin ! \
593 videoscale ! ffmpegcolorspace ! \
594 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \
595 videobox right=-640 ! mix.
598 gst-launch-0.10 \
599 filesrc location=./HelloNodeStatic.flv ! decodebin ! \
600 videoscale ! ffmpegcolorspace ! \
601 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \
602 videobox border-alpha=0 left=-640 ! \
603 videomixer name=mix ! ffmpegcolorspace ! videorate ! \
604 video/x-raw-yuv, width=1280, height=480, framerate=25/1 ! \
605 x264enc bitrate=1024 ! avimux ! progressreport update-freq=1 ! \
606 filesink location=../youtube/HelloNode.avi \
607 \
608 filesrc location=./HelloNodeMotion.flv ! decodebin ! \
609 videoscale ! ffmpegcolorspace ! \
610 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \
611 videobox right=-640 ! mix.
613 gst-launch-0.10 \
614 filesrc location=./HelloLoopStatic.flv ! decodebin ! \
615 videoscale ! ffmpegcolorspace ! \
616 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \
617 videobox border-alpha=0 left=-640 ! \
618 videomixer name=mix ! ffmpegcolorspace ! videorate ! \
619 video/x-raw-yuv, width=1280, height=480, framerate=25/1 ! \
620 x264enc bitrate=1024 ! avimux ! progressreport update-freq=1 ! \
621 filesink location=../youtube/HelloLoop.avi \
622 \
623 filesrc location=./HelloLoopMotion.flv ! decodebin ! \
624 videoscale ! ffmpegcolorspace ! \
625 video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \
626 videobox right=-640 ! mix.