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;
}
}