import ch.psi.pshell.imaging.SourceBase; import ch.psi.pshell.imaging.SourceConfig; import ch.psi.pshell.imaging.Utils; import java.awt.image.BufferedImage; import java.io.BufferedInputStream; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.logging.Level; /** * Image source receive frames from a mjpeg server. */ public class MjpegSource2 extends SourceBase { final String url; final boolean flushOnUpdate; public MjpegSource2(String name, String url) { this(name, url, false); } public MjpegSource2(String name, String url, boolean flushOnUpdate) { super(name, new SourceConfig()); this.url = url; this.flushOnUpdate = flushOnUpdate; } InputStream stream; @Override protected void doInitialize() throws IOException, InterruptedException { super.doInitialize(); URL aux = new URL(url); stream = aux.openStream(); if (!stream.markSupported()) { stream = new BufferedInputStream(stream); } } Thread monitoredThread; @Override protected void doSetMonitored(boolean value) { if (value && (monitoredThread == null)) { monitoredThread = new Thread(() -> { try { while (true) { try { doUpdate(); Thread.sleep(1); } catch (IOException ex) { getLogger().log(Level.FINE, null, ex); } } } catch (InterruptedException ex) { return; } }); monitoredThread.setDaemon(true); monitoredThread.start(); } else if (!value && (monitoredThread != null)) { monitoredThread.interrupt(); monitoredThread = null; } } final byte[] START_OF_FRAME = {(byte) 0xFF, (byte) 0xD8}; final byte[] END_OF_FRAME = {(byte) 0xFF, (byte) 0xD9}; final int MAX_FRAME_SIZE = 512 * 1024; @Override protected void doUpdate() throws IOException, InterruptedException { byte[] data = null; if (stream != null) { if (flushOnUpdate) { flush(); } try { data = readData(); } catch (EOFException ex) { //Try to reopen stream doInitialize(); data = readData(); } } if (data == null) { pushImage(null); } else { BufferedImage img = Utils.newImage(data); pushImage(img); } } byte[] readData() throws IOException { if (stream != null) { stream.mark(MAX_FRAME_SIZE); int startOfFrame = waitBytes(START_OF_FRAME) - START_OF_FRAME.length; if (startOfFrame >= 0) { int endOfFrame = waitBytes(END_OF_FRAME); if (endOfFrame >= 0) { stream.reset(); stream.skip(startOfFrame); int length = endOfFrame ;//- END_OF_FRAME.length ; byte[] data = new byte[length]; stream.read(data, 0, length); return data; } } } return null; } int waitBytes(byte[] data) throws IOException { int index = 0; int dataPos = 0; while (true) { int ret = stream.read(); if (ret < 0) { throw new EOFException(); } byte value = (byte) ret; if (value == data[dataPos]) { dataPos++; if (dataPos == data.length) { return (index + 1); } } else { dataPos = 0; } index++; if (index >= MAX_FRAME_SIZE) { return -1; } } } public void flush() throws IOException { //stream.skip(stream.available()); //TODO: Skipping won't make the current image to be displayed stream.close(); stream = new URL(url).openStream(); if (!stream.markSupported()) { stream = new BufferedInputStream(stream); } } }