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    }