1 package com.hammurapi.jcapture; 2 3 import java.awt.AWTException; 4 import java.awt.Component; 5 import java.awt.MouseInfo; 6 import java.awt.Point; 7 import java.awt.Rectangle; 8 import java.awt.Robot; 9 import java.awt.image.BufferedImage; 10 import java.io.IOException; 11 import java.nio.channels.FileChannel; 12 import java.util.ArrayList; 13 import java.util.Collections; 14 import java.util.Comparator; 15 import java.util.Iterator; 16 import java.util.List; 17 import java.util.Properties; 18 import java.util.ServiceLoader; 19 import java.util.concurrent.ExecutorService; 20 import java.util.concurrent.Future; 21 22 import javax.sound.sampled.AudioFormat; 23 24 public class CaptureConfig implements VideoEncoder.Config { 25 26 private static final String MP3_COMMAND_PROPERTY = "mp3command"; 27 private static final String TOOL_BAR_PROPERTY = "toolBar"; 28 private static final String SPEED_SCALE_PROPERTY = "speedScale"; 29 private static final String SOUND_PROPERTY = "sound"; 30 private static final String SCREEN_SCALE_PROPERTY = "screenScale"; 31 private static final String REMOVE_INACTIVITY_PROPERTY = "removeInactivity"; 32 private static final String PLAY_PROPERTY = "play"; 33 private static final String MOUSE_PROPERTY = "mouse"; 34 private static final String MIXER_NAME_PROPERTY = "mixerName"; 35 private static final String LOOP_PROPERTY = "loop"; 36 private static final String INACTIVITY_INTERVAL_PROPERTY = "inactivityInterval"; 37 private static final String IMAGE_FORMAT_PROPERTY = "imageFormat"; 38 private static final String FRAMES_PER_SECOND_PROPERTY = "framesPerSecond"; 39 private static final String BORDER_PROPERTY = "border"; 40 private static final String RECORDING_RECTANGLE_PROPERTY = "recordingRectangle"; 41 private static final String ENCODER_NAME_PROPERTY = "encoderName"; 42 private static final String AUDIO_FORMAT_SAMPLE_SIZE_PROPERTY = "audioFormat.sampleSize"; 43 private static final String AUDIO_FORMAT_SAMPLE_RATE_PROPERTY = "audioFormat.sampleRate"; 44 private static final String AUDIO_FORMAT_CHANNELS_PROPERTY = "audioFormat.channels"; 45 private AudioFormat audioFormat = new AudioFormat(22050.0F, 16, 1, true, false);; 46 private String mixerName; 47 private float framesPerSecond = 10.0f; 48 private double screenScale = 1.0; 49 private float speedScale = 1.0f; 50 private boolean removeInactivity; 51 private double inactivityInterval = 0.7; 52 private Component parentComponent; 53 private Rectangle recordingRectangle; 54 private boolean border = true; 55 private boolean toolBar = true; 56 private Robot robot; 57 private String imageFormat = "PNG"; 58 private boolean sound = true; 59 private boolean mouse = true; 60 private boolean loop = true; 61 private boolean play = false; 62 private VideoEncoder encoder; 63 private int grabRange = 3; 64 private ExecutorService backgroundProcessor; 65 private String mp3command; 66 getMp3command()67 public String getMp3command() { 68 return mp3command; 69 } 70 setMp3command(String mp3command)71 public void setMp3command(String mp3command) { 72 this.mp3command = mp3command; 73 } 74 getGrabRange()75 public int getGrabRange() { 76 return grabRange; 77 } 78 getBackgroundProcessor()79 public ExecutorService getBackgroundProcessor() { 80 return backgroundProcessor; 81 } 82 setBackgroundProcessor(ExecutorService backgroundProcessor)83 public void setBackgroundProcessor(ExecutorService backgroundProcessor) { 84 this.backgroundProcessor = backgroundProcessor; 85 } 86 setGrabRange(int grabRange)87 public void setGrabRange(int grabRange) { 88 this.grabRange = grabRange; 89 } 90 getEncoder()91 public VideoEncoder getEncoder() { 92 return encoder; 93 } 94 setEncoder(VideoEncoder encoder)95 public void setEncoder(VideoEncoder encoder) { 96 this.encoder = encoder; 97 } 98 isLoop()99 public boolean isLoop() { 100 return loop; 101 } 102 setLoop(boolean loop)103 public void setLoop(boolean loop) { 104 this.loop = loop; 105 } 106 isPlay()107 public boolean isPlay() { 108 return play; 109 } 110 setPlay(boolean play)111 public void setPlay(boolean play) { 112 this.play = play; 113 } 114 isSound()115 public boolean isSound() { 116 return sound; 117 } 118 setSound(boolean sound)119 public void setSound(boolean sound) { 120 this.sound = sound; 121 } 122 isMouse()123 public boolean isMouse() { 124 return mouse; 125 } 126 setMouse(boolean mouse)127 public void setMouse(boolean mouse) { 128 this.mouse = mouse; 129 } 130 getImageFormat()131 public String getImageFormat() { 132 return imageFormat; 133 } 134 setImageFormat(String imageFormat)135 public void setImageFormat(String imageFormat) { 136 this.imageFormat = imageFormat; 137 } 138 CaptureConfig()139 public CaptureConfig() throws AWTException { 140 robot = new Robot(); 141 142 ServiceLoader<VideoEncoder> sl = ServiceLoader.load(VideoEncoder.class); 143 List<VideoEncoder> accumulator = new ArrayList<VideoEncoder>(); 144 Iterator<VideoEncoder> vit = sl.iterator(); 145 while (vit.hasNext()) { 146 accumulator.add(vit.next()); 147 } 148 149 Collections.sort(accumulator, new Comparator<VideoEncoder>() { 150 151 @Override 152 public int compare(VideoEncoder o1, VideoEncoder o2) { 153 return o1.toString().compareTo(o2.toString()); 154 } 155 156 }); 157 158 encoders = Collections.unmodifiableList(accumulator); 159 if (encoder==null && !encoders.isEmpty()) { 160 encoder = encoders.get(0); 161 } 162 163 } 164 165 /** 166 * Submits screenshot for processing in a background thread. 167 * @param task 168 * @return 169 */ submit(ScreenShot task)170 public Future<ScreenShot> submit(ScreenShot task) { 171 return backgroundProcessor.submit(task); 172 } 173 getRobot()174 public Robot getRobot() { 175 return robot; 176 } 177 createScreenShot(ScreenShot prev, FileChannel imageChannel)178 public ScreenShot createScreenShot(ScreenShot prev, FileChannel imageChannel) throws IOException { 179 BufferedImage image = robot.createScreenCapture(recordingRectangle); 180 Point mouseLocation = MouseInfo.getPointerInfo().getLocation(); 181 if (mouse && recordingRectangle.contains(mouseLocation)) { 182 mouseLocation.move(mouseLocation.x-recordingRectangle.x, mouseLocation.y-recordingRectangle.y); 183 } else { 184 mouseLocation = null; 185 } 186 return new ScreenShot( 187 image, 188 mouseLocation, 189 prev, 190 System.currentTimeMillis(), 191 grabRange, 192 isTransparencySupported(), 193 border, 194 getScreenScale(), 195 imageChannel, 196 getImageFormat()); 197 } 198 isTransparencySupported()199 public boolean isTransparencySupported() { 200 return !"jpeg".equalsIgnoreCase(getImageFormat()) 201 && !"jpg".equalsIgnoreCase(getImageFormat()); 202 } 203 isToolBar()204 public boolean isToolBar() { 205 return toolBar; 206 } setToolBar(boolean toolBar)207 public void setToolBar(boolean toolBar) { 208 this.toolBar = toolBar; 209 } isBorder()210 public boolean isBorder() { 211 return border; 212 } setBorder(boolean border)213 public void setBorder(boolean border) { 214 this.border = border; 215 } getRecordingRectangle()216 public Rectangle getRecordingRectangle() { 217 return recordingRectangle; 218 } setRecordingRectangle(Rectangle recordingRectangle)219 public Properties setRecordingRectangle(Rectangle recordingRectangle) { 220 Rectangle oldValue = this.recordingRectangle; 221 this.recordingRectangle = recordingRectangle; 222 if (this.recordingRectangle!=null && !this.recordingRectangle.equals(oldValue)) { 223 return store(); 224 } 225 return null; 226 } getAudioFormat()227 public AudioFormat getAudioFormat() { 228 return audioFormat; 229 } setAudioFormat(AudioFormat audioFormat)230 public void setAudioFormat(AudioFormat audioFormat) { 231 this.audioFormat = audioFormat; 232 } getMixerName()233 public String getMixerName() { 234 return mixerName; 235 } setMixerName(String mixerName)236 public void setMixerName(String mixerName) { 237 this.mixerName = mixerName; 238 } getFramesPerSecond()239 public float getFramesPerSecond() { 240 return framesPerSecond; 241 } setFramesPerSecond(float framesPerSecond)242 public void setFramesPerSecond(float framesPerSecond) { 243 this.framesPerSecond = framesPerSecond; 244 } getScreenScale()245 public double getScreenScale() { 246 return screenScale; 247 } setScreenScale(double screenScale)248 public void setScreenScale(double screenScale) { 249 this.screenScale = screenScale; 250 } getSpeedScale()251 public float getSpeedScale() { 252 return speedScale; 253 } setSpeedScale(float speedScale)254 public void setSpeedScale(float speedScale) { 255 this.speedScale = speedScale; 256 } isRemoveInactivity()257 public boolean isRemoveInactivity() { 258 return removeInactivity; 259 } setRemoveInactivity(boolean removeInactivity)260 public void setRemoveInactivity(boolean removeInactivity) { 261 this.removeInactivity = removeInactivity; 262 } getInactivityInterval()263 public double getInactivityInterval() { 264 return inactivityInterval; 265 } setInactivityInterval(double inactivityInterval)266 public void setInactivityInterval(double inactivityInterval) { 267 this.inactivityInterval = inactivityInterval; 268 } getParentComponent()269 public Component getParentComponent() { 270 return parentComponent; 271 } setParentComponent(Component parentComponent)272 public void setParentComponent(Component parentComponent) { 273 this.parentComponent = parentComponent; 274 } 275 load(Properties properties)276 void load(Properties properties) { 277 if (properties!=null) { 278 try { 279 if (properties.containsKey(AUDIO_FORMAT_CHANNELS_PROPERTY)) { 280 audioFormat = new AudioFormat( 281 Float.parseFloat(properties.getProperty(AUDIO_FORMAT_SAMPLE_RATE_PROPERTY, String.valueOf(audioFormat.getSampleRate()))), 282 Integer.parseInt(properties.getProperty(AUDIO_FORMAT_SAMPLE_SIZE_PROPERTY, String.valueOf(audioFormat.getSampleSizeInBits()))), 283 Integer.parseInt(properties.getProperty(AUDIO_FORMAT_CHANNELS_PROPERTY, String.valueOf(audioFormat.getChannels()))), 284 true, false); 285 } 286 287 border=Boolean.parseBoolean(properties.getProperty(BORDER_PROPERTY, String.valueOf(border))); 288 framesPerSecond=Float.parseFloat(properties.getProperty(FRAMES_PER_SECOND_PROPERTY, String.valueOf(framesPerSecond))); 289 imageFormat=properties.getProperty(IMAGE_FORMAT_PROPERTY, String.valueOf(imageFormat)); 290 inactivityInterval=Double.parseDouble(properties.getProperty(INACTIVITY_INTERVAL_PROPERTY, String.valueOf(inactivityInterval))); 291 loop=Boolean.parseBoolean(properties.getProperty(LOOP_PROPERTY, String.valueOf(loop))); 292 mixerName=properties.getProperty(MIXER_NAME_PROPERTY, String.valueOf(mixerName)); 293 mouse=Boolean.parseBoolean(properties.getProperty(MOUSE_PROPERTY, String.valueOf(mouse))); 294 play=Boolean.parseBoolean(properties.getProperty(PLAY_PROPERTY, String.valueOf(play))); 295 removeInactivity=Boolean.parseBoolean(properties.getProperty(REMOVE_INACTIVITY_PROPERTY, String.valueOf(removeInactivity))); 296 screenScale=Double.parseDouble(properties.getProperty(SCREEN_SCALE_PROPERTY, String.valueOf(screenScale))); 297 sound=Boolean.parseBoolean(properties.getProperty(SOUND_PROPERTY, String.valueOf(sound))); 298 speedScale=Float.parseFloat(properties.getProperty(SPEED_SCALE_PROPERTY, String.valueOf(speedScale))); 299 toolBar=Boolean.parseBoolean(properties.getProperty(TOOL_BAR_PROPERTY, String.valueOf(toolBar))); 300 mp3command=properties.getProperty(MP3_COMMAND_PROPERTY); 301 encoder = null; 302 String encoderName = properties.getProperty(ENCODER_NAME_PROPERTY); 303 if (encoderName!=null) { 304 for (VideoEncoder candidate: getEncoders()) { 305 if (encoderName.equals(candidate.toString())) { 306 encoder = candidate; 307 break; 308 } 309 } 310 } 311 if (encoder==null && !getEncoders().isEmpty()) { 312 encoder = getEncoders().get(0); 313 } 314 315 String rr = properties.getProperty(RECORDING_RECTANGLE_PROPERTY); 316 if (rr!=null && rr.trim().length()>0) { 317 String[] dims = rr.split(";"); 318 recordingRectangle = new Rectangle(Integer.parseInt(dims[0]), Integer.parseInt(dims[1]), Integer.parseInt(dims[2]), Integer.parseInt(dims[3])); 319 } 320 321 } catch (Exception e) { 322 e.printStackTrace(); 323 } 324 } 325 } 326 327 private List<VideoEncoder> encoders; 328 329 /** 330 * @return array of available encoders. 331 */ getEncoders()332 public List<VideoEncoder> getEncoders() { 333 return encoders; 334 } 335 store()336 Properties store() { 337 Properties properties = new Properties(); 338 if (audioFormat!=null) { 339 properties.setProperty(AUDIO_FORMAT_CHANNELS_PROPERTY, String.valueOf(audioFormat.getChannels())); 340 properties.setProperty(AUDIO_FORMAT_SAMPLE_RATE_PROPERTY, String.valueOf(audioFormat.getSampleRate())); 341 properties.setProperty(AUDIO_FORMAT_SAMPLE_SIZE_PROPERTY, String.valueOf(audioFormat.getSampleSizeInBits())); 342 } 343 properties.setProperty(BORDER_PROPERTY, String.valueOf(border)); 344 properties.setProperty(FRAMES_PER_SECOND_PROPERTY, String.valueOf(framesPerSecond)); 345 properties.setProperty(IMAGE_FORMAT_PROPERTY, String.valueOf(imageFormat)); 346 properties.setProperty(INACTIVITY_INTERVAL_PROPERTY, String.valueOf(inactivityInterval)); 347 properties.setProperty(LOOP_PROPERTY, String.valueOf(loop)); 348 properties.setProperty(MIXER_NAME_PROPERTY, String.valueOf(mixerName)); 349 properties.setProperty(MOUSE_PROPERTY, String.valueOf(mouse)); 350 properties.setProperty(PLAY_PROPERTY, String.valueOf(play)); 351 properties.setProperty(REMOVE_INACTIVITY_PROPERTY, String.valueOf(removeInactivity)); 352 properties.setProperty(SCREEN_SCALE_PROPERTY, String.valueOf(screenScale)); 353 properties.setProperty(SOUND_PROPERTY, String.valueOf(sound)); 354 properties.setProperty(SPEED_SCALE_PROPERTY, String.valueOf(speedScale)); 355 properties.setProperty(TOOL_BAR_PROPERTY, String.valueOf(toolBar)); 356 if (recordingRectangle!=null) { 357 properties.setProperty(RECORDING_RECTANGLE_PROPERTY, recordingRectangle.x+";"+recordingRectangle.y+";"+recordingRectangle.width+";"+recordingRectangle.height); 358 } 359 if (mp3command!=null) { 360 properties.setProperty(MP3_COMMAND_PROPERTY, mp3command); 361 } 362 if (encoder!=null) { 363 properties.setProperty(ENCODER_NAME_PROPERTY, encoder.toString()); 364 } 365 366 return properties; 367 } 368 369 370 }