1111import edu .wpi .grip .core .Source ;
1212import edu .wpi .grip .core .events .UnexpectedThrowableEvent ;
1313import edu .wpi .grip .core .events .SourceRemovedEvent ;
14+ import edu .wpi .grip .core .events .SourceStartedEvent ;
15+ import edu .wpi .grip .core .events .SourceStoppedEvent ;
1416import org .bytedeco .javacpp .opencv_core .Mat ;
1517import org .bytedeco .javacv .*;
1618
@@ -42,7 +44,7 @@ public class CameraSource extends Source {
4244 private OutputSocket <Mat > frameOutputSocket ;
4345 private OutputSocket <Number > frameRateOutputSocket ;
4446 private Optional <Thread > frameThread ;
45- private Optional < FrameGrabber > grabber ;
47+ private FrameGrabber grabber ;
4648
4749 /**
4850 * Creates a camera source that can be used as an input to a pipeline
@@ -72,7 +74,6 @@ public CameraSource(EventBus eventBus, String address) throws IOException {
7274 * Used for serialization
7375 */
7476 public CameraSource () {
75- this .grabber = Optional .empty ();
7677 this .frameThread = Optional .empty ();
7778 }
7879
@@ -81,9 +82,8 @@ private void initialize(EventBus eventBus, FrameGrabber frameGrabber, String nam
8182 this .name = name ;
8283 this .frameOutputSocket = new OutputSocket <>(eventBus , imageOutputHint );
8384 this .frameRateOutputSocket = new OutputSocket <>(eventBus , frameRateOutputHint );
84-
85- this .eventBus .register (this );
86- this .startVideo (frameGrabber );
85+ this .grabber = frameGrabber ;
86+ eventBus .register (this );
8787 }
8888
8989 @ Override
@@ -127,109 +127,117 @@ public void createFromProperties(EventBus eventBus, Properties properties) throw
127127 }
128128
129129 /**
130- * Starts the video capture from the
131- *
132- * @param grabber A JavaCV {@link FrameGrabber} instance to capture from
130+ * Starts the video capture from the source device
133131 */
134- private synchronized void startVideo ( FrameGrabber grabber ) throws IOException {
132+ public CameraSource start ( ) throws IOException , IllegalStateException {
135133 final OpenCVFrameConverter .ToMat convertToMat = new OpenCVFrameConverter .ToMat ();
136- if (this .frameThread .isPresent ()) {
137- throw new IllegalStateException ("The video retrieval thread has already been started." );
138- }
139- if (this .grabber .isPresent ()) {
140- throw new IllegalStateException ("The Frame Grabber has already been started." );
141- }
142- try {
143- grabber .start ();
144- } catch (FrameGrabber .Exception e ) {
145- throw new IOException ("A problem occurred trying to start the frame grabber for " + this .name , e );
146- }
134+ synchronized (this .frameThread ) {
135+ if (this .frameThread .isPresent ()) {
136+ throw new IllegalStateException ("The video retrieval thread has already been started." );
137+ }
138+ try {
139+ grabber .start ();
140+ } catch (FrameGrabber .Exception e ) {
141+ throw new IOException ("A problem occurred trying to start the frame grabber for " + this .name , e );
142+ }
147143
148- // Store the grabber only once it has been started in the case that there is an exception.
149- this .grabber = Optional .of (grabber );
144+ final Thread frameExecutor = new Thread (() -> {
145+ long lastFrame = System .currentTimeMillis ();
146+ while (!Thread .interrupted ()) {
147+ final Frame videoFrame ;
148+ try {
149+ videoFrame = grabber .grab ();
150+ } catch (FrameGrabber .Exception e ) {
151+ throw new IllegalStateException ("Failed to grab image" , e );
152+ }
150153
151- final Thread frameExecutor = new Thread (() -> {
152- long lastFrame = System .currentTimeMillis ();
153- while (!Thread .interrupted ()) {
154- final Frame videoFrame ;
155- try {
156- videoFrame = grabber .grab ();
157- } catch (FrameGrabber .Exception e ) {
158- throw new IllegalStateException ("Failed to grab image" , e );
159- }
160- final Mat frameMat = convertToMat .convert (videoFrame );
154+ final Mat frameMat = convertToMat .convert (videoFrame );
161155
162- if (frameMat == null || frameMat .isNull ()) {
163- throw new IllegalStateException ("The camera returned a null frame Mat" );
156+ if (frameMat == null || frameMat .isNull ()) {
157+ throw new IllegalStateException ("The camera returned a null frame Mat" );
158+ }
159+
160+ frameMat .copyTo (frameOutputSocket .getValue ().get ());
161+ frameOutputSocket .setValue (frameOutputSocket .getValue ().get ());
162+ long thisMoment = System .currentTimeMillis ();
163+ frameRateOutputSocket .setValue (1000 / (thisMoment - lastFrame ));
164+ lastFrame = thisMoment ;
164165 }
166+ }, "Camera" );
165167
166- frameMat .copyTo (frameOutputSocket .getValue ().get ());
167- frameOutputSocket .setValue (frameOutputSocket .getValue ().get ());
168- long thisMoment = System .currentTimeMillis ();
169- frameRateOutputSocket .setValue (1000 / (thisMoment - lastFrame ));
170- lastFrame = thisMoment ;
171- }
172- }, "Camera" );
173- frameExecutor .setUncaughtExceptionHandler (
174- (thread , exception ) -> {
175- eventBus .post (new UnexpectedThrowableEvent (exception , "Webcam Frame Grabber Thread crashed with uncaught exception" ));
176- try {
177- stopVideo ();
178- } catch (TimeoutException e ) {
179- eventBus .post (new UnexpectedThrowableEvent (e , "Webcam Frame Grabber could not be stopped!" ));
168+ frameExecutor .setUncaughtExceptionHandler (
169+ (thread , exception ) -> {
170+ eventBus .post (new UnexpectedThrowableEvent (exception , "Camera Frame Grabber Thread crashed with uncaught exception" ));
171+ try {
172+ stop ();
173+ } catch (TimeoutException e ) {
174+ eventBus .post (new UnexpectedThrowableEvent (e , "Camera Frame Grabber could not be stopped!" ));
175+ }
180176 }
181- }
182- );
183- frameExecutor .setDaemon (true );
184- frameExecutor .start ();
185- frameThread = Optional .of (frameExecutor );
177+ );
178+ frameExecutor .setDaemon (true );
179+ frameExecutor .start ();
180+ this .frameThread = Optional .of (frameExecutor );
181+ }
182+ eventBus .post (new SourceStartedEvent (this ));
183+ return this ;
186184 }
187185
188186 /**
189187 * Stops the video feed from updating the output socket.
190188 *
191- * @throws TimeoutException If the thread running the Webcam fails to join this one after a timeout.
189+ * @throws TimeoutException If the thread running the Webcam fails to join this one after a timeout.
190+ * @throws IllegalStateException If the camera was already stopped
192191 */
193- private void stopVideo () throws TimeoutException {
194- if (frameThread .isPresent ()) {
195- final Thread ex = frameThread .get ();
196- ex .interrupt ();
197- try {
198- ex .join (TimeUnit .SECONDS .toMillis (2 ));
199- if (ex .isAlive ()) {
200- throw new TimeoutException ("Unable to terminate video feed from Web Camera" );
201- }
202- } catch (InterruptedException e ) {
203- Thread .currentThread ().interrupt ();
204- //TODO: Move this into a logging framework
205- System .out .println ("Caught Exception:" );
206- e .printStackTrace ();
207- } finally {
208- frameThread = Optional .empty ();
209- // This will always run even if a timeout exception occurs
192+ public CameraSource stop () throws TimeoutException , IllegalStateException {
193+ synchronized (this .frameThread ) {
194+ if (frameThread .isPresent ()) {
195+ final Thread ex = frameThread .get ();
196+ ex .interrupt ();
210197 try {
211- grabber .ifPresent (grabber -> {
212- try {
213- grabber .stop ();
214- } catch (FrameGrabber .Exception e ) {
215- throw new IllegalStateException ("A problem occurred trying to stop the frame grabber" , e );
216- }
217- });
198+ ex .join (TimeUnit .SECONDS .toMillis (500 ));
199+ if (ex .isAlive ()) {
200+ throw new TimeoutException ("Unable to terminate video feed from Web Camera" );
201+ }
202+ } catch (InterruptedException e ) {
203+ Thread .currentThread ().interrupt ();
204+ //TODO: Move this into a logging framework
205+ System .out .println ("Caught Exception:" );
206+ e .printStackTrace ();
218207 } finally {
219- // This will always run even if we fail to stop the grabber
220- grabber = Optional .empty ();
208+ // Clean up this resource as you can't restart a stopped thread
209+ this .frameThread = Optional .empty ();
210+ // This will always run even if a timeout exception occurs
211+ try {
212+ grabber .stop ();
213+ } catch (FrameGrabber .Exception e ) {
214+ throw new IllegalStateException ("A problem occurred trying to stop the frame grabber" , e );
215+ }
221216 }
217+ } else {
218+ throw new IllegalStateException ("Tried to stop a Webcam that is already stopped." );
222219 }
223- } else {
224- throw new IllegalStateException ("Tried to stop a Webcam that is already stopped." );
220+ }
221+ eventBus .post (new SourceStoppedEvent (this ));
222+ frameRateOutputSocket .setValue (0 );
223+ return this ;
224+ }
225+
226+ @ Override
227+ public boolean isRunning () {
228+ synchronized (this .frameThread ) {
229+ return this .frameThread .isPresent () && this .frameThread .get ().isAlive ();
225230 }
226231 }
227232
228233 @ Subscribe
229234 public void onSourceRemovedEvent (SourceRemovedEvent event ) throws TimeoutException {
230235 if (event .getSource () == this ) {
231- this .stopVideo ();
232- this .eventBus .unregister (this );
236+ try {
237+ this .stop ();
238+ } finally {
239+ this .eventBus .unregister (this );
240+ }
233241 }
234242 }
235243
0 commit comments