1 package com.nexuiz.demorecorder.application.jobs; 2 3 import java.io.BufferedReader; 4 import java.io.File; 5 import java.io.IOException; 6 import java.io.InputStream; 7 import java.io.InputStreamReader; 8 import java.io.Serializable; 9 import java.util.ArrayList; 10 import java.util.HashMap; 11 import java.util.List; 12 import java.util.Map; 13 import java.util.Properties; 14 15 import com.nexuiz.demorecorder.application.DemoRecorderApplication; 16 import com.nexuiz.demorecorder.application.DemoRecorderException; 17 import com.nexuiz.demorecorder.application.DemoRecorderUtils; 18 import com.nexuiz.demorecorder.application.NDRPreferences; 19 import com.nexuiz.demorecorder.application.DemoRecorderApplication.Preferences; 20 import com.nexuiz.demorecorder.application.democutter.DemoCutter; 21 import com.nexuiz.demorecorder.application.democutter.DemoCutterException; 22 import com.nexuiz.demorecorder.application.plugins.EncoderPlugin; 23 import com.nexuiz.demorecorder.application.plugins.EncoderPluginException; 24 25 public class RecordJob implements Runnable, Serializable { 26 27 private static final long serialVersionUID = -4585637490345587912L; 28 29 public enum State { 30 WAITING, PROCESSING, ERROR, ERROR_PLUGIN, DONE 31 } 32 33 public static final String CUT_DEMO_FILE_SUFFIX = "_autocut"; 34 public static final String CUT_DEMO_CAPVIDEO_NAMEFORMAT_OVERRIDE = "autocap"; 35 public static final String CUT_DEMO_CAPVIDEO_NUMBER_OVERRIDE = "1234567"; 36 protected static final String[] VIDEO_FILE_ENDINGS = {"avi", "ogv"}; 37 38 private DemoRecorderApplication appLayer; 39 protected String jobName; 40 private int jobIndex; 41 protected File enginePath; 42 protected String engineParameters; 43 protected File demoFile; 44 protected String relativeDemoPath; 45 protected File dpVideoPath; 46 protected File videoDestination; 47 protected String executeBeforeCap; 48 protected String executeAfterCap; 49 protected float startSecond; 50 protected float endSecond; 51 protected State state = State.WAITING; 52 protected DemoRecorderException lastException = null; 53 54 /** 55 * Points to the actual final file, including possible suffixes, e.g. _copy1, and the actualy ending 56 */ 57 protected File actualVideoDestination = null; 58 /** 59 * Map that identifies the plug-in by its name (String) and maps to the plug-in's job-specific settings 60 */ 61 protected Map<String, Properties> encoderPluginSettings = new HashMap<String, Properties>(); 62 63 private List<File> cleanUpFiles = null; 64 RecordJob( DemoRecorderApplication appLayer, String jobName, int jobIndex, File enginePath, String engineParameters, File demoFile, String relativeDemoPath, File dpVideoPath, File videoDestination, String executeBeforeCap, String executeAfterCap, float startSecond, float endSecond )65 public RecordJob( 66 DemoRecorderApplication appLayer, 67 String jobName, 68 int jobIndex, 69 File enginePath, 70 String engineParameters, 71 File demoFile, 72 String relativeDemoPath, 73 File dpVideoPath, 74 File videoDestination, 75 String executeBeforeCap, 76 String executeAfterCap, 77 float startSecond, 78 float endSecond 79 ) { 80 this.appLayer = appLayer; 81 this.jobName = jobName; 82 this.jobIndex = jobIndex; 83 84 this.setEnginePath(enginePath); 85 this.setEngineParameters(engineParameters); 86 this.setDemoFile(demoFile); 87 this.setRelativeDemoPath(relativeDemoPath); 88 this.setDpVideoPath(dpVideoPath); 89 this.setVideoDestination(videoDestination); 90 this.setExecuteBeforeCap(executeBeforeCap); 91 this.setExecuteAfterCap(executeAfterCap); 92 this.setStartSecond(startSecond); 93 this.setEndSecond(endSecond); 94 } 95 RecordJob()96 public RecordJob(){} 97 98 /** 99 * Constructor that can be used by other classes such as job templates. Won't throw exceptions 100 * as it won't check the input for validity. 101 */ RecordJob( File enginePath, String engineParameters, File demoFile, String relativeDemoPath, File dpVideoPath, File videoDestination, String executeBeforeCap, String executeAfterCap, float startSecond, float endSecond )102 protected RecordJob( 103 File enginePath, 104 String engineParameters, 105 File demoFile, 106 String relativeDemoPath, 107 File dpVideoPath, 108 File videoDestination, 109 String executeBeforeCap, 110 String executeAfterCap, 111 float startSecond, 112 float endSecond 113 ) { 114 this.jobIndex = -1; 115 this.enginePath = enginePath; 116 this.engineParameters = engineParameters; 117 this.demoFile = demoFile; 118 this.relativeDemoPath = relativeDemoPath; 119 this.dpVideoPath = dpVideoPath; 120 this.videoDestination = videoDestination; 121 this.executeBeforeCap = executeBeforeCap; 122 this.executeAfterCap = executeAfterCap; 123 this.startSecond = startSecond; 124 this.endSecond = endSecond; 125 } 126 execute()127 public void execute() { 128 if (this.state == State.PROCESSING) { 129 return; 130 } 131 boolean errorOccurred = false; 132 this.setState(State.PROCESSING); 133 this.appLayer.fireUserInterfaceUpdate(this); 134 cleanUpFiles = new ArrayList<File>(); 135 136 File cutDemo = computeCutDemoFile(); 137 cutDemo.delete(); //delete possibly old cutDemoFile 138 139 EncoderPlugin recentEncoder = null; 140 141 try { 142 this.cutDemo(cutDemo); 143 this.removeOldAutocaps(); 144 this.recordClip(cutDemo); 145 this.moveRecordedClip(); 146 for (EncoderPlugin plugin : this.appLayer.getEncoderPlugins()) { 147 recentEncoder = plugin; 148 plugin.executeEncoder(this); 149 } 150 } catch (DemoRecorderException e) { 151 errorOccurred = true; 152 this.lastException = e; 153 this.setState(State.ERROR); 154 } catch (EncoderPluginException e) { 155 errorOccurred = true; 156 this.lastException = new DemoRecorderException("Encoder plug-in " + recentEncoder.getName() + " failed: " 157 + e.getMessage(), e); 158 this.setState(State.ERROR_PLUGIN); 159 } catch (Exception e) { 160 errorOccurred = true; 161 this.lastException = new DemoRecorderException("Executing job failed, click on details for more info", e); 162 } finally { 163 NDRPreferences preferences = this.appLayer.getPreferences(); 164 if (!Boolean.valueOf(preferences.getProperty(NDRPreferences.MAIN_APPLICATION, Preferences.DO_NOT_DELETE_CUT_DEMOS))) { 165 cleanUpFiles.add(cutDemo); 166 } 167 if (!errorOccurred) { 168 this.setState(State.DONE); 169 } 170 this.cleanUpFiles(); 171 this.appLayer.fireUserInterfaceUpdate(this); 172 this.appLayer.saveJobQueue(); 173 } 174 } 175 176 /** 177 * Will execute just the specified encoder plug-in on an already "done" job. 178 * @param pluginName 179 */ executePlugin(EncoderPlugin plugin)180 public void executePlugin(EncoderPlugin plugin) { 181 if (this.getState() != State.DONE) { 182 return; 183 } 184 this.setState(State.PROCESSING); 185 this.appLayer.fireUserInterfaceUpdate(this); 186 187 try { 188 plugin.executeEncoder(this); 189 this.setState(State.DONE); 190 } catch (EncoderPluginException e) { 191 this.lastException = new DemoRecorderException("Encoder plug-in " + plugin.getName() + " failed: " 192 + e.getMessage(), e); 193 this.setState(State.ERROR_PLUGIN); 194 } 195 196 this.appLayer.fireUserInterfaceUpdate(this); 197 } 198 cleanUpFiles()199 private void cleanUpFiles() { 200 try { 201 for (File f : this.cleanUpFiles) { 202 f.delete(); 203 } 204 } catch (Exception e) {} 205 206 } 207 moveRecordedClip()208 private void moveRecordedClip() { 209 //1. Figure out whether the file is .avi or .ogv 210 File sourceFile = null; 211 for (String videoExtension : VIDEO_FILE_ENDINGS) { 212 String fileString = this.dpVideoPath.getAbsolutePath() + File.separator + CUT_DEMO_CAPVIDEO_NAMEFORMAT_OVERRIDE 213 + CUT_DEMO_CAPVIDEO_NUMBER_OVERRIDE + "." + videoExtension; 214 File videoFile = new File(fileString); 215 if (videoFile.exists()) { 216 sourceFile = videoFile; 217 break; 218 } 219 } 220 221 if (sourceFile == null) { 222 String p = this.dpVideoPath.getAbsolutePath() + File.separator + CUT_DEMO_CAPVIDEO_NAMEFORMAT_OVERRIDE 223 + CUT_DEMO_CAPVIDEO_NUMBER_OVERRIDE; 224 throw new DemoRecorderException("Could not locate the expected video file being generated by Nexuiz (should have been at " 225 + p + ".avi/.ogv"); 226 } 227 cleanUpFiles.add(sourceFile); 228 229 File destinationFile = null; 230 NDRPreferences preferences = this.appLayer.getPreferences(); 231 String sourceFileExtension = DemoRecorderUtils.getFileExtension(sourceFile); 232 String destinationFilePath = this.videoDestination + "." + sourceFileExtension; 233 destinationFile = new File(destinationFilePath); 234 if (destinationFile.exists()) { 235 if (Boolean.valueOf(preferences.getProperty(NDRPreferences.MAIN_APPLICATION, Preferences.OVERWRITE_VIDEO_FILE))) { 236 if (!destinationFile.delete()) { 237 throw new DemoRecorderException("Could not delete the existing video destinatin file " + destinationFile.getAbsolutePath() 238 + " (application setting to overwrite existing video files is enabled!)"); 239 } 240 } else { 241 destinationFilePath = this.videoDestination + "_copy" + this.getVideoDestinationCopyNr(sourceFileExtension) + "." + sourceFileExtension; 242 destinationFile = new File(destinationFilePath); 243 } 244 } 245 246 //finally move the file 247 if (!sourceFile.renameTo(destinationFile)) { 248 cleanUpFiles.add(destinationFile); 249 throw new DemoRecorderException("Could not move the video file from " + sourceFile.getAbsolutePath() 250 + " to " + destinationFile.getAbsolutePath()); 251 } 252 253 this.actualVideoDestination = destinationFile; 254 } 255 256 /** 257 * As destination video files, e.g. "test"[.avi] can already exist, we have to save the 258 * the video file to a file name such as test_copy1 or test_copy2. 259 * This function will figure out what the number (1, 2....) is. 260 * @return 261 */ getVideoDestinationCopyNr(String sourceFileExtension)262 private int getVideoDestinationCopyNr(String sourceFileExtension) { 263 int i = 1; 264 File lastFile; 265 while (true) { 266 lastFile = new File(this.videoDestination + "_copy" + i + "." + sourceFileExtension); 267 if (!lastFile.exists()) { 268 break; 269 } 270 271 i++; 272 } 273 return i; 274 } 275 computeCutDemoFile()276 private File computeCutDemoFile() { 277 String origFileString = this.demoFile.getAbsolutePath(); 278 int lastIndex = origFileString.lastIndexOf(File.separator); 279 String autoDemoFileName = origFileString.substring(lastIndex+1, origFileString.length()); 280 //strip .dem ending 281 autoDemoFileName = autoDemoFileName.substring(0, autoDemoFileName.length()-4); 282 autoDemoFileName = autoDemoFileName + CUT_DEMO_FILE_SUFFIX + ".dem"; 283 String finalString = origFileString.substring(0, lastIndex) + File.separator + autoDemoFileName; 284 File f = new File(finalString); 285 286 return f; 287 } 288 cutDemo(File cutDemo)289 private void cutDemo(File cutDemo) { 290 String injectAtStart = ""; 291 String injectBeforeCap = ""; 292 String injectAfterCap = ""; 293 294 NDRPreferences preferences = this.appLayer.getPreferences(); 295 if (Boolean.valueOf(preferences.getProperty(NDRPreferences.MAIN_APPLICATION, Preferences.DISABLE_RENDERING))) { 296 injectAtStart += "r_render 0;"; 297 injectBeforeCap += "r_render 1;"; 298 } 299 if (Boolean.valueOf(preferences.getProperty(NDRPreferences.MAIN_APPLICATION, Preferences.DISABLE_SOUND))) { 300 injectAtStart += "set _volume $volume;volume 0;"; 301 injectBeforeCap += "set volume $_volume;"; 302 } 303 injectBeforeCap += this.executeBeforeCap + "\n"; 304 injectBeforeCap += "set _cl_capturevideo_nameformat $cl_capturevideo_nameformat;set _cl_capturevideo_number $cl_capturevideo_number;"; 305 injectBeforeCap += "cl_capturevideo_nameformat " + CUT_DEMO_CAPVIDEO_NAMEFORMAT_OVERRIDE + ";"; 306 injectBeforeCap += "cl_capturevideo_number " + CUT_DEMO_CAPVIDEO_NUMBER_OVERRIDE + ";"; 307 308 injectAfterCap += this.executeAfterCap + "\n"; 309 injectAfterCap += "cl_capturevideo_nameformat $_cl_capturevideo_nameformat;cl_capturevideo_number $_cl_capturevideo_number;"; 310 311 312 DemoCutter cutter = new DemoCutter(); 313 int fwdSpeedFirstStage, fwdSpeedSecondStage; 314 try { 315 fwdSpeedFirstStage = Integer.parseInt(preferences.getProperty(NDRPreferences.MAIN_APPLICATION, Preferences.FFW_SPEED_FIRST_STAGE)); 316 fwdSpeedSecondStage = Integer.parseInt(preferences.getProperty(NDRPreferences.MAIN_APPLICATION, Preferences.FFW_SPEED_SECOND_STAGE)); 317 } catch (NumberFormatException e) { 318 throw new DemoRecorderException("Make sure that you specified valid numbers for the settings " 319 + Preferences.FFW_SPEED_FIRST_STAGE + " and " + Preferences.FFW_SPEED_SECOND_STAGE, e); 320 } 321 322 try { 323 cutter.cutDemo( 324 this.demoFile, 325 cutDemo, 326 this.startSecond, 327 this.endSecond, 328 injectAtStart, 329 injectBeforeCap, 330 injectAfterCap, 331 fwdSpeedFirstStage, 332 fwdSpeedSecondStage 333 ); 334 } catch (DemoCutterException e) { 335 throw new DemoRecorderException("Error occurred while trying to cut the demo: " + e.getMessage(), e); 336 } 337 338 } 339 removeOldAutocaps()340 private void removeOldAutocaps() { 341 for (String videoExtension : VIDEO_FILE_ENDINGS) { 342 String fileString = this.dpVideoPath.getAbsolutePath() + File.separator + CUT_DEMO_CAPVIDEO_NAMEFORMAT_OVERRIDE 343 + CUT_DEMO_CAPVIDEO_NUMBER_OVERRIDE + "." + videoExtension; 344 File videoFile = new File(fileString); 345 cleanUpFiles.add(videoFile); 346 if (videoFile.exists()) { 347 if (!videoFile.delete()) { 348 throw new DemoRecorderException("Could not delete old obsolete video file " + fileString); 349 } 350 } 351 } 352 } 353 recordClip(File cutDemo)354 private void recordClip(File cutDemo) { 355 Process nexProc; 356 String demoFileName = DemoRecorderUtils.getJustFileNameOfPath(cutDemo); 357 String execPath = this.enginePath.getAbsolutePath() + " " + this.engineParameters + " -demo " 358 + this.relativeDemoPath + "/" + demoFileName; 359 File engineDir = this.enginePath.getParentFile(); 360 try { 361 nexProc = Runtime.getRuntime().exec(execPath, null, engineDir); 362 nexProc.getErrorStream(); 363 nexProc.getOutputStream(); 364 InputStream is = nexProc.getInputStream(); 365 InputStreamReader isr = new InputStreamReader(is); 366 BufferedReader br = new BufferedReader(isr); 367 while (br.readLine() != null) { 368 //System.out.println(line); 369 } 370 } catch (IOException e) { 371 throw new DemoRecorderException("I/O Exception occurred when trying to execute the Nexuiz binary", e); 372 } 373 } 374 run()375 public void run() { 376 this.execute(); 377 } 378 setAppLayer(DemoRecorderApplication appLayer)379 public void setAppLayer(DemoRecorderApplication appLayer) { 380 this.appLayer = appLayer; 381 } 382 getJobIndex()383 public int getJobIndex() { 384 return jobIndex; 385 } 386 getEnginePath()387 public File getEnginePath() { 388 return enginePath; 389 } 390 setEnginePath(File enginePath)391 public void setEnginePath(File enginePath) { 392 this.checkForProcessingState(); 393 if (enginePath == null || !enginePath.exists()) { 394 throw new DemoRecorderException("Could not locate engine binary!"); 395 } 396 if (!enginePath.canExecute()) { 397 throw new DemoRecorderException("The file you specified is not executable!"); 398 } 399 this.enginePath = enginePath.getAbsoluteFile(); 400 } 401 getEngineParameters()402 public String getEngineParameters() { 403 return engineParameters; 404 } 405 setEngineParameters(String engineParameters)406 public void setEngineParameters(String engineParameters) { 407 this.checkForProcessingState(); 408 if (engineParameters == null) { 409 engineParameters = ""; 410 } 411 this.engineParameters = engineParameters.trim(); 412 } 413 getDemoFile()414 public File getDemoFile() { 415 return demoFile; 416 } 417 setDemoFile(File demoFile)418 public void setDemoFile(File demoFile) { 419 this.checkForProcessingState(); 420 if (demoFile == null) { 421 throw new DemoRecorderException("Could not locate demo file!"); 422 } 423 if (!demoFile.exists()) { 424 throw new DemoRecorderException("Could not locate demo file!: " + demoFile.getAbsolutePath()); 425 } 426 if (!doReadWriteTest(demoFile.getParentFile())) { 427 throw new DemoRecorderException("The directory you specified for the demo to be recorded is not writable!"); 428 } 429 if (!demoFile.getAbsolutePath().endsWith(".dem")) { 430 throw new DemoRecorderException("The demo file you specified must have the ending .dem"); 431 } 432 433 this.demoFile = demoFile.getAbsoluteFile(); 434 } 435 getRelativeDemoPath()436 public String getRelativeDemoPath() { 437 return relativeDemoPath; 438 } 439 setRelativeDemoPath(String relativeDemoPath)440 public void setRelativeDemoPath(String relativeDemoPath) { 441 this.checkForProcessingState(); 442 if (relativeDemoPath == null) { 443 relativeDemoPath = ""; 444 } 445 446 //get rid of possible slashes 447 while (relativeDemoPath.startsWith("/") || relativeDemoPath.startsWith("\\")) { 448 relativeDemoPath = relativeDemoPath.substring(1, relativeDemoPath.length()); 449 } 450 while (relativeDemoPath.endsWith("/") || relativeDemoPath.endsWith("\\")) { 451 relativeDemoPath = relativeDemoPath.substring(0, relativeDemoPath.length() - 1); 452 } 453 454 this.relativeDemoPath = relativeDemoPath.trim(); 455 } 456 getDpVideoPath()457 public File getDpVideoPath() { 458 return dpVideoPath; 459 } 460 setDpVideoPath(File dpVideoPath)461 public void setDpVideoPath(File dpVideoPath) { 462 this.checkForProcessingState(); 463 if (dpVideoPath == null || !dpVideoPath.isDirectory()) { 464 throw new DemoRecorderException("Could not locate the specified DPVideo directory!"); 465 } 466 467 if (!this.doReadWriteTest(dpVideoPath)) { 468 throw new DemoRecorderException("The DPVideo directory is not writable! It needs to be writable so that the file can be moved to its new location"); 469 } 470 this.dpVideoPath = dpVideoPath.getAbsoluteFile(); 471 } 472 getVideoDestination()473 public File getVideoDestination() { 474 return videoDestination; 475 } 476 setVideoDestination(File videoDestination)477 public void setVideoDestination(File videoDestination) { 478 this.checkForProcessingState(); 479 //keep in mind, the parameter videoDestination points to the final avi/ogg file w/o extension! 480 if (videoDestination == null || !videoDestination.getParentFile().isDirectory()) { 481 throw new DemoRecorderException("Could not locate the specified video destination"); 482 } 483 484 if (!this.doReadWriteTest(videoDestination.getParentFile())) { 485 throw new DemoRecorderException("The video destination directory is not writable! It needs to be writable so that the file can be moved to its new location"); 486 } 487 488 this.videoDestination = videoDestination.getAbsoluteFile(); 489 } 490 getExecuteBeforeCap()491 public String getExecuteBeforeCap() { 492 return executeBeforeCap; 493 } 494 setExecuteBeforeCap(String executeBeforeCap)495 public void setExecuteBeforeCap(String executeBeforeCap) { 496 this.checkForProcessingState(); 497 if (executeBeforeCap == null) { 498 executeBeforeCap = ""; 499 } 500 executeBeforeCap = executeBeforeCap.trim(); 501 while (executeBeforeCap.endsWith(";")) { 502 executeBeforeCap = executeBeforeCap.substring(0, executeBeforeCap.length()-1); 503 } 504 this.executeBeforeCap = executeBeforeCap; 505 } 506 getExecuteAfterCap()507 public String getExecuteAfterCap() { 508 return executeAfterCap; 509 } 510 setExecuteAfterCap(String executeAfterCap)511 public void setExecuteAfterCap(String executeAfterCap) { 512 this.checkForProcessingState(); 513 if (executeAfterCap == null) { 514 executeAfterCap = ""; 515 } 516 executeAfterCap = executeAfterCap.trim(); 517 while (executeAfterCap.endsWith(";")) { 518 executeAfterCap = executeAfterCap.substring(0, executeAfterCap.length()-1); 519 } 520 if (executeAfterCap.contains("cl_capturevideo_number") || executeAfterCap.contains("cl_capturevideo_nameformat")) { 521 throw new DemoRecorderException("Execute after String cannot contain cl_capturevideo_number or _nameformat changes!"); 522 } 523 this.executeAfterCap = executeAfterCap; 524 } 525 getStartSecond()526 public float getStartSecond() { 527 return startSecond; 528 } 529 setStartSecond(float startSecond)530 public void setStartSecond(float startSecond) { 531 this.checkForProcessingState(); 532 if (startSecond < 0) { 533 throw new DemoRecorderException("Start second cannot be < 0"); 534 } 535 this.startSecond = startSecond; 536 } 537 getEndSecond()538 public float getEndSecond() { 539 return endSecond; 540 } 541 setEndSecond(float endSecond)542 public void setEndSecond(float endSecond) { 543 this.checkForProcessingState(); 544 if (endSecond < this.startSecond) { 545 throw new DemoRecorderException("End second cannot be < start second"); 546 } 547 this.endSecond = endSecond; 548 } 549 getState()550 public State getState() { 551 return state; 552 } 553 setState(State state)554 public void setState(State state) { 555 this.state = state; 556 this.appLayer.fireUserInterfaceUpdate(this); 557 } 558 getJobName()559 public String getJobName() { 560 if (this.jobName == null || this.jobName.equals("")) { 561 return "Job " + this.jobIndex; 562 } 563 return this.jobName; 564 } 565 setJobName(String jobName)566 public void setJobName(String jobName) { 567 if (jobName == null || jobName.equals("")) { 568 this.jobIndex = appLayer.getNewJobIndex(); 569 this.jobName = "Job " + this.jobIndex; 570 } else { 571 this.jobName = jobName; 572 } 573 } 574 getLastException()575 public DemoRecorderException getLastException() { 576 return lastException; 577 } 578 579 /** 580 * Tests whether the given directory is writable by creating a file in there and deleting 581 * it again. 582 * @param directory 583 * @return true if directory is writable 584 */ doReadWriteTest(File directory)585 protected boolean doReadWriteTest(File directory) { 586 boolean writable = false; 587 String fileName = "tmp." + Math.random()*10000 + ".dat"; 588 File tempFile = new File(directory, fileName); 589 try { 590 writable = tempFile.createNewFile(); 591 if (writable) { 592 tempFile.delete(); 593 } 594 } catch (IOException e) { 595 writable = false; 596 } 597 return writable; 598 } 599 checkForProcessingState()600 private void checkForProcessingState() { 601 if (this.state == State.PROCESSING) { 602 throw new DemoRecorderException("Cannot modify this job while it is processing!"); 603 } 604 } 605 getEncoderPluginSettings(EncoderPlugin plugin)606 public Properties getEncoderPluginSettings(EncoderPlugin plugin) { 607 if (this.encoderPluginSettings.containsKey(plugin.getName())) { 608 return this.encoderPluginSettings.get(plugin.getName()); 609 } else { 610 return new Properties(); 611 } 612 } 613 setEncoderPluginSetting(String pluginName, String pluginSettingKey, String value)614 public void setEncoderPluginSetting(String pluginName, String pluginSettingKey, String value) { 615 Properties p = this.encoderPluginSettings.get(pluginName); 616 if (p == null) { 617 p = new Properties(); 618 this.encoderPluginSettings.put(pluginName, p); 619 } 620 621 p.put(pluginSettingKey, value); 622 } 623 getEncoderPluginSettings()624 public Map<String, Properties> getEncoderPluginSettings() { 625 return encoderPluginSettings; 626 } 627 setEncoderPluginSettings(Map<String, Properties> encoderPluginSettings)628 public void setEncoderPluginSettings(Map<String, Properties> encoderPluginSettings) { 629 this.encoderPluginSettings = encoderPluginSettings; 630 } 631 getActualVideoDestination()632 public File getActualVideoDestination() { 633 return actualVideoDestination; 634 } 635 setActualVideoDestination(File actualVideoDestination)636 public void setActualVideoDestination(File actualVideoDestination) { 637 this.actualVideoDestination = actualVideoDestination; 638 } 639 } 640