rlm@3
|
1 package com.aurellem.capture;
|
rlm@3
|
2
|
rlm@3
|
3 import java.io.File;
|
rlm@3
|
4 import java.io.IOException;
|
rlm@12
|
5 import java.util.concurrent.Callable;
|
rlm@3
|
6
|
rlm@12
|
7 import com.aurellem.capture.audio.MultiListener;
|
rlm@12
|
8 import com.aurellem.capture.audio.WaveFileWriter;
|
rlm@9
|
9 import com.aurellem.capture.video.AVIVideoRecorder;
|
rlm@10
|
10 import com.aurellem.capture.video.AbstractVideoRecorder;
|
rlm@39
|
11 import com.aurellem.capture.video.FileVideoRecorder;
|
rlm@10
|
12 import com.aurellem.capture.video.XuggleVideoRecorder;
|
rlm@3
|
13 import com.jme3.app.Application;
|
rlm@12
|
14 import com.jme3.audio.AudioRenderer;
|
rlm@12
|
15 import com.jme3.renderer.ViewPort;
|
rlm@12
|
16 import com.jme3.scene.Spatial;
|
rlm@49
|
17 import com.jme3.system.AppSettings;
|
rlm@44
|
18 import com.jme3.system.JmeSystem;
|
rlm@3
|
19
|
rlm@53
|
20 /**
|
rlm@56
|
21 * Use the methods in this class for capturing consistent, high quality video
|
rlm@56
|
22 * and audio from a jMonkeyEngine3 application. To capture audio or video do
|
rlm@56
|
23 * the following:
|
rlm@53
|
24 *
|
rlm@56
|
25 * 1.) Set your application's timer to an IsoTimer. Create the IsoTimer with the
|
rlm@56
|
26 * desired video frames-per-second.
|
rlm@56
|
27 *
|
rlm@56
|
28 * 2.) Call captureAudio and/or captureVideo on the Application as desired
|
rlm@56
|
29 * before starting the Application.
|
rlm@55
|
30 *
|
rlm@56
|
31 * See the Basic and Advanced demos in the examples section for more
|
rlm@56
|
32 * information. If you have any trouble, please PM me on the jMonkeyEngine
|
rlm@56
|
33 * forums. My username is bortreb.
|
rlm@55
|
34 *
|
rlm@53
|
35 * @author Robert McIntyre
|
rlm@53
|
36 */
|
rlm@53
|
37
|
rlm@3
|
38 public class Capture {
|
rlm@3
|
39
|
rlm@56
|
40 /**
|
rlm@56
|
41 * Use this function to capture video from your application. You
|
rlm@56
|
42 * specify the framerate at which the video will be recording by setting
|
rlm@56
|
43 * the Application's timer to an IsoTimer created with the desired
|
rlm@56
|
44 * frames-per-second.
|
rlm@56
|
45 *
|
rlm@56
|
46 * There are three ways to record and they are selected by the
|
rlm@56
|
47 * properties of the file that you specify.
|
rlm@56
|
48 *
|
rlm@56
|
49 * 1.) (Preferred) If you supply an empty directory as the file, then
|
rlm@56
|
50 * the video will be saved as a sequence of .png files, one file per
|
rlm@56
|
51 * frame. The files start at 0000000.png and increment from there.
|
rlm@56
|
52 * You can then combine the frames into your preferred
|
rlm@56
|
53 * container/codec. If the directory is not empty, then writing
|
rlm@56
|
54 * video frames to it will fail, and nothing will be written.
|
rlm@56
|
55 *
|
rlm@56
|
56 * 2.) If the filename ends in ".avi" then the frames will be encoded as
|
rlm@56
|
57 * a RAW stream inside an AVI 1.0 container. The resulting file
|
rlm@56
|
58 * will be quite large and you will probably want to re-encode it to
|
rlm@56
|
59 * your preferred container/codec format. Be advised that some
|
rlm@56
|
60 * video payers cannot process AVI with a RAW stream, and that AVI
|
rlm@56
|
61 * 1.0 files generated by this method that exceed 2.0GB are invalid
|
rlm@56
|
62 * according to the AVI 1.0 spec (but many programs can still deal
|
rlm@56
|
63 * with them.) Thanks to Werner Randelshofer for his excellent work
|
rlm@56
|
64 * which made AVI file writer option possible.
|
rlm@56
|
65 *
|
rlm@56
|
66 * 3.) Any non-directory file ending in anything other than ".avi" will
|
rlm@56
|
67 * be processed through Xuggle. Xuggle provides the option to use
|
rlm@56
|
68 * many codecs/containers, but you will have to install it on your
|
rlm@56
|
69 * system yourself in order to use this option. Please visit
|
rlm@56
|
70 * http://www.xuggle.com/ to learn how to do this.
|
rlm@56
|
71 *
|
rlm@56
|
72 * @param app The Application from which you wish to record Video.
|
rlm@56
|
73 * @param file The file to which the video will be captured.
|
rlm@56
|
74 * @throws IOException
|
rlm@56
|
75 */
|
rlm@54
|
76
|
rlm@56
|
77 public static void captureVideo(final Application app, final File file) throws IOException{
|
rlm@56
|
78 final AbstractVideoRecorder videoRecorder;
|
rlm@12
|
79
|
rlm@56
|
80 if (file.getCanonicalPath().endsWith(".avi")){
|
rlm@56
|
81 videoRecorder = new AVIVideoRecorder(file);}
|
rlm@56
|
82 else if (file.isDirectory()){
|
rlm@56
|
83 videoRecorder = new FileVideoRecorder(file);}
|
rlm@56
|
84 else { videoRecorder = new XuggleVideoRecorder(file);}
|
rlm@12
|
85
|
rlm@56
|
86 Callable<Object> thunk = new Callable<Object>(){
|
rlm@56
|
87 public Object call(){
|
rlm@12
|
88
|
rlm@56
|
89 ViewPort viewPort =
|
rlm@56
|
90 app.getRenderManager()
|
rlm@56
|
91 .createPostView("aurellem video record", app.getCamera());
|
rlm@12
|
92
|
rlm@56
|
93 viewPort.setClearFlags(false, false, false);
|
rlm@12
|
94
|
rlm@56
|
95 // get GUI node stuff
|
rlm@56
|
96 for (Spatial s : app.getGuiViewPort().getScenes()){
|
rlm@56
|
97 viewPort.attachScene(s);
|
rlm@56
|
98 }
|
rlm@12
|
99
|
rlm@56
|
100 app.getStateManager().attach(videoRecorder);
|
rlm@56
|
101 viewPort.addProcessor(videoRecorder);
|
rlm@56
|
102 return null;
|
rlm@56
|
103 }
|
rlm@56
|
104 };
|
rlm@56
|
105 app.enqueue(thunk);
|
rlm@56
|
106 }
|
rlm@12
|
107
|
rlm@54
|
108
|
rlm@56
|
109 /**
|
rlm@56
|
110 * Use this function to capture audio from your application. Audio data
|
rlm@56
|
111 * will be saved in linear PCM format at 44,100 hertz in the wav container
|
rlm@56
|
112 * format to the file that you specify.
|
rlm@56
|
113 *
|
rlm@56
|
114 * Note that you *have* to use an IsoTimer for your Application's timer
|
rlm@56
|
115 * while recording audio or the recording will fail. Also ensure that your
|
rlm@56
|
116 * IsoTimer obeys the following constraints:
|
rlm@56
|
117 *
|
rlm@56
|
118 * 1.) The frames-per-second value of the IsoTimer cannot be lower than 10
|
rlm@56
|
119 * frames-per-second.
|
rlm@56
|
120 *
|
rlm@56
|
121 * 2.) The frames-per-second value of the IsoTimer must evenly divide
|
rlm@56
|
122 * 44,100.
|
rlm@56
|
123 *
|
rlm@56
|
124 * @param app The Application from which you wish to record Audio.
|
rlm@56
|
125 * @param file the target file to which you want to record audio data.
|
rlm@56
|
126 * @throws IOException
|
rlm@56
|
127 */
|
rlm@12
|
128
|
rlm@56
|
129 public static void captureAudio(final Application app, final File file) throws IOException{
|
rlm@56
|
130 AppSettings settings = null;
|
rlm@56
|
131 if (app.getContext() != null){settings = app.getContext().getSettings();}
|
rlm@56
|
132 if (settings == null){settings = new AppSettings(true);}
|
rlm@56
|
133 settings.setAudioRenderer("Send");
|
rlm@56
|
134 app.setSettings(settings);
|
rlm@49
|
135
|
rlm@56
|
136 JmeSystem.setSystemDelegate(new AurellemSystemDelegate());
|
rlm@49
|
137
|
rlm@56
|
138 final WaveFileWriter writer = new WaveFileWriter(file);
|
rlm@12
|
139
|
rlm@56
|
140 Callable<Object> thunk = new Callable<Object>(){
|
rlm@56
|
141 public Object call(){
|
rlm@56
|
142 AudioRenderer ar = app.getAudioRenderer();
|
rlm@56
|
143 if (ar instanceof MultiListener){
|
rlm@56
|
144 MultiListener ml = (MultiListener)ar;
|
rlm@56
|
145 ml.registerSoundProcessor(writer);
|
rlm@56
|
146 }
|
rlm@56
|
147 return null;
|
rlm@56
|
148 }
|
rlm@56
|
149 };
|
rlm@56
|
150 app.enqueue(thunk);
|
rlm@56
|
151 }
|
rlm@3
|
152 }
|