55import com .google .common .eventbus .EventBus ;
66import com .google .common .eventbus .Subscribe ;
77import com .thoughtworks .xstream .annotations .XStreamAlias ;
8- import edu .wpi .grip .core .OutputSocket ;
9- import edu .wpi .grip .core .SocketHint ;
10- import edu .wpi .grip .core .SocketHints ;
11- import edu .wpi .grip .core .events .UnexpectedThrowableEvent ;
12- import edu .wpi .grip .core .StopStartSource ;
8+ import edu .wpi .grip .core .*;
139import edu .wpi .grip .core .events .SourceRemovedEvent ;
14- import edu .wpi .grip .core .events .SourceStartedEvent ;
15- import edu .wpi .grip .core .events .SourceStoppedEvent ;
10+ import edu .wpi .grip .core .events .StartedStoppedEvent ;
11+ import edu .wpi .grip .core .events .UnexpectedThrowableEvent ;
1612import org .bytedeco .javacpp .opencv_core .Mat ;
1713import org .bytedeco .javacv .*;
1814
2925 * Provides a way to generate a constantly updated {@link Mat} from a camera
3026 */
3127@ XStreamAlias (value = "grip:Camera" )
32- public class CameraSource extends StopStartSource {
28+ public final class CameraSource extends Source implements StartStoppable {
3329
3430 private final static String DEVICE_NUMBER_PROPERTY = "deviceNumber" ;
3531 private final static String ADDRESS_PROPERTY = "address" ;
@@ -126,9 +122,9 @@ public void createFromProperties(EventBus eventBus, Properties properties) throw
126122 }
127123
128124 /**
129- * Starts the video capture from the source device
125+ * Starts the video capture from this frame grabber.
130126 */
131- protected void start () throws IOException , IllegalStateException {
127+ public void start () throws IOException , IllegalStateException {
132128 final OpenCVFrameConverter .ToMat convertToMat = new OpenCVFrameConverter .ToMat ();
133129 synchronized (this ) {
134130 if (this .frameThread .isPresent ()) {
@@ -166,28 +162,33 @@ protected void start() throws IOException, IllegalStateException {
166162
167163 frameExecutor .setUncaughtExceptionHandler (
168164 (thread , exception ) -> {
165+ // TODO: This should use the ExceptionWitness once that has a UI component added for it
169166 eventBus .post (new UnexpectedThrowableEvent (exception , "Camera Frame Grabber Thread crashed with uncaught exception" ));
170167 try {
171168 stop ();
172169 } catch (TimeoutException e ) {
170+ // TODO: This should use the ExceptionWitness once that has a UI component added for it
173171 eventBus .post (new UnexpectedThrowableEvent (e , "Camera Frame Grabber could not be stopped!" ));
174172 }
175173 }
176174 );
177175 frameExecutor .setDaemon (true );
178176 frameExecutor .start ();
179177 this .frameThread = Optional .of (frameExecutor );
178+ // This should only be posted now that it is running
179+ eventBus .post (new StartedStoppedEvent (this ));
180180 }
181- eventBus .post (new SourceStartedEvent (this ));
182181 }
183182
184183 /**
185- * Stops the video feed from updating the output socket.
184+ * Stops this source.
185+ * This will stop the source publishing new socket values after this method returns.
186186 *
187- * @throws TimeoutException If the thread running the Webcam fails to join this one after a timeout.
188- * @throws IllegalStateException If the camera was already stopped
187+ * @return The source that was stopped
188+ * @throws TimeoutException if the thread running the source fails to stop.
189+ * @throws IOException If there is a problem stopping the Source
189190 */
190- public void stop () throws TimeoutException , IllegalStateException {
191+ public final void stop () throws TimeoutException , IllegalStateException {
191192 synchronized (this ) {
192193 if (frameThread .isPresent ()) {
193194 final Thread ex = frameThread .get ();
@@ -197,16 +198,17 @@ public void stop() throws TimeoutException, IllegalStateException {
197198 if (ex .isAlive ()) {
198199 throw new TimeoutException ("Unable to terminate video feed from Web Camera" );
199200 }
201+ // This should only be removed if the thread is successfully killed off
202+ frameThread = Optional .empty ();
200203 } catch (InterruptedException e ) {
201204 Thread .currentThread ().interrupt ();
202205 //TODO: Move this into a logging framework
203- System .out .println ("Caught Exception:" );
206+ System .err .println ("Caught Exception:" );
204207 e .printStackTrace ();
205208 } finally {
206- // Clean up this resource as you can't restart a stopped thread
207- this .frameThread = Optional .empty ();
208209 // This will always run even if a timeout exception occurs
209210 try {
211+ // Calling this multiple times will have no effect
210212 grabber .stop ();
211213 } catch (FrameGrabber .Exception e ) {
212214 throw new IllegalStateException ("A problem occurred trying to stop the frame grabber" , e );
@@ -216,22 +218,20 @@ public void stop() throws TimeoutException, IllegalStateException {
216218 throw new IllegalStateException ("Tried to stop a Webcam that is already stopped." );
217219 }
218220 }
219- eventBus .post (new SourceStoppedEvent (this ));
221+ eventBus .post (new StartedStoppedEvent (this ));
220222 frameRateOutputSocket .setValue (0 );
221223 }
222224
223225 @ Override
224- public boolean isRunning () {
225- synchronized (this ) {
226- return this .frameThread .isPresent () && this .frameThread .get ().isAlive ();
227- }
226+ public synchronized boolean isStarted () {
227+ return this .frameThread .isPresent () && this .frameThread .get ().isAlive ();
228228 }
229229
230230 @ Subscribe
231231 public void onSourceRemovedEvent (SourceRemovedEvent event ) throws TimeoutException {
232232 if (event .getSource () == this ) {
233233 try {
234- if (this .isRunning ()) this .stop ();
234+ if (this .isStarted ()) this .stop ();
235235 } finally {
236236 this .eventBus .unregister (this );
237237 }
0 commit comments