mirror of
https://github.com/aler9/rtsp-simple-server
synced 2025-11-02 11:54:10 +08:00
* add amf0 strict array decode * add amf0 long string decode * support amf0 types undefined, unsupported, xmldocument and date * fix error and add tests * modify tests to follow original way to test * do not skip markerUnsupported / markerUndefined * implement StrictArray marshaling; remove handling of long strings, XML documents, date --------- Co-authored-by: aler9 <46489434+aler9@users.noreply.github.com>
201 lines
3.0 KiB
Go
201 lines
3.0 KiB
Go
// Package amf0 contains an AMF0 marshaler and unmarshaler.
|
|
package amf0
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
)
|
|
|
|
// Marshal encodes AMF0 data.
|
|
func Marshal(data []interface{}) ([]byte, error) {
|
|
n, err := marshalSize(data)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
buf := make([]byte, n)
|
|
n = 0
|
|
|
|
for _, item := range data {
|
|
n += marshalItem(item, buf[n:])
|
|
}
|
|
|
|
return buf, nil
|
|
}
|
|
|
|
func marshalSize(data []interface{}) (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
|
|
}
|
|
}
|