package com.github.sarxos.webcam.ds.civil; import java.awt.Dimension; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.github.sarxos.webcam.WebcamDevice; import com.lti.civil.CaptureDeviceInfo; import com.lti.civil.CaptureException; import com.lti.civil.CaptureObserver; import com.lti.civil.CaptureStream; import com.lti.civil.CaptureSystem; import com.lti.civil.Image; import com.lti.civil.VideoFormat; import com.lti.civil.awt.AWTImageConverter; /** * Webcam device - LTI-CIVIL framework compatible implementation. * * @author Bartosz Firyn (SarXos) */ public class LtiCivilDevice implements WebcamDevice, CaptureObserver, WebcamDevice.FPSSource { private static final Logger LOG = LoggerFactory.getLogger(LtiCivilDevice.class); private CaptureDeviceInfo cdi = null; private List<Dimension> dimensions = null; private Dimension size = null; private Image image = null; private CaptureStream stream = null; private AtomicBoolean open = new AtomicBoolean(false); private volatile boolean capturing = false; private volatile boolean disposed = false; private long t1 = -1; private long t2 = -1; private volatile double fps = 0; protected LtiCivilDevice(CaptureDeviceInfo cdi) { this.cdi = cdi; } @Override public String getName() { return cdi.getDescription(); } @Override public Dimension[] getResolutions() { if (dimensions == null) { dimensions = new ArrayList<Dimension>(); CaptureSystem system = LtiCivilDriver.getCaptureSystem(); Set<Dimension> set = new HashSet<Dimension>(); try { stream = system.openCaptureDeviceStream(cdi.getDeviceID()); for (VideoFormat format : stream.enumVideoFormats()) { if (format.getFormatType() == VideoFormat.RGB24) { set.add(new Dimension(format.getWidth(), format.getHeight())); } } stream.dispose(); } catch (CaptureException e) { LOG.error("Capture exception when collecting formats dimension", e); } dimensions.addAll(set); Collections.sort(dimensions, new Comparator<Dimension>() { @Override public int compare(Dimension a, Dimension b) { int apx = a.width * a.height; int bpx = b.width * b.height; if (apx > bpx) { return 1; } else if (apx < bpx) { return -1; } else { return 0; } } }); } return dimensions.toArray(new Dimension[dimensions.size()]); } @Override public BufferedImage getImage() { if (!capturing) { return null; } return AWTImageConverter.toBufferedImage(image); } @Override public void onError(CaptureStream stream, CaptureException e) { LOG.error("Exception in capture stream", e); } @Override public void onNewImage(CaptureStream stream, Image image) { if (t1 == -1 || t2 == -1) { t1 = System.currentTimeMillis(); t2 = System.currentTimeMillis(); } this.image = image; this.capturing = true; t1 = t2; t2 = System.currentTimeMillis(); fps = (4 * fps + 1000 / (t2 - t1 + 1)) / 5; } @Override public void open() { if (disposed) { return; } if (open.compareAndSet(false, true)) { try { stream = LtiCivilDriver.getCaptureSystem().openCaptureDeviceStream(cdi.getDeviceID()); stream.setVideoFormat(findFormat()); stream.setObserver(this); stream.start(); } catch (CaptureException e) { LOG.error("Capture exception when opening Civil device", e); } } while (true) { if (capturing) { break; } try { Thread.sleep(100); } catch (InterruptedException e) { return; } } } private VideoFormat findFormat() { if (stream == null) { throw new RuntimeException("Stream is null"); } if (size == null) { throw new RuntimeException("Size is not set"); } try { for (VideoFormat format : stream.enumVideoFormats()) { if (format.getFormatType() == VideoFormat.RGB24) { boolean xok = size.width == format.getWidth(); boolean yok = size.height == format.getHeight(); if (xok && yok) { return format; } } } } catch (CaptureException e) { LOG.error("Capture exception when iterating thru video formats", e); } throw new RuntimeException("Cannot find RGB24 video format for size [" + size.width + "x" + size.height + "]"); } @Override public void close() { if (open.compareAndSet(true, false)) { try { stream.stop(); stream.dispose(); } catch (CaptureException e) { LOG.error("Capture exception when closing Civil device", e); } } } @Override public Dimension getResolution() { return size; } @Override public void setResolution(Dimension d) { this.size = d; } @Override public void dispose() { disposed = true; } @Override public boolean isOpen() { return open.get(); } @Override public double getFPS() { return fps; } }