]> git.xonotic.org Git - xonotic/xonotic.git/blob - misc/tools/NexuizDemoRecorder/plugins/virtualdub/src/main/java/com/nexuiz/demorecorder/application/plugins/impl/virtualdub/VirtualDubPlugin.java
initial checkin from nexuiz svn r8756
[xonotic/xonotic.git] / misc / tools / NexuizDemoRecorder / plugins / virtualdub / src / main / java / com / nexuiz / demorecorder / application / plugins / impl / virtualdub / VirtualDubPlugin.java
1 package com.nexuiz.demorecorder.application.plugins.impl.virtualdub;\r
2 \r
3 import java.io.BufferedReader;\r
4 import java.io.BufferedWriter;\r
5 import java.io.File;\r
6 import java.io.FileWriter;\r
7 import java.io.IOException;\r
8 import java.io.InputStreamReader;\r
9 import java.util.ArrayList;\r
10 import java.util.List;\r
11 import java.util.Properties;\r
12 \r
13 import com.nexuiz.demorecorder.application.DemoRecorderApplication;\r
14 import com.nexuiz.demorecorder.application.DemoRecorderException;\r
15 import com.nexuiz.demorecorder.application.DemoRecorderUtils;\r
16 import com.nexuiz.demorecorder.application.jobs.RecordJob;\r
17 import com.nexuiz.demorecorder.application.plugins.EncoderPlugin;\r
18 import com.nexuiz.demorecorder.application.plugins.EncoderPluginException;\r
19 \r
20 public class VirtualDubPlugin implements EncoderPlugin {\r
21         \r
22         private static final String PLUGIN_NAME = "Virtual Dub";\r
23         \r
24         private static class Preferences {\r
25                 public static final String ENABLED = "Enabled";\r
26                 public static final String VIRTUAL_DUB_BINARY_PATH = "Path to vdub.exe";\r
27                 public static final String VCF_PER_JOB_LIMIT = "Max. number of VCFs per job";\r
28                 public static final String OUTPUT_FILE_MODE = "Output as suffix (0) or file (1)";\r
29                 public static final String EXTRA_OPTIONS = "Show extra options";\r
30                 \r
31                 public static final String[] GLOBAL_PREFERENCES_ORDER = {\r
32                         ENABLED,\r
33                         VIRTUAL_DUB_BINARY_PATH,\r
34                         VCF_PER_JOB_LIMIT,\r
35                         OUTPUT_FILE_MODE,\r
36                         EXTRA_OPTIONS\r
37                 };\r
38                 \r
39                 //job-specific preferences\r
40                 public static final String CLEAR_JOBCONTROL = "Clear VDub job control on first VCF";\r
41                 public static final String RENDER_OUTPUT = "VDub renders queued jobs";\r
42                 public static final String VCF_PATH = "Path to VCF file "; //x will be attached, e.g. "Path to VCF file 1"\r
43                 public static final String OUTPUT_SUFFIX = "Suffix for output file "; //x will be attached, e.g. "Suffix for output file 1"\r
44                 public static final String OUTPUT_FILE = "Output file "; //x will be attached\r
45                 public static final String USE_ENCODED_VIDEO = "<HTML><BODY>Use encoded video from VCF "; //x will be attached\r
46                 public static final String USE_ENCODED_VIDEO_2 = "<BR>for consecutive VCFs</BODY></HTML>";\r
47                 public static final String DELETE_ORIG_FILE = "Delete orig. file after processing VCF "; //x will be attached\r
48         }\r
49         \r
50         private DemoRecorderApplication appLayer = null;\r
51         private Properties globalDefaultPreferences = new Properties();\r
52         \r
53         public VirtualDubPlugin() {\r
54                 this.createPreferenceDefaultValues();\r
55         }\r
56 \r
57         @Override\r
58         public void executeEncoder(RecordJob job) throws EncoderPluginException {\r
59                 this.checkAppLayer();\r
60                 if (!this.isEnabled()) {\r
61                         return;\r
62                 }\r
63                 \r
64                 if (job.getActualVideoDestination() == null) {\r
65                         //should never happen... but just to make sure!\r
66                         throw new EncoderPluginException("Actual video destination is not set (should have been set when processing the job)");\r
67                 }\r
68                 \r
69                 if (!job.getActualVideoDestination().exists()) {\r
70                         throw new EncoderPluginException("Could not locate video file (source) at location "\r
71                                         + job.getActualVideoDestination().getAbsolutePath());\r
72                 }\r
73                 \r
74                 String limitStr = this.appLayer.getPreferences().getProperty(this.getName(), Preferences.VCF_PER_JOB_LIMIT);\r
75                 int vcfCounter;\r
76                 try {\r
77                         vcfCounter = Integer.valueOf(limitStr);\r
78                 } catch (NumberFormatException e) {\r
79                         throw new EncoderPluginException("Invalid value \"" + limitStr + "\" for setting " + Preferences.VCF_PER_JOB_LIMIT);\r
80                 }\r
81                 \r
82                 //check vdub.exe\r
83                 String vDubBinary = this.appLayer.getPreferences().getProperty(this.getName(), Preferences.VIRTUAL_DUB_BINARY_PATH);\r
84                 File vDubBinaryFile = new File(vDubBinary);\r
85                 if (!vDubBinaryFile.exists() || !vDubBinaryFile.canExecute()) {\r
86                         throw new EncoderPluginException("Invalid location for the vdub.exe: " + vDubBinary);\r
87                 }\r
88                 \r
89                 this.doEncoding(job, vcfCounter);\r
90         }\r
91 \r
92         @Override\r
93         public Properties getGlobalPreferences() {\r
94                 return this.globalDefaultPreferences;\r
95         }\r
96 \r
97         @Override\r
98         public String[] getGlobalPreferencesOrder() {\r
99                 return Preferences.GLOBAL_PREFERENCES_ORDER;\r
100         }\r
101 \r
102         @Override\r
103         public Properties getJobSpecificPreferences() {\r
104                 this.checkAppLayer();\r
105                 Properties jobSpecificPreferences = new Properties();\r
106                 \r
107                 //static properties\r
108                 jobSpecificPreferences.setProperty(Preferences.CLEAR_JOBCONTROL, "true");\r
109                 jobSpecificPreferences.setProperty(Preferences.RENDER_OUTPUT, "true");\r
110                 \r
111                 //dynamic properties\r
112                 String limitStr = this.appLayer.getPreferences().getProperty(this.getName(), Preferences.VCF_PER_JOB_LIMIT);\r
113                 try {\r
114                         int limit = Integer.valueOf(limitStr);\r
115                         if (limit > 0) {\r
116                                 for (int i = 1; i <= limit; i++) {\r
117                                         jobSpecificPreferences.setProperty(Preferences.VCF_PATH + i, "filechooser");\r
118                                         if (Boolean.valueOf(this.appLayer.getPreferences().getProperty(this.getName(), Preferences.OUTPUT_FILE_MODE))) {\r
119                                                 //filechooser\r
120                                                 jobSpecificPreferences.setProperty(Preferences.OUTPUT_FILE + i, "filechooser");\r
121                                         } else {\r
122                                                 //suffix\r
123                                                 jobSpecificPreferences.setProperty(Preferences.OUTPUT_SUFFIX + i, "_vdub" + i);\r
124                                         }\r
125                                         \r
126                                         if (Boolean.valueOf(this.appLayer.getPreferences().getProperty(this.getName(), Preferences.EXTRA_OPTIONS))) {\r
127                                                 String useEncStringKey = Preferences.USE_ENCODED_VIDEO + i + Preferences.USE_ENCODED_VIDEO_2;\r
128                                                 jobSpecificPreferences.setProperty(useEncStringKey, "false");\r
129                                                 jobSpecificPreferences.setProperty(Preferences.DELETE_ORIG_FILE + i, "false");\r
130                                         }\r
131                                 }\r
132                         }\r
133                 } catch (NumberFormatException e) {\r
134                         throw new DemoRecorderException("Invalid value \"" + limitStr + "\" for setting " + Preferences.VCF_PER_JOB_LIMIT);\r
135                 }\r
136                 \r
137                 return jobSpecificPreferences;\r
138         }\r
139         \r
140         @Override\r
141         public String[] getJobSpecificPreferencesOrder() {\r
142                 this.checkAppLayer();\r
143                 List<String> preferencesOrderList = new ArrayList<String>();\r
144                 \r
145                 //static properties\r
146                 preferencesOrderList.add(Preferences.CLEAR_JOBCONTROL);\r
147                 preferencesOrderList.add(Preferences.RENDER_OUTPUT);\r
148                 \r
149                 //dynamic properties\r
150                 String limitStr = this.appLayer.getPreferences().getProperty(this.getName(), Preferences.VCF_PER_JOB_LIMIT);\r
151                 try {\r
152                         int limit = Integer.valueOf(limitStr);\r
153                         if (limit > 0) {\r
154                                 for (int i = 1; i <= limit; i++) {\r
155                                         preferencesOrderList.add(Preferences.VCF_PATH + i);\r
156                                         if (Boolean.valueOf(this.appLayer.getPreferences().getProperty(this.getName(), Preferences.OUTPUT_FILE_MODE))) {\r
157                                                 //filechooser\r
158                                                 preferencesOrderList.add(Preferences.OUTPUT_FILE + i);\r
159                                         } else {\r
160                                                 //suffix\r
161                                                 preferencesOrderList.add(Preferences.OUTPUT_SUFFIX + i);\r
162                                         }\r
163                                         \r
164                                         if (Boolean.valueOf(this.appLayer.getPreferences().getProperty(this.getName(), Preferences.EXTRA_OPTIONS))) {\r
165                                                 String useEncStringKey = Preferences.USE_ENCODED_VIDEO + i + Preferences.USE_ENCODED_VIDEO_2;\r
166                                                 preferencesOrderList.add(useEncStringKey);\r
167                                                 preferencesOrderList.add(Preferences.DELETE_ORIG_FILE + i);\r
168                                         }\r
169                                 }\r
170                         }\r
171                 } catch (NumberFormatException e) {\r
172                         throw new DemoRecorderException("Invalid value \"" + limitStr + "\" for setting " + Preferences.VCF_PER_JOB_LIMIT);\r
173                 }\r
174                 \r
175                 Object[] arr = preferencesOrderList.toArray();\r
176                 String[] stringArr = new String[arr.length];\r
177                 for (int i = 0; i < arr.length; i++) {\r
178                         stringArr[i] = (String) arr[i];\r
179                 }\r
180                 \r
181                 return stringArr;\r
182         }\r
183 \r
184         @Override\r
185         public String getName() {\r
186                 return PLUGIN_NAME;\r
187         }\r
188 \r
189         @Override\r
190         public boolean isEnabled() {\r
191                 this.checkAppLayer();\r
192                 String enabledString = this.appLayer.getPreferences().getProperty(this.getName(), Preferences.ENABLED);\r
193                 return Boolean.valueOf(enabledString);\r
194         }\r
195 \r
196         @Override\r
197         public void setApplicationLayer(DemoRecorderApplication appLayer) {\r
198                 this.appLayer = appLayer;\r
199         }\r
200         \r
201         private void checkAppLayer() {\r
202                 if (this.appLayer == null) {\r
203                         throw new DemoRecorderException("Error in plugin " + PLUGIN_NAME + "! Application layer not set!");\r
204                 }\r
205         }\r
206         \r
207         private void createPreferenceDefaultValues() {\r
208                 this.globalDefaultPreferences.setProperty(Preferences.ENABLED, "false");\r
209                 this.globalDefaultPreferences.setProperty(Preferences.VIRTUAL_DUB_BINARY_PATH, "filechooser");\r
210                 this.globalDefaultPreferences.setProperty(Preferences.VCF_PER_JOB_LIMIT, "1");\r
211                 this.globalDefaultPreferences.setProperty(Preferences.OUTPUT_FILE_MODE, "false");\r
212                 this.globalDefaultPreferences.setProperty(Preferences.EXTRA_OPTIONS, "false");\r
213         }\r
214         \r
215         private void doEncoding(RecordJob job, int vcfCounter) throws EncoderPluginException {\r
216                 boolean firstValidVCF = true;\r
217                 for (int i = 1; i <= vcfCounter; i++) {\r
218                         Properties jobSpecificSettings = job.getEncoderPluginSettings(this);\r
219                         String path = jobSpecificSettings.getProperty(Preferences.VCF_PATH + i);\r
220                         if (path != null) {\r
221                                 File vcfFile = new File(path);\r
222                                 if (vcfFile.exists()) {\r
223                                         if (Boolean.valueOf(this.appLayer.getPreferences().getProperty(this.getName(), Preferences.OUTPUT_FILE_MODE))) {\r
224                                                 //filechooser\r
225                                                 String outputPath = jobSpecificSettings.getProperty(Preferences.OUTPUT_FILE + i, "filechooser");\r
226                                                 if (outputPath == null || outputPath.equals("") || outputPath.equals("filechoose")) {\r
227                                                         //user has not yet selected a file\r
228                                                         continue;\r
229                                                 }\r
230                                         } else {\r
231                                                 //suffix\r
232                                                 String suffix = jobSpecificSettings.getProperty(Preferences.OUTPUT_SUFFIX + i);\r
233                                                 if (suffix == null || suffix.equals("")) {\r
234                                                         continue;\r
235                                                 }\r
236                                         }\r
237                                         BufferedWriter logWriter = this.getLogWriter(job.getJobName(), i);\r
238                                         this.executeVDub(job, i, firstValidVCF, logWriter);\r
239                                         firstValidVCF = false;\r
240                                 }\r
241                         }\r
242                 }\r
243         }\r
244         \r
245         private void executeVDub(RecordJob job, int index, boolean firstValidVCF, BufferedWriter logWriter) throws EncoderPluginException {\r
246                 String shellString = "";\r
247                 Properties jobSpecificSettings = job.getEncoderPluginSettings(this);\r
248                 File vcfFile = new File(jobSpecificSettings.getProperty(Preferences.VCF_PATH + index));\r
249                 File sourceFile = job.getActualVideoDestination();\r
250                 \r
251                 String vDubBinary = this.appLayer.getPreferences().getProperty(this.getName(), Preferences.VIRTUAL_DUB_BINARY_PATH);\r
252                 shellString += '"' + vDubBinary.trim() + '"';\r
253                 \r
254                 shellString += " /s " + '"' + vcfFile.getAbsolutePath() + '"';\r
255                 \r
256                 boolean clearJobControl = Boolean.valueOf(jobSpecificSettings.getProperty(Preferences.CLEAR_JOBCONTROL, "true"));\r
257                 if (clearJobControl && firstValidVCF) {\r
258                         shellString += " /c";\r
259                 }\r
260                 \r
261                 String outputFilePath = this.getOutputFilePath(job, index);\r
262                 File outputFile = new File(outputFilePath);\r
263                 shellString += " /p " + '"' + sourceFile.getAbsolutePath() + '"';\r
264                 shellString += " " + '"' + outputFilePath + '"';\r
265                 \r
266                 boolean renderOutput = Boolean.valueOf(jobSpecificSettings.getProperty(Preferences.RENDER_OUTPUT, "true"));\r
267                 if (renderOutput) {\r
268                         shellString += " /r";\r
269                 }\r
270                 \r
271                 shellString += " /x";\r
272                 \r
273                 try {\r
274                         logWriter.write("Executing commandline: " + shellString);\r
275                         logWriter.newLine();\r
276                         File vdubDir = new File(vDubBinary).getParentFile();\r
277                         Process vDubProc;\r
278                         vDubProc = Runtime.getRuntime().exec(shellString, null, vdubDir);\r
279                         vDubProc.getOutputStream();\r
280                         InputStreamReader isr = new InputStreamReader(vDubProc.getInputStream());\r
281                         BufferedReader bufferedInputStream = new BufferedReader(isr);\r
282                         String currentLine;\r
283                         while ((currentLine = bufferedInputStream.readLine()) != null) {\r
284                                 logWriter.write(currentLine);\r
285                                 logWriter.newLine();\r
286                         }\r
287                         InputStreamReader isrErr = new InputStreamReader(vDubProc.getErrorStream());\r
288                         BufferedReader bufferedInputStreamErr = new BufferedReader(isrErr);\r
289                         while ((currentLine = bufferedInputStreamErr.readLine()) != null) {\r
290                                 logWriter.write(currentLine);\r
291                                 logWriter.newLine();\r
292                         }\r
293                         logWriter.close();\r
294                         \r
295                 } catch (IOException e) {\r
296                         throw new EncoderPluginException("I/O Exception occurred when trying to execute the VDub binary or logging output", e);\r
297                 }\r
298                 \r
299                 //extra options: replace original video with encoded one, possibly delete original one\r
300                 if (Boolean.valueOf(this.appLayer.getPreferences().getProperty(this.getName(), Preferences.EXTRA_OPTIONS))) {\r
301                         String useEncStringKey = Preferences.USE_ENCODED_VIDEO + index + Preferences.USE_ENCODED_VIDEO_2;\r
302                         String useEncVideo = jobSpecificSettings.getProperty(useEncStringKey);\r
303                         File origFile = job.getActualVideoDestination();\r
304                         if (useEncVideo != null && Boolean.valueOf(useEncVideo)) {\r
305                                 job.setActualVideoDestination(outputFile);\r
306                         }\r
307                         \r
308                         String deleteOrigFile = jobSpecificSettings.getProperty(Preferences.DELETE_ORIG_FILE + index);\r
309                         if (deleteOrigFile != null && Boolean.valueOf(deleteOrigFile)) {\r
310                                 //only delete the original file if the encoded one exists:\r
311                                 if (outputFile.exists() && outputFile.length() > 0) {\r
312                                         origFile.delete();\r
313                                 }\r
314                         }\r
315                 }\r
316         }\r
317         \r
318         private String getOutputFilePath(RecordJob job, int index) {\r
319                 File sourceFile = job.getActualVideoDestination();\r
320                 String ext = DemoRecorderUtils.getFileExtension(sourceFile);\r
321                 String outputFilePath;\r
322                 Properties jobSpecificSettings = job.getEncoderPluginSettings(this);\r
323                 if (Boolean.valueOf(this.appLayer.getPreferences().getProperty(this.getName(), Preferences.OUTPUT_FILE_MODE))) {\r
324                         //filechooser\r
325                         outputFilePath = jobSpecificSettings.getProperty(Preferences.OUTPUT_FILE + index);\r
326                 } else {\r
327                         //suffix\r
328                         outputFilePath = sourceFile.getAbsolutePath();\r
329                         String suffix = jobSpecificSettings.getProperty(Preferences.OUTPUT_SUFFIX + index);\r
330                         int idx = outputFilePath.indexOf("." + ext);\r
331                         outputFilePath = outputFilePath.substring(0, idx);\r
332                         outputFilePath += suffix + "." + ext;\r
333                 }\r
334                 \r
335                 return outputFilePath;\r
336         }\r
337         \r
338         private BufferedWriter getLogWriter(String jobName, int vcfIndex) throws EncoderPluginException {\r
339                 File logDir = DemoRecorderUtils.computeLocalFile(DemoRecorderApplication.LOGS_DIRNAME, "");\r
340                 if (jobName == null || jobName.equals("")) {\r
341                         jobName = "unnamed_job";\r
342                 }\r
343                 String path = logDir.getAbsolutePath() + File.separator + PLUGIN_NAME + '_' + jobName + '_' + "vcf" + vcfIndex + ".log";\r
344                 File logFile = new File(path);\r
345                 if (!DemoRecorderUtils.attemptFileCreation(logFile)) {\r
346                         throw new EncoderPluginException("Could not create log file for VDub job at location: " + path);\r
347                 }\r
348                 try {\r
349                         FileWriter fileWriter = new FileWriter(logFile);\r
350                         return new BufferedWriter(fileWriter);\r
351                 } catch (IOException e) {\r
352                         throw new EncoderPluginException("Could not create log file for VDub job at location: " + path, e);\r
353                 }\r
354         }\r
355 }\r