view org/capture-video.org @ 95:e4bcd0c481ba

now with more accurate authorship
author Robert McIntyre <rlm@mit.edu>
date Tue, 10 Jan 2012 08:38:50 -0700
parents ecafe87ffddc
children da4de661c5d9
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 =./src/com/aurellem/capture/IsoTimer.java=
147 #+include ../../jmeCapture/src/com/aurellem/capture/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 * Encoding to Video
155 Now that the issue of time is solved, we just need a function that
156 writes each frame to a video. We can put this function somewhere
157 where it will be called exactly one per frame.
159 The basic functions that a =VideoRecorder= should support are
160 recording, starting, stopping, and possibly a final finishing step
161 there it finilizes the recording (such as writing headers for a video
162 file).
164 An appropiate interface describing this behaviour could look like
165 this:
167 =./src/com/aurellem/capture/video/VideoRecorder.java=
168 #+include ../../jmeCapture/src/com/aurellem/capture/video/VideoRecorder.java src java
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 However, it is also important to properly close the video stream and
176 write headers and such, and even though =SceneProcessor= has a
177 =.cleanup()= method, it is only called when the =SceneProcessor= is
178 removed from the =RenderManager=, not when the game is shutting down
179 when the user pressed ESC, for example. To obtain reliable shutdown
180 behaviour, we also have to implement =AppState=, which provides a
181 =.cleanup()= method that /is/ called on shutdown.
183 Here is an AbstractVideoRecorder class that takes care of the details
184 of setup and teardown.
186 =./src/com/aurellem/capture/video/AbstractVideoRecorder.java=
187 #+include ../../jmeCapture/src/com/aurellem/capture/video/AbstractVideoRecorder.java src java
189 If you want to generate video from Java, a great option is [[http://www.xuggle.com/][Xuggle]]. It
190 takes care of everything related to video encoding and decoding and
191 runs on Windows, Linux and Mac. Out of all the video frameworks for
192 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 a
195 video file.
197 =./src/com/aurellem/capture/video/XuggleVideoRecorder.java=
198 #+include ../../jmeCapture/src/com/aurellem/capture/video/XuggleVideoRecorder.java src java
200 With this, we are able to record video!
202 However, it can be hard to properly install Xuggle. For those of you
203 who would rather not use Xuggle, here is an alternate class that uses
204 [[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 java
209 This =AVIVideoRecorder= is more limited than the
210 =XuggleVideoRecorder=, but requires less external dependencies.
212 Finally, for those of you who prefer to create the final video from a
213 sequence of images, there is the =FileVideoRecorder=, which records
214 each frame to a folder as a sequentially numbered image file. Note
215 that you have to remember the FPS at which you recorded the video, as
216 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 java
222 * /Really/ Simple Video Recording
224 The most common case for recording a video is probably to just capture
225 whatever is on your screen exactly as you see it. In this case, this
226 method will do.
228 #+begin_src java
229 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 stuff
245 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_src
257 This will select the appropiate backend =VideoRecorder= class
258 depending on the file name you specify, and insturment your
259 application to record video to the file. You should still set the
260 game's timer to an =IsoTimer= with the desired fps.
262 This example will record video from the ocean scene from the
263 jMonkeyEngine test suite.
264 #+begin_src java
265 File video = File.createTempFile("JME-water-video", ".avi");
266 captureVideo(app, video);
267 app.start();
268 System.out.println(video.getCanonicalPath());
269 #+end_src
272 I've added support for this under a class called
273 =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=]] and
279 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 java
284 The videos are created in the =hello-video= directory
286 #+begin_src sh :results verbatim :exports both
287 du -h hello-video/*
288 #+end_src
290 #+results:
291 : 932K hello-video/hello-video-moving.flv
292 : 640K hello-video/hello-video-static.flv
294 And can be immediately uploaded to youtube
296 - [[http://www.youtube.com/watch?v=C8gxVAySaPg][hello-video-moving.flv]]
297 #+BEGIN_HTML
298 <iframe width="425" height="349"
299 src="http://www.youtube.com/embed/C8gxVAySaPg"
300 frameborder="0" allowfullscreen>
301 </iframe>
302 #+END_HTML
303 - [[http://www.youtube.com/watch?v=pHcFOtIS07Q][hello-video-static.flv]]
304 #+BEGIN_HTML
305 <iframe width="425" height="349"
306 src="http://www.youtube.com/embed/pHcFOtIS07Q"
307 frameborder="0" allowfullscreen>
308 </iframe>
310 #+END_HTML
313 * Summary
314 It's quite easy to augment your own application to record video,
315 almost regardless of how complicated the actual application is. You
316 can also record from multiple ViewPorts as the above example shows.
318 The process for adding video recording to your application is as
319 follows:
321 Assuming you want to record at 30 fps, add:
323 #+begin_src java :exports code
324 this.setTimer(new IsoTimer(30));
325 #+end_src
327 Somewhere in the initialization of your Application.
329 If you want to record from the game's main =ViewPort= to a file called
330 =/home/r/record.flv=, then add:
332 #+begin_src java :exports code
333 Capture.captureVideo(app, new File("/home/r/record.flv"));
334 #+end_src
336 Before you call =app.start()=;
338 * More Examples
339 ** COMMENT Hello Physics
340 =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 four
344 simultaneous views of the same scene of cannonballs careening into a
345 brick wall.
347 =./jme3/src/test/jme3test/helloworld/HelloPhysicsWithVideo.java=
348 #+include ./jme3/src/test/jme3test/helloworld/HelloPhysicsWithVideo.java src java
350 Running the program outputs four videos into the =./physics-videos=
351 directory.
353 #+begin_src sh :exports both :results verbatim
354 ls ./physics-videos | grep -
355 #+end_src
357 #+results:
358 : lower-left.flv
359 : lower-right.flv
360 : upper-left.flv
361 : upper-right.flv
363 The videos are fused together with the following =gstreamer= commands:
365 #+begin_src sh :results silent
366 cd physics-videos
368 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_src
383 #+begin_src sh :results silent
384 cd physics-videos
386 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_src
401 #+begin_src sh :results silent
402 cd physics-videos
404 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_src
419 #+begin_src sh :results verbatim
420 du -h youtube/helloPhysics.flv
421 #+end_src
423 #+results:
424 : 180M physics-videos/helloPhysics.flv
427 Thats a terribly large size!
428 Let's compress it:
430 ** COMMENT Compressing the HelloPhysics Video
431 First, we'll scale the video, then, we'll decrease it's bitrate. The
432 end result will be perfect for upload to YouTube.
434 #+begin_src sh :results silent
435 cd youtube
437 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.ogg
449 #+end_src
451 #+begin_src sh :results verbatim
452 du -h youtube/helloPhysics.ogg
453 #+end_src
455 #+results:
456 : 13M youtube/helloPhysics.ogg
458 [[http://www.youtube.com/watch?v=WIJt9aRGusc][helloPhysics.ogg]]
460 #+begin_html
461 <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_html
468 ** COMMENT failed attempts
469 Let's try the [[http://diracvideo.org/][Dirac]] video encoder.
471 #+begin_src sh :results verbatim
472 cd youtube
473 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/null
479 echo `expr $(( $(date +%s) - $START))`
480 #+end_src
483 #+results:
484 : 142
486 That took 142 seconds. Let's see how it does compression-wise:
488 #+begin_src sh :results verbatim
489 du -h ./youtube/helloPhysics.drc
490 #+end_src
492 #+results:
493 : 22M ./physics-videos/helloPhysics.drc
496 #+begin_src sh :results verbatim
497 cd youtube
498 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/null
505 echo `expr $(( $(date +%s) - $START))`
506 #+end_src
508 #+results:
509 : 123
511 #+begin_src sh :results verbatim
512 du -h youtube/helloPhysics.ogg
513 #+end_src
515 #+results:
516 : 59M physics-videos/helloPhysics.ogg
519 =*.drc= files can not be uploaded to YouTube, so I'll go for the
520 avi file.
523 ** COMMENT text for videos
524 Video output from JMonkeyEngine3 (www.jmonkeyengine.org/) using Xuggle
525 (www.xuggle.com/). Everything is explained at
526 http://aurellem.org/cortex/capture-video.html.
529 Video output from JMonkeyEngine3 (www.jmonkeyengine.org/) HelloPhysics
530 demo application using Xuggle (www.xuggle.com/). Everything is
531 explained at http://aurellem.org/cortex/capture-video.html. Here,
532 four points of view are simultaneously recorded and then glued
533 together later.
535 JME3 Xuggle Aurellem video capture
538 * Sample Videos
539 I encoded most of the original JME3 Hello demos for your viewing
540 pleasure, all using the =Capture= and =IsoTimer= classes.
542 ** HelloTerrain
543 [[http://youtu.be/5_4wyDFwrVQ][HelloTerrain.avi]]
545 #+begin_html
546 <iframe width="425" height="349"
547 src="http://www.youtube.com/embed/5_4wyDFwrVQ"
548 frameborder="0" allowfullscreen>
549 </iframe>
550 #+end_html
552 ** HelloAssets
553 [[http://www.youtube.com/watch?v=oGg-Q6k1BM4][HelloAssets.avi]]
555 #+begin_html
556 <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_html
562 ** HelloEffects
563 [[http://www.youtube.com/watch?v=TuxlLMe53hA][HelloEffects]]
565 #+begin_html
566 <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_html
572 ** HelloCollision
573 [[http://www.youtube.com/watch?v=GPlvJkiZfFw][HelloCollision.avi]]
575 #+begin_html
576 <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_html
582 ** HelloAnimation
583 [[http://www.youtube.com/watch?v=SDCfOSPYUkg][HelloAnimation.avi]]
585 #+begin_html
586 <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_html
592 ** HelloNode
593 [[http://www.youtube.com/watch?v=pL-0fR0-ilQ][HelloNode.avi]]
595 #+begin_html
596 <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_html
602 ** HelloLoop
603 [[http://www.youtube.com/watch?v=mosZzzcdE5w][HelloLoop.avi]]
605 #+begin_html
606 <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_html
613 *** COMMENT x-form the other stupid
614 progressreport update-freq=1
616 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.avi
645 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.avi
668 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.