Skip to content

Commit

Permalink
Stop WebcamPanel doesn't stop all required threads, fixes #90
Browse files Browse the repository at this point in the history
  • Loading branch information
sarxos committed May 2, 2013
1 parent 82cd706 commit bd53aba
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 62 deletions.
21 changes: 16 additions & 5 deletions webcam-capture/src/example/java/WebcamPanelExample.java
@@ -1,17 +1,19 @@


import javax.swing.JFrame;

import com.github.sarxos.webcam.Webcam;
import com.github.sarxos.webcam.WebcamPanel;
import com.github.sarxos.webcam.log.WebcamLogConfigurator;


public class WebcamPanelExample {

public static void main(String[] args) {
public static void main(String[] args) throws InterruptedException {

WebcamLogConfigurator.configure("src/example/resources/logback.xml");

JFrame window = new JFrame("Test webcam panel");

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

Thread.sleep(5000);
panel.stop();
Thread.sleep(5000);
panel.start();
Thread.sleep(5000);
panel.pause();
Thread.sleep(5000);
panel.resume();

}
}
2 changes: 1 addition & 1 deletion webcam-capture/src/example/resources/logback.xml
Expand Up @@ -4,7 +4,7 @@
<Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Pattern>
</layout>
</appender>
<root level="error">
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>
136 changes: 80 additions & 56 deletions webcam-capture/src/main/java/com/github/sarxos/webcam/WebcamPanel.java
Expand Up @@ -211,64 +211,82 @@ public Thread newThread(Runnable r) {
/**
* Scheduled executor acting as timer.
*/
private ScheduledExecutorService executor = Executors.newScheduledThreadPool(1, THREAD_FACTORY);
private ScheduledExecutorService executor = null;

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

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

@Override
public void run() {
public RepaintScheduler() {
setUncaughtExceptionHandler(WebcamExceptionHandler.getInstance());
setName(String.format("repaint-scheduler-%s", webcam.getName()));
setDaemon(true);
}

repaint();
@Override
public void run() {

while (starting) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
throw new RuntimeException(e);
if (!running.get()) {
return;
}
}

if (webcam.isOpen()) {
if (isFPSLimited()) {
executor.scheduleAtFixedRate(updater, 0, (long) (1000 / frequency), TimeUnit.MILLISECONDS);
repaint();

while (starting) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}

if (webcam.isOpen()) {
if (isFPSLimited()) {
executor.scheduleAtFixedRate(updater, 0, (long) (1000 / frequency), TimeUnit.MILLISECONDS);
} else {
executor.scheduleWithFixedDelay(updater, 100, 1, TimeUnit.MILLISECONDS);
}
} else {
executor.scheduleWithFixedDelay(updater, 100, 1, TimeUnit.MILLISECONDS);
executor.schedule(this, 500, TimeUnit.MILLISECONDS);
}
} else {
executor.schedule(this, 500, TimeUnit.MILLISECONDS);
}

}

}
private Thread scheduler = new RepaintScheduler();

/**
* Image updater reads images from camera and force panel to be repainted.
*
* @author Bartosz Firyn (SarXos)
*/
private class ImageUpdater implements Runnable {
private AtomicBoolean running = new AtomicBoolean(false);

public ImageUpdater() {
public void start() {
if (running.compareAndSet(false, true)) {
executor = Executors.newScheduledThreadPool(1, THREAD_FACTORY);
scheduler.start();
}
}

public void start() {
new Repainter().start();
public void stop() {
if (running.compareAndSet(true, false)) {
executor.shutdown();
}
}

@Override
public void run() {

if (!running.get()) {
return;
}

if (!webcam.isOpen()) {
return;
}
Expand Down Expand Up @@ -333,7 +351,7 @@ public void run() {
* Repainter is used to fetch images from camera and force panel repaint
* when image is ready.
*/
private volatile ImageUpdater updater = new ImageUpdater();
private volatile ImageUpdater updater = null;

/**
* Webcam is currently starting.
Expand Down Expand Up @@ -425,13 +443,7 @@ public WebcamPanel(Webcam webcam, Dimension size, boolean start) {
}

if (start) {
updater.start();
try {
errored = !webcam.open();
} catch (WebcamException e) {
errored = true;
throw e;
}
start();
}
}

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

@Override
public void webcamClosed(WebcamEvent we) {
if (updater != null) {
updater = null;
}
stop();
}

@Override
Expand All @@ -504,6 +514,8 @@ public void start() {
return;
}

LOG.debug("Starting panel rendering and trying to open attached webcam");

starting = true;

if (updater == null) {
Expand All @@ -526,14 +538,22 @@ public void start() {
* Stop rendering and close webcam.
*/
public void stop() {
if (started.compareAndSet(true, false)) {
image = null;
try {
errored = !webcam.close();
} catch (WebcamException e) {
errored = true;
throw e;
}
if (!started.compareAndSet(true, false)) {
return;
}

LOG.debug("Stopping panel rendering and closing attached webcam");

updater.stop();
updater = null;

image = null;

try {
errored = !webcam.close();
} catch (WebcamException e) {
errored = true;
throw e;
}
}

Expand All @@ -544,20 +564,24 @@ public void pause() {
if (paused) {
return;
}

LOG.debug("Pausing panel rendering");

paused = true;
}

/**
* Resume rendering.
*/
public void resume() {

if (!paused) {
return;
}

LOG.debug("Resuming panel rendering");

paused = false;
synchronized (updater) {
updater.notifyAll();
}
}

/**
Expand Down
Expand Up @@ -33,6 +33,13 @@ public class WebcamLogConfigurator {
*/
public static void configure(InputStream is) {

try {
Class.forName("ch.qos.logback.classic.LoggerContext");
} catch (ClassNotFoundException e1) {
LOG.error("Cannot configure logger because logback LoggerContext is not available in classpath");
return;
}

LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
JoranConfigurator configurator = new JoranConfigurator();
configurator.setContext(context);
Expand Down

0 comments on commit bd53aba

Please sign in to comment.