golanggohlsrtmpwebrtcmedia-serverobs-studiortcprtmp-proxyrtmp-serverrtprtsprtsp-proxyrtsp-relayrtsp-serversrtstreamingwebrtc-proxy
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
189 lines
3.3 KiB
189 lines
3.3 KiB
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") |
|
|
|
// Unmarshal decodes AMF0 data. |
|
func Unmarshal(buf []byte) ([]interface{}, error) { |
|
var out []interface{} |
|
|
|
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 |
|
|
|
default: |
|
return nil, nil, fmt.Errorf("unsupported marker 0x%.2x", marker) |
|
} |
|
}
|
|
|