![JAR search and dependency download from the Maven repository](/logo.png)
com.github.sarxos.webcam.WebcamStreamer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of webcam-capture Show documentation
Show all versions of webcam-capture Show documentation
This library allows you to use your PC webcam, IP or network cameras directly from Java. It's compatible with most operating systems (Windows, Linux, MacOS).
package com.github.sarxos.webcam;
import java.awt.image.BufferedImage;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.imageio.ImageIO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class WebcamStreamer implements ThreadFactory, WebcamListener {
private static final Logger LOG = LoggerFactory.getLogger(WebcamStreamer.class);
private static final String BOUNDARY = "mjpegframe";
private static final String CRLF = "\r\n";
private class Acceptor implements Runnable {
@Override
public void run() {
try {
ServerSocket server = new ServerSocket(port);
while (started.get()) {
Socket socket = server.accept();
LOG.info("New connection from {}", socket.getRemoteSocketAddress());
executor.execute(new Connection(socket));
}
} catch (Exception e) {
LOG.error("Cannot accept socket connection", e);
}
}
}
private class Connection implements Runnable {
private Socket socket = null;
public Connection(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
BufferedReader br = null;
BufferedOutputStream bos = null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
bos = new BufferedOutputStream(socket.getOutputStream());
} catch (IOException e) {
LOG.error("Fatal I/O exception when creating socket streams", e);
try {
socket.close();
} catch (IOException e1) {
LOG.error("Canot close socket connection from " + socket.getRemoteSocketAddress(), e1);
}
return;
}
// consume whole input
try {
while (br.ready()) {
br.readLine();
}
} catch (IOException e) {
LOG.error("Error when reading input", e);
return;
}
// stream
try {
socket.setSoTimeout(0);
socket.setKeepAlive(false);
socket.setTcpNoDelay(true);
while (started.get()) {
StringBuilder sb = new StringBuilder();
sb.append("HTTP/1.0 200 OK").append(CRLF);
sb.append("Connection: close").append(CRLF);
sb.append("Cache-Control: no-cache").append(CRLF);
sb.append("Cache-Control: private").append(CRLF);
sb.append("Pragma: no-cache").append(CRLF);
sb.append("Content-type: multipart/x-mixed-replace; boundary=--").append(BOUNDARY).append(CRLF);
sb.append(CRLF);
bos.write(sb.toString().getBytes());
do {
if (!webcam.isOpen() || socket.isInputShutdown() || socket.isClosed()) {
br.close();
bos.close();
return;
}
baos.reset();
long now = System.currentTimeMillis();
if (now > last + delay) {
image = webcam.getImage();
}
ImageIO.write(image, "JPG", baos);
sb.delete(0, sb.length());
sb.append("--").append(BOUNDARY).append(CRLF);
sb.append("Content-type: image/jpeg").append(CRLF);
sb.append("Content-Length: ").append(baos.size()).append(CRLF);
sb.append(CRLF);
try {
bos.write(sb.toString().getBytes());
bos.write(baos.toByteArray());
bos.write(CRLF.getBytes());
bos.flush();
} catch (SocketException e) {
LOG.error("Socket exception from " + socket.getRemoteSocketAddress(), e);
br.close();
bos.close();
return;
}
Thread.sleep(delay);
} while (started.get());
}
} catch (Exception e) {
String message = e.getMessage();
if (message != null) {
if (message.startsWith("Software caused connection abort")) {
LOG.info("User closed stream");
return;
}
if (message.startsWith("Broken pipe")) {
LOG.info("User connection broken");
return;
}
}
LOG.error("Error", e);
try {
bos.write("HTTP/1.0 501 Internal Server Error\r\n\r\n\r\n".getBytes());
} catch (IOException e1) {
LOG.error("Not ablte to write to output stream", e);
}
} finally {
for (Closeable closeable : new Closeable[] { br, bos, baos }) {
try {
closeable.close();
} catch (IOException e) {
LOG.error("Cannot close socket", e);
}
}
try {
socket.close();
} catch (IOException e) {
LOG.error("Cannot close socket", e);
}
}
}
}
private Webcam webcam = null;
private double fps = 0;
private int number = 0;
private int port = 0;
private long last = -1;
private long delay = -1;
private BufferedImage image = null;
private ExecutorService executor = Executors.newCachedThreadPool(this);
private AtomicBoolean started = new AtomicBoolean(false);
public WebcamStreamer(int port, Webcam webcam, double fps, boolean start) {
if (webcam == null) {
throw new IllegalArgumentException("Webcam for streaming cannot be null");
}
this.port = port;
this.webcam = webcam;
this.fps = fps;
this.delay = (long) (1000 / fps);
if (start) {
start();
}
}
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r, String.format("streamer-thread-%s", number++));
thread.setUncaughtExceptionHandler(WebcamExceptionHandler.getInstance());
thread.setDaemon(true);
return thread;
}
public void start() {
if (started.compareAndSet(false, true)) {
webcam.addWebcamListener(this);
webcam.open();
executor.execute(new Acceptor());
}
}
public void stop() {
if (started.compareAndSet(true, false)) {
executor.shutdown();
webcam.removeWebcamListener(this);
webcam.close();
}
}
@Override
public void webcamOpen(WebcamEvent we) {
start();
}
@Override
public void webcamClosed(WebcamEvent we) {
stop();
}
@Override
public void webcamDisposed(WebcamEvent we) {
}
@Override
public void webcamImageObtained(WebcamEvent we) {
}
public double getFPS() {
return fps;
}
public boolean isInitialized() {
return started.get();
}
public int getPort() {
return port;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy