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