All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.sarxos.webcam.WebcamStreamer Maven / Gradle / Ivy

Go to download

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).

There is a newer version: 0.3.12
Show newest version
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