package info.guardianproject.iocipher.player; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.util.Properties; import org.apache.http.HttpResponse; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.util.Log; public class MjpegInputStream extends DataInputStream { private final byte[] SOI_MARKER = { (byte) 0xFF, (byte) 0xD8 }; private final byte[] EOF_MARKER = { (byte) 0xFF, (byte) 0xD9 }; private final String CONTENT_LENGTH = "Content-Length"; //private final static int HEADER_MAX_LENGTH = 100; //private final static int FRAME_MAX_LENGTH = 40000 + HEADER_MAX_LENGTH; private final static int HEADER_MAX_LENGTH = 4096*4; private final static int FRAME_MAX_LENGTH = 1000000 + HEADER_MAX_LENGTH; private int mContentLength = -1; private SeekableStream stream; private int fullLength = 0; public static MjpegInputStream read(String url, File cacheDir) { HttpResponse res; DefaultHttpClient httpclient = new DefaultHttpClient(); try { res = httpclient.execute(new HttpGet(URI.create(url))); return new MjpegInputStream(res.getEntity().getContent(), cacheDir); } catch (ClientProtocolException e) { } catch (IOException e) {} return null; } public MjpegInputStream(InputStream in, File cacheDir) { super(new BufferedInputStream(in, FRAME_MAX_LENGTH)); Log.v("BLAH", cacheDir.getAbsolutePath()); //this.stream = SeekableStream.wrapInputStream(in, true); this.stream = null; try { this.stream = new FileCacheSeekableStream(this, cacheDir); } catch (IOException e) { System.out.println("Couldn't create FileCacheSeekableStream"); e.printStackTrace(); } } private int getEndOfSeqeunce(InputStream in, byte[] sequence) throws IOException { int seqIndex = 0; byte c; for(int i=0; i < FRAME_MAX_LENGTH; i++) { //c = (byte) stream.readUnsignedByte(); c = (byte) stream.read(); if(c == sequence[seqIndex]) { seqIndex++; if(seqIndex == sequence.length) return i + 1; } else seqIndex = 0; } return -1; } private int getStartOfSequence(InputStream 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 headerIn = new ByteArrayInputStream(headerBytes); Properties props = new Properties(); try { props.load(headerIn); } catch (IllegalArgumentException e) { System.err.println("IllegalArgumentException..."); e.printStackTrace(); } return Integer.parseInt(props.getProperty(CONTENT_LENGTH)); } public Bitmap readMjpegFrame() throws IOException { stream.mark(FRAME_MAX_LENGTH); int headerLen = getStartOfSequence(this, SOI_MARKER); stream.reset(); if (headerLen < 1) return null; byte[] header = new byte[headerLen]; //Log.v("MjpegInputStream", "headerLen: " + headerLen); stream.readFully(header); //buffer.write(header); try { mContentLength = parseContentLength(header); } catch (NumberFormatException nfe) { mContentLength = getEndOfSeqeunce(this, EOF_MARKER); } stream.reset(); fullLength += mContentLength; //Log.v("MjpegInputStream", "mContentLength: " + mContentLength); byte[] frameData = new byte[mContentLength]; stream.skipBytes(headerLen); stream.readFully(frameData); return BitmapFactory.decodeStream(new ByteArrayInputStream(frameData)); } public void seek(int pos) throws IOException { this.stream.seek(pos); } public long getFilePointer() throws IOException { return this.stream.getFilePointer(); } public double getPosition() throws IOException { long point = getFilePointer(); System.out.println("FilePointer = " + point); System.out.println("FullLength = " + fullLength); return (fullLength==0) ? 0 : (double)point / fullLength; } public int getSize() throws IOException { return fullLength; } public void seekTo(double ratio) throws IOException { int pos = (int) ((double)fullLength * ratio); System.out.println("seek to position = " + pos); seek(pos); } public void seekToLive() throws IOException { int nearEnd = fullLength-FRAME_MAX_LENGTH; seek((nearEnd > 0) ? fullLength-FRAME_MAX_LENGTH : fullLength); } }