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

com.github.sarxos.webcam.ds.ipcam.impl.IpCamMJPEGStream Maven / Gradle / Ivy

package com.github.sarxos.webcam.ds.ipcam.impl;

import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import javax.imageio.ImageIO;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class IpCamMJPEGStream extends DataInputStream {

	private static final Logger LOG = LoggerFactory.getLogger(IpCamMJPEGStream.class);

	/**
	 * The first two bytes of every JPEG stream are the Start Of Image (SOI)
	 * marker values FFh D8h.
	 */
	private final byte[] SOI_MARKER = { (byte) 0xFF, (byte) 0xD8 };

	/**
	 * All JPEG data streams end with the End Of Image (EOI) marker values FFh
	 * D9h.
	 */
	private final byte[] EOI_MARKER = { (byte) 0xFF, (byte) 0xD9 };

	/**
	 * Name of content length header.
	 */
	private final String CONTENT_LENGTH = "Content-Length";

	/**
	 * Maximum header length.
	 */
	private final static int HEADER_MAX_LENGTH = 100;

	/**
	 * Max frame length (100kB).
	 */
	private final static int FRAME_MAX_LENGTH = 100000 + HEADER_MAX_LENGTH;

	private boolean open = true;

	public IpCamMJPEGStream(InputStream in) {
		super(new BufferedInputStream(in, FRAME_MAX_LENGTH));
	}

	private int getEndOfSeqeunce(DataInputStream in, byte[] sequence) throws IOException {
		int s = 0;
		byte c;
		for (int i = 0; i < FRAME_MAX_LENGTH; i++) {
			c = (byte) in.readUnsignedByte();
			if (c == sequence[s]) {
				s++;
				if (s == sequence.length) {
					return i + 1;
				}
			} else {
				s = 0;
			}
		}
		return -1;
	}

	private int getStartOfSequence(DataInputStream in, byte[] sequence) throws IOException {
		int end = getEndOfSeqeunce(in, sequence);
		return end < 0 ? -1 : end - sequence.length;
	}

	private int parseContentLength(byte[] headerBytes) throws IOException, NumberFormatException {

		ByteArrayInputStream bais = new ByteArrayInputStream(headerBytes);
		InputStreamReader isr = new InputStreamReader(bais);
		BufferedReader br = new BufferedReader(isr);

		String line = null;
		while ((line = br.readLine()) != null) {
			if (line.startsWith(CONTENT_LENGTH)) {
				String[] parts = line.split(":");
				if (parts.length == 2) {
					return Integer.parseInt(parts[1].trim());
				}
			}
		}

		return 0;
	}

	public BufferedImage readFrame() throws IOException {

		if (!open) {
			return null;
		}

		byte[] header = null;
		byte[] frame = null;

		mark(FRAME_MAX_LENGTH);

		int n = getStartOfSequence(this, SOI_MARKER);

		reset();

		header = new byte[n];

		readFully(header);

		int length = -1;
		try {
			length = parseContentLength(header);
		} catch (NumberFormatException e) {
			length = getEndOfSeqeunce(this, EOI_MARKER);
		}

		if (length == 0) {
			LOG.error("Invalid MJPEG stream, EOI (0xFF,0xD9) not found!");
		}

		reset();

		frame = new byte[length];

		skipBytes(n);
		readFully(frame);

		try {
			return ImageIO.read(new ByteArrayInputStream(frame));
		} catch (IOException e) {
			return null;
		}
	}

	@Override
	public void close() throws IOException {
		open = false;
		super.close();
	}

	public boolean isClosed() {
		return !open;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy