Skip to content

Commit bd53aba

Browse files
committed
Stop WebcamPanel doesn't stop all required threads, fixes #90
1 parent 82cd706 commit bd53aba

4 files changed

Lines changed: 104 additions & 62 deletions

File tree

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
1-
2-
31
import javax.swing.JFrame;
42

53
import com.github.sarxos.webcam.Webcam;
64
import com.github.sarxos.webcam.WebcamPanel;
5+
import com.github.sarxos.webcam.log.WebcamLogConfigurator;
76

87

98
public class WebcamPanelExample {
109

11-
public static void main(String[] args) {
10+
public static void main(String[] args) throws InterruptedException {
11+
12+
WebcamLogConfigurator.configure("src/example/resources/logback.xml");
13+
1214
JFrame window = new JFrame("Test webcam panel");
1315

14-
WebcamPanel panel = new WebcamPanel(Webcam.getDefault());
16+
final WebcamPanel panel = new WebcamPanel(Webcam.getDefault());
1517
panel.setFPSDisplayed(true); // display FPS on screen
1618
panel.setFPSLimited(false); // no FPS limit
1719
panel.setFillArea(true); // image will be resized with window
@@ -20,6 +22,15 @@ public static void main(String[] args) {
2022
window.pack();
2123
window.setVisible(true);
2224
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
23-
}
2425

26+
Thread.sleep(5000);
27+
panel.stop();
28+
Thread.sleep(5000);
29+
panel.start();
30+
Thread.sleep(5000);
31+
panel.pause();
32+
Thread.sleep(5000);
33+
panel.resume();
34+
35+
}
2536
}

webcam-capture/src/example/resources/logback.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Pattern>
55
</layout>
66
</appender>
7-
<root level="error">
7+
<root level="debug">
88
<appender-ref ref="STDOUT" />
99
</root>
1010
</configuration>

webcam-capture/src/main/java/com/github/sarxos/webcam/WebcamPanel.java

Lines changed: 80 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -211,64 +211,82 @@ public Thread newThread(Runnable r) {
211211
/**
212212
* Scheduled executor acting as timer.
213213
*/
214-
private ScheduledExecutorService executor = Executors.newScheduledThreadPool(1, THREAD_FACTORY);
214+
private ScheduledExecutorService executor = null;
215215

216216
/**
217-
* Repainter updates panel when it is being started.
217+
* Image updater reads images from camera and force panel to be repainted.
218218
*
219-
* @author Bartosz Firyn (sarxos)
219+
* @author Bartosz Firyn (SarXos)
220220
*/
221-
private class Repainter extends Thread {
221+
private class ImageUpdater implements Runnable {
222222

223-
public Repainter() {
224-
setUncaughtExceptionHandler(WebcamExceptionHandler.getInstance());
225-
setName(String.format("repainter-%s", webcam.getName()));
226-
setDaemon(true);
227-
}
223+
/**
224+
* Repainter updates panel when it is being started.
225+
*
226+
* @author Bartosz Firyn (sarxos)
227+
*/
228+
private class RepaintScheduler extends Thread {
228229

229-
@Override
230-
public void run() {
230+
public RepaintScheduler() {
231+
setUncaughtExceptionHandler(WebcamExceptionHandler.getInstance());
232+
setName(String.format("repaint-scheduler-%s", webcam.getName()));
233+
setDaemon(true);
234+
}
231235

232-
repaint();
236+
@Override
237+
public void run() {
233238

234-
while (starting) {
235-
try {
236-
Thread.sleep(50);
237-
} catch (InterruptedException e) {
238-
throw new RuntimeException(e);
239+
if (!running.get()) {
240+
return;
239241
}
240-
}
241242

242-
if (webcam.isOpen()) {
243-
if (isFPSLimited()) {
244-
executor.scheduleAtFixedRate(updater, 0, (long) (1000 / frequency), TimeUnit.MILLISECONDS);
243+
repaint();
244+
245+
while (starting) {
246+
try {
247+
Thread.sleep(50);
248+
} catch (InterruptedException e) {
249+
throw new RuntimeException(e);
250+
}
251+
}
252+
253+
if (webcam.isOpen()) {
254+
if (isFPSLimited()) {
255+
executor.scheduleAtFixedRate(updater, 0, (long) (1000 / frequency), TimeUnit.MILLISECONDS);
256+
} else {
257+
executor.scheduleWithFixedDelay(updater, 100, 1, TimeUnit.MILLISECONDS);
258+
}
245259
} else {
246-
executor.scheduleWithFixedDelay(updater, 100, 1, TimeUnit.MILLISECONDS);
260+
executor.schedule(this, 500, TimeUnit.MILLISECONDS);
247261
}
248-
} else {
249-
executor.schedule(this, 500, TimeUnit.MILLISECONDS);
250262
}
263+
251264
}
252265

253-
}
266+
private Thread scheduler = new RepaintScheduler();
254267

255-
/**
256-
* Image updater reads images from camera and force panel to be repainted.
257-
*
258-
* @author Bartosz Firyn (SarXos)
259-
*/
260-
private class ImageUpdater implements Runnable {
268+
private AtomicBoolean running = new AtomicBoolean(false);
261269

262-
public ImageUpdater() {
270+
public void start() {
271+
if (running.compareAndSet(false, true)) {
272+
executor = Executors.newScheduledThreadPool(1, THREAD_FACTORY);
273+
scheduler.start();
274+
}
263275
}
264276

265-
public void start() {
266-
new Repainter().start();
277+
public void stop() {
278+
if (running.compareAndSet(true, false)) {
279+
executor.shutdown();
280+
}
267281
}
268282

269283
@Override
270284
public void run() {
271285

286+
if (!running.get()) {
287+
return;
288+
}
289+
272290
if (!webcam.isOpen()) {
273291
return;
274292
}
@@ -333,7 +351,7 @@ public void run() {
333351
* Repainter is used to fetch images from camera and force panel repaint
334352
* when image is ready.
335353
*/
336-
private volatile ImageUpdater updater = new ImageUpdater();
354+
private volatile ImageUpdater updater = null;
337355

338356
/**
339357
* Webcam is currently starting.
@@ -425,13 +443,7 @@ public WebcamPanel(Webcam webcam, Dimension size, boolean start) {
425443
}
426444

427445
if (start) {
428-
updater.start();
429-
try {
430-
errored = !webcam.open();
431-
} catch (WebcamException e) {
432-
errored = true;
433-
throw e;
434-
}
446+
start();
435447
}
436448
}
437449

@@ -480,9 +492,7 @@ public void webcamOpen(WebcamEvent we) {
480492

481493
@Override
482494
public void webcamClosed(WebcamEvent we) {
483-
if (updater != null) {
484-
updater = null;
485-
}
495+
stop();
486496
}
487497

488498
@Override
@@ -504,6 +514,8 @@ public void start() {
504514
return;
505515
}
506516

517+
LOG.debug("Starting panel rendering and trying to open attached webcam");
518+
507519
starting = true;
508520

509521
if (updater == null) {
@@ -526,14 +538,22 @@ public void start() {
526538
* Stop rendering and close webcam.
527539
*/
528540
public void stop() {
529-
if (started.compareAndSet(true, false)) {
530-
image = null;
531-
try {
532-
errored = !webcam.close();
533-
} catch (WebcamException e) {
534-
errored = true;
535-
throw e;
536-
}
541+
if (!started.compareAndSet(true, false)) {
542+
return;
543+
}
544+
545+
LOG.debug("Stopping panel rendering and closing attached webcam");
546+
547+
updater.stop();
548+
updater = null;
549+
550+
image = null;
551+
552+
try {
553+
errored = !webcam.close();
554+
} catch (WebcamException e) {
555+
errored = true;
556+
throw e;
537557
}
538558
}
539559

@@ -544,20 +564,24 @@ public void pause() {
544564
if (paused) {
545565
return;
546566
}
567+
568+
LOG.debug("Pausing panel rendering");
569+
547570
paused = true;
548571
}
549572

550573
/**
551574
* Resume rendering.
552575
*/
553576
public void resume() {
577+
554578
if (!paused) {
555579
return;
556580
}
581+
582+
LOG.debug("Resuming panel rendering");
583+
557584
paused = false;
558-
synchronized (updater) {
559-
updater.notifyAll();
560-
}
561585
}
562586

563587
/**

webcam-capture/src/main/java/com/github/sarxos/webcam/log/WebcamLogConfigurator.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,13 @@ public class WebcamLogConfigurator {
3333
*/
3434
public static void configure(InputStream is) {
3535

36+
try {
37+
Class.forName("ch.qos.logback.classic.LoggerContext");
38+
} catch (ClassNotFoundException e1) {
39+
LOG.error("Cannot configure logger because logback LoggerContext is not available in classpath");
40+
return;
41+
}
42+
3643
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
3744
JoranConfigurator configurator = new JoranConfigurator();
3845
configurator.setContext(context);

0 commit comments

Comments
 (0)