package com.almalence.sony.cameraremote.utils; import android.util.Log; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; /** * A parser class for Liveview data Packet defined by Camera Remote API */ public class SimpleLiveviewSlicer { private static final String TAG = SimpleLiveviewSlicer.class.getSimpleName(); /** * Payload data class. See also Camera Remote API specification document to * know the data structure. */ public static final class Payload { /** jpeg data container */ public final byte[] jpegData; /** padding data container */ public final byte[] paddingData; /** * Constructor */ private Payload(byte[] jpeg, byte[] padding) { this.jpegData = jpeg; this.paddingData = padding; } } private static final int CONNECTION_TIMEOUT = 2000; // [msec] private HttpURLConnection mHttpConn; private InputStream mInputStream; /** * Opens Liveview HTTP GET connection and prepares for reading Packet data. * * @param liveviewUrl Liveview data url that is obtained by DD.xml or result * of startLiveview API. * @throws IOException generic errors or exception. */ public void open(String liveviewUrl) throws IOException { if (mInputStream != null || mHttpConn != null) { throw new IllegalStateException("Slicer is already open."); } final URL urlObj = new URL(liveviewUrl); mHttpConn = (HttpURLConnection) urlObj.openConnection(); mHttpConn.setRequestMethod("GET"); mHttpConn.setConnectTimeout(CONNECTION_TIMEOUT); mHttpConn.connect(); if (mHttpConn.getResponseCode() == HttpURLConnection.HTTP_OK) { mInputStream = mHttpConn.getInputStream(); } } /** * Closes the connection. * * @throws IOException generic errors or exception. */ public void close() { try { if (mInputStream != null) { mInputStream.close(); mInputStream = null; } } catch (IOException e) { Log.w(TAG, "Close() IOException."); } if (mHttpConn != null) { mHttpConn.disconnect(); mHttpConn = null; } } public Payload nextPayload() throws IOException { Payload payload = null; while (mInputStream != null && payload == null) { // Common Header int readLength = 1 + 1 + 2 + 4; byte[] commonHeader = readBytes(mInputStream, readLength); if (commonHeader == null || commonHeader.length != readLength) { throw new IOException("Cannot read stream for common header."); } if (commonHeader[0] != (byte) 0xFF) { throw new IOException("Unexpected data format. (Start byte)"); } switch (commonHeader[1]) { case (byte) 0x12: // This is information header for streaming. // skip this packet. readLength = 4 + 3 + 1 + 2 + 118 + 4 + 4 + 24; commonHeader = null; readBytes(mInputStream, readLength); break; case (byte) 0x01: case (byte) 0x11: payload = readPayload(); break; default: break; } } return payload; } /** * Reads liveview stream and slice one Packet. If server is not ready for * liveview data, this API calling will be blocked until server returns next * data. * * @return Payload data of sliced Packet * @throws IOException generic errors or exception. */ public Payload readPayload() throws IOException { if (mInputStream != null) { // Payload Header int readLength = 4 + 3 + 1 + 4 + 1 + 115; byte[] payloadHeader = readBytes(mInputStream, readLength); if (payloadHeader == null || payloadHeader.length != readLength) { throw new IOException("Cannot read stream for payload header."); } if (payloadHeader[0] != (byte) 0x24 || payloadHeader[1] != (byte) 0x35 || payloadHeader[2] != (byte) 0x68 || payloadHeader[3] != (byte) 0x79) { throw new IOException("Unexpected data format. (Start code)"); } int jpegSize = bytesToInt(payloadHeader, 4, 3); int paddingSize = bytesToInt(payloadHeader, 7, 1); // Payload Data byte[] jpegData = readBytes(mInputStream, jpegSize); byte[] paddingData = readBytes(mInputStream, paddingSize); return new Payload(jpegData, paddingData); } return null; } /** * Converts byte array to int. * * @param byteData * @param startIndex * @param count * @return */ private static int bytesToInt(byte[] byteData, int startIndex, int count) { int ret = 0; for (int i = startIndex; i < startIndex + count; i++) { ret = (ret << 8) | (byteData[i] & 0xff); } return ret; } /** * Reads byte array from the indicated input stream. * * @param in * @param length * @return * @throws IOException */ private static byte[] readBytes(InputStream in, int length) throws IOException { ByteArrayOutputStream tmpByteArray = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; while (true) { int trialReadlen = Math.min(buffer.length, length - tmpByteArray.size()); int readlen = in.read(buffer, 0, trialReadlen); if (readlen < 0) { break; } tmpByteArray.write(buffer, 0, readlen); if (length <= tmpByteArray.size()) { break; } } byte[] ret = tmpByteArray.toByteArray(); tmpByteArray.close(); return ret; } }