001 package com.github.sarxos.webcam.ds.civil; 002 003 import java.awt.Dimension; 004 import java.awt.image.BufferedImage; 005 import java.util.ArrayList; 006 import java.util.Collections; 007 import java.util.Comparator; 008 import java.util.HashSet; 009 import java.util.List; 010 import java.util.Set; 011 012 import org.slf4j.Logger; 013 import org.slf4j.LoggerFactory; 014 015 import com.github.sarxos.webcam.WebcamDevice; 016 import com.lti.civil.CaptureDeviceInfo; 017 import com.lti.civil.CaptureException; 018 import com.lti.civil.CaptureObserver; 019 import com.lti.civil.CaptureStream; 020 import com.lti.civil.CaptureSystem; 021 import com.lti.civil.Image; 022 import com.lti.civil.VideoFormat; 023 import com.lti.civil.awt.AWTImageConverter; 024 025 026 /** 027 * Webcam device - LTI-CIVIL framework compatible implementation. 028 * 029 * @author Bartosz Firyn (SarXos) 030 */ 031 public class LtiCivilDevice implements WebcamDevice, CaptureObserver { 032 033 private static final Logger LOG = LoggerFactory.getLogger(LtiCivilDevice.class); 034 035 private CaptureDeviceInfo cdi = null; 036 private List<Dimension> dimensions = null; 037 private Dimension size = null; 038 private Image image = null; 039 private CaptureStream stream = null; 040 041 private volatile boolean open = false; 042 private volatile boolean capturing = false; 043 private volatile boolean disposed = false; 044 045 protected LtiCivilDevice(CaptureDeviceInfo cdi) { 046 this.cdi = cdi; 047 } 048 049 @Override 050 public String getName() { 051 return cdi.getDescription(); 052 } 053 054 @Override 055 public Dimension[] getResolutions() { 056 057 if (dimensions == null) { 058 dimensions = new ArrayList<Dimension>(); 059 060 CaptureSystem system = LtiCivilDriver.getCaptureSystem(); 061 Set<Dimension> set = new HashSet<Dimension>(); 062 063 try { 064 065 stream = system.openCaptureDeviceStream(cdi.getDeviceID()); 066 067 for (VideoFormat format : stream.enumVideoFormats()) { 068 if (format.getFormatType() == VideoFormat.RGB24) { 069 set.add(new Dimension(format.getWidth(), format.getHeight())); 070 } 071 } 072 073 stream.dispose(); 074 075 } catch (CaptureException e) { 076 LOG.error("Capture exception when collecting formats dimension", e); 077 } 078 079 dimensions.addAll(set); 080 081 Collections.sort(dimensions, new Comparator<Dimension>() { 082 083 @Override 084 public int compare(Dimension a, Dimension b) { 085 int apx = a.width * a.height; 086 int bpx = b.width * b.height; 087 if (apx > bpx) { 088 return 1; 089 } else if (apx < bpx) { 090 return -1; 091 } else { 092 return 0; 093 } 094 } 095 }); 096 } 097 098 return dimensions.toArray(new Dimension[dimensions.size()]); 099 } 100 101 @Override 102 public BufferedImage getImage() { 103 if (!capturing) { 104 return null; 105 } 106 return AWTImageConverter.toBufferedImage(image); 107 } 108 109 @Override 110 public void onError(CaptureStream stream, CaptureException e) { 111 LOG.error("Exception in capture stream", e); 112 } 113 114 @Override 115 public void onNewImage(CaptureStream stream, Image image) { 116 this.image = image; 117 this.capturing = true; 118 } 119 120 @Override 121 public void open() { 122 123 if (open || disposed) { 124 return; 125 } 126 127 try { 128 stream = LtiCivilDriver.getCaptureSystem().openCaptureDeviceStream(cdi.getDeviceID()); 129 stream.setVideoFormat(findFormat()); 130 stream.setObserver(this); 131 stream.start(); 132 } catch (CaptureException e) { 133 LOG.error("Capture exception when opening Civil device", e); 134 } 135 while (true) { 136 if (capturing) { 137 break; 138 } 139 try { 140 Thread.sleep(100); 141 } catch (InterruptedException e) { 142 } 143 } 144 } 145 146 private VideoFormat findFormat() { 147 if (stream == null) { 148 throw new RuntimeException("Stream is null"); 149 } 150 if (size == null) { 151 throw new RuntimeException("Size is not set"); 152 } 153 try { 154 for (VideoFormat format : stream.enumVideoFormats()) { 155 if (format.getFormatType() == VideoFormat.RGB24) { 156 boolean xok = size.width == format.getWidth(); 157 boolean yok = size.height == format.getHeight(); 158 if (xok && yok) { 159 return format; 160 } 161 } 162 } 163 } catch (CaptureException e) { 164 LOG.error("Capture exception when iterating thru video formats", e); 165 } 166 throw new RuntimeException("Cannot find RGB24 video format for size [" + size.width + "x" + size.height + "]"); 167 } 168 169 @Override 170 public void close() { 171 if (!open) { 172 return; 173 } 174 try { 175 stream.stop(); 176 stream.dispose(); 177 } catch (CaptureException e) { 178 LOG.error("Capture exception when closing Civil device", e); 179 } 180 } 181 182 @Override 183 public Dimension getResolution() { 184 return size; 185 } 186 187 @Override 188 public void setResolution(Dimension d) { 189 this.size = d; 190 } 191 192 @Override 193 public void dispose() { 194 disposed = true; 195 } 196 197 @Override 198 public boolean isOpen() { 199 return open; 200 } 201 }