Merged in rtmp-structure-improvements (pull request #48)

rtmp: structure improvements

Approved-by: kortschak <dan@kortschak.io>
This commit is contained in:
Saxon Milton 2018-08-30 11:06:44 +00:00 committed by kortschak
commit ddefd198d0
9 changed files with 2584 additions and 2661 deletions

16
rtmp/NOT_PORTED.txt Normal file
View File

@ -0,0 +1,16 @@
HandleAudio
HandleVideo
HandleMetadata
DecodeTEA
SendSecureTokenResponse
RTMP_FindFirstMatchingProperty
RTMP_SendServerBW
RTMP_SendCtrl
SendUsherToken
SendFCSubscribe
SendPlaylist
SendPlay
SendPlaylist
SendPong
SendCheckBWResult
RTMP_SendPause

848
rtmp/amf.go Normal file
View File

@ -0,0 +1,848 @@
/*
NAME
amf.go
DESCRIPTION
See Readme.md
AUTHORS
Saxon Nelson-Milton <saxon@ausocean.org>
Dan Kortschak <dan@ausocean.org>
Jake Lane <jake@ausocean.org>
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 <stdlib.h>
*/
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++
}
*/

117
rtmp/amf_headers.go Normal file
View File

@ -0,0 +1,117 @@
/*
NAME
amf_headers.go
DESCRIPTION
See Readme.md
AUTHORS
Saxon Nelson-Milton <saxon@ausocean.org>
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
}

335
rtmp/parseurl.go Normal file
View File

@ -0,0 +1,335 @@
/*
NAME
parseurl.go
DESCRIPTION
See Readme.md
AUTHOR
Saxon Nelson-Milton <saxon@ausocean.org>
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)))
}

View File

@ -1,86 +0,0 @@
/*
NAME
rtmp.c
DESCRIPTION
See Readme.md
AUTHOR
Saxon Nelson-Milton <saxon@ausocean.org>
Dan Kortschak <dan@ausocean.org>
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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <rtmp_sys.h>
#include <log.h>
#include <rtmp.h>
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;
}

File diff suppressed because it is too large Load Diff

256
rtmp/rtmp_headers.go Normal file
View File

@ -0,0 +1,256 @@
/*
NAME
rtmp_headers.go
DESCRIPTION
See Readme.md
AUTHORS
Saxon Nelson-Milton <saxon@ausocean.org>
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
}

7
rtmp/rtmp_sys.go Normal file
View File

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

97
rtmp/session.go Normal file
View File

@ -0,0 +1,97 @@
/*
NAME
session.go
DESCRIPTION
See Readme.md
AUTHORS
Saxon Nelson-Milton <saxon@ausocean.org>
Dan Kortschak <dan@ausocean.org>
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
}