56 changed files with 1159 additions and 321 deletions
@ -0,0 +1,171 @@
@@ -0,0 +1,171 @@
|
||||
// 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 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 |
||||
|
||||
default: |
||||
buf[0] = markerNull |
||||
return 1 |
||||
} |
||||
} |
@ -0,0 +1,17 @@
@@ -0,0 +1,17 @@
|
||||
package amf0 |
||||
|
||||
import ( |
||||
"testing" |
||||
|
||||
"github.com/stretchr/testify/require" |
||||
) |
||||
|
||||
func TestMarshal(t *testing.T) { |
||||
for _, ca := range cases { |
||||
t.Run(ca.name, func(t *testing.T) { |
||||
enc, err := Marshal(ca.dec) |
||||
require.NoError(t, err) |
||||
require.Equal(t, ca.enc, enc) |
||||
}) |
||||
} |
||||
} |
@ -0,0 +1,53 @@
@@ -0,0 +1,53 @@
|
||||
package amf0 |
||||
|
||||
// ObjectEntry is an entry of Object.
|
||||
type ObjectEntry struct { |
||||
Key string |
||||
Value interface{} |
||||
} |
||||
|
||||
// Object is an AMF0 object.
|
||||
type Object []ObjectEntry |
||||
|
||||
// ECMAArray is an AMF0 ECMA Array.
|
||||
type ECMAArray Object |
||||
|
||||
// Get returns the value corresponding to key.
|
||||
func (o Object) Get(key string) (interface{}, bool) { |
||||
for _, item := range o { |
||||
if item.Key == key { |
||||
return item.Value, true |
||||
} |
||||
} |
||||
return nil, false |
||||
} |
||||
|
||||
// GetString returns the value corresponding to key, only if that is a string.
|
||||
func (o Object) GetString(key string) (string, bool) { |
||||
v, ok := o.Get(key) |
||||
if !ok { |
||||
return "", false |
||||
} |
||||
|
||||
v2, ok2 := v.(string) |
||||
if !ok2 { |
||||
return "", false |
||||
} |
||||
|
||||
return v2, ok2 |
||||
} |
||||
|
||||
// GetFloat64 returns the value corresponding to key, only if that is a float64.
|
||||
func (o Object) GetFloat64(key string) (float64, bool) { |
||||
v, ok := o.Get(key) |
||||
if !ok { |
||||
return 0, false |
||||
} |
||||
|
||||
v2, ok2 := v.(float64) |
||||
if !ok2 { |
||||
return 0, false |
||||
} |
||||
|
||||
return v2, ok2 |
||||
} |
@ -0,0 +1,28 @@
@@ -0,0 +1,28 @@
|
||||
package amf0 |
||||
|
||||
import ( |
||||
"testing" |
||||
|
||||
"github.com/stretchr/testify/require" |
||||
) |
||||
|
||||
func TestObjectGet(t *testing.T) { |
||||
o := Object{{Key: "testme", Value: "ok"}} |
||||
v, ok := o.Get("testme") |
||||
require.Equal(t, true, ok) |
||||
require.Equal(t, "ok", v) |
||||
} |
||||
|
||||
func TestObjectGetString(t *testing.T) { |
||||
o := Object{{Key: "testme", Value: "ok"}} |
||||
v, ok := o.GetString("testme") |
||||
require.Equal(t, true, ok) |
||||
require.Equal(t, "ok", v) |
||||
} |
||||
|
||||
func TestObjectGetFloat64(t *testing.T) { |
||||
o := Object{{Key: "testme", Value: float64(123)}} |
||||
v, ok := o.GetFloat64("testme") |
||||
require.Equal(t, true, ok) |
||||
require.Equal(t, float64(123), v) |
||||
} |
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1 |
||||
[]byte("\x0200") |
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1 |
||||
[]byte("\b000000") |
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1 |
||||
[]byte("\b0000\x00\x000") |
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1 |
||||
[]byte("\x000") |
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1 |
||||
[]byte("0") |
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1 |
||||
[]byte("\x03\x00\x0200") |
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1 |
||||
[]byte("\b") |
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1 |
||||
[]byte("\x0300") |
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1 |
||||
[]byte("\x01") |
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1 |
||||
[]byte("\x03\x00\x000") |
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1 |
||||
[]byte("\b0000\x00\x00") |
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1 |
||||
[]byte("\b0000") |
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1 |
||||
[]byte("\b0000\x00\x06000000") |
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1 |
||||
[]byte("\x03") |
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1 |
||||
[]byte("\x03\x00\x00") |
@ -0,0 +1,189 @@
@@ -0,0 +1,189 @@
|
||||
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) |
||||
} |
||||
} |
@ -0,0 +1,313 @@
@@ -0,0 +1,313 @@
|
||||
package amf0 |
||||
|
||||
import ( |
||||
"testing" |
||||
|
||||
"github.com/stretchr/testify/require" |
||||
) |
||||
|
||||
var cases = []struct { |
||||
name string |
||||
enc []byte |
||||
dec []interface{} |
||||
}{ |
||||
{ |
||||
"on metadata", |
||||
[]byte{ |
||||
0x02, 0x00, 0x0d, 0x40, 0x73, 0x65, 0x74, 0x44, |
||||
0x61, 0x74, 0x61, 0x46, 0x72, 0x61, 0x6d, 0x65, |
||||
0x02, 0x00, 0x0a, 0x6f, 0x6e, 0x4d, 0x65, 0x74, |
||||
0x61, 0x44, 0x61, 0x74, 0x61, 0x08, 0x00, 0x00, |
||||
0x00, 0x0d, 0x00, 0x08, 0x64, 0x75, 0x72, 0x61, |
||||
0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x00, |
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x77, |
||||
0x69, 0x64, 0x74, 0x68, 0x00, 0x40, 0x94, 0x00, |
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x68, |
||||
0x65, 0x69, 0x67, 0x68, 0x74, 0x00, 0x40, 0x86, |
||||
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, |
||||
0x76, 0x69, 0x64, 0x65, 0x6f, 0x64, 0x61, 0x74, |
||||
0x61, 0x72, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, |
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, |
||||
0x66, 0x72, 0x61, 0x6d, 0x65, 0x72, 0x61, 0x74, |
||||
0x65, 0x00, 0x40, 0x4d, 0xf8, 0x53, 0xe2, 0x55, |
||||
0x6b, 0x28, 0x00, 0x0c, 0x76, 0x69, 0x64, 0x65, |
||||
0x6f, 0x63, 0x6f, 0x64, 0x65, 0x63, 0x69, 0x64, |
||||
0x00, 0x40, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, |
||||
0x00, 0x00, 0x0d, 0x61, 0x75, 0x64, 0x69, 0x6f, |
||||
0x64, 0x61, 0x74, 0x61, 0x72, 0x61, 0x74, 0x65, |
||||
0x00, 0x40, 0x57, 0x58, 0x90, 0x00, 0x00, 0x00, |
||||
0x00, 0x00, 0x0f, 0x61, 0x75, 0x64, 0x69, 0x6f, |
||||
0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x61, |
||||
0x74, 0x65, 0x00, 0x40, 0xe7, 0x70, 0x00, 0x00, |
||||
0x00, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x75, 0x64, |
||||
0x69, 0x6f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, |
||||
0x73, 0x69, 0x7a, 0x65, 0x00, 0x40, 0x30, 0x00, |
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x73, |
||||
0x74, 0x65, 0x72, 0x65, 0x6f, 0x01, 0x01, 0x00, |
||||
0x0c, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x63, 0x6f, |
||||
0x64, 0x65, 0x63, 0x69, 0x64, 0x00, 0x40, 0x24, |
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, |
||||
0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x02, |
||||
0x00, 0x0d, 0x4c, 0x61, 0x76, 0x66, 0x35, 0x36, |
||||
0x2e, 0x33, 0x36, 0x2e, 0x31, 0x30, 0x30, 0x00, |
||||
0x08, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x69, 0x7a, |
||||
0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
||||
0x00, 0x00, 0x00, 0x00, 0x09, |
||||
}, |
||||
[]interface{}{ |
||||
"@setDataFrame", |
||||
"onMetaData", |
||||
ECMAArray{ |
||||
{ |
||||
Key: "duration", |
||||
Value: float64(0), |
||||
}, |
||||
{ |
||||
Key: "width", |
||||
Value: float64(1280), |
||||
}, |
||||
{ |
||||
Key: "height", |
||||
Value: float64(720), |
||||
}, |
||||
{ |
||||
Key: "videodatarate", |
||||
Value: float64(0), |
||||
}, |
||||
{ |
||||
Key: "framerate", |
||||
Value: float64(59.94005994005994), |
||||
}, |
||||
{ |
||||
Key: "videocodecid", |
||||
Value: float64(7), |
||||
}, |
||||
{ |
||||
Key: "audiodatarate", |
||||
Value: float64(93.3837890625), |
||||
}, |
||||
{ |
||||
Key: "audiosamplerate", |
||||
Value: float64(48000), |
||||
}, |
||||
{ |
||||
Key: "audiosamplesize", |
||||
Value: float64(16), |
||||
}, |
||||
{ |
||||
Key: "stereo", |
||||
Value: true, |
||||
}, |
||||
{ |
||||
Key: "audiocodecid", |
||||
Value: float64(10), |
||||
}, |
||||
{ |
||||
Key: "encoder", |
||||
Value: "Lavf56.36.100", |
||||
}, |
||||
{ |
||||
Key: "filesize", |
||||
Value: float64(0), |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
{ |
||||
"connect", |
||||
[]byte{ |
||||
0x02, 0x00, 0x07, 0x63, 0x6f, 0x6e, 0x6e, 0x65, |
||||
0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, |
||||
0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x61, 0x70, |
||||
0x70, 0x02, 0x00, 0x02, 0x61, 0x70, 0x00, 0x04, |
||||
0x74, 0x79, 0x70, 0x65, 0x02, 0x00, 0x0a, 0x6e, |
||||
0x6f, 0x6e, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, |
||||
0x65, 0x00, 0x08, 0x66, 0x6c, 0x61, 0x73, 0x68, |
||||
0x56, 0x65, 0x72, 0x02, 0x00, 0x24, 0x46, 0x4d, |
||||
0x4c, 0x45, 0x2f, 0x33, 0x2e, 0x30, 0x20, 0x28, |
||||
0x63, 0x6f, 0x6d, 0x70, 0x61, 0x74, 0x69, 0x62, |
||||
0x6c, 0x65, 0x3b, 0x20, 0x4c, 0x61, 0x76, 0x66, |
||||
0x35, 0x36, 0x2e, 0x31, 0x35, 0x2e, 0x31, 0x30, |
||||
0x32, 0x29, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, |
||||
0x6c, 0x02, 0x00, 0x1c, 0x72, 0x74, 0x6d, 0x70, |
||||
0x3a, 0x2f, 0x2f, 0x31, 0x39, 0x32, 0x2e, 0x31, |
||||
0x36, 0x38, 0x2e, 0x31, 0x2e, 0x32, 0x33, 0x33, |
||||
0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x61, 0x70, |
||||
0x00, 0x00, 0x09, |
||||
}, |
||||
[]interface{}{ |
||||
"connect", |
||||
float64(1), |
||||
Object{ |
||||
{Key: "app", Value: "ap"}, |
||||
{Key: "type", Value: "nonprivate"}, |
||||
{Key: "flashVer", Value: "FMLE/3.0 (compatible; Lavf56.15.102)"}, |
||||
{Key: "tcUrl", Value: "rtmp://192.168.1.233:1935/ap"}, |
||||
}, |
||||
}, |
||||
}, |
||||
{ |
||||
"srs", |
||||
[]byte{ |
||||
0x02, 0x00, 0x07, 0x5f, 0x72, 0x65, 0x73, 0x75, |
||||
0x6c, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, |
||||
0x00, 0x00, 0x00, 0x03, 0x00, 0x06, 0x66, 0x6d, |
||||
0x73, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x46, |
||||
0x4d, 0x53, 0x2f, 0x33, 0x2c, 0x35, 0x2c, 0x33, |
||||
0x2c, 0x38, 0x38, 0x38, 0x00, 0x0c, 0x63, 0x61, |
||||
0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, |
||||
0x65, 0x73, 0x00, 0x40, 0x5f, 0xc0, 0x00, 0x00, |
||||
0x00, 0x00, 0x00, 0x00, 0x04, 0x6d, 0x6f, 0x64, |
||||
0x65, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, |
||||
0x00, 0x00, 0x00, 0x00, 0x09, 0x03, 0x00, 0x05, |
||||
0x6c, 0x65, 0x76, 0x65, 0x6c, 0x02, 0x00, 0x06, |
||||
0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x00, 0x04, |
||||
0x63, 0x6f, 0x64, 0x65, 0x02, 0x00, 0x1d, 0x4e, |
||||
0x65, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, |
||||
0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, |
||||
0x6e, 0x65, 0x63, 0x74, 0x2e, 0x53, 0x75, 0x63, |
||||
0x63, 0x65, 0x73, 0x73, 0x00, 0x0b, 0x64, 0x65, |
||||
0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, |
||||
0x6e, 0x02, 0x00, 0x14, 0x43, 0x6f, 0x6e, 0x6e, |
||||
0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, |
||||
0x75, 0x63, 0x63, 0x65, 0x65, 0x64, 0x65, 0x64, |
||||
0x00, 0x0e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, |
||||
0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, |
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
||||
0x00, 0x00, 0x04, 0x64, 0x61, 0x74, 0x61, 0x08, |
||||
0x00, 0x00, 0x00, 0x0f, 0x00, 0x07, 0x76, 0x65, |
||||
0x72, 0x73, 0x69, 0x6f, 0x6e, 0x02, 0x00, 0x09, |
||||
0x33, 0x2c, 0x35, 0x2c, 0x33, 0x2c, 0x38, 0x38, |
||||
0x38, 0x00, 0x07, 0x73, 0x72, 0x73, 0x5f, 0x73, |
||||
0x69, 0x67, 0x02, 0x00, 0x03, 0x53, 0x52, 0x53, |
||||
0x00, 0x0a, 0x73, 0x72, 0x73, 0x5f, 0x73, 0x65, |
||||
0x72, 0x76, 0x65, 0x72, 0x02, 0x00, 0x34, 0x53, |
||||
0x52, 0x53, 0x20, 0x31, 0x2e, 0x30, 0x2e, 0x31, |
||||
0x30, 0x20, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, |
||||
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x77, 0x69, |
||||
0x6e, 0x6c, 0x69, 0x6e, 0x76, 0x69, 0x70, 0x2f, |
||||
0x73, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x2d, 0x72, |
||||
0x74, 0x6d, 0x70, 0x2d, 0x73, 0x65, 0x72, 0x76, |
||||
0x65, 0x72, 0x29, 0x00, 0x0b, 0x73, 0x72, 0x73, |
||||
0x5f, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, |
||||
0x02, 0x00, 0x15, 0x54, 0x68, 0x65, 0x20, 0x4d, |
||||
0x49, 0x54, 0x20, 0x4c, 0x69, 0x63, 0x65, 0x6e, |
||||
0x73, 0x65, 0x20, 0x28, 0x4d, 0x49, 0x54, 0x29, |
||||
0x00, 0x08, 0x73, 0x72, 0x73, 0x5f, 0x72, 0x6f, |
||||
0x6c, 0x65, 0x02, 0x00, 0x12, 0x6f, 0x72, 0x69, |
||||
0x67, 0x69, 0x6e, 0x2f, 0x65, 0x64, 0x67, 0x65, |
||||
0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x00, |
||||
0x07, 0x73, 0x72, 0x73, 0x5f, 0x75, 0x72, 0x6c, |
||||
0x02, 0x00, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x73, |
||||
0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, |
||||
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x77, 0x69, |
||||
0x6e, 0x6c, 0x69, 0x6e, 0x76, 0x69, 0x70, 0x2f, |
||||
0x73, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x2d, 0x72, |
||||
0x74, 0x6d, 0x70, 0x2d, 0x73, 0x65, 0x72, 0x76, |
||||
0x65, 0x72, 0x00, 0x0b, 0x73, 0x72, 0x73, 0x5f, |
||||
0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x02, |
||||
0x00, 0x06, 0x31, 0x2e, 0x30, 0x2e, 0x31, 0x30, |
||||
0x00, 0x08, 0x73, 0x72, 0x73, 0x5f, 0x73, 0x69, |
||||
0x74, 0x65, 0x02, 0x00, 0x1c, 0x68, 0x74, 0x74, |
||||
0x70, 0x3a, 0x2f, 0x2f, 0x62, 0x6c, 0x6f, 0x67, |
||||
0x2e, 0x63, 0x73, 0x64, 0x6e, 0x2e, 0x6e, 0x65, |
||||
0x74, 0x2f, 0x77, 0x69, 0x6e, 0x5f, 0x6c, 0x69, |
||||
0x6e, 0x00, 0x09, 0x73, 0x72, 0x73, 0x5f, 0x65, |
||||
0x6d, 0x61, 0x69, 0x6c, 0x02, 0x00, 0x12, 0x77, |
||||
0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x40, 0x76, 0x69, |
||||
0x70, 0x2e, 0x31, 0x32, 0x36, 0x2e, 0x63, 0x6f, |
||||
0x6d, 0x00, 0x0d, 0x73, 0x72, 0x73, 0x5f, 0x63, |
||||
0x6f, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, |
||||
0x02, 0x00, 0x1e, 0x43, 0x6f, 0x70, 0x79, 0x72, |
||||
0x69, 0x67, 0x68, 0x74, 0x20, 0x28, 0x63, 0x29, |
||||
0x20, 0x32, 0x30, 0x31, 0x33, 0x2d, 0x32, 0x30, |
||||
0x31, 0x34, 0x20, 0x77, 0x69, 0x6e, 0x6c, 0x69, |
||||
0x6e, 0x00, 0x0b, 0x73, 0x72, 0x73, 0x5f, 0x70, |
||||
0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x02, 0x00, |
||||
0x06, 0x77, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x00, |
||||
0x0b, 0x73, 0x72, 0x73, 0x5f, 0x61, 0x75, 0x74, |
||||
0x68, 0x6f, 0x72, 0x73, 0x02, 0x00, 0x0b, 0x77, |
||||
0x65, 0x6e, 0x6a, 0x69, 0x65, 0x2e, 0x7a, 0x68, |
||||
0x61, 0x6f, 0x00, 0x0d, 0x73, 0x72, 0x73, 0x5f, |
||||
0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x69, |
||||
0x70, 0x02, 0x00, 0x0b, 0x31, 0x37, 0x32, 0x2e, |
||||
0x31, 0x37, 0x2e, 0x30, 0x2e, 0x31, 0x30, 0x00, |
||||
0x07, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x69, 0x64, |
||||
0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, |
||||
0x00, 0x00, 0x06, 0x73, 0x72, 0x73, 0x5f, 0x69, |
||||
0x64, 0x00, 0x40, 0x5a, 0x40, 0x00, 0x00, 0x00, |
||||
0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x09, |
||||
}, |
||||
[]interface{}{ |
||||
"_result", |
||||
float64(1), |
||||
Object{ |
||||
{Key: "fmsVer", Value: "FMS/3,5,3,888"}, |
||||
{Key: "capabilities", Value: float64(127)}, |
||||
{Key: "mode", Value: float64(1)}, |
||||
}, |
||||
Object{ |
||||
{Key: "level", Value: "status"}, |
||||
{Key: "code", Value: "NetConnection.Connect.Success"}, |
||||
{Key: "description", Value: "Connection succeeded"}, |
||||
{Key: "objectEncoding", Value: float64(0)}, |
||||
{ |
||||
Key: "data", |
||||
Value: ECMAArray{ |
||||
{Key: "version", Value: "3,5,3,888"}, |
||||
{Key: "srs_sig", Value: "SRS"}, |
||||
{Key: "srs_server", Value: "SRS 1.0.10 (github.com/winlinvip/simple-rtmp-server)"}, |
||||
{Key: "srs_license", Value: "The MIT License (MIT)"}, |
||||
{Key: "srs_role", Value: "origin/edge server"}, |
||||
{Key: "srs_url", Value: "https://github.com/winlinvip/simple-rtmp-server"}, |
||||
{Key: "srs_version", Value: "1.0.10"}, |
||||
{Key: "srs_site", Value: "http://blog.csdn.net/win_lin"}, |
||||
{Key: "srs_email", Value: "winlin@vip.126.com"}, |
||||
{Key: "srs_copyright", Value: "Copyright (c) 2013-2014 winlin"}, |
||||
{Key: "srs_primary", Value: "winlin"}, |
||||
{Key: "srs_authors", Value: "wenjie.zhao"}, |
||||
{Key: "srs_server_ip", Value: "172.17.0.10"}, |
||||
{Key: "srs_pid", Value: float64(1)}, |
||||
{Key: "srs_id", Value: float64(105)}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
{ |
||||
"play", |
||||
[]byte{ |
||||
0x02, 0x00, 0x04, 0x70, 0x6c, 0x61, 0x79, 0x00, |
||||
0x40, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
||||
0x05, 0x02, 0x00, 0x01, 0x31, 0x00, 0x00, 0x00, |
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
||||
}, |
||||
[]interface{}{ |
||||
"play", |
||||
float64(3), |
||||
nil, |
||||
"1", |
||||
float64(0), |
||||
}, |
||||
}, |
||||
} |
||||
|
||||
func TestUnmarshal(t *testing.T) { |
||||
for _, ca := range cases { |
||||
t.Run(ca.name, func(t *testing.T) { |
||||
dec, err := Unmarshal(ca.enc) |
||||
require.NoError(t, err) |
||||
require.Equal(t, ca.dec, dec) |
||||
}) |
||||
} |
||||
} |
||||
|
||||
func FuzzUnmarshal(f *testing.F) { |
||||
for _, ca := range cases { |
||||
f.Add(ca.enc) |
||||
} |
||||
|
||||
f.Fuzz(func(t *testing.T, b []byte) { |
||||
Unmarshal(b) //nolint:errcheck
|
||||
}) |
||||
} |
@ -0,0 +1,73 @@
@@ -0,0 +1,73 @@
|
||||
package message |
||||
|
||||
import ( |
||||
"testing" |
||||
|
||||
"github.com/bluenviron/mediamtx/internal/protocols/rtmp/amf0" |
||||
"github.com/bluenviron/mediamtx/internal/protocols/rtmp/rawmessage" |
||||
) |
||||
|
||||
func BenchmarkCommandAMF0Marshal(b *testing.B) { |
||||
msg := &CommandAMF0{ |
||||
ChunkStreamID: 3, |
||||
Name: "connect", |
||||
CommandID: 1, |
||||
Arguments: []interface{}{ |
||||
amf0.Object{ |
||||
{Key: "app", Value: "/stream"}, |
||||
{Key: "flashVer", Value: "LNX 9,0,124,2"}, |
||||
{Key: "tcUrl", Value: "http://example.com"}, |
||||
{Key: "fpad", Value: false}, |
||||
{Key: "capabilities", Value: 15}, |
||||
{Key: "audioCodecs", Value: 4071}, |
||||
{Key: "videoCodecs", Value: 252}, |
||||
{Key: "videoFunction", Value: 1}, |
||||
}, |
||||
}, |
||||
} |
||||
|
||||
for n := 0; n < b.N; n++ { |
||||
msg.marshal() //nolint:errcheck
|
||||
} |
||||
} |
||||
|
||||
func BenchmarkCommandAMF0Unmarshal(b *testing.B) { |
||||
raw := &rawmessage.Message{ |
||||
ChunkStreamID: 0x3, |
||||
Timestamp: 0, |
||||
Type: 0x14, |
||||
MessageStreamID: 0x0, |
||||
Body: []uint8{ |
||||
0x02, 0x00, 0x07, 0x63, 0x6f, 0x6e, 0x6e, 0x65, |
||||
0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, |
||||
0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x61, 0x70, |
||||
0x70, 0x02, 0x00, 0x07, 0x2f, 0x73, 0x74, 0x72, |
||||
0x65, 0x61, 0x6d, 0x00, 0x08, 0x66, 0x6c, 0x61, |
||||
0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, |
||||
0x4c, 0x4e, 0x58, 0x20, 0x39, 0x2c, 0x30, 0x2c, |
||||
0x31, 0x32, 0x34, 0x2c, 0x32, 0x00, 0x05, 0x74, |
||||
0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x12, 0x68, |
||||
0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x65, 0x78, |
||||
0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, |
||||
0x6d, 0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, |
||||
0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, |
||||
0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, |
||||
0x40, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
||||
0x00, 0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, |
||||
0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xaf, |
||||
0xce, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, |
||||
0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, |
||||
0x65, 0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, |
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x76, 0x69, |
||||
0x64, 0x65, 0x6f, 0x46, 0x75, 0x6e, 0x63, 0x74, |
||||
0x69, 0x6f, 0x6e, 0x00, 0x3f, 0xf0, 0x00, 0x00, |
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, |
||||
}, |
||||
} |
||||
|
||||
msg := &CommandAMF0{} |
||||
|
||||
for n := 0; n < b.N; n++ { |
||||
msg.unmarshal(raw) //nolint:errcheck
|
||||
} |
||||
} |
Loading…
Reference in new issue