/** * This file is part of jFlvTool. * * jFlvTool is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * jFlvTool is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * * file name : ByteHelper.java * authors : Jon Keys * created : July 3, 2007, 5:34 PM * copyright : Sony Digital Authoring Services * * modifications: * Date: Name: Description: * ---------- --------------- ---------------------------------------------- * July 3, 2007 Jon Keys Creation */ package edu.washington.cs.oneswarm.ui.gwt.server.ffmpeg.jflv.io; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import edu.washington.cs.oneswarm.ui.gwt.server.ffmpeg.jflv.metadata.AMFObject; import edu.washington.cs.oneswarm.ui.gwt.server.ffmpeg.jflv.metadata.AMFTime; /** * * @author Jon Keys */ public class ByteHelper { private boolean debug; private int numshift; /** Creates a new instance of ByteHelper */ public ByteHelper() { debug = false; numshift = 0; } public byte[] getUintBytes(int val, int len) { byte[] numBytes = new byte[len]; numshift = 0; for (int i = 0; i < len; i++) { numshift = ((len - 1 - i) * 8); numBytes[i] = (byte) ((val >> numshift) & 0xFF); }// for return numBytes; }// getUintBytes() // accomadate long vars public byte[] getUintBytes(long val, int len) { byte[] numBytes = new byte[len]; numshift = 0; for (int i = 0; i < len; i++) { numshift = ((len - 1 - i) * 8); numBytes[i] = (byte) ((val >> numshift) & 0xFF); }// for return numBytes; }// getUintBytes() public byte[] getAMFDataBytes(Object obj) { byte[] amfBytes = null; String objClass = obj.getClass().getName(); if (objClass.endsWith("String")) { amfBytes = getAMFStringBytes((String) obj); } else if (objClass.endsWith("Double")) { amfBytes = getAMFDoubleBytes(((Double) obj).doubleValue()); } else if (objClass.endsWith("Boolean")) { amfBytes = getAMFBooleanBytes(((Boolean) obj).booleanValue()); } else if (objClass.endsWith("HashMap")) { amfBytes = getAMFMixedArrayBytes((HashMap<String, Object>) obj); } else if (objClass.endsWith("ArrayList")) { amfBytes = getAMFArrayBytes((ArrayList<Object>) obj); } else if (objClass.endsWith("AMFTime")) { amfBytes = getAMFTimeBytes((AMFTime) obj); } else if (objClass.endsWith("AMFObject")) { amfBytes = getAMFObjectBytes((AMFObject) obj); } // System.out.println("wrote data : " + amfBytes.length); return amfBytes; }// getAMFDataBytes() public byte[] getAMFDoubleBytes(Double dblObj) { double dbl = dblObj.doubleValue(); byte[] dblHead = getUintBytes(0, 1); byte[] dblBytes = null; try { ByteArrayOutputStream bytestream = new ByteArrayOutputStream(); DataOutputStream datastream = new DataOutputStream(bytestream); datastream.writeDouble(dbl); datastream.flush(); dblBytes = bytestream.toByteArray(); } catch (Exception e) { System.out.println("Error - could not read double from given bytes"); if (debug) { e.printStackTrace(); } dblBytes = new byte[0]; } byte[] totBytes = new byte[dblHead.length + dblBytes.length]; System.arraycopy(dblHead, 0, totBytes, 0, dblHead.length); System.arraycopy(dblBytes, 0, totBytes, dblHead.length, dblBytes.length); dblHead = null; dblBytes = null; // System.out.println("wrote double : " + totBytes.length); return totBytes; }// getAMFDoubleBytes() public byte[] getAMFBooleanBytes(Boolean boolObj) { boolean bool = boolObj.booleanValue(); byte[] boolHead = getUintBytes(1, 1); byte[] boolBytes; if (bool) { boolBytes = getUintBytes(1, 1); } else { boolBytes = getUintBytes(0, 1); } byte[] totBytes = new byte[boolHead.length + boolBytes.length]; System.arraycopy(boolHead, 0, totBytes, 0, boolHead.length); System.arraycopy(boolBytes, 0, totBytes, boolHead.length, boolBytes.length); boolHead = null; boolBytes = null; // System.out.println("wrote boolean : " + totBytes.length); return totBytes; }// getAMFBooleanBytes() public byte[] getAMFStringBytes(String str) { byte[] strHead = getUintBytes(2, 1); byte[] strlen = getUintBytes(str.length(), 2); byte[] strBytes = str.getBytes(); byte[] totBytes = new byte[strHead.length + strlen.length + strBytes.length]; System.arraycopy(strHead, 0, totBytes, 0, strHead.length); System.arraycopy(strlen, 0, totBytes, strHead.length, strlen.length); System.arraycopy(strBytes, 0, totBytes, strlen.length + strHead.length, strBytes.length); strHead = null; strlen = null; strBytes = null; // System.out.println("wrote string '" + str + "': " + totBytes.length); return totBytes; }// getAMFStringBytes public byte[] getAMFObjectBytes(AMFObject amfObj) { int totalObjectBytes = 0; ArrayList<byte[]> keyArrays = new ArrayList<byte[]>(); ArrayList<byte[]> valArrays = new ArrayList<byte[]>(); byte[] objHead = getUintBytes(3, 1); totalObjectBytes += objHead.length; byte[] keyBytes; byte[] keyLenBytes; int mapsize = amfObj.size(); Iterator keyValuePairs = amfObj.entrySet().iterator(); for (int i = 0; i < mapsize; i++) { Map.Entry entry = (Map.Entry) keyValuePairs.next(); String key = (String) entry.getKey(); keyLenBytes = getUintBytes(key.length(), 2); keyBytes = key.getBytes(); byte[] keyTotBytes = new byte[keyLenBytes.length + keyBytes.length]; // System.out.println("key '" + key + "' bytes : " + // keyTotBytes.length); System.arraycopy(keyLenBytes, 0, keyTotBytes, 0, keyLenBytes.length); System.arraycopy(keyBytes, 0, keyTotBytes, keyLenBytes.length, keyBytes.length); keyLenBytes = null; keyBytes = null; keyArrays.add(keyTotBytes); valArrays.add(getAMFDataBytes(entry.getValue())); totalObjectBytes += keyTotBytes.length; totalObjectBytes += valArrays.get(valArrays.size() - 1).length; }// for byte[] objTail = getUintBytes(0, 2); totalObjectBytes += objTail.length; byte[] objClose = getUintBytes(9, 1); totalObjectBytes += objClose.length; // done reading in bytes -- now copy them all into one byte array int curPos = 0; byte[] objBytes = new byte[totalObjectBytes]; System.arraycopy(objHead, 0, objBytes, curPos, objHead.length); curPos += objHead.length; objHead = null; for (int q = 0; q < keyArrays.size(); q++) { System.arraycopy(keyArrays.get(q), 0, objBytes, curPos, keyArrays.get(q).length); curPos += keyArrays.get(q).length; System.arraycopy(valArrays.get(q), 0, objBytes, curPos, valArrays.get(q).length); curPos += valArrays.get(q).length; } keyArrays = null; valArrays = null; System.arraycopy(objTail, 0, objBytes, curPos, objTail.length); curPos += objTail.length; objTail = null; System.arraycopy(objClose, 0, objBytes, curPos, objClose.length); curPos += objClose.length; objClose = null; // System.out.println("wrote object : " + objBytes.length); return objBytes; }// getAMFObjectBytes() public byte[] getAMFMixedArrayBytes(HashMap<String, Object> mix) { int totalArrayBytes = 0; ArrayList<byte[]> keyArrays = new ArrayList<byte[]>(); ArrayList<byte[]> valArrays = new ArrayList<byte[]>(); byte[] arrayHead = getUintBytes(8, 1); totalArrayBytes += arrayHead.length; byte[] arrayLen = getUintBytes(mix.size(), 4); totalArrayBytes += arrayLen.length; byte[] keyBytes; byte[] keyLenBytes; int mapsize = mix.size(); Iterator keyValuePairs = mix.entrySet().iterator(); for (int i = 0; i < mapsize; i++) { Map.Entry entry = (Map.Entry) keyValuePairs.next(); String key = (String) entry.getKey(); keyLenBytes = getUintBytes(key.length(), 2); keyBytes = key.getBytes(); byte[] keyTotBytes = new byte[keyLenBytes.length + keyBytes.length]; // System.err.println("key '" + key + "' bytes : " + // keyTotBytes.length); System.arraycopy(keyLenBytes, 0, keyTotBytes, 0, keyLenBytes.length); System.arraycopy(keyBytes, 0, keyTotBytes, keyLenBytes.length, keyBytes.length); keyLenBytes = null; keyBytes = null; keyArrays.add(keyTotBytes); valArrays.add(getAMFDataBytes(entry.getValue())); totalArrayBytes += keyTotBytes.length; totalArrayBytes += valArrays.get(valArrays.size() - 1).length; }// for byte[] arrayTail = getUintBytes(0, 2); totalArrayBytes += arrayTail.length; byte[] arrayClose = getUintBytes(9, 1); totalArrayBytes += arrayClose.length; // done reading in bytes -- now copy them all into one byte array int curPos = 0; byte[] objBytes = new byte[totalArrayBytes]; System.arraycopy(arrayHead, 0, objBytes, curPos, arrayHead.length); curPos += arrayHead.length; arrayHead = null; System.arraycopy(arrayLen, 0, objBytes, curPos, arrayLen.length); curPos += arrayLen.length; arrayLen = null; for (int q = 0; q < keyArrays.size(); q++) { System.arraycopy(keyArrays.get(q), 0, objBytes, curPos, keyArrays.get(q).length); curPos += keyArrays.get(q).length; System.arraycopy(valArrays.get(q), 0, objBytes, curPos, valArrays.get(q).length); curPos += valArrays.get(q).length; } keyArrays = null; valArrays = null; System.arraycopy(arrayTail, 0, objBytes, curPos, arrayTail.length); curPos += arrayTail.length; arrayTail = null; System.arraycopy(arrayClose, 0, objBytes, curPos, arrayClose.length); curPos += arrayClose.length; arrayClose = null; // System.out.println("wrote hash : " + objBytes.length); return objBytes; }// getAMFMixedArrayBytes() public byte[] getAMFArrayBytes(ArrayList<Object> amfArray) { int totalArrayBytes = 0; ArrayList<byte[]> valArrays = new ArrayList<byte[]>(); byte[] arrayHead = getUintBytes(10, 1); totalArrayBytes += arrayHead.length; byte[] arrayLen = getUintBytes(amfArray.size(), 4); totalArrayBytes += arrayLen.length; for (Object obj : amfArray) { valArrays.add(getAMFDataBytes(obj)); totalArrayBytes += valArrays.get(valArrays.size() - 1).length; } int curPos = 0; byte[] amfArrayBytes = new byte[totalArrayBytes]; System.arraycopy(arrayHead, 0, amfArrayBytes, curPos, arrayHead.length); curPos += arrayHead.length; arrayHead = null; System.arraycopy(arrayLen, 0, amfArrayBytes, curPos, arrayLen.length); curPos += arrayLen.length; arrayLen = null; for (byte[] buf : valArrays) { System.arraycopy(buf, 0, amfArrayBytes, curPos, buf.length); curPos += buf.length; } valArrays = null; // System.out.println("wrote array : " + amfArrayBytes.length); return amfArrayBytes; }// getAMFArrayBytes() public byte[] getAMFTimeBytes(AMFTime dblTimeObj) { double dblTime = dblTimeObj.getUTCTime(); int gmt = dblTimeObj.getGMTOffset() / 60 / 1000; byte[] timeHead = getUintBytes(11, 1); byte[] gmtBytes = getUintBytes(gmt, 2); byte[] timeBytes = null; try { ByteArrayOutputStream bytestream = new ByteArrayOutputStream(); DataOutputStream datastream = new DataOutputStream(bytestream); datastream.writeDouble(dblTime); datastream.flush(); timeBytes = bytestream.toByteArray(); } catch (Exception e) { System.out.println("Error - could not read time from given bytes"); if (debug) { e.printStackTrace(); } timeBytes = new byte[0]; } byte[] totBytes = new byte[timeHead.length + timeBytes.length + gmtBytes.length]; System.arraycopy(timeHead, 0, totBytes, 0, timeHead.length); System.arraycopy(timeBytes, 0, totBytes, timeHead.length, timeBytes.length); System.arraycopy(gmtBytes, 0, totBytes, timeHead.length + timeBytes.length, gmtBytes.length); timeHead = null; timeBytes = null; gmtBytes = null; // System.out.println("wrote time : " + totBytes.length); return totBytes; }// getAMFTimeBytes() public boolean isDebug() { return debug; } public void setDebug(boolean debug) { this.debug = debug; } }// ByteHelper