214 lines
5.3 KiB
Java
214 lines
5.3 KiB
Java
package org.cl0zzzy.rtmp;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
|
|
public class AMF {
|
|
|
|
private ArrayList<AMFObject> AMFMessage = new ArrayList<AMFObject>();
|
|
private byte[] amf;
|
|
|
|
public boolean next = false;
|
|
public byte[] nextArr;
|
|
|
|
public AMF( byte[] amf ) {
|
|
this.amf = amf;
|
|
this.decodeAMF();
|
|
}
|
|
|
|
private AMFResult decodeNumber( int i ) {
|
|
byte[] number = new byte[8];
|
|
number[0] = amf[i+1];
|
|
number[1] = amf[i+2];
|
|
number[2] = amf[i+3];
|
|
number[3] = amf[i+4];
|
|
number[4] = amf[i+5];
|
|
number[5] = amf[i+6];
|
|
number[6] = amf[i+7];
|
|
number[7] = amf[i+8];
|
|
AMFResult result = new AMFResult();
|
|
result.value = this.numberToDouble( Util.bytesToHex( number ) );
|
|
result.index = i+8;
|
|
return result;
|
|
}
|
|
|
|
private AMFResult decodeString( int i ) {
|
|
byte[] stringLength = new byte[2];
|
|
stringLength[0] = amf[i+1];
|
|
stringLength[1] = amf[i+2];
|
|
int length = Integer.parseInt( Util.bytesToHex(stringLength), 16 );
|
|
ArrayList<Byte> str = new ArrayList<Byte>();
|
|
for( int u = (i+3); u < (i+3+length); u++ ) {
|
|
str.add( amf[u] );
|
|
}
|
|
AMFResult result = new AMFResult();
|
|
byte[] _a = new byte[str.size()];
|
|
int j = 0;
|
|
for(Byte b : str) {
|
|
_a[j++] = b.byteValue();
|
|
}
|
|
try { result.value = new String( _a, "UTF-8" ); } catch( Exception e ) { e.printStackTrace(); }
|
|
result.index = i+2+length;
|
|
return result;
|
|
}
|
|
|
|
private AMFResult decodeECMA( int i ) {
|
|
AMFEcmaArray ecma = new AMFEcmaArray();
|
|
if( amf[i] == AMFObject.AMF_ECMA_ARRAY_START &&
|
|
amf[i+1] == 0x00 &&
|
|
amf[i+2] == 0x00 &&
|
|
amf[i+3] == 0x00 &&
|
|
amf[i+4] == 0x00 &&
|
|
amf[i+5] == 0x00 &&
|
|
amf[i+6] == 0x00 &&
|
|
amf[i+7] == AMFObject.AMF_OBJECT_END ) {
|
|
i = i + 7;
|
|
AMFResult result = new AMFResult();
|
|
result.value = ecma;
|
|
result.index = i;
|
|
return result;
|
|
}
|
|
i+=5;
|
|
int index = 0;
|
|
while( true ) {
|
|
if( amf[i] == 0x00 && amf[i+1] == 0x00 && amf[i+2] == 0x09 ) {
|
|
i = i + 2;
|
|
break;
|
|
}
|
|
i+=3;
|
|
|
|
int __index = index;
|
|
__index = __index - ( __index % 10 );
|
|
if( __index > 0 ) {
|
|
int len = String.valueOf(__index).substring(1).length();
|
|
i += len;
|
|
}
|
|
Result res = this.decodeOne(i);
|
|
AMFObject value = new AMFObject( amf[i], res.val );
|
|
ecma.add(value);
|
|
i = res.i;
|
|
i++;
|
|
index++;
|
|
}
|
|
AMFResult result = new AMFResult();
|
|
result.value = ecma;
|
|
result.index = i;
|
|
return result;
|
|
}
|
|
|
|
private AMFResult decodeObj( int i ) {
|
|
i++;
|
|
AMFObjectArray array = new AMFObjectArray();
|
|
while( true ) {
|
|
if( amf[i] == 0x00 && amf[i+1] == 0x00 && amf[i+2] == 0x09 ) {
|
|
i = i + 2;
|
|
break;
|
|
}
|
|
byte[] length = new byte[2];
|
|
length[0] = amf[i];
|
|
length[1] = amf[i+1];
|
|
|
|
int decimal = Integer.parseInt( Util.bytesToHex(length), 16 );
|
|
String key = "";
|
|
for( int u = i+2; u < (i+2+decimal); u++ ) {
|
|
key = key + new String( new byte[] {amf[u]} );
|
|
}
|
|
|
|
Result res = this.decodeOne(i+2+decimal);
|
|
AMFObject obj = new AMFObject( amf[i+2+decimal], res.val );
|
|
array.add(key, obj);
|
|
i = res.i + 1;
|
|
}
|
|
|
|
|
|
AMFResult result = new AMFResult();
|
|
result.value = array;
|
|
result.index = i;
|
|
return result;
|
|
}
|
|
|
|
private Result decodeOne( int i ) {
|
|
Object val = null;
|
|
if( amf[i] == AMFObject.AMF_NUMBER ) {
|
|
AMFResult result = this.decodeNumber(i);
|
|
val = result.value;
|
|
i = result.index;
|
|
} else if( amf[i] == AMFObject.AMF_STRING ) {
|
|
AMFResult result = this.decodeString(i);
|
|
val = result.value;
|
|
i = result.index;
|
|
} else if( amf[i] == AMFObject.AMF_BOOLEAN ) {
|
|
if( amf[i+1] == 0x00 ) {
|
|
val = false;
|
|
} else {
|
|
val = true;
|
|
}
|
|
i++;
|
|
} else if( amf[i] == AMFObject.AMF_OBJECT_START ) {
|
|
AMFResult result = this.decodeObj(i);
|
|
val = result.value;
|
|
i = result.index;
|
|
} else if( amf[i] == AMFObject.AMF_NULL ) {
|
|
val = null;
|
|
} else if( amf[i] == AMFObject.AMF_ECMA_ARRAY_START ) {
|
|
AMFResult result = this.decodeECMA(i);
|
|
val = result.value;
|
|
i = result.index;
|
|
} else if( amf[i] == AMFObject.AMF_DATE ) {
|
|
val = "Date";
|
|
i = i + 10;
|
|
} else if( amf[i] == (byte) 0x43 || amf[i] == (byte) 0x83 ){
|
|
val = "end";
|
|
next = true;
|
|
nextArr = Arrays.copyOfRange(amf, i, amf.length);
|
|
}
|
|
Result res = new Result();
|
|
res.i = i;
|
|
res.val = val;
|
|
return res;
|
|
}
|
|
|
|
private void decodeAMF() {
|
|
for( int i = 0; i < amf.length; i++ ) {
|
|
Object val = null;
|
|
int _i = i;
|
|
Result res = this.decodeOne( i );
|
|
i = res.i;
|
|
val = res.val;
|
|
if( val != null && val.equals("end") ) {
|
|
break;
|
|
}
|
|
if( amf[_i] != AMFObject.AMF_BOOLEAN &&
|
|
amf[_i] != AMFObject.AMF_ECMA_ARRAY_START &&
|
|
amf[_i] != AMFObject.AMF_NUMBER &&
|
|
amf[_i] != AMFObject.AMF_OBJECT_START &&
|
|
amf[_i] != AMFObject.AMF_STRING ) {
|
|
amf[_i] = AMFObject.AMF_NULL;
|
|
}
|
|
AMFObject value = new AMFObject( amf[_i], val );
|
|
AMFMessage.add(value);
|
|
}
|
|
}
|
|
|
|
public int getLength() {
|
|
return this.AMFMessage.size();
|
|
}
|
|
|
|
public AMFObject get( int index ) {
|
|
return this.AMFMessage.get(index);
|
|
}
|
|
|
|
private double numberToDouble( String hexString ) {
|
|
long longHex = parseUnsignedHex(hexString);
|
|
return Double.longBitsToDouble(longHex);
|
|
}
|
|
|
|
private long parseUnsignedHex(String text) {
|
|
if (text.length() == 16) {
|
|
return (parseUnsignedHex(text.substring(0, 1)) << 60)
|
|
| parseUnsignedHex(text.substring(1));
|
|
}
|
|
return Long.parseLong(text, 16);
|
|
}
|
|
}
|