mirror of
https://github.com/aler9/rtsp-simple-server
synced 2025-11-01 11:32:42 +08:00
424 lines
7.1 KiB
Go
424 lines
7.1 KiB
Go
// Package amf0 contains an AMF0 decoder and encoder.
|
|
package amf0
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"math"
|
|
)
|
|
|
|
const (
|
|
markerNumber = 0x00
|
|
markerBoolean = 0x01
|
|
markerString = 0x02
|
|
markerObject = 0x03
|
|
markerMovieclip = 0x04
|
|
markerNull = 0x05
|
|
markerUndefined = 0x06
|
|
markerReference = 0x07
|
|
markerECMAArray = 0x08
|
|
markerObjectEnd = 0x09
|
|
markerStrictArray = 0x0A
|
|
markerDate = 0x0B
|
|
markerLongString = 0x0C
|
|
markerUnsupported = 0x0D
|
|
markerRecordset = 0x0E
|
|
markerXMLDocument = 0xF
|
|
markerTypedObject = 0x10
|
|
)
|
|
|
|
var errBufferTooShort = errors.New("buffer is too short")
|
|
|
|
// StrictArray is an AMF0 Strict Array.
|
|
type StrictArray []interface{}
|
|
|
|
// Data is a list of ActionScript object graphs.
|
|
type Data []interface{}
|
|
|
|
// Unmarshal decodes AMF0 data.
|
|
func Unmarshal(buf []byte) (Data, error) {
|
|
var out Data
|
|
|
|
for len(buf) != 0 {
|
|
var item interface{}
|
|
var err error
|
|
item, buf, err = unmarshal(buf)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
out = append(out, item)
|
|
}
|
|
|
|
return out, nil
|
|
}
|
|
|
|
func unmarshal(buf []byte) (interface{}, []byte, error) {
|
|
if len(buf) < 1 {
|
|
return nil, nil, errBufferTooShort
|
|
}
|
|
|
|
var marker byte
|
|
marker, buf = buf[0], buf[1:]
|
|
|
|
switch marker {
|
|
case markerNumber:
|
|
if len(buf) < 8 {
|
|
return nil, nil, errBufferTooShort
|
|
}
|
|
|
|
return math.Float64frombits(uint64(buf[0])<<56 | uint64(buf[1])<<48 | uint64(buf[2])<<40 | uint64(buf[3])<<32 |
|
|
uint64(buf[4])<<24 | uint64(buf[5])<<16 | uint64(buf[6])<<8 | uint64(buf[7])), buf[8:], nil
|
|
|
|
case markerBoolean:
|
|
if len(buf) < 1 {
|
|
return nil, nil, errBufferTooShort
|
|
}
|
|
|
|
return (buf[0] != 0), buf[1:], nil
|
|
|
|
case markerString:
|
|
if len(buf) < 2 {
|
|
return nil, nil, errBufferTooShort
|
|
}
|
|
|
|
le := uint16(buf[0])<<8 | uint16(buf[1])
|
|
buf = buf[2:]
|
|
|
|
if len(buf) < int(le) {
|
|
return nil, nil, errBufferTooShort
|
|
}
|
|
|
|
return string(buf[:le]), buf[le:], nil
|
|
|
|
case markerECMAArray:
|
|
if len(buf) < 4 {
|
|
return nil, nil, errBufferTooShort
|
|
}
|
|
|
|
buf = buf[4:]
|
|
|
|
out := ECMAArray{}
|
|
|
|
for {
|
|
if len(buf) < 2 {
|
|
return nil, nil, errBufferTooShort
|
|
}
|
|
|
|
keyLen := uint16(buf[0])<<8 | uint16(buf[1])
|
|
buf = buf[2:]
|
|
|
|
if keyLen == 0 {
|
|
break
|
|
}
|
|
|
|
if len(buf) < int(keyLen) {
|
|
return nil, nil, errBufferTooShort
|
|
}
|
|
|
|
key := string(buf[:keyLen])
|
|
buf = buf[keyLen:]
|
|
|
|
var value interface{}
|
|
var err error
|
|
value, buf, err = unmarshal(buf)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
out = append(out, ObjectEntry{
|
|
Key: key,
|
|
Value: value,
|
|
})
|
|
}
|
|
|
|
if len(buf) < 1 {
|
|
return nil, nil, errBufferTooShort
|
|
}
|
|
|
|
if buf[0] != markerObjectEnd {
|
|
return nil, nil, fmt.Errorf("object end not found")
|
|
}
|
|
|
|
return out, buf[1:], nil
|
|
|
|
case markerObject:
|
|
out := Object{}
|
|
|
|
for {
|
|
if len(buf) < 2 {
|
|
return nil, nil, errBufferTooShort
|
|
}
|
|
|
|
keyLen := uint16(buf[0])<<8 | uint16(buf[1])
|
|
buf = buf[2:]
|
|
|
|
if keyLen == 0 {
|
|
break
|
|
}
|
|
|
|
if len(buf) < int(keyLen) {
|
|
return nil, nil, errBufferTooShort
|
|
}
|
|
|
|
key := string(buf[:keyLen])
|
|
buf = buf[keyLen:]
|
|
|
|
var value interface{}
|
|
var err error
|
|
value, buf, err = unmarshal(buf)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
out = append(out, ObjectEntry{
|
|
Key: key,
|
|
Value: value,
|
|
})
|
|
}
|
|
|
|
if len(buf) < 1 {
|
|
return nil, nil, errBufferTooShort
|
|
}
|
|
|
|
if buf[0] != markerObjectEnd {
|
|
return nil, nil, fmt.Errorf("object end not found")
|
|
}
|
|
|
|
return out, buf[1:], nil
|
|
|
|
case markerNull:
|
|
return nil, buf, nil
|
|
|
|
case markerStrictArray:
|
|
if len(buf) < 4 {
|
|
return nil, nil, errBufferTooShort
|
|
}
|
|
|
|
arrayCount := uint32(buf[0])<<24 | uint32(buf[1])<<16 | uint32(buf[2])<<8 | uint32(buf[3])
|
|
buf = buf[4:]
|
|
|
|
out := StrictArray{}
|
|
|
|
for i := 0; i < int(arrayCount); i++ {
|
|
var value interface{}
|
|
var err error
|
|
value, buf, err = unmarshal(buf)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
out = append(out, value)
|
|
}
|
|
|
|
return out, buf, nil
|
|
|
|
default:
|
|
return nil, nil, fmt.Errorf("unsupported marker 0x%.2x", marker)
|
|
}
|
|
}
|
|
|
|
// Marshal encodes AMF0 data.
|
|
func (data Data) Marshal() ([]byte, error) {
|
|
n, err := data.MarshalSize()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
buf := make([]byte, n)
|
|
_, err = data.MarshalTo(buf)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return buf, nil
|
|
}
|
|
|
|
// MarshalTo encodes AMF0 data into an existing buffer.
|
|
func (data Data) MarshalTo(buf []byte) (int, error) {
|
|
n := 0
|
|
|
|
for _, item := range data {
|
|
n += marshalItem(item, buf[n:])
|
|
}
|
|
|
|
return n, nil
|
|
}
|
|
|
|
// MarshalSize returns the size needed to encode data in AMF0.
|
|
func (data Data) MarshalSize() (int, error) {
|
|
n := 0
|
|
|
|
for _, item := range data {
|
|
in, err := marshalSizeItem(item)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
n += in
|
|
}
|
|
|
|
return n, nil
|
|
}
|
|
|
|
func marshalSizeItem(item interface{}) (int, error) {
|
|
switch item := item.(type) {
|
|
case float64:
|
|
return 9, nil
|
|
|
|
case bool:
|
|
return 2, nil
|
|
|
|
case string:
|
|
return 3 + len(item), nil
|
|
|
|
case ECMAArray:
|
|
n := 5
|
|
|
|
for _, entry := range item {
|
|
en, err := marshalSizeItem(entry.Value)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
n += 2 + len(entry.Key) + en
|
|
}
|
|
|
|
n += 3
|
|
|
|
return n, nil
|
|
|
|
case Object:
|
|
n := 1
|
|
|
|
for _, entry := range item {
|
|
en, err := marshalSizeItem(entry.Value)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
n += 2 + len(entry.Key) + en
|
|
}
|
|
|
|
n += 3
|
|
|
|
return n, nil
|
|
|
|
case StrictArray:
|
|
n := 5
|
|
|
|
for _, entry := range item {
|
|
en, err := marshalSizeItem(entry)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
n += en
|
|
}
|
|
|
|
return n, nil
|
|
|
|
case nil:
|
|
return 1, nil
|
|
|
|
default:
|
|
return 0, fmt.Errorf("unsupported data type: %T", item)
|
|
}
|
|
}
|
|
|
|
func marshalItem(item interface{}, buf []byte) int {
|
|
switch item := item.(type) {
|
|
case float64:
|
|
v := math.Float64bits(item)
|
|
buf[0] = markerNumber
|
|
buf[1] = byte(v >> 56)
|
|
buf[2] = byte(v >> 48)
|
|
buf[3] = byte(v >> 40)
|
|
buf[4] = byte(v >> 32)
|
|
buf[5] = byte(v >> 24)
|
|
buf[6] = byte(v >> 16)
|
|
buf[7] = byte(v >> 8)
|
|
buf[8] = byte(v)
|
|
return 9
|
|
|
|
case bool:
|
|
buf[0] = markerBoolean
|
|
if item {
|
|
buf[1] = 1
|
|
}
|
|
return 2
|
|
|
|
case string:
|
|
le := len(item)
|
|
buf[0] = markerString
|
|
buf[1] = byte(le >> 8)
|
|
buf[2] = byte(le)
|
|
copy(buf[3:], item)
|
|
return 3 + le
|
|
|
|
case ECMAArray:
|
|
le := len(item)
|
|
buf[0] = markerECMAArray
|
|
buf[1] = byte(le >> 24)
|
|
buf[2] = byte(le >> 16)
|
|
buf[3] = byte(le >> 8)
|
|
buf[4] = byte(le)
|
|
n := 5
|
|
|
|
for _, entry := range item {
|
|
le = len(entry.Key)
|
|
buf[n] = byte(le >> 8)
|
|
buf[n+1] = byte(le)
|
|
copy(buf[n+2:], entry.Key)
|
|
n += 2 + le
|
|
|
|
n += marshalItem(entry.Value, buf[n:])
|
|
}
|
|
|
|
buf[n] = 0
|
|
buf[n+1] = 0
|
|
buf[n+2] = markerObjectEnd
|
|
|
|
return n + 3
|
|
|
|
case Object:
|
|
buf[0] = markerObject
|
|
n := 1
|
|
|
|
for _, entry := range item {
|
|
le := len(entry.Key)
|
|
buf[n] = byte(le >> 8)
|
|
buf[n+1] = byte(le)
|
|
copy(buf[n+2:], entry.Key)
|
|
n += 2 + le
|
|
|
|
n += marshalItem(entry.Value, buf[n:])
|
|
}
|
|
|
|
buf[n] = 0
|
|
buf[n+1] = 0
|
|
buf[n+2] = markerObjectEnd
|
|
|
|
return n + 3
|
|
|
|
case StrictArray:
|
|
le := len(item)
|
|
buf[0] = markerStrictArray
|
|
buf[1] = byte(le >> 24)
|
|
buf[2] = byte(le >> 16)
|
|
buf[3] = byte(le >> 8)
|
|
buf[4] = byte(le)
|
|
n := 5
|
|
|
|
for _, entry := range item {
|
|
n += marshalItem(entry, buf[n:])
|
|
}
|
|
|
|
return n
|
|
|
|
default:
|
|
buf[0] = markerNull
|
|
return 1
|
|
}
|
|
}
|