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 }