com.github.sarxos.webcam.ds.ipcam.IpCamDriver Maven / Gradle / Ivy
package com.github.sarxos.webcam.ds.ipcam;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.github.sarxos.webcam.WebcamDevice;
import com.github.sarxos.webcam.WebcamDiscoverySupport;
import com.github.sarxos.webcam.WebcamDriver;
import com.github.sarxos.webcam.WebcamExceptionHandler;
/**
* IP camera driver.
*
* @author Bartosz Firyn (sarxos)
*/
public class IpCamDriver implements WebcamDriver, WebcamDiscoverySupport {
/**
* Thread factory.
*
* @author Bartosz Firyn (sarxos)
*/
private static class DeviceCheckThreadFactory implements ThreadFactory {
/**
* Next number for created thread.
*/
private AtomicInteger number = new AtomicInteger();
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r, "online-check-" + number.incrementAndGet());
t.setUncaughtExceptionHandler(WebcamExceptionHandler.getInstance());
t.setDaemon(true);
return t;
}
}
/**
* Logger.
*/
private static final Logger LOG = LoggerFactory.getLogger(IpCamDriver.class);
/**
* Thread factory.
*/
private static final ThreadFactory THREAD_FACTORY = new DeviceCheckThreadFactory();
/**
* The callable to query single IP camera device. Callable getter will return device if it's
* online or null if it's offline.
*
* @author Bartosz Firyn (sarxos)
*/
private static class DeviceOnlineCheck implements Callable {
/**
* IP camera device.
*/
private final IpCamDevice device;
private final CountDownLatch latch;
/**
* The callable to query single IP camera device.
*
* @param device the device to check online status
* @param latch the count down latch
*/
public DeviceOnlineCheck(IpCamDevice device, CountDownLatch latch) {
this.device = device;
this.latch = latch;
}
@Override
public IpCamDevice call() throws Exception {
try {
return device.isOnline() ? device : null;
} finally {
latch.countDown();
}
}
}
/**
* Discovery scan interval in milliseconds.
*/
private volatile long scanInterval = 10000;
/**
* Discovery scan timeout in milliseconds. This is maximum time which executor will wait for
* online detection to succeed.
*/
private volatile long scanTimeout = 10000;
/**
* Is discovery scanning possible.
*/
private volatile boolean scanning = false;
/**
* Execution service.
*/
private final ExecutorService executor = Executors.newCachedThreadPool(THREAD_FACTORY);
public IpCamDriver() {
this(null, false);
}
public IpCamDriver(boolean scanning) {
this(null, scanning);
}
public IpCamDriver(IpCamStorage storage) {
this(storage, false);
}
public IpCamDriver(IpCamStorage storage, boolean scanning) {
if (storage != null) {
storage.open();
}
this.scanning = scanning;
}
@Override
public List getDevices() {
// in case when scanning is disabled (by default) this method will
// return all registered devices
if (!isScanPossible()) {
return Collections.unmodifiableList((List extends WebcamDevice>) IpCamDeviceRegistry.getIpCameras());
}
// if scanning is enabled, this method will first perform HTTP lookup
// for every IP camera device and only online devices will be returned
List devices = IpCamDeviceRegistry.getIpCameras();
CountDownLatch latch = new CountDownLatch(devices.size());
List> futures = new ArrayList>(devices.size());
for (IpCamDevice device : devices) {
futures.add(executor.submit(new DeviceOnlineCheck(device, latch)));
}
try {
if (!latch.await(scanTimeout, TimeUnit.MILLISECONDS)) {
for (Future future : futures) {
if (!future.isDone()) {
future.cancel(true);
}
}
}
} catch (InterruptedException e1) {
return null;
}
List online = new ArrayList(devices.size());
for (Future future : futures) {
IpCamDevice device = null;
try {
if ((device = future.get()) != null) {
online.add(device);
}
} catch (InterruptedException e) {
LOG.debug(e.getMessage(), e);
} catch (CancellationException e) {
continue;
} catch (ExecutionException e) {
LOG.error(e.getMessage(), e);
}
}
return Collections.unmodifiableList((List extends WebcamDevice>) online);
}
public void register(IpCamDevice device) {
IpCamDeviceRegistry.register(device);
}
public void unregister(IpCamDevice device) {
IpCamDeviceRegistry.unregister(device);
}
@Override
public boolean isThreadSafe() {
return true;
}
@Override
public String toString() {
return getClass().getSimpleName();
}
@Override
public long getScanInterval() {
return scanInterval;
}
/**
* Set new scan interval. Value must be given in milliseconds and shall not be negative.
*
* @param scanInterval
*/
public void setScanInterval(long scanInterval) {
if (scanInterval > 0) {
this.scanInterval = scanInterval;
} else {
throw new IllegalArgumentException("Scan interval for IP camera cannot be negative");
}
}
@Override
public boolean isScanPossible() {
return scanning;
}
/**
* Set discovery scanning possible.
*
* @param scanning
*/
public void setScanPossible(boolean scanning) {
this.scanning = scanning;
}
/**
* @return Scan timeout in milliseconds
*/
public long getScanTimeout() {
return scanTimeout;
}
/**
* Set new scan timeout. This value cannot be less than 1000 milliseconds (which equals 1
* second).
*
* @param scanTimeout the scan timeout in milliseconds
*/
public void setScanTimeout(long scanTimeout) {
if (scanTimeout < 1000) {
scanTimeout = 1000;
}
this.scanTimeout = scanTimeout;
}
}