diff --git a/rtmp/amf.go b/rtmp/amf.go index d46ea16e..1977254c 100644 --- a/rtmp/amf.go +++ b/rtmp/amf.go @@ -3,15 +3,16 @@ NAME amf.go DESCRIPTION - See Readme.md + RMTP encoding/decoding functions. AUTHORS Saxon Nelson-Milton Dan Kortschak Jake Lane + Alan Noble LICENSE - amf.go is Copyright (C) 2017 the Australian Ocean Lab (AusOcean) + amf.go is Copyright (C) 2017-2019 the Australian Ocean Lab (AusOcean) It is free software: you can redistribute it and/or modify them under the terms of the GNU General Public License as published by the @@ -35,71 +36,90 @@ package rtmp import ( "encoding/binary" - "log" "math" ) -var ( - AMFObj_Invalid C_AMFObject - AMFProp_Invalid = C_AMFObjectProperty{p_type: AMF_INVALID} -) - const ( - AMF3_INTEGER_MAX = 268435455 - AMF3_INTEGER_MIN = -268435456 + amfNumber = iota + amfBoolean + amfString + amfObject + amfMovieClip // reserved, not implemented + amfNull + amfUndefined + amfReference + amfEcmaArray + amfObjectEnd + amfStrictArray + amfDate + amfLongSring + amfUnsupported + amfRecordset // reserved, not implemented + amfXmlDoc + amfTypedObject + amfAvmplus // reserved, not implemented + amfInvalid = 0xff ) -// unsigned short AMF_DecodeInt16(const char* data); -// amf.c +41 -func C_AMF_DecodeInt16(data []byte) uint16 { - return uint16(data[0])<<8 | uint16(data[1]) +type AMF struct { + props []AMFProperty } -// unsigned int AMF_DecodeInt24(const char* data); -// amf.c +50 -func C_AMF_DecodeInt24(data []byte) uint32 { +type AMFProperty struct { + name string + atype AMFDataType + vu vu + UTCoffset int16 +} + +type vu struct { + number float64 + aval string + obj AMF +} + +type AMFDataType int32 + +var ( + amfObjInvalid AMF + amfPropInvalid = AMFProperty{atype: amfInvalid} +) + +func amfDecodeInt16(data []byte) uint16 { + return uint16(binary.BigEndian.Uint16(data)) +} + +func amfDecodeInt24(data []byte) uint32 { return uint32(data[0])<<16 | uint32(data[1])<<8 | uint32(data[2]) + // return uint16(data[0])<<8 | uint16(data[1]) } -// unsigned int AMF_DeocdeInt32(const char* data); -// amf.c +59 -func C_AMF_DecodeInt32(data []byte) uint32 { - return uint32(data[0])<<24 | uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3]) +func amfDecodeInt32(data []byte) uint32 { + return uint32(binary.BigEndian.Uint32(data)) } -// void AMF_DecodeString(const char* data, C_AVal* bv); -// amf.c +68 -func C_AMF_DecodeString(data []byte) string { - n := C_AMF_DecodeInt16(data) +func amfDecodeString(data []byte) string { + n := amfDecodeInt16(data) return string(data[2 : 2+n]) } -// void AMF_DecodeLongString(const char *data, AVal *bv); -// amf.c +75 -func C_AMF_DecodeLongString(data []byte) string { - n := C_AMF_DecodeInt32(data) +func amfDecodeLongString(data []byte) string { + n := amfDecodeInt32(data) return string(data[2 : 2+n]) } -// double AMF_DecodeNumber(const char* data); -// amf.c +82 -func C_AMF_DecodeNumber(data []byte) float64 { +func amfDecodeNumber(data []byte) float64 { return math.Float64frombits(binary.BigEndian.Uint64(data)) } -// int AMF_DecodeBoolean(const char *data); -// amf.c +132 -func C_AMF_DecodeBoolean(data []byte) bool { +func amfDecodeBoolean(data []byte) bool { return data[0] != 0 } -// char* AMF_EncodeInt24(char* output, char* outend, int nVal); -// amf.c +149 -func C_AMF_EncodeInt24(dst []byte, val int32) []byte { +func amfEncodeInt24(dst []byte, val int32) []byte { if len(dst) < 3 { return nil } - _ = dst[2] dst[0] = byte(val >> 16) dst[1] = byte(val >> 8) dst[2] = byte(val) @@ -109,9 +129,7 @@ func C_AMF_EncodeInt24(dst []byte, val int32) []byte { return dst[3:] } -// char* AMF_EncodeInt32(char* output, char* outend, int nVal); -// amf.c +160 -func C_AMF_EncodeInt32(dst []byte, val int32) []byte { +func amfEncodeInt32(dst []byte, val int32) []byte { if len(dst) < 4 { return nil } @@ -122,9 +140,7 @@ func C_AMF_EncodeInt32(dst []byte, val int32) []byte { return dst[4:] } -// char* AMF_EncodeString(char* output, char* outend, const C_AVal* bv); -// amf.c +173 -func C_AMF_EncodeString(dst []byte, val string) []byte { +func amfEncodeString(dst []byte, val string) []byte { const typeSize = 1 if len(val) < 65536 && len(val)+typeSize+binary.Size(int16(0)) > len(dst) { return nil @@ -134,7 +150,7 @@ func C_AMF_EncodeString(dst []byte, val string) []byte { } if len(val) < 65536 { - dst[0] = AMF_STRING + dst[0] = amfString dst = dst[1:] binary.BigEndian.PutUint16(dst[:2], uint16(len(val))) dst = dst[2:] @@ -144,7 +160,7 @@ func C_AMF_EncodeString(dst []byte, val string) []byte { } return dst[len(val):] } - dst[0] = AMF_LONG_STRING + dst[0] = amfLongSring dst = dst[1:] binary.BigEndian.PutUint32(dst[:4], uint32(len(val))) dst = dst[4:] @@ -155,25 +171,21 @@ func C_AMF_EncodeString(dst []byte, val string) []byte { return dst[len(val):] } -// char* AMF_EncodeNumber(char* output, char* outend, double dVal); -// amf.c +199 -func C_AMF_EncodeNumber(dst []byte, val float64) []byte { +func amfEncodeNumber(dst []byte, val float64) []byte { if len(dst) < 9 { return nil } - dst[0] = AMF_NUMBER + dst[0] = amfNumber dst = dst[1:] binary.BigEndian.PutUint64(dst, math.Float64bits(val)) return dst[8:] } -// char* AMF_EncodeBoolean(char* output, char* outend, int bVal); -// amf.c +260 -func C_AMF_EncodeBoolean(dst []byte, val bool) []byte { +func amfEncodeBoolean(dst []byte, val bool) []byte { if len(dst) < 2 { return nil } - dst[0] = AMF_BOOLEAN + dst[0] = amfBoolean if val { dst[1] = 1 } @@ -184,9 +196,7 @@ func C_AMF_EncodeBoolean(dst []byte, val bool) []byte { } -// char* AMF_EncodeNamedString(char* output, char* outend, const C_AVal* strName, const C_AVal* strValue); -// amf.c +273 -func C_AMF_EncodeNamedString(dst []byte, key, val string) []byte { +func amfEncodeNamedString(dst []byte, key, val string) []byte { if 2+len(key) > len(dst) { return nil } @@ -196,12 +206,10 @@ func C_AMF_EncodeNamedString(dst []byte, key, val string) []byte { if len(key) == len(dst) { return nil } - return C_AMF_EncodeString(dst[len(key):], val) + return amfEncodeString(dst[len(key):], val) } -// char* AMF_EncodeNamedNumber(char* output, char* outend, const C_AVal* strName, double dVal); -// amf.c +286 -func C_AMF_EncodeNamedNumber(dst []byte, key string, val float64) []byte { +func amfEncodeNamedNumber(dst []byte, key string, val float64) []byte { if 2+len(key) > len(dst) { return nil } @@ -211,12 +219,10 @@ func C_AMF_EncodeNamedNumber(dst []byte, key string, val float64) []byte { if len(key) == len(dst) { return nil } - return C_AMF_EncodeNumber(dst[len(key):], val) + return amfEncodeNumber(dst[len(key):], val) } -// char* AMF_EncodeNamedBoolean(char* output, char* outend, const C_AVal* strname, int bVal); -// amf.c +299 -func C_AMF_EncodeNamedBoolean(dst []byte, key string, val bool) []byte { +func amfEncodeNamedBoolean(dst []byte, key string, val bool) []byte { if 2+len(key) > len(dst) { return nil } @@ -226,112 +232,100 @@ func C_AMF_EncodeNamedBoolean(dst []byte, key string, val bool) []byte { if len(key) == len(dst) { return nil } - return C_AMF_EncodeBoolean(dst[len(key):], val) + return amfEncodeBoolean(dst[len(key):], val) } -// void AMFProp_SetName(AMFObjectProperty *prop, AVal *name); -// amf.c +318 -func C_AMFProp_SetName(prop *C_AMFObjectProperty, name string) { - prop.p_name = name +func amfPropSetName(prop *AMFProperty, name string) { + prop.name = name } -// double AMFProp_GetNumber(AMFObjectProperty* prop); -// amf.c +330 -func C_AMFProp_GetNumber(prop *C_AMFObjectProperty) float64 { - return prop.p_vu.p_number +func amfPropGetNumber(prop *AMFProperty) float64 { + return prop.vu.number } -// void AMFProp_GetString(AMFObjectProperty* prop, AVal* str); -// amf.c +341 -func C_AMFProp_GetString(prop *C_AMFObjectProperty) string { - if prop.p_type == AMF_STRING { - return prop.p_vu.p_aval +func amfPropGetString(prop *AMFProperty) string { + if prop.atype == amfString { + return prop.vu.aval } return "" } -// void AMFProp_GetObject(AMFObjectProperty *prop, AMFObject *obj); -// amf.c +351 -func C_AMFProp_GetObject(prop *C_AMFObjectProperty, obj *C_AMFObject) { - if prop.p_type == AMF_OBJECT { - *obj = prop.p_vu.p_object +func amfPropGetObject(prop *AMFProperty, a *AMF) { + if prop.atype == amfObject { + *a = prop.vu.obj } else { - *obj = AMFObj_Invalid + *a = amfObjInvalid } } -// char* AMFPropEncode(AMFOBjectProperty* prop, char* pBufer, char* pBufEnd); -// amf.c +366 -func C_AMF_PropEncode(p *C_AMFObjectProperty, dst []byte) []byte { - if p.p_type == AMF_INVALID { +func amfPropEncode(p *AMFProperty, dst []byte) []byte { + if p.atype == amfInvalid { return nil } - if p.p_type != AMF_NULL && len(p.p_name)+2+1 >= len(dst) { + if p.atype != amfNull && len(p.name)+2+1 >= len(dst) { return nil } - if p.p_type != AMF_NULL && len(p.p_name) != 0 { - binary.BigEndian.PutUint16(dst[:2], uint16(len(p.p_name))) + if p.atype != amfNull && len(p.name) != 0 { + binary.BigEndian.PutUint16(dst[:2], uint16(len(p.name))) dst = dst[2:] - copy(dst, p.p_name) - dst = dst[len(p.p_name):] + copy(dst, p.name) + dst = dst[len(p.name):] } - switch p.p_type { - case AMF_NUMBER: - dst = C_AMF_EncodeNumber(dst, p.p_vu.p_number) - case AMF_BOOLEAN: - dst = C_AMF_EncodeBoolean(dst, p.p_vu.p_number != 0) - case AMF_STRING: - dst = C_AMF_EncodeString(dst, p.p_vu.p_aval) - case AMF_NULL: + switch p.atype { + case amfNumber: + dst = amfEncodeNumber(dst, p.vu.number) + case amfBoolean: + dst = amfEncodeBoolean(dst, p.vu.number != 0) + case amfString: + dst = amfEncodeString(dst, p.vu.aval) + case amfNull: if len(dst) < 2 { return nil } - dst[0] = AMF_NULL + dst[0] = amfNull dst = dst[1:] - case AMF_OBJECT: - dst = C_AMF_Encode(&p.p_vu.p_object, dst) - case AMF_ECMA_ARRAY: - dst = C_AMF_EncodeEcmaArray(&p.p_vu.p_object, dst) - case AMF_STRICT_ARRAY: - dst = C_AMF_EncodeArray(&p.p_vu.p_object, dst) + case amfObject: + dst = amfEncode(&p.vu.obj, dst) + case amfEcmaArray: + dst = amfEncodeEcmaArray(&p.vu.obj, dst) + case amfStrictArray: + dst = amfEncodeArray(&p.vu.obj, dst) default: - log.Println("C_AMF_PropEncode: invalid type!") + // ??? log.Println("amfPropEncode: invalid type!") dst = nil } return dst } -// int AMFProp_Decode(C_AMFObjectProperty* prop, const char* pBuffer, int nSize, int bDecodeName); -// amf.c +619 -func C_AMFProp_Decode(prop *C_AMFObjectProperty, data []byte, bDecodeName int32) int32 { - prop.p_name = "" +func amfProDecode(prop *AMFProperty, data []byte, bDecodeName int32) int32 { + prop.name = "" nOriginalSize := len(data) if len(data) == 0 { // TODO use new logger here - // RTMP_Log(RTMP_LOGDEBUG, "%s: Empty buffer/no buffer pointer!", __FUNCTION__); + // RTMLog(RTMLOGDEBUG, "%s: Empty buffer/no buffer pointer!", __FUNCTION__); return -1 } if bDecodeName != 0 && len(data) < 4 { // at least name (length + at least 1 byte) and 1 byte of data // TODO use new logger here - // RTMP_Log(RTMP_LOGDEBUG, "%s: Not enough data for decoding with name, less than 4 bytes!",__FUNCTION__); + // RTMLog(RTMLOGDEBUG, "%s: Not enough data for decoding with name, less than 4 bytes!",__FUNCTION__); return -1 } if bDecodeName != 0 { - nNameSize := C_AMF_DecodeInt16(data[:2]) + nNameSize := amfDecodeInt16(data[:2]) if int(nNameSize) > len(data)-2 { // TODO use new logger here - //RTMP_Log(RTMP_LOGDEBUG, "%s: Name size out of range: namesize (%d) > len (%d) - 2",__FUNCTION__, nNameSize, nSize); + //RTMLog(RTMLOGDEBUG, "%s: Name size out of range: namesize (%d) > len (%d) - 2",__FUNCTION__, nNameSize, nSize); return -1 } - prop.p_name = C_AMF_DecodeString(data) + prop.name = amfDecodeString(data) data = data[2+nNameSize:] } @@ -339,122 +333,118 @@ func C_AMFProp_Decode(prop *C_AMFObjectProperty, data []byte, bDecodeName int32) return -1 } - prop.p_type = C_AMFDataType(data[0]) + prop.atype = AMFDataType(data[0]) data = data[1:] var nRes int32 - switch prop.p_type { - case AMF_NUMBER: + switch prop.atype { + case amfNumber: if len(data) < 8 { return -1 } - prop.p_vu.p_number = C_AMF_DecodeNumber(data[:8]) + prop.vu.number = amfDecodeNumber(data[:8]) data = data[8:] - case AMF_BOOLEAN: - panic("AMF_BOOLEAN not supported") + case amfBoolean: + panic("amfBoolean not supported") - case AMF_STRING: - nStringSize := C_AMF_DecodeInt16(data[:2]) + case amfString: + nStringSize := amfDecodeInt16(data[:2]) if len(data) < int(nStringSize+2) { return -1 } - prop.p_vu.p_aval = C_AMF_DecodeString(data) + prop.vu.aval = amfDecodeString(data) data = data[2+nStringSize:] - case AMF_OBJECT: - nRes := C_AMF_Decode(&prop.p_vu.p_object, data, 1) + case amfObject: + nRes := amfDecode(&prop.vu.obj, data, 1) if nRes == -1 { return -1 } data = data[nRes:] - case AMF_MOVIECLIP: + case amfMovieClip: // TODO use new logger here - log.Println("AMFProp_Decode: MAF_MOVIECLIP reserved!") - //RTMP_Log(RTMP_LOGERROR, "AMF_MOVIECLIP reserved!"); + // ??? log.Println("AMFProDecode: MAF_MOVIECLIP reserved!") + //RTMLog(RTMLOGERROR, "amfMovieClip reserved!"); return -1 - case AMF_NULL, AMF_UNDEFINED, AMF_UNSUPPORTED: - prop.p_type = AMF_NULL + case amfNull, amfUndefined, amfUnsupported: + prop.atype = amfNull - case AMF_REFERENCE: + case amfReference: // TODO use new logger here - log.Println("AMFProp_Decode: AMF_REFERENCE not supported!") - //RTMP_Log(RTMP_LOGERROR, "AMF_REFERENCE not supported!"); + // ??? log.Println("AMFProDecode: amfReference not supported!") + //RTMLog(RTMLOGERROR, "amfReference not supported!"); return -1 - case AMF_ECMA_ARRAY: + case amfEcmaArray: // next comes the rest, mixed array has a final 0x000009 mark and names, so its an object data = data[4:] - nRes = C_AMF_Decode(&prop.p_vu.p_object, data, 1) + nRes = amfDecode(&prop.vu.obj, data, 1) if nRes == -1 { return -1 } data = data[nRes:] - case AMF_OBJECT_END: + case amfObjectEnd: return -1 - case AMF_STRICT_ARRAY: - panic("AMF_STRICT_ARRAY not supported") + case amfStrictArray: + panic("amfStrictArray not supported") - case AMF_DATE: - panic("AMF_DATE not supported") + case amfDate: + panic("amfDate not supported") - case AMF_LONG_STRING, AMF_XML_DOC: - panic("AMF_LONG_STRING, AMF_XML_DOC not supported") + case amfLongSring, amfXmlDoc: + panic("amfLongSring, amfXmlDoc not supported") - case AMF_RECORDSET: + case amfRecordset: // TODO use new logger here - log.Println("AMFProp_Decode: AMF_RECORDSET reserved!") - //RTMP_Log(RTMP_LOGERROR, "AMF_RECORDSET reserved!"); + // ??? log.Println("AMFProDecode: amfRecordset reserved!") + //RTMLog(RTMLOGERROR, "amfRecordset reserved!"); return -1 - case AMF_TYPED_OBJECT: + case amfTypedObject: // TODO use new logger here - // RTMP_Log(RTMP_LOGERROR, "AMF_TYPED_OBJECT not supported!") + // RTMLog(RTMLOGERROR, "amfTyped_object not supported!") return -1 - case AMF_AVMPLUS: - panic("AMF_AVMPLUS not supported") + case amfAvmplus: + panic("amfAvmplus not supported") default: // TODO use new logger here - //RTMP_Log(RTMP_LOGDEBUG, "%s - unknown datatype 0x%02x, @%p", __FUNCTION__, - //prop.p_type, pBuffer - 1); + //RTMLog(RTMLOGDEBUG, "%s - unknown datatype 0x%02x, @%p", __FUNCTION__, + //prop.atype, pBuffer - 1); return -1 } return int32(nOriginalSize - len(data)) } -// void AMFProp_Reset(AMFObjectProperty* prop); -// amf.c +875 -func C_AMFProp_Reset(prop *C_AMFObjectProperty) { - if prop.p_type == AMF_OBJECT || prop.p_type == AMF_ECMA_ARRAY || - prop.p_type == AMF_STRICT_ARRAY { - C_AMF_Reset(&prop.p_vu.p_object) +func amfPropReset(prop *AMFProperty) { + if prop.atype == amfObject || prop.atype == amfEcmaArray || + prop.atype == amfStrictArray { + amfReset(&prop.vu.obj) } else { - prop.p_vu.p_aval = "" + prop.vu.aval = "" } - prop.p_type = AMF_INVALID + prop.atype = amfInvalid } -// char* AMF_Encode(AMFObject* obj, char* pBuffer, char* pBufEnd); -// amf.c +891 -func C_AMF_Encode(obj *C_AMFObject, dst []byte) []byte { +func amfEncode(a *AMF, dst []byte) []byte { if len(dst) < 5 { return nil } - dst[0] = AMF_OBJECT + dst[0] = amfObject dst = dst[1:] - for i := 0; i < len(obj.o_props); i++ { - dst = C_AMF_PropEncode(&obj.o_props[i], dst) + for i := 0; i < len(a.props); i++ { + dst = amfPropEncode(&a.props[i], dst) if dst == nil { - log.Println("C_AMF_Encode: failed to encode property in index") + // ??? log.Println("amfEncode: failed to encode property in index") break } } @@ -462,25 +452,23 @@ func C_AMF_Encode(obj *C_AMFObject, dst []byte) []byte { if len(dst) < 4 { return nil } - return C_AMF_EncodeInt24(dst, AMF_OBJECT_END) + return amfEncodeInt24(dst, amfObjectEnd) } -// char* AMF_EncodeEcmaArray(AMFObject* obj, char* pBuffer, char* pBufEnd); -// amf.c +924 -func C_AMF_EncodeEcmaArray(obj *C_AMFObject, dst []byte) []byte { +func amfEncodeEcmaArray(a *AMF, dst []byte) []byte { if len(dst) < 5 { return nil } - dst[0] = AMF_ECMA_ARRAY + dst[0] = amfEcmaArray dst = dst[1:] - binary.BigEndian.PutUint32(dst[:4], uint32(len(obj.o_props))) + binary.BigEndian.PutUint32(dst[:4], uint32(len(a.props))) dst = dst[4:] - for i := 0; i < len(obj.o_props); i++ { - dst = C_AMF_PropEncode(&obj.o_props[i], dst) + for i := 0; i < len(a.props); i++ { + dst = amfPropEncode(&a.props[i], dst) if dst == nil { - log.Println("C_AMF_EncodeEcmaArray: failed to encode property!") + // ??? log.Println("amfEncodeEcmaArray: failed to encode property!") break } } @@ -488,25 +476,24 @@ func C_AMF_EncodeEcmaArray(obj *C_AMFObject, dst []byte) []byte { if len(dst) < 4 { return nil } - return C_AMF_EncodeInt24(dst, AMF_OBJECT_END) + return amfEncodeInt24(dst, amfObjectEnd) } -// char* AMF_EncodeArray(AMFObject* obj, char* pBuffer, char* pBufEnd); -// amf.c +959 -func C_AMF_EncodeArray(obj *C_AMFObject, dst []byte) []byte { +// not used? +func amfEncodeArray(a *AMF, dst []byte) []byte { if len(dst) < 5 { return nil } - dst[0] = AMF_STRICT_ARRAY + dst[0] = amfStrictArray dst = dst[1:] - binary.BigEndian.PutUint32(dst[:4], uint32(len(obj.o_props))) + binary.BigEndian.PutUint32(dst[:4], uint32(len(a.props))) dst = dst[4:] - for i := 0; i < len(obj.o_props); i++ { - dst = C_AMF_PropEncode(&obj.o_props[i], dst) + for i := 0; i < len(a.props); i++ { + dst = amfPropEncode(&a.props[i], dst) if dst == nil { - log.Println("C_AMF_EncodeArray: failed to encode property!") + // ??? log.Println("amfEncodeArray: failed to encode property!") break } } @@ -514,66 +501,48 @@ func C_AMF_EncodeArray(obj *C_AMFObject, dst []byte) []byte { return dst } -// int AMF_Decode(AMFObject *obj, const char* pBuffer, int nSize, int bDecodeName); -// amf.c +1180 -func C_AMF_Decode(obj *C_AMFObject, data []byte, bDecodeName int32) int32 { +func amfDecode(a *AMF, data []byte, bDecodeName int32) int32 { nOriginalSize := len(data) - obj.o_props = obj.o_props[:0] + a.props = a.props[:0] for len(data) != 0 { - if len(data) >= 3 && C_AMF_DecodeInt24(data[:3]) == AMF_OBJECT_END { + if len(data) >= 3 && amfDecodeInt24(data[:3]) == amfObjectEnd { data = data[3:] break } - var prop C_AMFObjectProperty - nRes := C_AMFProp_Decode(&prop, data, bDecodeName) - // nRes = int32(C.AMFProp_Decode(&prop, (*byte)(unsafe.Pointer(pBuffer)), + var prop AMFProperty + nRes := amfProDecode(&prop, data, bDecodeName) + // nRes = int32(C.AMFProDecode(&prop, (*byte)(unsafe.Pointer(pBuffer)), // int32(nSize), int32(bDecodeName))) if nRes == -1 { return -1 } data = data[nRes:] - obj.o_props = append(obj.o_props, prop) + a.props = append(a.props, prop) } return int32(nOriginalSize - len(data)) } -// AMFObjectProperty* AMF_GetProp(AMFObject *obj, const AVal* name, int nIndex); -// amf.c + 1249 -func C_AMF_GetProp(obj *C_AMFObject, name string, idx int32) *C_AMFObjectProperty { +func amfGetProp(a *AMF, name string, idx int32) *AMFProperty { if idx >= 0 { - if idx < int32(len(obj.o_props)) { - return &obj.o_props[idx] + if idx < int32(len(a.props)) { + return &a.props[idx] } } else { - for i, p := range obj.o_props { - if p.p_name == name { - return &obj.o_props[i] + for i, p := range a.props { + if p.name == name { + return &a.props[i] } } } - return &AMFProp_Invalid + return &amfPropInvalid } -// void AMF_Reset(AMFObject* obj); -// amf.c +1282 -func C_AMF_Reset(obj *C_AMFObject) { - for i := range obj.o_props { - C_AMFProp_Reset(&obj.o_props[i]) +func amfReset(a *AMF) { + for i := range a.props { + amfPropReset(&a.props[i]) } - obj.o_props = obj.o_props[:0] + *a = AMF{} } - -/* -// void AMF3CD_AddProp(AMF3ClassDef *cd, AVal *prop); -// amf.c +1298 -func AMF3CD_AddProp(cd *C.AMF3ClassDef, prop *C_AVal) { - if cd.cd_num&0x0f == 0 { - cd.cd_props = (*C_AVal)(realloc(unsafe.Pointer(cd.cd_props), int(uintptr(cd.cd_num+16)*unsafe.Sizeof(C_AVal{})))) - } - *(*C_AVal)(incPtr(unsafe.Pointer(cd.cd_props), int(cd.cd_num), int(unsafe.Sizeof(C_AVal{})))) = *prop - cd.cd_num++ -} -*/ diff --git a/rtmp/amf_headers.go b/rtmp/amf_headers.go index 453b38de..61a7904b 100644 --- a/rtmp/amf_headers.go +++ b/rtmp/amf_headers.go @@ -31,51 +31,4 @@ LICENSE */ package rtmp -const ( - AMF_NUMBER = iota - AMF_BOOLEAN - AMF_STRING - AMF_OBJECT - AMF_MOVIECLIP /* reserved, not used */ - AMF_NULL - AMF_UNDEFINED - AMF_REFERENCE - AMF_ECMA_ARRAY - AMF_OBJECT_END - AMF_STRICT_ARRAY - AMF_DATE - AMF_LONG_STRING - AMF_UNSUPPORTED - AMF_RECORDSET /* reserved, not used */ - AMF_XML_DOC - AMF_TYPED_OBJECT - AMF_AVMPLUS /* switch to AMF3 */ - AMF_INVALID = 0xff -) -// typedef enum -// amf.h +40 -type C_AMFDataType int32 - -// typedef struct AMF_Object -// amf.h +67 -type C_AMFObject struct { - o_props []C_AMFObjectProperty -} - -// typedef struct P_vu -// amf.h +73 -type P_vu struct { - p_number float64 - p_aval string - p_object C_AMFObject -} - -// typedef struct AMFObjectProperty -// amf.h +79 -type C_AMFObjectProperty struct { - p_name string - p_type C_AMFDataType - p_vu P_vu - p_UTCoffset int16 -} diff --git a/rtmp/packet.go b/rtmp/packet.go index dac3d803..a61d7bc3 100644 --- a/rtmp/packet.go +++ b/rtmp/packet.go @@ -182,9 +182,9 @@ func (pkt *packet) read(s *Session) error { hSize := len(hbuf) - len(header) + size if size >= 3 { - pkt.timestamp = C_AMF_DecodeInt24(header[:3]) + pkt.timestamp = amfDecodeInt24(header[:3]) if size >= 6 { - pkt.bodySize = C_AMF_DecodeInt24(header[3:6]) + pkt.bodySize = amfDecodeInt24(header[3:6]) pkt.bytesRead = 0 if size > 6 { @@ -205,7 +205,7 @@ func (pkt *packet) read(s *Session) error { return err } // TODO: port this - pkt.timestamp = C_AMF_DecodeInt32(header[size : size+4]) + pkt.timestamp = amfDecodeInt32(header[size : size+4]) hSize += 4 } @@ -392,12 +392,12 @@ func (pkt *packet) write(s *Session, queue bool) error { if ts > 0xffffff { res = 0xffffff } - C_AMF_EncodeInt24(headBytes[headerIdx:], int32(res)) + amfEncodeInt24(headBytes[headerIdx:], int32(res)) headerIdx += 3 // 24bits } if headerSizes[pkt.headerType] > 4 { - C_AMF_EncodeInt24(headBytes[headerIdx:], int32(pkt.bodySize)) + amfEncodeInt24(headBytes[headerIdx:], int32(pkt.bodySize)) headerIdx += 3 // 24bits headBytes[headerIdx] = pkt.packetType headerIdx++ @@ -409,7 +409,7 @@ func (pkt *packet) write(s *Session, queue bool) error { } if ts >= 0xffffff { - C_AMF_EncodeInt32(headBytes[headerIdx:], int32(ts)) + amfEncodeInt32(headBytes[headerIdx:], int32(ts)) headerIdx += 4 // 32bits } @@ -479,7 +479,7 @@ func (pkt *packet) write(s *Session, queue bool) error { } if ts >= 0xffffff { extendedTimestamp := headBytes[origIdx+1+cSize:] - C_AMF_EncodeInt32(extendedTimestamp[:4], int32(ts)) + amfEncodeInt32(extendedTimestamp[:4], int32(ts)) } } } @@ -487,12 +487,12 @@ func (pkt *packet) write(s *Session, queue bool) error { // We invoked a remote method if pkt.packetType == packetTypeInvoke { buf := pkt.body[1:] - meth := C_AMF_DecodeString(buf) + meth := amfDecodeString(buf) s.log(DebugLevel, pkg+"invoking method "+meth) // keep it in call queue till result arrives if queue { buf = buf[3+len(meth):] - txn := int32(C_AMF_DecodeNumber(buf[:8])) + txn := int32(amfDecodeNumber(buf[:8])) s.methodCalls = append(s.methodCalls, method{name: meth, num: txn}) } } diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index a5a8e82b..c530ed38 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -7,7 +7,7 @@ DESCRIPTION AUTHORS Saxon Nelson-Milton - Dan Kortschak + Dan Kortschak ! Alan Noble LICENSE @@ -252,20 +252,20 @@ func handlePacket(s *Session, pkt *packet) error { switch pkt.packetType { case packetTypeChunkSize: if pkt.bodySize >= 4 { - s.inChunkSize = int32(C_AMF_DecodeInt32(pkt.body[:4])) + s.inChunkSize = int32(amfDecodeInt32(pkt.body[:4])) } case packetTypeBytesReadReport: - s.serverBW = int32(C_AMF_DecodeInt32(pkt.body[:4])) + s.serverBW = int32(amfDecodeInt32(pkt.body[:4])) case packetTypeControl: s.log(FatalLevel, pkg+"unsupported packet type packetTypeControl") case packetTypeServerBW: - s.serverBW = int32(C_AMF_DecodeInt32(pkt.body[:4])) + s.serverBW = int32(amfDecodeInt32(pkt.body[:4])) case packetTypeClientBW: - s.clientBW = int32(C_AMF_DecodeInt32(pkt.body[:4])) + s.clientBW = int32(amfDecodeInt32(pkt.body[:4])) if pkt.bodySize > 4 { s.clientBW2 = pkt.body[4] } else { @@ -312,72 +312,72 @@ func sendConnectPacket(s *Session) error { } enc := pkt.body - enc = C_AMF_EncodeString(enc, avConnect) + enc = amfEncodeString(enc, avConnect) if enc == nil { return errEncoding } s.numInvokes += 1 - enc = C_AMF_EncodeNumber(enc, float64(s.numInvokes)) + enc = amfEncodeNumber(enc, float64(s.numInvokes)) if enc == nil { return errEncoding } - enc[0] = AMF_OBJECT + enc[0] = amfObject enc = enc[1:] - enc = C_AMF_EncodeNamedString(enc, avApp, s.link.app) + enc = amfEncodeNamedString(enc, avApp, s.link.app) if enc == nil { return errEncoding } if s.link.protocol&featureWrite != 0 { - enc = C_AMF_EncodeNamedString(enc, avType, avNonprivate) + enc = amfEncodeNamedString(enc, avType, avNonprivate) if enc == nil { return errEncoding } } if s.link.flashVer != "" { - enc = C_AMF_EncodeNamedString(enc, avFlashver, s.link.flashVer) + enc = amfEncodeNamedString(enc, avFlashver, s.link.flashVer) if enc == nil { return errEncoding } } if s.link.swfUrl != "" { - enc = C_AMF_EncodeNamedString(enc, avSwfUrl, s.link.swfUrl) + enc = amfEncodeNamedString(enc, avSwfUrl, s.link.swfUrl) if enc == nil { return errEncoding } } if s.link.tcUrl != "" { - enc = C_AMF_EncodeNamedString(enc, avTcUrl, s.link.tcUrl) + enc = amfEncodeNamedString(enc, avTcUrl, s.link.tcUrl) if enc == nil { return errEncoding } } if s.link.protocol&featureWrite == 0 { - enc = C_AMF_EncodeNamedBoolean(enc, avFpad, false) + enc = amfEncodeNamedBoolean(enc, avFpad, false) if enc == nil { return errEncoding } - enc = C_AMF_EncodeNamedNumber(enc, avCapabilities, 15) + enc = amfEncodeNamedNumber(enc, avCapabilities, 15) if enc == nil { return errEncoding } - enc = C_AMF_EncodeNamedNumber(enc, avAudioCodecs, s.audioCodecs) + enc = amfEncodeNamedNumber(enc, avAudioCodecs, s.audioCodecs) if enc == nil { return errEncoding } - enc = C_AMF_EncodeNamedNumber(enc, avVideoCodecs, s.videoCodecs) + enc = amfEncodeNamedNumber(enc, avVideoCodecs, s.videoCodecs) if enc == nil { return errEncoding } - enc = C_AMF_EncodeNamedNumber(enc, avVideoFunction, 1) + enc = amfEncodeNamedNumber(enc, avVideoFunction, 1) if enc == nil { return errEncoding } if s.link.pageUrl != "" { - enc = C_AMF_EncodeNamedString(enc, avPageUrl, s.link.pageUrl) + enc = amfEncodeNamedString(enc, avPageUrl, s.link.pageUrl) if enc == nil { return errEncoding } @@ -385,29 +385,29 @@ func sendConnectPacket(s *Session) error { } if s.encoding != 0.0 || s.sendEncoding { - enc = C_AMF_EncodeNamedNumber(enc, avObjectEncoding, s.encoding) + enc = amfEncodeNamedNumber(enc, avObjectEncoding, s.encoding) if enc == nil { return errEncoding } } - copy(enc, []byte{0, 0, AMF_OBJECT_END}) + copy(enc, []byte{0, 0, amfObjectEnd}) enc = enc[3:] // add auth string if s.link.auth != "" { - enc = C_AMF_EncodeBoolean(enc, s.link.flags&linkAuth != 0) + enc = amfEncodeBoolean(enc, s.link.flags&linkAuth != 0) if enc == nil { return errEncoding } - enc = C_AMF_EncodeString(enc, s.link.auth) + enc = amfEncodeString(enc, s.link.auth) if enc == nil { return errEncoding } } - for i := range s.link.extras.o_props { - enc = C_AMF_PropEncode(&s.link.extras.o_props[i], enc) + for i := range s.link.extras.props { + enc = amfPropEncode(&s.link.extras.props[i], enc) if enc == nil { return errEncoding } @@ -429,16 +429,16 @@ func sendCreateStream(s *Session) error { } enc := pkt.body - enc = C_AMF_EncodeString(enc, avCreatestream) + enc = amfEncodeString(enc, avCreatestream) if enc == nil { return errEncoding } s.numInvokes++ - enc = C_AMF_EncodeNumber(enc, float64(s.numInvokes)) + enc = amfEncodeNumber(enc, float64(s.numInvokes)) if enc == nil { return errEncoding } - enc[0] = AMF_NULL + enc[0] = amfNull enc = enc[1:] pkt.bodySize = uint32((len(pbuf) - fullHeaderSize) - len(enc)) @@ -457,18 +457,18 @@ func sendReleaseStream(s *Session) error { } enc := pkt.body - enc = C_AMF_EncodeString(enc, avReleasestream) + enc = amfEncodeString(enc, avReleasestream) if enc == nil { return errEncoding } s.numInvokes++ - enc = C_AMF_EncodeNumber(enc, float64(s.numInvokes)) + enc = amfEncodeNumber(enc, float64(s.numInvokes)) if enc == nil { return errEncoding } - enc[0] = AMF_NULL + enc[0] = amfNull enc = enc[1:] - enc = C_AMF_EncodeString(enc, s.link.playpath) + enc = amfEncodeString(enc, s.link.playpath) if enc == nil { return errEncoding } @@ -488,18 +488,18 @@ func sendFCPublish(s *Session) error { } enc := pkt.body - enc = C_AMF_EncodeString(enc, avFCPublish) + enc = amfEncodeString(enc, avFCPublish) if enc == nil { return errEncoding } s.numInvokes++ - enc = C_AMF_EncodeNumber(enc, float64(s.numInvokes)) + enc = amfEncodeNumber(enc, float64(s.numInvokes)) if enc == nil { return errEncoding } - enc[0] = AMF_NULL + enc[0] = amfNull enc = enc[1:] - enc = C_AMF_EncodeString(enc, s.link.playpath) + enc = amfEncodeString(enc, s.link.playpath) if enc == nil { return errEncoding } @@ -520,18 +520,18 @@ func sendFCUnpublish(s *Session) error { } enc := pkt.body - enc = C_AMF_EncodeString(enc, avFCUnpublish) + enc = amfEncodeString(enc, avFCUnpublish) if enc == nil { return errEncoding } s.numInvokes++ - enc = C_AMF_EncodeNumber(enc, float64(s.numInvokes)) + enc = amfEncodeNumber(enc, float64(s.numInvokes)) if enc == nil { return errEncoding } - enc[0] = AMF_NULL + enc[0] = amfNull enc = enc[1:] - enc = C_AMF_EncodeString(enc, s.link.playpath) + enc = amfEncodeString(enc, s.link.playpath) if enc == nil { return errEncoding } @@ -552,22 +552,22 @@ func sendPublish(s *Session) error { } enc := pkt.body - enc = C_AMF_EncodeString(enc, avPublish) + enc = amfEncodeString(enc, avPublish) if enc == nil { return errEncoding } s.numInvokes++ - enc = C_AMF_EncodeNumber(enc, float64(s.numInvokes)) + enc = amfEncodeNumber(enc, float64(s.numInvokes)) if enc == nil { return errEncoding } - enc[0] = AMF_NULL + enc[0] = amfNull enc = enc[1:] - enc = C_AMF_EncodeString(enc, s.link.playpath) + enc = amfEncodeString(enc, s.link.playpath) if enc == nil { return errEncoding } - enc = C_AMF_EncodeString(enc, avLive) + enc = amfEncodeString(enc, avLive) if enc == nil { return errEncoding } @@ -588,18 +588,18 @@ func sendDeleteStream(s *Session, dStreamId float64) error { } enc := pkt.body - enc = C_AMF_EncodeString(enc, avDeletestream) + enc = amfEncodeString(enc, avDeletestream) if enc == nil { return errEncoding } s.numInvokes++ - enc = C_AMF_EncodeNumber(enc, float64(s.numInvokes)) + enc = amfEncodeNumber(enc, float64(s.numInvokes)) if enc == nil { return errEncoding } - enc[0] = AMF_NULL + enc[0] = amfNull enc = enc[1:] - enc = C_AMF_EncodeNumber(enc, dStreamId) + enc = amfEncodeNumber(enc, dStreamId) if enc == nil { return errEncoding } @@ -621,7 +621,7 @@ func sendBytesReceived(s *Session) error { enc := pkt.body s.nBytesInSent = s.nBytesIn - enc = C_AMF_EncodeInt32(enc, s.nBytesIn) + enc = amfEncodeInt32(enc, s.nBytesIn) if enc == nil { return errEncoding } @@ -641,16 +641,16 @@ func sendCheckBW(s *Session) error { } enc := pkt.body - enc = C_AMF_EncodeString(enc, av_checkbw) + enc = amfEncodeString(enc, av_checkbw) if enc == nil { return errEncoding } s.numInvokes++ - enc = C_AMF_EncodeNumber(enc, float64(s.numInvokes)) + enc = amfEncodeNumber(enc, float64(s.numInvokes)) if enc == nil { return errEncoding } - enc[0] = AMF_NULL + enc[0] = amfNull enc = enc[1:] pkt.bodySize = uint32((len(pbuf) - fullHeaderSize) - len(enc)) @@ -670,15 +670,15 @@ func handleInvoke(s *Session, body []byte) error { if body[0] != 0x02 { return errInvalidBody } - var obj C_AMFObject - nRes := C_AMF_Decode(&obj, body, 0) + var obj AMF + nRes := amfDecode(&obj, body, 0) if nRes < 0 { return errDecoding } - meth := C_AMFProp_GetString(C_AMF_GetProp(&obj, "", 0)) + meth := amfPropGetString(amfGetProp(&obj, "", 0)) s.log(DebugLevel, pkg+"invoking method "+meth) - txn := C_AMFProp_GetNumber(C_AMF_GetProp(&obj, "", 1)) + txn := amfPropGetNumber(amfGetProp(&obj, "", 1)) switch meth { case av_result: @@ -718,7 +718,7 @@ func handleInvoke(s *Session, body []byte) error { } case avCreatestream: - s.streamID = int32(C_AMFProp_GetNumber(C_AMF_GetProp(&obj, "", 3))) + s.streamID = int32(amfPropGetNumber(amfGetProp(&obj, "", 3))) if s.link.protocol&featureWrite == 0 { return errNotWritable } @@ -755,10 +755,10 @@ func handleInvoke(s *Session, body []byte) error { s.log(FatalLevel, pkg+"unsupported method avClose") case avOnStatus: - var obj2 C_AMFObject - C_AMFProp_GetObject(C_AMF_GetProp(&obj, "", 3), &obj2) - code := C_AMFProp_GetString(C_AMF_GetProp(&obj2, avCode, -1)) - level := C_AMFProp_GetString(C_AMF_GetProp(&obj2, avLevel, -1)) + var obj2 AMF + amfPropGetObject(amfGetProp(&obj, "", 3), &obj2) + code := amfPropGetString(amfGetProp(&obj2, avCode, -1)) + level := amfPropGetString(amfGetProp(&obj2, avLevel, -1)) s.log(DebugLevel, pkg+"onStatus", "code", code, "level", level) switch code { @@ -797,7 +797,7 @@ func handleInvoke(s *Session, body []byte) error { s.log(FatalLevel, pkg+"unknown method "+meth) } leave: - C_AMF_Reset(&obj) + amfReset(&obj) return nil } diff --git a/rtmp/session.go b/rtmp/session.go index be6b8945..aaba6e85 100644 --- a/rtmp/session.go +++ b/rtmp/session.go @@ -79,7 +79,7 @@ type link struct { auth string flashVer string token string - extras C_AMFObject + extras AMF flags int32 swfAge int32 protocol int32 @@ -187,8 +187,8 @@ func (s *Session) Write(data []byte) (int, error) { pkt := packet{ packetType: data[0], - bodySize: C_AMF_DecodeInt24(data[1:4]), - timestamp: C_AMF_DecodeInt24(data[4:7]) | uint32(data[7])<<24, + bodySize: amfDecodeInt24(data[1:4]), + timestamp: amfDecodeInt24(data[4:7]) | uint32(data[7])<<24, channel: chanSource, info: s.streamID, }