Skip to content

Commit 1b144fa

Browse files
committed
Merge pull request #323 from krok32/master
Parametrized FsWebcamDriver and FsWebcamDevice issue #307
2 parents e90f760 + 8e8c320 commit 1b144fa

File tree

2 files changed

+135
-36
lines changed

2 files changed

+135
-36
lines changed

webcam-capture-drivers/driver-fswebcam/src/main/java/com/github/sarxos/webcam/ds/fswebcam/FsWebcamDevice.java

Lines changed: 125 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
import java.io.IOException;
1313
import java.io.InputStream;
1414
import java.io.InputStreamReader;
15+
import java.util.ArrayList;
16+
import java.util.List;
17+
import java.util.Map;
1518
import java.util.concurrent.ExecutorService;
1619
import java.util.concurrent.Executors;
1720
import java.util.concurrent.ThreadFactory;
@@ -24,11 +27,19 @@
2427
import org.slf4j.LoggerFactory;
2528

2629
import com.github.sarxos.webcam.WebcamDevice;
30+
import com.github.sarxos.webcam.WebcamDevice.Configurable;
2731
import com.github.sarxos.webcam.WebcamExceptionHandler;
2832
import com.github.sarxos.webcam.WebcamResolution;
2933

3034

31-
public class FsWebcamDevice implements WebcamDevice {
35+
public class FsWebcamDevice implements WebcamDevice, Configurable {
36+
37+
public static final String PARAM_KEY_COMPRESSION = "compression";
38+
public static final String PARAM_KEY_FORMAT = "format";
39+
public static final String PARAM_KEY_SKIP = "skip";
40+
public static final String PARAM_KEY_FRAMES = "frames";
41+
public static final String PARAM_KEY_LOG = "log";
42+
public static final String PARAM_KEY_VERBOSE = "verbose";
3243

3344
public static final class ExecutorThreadFactory implements ThreadFactory {
3445

@@ -89,6 +100,7 @@ public void run() {
89100

90101
private final File vfile;
91102
private final String name;
103+
private long counter;
92104

93105
private Dimension resolution = null;
94106
private Process process = null;
@@ -99,6 +111,13 @@ public void run() {
99111
private AtomicBoolean open = new AtomicBoolean(false);
100112
private AtomicBoolean disposed = new AtomicBoolean(false);
101113

114+
private String logFilePathString;
115+
private int frames = 1;
116+
private int skip = 0;
117+
private String format = "jpeg";
118+
private int compression = -1;
119+
private boolean verbose = false;
120+
102121
protected FsWebcamDevice(File vfile) {
103122
this.vfile = vfile;
104123
this.name = vfile.getAbsolutePath();
@@ -175,46 +194,17 @@ private synchronized byte[] readBytes() {
175194

176195
@Override
177196
public BufferedImage getImage() {
197+
counter++;
178198

179199
if (!open.get()) {
180200
return null;
181201
}
182202

183-
//@formatter:off
184-
String[] cmd = new String[] {
185-
"/usr/bin/fswebcam",
186-
"--no-banner", // only image - no texts, banners, etc
187-
"--no-shadow",
188-
"--no-title",
189-
"--no-subtitle",
190-
"--no-timestamp",
191-
"--no-info",
192-
"--no-underlay",
193-
"--no-overlay",
194-
"-d", vfile.getAbsolutePath(), // input video file
195-
"-r", getResolutionString(), // resolution
196-
pipe.getAbsolutePath(), // output file (pipe)
197-
LOG.isDebugEnabled() ? "-v" : "", // enable verbosity if debug mode is enabled
198-
};
199-
//@formatter:on
200-
201-
if (LOG.isDebugEnabled()) {
202-
StringBuilder sb = new StringBuilder();
203-
for (String c : cmd) {
204-
sb.append(c).append(' ');
205-
}
206-
LOG.debug("Invoking command: {}", sb.toString());
207-
}
208-
209203
BufferedImage image = null;
210204

211205
try {
212206

213-
process = RT.exec(cmd);
214-
215-
// print process output
216-
EXECUTOR.execute(new StreamReader(process.getInputStream(), false));
217-
EXECUTOR.execute(new StreamReader(process.getErrorStream(), true));
207+
executeFsWebcamProcess();
218208

219209
try {
220210
dis = new DataInputStream(new FileInputStream(pipe));
@@ -238,14 +228,18 @@ public BufferedImage getImage() {
238228

239229
process.waitFor();
240230

231+
if (LOG.isDebugEnabled()) {
232+
LOG.debug("Image #"+counter+" done");
233+
}
241234
} catch (IOException e) {
242-
LOG.error("Process IO exception", e);
235+
LOG.error("Process #"+counter+" IO exception", e);
243236
} catch (InterruptedException e) {
244237
process.destroy();
245238
} finally {
246239

247240
try {
248-
dis.close();
241+
if (dis != null)
242+
dis.close();
249243
} catch (IOException e) {
250244
throw new RuntimeException(e);
251245
}
@@ -254,13 +248,63 @@ public BufferedImage getImage() {
254248
// call in finally block to reset thread flags
255249

256250
if (Thread.interrupted()) {
257-
throw new RuntimeException("Thread has been interrupted");
251+
throw new RuntimeException("Thread has been interrupted #"+counter);
258252
}
259253
}
260254

261255
return image;
262256
}
263257

258+
private void executeFsWebcamProcess() throws IOException {
259+
//@formatter:off
260+
List<String> c = new ArrayList<String>(24);
261+
262+
c.add("/usr/bin/fswebcam");
263+
c.add("--skip"); // number of skipped images
264+
c.add(String.valueOf(skip));
265+
c.add("--frames"); // number of images merged to the single output (default 1)
266+
c.add(String.valueOf(frames));
267+
c.add("--"+format); // format jpeg | png
268+
c.add(String.valueOf(compression));
269+
c.add("--no-banner"); // only image - no texts, banners, etc
270+
c.add("--no-shadow");
271+
c.add("--no-title");
272+
c.add("--no-subtitle");
273+
c.add("--no-timestamp");
274+
c.add("--no-info");
275+
c.add("--no-underlay");
276+
c.add("--no-overlay");
277+
c.add("--resolution"); // resolution
278+
c.add(getResolutionString());
279+
if (verbose) {
280+
c.add("--verbose");
281+
}
282+
if (logFilePathString != null) {
283+
c.add("--log"); // log file
284+
c.add(logFilePathString);
285+
}
286+
c.add("--device"); // input video file
287+
c.add(this.vfile.getAbsolutePath());
288+
c.add(pipe.getAbsolutePath()); // output file (pipe)
289+
//@formatter:on
290+
291+
String[] cmd = c.toArray(new String[c.size()]);
292+
293+
if (LOG.isDebugEnabled()) {
294+
StringBuilder sb = new StringBuilder();
295+
for (String cc : cmd) {
296+
sb.append(cc).append(' ');
297+
}
298+
LOG.debug("Invoking command: #"+counter+" \n"+ sb.toString());
299+
}
300+
301+
process = RT.exec(cmd);
302+
303+
// print process output
304+
EXECUTOR.execute(new StreamReader(process.getInputStream(), false));
305+
EXECUTOR.execute(new StreamReader(process.getErrorStream(), true));
306+
}
307+
264308
@Override
265309
public synchronized void open() {
266310

@@ -346,4 +390,50 @@ public boolean isOpen() {
346390
public String toString() {
347391
return "video device " + name;
348392
}
393+
394+
/**
395+
* Call this method to set device specific parameters.
396+
* Should be called before {@link #open()} method.
397+
* For details about config options, please see fswebcam manual.
398+
* <ul>
399+
* <li>verbose - Boolean type - If true, fswebcam command-line option --verbose is set.
400+
* <li>log - String type - If set, it's passed to fswebcam as value of command-line option --log.
401+
* <li>frames - Integer type - If set, it's passed to fswebcam as value of command-line option --frames.
402+
* <li>skip - Integer type - If set, it's passed to fswebcam as value of command-line option --skip.
403+
* <li>format - String type - Possible values are: "jpeg" (default) | "png". Passed to fswebcam as option: --[format]
404+
* <li>compression - Integer type - Passed to fswebcam together with format --[format] [compression]. Default is -1, which means automatic.
405+
* </ul>
406+
* All Boolean or Integer types may be also specified as String values. E.g. both "true" String or Boolean.TRUE are valid values.
407+
*/
408+
@Override
409+
public void setParameters(Map<String, ?> parameters) {
410+
if (parameters != null) {
411+
Object value = null;
412+
value = parameters.get(PARAM_KEY_VERBOSE);
413+
if (value != null) {
414+
verbose = Boolean.parseBoolean(String.valueOf(value));
415+
}
416+
value = parameters.get(PARAM_KEY_LOG);
417+
if (value != null) {
418+
logFilePathString = String.valueOf(value);
419+
}
420+
value = parameters.get(PARAM_KEY_FRAMES);
421+
if (value != null) {
422+
frames = Integer.parseInt(String.valueOf(value));
423+
}
424+
value = parameters.get(PARAM_KEY_SKIP);
425+
if (value != null) {
426+
skip = Integer.parseInt(String.valueOf(value));
427+
}
428+
value = parameters.get(PARAM_KEY_FORMAT);
429+
if (value != null) {
430+
format = String.valueOf(value);
431+
}
432+
value = parameters.get(PARAM_KEY_COMPRESSION);
433+
if (value != null) {
434+
compression = Integer.parseInt(String.valueOf(value));
435+
}
436+
}
437+
}
438+
349439
}

webcam-capture-drivers/driver-fswebcam/src/main/java/com/github/sarxos/webcam/ds/fswebcam/FsWebcamDriver.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ public class FsWebcamDriver implements WebcamDriver, WebcamDiscoverySupport {
1919

2020
private static final VideoDeviceFilenameFilter VFFILTER = new VideoDeviceFilenameFilter();
2121

22+
private long scanInterval;
23+
2224
@Override
2325
public List<WebcamDevice> getDevices() {
2426

@@ -37,9 +39,16 @@ public boolean isThreadSafe() {
3739
return false;
3840
}
3941

42+
/**
43+
* @see FsWebcamDriver#getScanInterval()
44+
*/
45+
public void setScanInterval(long scanInterval) {
46+
this.scanInterval = scanInterval;
47+
}
48+
4049
@Override
4150
public long getScanInterval() {
42-
return 10000;
51+
return scanInterval;
4352
}
4453

4554
@Override

0 commit comments

Comments
 (0)