diff --git a/rtmp/NOT_PORTED.txt b/rtmp/NOT_PORTED.txt new file mode 100644 index 00000000..b1c983e3 --- /dev/null +++ b/rtmp/NOT_PORTED.txt @@ -0,0 +1,16 @@ +HandleAudio +HandleVideo +HandleMetadata +DecodeTEA +SendSecureTokenResponse +RTMP_FindFirstMatchingProperty +RTMP_SendServerBW +RTMP_SendCtrl +SendUsherToken +SendFCSubscribe +SendPlaylist +SendPlay +SendPlaylist +SendPong +SendCheckBWResult +RTMP_SendPause diff --git a/rtmp/amf.go b/rtmp/amf.go new file mode 100644 index 00000000..5745ff70 --- /dev/null +++ b/rtmp/amf.go @@ -0,0 +1,848 @@ +/* +NAME + amf.go + +DESCRIPTION + See Readme.md + +AUTHORS + Saxon Nelson-Milton + Dan Kortschak + Jake Lane + +LICENSE + amf.go is Copyright (C) 2017 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 + Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + It 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 revid in gpl.txt. If not, see http://www.gnu.org/licenses. + + Derived from librtmp under the GNU Lesser General Public License 2.1 + Copyright (C) 2005-2008 Team XBMC http://www.xbmc.org + Copyright (C) 2008-2009 Andrej Stepanchuk + Copyright (C) 2009-2010 Howard Chu +*/ +package rtmp + +/* +#include +*/ +import "C" + +import ( + "log" + "unsafe" +) + +var ( + AMFObj_Invalid C_AMFObject + AMFProp_Invalid = C_AMFObjectProperty{p_type: AMF_INVALID} +) + +const ( + AMF3_INTEGER_MAX = 268435455 + AMF3_INTEGER_MIN = -268435456 +) + +// unsigned short AMF_DecodeInt16(const char* data); +// amf.c +41 +func C_AMF_DecodeInt16(data *byte) uint16 { + c := unsafe.Pointer(data) + return uint16(*(*uint8)(c)<<8 | *(*byte)(incBytePtr(c, 1))) +} + +// unsigned int AMF_DecodeInt24(const char* data); +// amf.c +50 +func C_AMF_DecodeInt24(data *byte) uint32 { + c := (*[3]byte)(unsafe.Pointer(data)) + return (uint32(c[0]) << 16) | (uint32(c[1]) << 8) | uint32(c[2]) + /* + // TODO Understand logic and simplify + c := (*uint8)(unsafe.Pointer(data)) + dst := uint32(int32(*c) << 16) + dst |= uint32(int32(*((*uint8)(unsafe.Pointer(uintptr(unsafe.Pointer(c)) + + (uintptr)(int32(1))*unsafe.Sizeof(*c))))) << 8) + dst |= uint32(int32(*((*uint8)(unsafe.Pointer(uintptr(unsafe.Pointer(c)) + + (uintptr)(int32(2))*unsafe.Sizeof(*c)))))) + return dst + */ +} + +// unsigned int AMF_DeocdeInt32(const char* data); +// amf.c +59 +func C_AMF_DecodeInt32(data *byte) uint32 { + c := (*uint8)(data) + val := uint32( + int32(*c)<<24 | + int32(*(*uint8)(incBytePtr(unsafe.Pointer(c), 1)))<<16 | + int32(*(*uint8)(incBytePtr(unsafe.Pointer(c), 2)))<<8 | + int32(*(*uint8)(incBytePtr(unsafe.Pointer(c), 3)))) + return uint32(val) +} + +// void AMF_DecodeString(const char* data, C_AVal* bv); +// amf.c +68 +func C_AMF_DecodeString(data *byte, bv *C_AVal) { + dataPtr := unsafe.Pointer(data) + //bv.av_len = int32(C.AMF_DecodeInt16((*byte)(dataPtr))) + bv.av_len = int32(C_AMF_DecodeInt16((*byte)(dataPtr))) + if bv.av_len > 0 { + bv.av_val = (*byte)(incBytePtr(dataPtr, 2)) + } else { + bv.av_val = nil + } +} + +// void AMF_DecodeLongString(const char *data, AVal *bv); +// amf.c +75 +func C_AMF_DecodeLongString(data *byte, bv *C_AVal) { + bv.av_len = int32(C_AMF_DecodeInt32(data)) + if bv.av_len > 0 { + bv.av_val = (*byte)(incBytePtr(unsafe.Pointer(data), 4)) + } else { + bv.av_val = nil + } +} + +// double AMF_DecodeNumber(const char* data); +// amf.c +82 +func C_AMF_DecodeNumber(data *byte) float64 { + var dVal float64 + var ci, co *uint8 + ci = (*uint8)(unsafe.Pointer(data)) + co = (*uint8)(unsafe.Pointer(&dVal)) + for i := 0; i < 8; i++ { + *indxBytePtr(unsafe.Pointer(co), i) = *indxBytePtr(unsafe.Pointer(ci), 7-i) + } + return dVal +} + +// int AMF_DecodeBoolean(const char *data); +// amf.c +132 +func C_AMF_DecodeBoolean(data *byte) int32 { + if *data != 0 { + return 1 + } + return 0 +} + +// char* AMF_EncodeInt16(char* output, char* outend, short nVal); +// amf.c +138 +func C_AMF_EncodeInt16(output *byte, outend *byte, nVal int16) *byte { + outputPtr := unsafe.Pointer(output) + outendPtr := unsafe.Pointer(outend) + if uintptr(outputPtr)+2 > uintptr(outendPtr) { + // length < 2 + return nil + } + // Assign output[1] + second := (*byte)(incBytePtr(outputPtr, 1)) + *second = (byte)(nVal & 0xff) + // Assign output[0] + *output = (byte)(nVal >> 8) + return (*byte)(incBytePtr(outputPtr, 2)) +} + +// char* AMF_EncodeInt24(char* output, char* outend, int nVal); +// amf.c +149 +func C_AMF_EncodeInt24(output *byte, outend *byte, nVal int32) *byte { + outputPtr := unsafe.Pointer(output) + outendPtr := unsafe.Pointer(outend) + if uintptr(outputPtr)+3 > uintptr(outendPtr) { + // length < 3 + return nil + } + // Assign output[2] + third := (*byte)(incBytePtr(outputPtr, 2)) + *third = (byte)(nVal & 0xff) + // Assign output[1] + second := (*byte)(incBytePtr(outputPtr, 1)) + *second = (byte)(nVal >> 8) + // Assign output[0] + *output = (byte)(nVal >> 16) + return (*byte)(incBytePtr(outputPtr, 3)) +} + +// char* AMF_EncodeInt32(char* output, char* outend, int nVal); +// amf.c +161 +func C_AMF_EncodeInt32(output *byte, outend *byte, nVal int32) *byte { + outputPtr := unsafe.Pointer(output) + outendPtr := unsafe.Pointer(outend) + if uintptr(outputPtr)+4 > uintptr(outendPtr) { + // length < 4 + return nil + } + // Assign output[3] + forth := (*byte)(incBytePtr(outputPtr, 3)) + *forth = (byte)(nVal & 0xff) + // Assign output[2] + third := (*byte)(incBytePtr(outputPtr, 2)) + *third = (byte)(nVal >> 8) + // Assign output[1] + second := (*byte)(incBytePtr(outputPtr, 1)) + *second = (byte)(nVal >> 16) + // Assign output[0] + *output = (byte)(nVal >> 24) + return (*byte)(incBytePtr(outputPtr, 4)) +} + +// char* AMF_EncodeString(char* output, char* outend, const C_AVal* bv); +// amf.c +174 +func C_AMF_EncodeString(output *byte, outend *byte, bv *C_AVal) *byte { + outputPtr := unsafe.Pointer(output) + outendPtr := unsafe.Pointer(outend) + if (bv.av_len < 65536 && uintptr(incBytePtr(outputPtr, 1+2+int(bv.av_len))) > + uintptr(outendPtr)) || uintptr(incBytePtr(outputPtr, 1+4+int(bv.av_len))) > + uintptr(outendPtr) { + return nil + } + if bv.av_len < 65536 { + *(*byte)(outputPtr) = AMF_STRING + outputPtr = incBytePtr(outputPtr, 1) + // TODO: port AMF_EncodeInt16 + outputPtr = unsafe.Pointer(C_AMF_EncodeInt16((*byte)(outputPtr), (*byte)( + outendPtr), int16(bv.av_len))) + //outputPtr = unsafe.Pointer(C_AMF_EncodeInt16((*byte)(outputPtr), + //(*byte)(outendPtr), (int16)(bv.av_len))) + } else { + *(*byte)(outputPtr) = AMF_LONG_STRING + outputPtr = incBytePtr(outputPtr, 1) + outputPtr = unsafe.Pointer(C_AMF_EncodeInt32((*byte)(outputPtr), (*byte)( + outendPtr), int32(bv.av_len))) + //outputPtr = unsafe.Pointer(C_AMF_EncodeInt32((*byte)(outputPtr), + //(*byte)(outendPtr), (int32)(bv.av_len))) + } + memmove(unsafe.Pointer(outputPtr), unsafe.Pointer(bv.av_val), uintptr(bv.av_len)) + //C.memcpy(unsafe.Pointer(outputPtr), unsafe.Pointer(bv.av_val), (C.size_t)(bv.av_len)) + outputPtr = incBytePtr(outputPtr, int(bv.av_len)) + return (*byte)(outputPtr) +} + +// char* AMF_EncodeNumber(char* output, char* outend, double dVal); +// amf.c +199 +func C_AMF_EncodeNumber(output *byte, outend *byte, dVal float64) *byte { + if int(uintptr(unsafe.Pointer(output)))+1+8 > int(uintptr(unsafe.Pointer(outend))) { + return nil + } + // TODO: port this + *(*byte)(unsafe.Pointer(output)) = AMF_NUMBER + output = (*byte)(incBytePtr(unsafe.Pointer(output), 1)) + // NOTE: here we are assuming little endian for both byte order and float + // word order + var ci, co *uint8 + ci = (*uint8)(unsafe.Pointer(&dVal)) + co = (*uint8)(unsafe.Pointer(output)) + for i := 0; i < 8; i++ { + *indxBytePtr(unsafe.Pointer(co), i) = *indxBytePtr(unsafe.Pointer(ci), 7-i) + } + return (*byte)(incBytePtr(unsafe.Pointer(output), 8)) +} + +// char* AMF_EncodeBoolean(char* output, char* outend, int bVal); +// amf.c +260 +func C_AMF_EncodeBoolean(output *byte, outend *byte, bVal int) *byte { + if int(uintptr(unsafe.Pointer(output)))+2 > int(uintptr(unsafe.Pointer(outend))) { + return nil + } + *(*byte)(unsafe.Pointer(output)) = AMF_BOOLEAN + output = (*byte)(incBytePtr(unsafe.Pointer(output), 1)) + val := byte(0x01) + if bVal == 0 { + val = byte(0x00) + } + *(*byte)(unsafe.Pointer(output)) = val + output = (*byte)(incBytePtr(unsafe.Pointer(output), 1)) + return output +} + +// char* AMF_EncodeNamedString(char* output, char* outend, const C_AVal* strName, const C_AVal* strValue); +// amf.c +273 +func C_AMF_EncodeNamedString(output *byte, outend *byte, strName *C_AVal, strValue *C_AVal) *byte { + if int(uintptr(unsafe.Pointer(output)))+2+int(strName.av_len) > int(uintptr(unsafe.Pointer(outend))) { + return nil + } + output = C_AMF_EncodeInt16(output, outend, int16(strName.av_len)) + memmove(unsafe.Pointer(output), unsafe.Pointer(strName.av_val), uintptr(strName.av_len)) + output = (*byte)(incBytePtr(unsafe.Pointer(output), int(strName.av_len))) + return C_AMF_EncodeString(output, outend, strValue) +} + +// char* AMF_EncodeNamedNumber(char* output, char* outend, const C_AVal* strName, double dVal); +// amf.c +286 +func C_AMF_EncodeNamedNumber(output *byte, outend *byte, strName *C_AVal, dVal float64) *byte { + if int(uintptr(unsafe.Pointer(output)))+2+int(strName.av_len) > int(uintptr(unsafe.Pointer(outend))) { + return nil + } + output = C_AMF_EncodeInt16(output, outend, int16(strName.av_len)) + memmove(unsafe.Pointer(output), unsafe.Pointer(strName.av_val), uintptr(strName.av_len)) + output = (*byte)(incBytePtr(unsafe.Pointer(output), int(strName.av_len))) + return C_AMF_EncodeNumber(output, outend, dVal) +} + +// char* AMF_EncodeNamedBoolean(char* output, char* outend, const C_AVal* strname, int bVal); +// amf.c +299 +func C_AMF_EncodeNamedBoolean(output *byte, outend *byte, strName *C_AVal, bVal int) *byte { + if int(uintptr(unsafe.Pointer(output)))+2+int(strName.av_len) > int(uintptr(unsafe.Pointer(outend))) { + return nil + } + output = C_AMF_EncodeInt16(output, outend, int16(strName.av_len)) + memmove(unsafe.Pointer(output), unsafe.Pointer(strName.av_val), uintptr(strName.av_len)) + output = (*byte)(incBytePtr(unsafe.Pointer(output), int(strName.av_len))) + return C_AMF_EncodeBoolean(output, outend, bVal) +} + +// void AMFProp_SetName(AMFObjectProperty *prop, AVal *name); +// amf.c +318 +func C_AMFProp_SetName(prop *C_AMFObjectProperty, name *C_AVal) { + prop.p_name = *name +} + +// double AMFProp_GetNumber(AMFObjectProperty* prop); +// amf.c +330 +func C_AMFProp_GetNumber(prop *C_AMFObjectProperty) float64 { + return float64(prop.p_vu.p_number) +} + +// void AMFProp_GetString(AMFObjectProperty* prop, AVal* str); +// amf.c +341 +func C_AMFProp_GetString(prop *C_AMFObjectProperty, str *C_AVal) { + if prop.p_type == AMF_STRING { + *str = prop.p_vu.p_aval + } else { + *str = AV_empty + } +} + +// 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 + } else { + *obj = AMFObj_Invalid + } +} + +// char* AMFPropEncode(AMFOBjectProperty* prop, char* pBufer, char* pBufEnd); +// amf.c +366 +func C_AMF_PropEncode(p *C_AMFObjectProperty, pBuffer *byte, pBufEnd *byte) *byte { + if p.p_type == AMF_INVALID { + return nil + } + + if p.p_type != AMF_NULL && int(uintptr(unsafe.Pointer(pBuffer)))+ + int(p.p_name.av_len)+2+1 >= int( + uintptr(unsafe.Pointer(pBufEnd))) { + return nil + } + + if p.p_type != AMF_NULL && p.p_name.av_len != 0 { + *indxBytePtr(unsafe.Pointer(pBuffer), 0) = byte(p.p_name.av_len >> 8) + pBuffer = (*byte)(incBytePtr(unsafe.Pointer(pBuffer), 1)) + *indxBytePtr(unsafe.Pointer(pBuffer), 0) = byte(p.p_name.av_len & 0xff) + pBuffer = (*byte)(incBytePtr(unsafe.Pointer(pBuffer), 1)) + memmove(unsafe.Pointer(pBuffer), unsafe.Pointer(p.p_name.av_val), + uintptr(p.p_name.av_len)) + pBuffer = (*byte)(incBytePtr(unsafe.Pointer(pBuffer), int(p.p_name.av_len))) + } + + switch p.p_type { + case AMF_NUMBER: + pBuffer = C_AMF_EncodeNumber(pBuffer, pBufEnd, float64(p.p_vu.p_number)) + case AMF_BOOLEAN: + val := 0 + if p.p_vu.p_number != 0 { + val = 1 + } + pBuffer = C_AMF_EncodeBoolean(pBuffer, pBufEnd, val) + case AMF_STRING: + pBuffer = C_AMF_EncodeString(pBuffer, pBufEnd, &p.p_vu.p_aval) + case AMF_NULL: + if uintptr(incBytePtr(unsafe.Pointer(pBuffer), 1)) >= uintptr(unsafe.Pointer( + pBufEnd)) { + return nil + } + *(*byte)(unsafe.Pointer(pBuffer)) = AMF_NULL + pBuffer = (*byte)(incBytePtr(unsafe.Pointer(pBuffer), 1)) + case AMF_OBJECT: + pBuffer = C_AMF_Encode(&p.p_vu.p_object, pBuffer, pBufEnd) + //pBuffer = (*byte)(unsafe.Pointer(C.AMF_Encode(&p.p_vu.p_object, (*byte)( + //unsafe.Pointer(pBuffer)), (*byte)(unsafe.Pointer(pBufEnd))))) + case AMF_ECMA_ARRAY: + pBuffer = C_AMF_EncodeEcmaArray(&p.p_vu.p_object, pBuffer, pBufEnd) + //pBuffer = (*byte)(unsafe.Pointer(C.AMF_EncodeEcmaArray(&p.p_vu.p_object, (*byte)(unsafe.Pointer(pBuffer)), (*byte)(unsafe.Pointer(pBufEnd))))) + case AMF_STRICT_ARRAY: + //pBuffer = (*byte)(unsafe.Pointer(C.AMF_EncodeArray(&p.p_vu.p_object, (*byte)(unsafe.Pointer(pBuffer)), (*byte)(unsafe.Pointer(pBufEnd))))) + pBuffer = C_AMF_EncodeArray(&p.p_vu.p_object, pBuffer, pBufEnd) + default: + log.Println("C_AMF_PropEncode: invalid type!") + pBuffer = nil + } + return pBuffer +} + +// int AMF3ReadInteger(const char *data, int32_t *valp); +// amf.c +426 +// TODO test +func C_AMF3ReadInteger(data *byte, valp *int32) int32 { + var i int + var val int32 + + for i <= 2 { + /* handle first 3 bytes */ + if *indxBytePtr(unsafe.Pointer(data), i)&0x80 != 0 { + /* byte used */ + val <<= 7 /* shift up */ + val |= int32(*indxBytePtr(unsafe.Pointer(data), i) & 0x7f) /* add bits */ + i++ + } else { + break + } + } + + if i > 2 { + /* use 4th byte, all 8bits */ + val <<= 8 + val |= int32(*indxBytePtr(unsafe.Pointer(data), 3)) + + /* range check */ + if val > AMF3_INTEGER_MAX { + val -= (1 << 29) + } + } else { + /* use 7bits of last unparsed byte (0xxxxxxx) */ + val <<= 7 + val |= int32(*indxBytePtr(unsafe.Pointer(data), i)) + } + + *valp = val + + if i > 2 { + return 4 + } + return int32(i + 1) +} + +// int AMF3ReadString(const char *data, AVal *str); +// amf.c +466 +func C_AMF3ReadString(data *byte, str *C_AVal) int32 { + var ref int32 + // assert elided - we will get a panic if it's nil. + + len := C_AMF3ReadInteger(data, &ref) + data = indxBytePtr(unsafe.Pointer(data), int(len)) + + if ref&0x1 == 0 { + /* reference: 0xxx */ + // TODO(kortschak) Logging. + // refIndex := (ref >> 1) + // RTMP_Log(RTMP_LOGDEBUG, + // "%s, string reference, index: %d, not supported, ignoring!", + // __FUNCTION__, refIndex); + str.av_val = nil + str.av_len = 0 + return len + } else { + nSize := (ref >> 1) + str.av_val = (*byte)(unsafe.Pointer(data)) + str.av_len = int32(nSize) + return len + nSize + } + return len +} + +// int AMFProp_Decode(C_AMFObjectProperty* prop, const char* pBuffer, int nSize, int bDecodeName); +// amf.c +619 +func C_AMFProp_Decode(prop *C_AMFObjectProperty, pBuffer *byte, nSize, bDecodeName int32) int32 { + + var nOriginalSize int32 = nSize + var nRes int32 + + prop.p_name.av_len = 0 + prop.p_name.av_val = nil + + if nSize == 0 || pBuffer == nil { + // TODO use new logger here + // RTMP_Log(RTMP_LOGDEBUG, "%s: Empty buffer/no buffer pointer!", __FUNCTION__); + return -1 + } + + if bDecodeName != 0 && nSize < 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__); + return -1 + } + + if bDecodeName != 0 { + nNameSize := C_AMF_DecodeInt16(pBuffer) + if int32(nNameSize) > nSize-2 { + // TODO use new logger here + //RTMP_Log(RTMP_LOGDEBUG, "%s: Name size out of range: namesize (%d) > len (%d) - 2",__FUNCTION__, nNameSize, nSize); + return -1 + } + + C_AMF_DecodeString(pBuffer, &prop.p_name) + nSize -= int32(2 + nNameSize) + pBuffer = (*byte)(incBytePtr(unsafe.Pointer(pBuffer), int(2+nNameSize))) + } + + if nSize == 0 { + return -1 + } + + nSize-- + + prop.p_type = (C_AMFDataType)(int32(*pBuffer)) + pBuffer = (*byte)(incBytePtr(unsafe.Pointer(pBuffer), 1)) + + switch prop.p_type { + case AMF_NUMBER: + if nSize < 8 { + return -1 + } + prop.p_vu.p_number = float64(C_AMF_DecodeNumber(pBuffer)) + nSize -= 8 + + case AMF_BOOLEAN: + panic("AMF_BOOLEAN not supported") + + case AMF_STRING: + var nStringSize = C_AMF_DecodeInt16(pBuffer) + + if int64(nSize) < int64(nStringSize)+2 { + return -1 + } + C_AMF_DecodeString(pBuffer, &prop.p_vu.p_aval) + nSize -= int32(2 + nStringSize) + + case AMF_OBJECT: + var nRes int32 = int32(C_AMF_Decode(&prop.p_vu.p_object, pBuffer, nSize, 1)) + if nRes == -1 { + return -1 + } + nSize -= nRes + + case AMF_MOVIECLIP: + // TODO use new logger here + log.Println("AMFProp_Decode: MAF_MOVIECLIP reserved!") + //RTMP_Log(RTMP_LOGERROR, "AMF_MOVIECLIP reserved!"); + return -1 + + case AMF_NULL, AMF_UNDEFINED, AMF_UNSUPPORTED: + prop.p_type = AMF_NULL + + case AMF_REFERENCE: + + // TODO use new logger here + log.Println("AMFProp_Decode: AMF_REFERENCE not supported!") + //RTMP_Log(RTMP_LOGERROR, "AMF_REFERENCE not supported!"); + return -1 + + case AMF_ECMA_ARRAY: + + nSize -= 4 + + // next comes the rest, mixed array has a final 0x000009 mark and names, so its an object + nRes = C_AMF_Decode(&prop.p_vu.p_object, (*byte)(incBytePtr( + unsafe.Pointer(pBuffer), 4)), nSize, 1) + if nRes == -1 { + return -1 + } + nSize -= nRes + + case AMF_OBJECT_END: + + return -1 + + case AMF_STRICT_ARRAY: + panic("AMF_STRICT_ARRAY not supported") + + case AMF_DATE: + panic("AMF_DATE not supported") + + case AMF_LONG_STRING, AMF_XML_DOC: + panic("AMF_LONG_STRING, AMF_XML_DOC not supported") + + case AMF_RECORDSET: + // TODO use new logger here + log.Println("AMFProp_Decode: AMF_RECORDSET reserved!") + //RTMP_Log(RTMP_LOGERROR, "AMF_RECORDSET reserved!"); + return -1 + + case AMF_TYPED_OBJECT: + + // TODO use new logger here + // RTMP_Log(RTMP_LOGERROR, "AMF_TYPED_OBJECT not supported!") + return -1 + + case AMF_AVMPLUS: + panic("AMF_AVMPLUS not supported") + + default: + // TODO use new logger here + //RTMP_Log(RTMP_LOGDEBUG, "%s - unknown datatype 0x%02x, @%p", __FUNCTION__, + //prop.p_type, pBuffer - 1); + return -1 + } + + return nOriginalSize - nSize +} + +// 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) + } else { + prop.p_vu.p_aval.av_len = 0 + prop.p_vu.p_aval.av_val = nil + } + prop.p_type = AMF_INVALID +} + +// char* AMF_Encode(AMFObject* obj, char* pBuffer, char* pBufEnd); +// amf.c +891 +func C_AMF_Encode(obj *C_AMFObject, pBuffer *byte, pBufEnd *byte) *byte { + if uintptr(unsafe.Pointer(pBuffer))+uintptr(4) >= uintptr(unsafe.Pointer(pBufEnd)) { + return nil + } + + *pBuffer = AMF_OBJECT + pBuffer = (*byte)(incBytePtr(unsafe.Pointer(pBuffer), 1)) + + for i := 0; i < int(obj.o_num); i++ { + res := C_AMF_PropEncode((*C_AMFObjectProperty)(incPtr(unsafe.Pointer( + obj.o_props), i, int(unsafe.Sizeof(*obj.o_props)))), pBuffer, pBufEnd) + if res == nil { + log.Println("C_AMF_Encode: failed to encode property in index") + break + } else { + pBuffer = res + } + } + + if uintptr(incBytePtr(unsafe.Pointer(pBuffer), 3)) >= uintptr(unsafe.Pointer(pBufEnd)) { + return nil + } + + pBuffer = C_AMF_EncodeInt24(pBuffer, pBufEnd, int32(AMF_OBJECT_END)) + + return pBuffer +} + +// char* AMF_EncodeEcmaArray(AMFObject* obj, char* pBuffer, char* pBufEnd); +// amf.c +924 +func C_AMF_EncodeEcmaArray(obj *C_AMFObject, pBuffer *byte, pBufEnd *byte) *byte { + if int(uintptr(unsafe.Pointer(pBuffer)))+4 >= int(uintptr(unsafe.Pointer(pBufEnd))) { + return nil + } + + *pBuffer = AMF_ECMA_ARRAY + pBuffer = (*byte)(incBytePtr(unsafe.Pointer(pBuffer), 1)) + + pBuffer = C_AMF_EncodeInt32(pBuffer, pBufEnd, int32(obj.o_num)) + + for i := 0; i < int(obj.o_num); i++ { + res := C_AMF_PropEncode((*C_AMFObjectProperty)(incPtr(unsafe.Pointer( + obj.o_props), i, int(unsafe.Sizeof(*obj.o_props)))), pBuffer, pBufEnd) + if res == nil { + log.Println("C_AMF_EncodeEcmaArray: failed to encode property!") + break + } else { + pBuffer = res + } + } + + if int(uintptr(unsafe.Pointer(pBuffer)))+3 >= int(uintptr(unsafe.Pointer(pBufEnd))) { + return nil + } + + pBuffer = C_AMF_EncodeInt24(pBuffer, pBufEnd, AMF_OBJECT_END) + + return pBuffer +} + +// char* AMF_EncodeArray(AMFObject* obj, char* pBuffer, char* pBufEnd); +// amf.c +959 +func C_AMF_EncodeArray(obj *C_AMFObject, pBuffer *byte, pBufEnd *byte) *byte { + if int(uintptr(unsafe.Pointer(pBuffer)))+4 >= int(uintptr(unsafe.Pointer(pBufEnd))) { + return nil + } + + *pBuffer = AMF_STRICT_ARRAY + pBuffer = (*byte)(incBytePtr(unsafe.Pointer(pBuffer), 1)) + + pBuffer = C_AMF_EncodeInt32(pBuffer, pBufEnd, int32(obj.o_num)) + + for i := 0; i < int(obj.o_num); i++ { + res := C_AMF_PropEncode((*C_AMFObjectProperty)(incPtr(unsafe.Pointer( + obj.o_props), i, int(unsafe.Sizeof(*obj.o_props)))), pBuffer, pBufEnd) + if res == nil { + log.Println("C_AMF_EncodeEcmaArray: failed to encode property!") + break + } else { + pBuffer = res + } + } + + return pBuffer +} + +// int AMF_DecodeArray(AMFObject *obj, const char *pBuffer, int nSize, int nArrayLen, int bDecodeName); +// amf.c +993 +func C_AMF_DecodeArray(obj *C_AMFObject, pBuffer *byte, nSize, nArrayLen, bDecodeName int32) int32 { + nOriginalSize := nSize + var bError int32 = 0 + + obj.o_num = 0 + obj.o_props = nil + for nArrayLen > 0 { + var prop C_AMFObjectProperty + var nRes int32 + nArrayLen-- + + if nSize <= 0 { + bError = 1 + break + } + nRes = C_AMFProp_Decode(&prop, pBuffer, nSize, bDecodeName) + if nRes == -1 { + bError = 1 + break + } else { + nSize -= nRes + pBuffer = (*byte)(incBytePtr(unsafe.Pointer(pBuffer), int(nRes))) + C_AMF_AddProp(obj, &prop) + } + } + if bError != 0 { + return -1 + } + + return nOriginalSize - nSize +} + +// int AMF_Decode(AMFObject *obj, const char* pBuffer, int nSize, int bDecodeName); +// amf.c +1180 +func C_AMF_Decode(obj *C_AMFObject, pBuffer *byte, nSize int32, bDecodeName int32) int32 { + var nOriginalSize int32 = nSize + var bError int32 = 0 + + obj.o_num = 0 + obj.o_props = nil + + for nSize > 0 { + var prop C_AMFObjectProperty + var nRes int32 + + if nSize >= 3 && C_AMF_DecodeInt24(pBuffer) == AMF_OBJECT_END { + nSize -= 3 + bError = 0 + break + } + + if bError != 0 { + // TODO use new logger here + log.Println("AMF_Decode: decoding error, ignoring bytes until next known pattern!") + nSize-- + pBuffer = (*byte)(incBytePtr(unsafe.Pointer(pBuffer), 1)) + continue + } + // TODO port AMFProp_Decode + nRes = int32(C_AMFProp_Decode(&prop, (*byte)(unsafe.Pointer(pBuffer)), + int32(nSize), int32(bDecodeName))) + // nRes = int32(C.AMFProp_Decode(&prop, (*byte)(unsafe.Pointer(pBuffer)), + // int32(nSize), int32(bDecodeName))) + if nRes == -1 { + bError = 1 + break + } else { + nSize -= nRes + if nSize < 0 { + bError = 1 + break + } + pBuffer = (*byte)(incBytePtr(unsafe.Pointer(pBuffer), int(nRes))) + C_AMF_AddProp(obj, &prop) + } + } + + if bError != 0 { + return -1 + } + + return nOriginalSize - nSize +} + +// void AMF_AddProp(AMFObject* obj, const AMFObjectProperty* prop); +// amf.c + 1234 +func C_AMF_AddProp(obj *C_AMFObject, prop *C_AMFObjectProperty) { + if (obj.o_num & 0x0f) == 0 { + //obj.o_props = (*C_AMFObjectProperty)(realloc(unsafe.Pointer(obj.o_props), + //uint32(int(obj.o_num+16)*int(unsafe.Sizeof(*obj.o_props))))) + obj.o_props = (*C_AMFObjectProperty)(C.realloc(unsafe.Pointer(obj.o_props), + C.size_t(int(obj.o_num+16)*int(unsafe.Sizeof(*obj.o_props))))) + } + memmove(unsafe.Pointer(&(*(*C_AMFObjectProperty)(incPtr( + unsafe.Pointer(obj.o_props), int(obj.o_num), int(unsafe.Sizeof(*obj.o_props)))))), + unsafe.Pointer(prop), unsafe.Sizeof(*obj.o_props)) + obj.o_num++ +} + +// AMFObjectProperty* AMF_GetProp(AMFObject *obj, const AVal* name, int nIndex); +// amf.c + 1249 +func C_AMF_GetProp(obj *C_AMFObject, name *C_AVal, nIndex int32) *C_AMFObjectProperty { + if nIndex >= 0 { + if nIndex < int32(obj.o_num) { + return &(*(*C_AMFObjectProperty)(incPtr(unsafe.Pointer(obj.o_props), + int(nIndex), int(unsafe.Sizeof(*obj.o_props))))) + //return &obj.o_props[nIndex] + } + } else { + var n int32 + for n = 0; n < int32(obj.o_num); n++ { + if C_AVMATCH(&(*(*C_AMFObjectProperty)(incPtr(unsafe.Pointer(obj.o_props), + int(n), int(unsafe.Sizeof(*obj.o_props))))).p_name, name) != 0 { + return &(*(*C_AMFObjectProperty)(incPtr(unsafe.Pointer(obj.o_props), + int(n), int(unsafe.Sizeof(*obj.o_props))))) + } + } + } + return (*C_AMFObjectProperty)(&AMFProp_Invalid) +} + +// void AMF_Reset(AMFObject* obj); +// amf.c +1282 +func C_AMF_Reset(obj *C_AMFObject) { + var n int32 + for n = 0; n < int32(obj.o_num); n++ { + C_AMFProp_Reset(&(*(*C_AMFObjectProperty)(incPtr(unsafe.Pointer(obj.o_props), + int(n), int(unsafe.Sizeof(*obj.o_props)))))) + } + //C.free(unsafe.Pointer(obj.o_props)) + obj.o_props = nil + obj.o_num = 0 +} + +/* +// 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 new file mode 100644 index 00000000..02e5343b --- /dev/null +++ b/rtmp/amf_headers.go @@ -0,0 +1,117 @@ +/* +NAME + amf_headers.go + +DESCRIPTION + See Readme.md + +AUTHORS + Saxon Nelson-Milton + +LICENSE + amf_headers.go is Copyright (C) 2017 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 + Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + It 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 revid in gpl.txt. If not, see http://www.gnu.org/licenses. + + Derived from librtmp under the GNU Lesser General Public License 2.1 + Copyright (C) 2005-2008 Team XBMC http://www.xbmc.org + Copyright (C) 2008-2009 Andrej Stepanchuk + Copyright (C) 2009-2010 Howard Chu +*/ +package rtmp + +import ( + "unsafe" +) + +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 C_AVal +// amf.h +57 +type C_AVal struct { + av_val *byte + av_len int32 +} + +// C_AVal is in amf.h +// amf.h +62 +func AVC(str string) C_AVal { + var aval C_AVal + if len(str) != 0 { + aval.av_val = &([]byte(str)[0]) + } else { + aval.av_val = nil + } + aval.av_len = int32(len(str)) + return aval +} + +// #define AVMATCH(a1,a2) +// amf.h +63 +func C_AVMATCH(a1, a2 *C_AVal) int32 { + if a1.av_len == a2.av_len && memcmp(unsafe.Pointer(a1.av_val), + unsafe.Pointer(a2.av_val), int(a1.av_len)) == 0 { + return 1 + } else { + return 0 + } +} + +// typedef struct AMF_Object +// amf.h +67 +type C_AMFObject struct { + o_num int32 + o_props *C_AMFObjectProperty +} + +// typedef struct P_vu +// amf.h +73 +type P_vu struct { + p_number float64 + p_aval C_AVal + p_object C_AMFObject +} + +// typedef struct AMFObjectProperty +// amf.h +79 +type C_AMFObjectProperty struct { + p_name C_AVal + p_type C_AMFDataType + p_vu P_vu + p_UTCoffset int16 +} diff --git a/rtmp/parseurl.go b/rtmp/parseurl.go new file mode 100644 index 00000000..06b871db --- /dev/null +++ b/rtmp/parseurl.go @@ -0,0 +1,335 @@ +/* +NAME + parseurl.go + +DESCRIPTION + See Readme.md + +AUTHOR + Saxon Nelson-Milton + +LICENSE + parseurl.go is Copyright (C) 2017-2018 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 + Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + It 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 revid in gpl.txt. If not, see http://www.gnu.org/licenses. + + Derived from librtmp under the GNU Lesser General Public License 2.1 + Copyright (C) 2005-2008 Team XBMC http://www.xbmc.org + Copyright (C) 2008-2009 Andrej Stepanchuk + Copyright (C) 2009-2010 Howard Chu +*/ +package rtmp + +import ( + "fmt" + "log" + "strconv" + "strings" + "unsafe" +) + +// int RTMP_ParseURL(const char *url, int *protocol, AVal *host, unsigned int *port, +// AVal *playpath, AVal *app); +// parseurl.c +33 +func C_RTMP_ParseURL(url *byte, protocol *int32, host *C_AVal, port *uint32, + playpath *C_AVal, app *C_AVal) int { + + var p, end, col, ques, slash *byte + // TODO: use our logger here + // RTMP_Log(RTMP_LOGDEBUG, "Parsing..."); + + *protocol = RTMP_PROTOCOL_RTMP + *port = 0 + playpath.av_len = 0 + playpath.av_val = nil + app.av_len = 0 + app.av_val = nil + + p = strstr(url, goStrToCStr("://")) + + if p == nil { + // TODO: use our logger here + log.Println("RTMP URL: No :// in url!") + return 0 + } + /* + NOTE: the following code nees to be ported if we're using anything other than + rtmp! + { + int len = (int)(p-url); + + if(len == 4 && strncasecmp(url, "rtmp", 4)==0) + *protocol = RTMP_PROTOCOL_RTMP; + else if(len == 5 && strncasecmp(url, "rtmpt", 5)==0) + *protocol = RTMP_PROTOCOL_RTMPT; + else if(len == 5 && strncasecmp(url, "rtmps", 5)==0) + *protocol = RTMP_PROTOCOL_RTMPS; + else if(len == 5 && strncasecmp(url, "rtmpe", 5)==0) + *protocol = RTMP_PROTOCOL_RTMPE; + else if(len == 5 && strncasecmp(url, "rtmfp", 5)==0) + *protocol = RTMP_PROTOCOL_RTMFP; + else if(len == 6 && strncasecmp(url, "rtmpte", 6)==0) + *protocol = RTMP_PROTOCOL_RTMPTE; + else if(len == 6 && strncasecmp(url, "rtmpts", 6)==0) + *protocol = RTMP_PROTOCOL_RTMPTS; + else { + RTMP_Log(RTMP_LOGWARNING, "Unknown protocol!\n"); + goto parsehost; + } + } + */ + // TODO: implement new logger here + // RTMP_Log(RTMP_LOGDEBUG, "Parsed protocol: %d", *protocol); + + // Get the hostname + p = (*byte)(incBytePtr(unsafe.Pointer(p), 3)) + + // check for sudden death + if *p == 0 { + // TODO: use new logger here + // RTMP_Log(RTMP_LOGWARNING, "No hostname in URL!"); + return 0 + } + + end = (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(p)) + uintptr(strlen(p)))) + col = strchr(p, ':') + ques = strchr(p, '?') + slash = strchr(p, '/') + + { + var hostlen int32 + if slash != nil { + hostlen = int32(uintptr(unsafe.Pointer(slash)) - uintptr(unsafe.Pointer(p))) + } else { + hostlen = int32(uintptr(unsafe.Pointer(end)) - uintptr(unsafe.Pointer(p))) + } + if col != nil && int32(uintptr(unsafe.Pointer(col))-uintptr(unsafe.Pointer(p))) < hostlen { + hostlen = int32(uintptr(unsafe.Pointer(col)) - uintptr(unsafe.Pointer(p))) + } + + if hostlen < 256 { + host.av_val = (*byte)(unsafe.Pointer(p)) + host.av_len = int32(hostlen) + // TODO: use new logger with this + //RTMP_Log(RTMP_LOGDEBUG, "Parsed host : %.*s", hostlen, host.av_val); + } else { + // TODO: use new logger with this + // RTMP_Log(RTMP_LOGWARNING, "Hostname exceeds 255 characters!"); + } + + p = (*byte)(incBytePtr(unsafe.Pointer(p), int(hostlen))) + } + + // get port number if available + if *p == ':' { + var p2 uint32 + p = (*byte)(incBytePtr(unsafe.Pointer(p), 1)) + tmp, _ := strconv.Atoi(cStrToGoStr(p)) + p2 = uint32(tmp) + if p2 > 65535 { + // TODO: use new logger with this + // RTMP_Log(RTMP_LOGWARNING, "Invalid port number!"); + } else { + *port = p2 + } + } + + if slash == nil { + // TODO: use new logger + // RTMP_Log(RTMP_LOGWARNING, "No application or playpath in URL!"); + return 1 + } + + p = (*byte)(incBytePtr(unsafe.Pointer(slash), 1)) + + { + /* parse application + * + * rtmp://host[:port]/app[/appinstance][/...] + * application = app[/appinstance] + */ + + var slash2 *byte + var slash3 *byte = nil + var slash4 *byte = nil + var applen, appnamelen int32 + + slash2 = strchr(p, '/') + + if slash2 != nil { + slash3 = strchr((*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(slash2))+ + uintptr(1))), '/') + } + if slash3 != nil { + slash4 = strchr((*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(slash3))+ + uintptr(1))), '/') + } + + // ondemand, pass all parameters as app + applen = int32(uintptr(unsafe.Pointer(end)) - uintptr(unsafe.Pointer(p))) + appnamelen = applen + + switch { + case ques != nil && strstr(p, goStrToCStr("slist=")) != nil: + appnamelen = int32(uintptr(unsafe.Pointer(ques)) - uintptr(unsafe.Pointer(p))) + case strings.Compare(cStrToGoStr(p)[:9], "ondemand/") == 0: + /* app = ondemand/foobar, only pass app=ondemand */ + applen = 8 + appnamelen = 8 + default: + switch { + case slash4 != nil: + appnamelen = int32(uintptr(unsafe.Pointer(slash4)) - uintptr( + unsafe.Pointer(p))) + case slash3 != nil: + appnamelen = int32(uintptr(unsafe.Pointer(slash3)) - uintptr( + unsafe.Pointer(p))) + case slash2 != nil: + appnamelen = int32(uintptr(unsafe.Pointer(slash2)) - uintptr( + unsafe.Pointer(p))) + } + + applen = appnamelen + } + + app.av_val = (*byte)(unsafe.Pointer(p)) + app.av_len = int32(applen) + // TODO: use new logging here + // RTMP_Log(RTMP_LOGDEBUG, "Parsed app : %.*s", applen, p); + + p = (*byte)(incBytePtr(unsafe.Pointer(p), int(appnamelen))) + } + + if *p == '/' { + p = (*byte)(incBytePtr(unsafe.Pointer(p), 1)) + } + // NOTE: don't think we currently need this section - see 787 for this func + + if int(uintptr(unsafe.Pointer(end))-uintptr(unsafe.Pointer(p))) != 0 { + var av C_AVal + av.av_val = (*byte)(unsafe.Pointer(p)) + av.av_len = int32(uintptr(unsafe.Pointer(end)) - uintptr(unsafe.Pointer(p))) + // TODO: port THis + //C.RTMP_ParsePlaypath(&av, playpath) + C_RTMP_ParsePlaypath(&av, playpath) + } + + return 1 +} + +// void RTMP_ParsePlaypath(AVal *in, AVal *out); +// parseurl.c +201 +func C_RTMP_ParsePlaypath(in, out *C_AVal) { + var addMP4 int32 = 0 + var addMP3 int32 = 0 + var subExt int32 = 0 + playpath := in.av_val + var temp, q *byte + var ext *byte = nil + ppstart := (*byte)(unsafe.Pointer(playpath)) + var streamname, destptr, p *byte + + pplen := int32(in.av_len) + + out.av_val = nil + out.av_len = 0 + + temp = strstr((*byte)(unsafe.Pointer(ppstart)), goStrToCStr("slist=")) + if *ppstart == '?' && temp != nil { + ppstart = (*byte)(incBytePtr(unsafe.Pointer(temp), 6)) + pplen = int32(strlen(ppstart)) + + temp = strchr(ppstart, '&') + + if temp != nil { + pplen = int32(uintptr(unsafe.Pointer(temp)) - uintptr(unsafe.Pointer(ppstart))) + } + } + + q = strchr(ppstart, '?') + + if pplen >= 4 { + if q != nil { + ext = (*byte)(decBytePtr(unsafe.Pointer(q), 4)) + } else { + ext = (*byte)(indxBytePtr(unsafe.Pointer(ppstart), int(uintptr(pplen)- + uintptr(4)))) + } + switch { + case strings.Compare(cStrToGoStr(ext)[:4], ".f4v") == 0 || + strings.Compare(cStrToGoStr(ext)[:4], ".mp4") == 0: + addMP4 = 1 + subExt = 1 + case ppstart == (*byte)(unsafe.Pointer(playpath)) && strings.Compare( + cStrToGoStr(ext)[:4], ".flv") == 0: + subExt = 1 + case strings.Compare(cStrToGoStr(ext)[:4], ".mp3") == 0: + addMP3 = 1 + subExt = 1 + } + } + + streamname = (*byte)(malloc(uintptr(pplen + 4 + 1))) + + if streamname == nil { + return + } + + destptr = streamname + switch { + case addMP4 != 0: + if strings.Compare(cStrToGoStr(ppstart)[:4], "mp4") != 0 { + memmove(unsafe.Pointer(destptr), unsafe.Pointer(goStrToCStr("mp4:")), + uintptr(len("mp4:"))) + destptr = (*byte)(incBytePtr(unsafe.Pointer(destptr), 4)) + } else { + subExt = 0 + } + case addMP3 != 0: + if strings.Compare(cStrToGoStr(ppstart)[:4], "mp3") != 0 { + memmove(unsafe.Pointer(destptr), unsafe.Pointer(goStrToCStr("mp3:")), + uintptr(len("mp4:"))) + destptr = (*byte)(incBytePtr(unsafe.Pointer(destptr), 4)) + } else { + subExt = 0 + } + } + + p = (*byte)(ppstart) + for pplen > 0 { + if subExt != 0 && p == ext { + p = (*byte)(incBytePtr(unsafe.Pointer(p), 4)) + pplen -= 4 + continue + } + if *p == '%' { + var c uint32 + fmt.Sscanf(cStrToGoStr((*byte)(incBytePtr(unsafe.Pointer(p), 1))), "%02x", &c) + *indxBytePtr(unsafe.Pointer(destptr), 0) = byte(c) + destptr = (*byte)(incBytePtr(unsafe.Pointer(destptr), 1)) + pplen -= 3 + p = (*byte)(incBytePtr(unsafe.Pointer(p), 3)) + } else { + *indxBytePtr(unsafe.Pointer(destptr), 0) = *p + destptr = (*byte)(incBytePtr(unsafe.Pointer(destptr), 1)) + p = (*byte)(incBytePtr(unsafe.Pointer(p), 1)) + pplen-- + } + } + *destptr = '\x00' + + out.av_val = (*byte)(unsafe.Pointer(streamname)) + out.av_len = int32(uintptr(unsafe.Pointer(destptr)) - uintptr(unsafe.Pointer( + streamname))) +} diff --git a/rtmp/rtmp.c b/rtmp/rtmp.c deleted file mode 100644 index 663052a4..00000000 --- a/rtmp/rtmp.c +++ /dev/null @@ -1,86 +0,0 @@ -/* -NAME - rtmp.c - -DESCRIPTION - See Readme.md - -AUTHOR - Saxon Nelson-Milton - Dan Kortschak - -LICENSE - RTMPWrapper.c is Copyright (C) 2017 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 - Free Software Foundation, either version 3 of the License, or (at your - option) any later version. - - It 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 revid in gpl.txt. If not, see http://www.gnu.org/licenses. -*/ -#include -#include -#include -#include -#include -#include -#include - -RTMP* start_session(RTMP* rtmp, char* url, uint connect_timeout) { - rtmp = RTMP_Alloc(); - RTMP_Init(rtmp); - rtmp->Link.timeout = connect_timeout; - if (!RTMP_SetupURL(rtmp, url)) { - RTMP_Close(rtmp); - RTMP_Free(rtmp); - errno = EINVAL; - return NULL; - } - - RTMP_EnableWrite(rtmp); - RTMP_SetBufferMS(rtmp, 3600 * 1000); - if (!RTMP_Connect(rtmp, NULL)) { - RTMP_Close(rtmp); - RTMP_Free(rtmp); - errno = EIO; - return NULL; - } - - if (!RTMP_ConnectStream(rtmp, 0)) { - RTMP_Close(rtmp); - RTMP_Free(rtmp); - errno = EIO; - return NULL; - } - - return rtmp; -} - -unsigned int write_frame(RTMP* rtmp, char* data, uint data_length) { - if (!RTMP_IsConnected(rtmp)) { - return 1; - } - - if (!RTMP_Write(rtmp, (const char*)data, data_length)) { - return 2; - } - - return 0; -} - -unsigned int end_session(RTMP* rtmp) { - if (rtmp == NULL) { - return 3; - } - - RTMP_Close(rtmp); - RTMP_Free(rtmp); - return 0; -} diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index d04f1601..fcf49df1 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -5,10 +5,9 @@ NAME DESCRIPTION See Readme.md -AUTHOR +AUTHORS Saxon Nelson-Milton Dan Kortschak - Jake Lane LICENSE rtmp.go is Copyright (C) 2017 the Australian Ocean Lab (AusOcean) @@ -24,42 +23,24 @@ LICENSE for more details. You should have received a copy of the GNU General Public License - along with revid in gpl.txt. If not, see http://www.gnu.org/licenses. + along with revid in gpl.txt. If not, see http://www.gnu.org/licenses. - Derived from librtmp under the GNU Lesser General Public License 2.1 - Copyright (C) 2005-2008 Team XBMC http://www.xbmc.org - Copyright (C) 2008-2009 Andrej Stepanchuk - Copyright (C) 2009-2010 Howard Chu + Derived from librtmp under the GNU Lesser General Public License 2.1 + Copyright (C) 2005-2008 Team XBMC http://www.xbmc.org + Copyright (C) 2008-2009 Andrej Stepanchuk + Copyright (C) 2009-2010 Howard Chu */ package rtmp /* -#cgo CFLAGS: -I/usr/local/include/librtmp -#cgo LDFLAGS: -lrtmp -lz -Wl,-rpath=/usr/local/lib - #include -#include -#include -#include -#include -#include #include - -typedef enum { - RTMPT_OPEN=0, RTMPT_SEND, RTMPT_IDLE, RTMPT_CLOSE -} RTMPTCmd; +#include +#include typedef struct sockaddr_in sockaddr_in; typedef struct sockaddr sockaddr; -int add_addr_info(struct sockaddr_in *service, AVal *host, int port); -RTMP* start_session(RTMP* rtmp, char* url, uint connect_timeout); -int write_frame(RTMP* rtmp, char* data, uint data_length); -int end_session(RTMP* rtmp); -void AV_queue(RTMP_METHOD **vals, int *num, AVal *av, int txn); -int C_WriteN(RTMP *r, const char *buffer, int n); -int EncodeInt32LE(char *output, int nVal); -int HTTP_Post(RTMP *r, RTMPTCmd cmd, const char *buf, int len); */ import "C" @@ -71,109 +52,16 @@ import ( "math/rand" "reflect" "strconv" - "strings" "time" "unsafe" "github.com/chamaken/cgolmnl/inet" ) -const ( - RTMPT_OPEN = iota - RTMPT_SEND - RTMPT_IDLE - RTMPT_CLOSE -) - -// typedef enum -// amf.h +40 -type C_AMFDataType int32 - -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 -) - -const ( - RTMP_PACKET_TYPE_CHUNK_SIZE = 0x01 - RTMP_PACKET_TYPE_BYTES_READ_REPORT = 0x03 - RTMP_PACKET_TYPE_CONTROL = 0x04 - RTMP_PACKET_TYPE_SERVER_BW = 0x05 - RTMP_PACKET_TYPE_CLIENT_BW = 0x06 - RTMP_PACKET_TYPE_AUDIO = 0x08 - RTMP_PACKET_TYPE_VIDEO = 0x09 - RTMP_PACKET_TYPE_FLEX_MESSAGE = 0x11 - RTMP_PACKET_TYPE_INFO = 0x12 - RTMP_PACKET_TYPE_INVOKE = 0x14 - RTMP_PACKET_TYPE_FLASH_VIDEO = 0x16 -) - -const ( - RTMP_PACKET_SIZE_LARGE = 0 - RTMP_PACKET_SIZE_MEDIUM = 1 - RTMP_PACKET_SIZE_SMALL = 2 - RTMP_PACKET_SIZE_MINIMUM = 3 -) - -const ( - RTMP_READ_HEADER = 0x01 - RTMP_READ_SEEKING = 0x2 -) - -const ( - RTMP_LF_AUTH = 0x0001 /* using auth param */ - RTMP_LF_LIVE = 0x0002 /* stream is live */ - RTMP_LF_PLST = 0x0008 /* send playlist before play */ - RTMP_LF_FTCU = 0x0020 /* free tcUrl on close */ - RTMP_LF_FAPU = 0x0040 /* free app on close */ -) - -const ( - RTMP_FEATURE_HTTP = 0x01 - RTMP_FEATURE_ENC = 0x02 - RTMP_FEATURE_SSL = 0x04 - RTMP_FEATURE_MFP = 0x08 /* not yet supported */ - RTMP_FEATURE_WRITE = 0x10 /* publish, not play */ -) - -const ( - RTMP_PROTOCOL_RTMP = 0 - RTMP_PROTOCOL_RTMPE = RTMP_FEATURE_ENC - RTMP_PROTOCOL_RTMPT = RTMP_FEATURE_HTTP - RTMP_PROTOCOL_RTMPS = RTMP_FEATURE_SSL - RTMP_PROTOCOL_RTMPTE = (RTMP_FEATURE_HTTP | RTMP_FEATURE_ENC) - RTMP_PROTOCOL_RTMPTS = (RTMP_FEATURE_HTTP | RTMP_FEATURE_SSL) - RTMP_PROTOCOL_RTMFP = RTMP_FEATURE_MFP -) - -const ( - RTMP_DEFAULT_CHUNKSIZE = 128 - RTMP_BUFFER_CACHE_SIZE = (16 * 1024) - RTMP_SIG_SIZE = 1536 - RTMP_LARGE_HEADER_SIZE = 12 - RTMP_MAX_HEADER_SIZE = 18 -) - const ( minDataSize = 11 debugMode = false + length = 512 ) // av_setDataFrame is a static const global in rtmp.c @@ -229,9 +117,14 @@ var ( av_live = AVC("live") ) +var RTMPT_cmds = []string{ + "open", + "send", + "idle", + "close", +} + var ( - AMFObj_Invalid C_AMFObject - AMFProp_Invalid = C_AMFObjectProperty{p_type: AMF_INVALID} packetSize = [...]int{12, 8, 4, 1} RTMPProtocolStringsLower = [...]string{ "rtmp", @@ -246,242 +139,6 @@ var ( } ) -// session provides parameters required for an rtmp communication session. -type Session struct { - rtmp *C_RTMP - url string - timeout uint -} - -// typedef struct RTMP -// rtmp.h +237 -type C_RTMP struct { - m_inChunkSize int32 - m_outChunkSize int32 - m_nBWCheckCounter int32 - m_nBytesIn int32 - m_nBytesInSent int32 - m_nBufferMS int32 - m_stream_id int32 - m_mediaChannel int32 - m_mediaStamp uint32 - m_pauseStamp uint32 - m_pausing int32 - m_nServerBW int32 - m_nClientBW int32 - m_nClientBW2 uint8 - m_bPlaying uint8 - m_bSendEncoding uint8 - m_bSendCounter uint8 - m_numInvokes int32 - m_numCalls int32 - m_methodCalls *C_RTMP_METHOD - m_channelsAllocatedIn int32 - m_channelsAllocatedOut int32 - m_vecChannelsIn **C_RTMPPacket - m_vecChannelsOut **C_RTMPPacket - m_channelTimestamp *int32 - m_fAudioCodecs float64 - m_fVideoCodecs float64 - m_fEncoding float64 - m_fDuration float64 - m_msgCounter int32 - m_polling int32 - m_resplen int32 - m_unackd int32 - m_clientID C_AVal - m_read C_RTMP_READ - m_write C_RTMPPacket - m_sb C_RTMPSockBuf - Link C_RTMP_LNK -} - -// typedef struct RTMPPacket -// rtmp.h +113 -type C_RTMPPacket struct { - m_headerType uint8 - m_packetType uint8 - m_hasAbsTimestamp uint8 - m_nChannel int32 - m_nTimeStamp uint32 - m_nInfoField2 int32 - m_nBodySize uint32 - m_nBytesRead uint32 - m_chunk *C_RTMPChunk - m_body *byte -} - -// typedef struct RTMPMethod -// rtmp.h +231 -type C_RTMP_METHOD struct { - name C_AVal - num int32 -} - -// typedef struct C_AVal -// amf.h +57 -type C_AVal struct { - av_val *byte - av_len int32 -} - -// typedef struct RTMP_READ -// rtmp.h +200 -type C_RTMP_READ struct { - buf *byte - bufpos *byte - buflen uint - timestamp uint32 - dataType uint8 - flags uint8 - status int8 - initialFrameType uint8 - nResumeTS uint32 - metaHeader *byte - initialFrame *byte - nMetaHeaderSize uint32 - nInitialFrameSize uint32 - nIgnoredFrameCounter uint32 - nIgnoredFlvFrameCounter uint32 -} - -// typedef struct RTMP_READ -// rtmp.h +200 -type C_RTMPSockBuf struct { - sb_socket int32 - sb_size int32 - sb_start *byte - sb_buf [RTMP_BUFFER_CACHE_SIZE]byte // port const - sb_timedout int32 - sb_ssl uintptr -} - -// typedef struct RTMPChunk -// rtmp.h +105 -type C_RTMPChunk struct { - c_headerSize int32 - c_chunkSize int32 - c_chunk *byte - c_header [RTMP_MAX_HEADER_SIZE]byte -} - -// typedef struct RTMP_LNK -// rtmp.h +144 -type C_RTMP_LNK struct { - hostname C_AVal - sockshost C_AVal - playpath0 C_AVal - playpath C_AVal - tcUrl C_AVal - swfUrl C_AVal - pageUrl C_AVal - app C_AVal - auth C_AVal - flashVer C_AVal - subscribepath C_AVal - usherToken C_AVal - token C_AVal - pubUser C_AVal - pubPasswd C_AVal - extras C_AMFObject - edepth int32 - seekTime int32 - stopTime int32 - lFlags int32 - swfAge int32 - protocol int32 - timeout int32 - pFlags int32 - socksport uint16 - port uint16 -} - -// typedef struct AMF_Object -// amf.h +67 -type C_AMFObject struct { - o_num int32 - o_props *C_AMFObjectProperty -} - -// typedef struct AMFObjectProperty -// amf.h +79 -type C_AMFObjectProperty struct { - p_name C_AVal - p_type C_AMFDataType - p_vu P_vu - p_UTCoffset int16 -} - -// typedef struct P_vu -// amf.h +73 -type P_vu struct { - p_number float64 - p_aval C_AVal - p_object C_AMFObject -} - -// NewSession returns a new session. -func NewSession(url string, connectTimeout uint) *Session { - return &Session{ - url: url, - timeout: connectTimeout, - } -} - -// Open establishes an rtmp connection with the url passed into the -// constructor -func (s *Session) Open() error { - if s.rtmp != nil { - return errors.New("rtmp: attempt to start already running session") - } - var err error - s.rtmp, err = startSession(s.rtmp, s.url, uint32(s.timeout)) - if s.rtmp == nil { - return err - } - return nil -} - -// Close terminates the rtmp connection -func (s *Session) Close() error { - if s.rtmp == nil { - return Err(3) - } - ret := endSession(s.rtmp) - s.rtmp = nil - if ret != 0 { - return Err(ret) - } - return nil -} - -// Write writes a frame (flv tag) to the rtmp connection -func (s *Session) Write(data []byte) (int, error) { - if s.rtmp == nil { - return 0, Err(3) - } - - if C_RTMP_IsConnected(s.rtmp) == 0 { - //if C.RTMP_IsConnected(s.rtmp) == 0 { - return 0, Err(1) - } - - if C_RTMP_Write(s.rtmp, data) == 0 { - //if C.RTMP_Write(s.rtmp, (*byte)(unsafe.Pointer(&data[0])), int32(len(data))) == 0 { - return 0, Err(2) - } - return len(data), nil -} - -// int RTMP_IsConnected(RTMP *r); -// rtmp.c +363 -func C_RTMP_IsConnected(r *C_RTMP) int32 { - if r.m_sb.sb_socket != -1 { - return 1 - } - return 0 -} - func startSession(rtmp *C_RTMP, u string, timeout uint32) (*C_RTMP, error) { connect_timeout := int32(timeout) rtmp = C_RTMP_Alloc() @@ -491,7 +148,7 @@ func startSession(rtmp *C_RTMP, u string, timeout uint32) (*C_RTMP, error) { rtmp.Link.timeout = connect_timeout if C_RTMP_SetupURL(rtmp, u) == 0 { // if C.RTMP_SetupURL(rtmp, C.CString(u)) == 0 { - //C.RTMP_Close(rtmp) + C_RTMP_Close(rtmp) //C.RTMP_Free(rtmp) return nil, errors.New("rtmp startSession: Failed to setup URL!") } @@ -502,7 +159,7 @@ func startSession(rtmp *C_RTMP, u string, timeout uint32) (*C_RTMP, error) { //C.RTMP_SetBufferMS(rtmp, 3600*1000) if C_RTMP_Connect(rtmp, nil) == 0 { //if C.RTMP_Connect(rtmp, nil) == 0 { - //C.RTMP_Close(rtmp) + C_RTMP_Close(rtmp) //C.RTMP_Free(rtmp) return nil, errors.New("rtmp startSession: Failed to connect!") } @@ -510,7 +167,7 @@ func startSession(rtmp *C_RTMP, u string, timeout uint32) (*C_RTMP, error) { // TODO: port this if C_RTMP_ConnectStream(rtmp, 0) == 0 { //if C.RTMP_ConnectStream(rtmp, 0) == 0 { - //C.RTMP_Close(rtmp) + C_RTMP_Close(rtmp) //C.RTMP_Free(rtmp) return nil, errors.New("rtmp startSession: Failed to connect stream!") } @@ -518,17 +175,63 @@ func startSession(rtmp *C_RTMP, u string, timeout uint32) (*C_RTMP, error) { return rtmp, nil } +func endSession(rtmp *C_RTMP) uint32 { + if rtmp == nil { + return 3 + } + + C_RTMP_Close(rtmp) + //C.RTMP_Free(rtmp) + return 0 +} + +// uint32_t RTMP_GetTime(); +// rtmp.c +156 +func C_RTMP_GetTime() int32 { + return int32(time.Now().UnixNano() / 1000000) +} + +// int RTMPPacket_Alloc(RTMPPacket* p, uint32_t nSize); +// rtmp.c +189 +func C_RTMPPacket_Alloc(p *C_RTMPPacket, nSize uint32) int { + var ptr *byte + // TODO: port C.SIZE_MAX + // also work out how to deal with the overfloat + /* + if int64(nSize) > (C.SIZE_MAX - RTMP_MAX_HEADER_SIZE) { + return 0 + } + */ + ptr = (*byte)(calloc(1, uintptr(nSize+RTMP_MAX_HEADER_SIZE))) + + if ptr == nil { + return 0 + } + p.m_body = (*byte)(incBytePtr(unsafe.Pointer(ptr), RTMP_MAX_HEADER_SIZE)) + p.m_nBytesRead = 0 + return 1 +} + +// void RTMPPacket_Free(RTMPPacket* p); +// rtmp.c +203 +func C_RTMPPacket_Free(p *C_RTMPPacket) { + if p.m_body != nil { + //C.free(decBytePtr(unsafe.Pointer(p.m_body), RTMP_MAX_HEADER_SIZE)) + p.m_body = nil + } +} + // RTMP* RTMP_IsConnected(); // rtmp.c +317 func C_RTMP_Alloc() *C_RTMP { - var r C_RTMP - return (*C_RTMP)(malloc(uintptr(unsafe.Sizeof(r)))) + return &C_RTMP{} //return (*C_RTMP)(allocate(unsafe.Sizeof(r))) } // void RTMP_Init(RTMP *r); // rtmp.c +329 func C_RTMP_Init(r *C_RTMP) { + *r = C_RTMP{} r.m_sb.sb_socket = -1 r.m_inChunkSize = RTMP_DEFAULT_CHUNKSIZE r.m_outChunkSize = RTMP_DEFAULT_CHUNKSIZE @@ -542,6 +245,59 @@ func C_RTMP_Init(r *C_RTMP) { r.Link.swfAge = 30 } +// void RTMP_EnableWrite(RTMP *r); +// rtmp.c +351 +func C_RTMP_EnableWrite(r *C_RTMP) { + r.Link.protocol |= RTMP_FEATURE_WRITE +} + +// int RTMP_IsConnected(RTMP *r); +// rtmp.c +363 +func C_RTMP_IsConnected(r *C_RTMP) int32 { + if r.m_sb.sb_socket != -1 { + return 1 + } + return 0 +} + +// void RTMP_SetBufferMS(RTMP *r, int size); +// rtmp.c +381 +func C_RTMP_SetBufferMS(r *C_RTMP, size int32) { + r.m_nBufferMS = int32(size) +} + +// void SocksSetup(RTMP *r, C_AVal* sockshost); +// rtmp.c +410 +func C_SocksSetup(r *C_RTMP, sockshost *C_AVal) { + if sockshost.av_len != 0 { + socksport := strchr((*byte)(unsafe.Pointer(sockshost.av_val)), ':') + hostname := strdup((*byte)(unsafe.Pointer(sockshost.av_val))) + + if unsafe.Pointer(socksport) != nil { + *indxBytePtr(unsafe.Pointer(hostname), + int(uintptr(decBytePtr(unsafe.Pointer(socksport), + int(uintptr(unsafe.Pointer(sockshost.av_val))))))) = '\000' + r.Link.sockshost.av_val = (*byte)(unsafe.Pointer(hostname)) + r.Link.sockshost.av_len = int32(strlen(hostname)) + + value, err := strconv.Atoi(string(ptrToSlice(unsafe.Pointer(uintptr( + unsafe.Pointer(socksport))+uintptr(1)), int(strlen((*byte)(unsafe.Pointer( + uintptr(unsafe.Pointer(socksport))+uintptr(1)))))+1))) + if err != nil { + log.Println("C_SocksSetup: bad string conversion!") + } + if uintptr(unsafe.Pointer(socksport)) == 0 { + value = 1080 + } + r.Link.socksport = uint16(value) + } + } else { + r.Link.sockshost.av_val = nil + r.Link.sockshost.av_len = 0 + r.Link.socksport = 0 + } +} + // int RTMP_SetupURL(RTMP *r, char* url); // rtmp.c +757 // NOTE: code dealing with rtmp over http has been disregarded @@ -616,466 +372,33 @@ func C_RTMP_SetupURL(r *C_RTMP, u string) int32 { return 1 } -// int RTMP_ParseURL(const char *url, int *protocol, AVal *host, unsigned int *port, -// AVal *playpath, AVal *app); -// parseurl.c +33 -func C_RTMP_ParseURL(url *byte, protocol *int32, host *C_AVal, port *uint32, - playpath *C_AVal, app *C_AVal) int { - - var p, end, col, ques, slash *byte - // TODO: use our logger here - // RTMP_Log(RTMP_LOGDEBUG, "Parsing..."); - - *protocol = RTMP_PROTOCOL_RTMP - *port = 0 - playpath.av_len = 0 - playpath.av_val = nil - app.av_len = 0 - app.av_val = nil - - p = strstr(url, goStrToCStr("://")) - - if p == nil { - // TODO: use our logger here - log.Println("RTMP URL: No :// in url!") - return 0 - } - /* - NOTE: the following code nees to be ported if we're using anything other than - rtmp! - { - int len = (int)(p-url); - - if(len == 4 && strncasecmp(url, "rtmp", 4)==0) - *protocol = RTMP_PROTOCOL_RTMP; - else if(len == 5 && strncasecmp(url, "rtmpt", 5)==0) - *protocol = RTMP_PROTOCOL_RTMPT; - else if(len == 5 && strncasecmp(url, "rtmps", 5)==0) - *protocol = RTMP_PROTOCOL_RTMPS; - else if(len == 5 && strncasecmp(url, "rtmpe", 5)==0) - *protocol = RTMP_PROTOCOL_RTMPE; - else if(len == 5 && strncasecmp(url, "rtmfp", 5)==0) - *protocol = RTMP_PROTOCOL_RTMFP; - else if(len == 6 && strncasecmp(url, "rtmpte", 6)==0) - *protocol = RTMP_PROTOCOL_RTMPTE; - else if(len == 6 && strncasecmp(url, "rtmpts", 6)==0) - *protocol = RTMP_PROTOCOL_RTMPTS; - else { - RTMP_Log(RTMP_LOGWARNING, "Unknown protocol!\n"); - goto parsehost; - } - } - */ - // TODO: implement new logger here - // RTMP_Log(RTMP_LOGDEBUG, "Parsed protocol: %d", *protocol); - - // Get the hostname - p = (*byte)(incBytePtr(unsafe.Pointer(p), 3)) - - // check for sudden death - if *p == 0 { - // TODO: use new logger here - // RTMP_Log(RTMP_LOGWARNING, "No hostname in URL!"); - return 0 +// int add_addr_info(struct sockaddr_in *service, AVal *host, int port) +// rtmp.c +869 +func C_add_addr_info(service *C.sockaddr_in, host *C_AVal, port C.int) int32 { + var hostname *byte + if (*[1 << 20]byte)(unsafe.Pointer(host.av_val))[host.av_len] != 0 { + name := make([]byte, host.av_len+1) + copy(name, (*[1 << 20]byte)(unsafe.Pointer(host.av_val))[:host.av_len]) + name[len(name)-1] = 0 + hostname = (*byte)(unsafe.Pointer(&name[0])) + } else { + hostname = host.av_val } - end = (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(p)) + uintptr(strlen(p)))) - col = strchr(p, ':') - ques = strchr(p, '?') - slash = strchr(p, '/') - - { - var hostlen int32 - if slash != nil { - hostlen = int32(uintptr(unsafe.Pointer(slash)) - uintptr(unsafe.Pointer(p))) - } else { - hostlen = int32(uintptr(unsafe.Pointer(end)) - uintptr(unsafe.Pointer(p))) + service.sin_addr.s_addr = C.inet_addr((*C.char)(unsafe.Pointer(hostname))) + if service.sin_addr.s_addr == C.INADDR_NONE { + host := C.gethostbyname((*C.char)(unsafe.Pointer(hostname))) + if host == nil || *host.h_addr_list == nil { + //RTMP_Log(RTMP_LOGERROR, "Problem accessing the DNS. (addr: %s)", hostname) + return 0 } - if col != nil && int32(uintptr(unsafe.Pointer(col))-uintptr(unsafe.Pointer(p))) < hostlen { - hostlen = int32(uintptr(unsafe.Pointer(col)) - uintptr(unsafe.Pointer(p))) - } - - if hostlen < 256 { - host.av_val = (*byte)(unsafe.Pointer(p)) - host.av_len = int32(hostlen) - // TODO: use new logger with this - //RTMP_Log(RTMP_LOGDEBUG, "Parsed host : %.*s", hostlen, host.av_val); - } else { - // TODO: use new logger with this - // RTMP_Log(RTMP_LOGWARNING, "Hostname exceeds 255 characters!"); - } - - p = (*byte)(incBytePtr(unsafe.Pointer(p), int(hostlen))) - } - - // get port number if available - if *p == ':' { - var p2 uint32 - p = (*byte)(incBytePtr(unsafe.Pointer(p), 1)) - tmp, _ := strconv.Atoi(cStrToGoStr(p)) - p2 = uint32(tmp) - if p2 > 65535 { - // TODO: use new logger with this - // RTMP_Log(RTMP_LOGWARNING, "Invalid port number!"); - } else { - *port = p2 - } - } - - if slash == nil { - // TODO: use new logger - // RTMP_Log(RTMP_LOGWARNING, "No application or playpath in URL!"); - return 1 - } - - p = (*byte)(incBytePtr(unsafe.Pointer(slash), 1)) - - { - /* parse application - * - * rtmp://host[:port]/app[/appinstance][/...] - * application = app[/appinstance] - */ - - var slash2 *byte - var slash3 *byte = nil - var slash4 *byte = nil - var applen, appnamelen int32 - - slash2 = strchr(p, '/') - - if slash2 != nil { - slash3 = strchr((*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(slash2))+ - uintptr(1))), '/') - } - if slash3 != nil { - slash4 = strchr((*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(slash3))+ - uintptr(1))), '/') - } - - // ondemand, pass all parameters as app - applen = int32(uintptr(unsafe.Pointer(end)) - uintptr(unsafe.Pointer(p))) - appnamelen = applen - - switch { - case ques != nil && strstr(p, goStrToCStr("slist=")) != nil: - appnamelen = int32(uintptr(unsafe.Pointer(ques)) - uintptr(unsafe.Pointer(p))) - case strings.Compare(cStrToGoStr(p)[:9], "ondemand/") == 0: - /* app = ondemand/foobar, only pass app=ondemand */ - applen = 8 - appnamelen = 8 - default: - switch { - case slash4 != nil: - appnamelen = int32(uintptr(unsafe.Pointer(slash4)) - uintptr( - unsafe.Pointer(p))) - case slash3 != nil: - appnamelen = int32(uintptr(unsafe.Pointer(slash3)) - uintptr( - unsafe.Pointer(p))) - case slash2 != nil: - appnamelen = int32(uintptr(unsafe.Pointer(slash2)) - uintptr( - unsafe.Pointer(p))) - } - - applen = appnamelen - } - - app.av_val = (*byte)(unsafe.Pointer(p)) - app.av_len = int32(applen) - // TODO: use new logging here - // RTMP_Log(RTMP_LOGDEBUG, "Parsed app : %.*s", applen, p); - - p = (*byte)(incBytePtr(unsafe.Pointer(p), int(appnamelen))) - } - - if *p == '/' { - p = (*byte)(incBytePtr(unsafe.Pointer(p), 1)) - } - // NOTE: don't think we currently need this section - see 787 for this func - - if int(uintptr(unsafe.Pointer(end))-uintptr(unsafe.Pointer(p))) != 0 { - var av C_AVal - av.av_val = (*byte)(unsafe.Pointer(p)) - av.av_len = int32(uintptr(unsafe.Pointer(end)) - uintptr(unsafe.Pointer(p))) - // TODO: port THis - //C.RTMP_ParsePlaypath(&av, playpath) - C_RTMP_ParsePlaypath(&av, playpath) + service.sin_addr = *(*C.struct_in_addr)(unsafe.Pointer(*host.h_addr_list)) } + service.sin_port = C.ushort(inet.Htons(uint16(port))) return 1 } -// void RTMP_ParsePlaypath(AVal *in, AVal *out); -// parseurl.c +201 -func C_RTMP_ParsePlaypath(in, out *C_AVal) { - var addMP4 int32 = 0 - var addMP3 int32 = 0 - var subExt int32 = 0 - playpath := in.av_val - var temp, q *byte - var ext *byte = nil - ppstart := (*byte)(unsafe.Pointer(playpath)) - var streamname, destptr, p *byte - - pplen := int32(in.av_len) - - out.av_val = nil - out.av_len = 0 - - temp = strstr((*byte)(unsafe.Pointer(ppstart)), goStrToCStr("slist=")) - if *ppstart == '?' && temp != nil { - ppstart = (*byte)(incBytePtr(unsafe.Pointer(temp), 6)) - pplen = int32(strlen(ppstart)) - - temp = strchr(ppstart, '&') - - if temp != nil { - pplen = int32(uintptr(unsafe.Pointer(temp)) - uintptr(unsafe.Pointer(ppstart))) - } - } - - q = strchr(ppstart, '?') - - if pplen >= 4 { - if q != nil { - ext = (*byte)(decBytePtr(unsafe.Pointer(q), 4)) - } else { - ext = (*byte)(indxBytePtr(unsafe.Pointer(ppstart), int(uintptr(pplen)- - uintptr(4)))) - } - switch { - case strings.Compare(cStrToGoStr(ext)[:4], ".f4v") == 0 || - strings.Compare(cStrToGoStr(ext)[:4], ".mp4") == 0: - addMP4 = 1 - subExt = 1 - case ppstart == (*byte)(unsafe.Pointer(playpath)) && strings.Compare( - cStrToGoStr(ext)[:4], ".flv") == 0: - subExt = 1 - case strings.Compare(cStrToGoStr(ext)[:4], ".mp3") == 0: - addMP3 = 1 - subExt = 1 - } - } - - streamname = (*byte)(malloc(uintptr(pplen + 4 + 1))) - - if streamname == nil { - return - } - - destptr = streamname - switch { - case addMP4 != 0: - if strings.Compare(cStrToGoStr(ppstart)[:4], "mp4") != 0 { - memmove(unsafe.Pointer(destptr), unsafe.Pointer(goStrToCStr("mp4:")), - uintptr(len("mp4:"))) - destptr = (*byte)(incBytePtr(unsafe.Pointer(destptr), 4)) - } else { - subExt = 0 - } - case addMP3 != 0: - if strings.Compare(cStrToGoStr(ppstart)[:4], "mp3") != 0 { - memmove(unsafe.Pointer(destptr), unsafe.Pointer(goStrToCStr("mp3:")), - uintptr(len("mp4:"))) - destptr = (*byte)(incBytePtr(unsafe.Pointer(destptr), 4)) - } else { - subExt = 0 - } - } - - p = (*byte)(ppstart) - for pplen > 0 { - if subExt != 0 && p == ext { - p = (*byte)(incBytePtr(unsafe.Pointer(p), 4)) - pplen -= 4 - continue - } - if *p == '%' { - var c uint32 - fmt.Sscanf(cStrToGoStr((*byte)(incBytePtr(unsafe.Pointer(p), 1))), "%02x", &c) - *indxBytePtr(unsafe.Pointer(destptr), 0) = byte(c) - destptr = (*byte)(incBytePtr(unsafe.Pointer(destptr), 1)) - pplen -= 3 - p = (*byte)(incBytePtr(unsafe.Pointer(p), 3)) - } else { - *indxBytePtr(unsafe.Pointer(destptr), 0) = *p - destptr = (*byte)(incBytePtr(unsafe.Pointer(destptr), 1)) - p = (*byte)(incBytePtr(unsafe.Pointer(p), 1)) - pplen-- - } - } - *destptr = '\x00' - - out.av_val = (*byte)(unsafe.Pointer(streamname)) - out.av_len = int32(uintptr(unsafe.Pointer(destptr)) - uintptr(unsafe.Pointer( - streamname))) -} - -// void SocksSetup(RTMP *r, C_AVal* sockshost); -// rtmp.c +410 -func C_SocksSetup(r *C_RTMP, sockshost *C_AVal) { - if sockshost.av_len != 0 { - socksport := strchr((*byte)(unsafe.Pointer(sockshost.av_val)), ':') - hostname := strdup((*byte)(unsafe.Pointer(sockshost.av_val))) - - if unsafe.Pointer(socksport) != nil { - *indxBytePtr(unsafe.Pointer(hostname), - int(uintptr(decBytePtr(unsafe.Pointer(socksport), - int(uintptr(unsafe.Pointer(sockshost.av_val))))))) = '\000' - r.Link.sockshost.av_val = (*byte)(unsafe.Pointer(hostname)) - r.Link.sockshost.av_len = int32(strlen(hostname)) - - value, err := strconv.Atoi(string(ptrToSlice(unsafe.Pointer(uintptr( - unsafe.Pointer(socksport))+uintptr(1)), int(strlen((*byte)(unsafe.Pointer( - uintptr(unsafe.Pointer(socksport))+uintptr(1)))))+1))) - if err != nil { - log.Println("C_SocksSetup: bad string conversion!") - } - if uintptr(unsafe.Pointer(socksport)) == 0 { - value = 1080 - } - r.Link.socksport = uint16(value) - } - } else { - r.Link.sockshost.av_val = nil - r.Link.sockshost.av_len = 0 - r.Link.socksport = 0 - } -} - -/* -func rtmpClose(r *C_RTMP) { - closeInternal(r, 0) -} - -func closeInternal(r *C_RTMP, reconnect int32) { - var i int32 - - if C_RTMP_IsConnected(r) != 0 { - if r.m_stream_id > 0 { - i = int32(r.m_stream_id) - if r.Link.protocol&RTMP_FEATURE_WRITE != 0 { - C.SendFCUnpublish(r) - } - C.SendDeleteStream(r, float64(i)) - } - C.RTMPSockBuf_Close(&r.m_sb) - } - - r.m_stream_id = -1 - r.m_sb.sb_socket = -1 - r.m_nBWCheckCounter = 0 - r.m_nBytesIn = 0 - r.m_nBytesInSent = 0 - - if r.m_read.flags&RTMP_READ_HEADER != 0 { - //C.free(unsafe.Pointer(r.m_read.buf)) - r.m_read.buf = nil - } - - r.m_read.dataType = 0 - r.m_read.flags = 0 - r.m_read.status = 0 - r.m_read.nResumeTS = 0 - r.m_read.nIgnoredFrameCounter = 0 - r.m_read.nIgnoredFlvFrameCounter = 0 - - r.m_write.m_nBytesRead = 0 - C.RTMPPacket_Free(&r.m_write) - - for i := 0; i < int(r.m_channelsAllocatedIn); i++ { - if *(**C_RTMPPacket)(incPtr(unsafe.Pointer(r.m_vecChannelsIn), i, - int(unsafe.Sizeof(&r.m_write)))) != nil { - - C.RTMPPacket_Free(*(**C_RTMPPacket)(incPtr(unsafe.Pointer(r.m_vecChannelsIn), i, - int(unsafe.Sizeof(&r.m_write))))) - - //C.free(unsafe.Pointer(*(**C_RTMPPacket)(incPtr(unsafe.Pointer(r.m_vecChannelsIn), - i, int(unsafe.Sizeof(&r.m_write)))))) - - *(**C_RTMPPacket)(incPtr(unsafe.Pointer(r.m_vecChannelsIn), - i, int(unsafe.Sizeof(&r.m_write)))) = nil - } - } - - //C.free(unsafe.Pointer(r.m_vecChannelsOut)) - r.m_vecChannelsOut = nil - r.m_channelsAllocatedOut = 0 - C.AV_clear(r.m_methodCalls, r.m_numCalls) - - r.m_methodCalls = nil - r.m_numCalls = 0 - r.m_numInvokes = 0 - - r.m_bPlaying = C.uchar(0) - r.m_sb.sb_size = 0 - - r.m_msgCounter = 0 - r.m_resplen = 0 - r.m_unackd = 0 - - if ((r.Link.lFlags & RTMP_LF_FTCU) != 0) && (reconnect == 0) { - //C.free(unsafe.Pointer(r.Link.app.av_val)) - r.Link.app.av_val = nil - r.Link.lFlags ^= RTMP_LF_FAPU - } - - if reconnect == 0 { - //C.free(unsafe.Pointer(r.Link.playpath0.av_val)) - r.Link.playpath0.av_val = nil - } -} -*/ - -// void RTMP_EnableWrite(RTMP *r); -// rtmp.c +351 -func C_RTMP_EnableWrite(r *C_RTMP) { - r.Link.protocol |= RTMP_FEATURE_WRITE -} - -// void RTMP_SetBufferMS(RTMP *r, int size); -// rtmp.c +381 -func C_RTMP_SetBufferMS(r *C_RTMP, size int32) { - r.m_nBufferMS = int32(size) -} - -// int RTMP_Connect(RTMP *r, RTMPPacket* cp); -// rtmp.c +1032 -func C_RTMP_Connect(r *C_RTMP, cp *C_RTMPPacket) int { - // TODO: port this - var service C.sockaddr_in - - if r.Link.hostname.av_len == 0 { - return 0 - } - memset((*byte)(unsafe.Pointer(&service)), 0, int(unsafe.Sizeof(service))) - - // TODO: port this - service.sin_family = C.AF_INET - - if r.Link.socksport != 0 { - // TODO: port this - if C.add_addr_info(&service, (*C.AVal)(unsafe.Pointer(&r.Link.sockshost)), C.int(r.Link.socksport)) == 0 { - return 0 - } - } else { - // connect directly - if C.add_addr_info(&service, (*C.AVal)(unsafe.Pointer(&r.Link.hostname)), - C.int(r.Link.port)) == 0 { - return 0 - } - } - //if C.RTMP_Connect0(r, (*C.sockaddr)(unsafe.Pointer(&service))) == 0 { - if C_RTMP_Connect0(r, (*C.sockaddr)(unsafe.Pointer(&service))) == 0 { - return 0 - } - - r.m_bSendCounter = 1 - - return int(C_RTMP_Connect1(r, cp)) - //return int(C.RTMP_Connect1(r, cp)) -} - // int RTMP_Connect0(RTMP *r, struct sockaddr* service); // rtmp.c +906 func C_RTMP_Connect0(r *C_RTMP, service *C.sockaddr) int { @@ -1124,6 +447,64 @@ func C_RTMP_Connect0(r *C_RTMP, service *C.sockaddr) int { return 1 } +// int RTMP_Connect1(RTMP* r, RTMPPacket* cp); +// rtmp.c +978 +func C_RTMP_Connect1(r *C_RTMP, cp *C_RTMPPacket) int { + if debugMode { + log.Println("... connected, handshaking...") + } + //if C.HandShake(r, 1) == 0 { + if C_HandShake(r, 1) == 0 { + log.Println("C_RTMP_Connect1: handshake failed!") + return 0 + } + if debugMode { + log.Println("... handshaked...") + } + //if C.SendConnectPacket(r, cp) == 0 { + if C_SendConnectPacket(r, cp) == 0 { + log.Println("RTMP connect failed!") + return 0 + } + return 1 +} + +// int RTMP_Connect(RTMP *r, RTMPPacket* cp); +// rtmp.c +1032 +func C_RTMP_Connect(r *C_RTMP, cp *C_RTMPPacket) int { + // TODO: port this + var service C.sockaddr_in + + if r.Link.hostname.av_len == 0 { + return 0 + } + memset((*byte)(unsafe.Pointer(&service)), 0, int(unsafe.Sizeof(service))) + + // TODO: port this + service.sin_family = C.AF_INET + + if r.Link.socksport != 0 { + if C_add_addr_info(&service, (*C_AVal)(unsafe.Pointer(&r.Link.sockshost)), C.int(r.Link.socksport)) == 0 { + return 0 + } + } else { + // connect directly + if C_add_addr_info(&service, (*C_AVal)(unsafe.Pointer(&r.Link.hostname)), + C.int(r.Link.port)) == 0 { + return 0 + } + } + //if C.RTMP_Connect0(r, (*C.sockaddr)(unsafe.Pointer(&service))) == 0 { + if C_RTMP_Connect0(r, (*C.sockaddr)(unsafe.Pointer(&service))) == 0 { + return 0 + } + + r.m_bSendCounter = 1 + + return int(C_RTMP_Connect1(r, cp)) + //return int(C.RTMP_Connect1(r, cp)) +} + // int SocksNegotiate(RTMP* r); // rtmp.c +1062 func C_SocksNegotiate(r *C_RTMP) int { @@ -1132,7 +513,7 @@ func C_SocksNegotiate(r *C_RTMP) int { memset((*byte)(unsafe.Pointer(&service)), 0, int(unsafe.Sizeof(service))) - C.add_addr_info(&service, (*C.AVal)(unsafe.Pointer(&r.Link.hostname)), C.int(r.Link.port)) + C_add_addr_info(&service, (*C_AVal)(unsafe.Pointer(&r.Link.hostname)), C.int(r.Link.port)) addr = int32(inet.Htonl(uint32(service.sin_addr.s_addr))) { @@ -1161,113 +542,109 @@ func C_SocksNegotiate(r *C_RTMP) int { } } -// TODO: find location in c file -func SET_RCVTIMEO(tv *int32, s int32) { - *tv = s * 1000 +// int RTMP_ConnectStream(RTMP* r, int seekTime); +// rtmp.c +1099 +func C_RTMP_ConnectStream(r *C_RTMP, seekTime int32) int { + var packet C_RTMPPacket + memset((*byte)(unsafe.Pointer(&packet)), 0, int(unsafe.Sizeof(packet))) + + if seekTime > 0 { + r.Link.seekTime = int32(seekTime) + } + + r.m_mediaChannel = 0 + + // TODO: read packet + for r.m_bPlaying == 0 && C_RTMP_IsConnected(r) != 0 && + //C.RTMP_ReadPacket(r, &packet) != 0 { + C_RTMP_ReadPacket(r, &packet) != 0 { + + // TODO: port is ready + if C_RTMPPacket_IsReady(&packet) != 0 { + if packet.m_nBodySize == 0 { + continue + } + + if packet.m_packetType == RTMP_PACKET_TYPE_AUDIO || + packet.m_packetType == RTMP_PACKET_TYPE_VIDEO || + packet.m_packetType == RTMP_PACKET_TYPE_INFO { + log.Println("C_RTMP_ConnectStream: got packet before play()! Ignoring.") + C_RTMPPacket_Free(&packet) + continue + } + + //C.RTMP_ClientPacket(r, &packet) + C_RTMP_ClientPacket(r, &packet) + C_RTMPPacket_Free(&packet) + } + } + return int(r.m_bPlaying) } -// int RTMP_Connect1(RTMP* r, RTMPPacket* cp); -// rtmp.c +978 -func C_RTMP_Connect1(r *C_RTMP, cp *C_RTMPPacket) int { - if debugMode { - log.Println("... connected, handshaking...") - } - //if C.HandShake(r, 1) == 0 { - if C_HandShake(r, 1) == 0 { - log.Println("C_RTMP_Connect1: handshake failed!") - return 0 - } - if debugMode { - log.Println("... handshaked...") - } - //if C.SendConnectPacket(r, cp) == 0 { - if C_SendConnectPacket(r, cp) == 0 { - log.Println("RTMP connect failed!") - return 0 - } - return 1 -} +// int RTMP_ClientPacket() +// rtmp.c +1226 +// NOTE cases have been commented out that are not currently used by AusOcean +func C_RTMP_ClientPacket(r *C_RTMP, packet *C_RTMPPacket) int32 { + var bHasMediaPacket int32 = 0 + switch packet.m_packetType { -// int HandShake(RTMP* r, int FP9HandShake); -// rtmp.c +3744 -func C_HandShake(r *C_RTMP, FP9HandShake int32) int { - var bMatch int - //uptime := uint32(0) - //suptime := uint32(0) - //typ := byte(0) - var uptime, suptime uint32 - var typ byte - //clientbuf := make([]byte, RTMP_SIG_SIZE+1) - var clientbuf [RTMP_SIG_SIZE + 1]byte - clientsig := (*byte)(incBytePtr(unsafe.Pointer(&clientbuf[0]), 1)) - //serversig := make([]byte, RTMP_SIG_SIZE) - var serversig [RTMP_SIG_SIZE]byte + case RTMP_PACKET_TYPE_CHUNK_SIZE: + // TODO: port this + //C.HandleChangeChunkSize(r, packet) + C_HandleChangeChunkSize(r, packet) - clientbuf[0] = 0x03 // not encrypted + case RTMP_PACKET_TYPE_BYTES_READ_REPORT: + // TODO: usue new logger here + //RTMP_Log(RTMP_LOGDEBUG, "%s, received: bytes read report", __FUNCTION__); - // TODO: port rtmp_getTime - //uptime = inet.Htonl(uint32(C.RTMP_GetTime())) - uptime = inet.Htonl(uint32(C_RTMP_GetTime())) - memmove(unsafe.Pointer(clientsig), unsafe.Pointer(&uptime), 4) + case RTMP_PACKET_TYPE_CONTROL: + panic("Unsupported packet type RTMP_PACKET_TYPE_CONTROL") + /* + log.Println("RTMP_PACKET_TYPE_CONTROL") + // TODO: port this + C.HandleCtrl(r, packet) + */ + case RTMP_PACKET_TYPE_SERVER_BW: + // TODO: port this + //C.HandleServerBW(r, packet) + C_HandlServerBW(r, packet) - memset(indxBytePtr(unsafe.Pointer(clientsig), 4), 0, 4) + case RTMP_PACKET_TYPE_CLIENT_BW: + // TODO: port this + //C.HandleClientBW(r, packet) + C_HandleClientBW(r, packet) - for i := 8; i < RTMP_SIG_SIZE; i++ { - *indxBytePtr(unsafe.Pointer(clientsig), i) = byte(rand.Intn(256)) + case RTMP_PACKET_TYPE_AUDIO: + panic("Unsupported packet type RTMP_PACKET_TYPE_AUDIO") + + case RTMP_PACKET_TYPE_VIDEO: + panic("Unsupported packet type RTMP_PACKET_TYPE_VIDEO") + + case RTMP_PACKET_TYPE_FLEX_MESSAGE: + panic("Unsupported packet type RTMP_PACKET_TYPE_FLEX_MESSAGE") + + case RTMP_PACKET_TYPE_INFO: + panic("Unsupported packet type RTMP_PACKET_TYPE_INFO") + + case RTMP_PACKET_TYPE_INVOKE: + log.Println("RTMP_PACKET_TYPE_INVOKE:") + // TODO use new logger here + //RTMP_Log(RTMP_LOGDEBUG, "%s, received: invoke %u bytes", __FUNCTION__,packet.m_nBodySize); + + //if C.HandleInvoke(r, packet.m_body, C.uint(packet.m_nBodySize)) == 1 { + if C_HandleInvoke(r, (*byte)(unsafe.Pointer(packet.m_body)), uint32(packet.m_nBodySize)) == 1 { + log.Println("HasMediaPacket") + bHasMediaPacket = 2 + } + + case RTMP_PACKET_TYPE_FLASH_VIDEO: + panic("Unsupported packet type RTMP_PACKET_TYPE_FLASH_VIDEO") + + default: + // TODO use new logger here + // RTMP_Log(RTMP_LOGDEBUG, "%s, unknown packet type received: 0x%02x", __FUNCTION__,packet.m_packetType); } - - if C_WriteN(r, unsafe.Pointer(&clientbuf[0]), RTMP_SIG_SIZE+1) == 0 { - return 0 - } - - //if C.ReadN(r, (*byte)(unsafe.Pointer(&typ)), 1) != 1 { - if C_ReadN(r, (*byte)(unsafe.Pointer(&typ)), 1) != 1 { - return 0 - } - - if debugMode { - log.Println("C_HandShake: Type answer: %v", typ) - } - if typ != clientbuf[0] { - log.Println("C_HandShake: type mismatch: client sent %v, server sent: %v", - clientbuf[0], typ) - } - if C_ReadN(r, (*byte)(unsafe.Pointer(&serversig[0])), RTMP_SIG_SIZE) != RTMP_SIG_SIZE { - //if C.ReadN(r, (*byte)(unsafe.Pointer(&serversig[0])), RTMP_SIG_SIZE) != RTMP_SIG_SIZE { - return 0 - } - - // decode server response - memmove(unsafe.Pointer(&suptime), unsafe.Pointer(&serversig[0]), 4) - suptime = inet.Ntohl(suptime) - - // 2nd part of handshake - if C_WriteN(r, unsafe.Pointer(&serversig[0]), RTMP_SIG_SIZE) == 0 { - return 0 - } - - if C_ReadN(r, (*byte)(unsafe.Pointer(&serversig[0])), RTMP_SIG_SIZE) != RTMP_SIG_SIZE { - //if C.ReadN(r, (*byte)(unsafe.Pointer(&serversig[0])), RTMP_SIG_SIZE) != RTMP_SIG_SIZE { - return 0 - } - - // TODO: find golang memcmp - bMatch = 0 - if memcmp(unsafe.Pointer(&serversig[0]), unsafe.Pointer(clientsig), - RTMP_SIG_SIZE) == 0 { - bMatch = 1 - } - - if bMatch == 0 { - log.Println("Client signature does not match!") - } - return 1 -} - -// uint32_t RTMP_GetTime(); -// rtmp.c +156 -func C_RTMP_GetTime() int32 { - return int32(time.Now().UnixNano() / 1000000) + return bHasMediaPacket } // int ReadN(RTMP* r, char* buffer, int n); @@ -1320,7 +697,7 @@ func C_ReadN(r *C_RTMP, buffer *byte, n int) int { if nBytes == 0 { log.Println("RTMP socket closed by peer") - // C.RTMP_Close(r) + C_RTMP_Close(r) break } @@ -1331,59 +708,38 @@ func C_ReadN(r *C_RTMP, buffer *byte, n int) int { return nOriginalSize - n } -// int RTMPSockBuf_Fill(RTMPSockBuf* sb); -// rtmp.c +4253 -func C_RTMPSockBuf_Fill(sb *C_RTMPSockBuf) int { - var nBytes int +// int WriteN(RTMP* r, const char* buffer, int n); +// rtmp.c +1502 +func C_WriteN(r *C_RTMP, buffer unsafe.Pointer, n int) int { + ptr := buffer + for n > 0 { + var nBytes int - if sb.sb_size == 0 { - sb.sb_start = &sb.sb_buf[0] - } + nBytes = int(C_RTMPSockBuf_Send(&r.m_sb, (*byte)(ptr), int32(n))) - for { - nBytes = int(unsafe.Sizeof(sb.sb_buf)) - 1 - int(sb.sb_size) - - int(uintptr(unsafe.Pointer(sb.sb_start))-uintptr(unsafe.Pointer( - &sb.sb_buf[0]))) + if nBytes < 0 { + if debugMode { + log.Println("C_WriteN, RTMP send error") + } - // TODO: figure out what to do with recv - nBytes = int(C.recv(C.int(sb.sb_socket), unsafe.Pointer(uintptr(unsafe.Pointer( - sb.sb_start))+uintptr(int(sb.sb_size))), C.size_t(nBytes), 0)) - - if nBytes != -1 { - sb.sb_size += int32(nBytes) - } else { - log.Println("C_RTMPSockBuf_Fill: recv error!") + // TODO: port this + C_RTMP_Close(r) + n = 1 + break } - break + + if nBytes == 0 { + break + } + + n -= nBytes + ptr = incBytePtr(ptr, nBytes) } - return nBytes -} -// int SendBytesReceived(RTMP* r); -// rtmp.c +2080 -func C_SendBytesReceived(r *C_RTMP) int { - var packet C_RTMPPacket - var pbuf [256]byte - pend := (*byte)(incBytePtr(unsafe.Pointer(&pbuf[0]), 256)) - - packet.m_nChannel = 0x02 /* control channel (invoke) */ - packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM - packet.m_packetType = RTMP_PACKET_TYPE_BYTES_READ_REPORT - packet.m_nTimeStamp = 0 - packet.m_nInfoField2 = 0 - packet.m_hasAbsTimestamp = 0 - packet.m_body = (*byte)(incBytePtr(unsafe.Pointer(&pbuf[0]), - RTMP_MAX_HEADER_SIZE)) - - packet.m_nBodySize = 4 - - C_AMF_EncodeInt32((*byte)(unsafe.Pointer(packet.m_body)), pend, int32(r.m_nBytesIn)) - // C.AMF_EncodeInt32(packet.m_body, (*byte)(unsafe.Pointer(pend)), r.m_nBytesIn) - - r.m_nBytesInSent = r.m_nBytesIn - - //return int(C.RTMP_SendPacket(r, &packet, 0)) - return C_RTMP_SendPacket(r, &packet, 0) + if n == 0 { + return 1 + } + return 0 } // int SendConnectPacket(RTMP* r, RTMPPacket* cp); @@ -1566,699 +922,35 @@ func C_SendConnectPacket(r *C_RTMP, cp *C_RTMPPacket) int { return C_RTMP_SendPacket(r, &packet, 1) } -// char* AMFPropEncode(AMFOBjectProperty* prop, char* pBufer, char* pBufEnd); -// amf.c +366 -func C_AMF_PropEncode(p *C_AMFObjectProperty, pBuffer *byte, pBufEnd *byte) *byte { - if p.p_type == AMF_INVALID { - return nil - } - - if p.p_type != AMF_NULL && int(uintptr(unsafe.Pointer(pBuffer)))+ - int(p.p_name.av_len)+2+1 >= int( - uintptr(unsafe.Pointer(pBufEnd))) { - return nil - } - - if p.p_type != AMF_NULL && p.p_name.av_len != 0 { - *indxBytePtr(unsafe.Pointer(pBuffer), 0) = byte(p.p_name.av_len >> 8) - pBuffer = (*byte)(incBytePtr(unsafe.Pointer(pBuffer), 1)) - *indxBytePtr(unsafe.Pointer(pBuffer), 0) = byte(p.p_name.av_len & 0xff) - pBuffer = (*byte)(incBytePtr(unsafe.Pointer(pBuffer), 1)) - memmove(unsafe.Pointer(pBuffer), unsafe.Pointer(p.p_name.av_val), - uintptr(p.p_name.av_len)) - pBuffer = (*byte)(incBytePtr(unsafe.Pointer(pBuffer), int(p.p_name.av_len))) - } - - switch p.p_type { - case AMF_NUMBER: - pBuffer = C_AMF_EncodeNumber(pBuffer, pBufEnd, float64(p.p_vu.p_number)) - case AMF_BOOLEAN: - val := 0 - if p.p_vu.p_number != 0 { - val = 1 - } - pBuffer = C_AMF_EncodeBoolean(pBuffer, pBufEnd, val) - case AMF_STRING: - pBuffer = C_AMF_EncodeString(pBuffer, pBufEnd, &p.p_vu.p_aval) - case AMF_NULL: - if uintptr(incBytePtr(unsafe.Pointer(pBuffer), 1)) >= uintptr(unsafe.Pointer( - pBufEnd)) { - return nil - } - *(*byte)(unsafe.Pointer(pBuffer)) = AMF_NULL - pBuffer = (*byte)(incBytePtr(unsafe.Pointer(pBuffer), 1)) - case AMF_OBJECT: - pBuffer = C_AMF_Encode(&p.p_vu.p_object, pBuffer, pBufEnd) - //pBuffer = (*byte)(unsafe.Pointer(C.AMF_Encode(&p.p_vu.p_object, (*byte)( - //unsafe.Pointer(pBuffer)), (*byte)(unsafe.Pointer(pBufEnd))))) - case AMF_ECMA_ARRAY: - pBuffer = C_AMF_EncodeEcmaArray(&p.p_vu.p_object, pBuffer, pBufEnd) - //pBuffer = (*byte)(unsafe.Pointer(C.AMF_EncodeEcmaArray(&p.p_vu.p_object, (*byte)(unsafe.Pointer(pBuffer)), (*byte)(unsafe.Pointer(pBufEnd))))) - case AMF_STRICT_ARRAY: - //pBuffer = (*byte)(unsafe.Pointer(C.AMF_EncodeArray(&p.p_vu.p_object, (*byte)(unsafe.Pointer(pBuffer)), (*byte)(unsafe.Pointer(pBufEnd))))) - pBuffer = C_AMF_EncodeArray(&p.p_vu.p_object, pBuffer, pBufEnd) - default: - log.Println("C_AMF_PropEncode: invalid type!") - pBuffer = nil - } - return pBuffer -} - -// char* AMF_Encode(AMFObject* obj, char* pBuffer, char* pBufEnd); -// amf.c +891 -func C_AMF_Encode(obj *C_AMFObject, pBuffer *byte, pBufEnd *byte) *byte { - if uintptr(unsafe.Pointer(pBuffer))+uintptr(4) >= uintptr(unsafe.Pointer(pBufEnd)) { - return nil - } - - *pBuffer = AMF_OBJECT - pBuffer = (*byte)(incBytePtr(unsafe.Pointer(pBuffer), 1)) - - for i := 0; i < int(obj.o_num); i++ { - res := C_AMF_PropEncode((*C_AMFObjectProperty)(incPtr(unsafe.Pointer( - obj.o_props), i, int(unsafe.Sizeof(*obj.o_props)))), pBuffer, pBufEnd) - if res == nil { - log.Println("C_AMF_Encode: failed to encode property in index") - break - } else { - pBuffer = res - } - } - - if uintptr(incBytePtr(unsafe.Pointer(pBuffer), 3)) >= uintptr(unsafe.Pointer(pBufEnd)) { - return nil - } - - pBuffer = C_AMF_EncodeInt24(pBuffer, pBufEnd, int32(AMF_OBJECT_END)) - - return pBuffer -} - -// char* AMF_EncodeEcmaArray(AMFObject* obj, char* pBuffer, char* pBufEnd); -// amf.c +924 -func C_AMF_EncodeEcmaArray(obj *C_AMFObject, pBuffer *byte, pBufEnd *byte) *byte { - if int(uintptr(unsafe.Pointer(pBuffer)))+4 >= int(uintptr(unsafe.Pointer(pBufEnd))) { - return nil - } - - *pBuffer = AMF_ECMA_ARRAY - pBuffer = (*byte)(incBytePtr(unsafe.Pointer(pBuffer), 1)) - - pBuffer = C_AMF_EncodeInt32(pBuffer, pBufEnd, int32(obj.o_num)) - - for i := 0; i < int(obj.o_num); i++ { - res := C_AMF_PropEncode((*C_AMFObjectProperty)(incPtr(unsafe.Pointer( - obj.o_props), i, int(unsafe.Sizeof(*obj.o_props)))), pBuffer, pBufEnd) - if res == nil { - log.Println("C_AMF_EncodeEcmaArray: failed to encode property!") - break - } else { - pBuffer = res - } - } - - if int(uintptr(unsafe.Pointer(pBuffer)))+3 >= int(uintptr(unsafe.Pointer(pBufEnd))) { - return nil - } - - pBuffer = C_AMF_EncodeInt24(pBuffer, pBufEnd, AMF_OBJECT_END) - - return pBuffer -} - -// char* AMF_EncodeArray(AMFObject* obj, char* pBuffer, char* pBufEnd); -// amf.c +959 -func C_AMF_EncodeArray(obj *C_AMFObject, pBuffer *byte, pBufEnd *byte) *byte { - if int(uintptr(unsafe.Pointer(pBuffer)))+4 >= int(uintptr(unsafe.Pointer(pBufEnd))) { - return nil - } - - *pBuffer = AMF_STRICT_ARRAY - pBuffer = (*byte)(incBytePtr(unsafe.Pointer(pBuffer), 1)) - - pBuffer = C_AMF_EncodeInt32(pBuffer, pBufEnd, int32(obj.o_num)) - - for i := 0; i < int(obj.o_num); i++ { - res := C_AMF_PropEncode((*C_AMFObjectProperty)(incPtr(unsafe.Pointer( - obj.o_props), i, int(unsafe.Sizeof(*obj.o_props)))), pBuffer, pBufEnd) - if res == nil { - log.Println("C_AMF_EncodeEcmaArray: failed to encode property!") - break - } else { - pBuffer = res - } - } - - return pBuffer -} - -// int AMF_DecodeArray(AMFObject *obj, const char *pBuffer, int nSize, int nArrayLen, int bDecodeName); -func C_AMF_DecodeArray(obj *C_AMFObject, pBuffer *byte, nSize, nArrayLen, bDecodeName int32) int32 { - nOriginalSize := nSize - var bError int32 = 0 - - obj.o_num = 0 - obj.o_props = nil - for nArrayLen > 0 { - var prop C_AMFObjectProperty - var nRes int32 - nArrayLen-- - - if nSize <= 0 { - bError = 1 - break - } - nRes = C_AMFProp_Decode(&prop, pBuffer, nSize, bDecodeName) - if nRes == -1 { - bError = 1 - break - } else { - nSize -= nRes - pBuffer = (*byte)(incBytePtr(unsafe.Pointer(pBuffer), int(nRes))) - C_AMF_AddProp(obj, &prop) - } - } - if bError != 0 { - return -1 - } - - return nOriginalSize - nSize -} - -// int RTMP_ConnectStream(RTMP* r, int seekTime); -// rtmp.c +1099 -func C_RTMP_ConnectStream(r *C_RTMP, seekTime int32) int { +// int RTMP_SendCreateStream(RTMP* r); +// rtmp.c +1725 +func C_RTMP_SendCreateStream(r *C_RTMP) int32 { var packet C_RTMPPacket - memset((*byte)(unsafe.Pointer(&packet)), 0, int(unsafe.Sizeof(packet))) + var pbuf [256]byte + var pend *byte = (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(&pbuf[0])) + + unsafe.Sizeof(pbuf))) + var enc *byte - if seekTime > 0 { - r.Link.seekTime = int32(seekTime) - } + packet.m_nChannel = 0x03 /* control channel (invoke) */ + packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM + packet.m_packetType = RTMP_PACKET_TYPE_INVOKE + packet.m_nTimeStamp = 0 + packet.m_nInfoField2 = 0 + packet.m_hasAbsTimestamp = 0 + packet.m_body = (*byte)(incBytePtr(unsafe.Pointer(&pbuf[0]), + int(RTMP_MAX_HEADER_SIZE))) - r.m_mediaChannel = 0 + enc = (*byte)(unsafe.Pointer(packet.m_body)) + enc = C_AMF_EncodeString(enc, pend, &av_createStream) + r.m_numInvokes++ + enc = C_AMF_EncodeNumber(enc, pend, float64(r.m_numInvokes)) + *enc = AMF_NULL + enc = (*byte)(incBytePtr(unsafe.Pointer(enc), 1)) - // TODO: read packet - for r.m_bPlaying == 0 && C_RTMP_IsConnected(r) != 0 && - //C.RTMP_ReadPacket(r, &packet) != 0 { - C_RTMP_ReadPacket(r, &packet) != 0 { + packet.m_nBodySize = uint32(uintptr(unsafe.Pointer(enc)) - uintptr( + unsafe.Pointer(packet.m_body))) - // TODO: port is ready - if C_RTMPPacket_IsReady(&packet) != 0 { - if packet.m_nBodySize == 0 { - continue - } - - if packet.m_packetType == RTMP_PACKET_TYPE_AUDIO || - packet.m_packetType == RTMP_PACKET_TYPE_VIDEO || - packet.m_packetType == RTMP_PACKET_TYPE_INFO { - log.Println("C_RTMP_ConnectStream: got packet before play()! Ignoring.") - C_RTMPPacket_Free(&packet) - continue - } - - //C.RTMP_ClientPacket(r, &packet) - C_RTMP_ClientPacket(r, &packet) - C_RTMPPacket_Free(&packet) - } - } - return int(r.m_bPlaying) -} - -// int RTMP_ClientPacket() -// rtmp.c +1226 -// NOTE cases have been commented out that are not currently used by AusOcean -func C_RTMP_ClientPacket(r *C_RTMP, packet *C_RTMPPacket) int32 { - var bHasMediaPacket int32 = 0 - switch packet.m_packetType { - - case RTMP_PACKET_TYPE_CHUNK_SIZE: - log.Println("RTMP_PACKET_TYPE_CHUNK_SIZE") - // TODO: port this - //C.HandleChangeChunkSize(r, packet) - C_HandleChangeChunkSize(r, packet) - /* - case RTMP_PACKET_TYPE_BYTES_READ_REPORT: - log.Println("RTMP_PACKET_TYPE_BYTES_READ_REPORT") - // TODO: usue new logger here - //RTMP_Log(RTMP_LOGDEBUG, "%s, received: bytes read report", __FUNCTION__); - - case RTMP_PACKET_TYPE_CONTROL: - log.Println("RTMP_PACKET_TYPE_CONTROL") - // TODO: port this - C.HandleCtrl(r, packet) - */ - case RTMP_PACKET_TYPE_SERVER_BW: - log.Println("RTMP_PACKET_TYPE_SERVER_BW") - // TODO: port this - //C.HandleServerBW(r, packet) - C_HandlServerBW(r, packet) - - case RTMP_PACKET_TYPE_CLIENT_BW: - log.Println("RTMP_PACKET_TYPE_CLIENT_BW") - // TODO: port this - //C.HandleClientBW(r, packet) - C_HandleClientBW(r, packet) - /* - case RTMP_PACKET_TYPE_AUDIO: - log.Println("RTMP_PACKET_TYPE_AUDIO") - // TODO port this - //C.HandleAudio(r, packet) NOTE this does nothing - bHasMediaPacket = 1 - if r.m_mediaChannel == 0 { - r.m_mediaChannel = packet.m_nChannel - } - - if r.m_pausing == 0 { - r.m_mediaStamp = packet.m_nTimeStamp - } - - case RTMP_PACKET_TYPE_VIDEO: - log.Println("RTMP_PACKET_TYPE_VIDEO:") - // TODO port this - // C.HandleVideo(r, packet) NOTE this does nothing - bHasMediaPacket = 1 - if r.m_mediaChannel == 0 { - r.m_mediaChannel = packet.m_nChannel - } - if r.m_pausing == 0 { - r.m_mediaStamp = packet.m_nTimeStamp - } - - case RTMP_PACKET_TYPE_FLEX_MESSAGE: - log.Println("RTMP_PACKET_TYPE_FLEX_MESSAGE:") - { - // TODO use new logger here - // RTMP_Log(RTMP_LOGDEBUG,"%s, flex message, size %u bytes, not fully supported", __FUNCTION__, packet.m_nBodySize); - - if C.HandleInvoke(r, (*byte)(incBytePtr(unsafe.Pointer(packet.m_body), 1)), - C.uint(packet.m_nBodySize-1)) == 1 { - bHasMediaPacket = 2 - } - } - case RTMP_PACKET_TYPE_INFO: - log.Println(" RTMP_PACKET_TYPE_INFO:") - // TODO use new logger here - //RTMP_Log(RTMP_LOGDEBUG, "%s, received: notify %u bytes", __FUNCTION__,packet.m_nBodySize); - if C.HandleMetadata(r, packet.m_body, C.uint(packet.m_nBodySize)) != 0 { - bHasMediaPacket = 1 - } - */ - - case RTMP_PACKET_TYPE_INVOKE: - log.Println("RTMP_PACKET_TYPE_INVOKE:") - // TODO use new logger here - //RTMP_Log(RTMP_LOGDEBUG, "%s, received: invoke %u bytes", __FUNCTION__,packet.m_nBodySize); - - //if C.HandleInvoke(r, packet.m_body, C.uint(packet.m_nBodySize)) == 1 { - if C_HandleInvoke(r, (*byte)(unsafe.Pointer(packet.m_body)), uint32(packet.m_nBodySize)) == 1 { - log.Println("HasMediaPacket") - bHasMediaPacket = 2 - } - /* - case RTMP_PACKET_TYPE_FLASH_VIDEO: - log.Println("RTMP_PACKET_TYPE_FLASH_VIDEO:") - { - var pos uint32 = 0 - var nTimeStamp uint32 = uint32(packet.m_nTimeStamp) - - for pos+11 < uint32(packet.m_nBodySize) { - var dataSize uint32 = C_AMF_DecodeInt24((*byte)(incBytePtr(unsafe.Pointer( - packet.m_body), int(pos+1)))) - - if pos+11+dataSize+4 > uint32(packet.m_nBodySize) { - // TODO use new logger here - // RTMP_Log(RTMP_LOGWARNING, "Stream corrupt?!"); - break - } - - switch { - case *indxBytePtr(unsafe.Pointer(packet.m_body), int(pos)) == 0x12: - C.HandleMetadata(r, (*byte)(incBytePtr(unsafe.Pointer(packet.m_body), - int(pos+11))), C.uint(dataSize)) - case *indxBytePtr(unsafe.Pointer(packet.m_body), int(pos)) == 8 || - *indxBytePtr(unsafe.Pointer(packet.m_body), int(pos)) == 9: - nTimeStamp = C_AMF_DecodeInt24((*byte)(incBytePtr(unsafe.Pointer( - packet.m_body), int(pos+4)))) - nTimeStamp |= uint32(*indxBytePtr(unsafe.Pointer(packet.m_body), - int(pos+7)) << 24) - } - pos += (11 + dataSize + 4) - } - if r.m_pausing == 0 { - r.m_mediaStamp = uint32(nTimeStamp) - } - - bHasMediaPacket = 1 - - } - default: - log.Println("DEFAULT") - // TODO use new logger here - // RTMP_Log(RTMP_LOGDEBUG, "%s, unknown packet type received: 0x%02x", __FUNCTION__,packet.m_packetType); - */ - } - return bHasMediaPacket -} - -// void HandleChangeChunkSize(RTMP* r, const RTMPPacket* packet); -// rtmp.c +3345 -func C_HandleChangeChunkSize(r *C_RTMP, packet *C_RTMPPacket) { - if packet.m_nBodySize >= 4 { - //r.m_inChunkSize = int32(C.AMF_DecodeInt32((*byte)(unsafe.Pointer(packet.m_body)))) - r.m_inChunkSize = int32(C_AMF_DecodeInt32((*byte)(unsafe.Pointer(packet.m_body)))) - // TODO use new logger here - // RTMP_Log(RTMP_LOGDEBUG, "%s, received: chunk size change to %d", __FUNCTION__, r.m_inChunkSize); - } -} - -// void HandleServerBW(RTMP* r, const RTMPPacket* packet); -// rtmp.c +3508 -func C_HandlServerBW(r *C_RTMP, packet *C_RTMPPacket) { - r.m_nServerBW = int32(C_AMF_DecodeInt32((*byte)(unsafe.Pointer(packet.m_body)))) - //r.m_nServerBW = int32(C.AMF_DecodeInt32((*byte)(unsafe.Pointer(packet.m_body)))) - // TODO use new logger here - // RTMP_Log(RTMP_LOGDEBUG, "%s: server BW = %d", __FUNCTION__, r.m_nServerBW); -} - -// void HandleClientBW(RTMP* r, const RTMPPacket* packet); -// rtmp.c +3515 -func C_HandleClientBW(r *C_RTMP, packet *C_RTMPPacket) { - r.m_nClientBW = int32(C_AMF_DecodeInt32((*byte)(unsafe.Pointer(packet.m_body)))) - //r.m_nClientBW = int32(C.AMF_DecodeInt32((*byte)(unsafe.Pointer(packet.m_body)))) - - if packet.m_nBodySize > 4 { - r.m_nClientBW2 = (uint8)(*indxBytePtr(unsafe.Pointer(packet.m_body), 4)) - } else { - //r.m_nClientBW2 = -1 - r.m_nClientBW2 = 0 - } - // TODO use new logger here - // RTMP_Log(RTMP_LOGDEBUG, "%s: client BW = %d %d", __FUNCTION__, r.m_nClientBW, - //r.m_nClientBW2); -} - -// int HandleInvoke(RTMP* r, const char* body, unsigned int nBodySize); -// rtmp.c +2912 -func C_HandleInvoke(r *C_RTMP, body *byte, nBodySize uint32) int32 { - var obj C_AMFObject - var method C_AVal - var txn float64 - var ret int32 = 0 - var nRes int32 - - if *body != 0x02 { - // TODO use new logger here - //RTMP_Log(RTMP_LOGWARNING, "%s, Sanity failed. no string method in invoke packet", - //__FUNCTION__); - return 0 - } - nRes = C_AMF_Decode(&obj, body, int32(nBodySize), 0) - //nRes = int32(C.AMF_Decode(&obj, (*byte)(unsafe.Pointer(body)), int32(nBodySize), 0)) - if nRes < 0 { - // TODO use new logger here - //RTMP_Log(RTMP_LOGERROR, "%s, error decoding invoke packet", __FUNCTION__); - return 0 - } - - // NOTE we don't really need this ?? still functions without it - //C.AMF_Dump(&obj) - //C.AMFProp_GetString(C_AMF_GetProp(&obj, nil, 0), &method) - C_AMFProp_GetString(C_AMF_GetProp(&obj, nil, 0), &method) - txn = float64(C_AMFProp_GetNumber(C_AMF_GetProp(&obj, nil, 1))) - // TODO use new logger here - // RTMP_Log(RTMP_LOGDEBUG, "%s, server invoking <%s>", __FUNCTION__, method.av_val); - - switch { - case C_AVMATCH(&method, &av__result) != 0: - { - var methodInvoked C_AVal - var i int32 - for i = 0; i < int32(r.m_numCalls); i++ { - if float64((*(*C_RTMP_METHOD)(incPtr(unsafe.Pointer(r.m_methodCalls), int(i), - int(unsafe.Sizeof(*r.m_methodCalls))))).num) == txn { - methodInvoked = (*(*C_RTMP_METHOD)(incPtr(unsafe.Pointer(r.m_methodCalls), - int(i), int(unsafe.Sizeof(*r.m_methodCalls))))).name - //C.AV_erase(r.m_methodCalls, &r.m_numCalls, int32(i), 0) - C_AV_erase(r.m_methodCalls, (*int32)(&r.m_numCalls), int32(i), 0) - break - } - } - if methodInvoked.av_val == nil { - // TODO use new logger here - //RTMP_Log(RTMP_LOGDEBUG, "%s, received result id %f without matching request", - //__FUNCTION__, txn); - goto leave - } - // TODO use new logger here - //RTMP_Log(RTMP_LOGDEBUG, "%s, received result for method call <%s>", __FUNCTION__, - //methodInvoked.av_val); - switch { - case C_AVMATCH(&methodInvoked, &av_connect) != 0: - { - /* NOTE This code doesn't run in our use case - TODO port this eventually - if r.Link.token.av_len != 0 { - log.Println("2.1") - var p C_AMFObjectProperty - if C.RTMP_FindFirstMatchingProperty(&obj, &av_secureToken, &p) != 0 { - log.Println("2.2") - C.DecodeTEA(&r.Link.token, &p.p_vu.p_aval) - C.SendSecureTokenResponse(r, &p.p_vu.p_aval) - } - } - */ - if (r.Link.protocol & RTMP_FEATURE_WRITE) != 0 { - C_SendReleaseStream(r) - C_SendFCPublish(r) - } /* NOTE This code doesn't run in our use case - else { - log.Println("2.4") - C.RTMP_SendServerBW(r) - C.RTMP_SendCtrl(r, 3, 0, 300) - } - */ - C_RTMP_SendCreateStream(r) - /* NOTE This code doesn't run in our use case - if (r.Link.protocol & RTMP_FEATURE_WRITE) == 0 { - log.Println("2.5") - // Authenticate on Justin.tv legacy servers before sending FCSubscribe - if r.Link.usherToken.av_len != 0 { - log.Println("2.6") - C.SendUsherToken(r, &r.Link.usherToken) - } - // Send the FCSubscribe if live stream or if subscribepath is set - switch { - case r.Link.subscribepath.av_len != 0: - { - log.Println("3") - C.SendFCSubscribe(r, &r.Link.subscribepath) - } - case (r.Link.lFlags & RTMP_LF_LIVE) != 0: - { - log.Println("4") - C.SendFCSubscribe(r, &r.Link.playpath) - } - } - } - */ - } - case C_AVMATCH(&methodInvoked, &av_createStream) != 0: - { - r.m_stream_id = int32(C_AMFProp_GetNumber(C_AMF_GetProp(&obj, nil, 3))) - - if (r.Link.protocol & RTMP_FEATURE_WRITE) != 0 { - C_SendPublish(r) - } /* NOTE This code doesn't run in our use case - else { - log.Println("5.2") - if (r.Link.lFlags & RTMP_LF_PLST) != 0 { - log.Println("5.3") - C.SendPlaylist(r) - } - C.SendPlay(r) - C.RTMP_SendCtrl(r, 3, C.uint(r.m_stream_id), C.uint(r.m_nBufferMS)) - } - */ - } - /*NOTE This code doesn't run in our use case - case C_AVMATCH(&methodInvoked, &av_play) != 0 || - C_AVMATCH(&methodInvoked, &av_publish) != 0: - { - log.Println("6") - r.m_bPlaying = 1 - } - */ - } - //C.free(unsafe.Pointer(methodInvoked.av_val)) - } - case C_AVMATCH(&method, &av_onBWDone) != 0: - { - if r.m_nBWCheckCounter == 0 { - C_SendCheckBW(r) - } - } - /*NOTE This code doesn't run in our use case - case C_AVMATCH(&method, &av_onFCSubscribe) != 0: - case C_AVMATCH(&method, &av_onFCUnsubscribe) != 0: - { - log.Println("8") - C.RTMP_Close(r) - ret = 1 - } - case C_AVMATCH(&method, &av_ping) != 0: - { - log.Println("9") - C.SendPong(r, float64(txn)) - } - case C_AVMATCH(&method, &av__onbwcheck) != 0: - { - log.Println("10") - C.SendCheckBWResult(r, float64(txn)) - } - case C_AVMATCH(&method, &av__onbwdone) != 0: - { - log.Println("11") - var i int32 - for i = 0; i < int32(r.m_numCalls); i++ { - if C_AVMATCH(&(*(*C_RTMP_METHOD)(incPtr(unsafe.Pointer(r.m_methodCalls), int(i), - int(unsafe.Sizeof(*r.m_methodCalls))))).name, &av__checkbw) != 0 { - C.AV_erase(r.m_methodCalls, &r.m_numCalls, int32(i), 1) - break - } - } - } - case C_AVMATCH(&method, &av_close) != 0: - { - log.Println("12") - // TODO use new logger - //RTMP_Log(RTMP_LOGERROR, "rtmp server requested close"); - C.RTMP_Close(r) - } - */ - case C_AVMATCH(&method, &av_onStatus) != 0: - { - var obj2 C_AMFObject - var code, level C_AVal - C_AMFProp_GetObject(C_AMF_GetProp(&obj, nil, 3), &obj2) - C_AMFProp_GetString(C_AMF_GetProp(&obj2, &av_code, -1), &code) - C_AMFProp_GetString(C_AMF_GetProp(&obj2, &av_level, -1), &level) - - // TODO use new logger - // RTMP_Log(RTMP_LOGDEBUG, "%s, onStatus: %s", __FUNCTION__, code.av_val); - switch { - /*NOTE This code doesn't run in our use case - case C_AVMATCH(&code, &av_NetStream_Failed) != 0 || - C_AVMATCH(&code, &av_NetStream_Play_Failed) != 0 || - C_AVMATCH(&code, &av_NetStream_Play_StreamNotFound) != 0 || - C_AVMATCH(&code, &av_NetConnection_Connect_InvalidApp) != 0: - { - log.Println("14") - r.m_stream_id = -1 - C.RTMP_Close(r) - // TODO use new logger - // RTMP_Log(RTMP_LOGERROR, "Closing connection: %s", code.av_val); - } - case C_AVMATCH(&code, &av_NetStream_Play_Start) != 0 || - C_AVMATCH(&code, &av_NetStream_Play_PublishNotify) != 0: - { - log.Println("15") - var i int32 - r.m_bPlaying = 1 - for i = 0; i < int32(r.m_numCalls); i++ { - if C_AVMATCH(&(*(*C_RTMP_METHOD)(incPtr(unsafe.Pointer(r.m_methodCalls), int(i), - int(unsafe.Sizeof(*r.m_methodCalls))))).name, &av_play) != 0 { - log.Println("15.1") - C.AV_erase(r.m_methodCalls, &r.m_numCalls, int32(i), 1) - break - } - } - } - */ - case C_AVMATCH(&code, &av_NetStream_Publish_Start) != 0: - { - var i int32 - r.m_bPlaying = 1 - for i = 0; i < int32(r.m_numCalls); i++ { - if C_AVMATCH(&(*(*C_RTMP_METHOD)(incPtr(unsafe.Pointer(r.m_methodCalls), int(i), - int(unsafe.Sizeof(*r.m_methodCalls))))).name, &av_publish) != 0 { - //C.AV_erase(r.m_methodCalls, &r.m_numCalls, int32(i), 1) - C_AV_erase(r.m_methodCalls, (*int32)(&r.m_numCalls), int32(i), 1) - break - } - } - } - /*NOTE This code doesn't run in our use case - case C_AVMATCH(&code, &av_NetStream_Play_Complete) != 0 || - C_AVMATCH(&code, &av_NetStream_Play_Stop) != 0 || - C_AVMATCH(&code, &av_NetStream_Play_UnpublishNotify) != 0: - { - log.Println("17") - C.RTMP_Close(r) - ret = 1 - } - case C_AVMATCH(&code, &av_NetStream_Seek_Notify) != 0: - { - log.Println("18") - // NOTE ~ has been replace by ^ - is this correct ? - r.m_read.flags = uint8(int8(r.m_read.flags) & ^RTMP_READ_SEEKING) - } - case C_AVMATCH(&code, &av_NetStream_Pause_Notify) != 0: - { - log.Println("19") - if r.m_pausing == 1 || r.m_pausing == 2 { - C.RTMP_SendPause(r, 0, int32(r.m_pauseStamp)) - r.m_pausing = 3 - } - } - */ - } - } - /*NOTE This code doesn't run in our use case - case C_AVMATCH(&method, &av_playlist_ready) != 0: - { - log.Println("19") - var i int32 - for i = 0; i < int32(r.m_numCalls); i++ { - if C_AVMATCH(&(*(*C_RTMP_METHOD)(incPtr(unsafe.Pointer(r.m_methodCalls), int(i), - int(unsafe.Sizeof(*r.m_methodCalls))))).name, &av_set_playlist) != 0 { - C.AV_erase(r.m_methodCalls, &r.m_numCalls, int32(i), 1) - break - } - } - } - default: - log.Println("20") - */ - } -leave: - C_AMF_Reset(&obj) - return ret -} - -// void AMF_Reset(AMFObject* obj); -// amf.c +1282 -func C_AMF_Reset(obj *C_AMFObject) { - var n int32 - for n = 0; n < int32(obj.o_num); n++ { - C_AMFProp_Reset(&(*(*C_AMFObjectProperty)(incPtr(unsafe.Pointer(obj.o_props), - int(n), int(unsafe.Sizeof(*obj.o_props)))))) - } - //C.free(unsafe.Pointer(obj.o_props)) - obj.o_props = nil - obj.o_num = 0 -} - -// 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) - } else { - prop.p_vu.p_aval.av_len = 0 - prop.p_vu.p_aval.av_val = nil - } - prop.p_type = AMF_INVALID + return int32(C_RTMP_SendPacket(r, &packet, 1)) } // int SendReleaseStream(RTMP* r); @@ -2329,35 +1021,40 @@ func C_SendFCPublish(r *C_RTMP) int32 { return int32(C_RTMP_SendPacket(r, &packet, 0)) } -// int RTMP_SendCreateStream(RTMP* r); -// rtmp.c +1725 -func C_RTMP_SendCreateStream(r *C_RTMP) int32 { - var packet C_RTMPPacket - var pbuf [256]byte - var pend *byte = (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(&pbuf[0])) + - unsafe.Sizeof(pbuf))) - var enc *byte +// int SendFCUnpublish(RTMP *r); +// rtmp.c +1875 +func C_SendFCUnpublish(r *C_RTMP) int32 { + // TODO finish porting + /* + var packet C_RTMPPacket + var pbuf [1024]byte + var pend []byte = pbuf[1024:] + var enc []byte - packet.m_nChannel = 0x03 /* control channel (invoke) */ - packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM - packet.m_packetType = RTMP_PACKET_TYPE_INVOKE - packet.m_nTimeStamp = 0 - packet.m_nInfoField2 = 0 - packet.m_hasAbsTimestamp = 0 - packet.m_body = (*byte)(incBytePtr(unsafe.Pointer(&pbuf[0]), - int(RTMP_MAX_HEADER_SIZE))) + packet.m_nChannel = 0x03 + packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM + packet.m_packetType = RTMP_PACKET_TYPE_INVOKE + packet.m_nTimeStamp = 0 + packet.m_nInfoField2 = 0 + packet.m_hasAbsTimestamp = 0 + packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE - enc = (*byte)(unsafe.Pointer(packet.m_body)) - enc = C_AMF_EncodeString(enc, pend, &av_createStream) - r.m_numInvokes++ - enc = C_AMF_EncodeNumber(enc, pend, float64(r.m_numInvokes)) - *enc = AMF_NULL - enc = (*byte)(incBytePtr(unsafe.Pointer(enc), 1)) + // NOTE use of unsafe pointer will be remove here when packet.m_nBody becomes []byte + enc = []byte(unsafe.Pointer(packet.m_nBody)) + enc = C_AMF_EncodeString((*byte)(unsafe.Pointer(&enc[0])), + (*byte)(unsafe.Pointer(&pend[0])), &av_FCUnpublish) + r.m_numInvokes++ + enc = C_AMF_EncodeNumber(enc, pend, r->m_numInvokes); + *enc++ = AMF_NULL; + enc = AMF_EncodeString(enc, pend, &r->Link.playpath); + if (!enc) + return FALSE; - packet.m_nBodySize = uint32(uintptr(unsafe.Pointer(enc)) - uintptr( - unsafe.Pointer(packet.m_body))) + packet.m_nBodySize = enc - packet.m_body; - return int32(C_RTMP_SendPacket(r, &packet, 1)) + return RTMP_SendPacket(r, &packet, FALSE); + */ + return 0 } // int SendPublish(RTMP* r); @@ -2401,6 +1098,33 @@ func C_SendPublish(r *C_RTMP) int32 { return int32(C_RTMP_SendPacket(r, &packet, 1)) } +// int SendBytesReceived(RTMP* r); +// rtmp.c +2080 +func C_SendBytesReceived(r *C_RTMP) int { + var packet C_RTMPPacket + var pbuf [256]byte + pend := (*byte)(incBytePtr(unsafe.Pointer(&pbuf[0]), 256)) + + packet.m_nChannel = 0x02 /* control channel (invoke) */ + packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM + packet.m_packetType = RTMP_PACKET_TYPE_BYTES_READ_REPORT + packet.m_nTimeStamp = 0 + packet.m_nInfoField2 = 0 + packet.m_hasAbsTimestamp = 0 + packet.m_body = (*byte)(incBytePtr(unsafe.Pointer(&pbuf[0]), + RTMP_MAX_HEADER_SIZE)) + + packet.m_nBodySize = 4 + + C_AMF_EncodeInt32((*byte)(unsafe.Pointer(packet.m_body)), pend, int32(r.m_nBytesIn)) + // C.AMF_EncodeInt32(packet.m_body, (*byte)(unsafe.Pointer(pend)), r.m_nBytesIn) + + r.m_nBytesInSent = r.m_nBytesIn + + //return int(C.RTMP_SendPacket(r, &packet, 0)) + return C_RTMP_SendPacket(r, &packet, 0) +} + // int SendCheckBW(RTMP* r); // rtmp.c +2105 func C_SendCheckBW(r *C_RTMP) int32 { @@ -2432,65 +1156,6 @@ func C_SendCheckBW(r *C_RTMP) int32 { return int32(C_RTMP_SendPacket(r, &packet, 0)) } -// #define AVMATCH(a1,a2) -// amf.h +63 -func C_AVMATCH(a1, a2 *C_AVal) int32 { - if a1.av_len == a2.av_len && memcmp(unsafe.Pointer(a1.av_val), - unsafe.Pointer(a2.av_val), int(a1.av_len)) == 0 { - return 1 - } else { - return 0 - } -} - -// void AMFProp_GetString(AMFObjectProperty* prop, AVal* str); -// amf.c -func C_AMFProp_GetString(prop *C_AMFObjectProperty, str *C_AVal) { - if prop.p_type == AMF_STRING { - *str = prop.p_vu.p_aval - } else { - *str = AV_empty - } -} - -// 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 - } else { - *obj = AMFObj_Invalid - } -} - -// AMFObjectProperty* AMF_GetProp(AMFObject *obj, const AVal* name, int nIndex); -// amf.c + 1249 -func C_AMF_GetProp(obj *C_AMFObject, name *C_AVal, nIndex int32) *C_AMFObjectProperty { - if nIndex >= 0 { - if nIndex < int32(obj.o_num) { - return &(*(*C_AMFObjectProperty)(incPtr(unsafe.Pointer(obj.o_props), - int(nIndex), int(unsafe.Sizeof(*obj.o_props))))) - //return &obj.o_props[nIndex] - } - } else { - var n int32 - for n = 0; n < int32(obj.o_num); n++ { - if C_AVMATCH(&(*(*C_AMFObjectProperty)(incPtr(unsafe.Pointer(obj.o_props), - int(n), int(unsafe.Sizeof(*obj.o_props))))).p_name, name) != 0 { - return &(*(*C_AMFObjectProperty)(incPtr(unsafe.Pointer(obj.o_props), - int(n), int(unsafe.Sizeof(*obj.o_props))))) - } - } - } - return (*C_AMFObjectProperty)(&AMFProp_Invalid) -} - -// double AMFProp_GetNumber(AMFObjectProperty* prop); -// amf.c +330 -func C_AMFProp_GetNumber(prop *C_AMFObjectProperty) float64 { - return float64(prop.p_vu.p_number) -} - // void AV_erase(C_RTMP_METHOD* vals, int* num, int i, int freeit); // rtmp.c +2393 func C_AV_erase(vals *C_RTMP_METHOD, num *int32, i, freeit int32) { @@ -2512,13 +1177,249 @@ func C_AV_erase(vals *C_RTMP_METHOD, num *int32, i, freeit int32) { int(unsafe.Sizeof(*vals))))).num = 0 } -// void RTMPPacket_Free(RTMPPacket* p); -// rtmp.c +203 -func C_RTMPPacket_Free(p *C_RTMPPacket) { - if p.m_body != nil { - //C.free(decBytePtr(unsafe.Pointer(p.m_body), RTMP_MAX_HEADER_SIZE)) - p.m_body = nil +// void AV_queue(RTMP_METHOD** vals, int* num, C_AVal* av, int txn); +// rtmp.c +2414 +func C_AV_queue(vals **C_RTMP_METHOD, num *int32, av *C_AVal, txn int32) { + if (*num & 0x0f) == 0 { + //*vals = (*C_RTMP_METHOD)(realloc(unsafe.Pointer(*vals), uint32((*num+16)* + //int32(unsafe.Sizeof(*(*vals)))))) + *vals = (*C_RTMP_METHOD)(C.realloc(unsafe.Pointer(*vals), C.size_t((*num+16)* + int32(unsafe.Sizeof(*(*vals)))))) } + tmp := malloc(uintptr(av.av_len + 1)) + //tmp := allocate(uintptr(av.av_len + 1)) + memmove(tmp, unsafe.Pointer(av.av_val), uintptr(av.av_len)) + *indxBytePtr(tmp, int(av.av_len)) = '\000' + + (*(*C_RTMP_METHOD)(incPtr(unsafe.Pointer(*vals), int(*num), + int(unsafe.Sizeof(*(*vals)))))).num = int32(txn) + (*(*C_RTMP_METHOD)(incPtr(unsafe.Pointer(*vals), int(*num), + int(unsafe.Sizeof(*(*vals)))))).name.av_len = av.av_len + (*(*C_RTMP_METHOD)(incPtr(unsafe.Pointer(*vals), int(*num), + int(unsafe.Sizeof(*(*vals)))))).name.av_val = (*byte)(tmp) + (*num)++ +} + +// int HandleInvoke(RTMP* r, const char* body, unsigned int nBodySize); +// rtmp.c +2912 +func C_HandleInvoke(r *C_RTMP, body *byte, nBodySize uint32) int32 { + var obj C_AMFObject + var method C_AVal + var txn float64 + var ret int32 = 0 + var nRes int32 + + if *body != 0x02 { + // TODO use new logger here + //RTMP_Log(RTMP_LOGWARNING, "%s, Sanity failed. no string method in invoke packet", + //__FUNCTION__); + return 0 + } + nRes = C_AMF_Decode(&obj, body, int32(nBodySize), 0) + //nRes = int32(C.AMF_Decode(&obj, (*byte)(unsafe.Pointer(body)), int32(nBodySize), 0)) + if nRes < 0 { + // TODO use new logger here + //RTMP_Log(RTMP_LOGERROR, "%s, error decoding invoke packet", __FUNCTION__); + return 0 + } + + // NOTE we don't really need this ?? still functions without it + //C.AMF_Dump(&obj) + //C.AMFProp_GetString(C_AMF_GetProp(&obj, nil, 0), &method) + C_AMFProp_GetString(C_AMF_GetProp(&obj, nil, 0), &method) + txn = float64(C_AMFProp_GetNumber(C_AMF_GetProp(&obj, nil, 1))) + // TODO use new logger here + // RTMP_Log(RTMP_LOGDEBUG, "%s, server invoking <%s>", __FUNCTION__, method.av_val); + + switch { + case C_AVMATCH(&method, &av__result) != 0: + { + var methodInvoked C_AVal + var i int32 + for i = 0; i < int32(r.m_numCalls); i++ { + if float64((*(*C_RTMP_METHOD)(incPtr(unsafe.Pointer(r.m_methodCalls), int(i), + int(unsafe.Sizeof(*r.m_methodCalls))))).num) == txn { + methodInvoked = (*(*C_RTMP_METHOD)(incPtr(unsafe.Pointer(r.m_methodCalls), + int(i), int(unsafe.Sizeof(*r.m_methodCalls))))).name + //C.AV_erase(r.m_methodCalls, &r.m_numCalls, int32(i), 0) + C_AV_erase(r.m_methodCalls, (*int32)(&r.m_numCalls), int32(i), 0) + break + } + } + if methodInvoked.av_val == nil { + // TODO use new logger here + //RTMP_Log(RTMP_LOGDEBUG, "%s, received result id %f without matching request", + //__FUNCTION__, txn); + goto leave + } + // TODO use new logger here + //RTMP_Log(RTMP_LOGDEBUG, "%s, received result for method call <%s>", __FUNCTION__, + //methodInvoked.av_val); + switch { + case C_AVMATCH(&methodInvoked, &av_connect) != 0: + if r.Link.token.av_len != 0 { + panic("No support for link token") + + } + if (r.Link.protocol & RTMP_FEATURE_WRITE) != 0 { + C_SendReleaseStream(r) + C_SendFCPublish(r) + } else { + panic("Link protocol has no RTMP_FEATURE_WRITE") + } + + C_RTMP_SendCreateStream(r) + if (r.Link.protocol & RTMP_FEATURE_WRITE) == 0 { + panic("Link protocol has no RTMP_FEATURE_WRITE") + } + + case C_AVMATCH(&methodInvoked, &av_createStream) != 0: + r.m_stream_id = int32(C_AMFProp_GetNumber(C_AMF_GetProp(&obj, nil, 3))) + + if (r.Link.protocol & RTMP_FEATURE_WRITE) != 0 { + C_SendPublish(r) + } else { + panic("Link protocol has no RTMP_FEATURE_WRITE") + } + + case C_AVMATCH(&methodInvoked, &av_play) != 0 || + C_AVMATCH(&methodInvoked, &av_publish) != 0: + panic("Unsupported method av_play/av_publish") + } + //C.free(unsafe.Pointer(methodInvoked.av_val)) + } + case C_AVMATCH(&method, &av_onBWDone) != 0: + if r.m_nBWCheckCounter == 0 { + C_SendCheckBW(r) + } + + case C_AVMATCH(&method, &av_onFCUnsubscribe) != 0 || C_AVMATCH(&method, &av_onFCSubscribe) != 0: + panic("Unsupported method av_onFCUnsubscribe/av_onFCSubscribe") + + case C_AVMATCH(&method, &av_ping) != 0: + panic("Unsupported method av_ping") + + case C_AVMATCH(&method, &av__onbwcheck) != 0: + panic("Unsupported method av_onbwcheck") + + case C_AVMATCH(&method, &av__onbwdone) != 0: + panic("Unsupported method av_onbwdone") + + case C_AVMATCH(&method, &av_close) != 0: + panic("Unsupported method av_close") + + case C_AVMATCH(&method, &av_onStatus) != 0: + var obj2 C_AMFObject + var code, level C_AVal + C_AMFProp_GetObject(C_AMF_GetProp(&obj, nil, 3), &obj2) + C_AMFProp_GetString(C_AMF_GetProp(&obj2, &av_code, -1), &code) + C_AMFProp_GetString(C_AMF_GetProp(&obj2, &av_level, -1), &level) + + // TODO use new logger + // RTMP_Log(RTMP_LOGDEBUG, "%s, onStatus: %s", __FUNCTION__, code.av_val); + switch { + case C_AVMATCH(&code, &av_NetStream_Failed) != 0 || + C_AVMATCH(&code, &av_NetStream_Play_Failed) != 0 || + C_AVMATCH(&code, &av_NetStream_Play_StreamNotFound) != 0 || + C_AVMATCH(&code, &av_NetConnection_Connect_InvalidApp) != 0: + panic("Unsupported method av_NetStream/av_NetStream_Play_Failed/av_netSTream_Play_StreamNotFound/av_netConnection_Connect_invalidApp") + + case C_AVMATCH(&code, &av_NetStream_Play_Start) != 0 || + C_AVMATCH(&code, &av_NetStream_Play_PublishNotify) != 0: + panic("Unsupported method av_NetStream_Play_Start/av_NetStream_Play_PublishNotify") + + case C_AVMATCH(&code, &av_NetStream_Publish_Start) != 0: + + var i int32 + r.m_bPlaying = 1 + for i = 0; i < int32(r.m_numCalls); i++ { + if C_AVMATCH(&(*(*C_RTMP_METHOD)(incPtr(unsafe.Pointer(r.m_methodCalls), int(i), + int(unsafe.Sizeof(*r.m_methodCalls))))).name, &av_publish) != 0 { + //C.AV_erase(r.m_methodCalls, &r.m_numCalls, int32(i), 1) + C_AV_erase(r.m_methodCalls, (*int32)(&r.m_numCalls), int32(i), 1) + break + } + } + + case C_AVMATCH(&code, &av_NetStream_Play_Complete) != 0 || + C_AVMATCH(&code, &av_NetStream_Play_Stop) != 0 || + C_AVMATCH(&code, &av_NetStream_Play_UnpublishNotify) != 0: + panic("Unsupported method av_NetStream_Play_Complete/av_NetStream_Play_Stop/av_NetStream_Play_UnpublishNotify") + + case C_AVMATCH(&code, &av_NetStream_Seek_Notify) != 0: + panic("Unsupported method av_netStream_Seek_Notify") + + case C_AVMATCH(&code, &av_NetStream_Pause_Notify) != 0: + panic("Unsupported method av_NetStream_Pause_Notify") + } + + case C_AVMATCH(&method, &av_playlist_ready) != 0: + panic("Unsupported method av_playlist_ready") + + default: + } +leave: + C_AMF_Reset(&obj) + return ret +} + +// void HandleChangeChunkSize(RTMP* r, const RTMPPacket* packet); +// rtmp.c +3345 +func C_HandleChangeChunkSize(r *C_RTMP, packet *C_RTMPPacket) { + if packet.m_nBodySize >= 4 { + //r.m_inChunkSize = int32(C.AMF_DecodeInt32((*byte)(unsafe.Pointer(packet.m_body)))) + r.m_inChunkSize = int32(C_AMF_DecodeInt32((*byte)(unsafe.Pointer(packet.m_body)))) + // TODO use new logger here + // RTMP_Log(RTMP_LOGDEBUG, "%s, received: chunk size change to %d", __FUNCTION__, r.m_inChunkSize); + } +} + +// void HandleServerBW(RTMP* r, const RTMPPacket* packet); +// rtmp.c +3508 +func C_HandlServerBW(r *C_RTMP, packet *C_RTMPPacket) { + r.m_nServerBW = int32(C_AMF_DecodeInt32((*byte)(unsafe.Pointer(packet.m_body)))) + //r.m_nServerBW = int32(C.AMF_DecodeInt32((*byte)(unsafe.Pointer(packet.m_body)))) + // TODO use new logger here + // RTMP_Log(RTMP_LOGDEBUG, "%s: server BW = %d", __FUNCTION__, r.m_nServerBW); +} + +// void HandleClientBW(RTMP* r, const RTMPPacket* packet); +// rtmp.c +3515 +func C_HandleClientBW(r *C_RTMP, packet *C_RTMPPacket) { + r.m_nClientBW = int32(C_AMF_DecodeInt32((*byte)(unsafe.Pointer(packet.m_body)))) + //r.m_nClientBW = int32(C.AMF_DecodeInt32((*byte)(unsafe.Pointer(packet.m_body)))) + + if packet.m_nBodySize > 4 { + r.m_nClientBW2 = (uint8)(*indxBytePtr(unsafe.Pointer(packet.m_body), 4)) + } else { + r.m_nClientBW2 = 255 + } + // TODO use new logger here + // RTMP_Log(RTMP_LOGDEBUG, "%s: client BW = %d %d", __FUNCTION__, r.m_nClientBW, + //r.m_nClientBW2); +} + +// static int DecodeInt32LE(const char* data); +// rtmp.c +3527 +func C_DecodeInt32LE(data *byte) int32 { + var c *uint8 = (*uint8)(data) + return int32((*indxBytePtr(unsafe.Pointer(c), 3) << 24) | + (*indxBytePtr(unsafe.Pointer(c), 2) << 16) | + (*indxBytePtr(unsafe.Pointer(c), 1) << 8) | + *c) +} + +// int EncodeInt32LE(char* output, int nVal); +// rtmp.c +3537 +func C_EncodeInt32LE(output *byte, nVal int32) int32 { + *output = byte(nVal) + nVal >>= 8 + *indxBytePtr(unsafe.Pointer(output), 1) = byte(nVal) + nVal >>= 8 + *indxBytePtr(unsafe.Pointer(output), 2) = byte(nVal) + nVal >>= 8 + *indxBytePtr(unsafe.Pointer(output), 3) = byte(nVal) + return 4 } // int RTMP_ReadPacket(RTMP* r, RTMPPacket* packet); @@ -2741,460 +1642,79 @@ func C_RTMP_ReadPacket(r *C_RTMP, packet *C_RTMPPacket) int32 { return 1 } -// int AMF_Decode(AMFObject *obj, const char* pBuffer, int nSize, int bDecodeName); -// amf.c +1180 -func C_AMF_Decode(obj *C_AMFObject, pBuffer *byte, nSize int32, bDecodeName int32) int32 { - var nOriginalSize int32 = nSize - var bError int32 = 0 +// int HandShake(RTMP* r, int FP9HandShake); +// rtmp.c +3744 +func C_HandShake(r *C_RTMP, FP9HandShake int32) int { + var bMatch int + //uptime := uint32(0) + //suptime := uint32(0) + //typ := byte(0) + var uptime, suptime uint32 + var typ byte + //clientbuf := make([]byte, RTMP_SIG_SIZE+1) + var clientbuf [RTMP_SIG_SIZE + 1]byte + clientsig := (*byte)(incBytePtr(unsafe.Pointer(&clientbuf[0]), 1)) + //serversig := make([]byte, RTMP_SIG_SIZE) + var serversig [RTMP_SIG_SIZE]byte - obj.o_num = 0 - obj.o_props = nil + clientbuf[0] = 0x03 // not encrypted - for nSize > 0 { - var prop C_AMFObjectProperty - var nRes int32 + // TODO: port rtmp_getTime + //uptime = inet.Htonl(uint32(C.RTMP_GetTime())) + uptime = inet.Htonl(uint32(C_RTMP_GetTime())) + memmove(unsafe.Pointer(clientsig), unsafe.Pointer(&uptime), 4) - if nSize >= 3 && C_AMF_DecodeInt24(pBuffer) == AMF_OBJECT_END { - nSize -= 3 - bError = 0 - break - } + memset(indxBytePtr(unsafe.Pointer(clientsig), 4), 0, 4) - if bError != 0 { - // TODO use new logger here - log.Println("AMF_Decode: decoding error, ignoring bytes until next known pattern!") - nSize-- - pBuffer = (*byte)(incBytePtr(unsafe.Pointer(pBuffer), 1)) - continue - } - // TODO port AMFProp_Decode - nRes = int32(C_AMFProp_Decode(&prop, (*byte)(unsafe.Pointer(pBuffer)), - int32(nSize), int32(bDecodeName))) - // nRes = int32(C.AMFProp_Decode(&prop, (*byte)(unsafe.Pointer(pBuffer)), - // int32(nSize), int32(bDecodeName))) - if nRes == -1 { - bError = 1 - break - } else { - nSize -= nRes - if nSize < 0 { - bError = 1 - break - } - pBuffer = (*byte)(incBytePtr(unsafe.Pointer(pBuffer), int(nRes))) - C_AMF_AddProp(obj, &prop) - } + for i := 8; i < RTMP_SIG_SIZE; i++ { + *indxBytePtr(unsafe.Pointer(clientsig), i) = byte(rand.Intn(256)) } - if bError != 0 { - return -1 - } - - return nOriginalSize - nSize -} - -// int AMFProp_Decode(C_AMFObjectProperty* prop, const char* pBuffer, int nSize, int bDecodeName); -// amf.c +619 -func C_AMFProp_Decode(prop *C_AMFObjectProperty, pBuffer *byte, nSize, bDecodeName int32) int32 { - - var nOriginalSize int32 = nSize - var nRes int32 - - prop.p_name.av_len = 0 - prop.p_name.av_val = nil - - if nSize == 0 || pBuffer == nil { - // TODO use new logger here - // RTMP_Log(RTMP_LOGDEBUG, "%s: Empty buffer/no buffer pointer!", __FUNCTION__); - return -1 - } - - if bDecodeName != 0 && nSize < 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__); - return -1 - } - - if bDecodeName != 0 { - nNameSize := C_AMF_DecodeInt16(pBuffer) - if int32(nNameSize) > nSize-2 { - // TODO use new logger here - //RTMP_Log(RTMP_LOGDEBUG, "%s: Name size out of range: namesize (%d) > len (%d) - 2",__FUNCTION__, nNameSize, nSize); - return -1 - } - - C_AMF_DecodeString(pBuffer, &prop.p_name) - nSize -= int32(2 + nNameSize) - pBuffer = (*byte)(incBytePtr(unsafe.Pointer(pBuffer), int(2+nNameSize))) - } - - if nSize == 0 { - return -1 - } - - nSize-- - - prop.p_type = (C_AMFDataType)(int32(*pBuffer)) - pBuffer = (*byte)(incBytePtr(unsafe.Pointer(pBuffer), 1)) - - switch prop.p_type { - case AMF_NUMBER: - log.Println("1") - if nSize < 8 { - return -1 - } - prop.p_vu.p_number = float64(C_AMF_DecodeNumber(pBuffer)) - nSize -= 8 - - /* - case AMF_BOOLEAN: - log.Println("2") - if nSize < 1 { - return -1 - } - prop.p_vu.p_number = float64(C_AMF_DecodeBoolean((*byte)(unsafe.Pointer(pBuffer)))) - nSize-- - */ - case AMF_STRING: - { - log.Println("3") - var nStringSize = C_AMF_DecodeInt16(pBuffer) - - if int64(nSize) < int64(nStringSize)+2 { - return -1 - } - C_AMF_DecodeString(pBuffer, &prop.p_vu.p_aval) - nSize -= int32(2 + nStringSize) - } - - case AMF_OBJECT: - { - log.Println("4") - var nRes int32 = int32(C_AMF_Decode(&prop.p_vu.p_object, pBuffer, nSize, 1)) - if nRes == -1 { - return -1 - } - nSize -= nRes - - } - - case AMF_MOVIECLIP: - { - log.Println("5") - // TODO use new logger here - log.Println("AMFProp_Decode: MAF_MOVIECLIP reserved!") - //RTMP_Log(RTMP_LOGERROR, "AMF_MOVIECLIP reserved!"); - return -1 - - } - case AMF_NULL: - log.Println("6") - case AMF_UNDEFINED: - log.Println("7") - case AMF_UNSUPPORTED: - log.Println("8") - prop.p_type = AMF_NULL - - case AMF_REFERENCE: - { - log.Println("9") - // TODO use new logger here - log.Println("AMFProp_Decode: AMF_REFERENCE not supported!") - //RTMP_Log(RTMP_LOGERROR, "AMF_REFERENCE not supported!"); - return -1 - } - - case AMF_ECMA_ARRAY: - { - log.Println("10") - nSize -= 4 - - // next comes the rest, mixed array has a final 0x000009 mark and names, so its an object - nRes = C_AMF_Decode(&prop.p_vu.p_object, (*byte)(incBytePtr( - unsafe.Pointer(pBuffer), 4)), nSize, 1) - if nRes == -1 { - return -1 - } - nSize -= nRes - } - - case AMF_OBJECT_END: - { - log.Println("11") - return -1 - } - /* - case AMF_STRICT_ARRAY: - { - log.Println("12") - nArrayLen := int32(C_AMF_DecodeInt32(pBuffer)) - nSize -= 4 - - nRes = C_AMF_DecodeArray(&prop.p_vu.p_object, (*byte)(incBytePtr(unsafe.Pointer(pBuffer), 4)), nSize, int32(nArrayLen), FALSE) - if nRes == -1 { - return -1 - } - nSize -= nRes - } - */ - /* - case AMF_DATE: - { - log.Println("13") - // TODO use new logger here - //RTMP_Log(RTMP_LOGDEBUG, "AMF_DATE"); - - if nSize < 10 { - return -1 - } - - prop.p_vu.p_number = float64(C_AMF_DecodeNumber(pBuffer)) - prop.p_UTCoffset = C.int16_t(C_AMF_DecodeInt16((*byte)(incBytePtr(unsafe.Pointer(pBuffer), 8)))) - - nSize -= 10 - - } - */ - case AMF_LONG_STRING: - log.Println("14") - /* - case AMF_XML_DOC: - { - log.Println("15") - var nStringSize uint32 = C_AMF_DecodeInt32(pBuffer) - if int64(nSize) < int64(nStringSize)+4 { - return -1 - } - C_AMF_DecodeLongString(pBuffer, &prop.p_vu.p_aval) - nSize -= int32(4 + nStringSize) - if prop.p_type == AMF_LONG_STRING { - prop.p_type = AMF_STRING - } - } - */ - case AMF_RECORDSET: - { - log.Println("16") - // TODO use new logger here - log.Println("AMFProp_Decode: AMF_RECORDSET reserved!") - //RTMP_Log(RTMP_LOGERROR, "AMF_RECORDSET reserved!"); - return -1 - - } - case AMF_TYPED_OBJECT: - { - log.Println("17") - // TODO use new logger here - // RTMP_Log(RTMP_LOGERROR, "AMF_TYPED_OBJECT not supported!") - return -1 - } - - /* - case AMF_AVMPLUS: - { - log.Println("18") - nRes := int32(C.AMF3_Decode(&prop.p_vu.p_object, (*byte)(unsafe.Pointer(pBuffer)), int32(nSize), 1)) - if nRes == -1 { - return -1 - } - nSize -= nRes - prop.p_type = AMF_OBJECT - } - */ - default: - log.Println("19") - // TODO use new logger here - //RTMP_Log(RTMP_LOGDEBUG, "%s - unknown datatype 0x%02x, @%p", __FUNCTION__, - //prop.p_type, pBuffer - 1); - return -1 - } - - return nOriginalSize - nSize -} - -// void AMF_AddProp(AMFObject* obj, const AMFObjectProperty* prop); -// amf.c + 1234 -func C_AMF_AddProp(obj *C_AMFObject, prop *C_AMFObjectProperty) { - if (obj.o_num & 0x0f) == 0 { - //obj.o_props = (*C_AMFObjectProperty)(realloc(unsafe.Pointer(obj.o_props), - //uint32(int(obj.o_num+16)*int(unsafe.Sizeof(*obj.o_props))))) - obj.o_props = (*C_AMFObjectProperty)(C.realloc(unsafe.Pointer(obj.o_props), - C.size_t(int(obj.o_num+16)*int(unsafe.Sizeof(*obj.o_props))))) - } - memmove(unsafe.Pointer(&(*(*C_AMFObjectProperty)(incPtr( - unsafe.Pointer(obj.o_props), int(obj.o_num), int(unsafe.Sizeof(*obj.o_props)))))), - unsafe.Pointer(prop), unsafe.Sizeof(*obj.o_props)) - obj.o_num++ -} - -// static int DecodeInt32LE(const char* data); -// rtmp.c +3527 -func C_DecodeInt32LE(data *byte) int32 { - var c *uint8 = (*uint8)(data) - return int32((*indxBytePtr(unsafe.Pointer(c), 3) << 24) | - (*indxBytePtr(unsafe.Pointer(c), 2) << 16) | - (*indxBytePtr(unsafe.Pointer(c), 1) << 8) | - *c) -} - -// unsigned int AMF_DeocdeInt32(const char* data); -// amf.c +59 -func C_AMF_DecodeInt32(data *byte) uint32 { - c := (*uint8)(data) - val := uint32( - int32(*c)<<24 | - int32(*(*uint8)(incBytePtr(unsafe.Pointer(c), 1)))<<16 | - int32(*(*uint8)(incBytePtr(unsafe.Pointer(c), 2)))<<8 | - int32(*(*uint8)(incBytePtr(unsafe.Pointer(c), 3)))) - return uint32(val) -} - -// int EncodeInt32LE(char* output, int nVal); -// rtmp.c +3537 -func C_EncodeInt32LE(output *byte, nVal int32) int32 { - *output = byte(nVal) - nVal >>= 8 - *indxBytePtr(unsafe.Pointer(output), 1) = byte(nVal) - nVal >>= 8 - *indxBytePtr(unsafe.Pointer(output), 2) = byte(nVal) - nVal >>= 8 - *indxBytePtr(unsafe.Pointer(output), 3) = byte(nVal) - return 4 -} - -// #define RTMPPacket_IsReady(a) -// rtmp.h +142 -func C_RTMPPacket_IsReady(p *C_RTMPPacket) int { - if p.m_nBytesRead == p.m_nBodySize { - return 1 - } - return 0 -} - -func endSession(rtmp *C_RTMP) uint32 { - if rtmp == nil { - return 3 - } - - //C.RTMP_Close(rtmp) - //C.RTMP_Free(rtmp) - return 0 -} - -// int RTMP_Write(RTMP* r, const char* buf, int size); -// rtmp.c +5095 -func C_RTMP_Write(r *C_RTMP, data []byte) int { - buf := sliceToPtr(data) - // TODO: port RTMPPacket - var pkt = &r.m_write - var pend, enc unsafe.Pointer - size := len(data) - s2 := size - var ret, num int - - pkt.m_nChannel = 0x04 - pkt.m_nInfoField2 = int32(r.m_stream_id) - for s2 != 0 { - if pkt.m_nBytesRead == 0 { - if size < minDataSize { - log.Printf("size: %d\n", size) - log.Printf("too small \n") - return 0 - } - - if *indxBytePtr(buf, 0) == 'F' && *indxBytePtr(buf, 1) == 'L' && *indxBytePtr(buf, 2) == 'V' { - buf = unsafe.Pointer(uintptr(buf) + uintptr(13)) - s2 -= 13 - } - - pkt.m_packetType = uint8(*indxBytePtr(buf, 0)) - buf = incBytePtr(buf, 1) - pkt.m_nBodySize = uint32(C_AMF_DecodeInt24((*byte)(buf))) - buf = incBytePtr(buf, 3) - pkt.m_nTimeStamp = uint32(C_AMF_DecodeInt24((*byte)(buf))) - buf = incBytePtr(buf, 3) - pkt.m_nTimeStamp |= uint32(*indxBytePtr(buf, 0)) << 24 - buf = incBytePtr(buf, 4) - s2 -= 11 - - if ((pkt.m_packetType == RTMP_PACKET_TYPE_AUDIO || - pkt.m_packetType == RTMP_PACKET_TYPE_VIDEO) && - pkt.m_nTimeStamp == 0) || pkt.m_packetType == RTMP_PACKET_TYPE_INFO { - - pkt.m_headerType = RTMP_PACKET_SIZE_LARGE - - if pkt.m_packetType == RTMP_PACKET_TYPE_INFO { - pkt.m_nBodySize += 16 - } - } else { - pkt.m_headerType = RTMP_PACKET_SIZE_MEDIUM - } - // TODO: Port this - if int(C_RTMPPacket_Alloc(pkt, uint32(pkt.m_nBodySize))) == 0 { - log.Println("Failed to allocate packet") - return 0 - } - - enc = unsafe.Pointer(pkt.m_body) - pend = incBytePtr(enc, int(pkt.m_nBodySize)) - - if pkt.m_packetType == RTMP_PACKET_TYPE_INFO { - enc = unsafe.Pointer(C_AMF_EncodeString((*byte)(enc), (*byte)(pend), &setDataFrame)) - pkt.m_nBytesRead = uint32(math.Abs(float64(uintptr(enc) - - uintptr(unsafe.Pointer(pkt.m_body))))) - } - - } else { - enc = incBytePtr(unsafe.Pointer(pkt.m_body), int(pkt.m_nBytesRead)) - } - num = int(pkt.m_nBodySize - pkt.m_nBytesRead) - if num > s2 { - num = s2 - } - - memmove(enc, buf, uintptr(num)) - pkt.m_nBytesRead += uint32(num) - s2 -= num - buf = incBytePtr(buf, num) - if pkt.m_nBytesRead == pkt.m_nBodySize { - // TODO: Port this - ret = C_RTMP_SendPacket(r, pkt, 0) - // TODO: Port this - //C.RTMPPacket_Free(pkt) - C_RTMPPacket_Free(pkt) - pkt.m_nBytesRead = 0 - if ret == 0 { - return -1 - } - buf = incBytePtr(buf, 4) - s2 -= 4 - if s2 < 0 { - break - } - } - } - return size + s2 -} - -// int RTMPPacket_Alloc(RTMPPacket* p, uint32_t nSize); -// rtmp.c +189 -func C_RTMPPacket_Alloc(p *C_RTMPPacket, nSize uint32) int { - var ptr *byte - // TODO: port C.SIZE_MAX - // also work out how to deal with the overfloat - /* - if int64(nSize) > (C.SIZE_MAX - RTMP_MAX_HEADER_SIZE) { - return 0 - } - */ - ptr = (*byte)(calloc(1, uintptr(nSize+RTMP_MAX_HEADER_SIZE))) - - if ptr == nil { + if C_WriteN(r, unsafe.Pointer(&clientbuf[0]), RTMP_SIG_SIZE+1) == 0 { return 0 } - p.m_body = (*byte)(incBytePtr(unsafe.Pointer(ptr), RTMP_MAX_HEADER_SIZE)) - p.m_nBytesRead = 0 + + //if C.ReadN(r, (*byte)(unsafe.Pointer(&typ)), 1) != 1 { + if C_ReadN(r, (*byte)(unsafe.Pointer(&typ)), 1) != 1 { + return 0 + } + + if debugMode { + log.Println("C_HandShake: Type answer: %v", typ) + } + if typ != clientbuf[0] { + log.Println("C_HandShake: type mismatch: client sent %v, server sent: %v", + clientbuf[0], typ) + } + if C_ReadN(r, (*byte)(unsafe.Pointer(&serversig[0])), RTMP_SIG_SIZE) != RTMP_SIG_SIZE { + //if C.ReadN(r, (*byte)(unsafe.Pointer(&serversig[0])), RTMP_SIG_SIZE) != RTMP_SIG_SIZE { + return 0 + } + + // decode server response + memmove(unsafe.Pointer(&suptime), unsafe.Pointer(&serversig[0]), 4) + suptime = inet.Ntohl(suptime) + + // 2nd part of handshake + if C_WriteN(r, unsafe.Pointer(&serversig[0]), RTMP_SIG_SIZE) == 0 { + return 0 + } + + if C_ReadN(r, (*byte)(unsafe.Pointer(&serversig[0])), RTMP_SIG_SIZE) != RTMP_SIG_SIZE { + //if C.ReadN(r, (*byte)(unsafe.Pointer(&serversig[0])), RTMP_SIG_SIZE) != RTMP_SIG_SIZE { + return 0 + } + + // TODO: find golang memcmp + bMatch = 0 + if memcmp(unsafe.Pointer(&serversig[0]), unsafe.Pointer(clientsig), + RTMP_SIG_SIZE) == 0 { + bMatch = 1 + } + + if bMatch == 0 { + log.Println("Client signature does not match!") + } return 1 } @@ -3460,47 +1980,126 @@ func C_RTMP_SendPacket(r *C_RTMP, packet *C_RTMPPacket, queue int) int { return 1 } -// int WriteN(RTMP* r, const char* buffer, int n); -// rtmp.c +1502 -func C_WriteN(r *C_RTMP, buffer unsafe.Pointer, n int) int { - ptr := buffer - for n > 0 { - var nBytes int - - nBytes = int(C_RTMPSockBuf_Send(&r.m_sb, (*byte)(ptr), int32(n))) - - if nBytes < 0 { - if debugMode { - log.Println("C_WriteN, RTMP send error") - } - - // TODO: port this - //C.RTMP_Close(r) - n = 1 - break - } - - if nBytes == 0 { - break - } - - n -= nBytes - ptr = incBytePtr(ptr, nBytes) - } - - if n == 0 { - return 1 - } - return 0 +// void RTMP_Close(RTMP *r); +// rtmp.c +4168 +func C_RTMP_Close(r *C_RTMP) { + C_CloseInternal(r, 0) } -const length = 512 +// static void CloseInternal(RTMP *r, int reconnect); +// rtmp.c +4175 +func C_CloseInternal(r *C_RTMP, reconnect int32) { + // TODO: port SendFCUnpublish + // TODO: port SendDeleteStream + // TODO: port RTMPSockBuf_Close + // TODO: port AV_Clear + /* + var i int32 -var RTMPT_cmds = []string{ - "open", - "send", - "idle", - "close", + if C_RTMP_IsConnected(r) != 0 { + if r.m_stream_id > 0 { + i = int32(r.m_stream_id) + if r.Link.protocol&RTMP_FEATURE_WRITE != 0 { + C.SendFCUnpublish(r) + } + C.SendDeleteStream(r, float64(i)) + } + C.RTMPSockBuf_Close(&r.m_sb) + } + + r.m_stream_id = -1 + r.m_sb.sb_socket = -1 + r.m_nBWCheckCounter = 0 + r.m_nBytesIn = 0 + r.m_nBytesInSent = 0 + + if r.m_read.flags&RTMP_READ_HEADER != 0 { + //C.free(unsafe.Pointer(r.m_read.buf)) + r.m_read.buf = nil + } + + r.m_read.dataType = 0 + r.m_read.flags = 0 + r.m_read.status = 0 + r.m_read.nResumeTS = 0 + r.m_read.nIgnoredFrameCounter = 0 + r.m_read.nIgnoredFlvFrameCounter = 0 + + r.m_write.m_nBytesRead = 0 + C.RTMPPacket_Free(&r.m_write) + + // NOTE: C frees - not using in our case + for i := 0; i < int(r.m_channelsAllocatedIn); i++ { + if *(**C_RTMPPacket)(incPtr(unsafe.Pointer(r.m_vecChannelsIn), i, + int(unsafe.Sizeof(&r.m_write)))) != nil { + + //C.RTMPPacket_Free(*(**C_RTMPPacket)(incPtr(unsafe.Pointer(r.m_vecChannelsIn), i, + //int(unsafe.Sizeof(&r.m_write))))) + + //C.free(unsafe.Pointer(*(**C_RTMPPacket)(incPtr(unsafe.Pointer(r.m_vecChannelsIn), + //i, int(unsafe.Sizeof(&r.m_write)))))) + + *(**C_RTMPPacket)(incPtr(unsafe.Pointer(r.m_vecChannelsIn), + i, int(unsafe.Sizeof(&r.m_write)))) = nil + } + } + + + //C.free(unsafe.Pointer(r.m_vecChannelsOut)) + r.m_vecChannelsOut = nil + r.m_channelsAllocatedOut = 0 + C.AV_clear(r.m_methodCalls, r.m_numCalls) + + r.m_methodCalls = nil + r.m_numCalls = 0 + r.m_numInvokes = 0 + + r.m_bPlaying = C.uchar(0) + r.m_sb.sb_size = 0 + + r.m_msgCounter = 0 + r.m_resplen = 0 + r.m_unackd = 0 + + if ((r.Link.lFlags & RTMP_LF_FTCU) != 0) && (reconnect == 0) { + //C.free(unsafe.Pointer(r.Link.app.av_val)) + r.Link.app.av_val = nil + r.Link.lFlags ^= RTMP_LF_FAPU + } + + if reconnect == 0 { + //C.free(unsafe.Pointer(r.Link.playpath0.av_val)) + r.Link.playpath0.av_val = nil + } + */ +} + +// int RTMPSockBuf_Fill(RTMPSockBuf* sb); +// rtmp.c +4253 +func C_RTMPSockBuf_Fill(sb *C_RTMPSockBuf) int { + var nBytes int + + if sb.sb_size == 0 { + sb.sb_start = &sb.sb_buf[0] + } + + for { + nBytes = int(unsafe.Sizeof(sb.sb_buf)) - 1 - int(sb.sb_size) - + int(uintptr(unsafe.Pointer(sb.sb_start))-uintptr(unsafe.Pointer( + &sb.sb_buf[0]))) + + // TODO: figure out what to do with recv + nBytes = int(C.recv(C.int(sb.sb_socket), unsafe.Pointer(uintptr(unsafe.Pointer( + sb.sb_start))+uintptr(int(sb.sb_size))), C.size_t(nBytes), 0)) + + if nBytes != -1 { + sb.sb_size += int32(nBytes) + } else { + log.Println("C_RTMPSockBuf_Fill: recv error!") + } + break + } + return nBytes } // int RTMPSockBuf_Send(RTMPSockBuf* sb, const char* buf, int len); @@ -3510,354 +2109,101 @@ func C_RTMPSockBuf_Send(sb *C_RTMPSockBuf, buf *byte, l int32) int32 { return int32(C.send(C.int(sb.sb_socket), unsafe.Pointer(buf), C.size_t(l), 0)) } -// void AV_queue(RTMP_METHOD** vals, int* num, C_AVal* av, int txn); -// rtmp.c +2414 -func C_AV_queue(vals **C_RTMP_METHOD, num *int32, av *C_AVal, txn int32) { - if (*num & 0x0f) == 0 { - //*vals = (*C_RTMP_METHOD)(realloc(unsafe.Pointer(*vals), uint32((*num+16)* - //int32(unsafe.Sizeof(*(*vals)))))) - *vals = (*C_RTMP_METHOD)(C.realloc(unsafe.Pointer(*vals), C.size_t((*num+16)* - int32(unsafe.Sizeof(*(*vals)))))) - } - tmp := malloc(uintptr(av.av_len + 1)) - //tmp := allocate(uintptr(av.av_len + 1)) - memmove(tmp, unsafe.Pointer(av.av_val), uintptr(av.av_len)) - *indxBytePtr(tmp, int(av.av_len)) = '\000' +// int RTMP_Write(RTMP* r, const char* buf, int size); +// rtmp.c +5095 +func C_RTMP_Write(r *C_RTMP, data []byte) int { + buf := sliceToPtr(data) + // TODO: port RTMPPacket + var pkt = &r.m_write + var pend, enc unsafe.Pointer + size := len(data) + s2 := size + var ret, num int - (*(*C_RTMP_METHOD)(incPtr(unsafe.Pointer(*vals), int(*num), - int(unsafe.Sizeof(*(*vals)))))).num = int32(txn) - (*(*C_RTMP_METHOD)(incPtr(unsafe.Pointer(*vals), int(*num), - int(unsafe.Sizeof(*(*vals)))))).name.av_len = av.av_len - (*(*C_RTMP_METHOD)(incPtr(unsafe.Pointer(*vals), int(*num), - int(unsafe.Sizeof(*(*vals)))))).name.av_val = (*byte)(tmp) - (*num)++ -} + pkt.m_nChannel = 0x04 + pkt.m_nInfoField2 = int32(r.m_stream_id) + for s2 != 0 { + if pkt.m_nBytesRead == 0 { + if size < minDataSize { + log.Printf("size: %d\n", size) + log.Printf("too small \n") + return 0 + } -// char* AMF_EncodeNamedNumber(char* output, char* outend, const C_AVal* strName, double dVal); -// amf.c +286 -func C_AMF_EncodeNamedNumber(output *byte, outend *byte, strName *C_AVal, dVal float64) *byte { - if int(uintptr(unsafe.Pointer(output)))+2+int(strName.av_len) > int(uintptr(unsafe.Pointer(outend))) { - return nil - } - output = C_AMF_EncodeInt16(output, outend, int16(strName.av_len)) - memmove(unsafe.Pointer(output), unsafe.Pointer(strName.av_val), uintptr(strName.av_len)) - output = (*byte)(incBytePtr(unsafe.Pointer(output), int(strName.av_len))) - return C_AMF_EncodeNumber(output, outend, dVal) -} + if *indxBytePtr(buf, 0) == 'F' && *indxBytePtr(buf, 1) == 'L' && *indxBytePtr(buf, 2) == 'V' { + buf = unsafe.Pointer(uintptr(buf) + uintptr(13)) + s2 -= 13 + } -// char* AMF_EncodeNamedBoolean(char* output, char* outend, const C_AVal* strname, int bVal); -// amf.c +299 -func C_AMF_EncodeNamedBoolean(output *byte, outend *byte, strName *C_AVal, bVal int) *byte { - if int(uintptr(unsafe.Pointer(output)))+2+int(strName.av_len) > int(uintptr(unsafe.Pointer(outend))) { - return nil - } - output = C_AMF_EncodeInt16(output, outend, int16(strName.av_len)) - memmove(unsafe.Pointer(output), unsafe.Pointer(strName.av_val), uintptr(strName.av_len)) - output = (*byte)(incBytePtr(unsafe.Pointer(output), int(strName.av_len))) - return C_AMF_EncodeBoolean(output, outend, bVal) -} + pkt.m_packetType = uint8(*indxBytePtr(buf, 0)) + buf = incBytePtr(buf, 1) + pkt.m_nBodySize = uint32(C_AMF_DecodeInt24((*byte)(buf))) + buf = incBytePtr(buf, 3) + pkt.m_nTimeStamp = uint32(C_AMF_DecodeInt24((*byte)(buf))) + buf = incBytePtr(buf, 3) + pkt.m_nTimeStamp |= uint32(*indxBytePtr(buf, 0)) << 24 + buf = incBytePtr(buf, 4) + s2 -= 11 -// void AMFProp_SetName(AMFObjectProperty *prop, AVal *name); -// amf.c +318 -func C_AMFProp_SetName(prop *C_AMFObjectProperty, name *C_AVal) { - prop.p_name = *name -} + if ((pkt.m_packetType == RTMP_PACKET_TYPE_AUDIO || + pkt.m_packetType == RTMP_PACKET_TYPE_VIDEO) && + pkt.m_nTimeStamp == 0) || pkt.m_packetType == RTMP_PACKET_TYPE_INFO { -const ( - AMF3_INTEGER_MAX = 268435455 - AMF3_INTEGER_MIN = -268435456 -) + pkt.m_headerType = RTMP_PACKET_SIZE_LARGE -// int AMF3ReadInteger(const char *data, int32_t *valp); -// amf.c +426 -// TODO test -func C_AMF3ReadInteger(data *byte, valp *int32) int32 { - var i int - var val int32 + if pkt.m_packetType == RTMP_PACKET_TYPE_INFO { + pkt.m_nBodySize += 16 + } + } else { + pkt.m_headerType = RTMP_PACKET_SIZE_MEDIUM + } + // TODO: Port this + if int(C_RTMPPacket_Alloc(pkt, uint32(pkt.m_nBodySize))) == 0 { + log.Println("Failed to allocate packet") + return 0 + } + + enc = unsafe.Pointer(pkt.m_body) + pend = incBytePtr(enc, int(pkt.m_nBodySize)) + + if pkt.m_packetType == RTMP_PACKET_TYPE_INFO { + enc = unsafe.Pointer(C_AMF_EncodeString((*byte)(enc), (*byte)(pend), &setDataFrame)) + pkt.m_nBytesRead = uint32(math.Abs(float64(uintptr(enc) - + uintptr(unsafe.Pointer(pkt.m_body))))) + } - for i <= 2 { - /* handle first 3 bytes */ - if *indxBytePtr(unsafe.Pointer(data), i)&0x80 != 0 { - /* byte used */ - val <<= 7 /* shift up */ - val |= int32(*indxBytePtr(unsafe.Pointer(data), i) & 0x7f) /* add bits */ - i++ } else { - break + enc = incBytePtr(unsafe.Pointer(pkt.m_body), int(pkt.m_nBytesRead)) + } + num = int(pkt.m_nBodySize - pkt.m_nBytesRead) + if num > s2 { + num = s2 + } + + memmove(enc, buf, uintptr(num)) + pkt.m_nBytesRead += uint32(num) + s2 -= num + buf = incBytePtr(buf, num) + if pkt.m_nBytesRead == pkt.m_nBodySize { + // TODO: Port this + ret = C_RTMP_SendPacket(r, pkt, 0) + // TODO: Port this + //C.RTMPPacket_Free(pkt) + C_RTMPPacket_Free(pkt) + pkt.m_nBytesRead = 0 + if ret == 0 { + return -1 + } + buf = incBytePtr(buf, 4) + s2 -= 4 + if s2 < 0 { + break + } } } - - if i > 2 { - /* use 4th byte, all 8bits */ - val <<= 8 - val |= int32(*indxBytePtr(unsafe.Pointer(data), 3)) - - /* range check */ - if val > AMF3_INTEGER_MAX { - val -= (1 << 29) - } - } else { - /* use 7bits of last unparsed byte (0xxxxxxx) */ - val <<= 7 - val |= int32(*indxBytePtr(unsafe.Pointer(data), i)) - } - - *valp = val - - if i > 2 { - return 4 - } - return int32(i + 1) + return size + s2 } -// int AMF3ReadString(const char *data, AVal *str); -// amf.c +466 -func C_AMF3ReadString(data *byte, str *C_AVal) int32 { - var ref int32 - // assert elided - we will get a panic if it's nil. - - len := C_AMF3ReadInteger(data, &ref) - data = indxBytePtr(unsafe.Pointer(data), int(len)) - - if ref&0x1 == 0 { - /* reference: 0xxx */ - // TODO(kortschak) Logging. - // refIndex := (ref >> 1) - // RTMP_Log(RTMP_LOGDEBUG, - // "%s, string reference, index: %d, not supported, ignoring!", - // __FUNCTION__, refIndex); - str.av_val = nil - str.av_len = 0 - return len - } else { - nSize := (ref >> 1) - str.av_val = (*byte)(unsafe.Pointer(data)) - str.av_len = int32(nSize) - return len + nSize - } - return len -} - -// char* AMF_EncodeBoolean(char* output, char* outend, int bVal); -// amf.c +260 -func C_AMF_EncodeBoolean(output *byte, outend *byte, bVal int) *byte { - if int(uintptr(unsafe.Pointer(output)))+2 > int(uintptr(unsafe.Pointer(outend))) { - return nil - } - *(*byte)(unsafe.Pointer(output)) = AMF_BOOLEAN - output = (*byte)(incBytePtr(unsafe.Pointer(output), 1)) - val := byte(0x01) - if bVal == 0 { - val = byte(0x00) - } - *(*byte)(unsafe.Pointer(output)) = val - output = (*byte)(incBytePtr(unsafe.Pointer(output), 1)) - return output -} - -// char* AMF_EncodeNumber(char* output, char* outend, double dVal); -// amf.c +199 -func C_AMF_EncodeNumber(output *byte, outend *byte, dVal float64) *byte { - if int(uintptr(unsafe.Pointer(output)))+1+8 > int(uintptr(unsafe.Pointer(outend))) { - return nil - } - // TODO: port this - *(*byte)(unsafe.Pointer(output)) = AMF_NUMBER - output = (*byte)(incBytePtr(unsafe.Pointer(output), 1)) - // NOTE: here we are assuming little endian for both byte order and float - // word order - var ci, co *uint8 - ci = (*uint8)(unsafe.Pointer(&dVal)) - co = (*uint8)(unsafe.Pointer(output)) - for i := 0; i < 8; i++ { - *indxBytePtr(unsafe.Pointer(co), i) = *indxBytePtr(unsafe.Pointer(ci), 7-i) - } - return (*byte)(incBytePtr(unsafe.Pointer(output), 8)) -} - -// void AMF_DecodeLongString(const char *data, AVal *bv); -// amf.c +75 -func C_AMF_DecodeLongString(data *byte, bv *C_AVal) { - bv.av_len = int32(C_AMF_DecodeInt32(data)) - if bv.av_len > 0 { - bv.av_val = (*byte)(incBytePtr(unsafe.Pointer(data), 4)) - } else { - bv.av_val = nil - } -} - -// double AMF_DecodeNumber(const char* data); -// amf.c +82 -func C_AMF_DecodeNumber(data *byte) float64 { - var dVal float64 - var ci, co *uint8 - ci = (*uint8)(unsafe.Pointer(data)) - co = (*uint8)(unsafe.Pointer(&dVal)) - for i := 0; i < 8; i++ { - *indxBytePtr(unsafe.Pointer(co), i) = *indxBytePtr(unsafe.Pointer(ci), 7-i) - } - return dVal -} - -// int AMF_DecodeBoolean(const char *data); -// amf.c +132 -func C_AMF_DecodeBoolean(data *byte) int32 { - if *data != 0 { - return 1 - } - return 0 -} - -// char* AMF_EncodeNamedString(char* output, char* outend, const C_AVal* strName, const C_AVal* strValue); -// amf.c +273 -func C_AMF_EncodeNamedString(output *byte, outend *byte, strName *C_AVal, strValue *C_AVal) *byte { - if int(uintptr(unsafe.Pointer(output)))+2+int(strName.av_len) > int(uintptr(unsafe.Pointer(outend))) { - return nil - } - output = C_AMF_EncodeInt16(output, outend, int16(strName.av_len)) - memmove(unsafe.Pointer(output), unsafe.Pointer(strName.av_val), uintptr(strName.av_len)) - output = (*byte)(incBytePtr(unsafe.Pointer(output), int(strName.av_len))) - return C_AMF_EncodeString(output, outend, strValue) -} - -// void AMF_DecodeString(const char* data, C_AVal* bv); -// amf.c +68 -func C_AMF_DecodeString(data *byte, bv *C_AVal) { - dataPtr := unsafe.Pointer(data) - //bv.av_len = int32(C.AMF_DecodeInt16((*byte)(dataPtr))) - bv.av_len = int32(C_AMF_DecodeInt16((*byte)(dataPtr))) - if bv.av_len > 0 { - bv.av_val = (*byte)(incBytePtr(dataPtr, 2)) - } else { - bv.av_val = nil - } -} - -// unsigned short AMF_DecodeInt16(const char* data); -// amf.c +41 -func C_AMF_DecodeInt16(data *byte) uint16 { - c := unsafe.Pointer(data) - return uint16(*(*uint8)(c)<<8 | *(*byte)(incBytePtr(c, 1))) -} - -// char* AMF_EncodeInt24(char* output, char* outend, int nVal); -// amf.c +149 -func C_AMF_EncodeInt24(output *byte, outend *byte, nVal int32) *byte { - outputPtr := unsafe.Pointer(output) - outendPtr := unsafe.Pointer(outend) - if uintptr(outputPtr)+3 > uintptr(outendPtr) { - // length < 3 - return nil - } - // Assign output[2] - third := (*byte)(incBytePtr(outputPtr, 2)) - *third = (byte)(nVal & 0xff) - // Assign output[1] - second := (*byte)(incBytePtr(outputPtr, 1)) - *second = (byte)(nVal >> 8) - // Assign output[0] - *output = (byte)(nVal >> 16) - return (*byte)(incBytePtr(outputPtr, 3)) -} - -// unsigned int AMF_DecodeInt24(const char* data); -// amf.c +50 -func C_AMF_DecodeInt24(data *byte) uint32 { - // TODO Understand logic and simplify - c := (*uint8)(unsafe.Pointer(data)) - dst := uint32(int32(*c) << 16) - dst |= uint32(int32(*((*uint8)(unsafe.Pointer(uintptr(unsafe.Pointer(c)) + - (uintptr)(int32(1))*unsafe.Sizeof(*c))))) << 8) - dst |= uint32(int32(*((*uint8)(unsafe.Pointer(uintptr(unsafe.Pointer(c)) + - (uintptr)(int32(2))*unsafe.Sizeof(*c)))))) - return dst -} - -// char* AMF_EncodeString(char* output, char* outend, const C_AVal* bv); -// amf.c +174 -func C_AMF_EncodeString(output *byte, outend *byte, bv *C_AVal) *byte { - outputPtr := unsafe.Pointer(output) - outendPtr := unsafe.Pointer(outend) - if (bv.av_len < 65536 && uintptr(incBytePtr(outputPtr, 1+2+int(bv.av_len))) > - uintptr(outendPtr)) || uintptr(incBytePtr(outputPtr, 1+4+int(bv.av_len))) > - uintptr(outendPtr) { - return nil - } - if bv.av_len < 65536 { - *(*byte)(outputPtr) = AMF_STRING - outputPtr = incBytePtr(outputPtr, 1) - // TODO: port AMF_EncodeInt16 - outputPtr = unsafe.Pointer(C_AMF_EncodeInt16((*byte)(outputPtr), (*byte)( - outendPtr), int16(bv.av_len))) - //outputPtr = unsafe.Pointer(C_AMF_EncodeInt16((*byte)(outputPtr), - //(*byte)(outendPtr), (int16)(bv.av_len))) - } else { - *(*byte)(outputPtr) = AMF_LONG_STRING - outputPtr = incBytePtr(outputPtr, 1) - outputPtr = unsafe.Pointer(C_AMF_EncodeInt32((*byte)(outputPtr), (*byte)( - outendPtr), int32(bv.av_len))) - //outputPtr = unsafe.Pointer(C_AMF_EncodeInt32((*byte)(outputPtr), - //(*byte)(outendPtr), (int32)(bv.av_len))) - } - memmove(unsafe.Pointer(outputPtr), unsafe.Pointer(bv.av_val), uintptr(bv.av_len)) - //C.memcpy(unsafe.Pointer(outputPtr), unsafe.Pointer(bv.av_val), (C.size_t)(bv.av_len)) - outputPtr = incBytePtr(outputPtr, int(bv.av_len)) - return (*byte)(outputPtr) -} - -// char* AMF_EncodeInt16(char* output, char* outend, short nVal); -// amf.c +138 -func C_AMF_EncodeInt16(output *byte, outend *byte, nVal int16) *byte { - outputPtr := unsafe.Pointer(output) - outendPtr := unsafe.Pointer(outend) - if uintptr(outputPtr)+2 > uintptr(outendPtr) { - // length < 2 - return nil - } - // Assign output[1] - second := (*byte)(incBytePtr(outputPtr, 1)) - *second = (byte)(nVal & 0xff) - // Assign output[0] - *output = (byte)(nVal >> 8) - return (*byte)(incBytePtr(outputPtr, 2)) -} - -// char* AMF_EncodeInt32(char* output, char* outend, int nVal); -// amf.c +161 -func C_AMF_EncodeInt32(output *byte, outend *byte, nVal int32) *byte { - outputPtr := unsafe.Pointer(output) - outendPtr := unsafe.Pointer(outend) - if uintptr(outputPtr)+4 > uintptr(outendPtr) { - // length < 4 - return nil - } - // Assign output[3] - forth := (*byte)(incBytePtr(outputPtr, 3)) - *forth = (byte)(nVal & 0xff) - // Assign output[2] - third := (*byte)(incBytePtr(outputPtr, 2)) - *third = (byte)(nVal >> 8) - // Assign output[1] - second := (*byte)(incBytePtr(outputPtr, 1)) - *second = (byte)(nVal >> 16) - // Assign output[0] - *output = (byte)(nVal >> 24) - return (*byte)(incBytePtr(outputPtr, 4)) -} - -// void AMF3CD_AddProp(AMF3ClassDef *cd, AVal *prop); -// amf.c +1298 -// TODO test -/* -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++ -} -*/ - /* func realloc(ptr unsafe.Pointer, newSize uint32) unsafe.Pointer { return unsafe.Pointer(crt.Xrealloc(crt.TLS(uintptr(unsafe.Pointer(nil))), @@ -4094,19 +2440,6 @@ func ptrToSlice(data unsafe.Pointer, size int) []byte { return ret } -// C_AVal is in amf.h -// See AVC(str) {str, sizeof(str)-1} in amf.h -func AVC(str string) C_AVal { - var aval C_AVal - if len(str) != 0 { - aval.av_val = &([]byte(str)[0]) - } else { - aval.av_val = nil - } - aval.av_len = int32(len(str)) - return aval -} - var rtmpErrs = [...]string{ 1: "rtmp: not connected", 2: "rtmp: write error", diff --git a/rtmp/rtmp_headers.go b/rtmp/rtmp_headers.go new file mode 100644 index 00000000..a45afd69 --- /dev/null +++ b/rtmp/rtmp_headers.go @@ -0,0 +1,256 @@ +/* +NAME + rtmp_headers.go + +DESCRIPTION + See Readme.md + +AUTHORS + Saxon Nelson-Milton + +LICENSE + rtmp_headers.go is Copyright (C) 2017 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 + Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + It 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 revid in gpl.txt. If not, see http://www.gnu.org/licenses. + + Derived from librtmp under the GNU Lesser General Public License 2.1 + Copyright (C) 2005-2008 Team XBMC http://www.xbmc.org + Copyright (C) 2008-2009 Andrej Stepanchuk + Copyright (C) 2009-2010 Howard Chu +*/ +package rtmp + +const ( + RTMPT_OPEN = iota + RTMPT_SEND + RTMPT_IDLE + RTMPT_CLOSE +) + +const ( + RTMP_PACKET_TYPE_CHUNK_SIZE = 0x01 + RTMP_PACKET_TYPE_BYTES_READ_REPORT = 0x03 + RTMP_PACKET_TYPE_CONTROL = 0x04 + RTMP_PACKET_TYPE_SERVER_BW = 0x05 + RTMP_PACKET_TYPE_CLIENT_BW = 0x06 + RTMP_PACKET_TYPE_AUDIO = 0x08 + RTMP_PACKET_TYPE_VIDEO = 0x09 + RTMP_PACKET_TYPE_FLEX_STREAM_SEND = 0x0F + RTMP_PACKET_TYPE_FLEX_SHARED_OBJECT = 0x10 + RTMP_PACKET_TYPE_FLEX_MESSAGE = 0x11 + RTMP_PACKET_TYPE_INFO = 0x12 + RTMP_PACKET_TYPE_INVOKE = 0x14 + RTMP_PACKET_TYPE_FLASH_VIDEO = 0x16 +) + +const ( + RTMP_PACKET_SIZE_LARGE = 0 + RTMP_PACKET_SIZE_MEDIUM = 1 + RTMP_PACKET_SIZE_SMALL = 2 + RTMP_PACKET_SIZE_MINIMUM = 3 +) +const ( + RTMP_READ_HEADER = 0x01 + RTMP_READ_RESUME = 0x02 + RTMP_READ_NO_IGNORE = 0x04 + RTMP_READ_GOTKF = 0x08 + RTMP_READ_GOTFLVK = 0x10 + RTMP_READ_SEEKING = 0x20 + RTMP_READ_COMPLETE = -3 + RTMP_READ_ERROR = -2 + RTMP_READ_EOF = -1 + RTMP_READ_IGNORE = 0 +) + +const ( + RTMP_LF_AUTH = 0x0001 /* using auth param */ + RTMP_LF_LIVE = 0x0002 /* stream is live */ + RTMP_LF_SWFV = 0x0004 /* do SWF verification */ + RTMP_LF_PLST = 0x0008 /* send playlist before play */ + RTMP_LF_BUFX = 0x0010 /* toggle stream on BufferEmpty msg */ + RTMP_LF_FTCU = 0x0020 /* free tcUrl on close */ + RTMP_LF_FAPU = 0x0040 /* free app on close */ +) + +const ( + RTMP_FEATURE_HTTP = 0x01 + RTMP_FEATURE_ENC = 0x02 + RTMP_FEATURE_SSL = 0x04 + RTMP_FEATURE_MFP = 0x08 /* not yet supported */ + RTMP_FEATURE_WRITE = 0x10 /* publish, not play */ + RTMP_FEATURE_HTTP2 = 0x20 /* server-side rtmpt */ +) + +const ( + RTMP_PROTOCOL_RTMP = 0 + RTMP_PROTOCOL_RTMPE = RTMP_FEATURE_ENC + RTMP_PROTOCOL_RTMPT = RTMP_FEATURE_HTTP + RTMP_PROTOCOL_RTMPS = RTMP_FEATURE_SSL + RTMP_PROTOCOL_RTMPTE = (RTMP_FEATURE_HTTP | RTMP_FEATURE_ENC) + RTMP_PROTOCOL_RTMPTS = (RTMP_FEATURE_HTTP | RTMP_FEATURE_SSL) + RTMP_PROTOCOL_RTMFP = RTMP_FEATURE_MFP +) + +const ( + RTMP_DEFAULT_CHUNKSIZE = 128 + RTMP_BUFFER_CACHE_SIZE = (16 * 1024) + RTMP_SIG_SIZE = 1536 + RTMP_LARGE_HEADER_SIZE = 12 + RTMP_MAX_HEADER_SIZE = 18 +) + +// typedef struct RTMPChunk +// rtmp.h +105 +type C_RTMPChunk struct { + c_headerSize int32 + c_chunkSize int32 + c_chunk *byte + c_header [RTMP_MAX_HEADER_SIZE]byte +} + +// typedef struct RTMPPacket +// rtmp.h +113 +type C_RTMPPacket struct { + m_headerType uint8 + m_packetType uint8 + m_hasAbsTimestamp uint8 + m_nChannel int32 + m_nTimeStamp uint32 + m_nInfoField2 int32 + m_nBodySize uint32 + m_nBytesRead uint32 + m_chunk *C_RTMPChunk + m_body *byte +} + +// typedef struct RTMPSockBuf +// rtmp.h +127 +type C_RTMPSockBuf struct { + sb_socket int32 + sb_size int32 + sb_start *byte + sb_buf [RTMP_BUFFER_CACHE_SIZE]byte // port const + sb_timedout int32 + sb_ssl uintptr +} + +// RTMPPacket_IsReady(a) +// rtmp.h +142 +func C_RTMPPacket_IsReady(p *C_RTMPPacket) int { + if p.m_nBytesRead == p.m_nBodySize { + return 1 + } + return 0 +} + +// typedef struct RTMP_LNK +// rtmp.h +144 +type C_RTMP_LNK struct { + hostname C_AVal + sockshost C_AVal + playpath0 C_AVal + playpath C_AVal + tcUrl C_AVal + swfUrl C_AVal + pageUrl C_AVal + app C_AVal + auth C_AVal + flashVer C_AVal + subscribepath C_AVal + usherToken C_AVal + token C_AVal + pubUser C_AVal + pubPasswd C_AVal + extras C_AMFObject + edepth int32 + seekTime int32 + stopTime int32 + lFlags int32 + swfAge int32 + protocol int32 + timeout int32 + pFlags int32 + socksport uint16 + port uint16 +} + +// typedef struct RTMP_READ +// rtmp.h +200 +type C_RTMP_READ struct { + buf *byte + bufpos *byte + buflen uint + timestamp uint32 + dataType uint8 + flags uint8 + status int8 + initialFrameType uint8 + nResumeTS uint32 + metaHeader *byte + initialFrame *byte + nMetaHeaderSize uint32 + nInitialFrameSize uint32 + nIgnoredFrameCounter uint32 + nIgnoredFlvFrameCounter uint32 +} + +// typedef struct RTMPMethod +// rtmp.h +231 +type C_RTMP_METHOD struct { + name C_AVal + num int32 +} + +// typedef struct RTMP +// rtmp.h +237 +type C_RTMP struct { + m_inChunkSize int32 + m_outChunkSize int32 + m_nBWCheckCounter int32 + m_nBytesIn int32 + m_nBytesInSent int32 + m_nBufferMS int32 + m_stream_id int32 + m_mediaChannel int32 + m_mediaStamp uint32 + m_pauseStamp uint32 + m_pausing int32 + m_nServerBW int32 + m_nClientBW int32 + m_nClientBW2 uint8 + m_bPlaying uint8 + m_bSendEncoding uint8 + m_bSendCounter uint8 + m_numInvokes int32 + m_numCalls int32 + m_methodCalls *C_RTMP_METHOD + m_channelsAllocatedIn int32 + m_channelsAllocatedOut int32 + m_vecChannelsIn **C_RTMPPacket + m_vecChannelsOut **C_RTMPPacket + m_channelTimestamp *int32 + m_fAudioCodecs float64 + m_fVideoCodecs float64 + m_fEncoding float64 + m_fDuration float64 + m_msgCounter int32 + m_polling int32 + m_resplen int32 + m_unackd int32 + m_clientID C_AVal + m_read C_RTMP_READ + m_write C_RTMPPacket + m_sb C_RTMPSockBuf + Link C_RTMP_LNK +} diff --git a/rtmp/rtmp_sys.go b/rtmp/rtmp_sys.go new file mode 100644 index 00000000..13fdc361 --- /dev/null +++ b/rtmp/rtmp_sys.go @@ -0,0 +1,7 @@ +package rtmp + +// #define SET_RCVTIMEO(tv,s) int tv = s*1000 +// rtmp_sys.h +43 +func SET_RCVTIMEO(tv *int32, s int32) { + *tv = s * 1000 +} diff --git a/rtmp/session.go b/rtmp/session.go new file mode 100644 index 00000000..d06037e4 --- /dev/null +++ b/rtmp/session.go @@ -0,0 +1,97 @@ +/* +NAME + session.go + +DESCRIPTION + See Readme.md + +AUTHORS + Saxon Nelson-Milton + Dan Kortschak + +LICENSE + session.go is Copyright (C) 2017 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 + Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + It 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 revid in gpl.txt. If not, see http://www.gnu.org/licenses. + + Derived from librtmp under the GNU Lesser General Public License 2.1 + Copyright (C) 2005-2008 Team XBMC http://www.xbmc.org + Copyright (C) 2008-2009 Andrej Stepanchuk + Copyright (C) 2009-2010 Howard Chu +*/ +package rtmp + +import ( + "errors" +) + +// session provides parameters required for an rtmp communication session. +type Session struct { + rtmp *C_RTMP + url string + timeout uint +} + +// NewSession returns a new session. +func NewSession(url string, connectTimeout uint) *Session { + return &Session{ + url: url, + timeout: connectTimeout, + } +} + +// Open establishes an rtmp connection with the url passed into the +// constructor +func (s *Session) Open() error { + if s.rtmp != nil { + return errors.New("rtmp: attempt to start already running session") + } + var err error + s.rtmp, err = startSession(s.rtmp, s.url, uint32(s.timeout)) + if s.rtmp == nil { + return err + } + return nil +} + +// Close terminates the rtmp connection +func (s *Session) Close() error { + if s.rtmp == nil { + return Err(3) + } + ret := endSession(s.rtmp) + s.rtmp = nil + if ret != 0 { + return Err(ret) + } + return nil +} + +// Write writes a frame (flv tag) to the rtmp connection +func (s *Session) Write(data []byte) (int, error) { + if s.rtmp == nil { + return 0, Err(3) + } + + if C_RTMP_IsConnected(s.rtmp) == 0 { + //if C.RTMP_IsConnected(s.rtmp) == 0 { + return 0, Err(1) + } + + if C_RTMP_Write(s.rtmp, data) == 0 { + //if C.RTMP_Write(s.rtmp, (*byte)(unsafe.Pointer(&data[0])), int32(len(data))) == 0 { + return 0, Err(2) + } + return len(data), nil +}