mirror of
https://github.com/aler9/rtsp-simple-server
synced 2025-10-16 12:40:56 +08:00
rtmp: add new AMF0 encoder and encoder (#3069)
This improves performance, security and removes a dependency.
This commit is contained in:
@@ -1970,6 +1970,7 @@ All the code in this repository is released under the [MIT License](LICENSE). Co
|
||||
|[HLS specifications](https://github.com/bluenviron/gohlslib#specifications)|HLS|
|
||||
|[RTMP](https://rtmp.veriskope.com/pdf/rtmp_specification_1.0.pdf)|RTMP|
|
||||
|[Enhanced RTMP](https://raw.githubusercontent.com/veovera/enhanced-rtmp/main/enhanced-rtmp-v1.pdf)|RTMP|
|
||||
|[Action Message Format](https://rtmp.veriskope.com/pdf/amf0-file-format-specification.pdf)|RTMP|
|
||||
|[WebRTC: Real-Time Communication in Browsers](https://www.w3.org/TR/webrtc/)|WebRTC|
|
||||
|[WebRTC HTTP Ingestion Protocol (WHIP)](https://datatracker.ietf.org/doc/draft-ietf-wish-whip/)|WebRTC|
|
||||
|[WebRTC HTTP Egress Protocol (WHEP)](https://datatracker.ietf.org/doc/draft-murillo-whep/)|WebRTC|
|
||||
@@ -1987,7 +1988,6 @@ All the code in this repository is released under the [MIT License](LICENSE). Co
|
||||
* [pion/sdp (SDP library used internally)](https://github.com/pion/sdp)
|
||||
* [pion/rtp (RTP library used internally)](https://github.com/pion/rtp)
|
||||
* [pion/rtcp (RTCP library used internally)](https://github.com/pion/rtcp)
|
||||
* [notedit/rtmp (RTMP library used internally)](https://github.com/notedit/rtmp)
|
||||
* [go-astits (MPEG-TS library used internally)](https://github.com/asticode/go-astits)
|
||||
* [go-mp4 (MP4 library used internally)](https://github.com/abema/go-mp4)
|
||||
* [hls.js (browser-side HLS library used internally)](https://github.com/video-dev/hls.js)
|
||||
|
1
go.mod
1
go.mod
@@ -17,7 +17,6 @@ require (
|
||||
github.com/gorilla/websocket v1.5.1
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||
github.com/matthewhartstonge/argon2 v1.0.0
|
||||
github.com/notedit/rtmp v0.0.2
|
||||
github.com/pion/ice/v2 v2.3.11
|
||||
github.com/pion/interceptor v0.1.25
|
||||
github.com/pion/logging v0.2.2
|
||||
|
2
go.sum
2
go.sum
@@ -107,8 +107,6 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/notedit/rtmp v0.0.2 h1:5+to4yezKATiJgnrcETu9LbV5G/QsWkOV9Ts2M/p33w=
|
||||
github.com/notedit/rtmp v0.0.2/go.mod h1:vzuE21rowz+lT1NGsWbreIvYulgBpCGnQyeTyFblUHc=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
|
171
internal/protocols/rtmp/amf0/marshal.go
Normal file
171
internal/protocols/rtmp/amf0/marshal.go
Normal file
@@ -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
|
||||
}
|
||||
}
|
17
internal/protocols/rtmp/amf0/marshal_test.go
Normal file
17
internal/protocols/rtmp/amf0/marshal_test.go
Normal file
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
53
internal/protocols/rtmp/amf0/object.go
Normal file
53
internal/protocols/rtmp/amf0/object.go
Normal file
@@ -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
|
||||
}
|
28
internal/protocols/rtmp/amf0/object_test.go
Normal file
28
internal/protocols/rtmp/amf0/object_test.go
Normal file
@@ -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)
|
||||
}
|
2
internal/protocols/rtmp/amf0/testdata/fuzz/FuzzUnmarshal/110121ffaa6941a6
vendored
Normal file
2
internal/protocols/rtmp/amf0/testdata/fuzz/FuzzUnmarshal/110121ffaa6941a6
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\x0200")
|
2
internal/protocols/rtmp/amf0/testdata/fuzz/FuzzUnmarshal/118a6dec0931d635
vendored
Normal file
2
internal/protocols/rtmp/amf0/testdata/fuzz/FuzzUnmarshal/118a6dec0931d635
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\b000000")
|
2
internal/protocols/rtmp/amf0/testdata/fuzz/FuzzUnmarshal/13ba6ce8ecfbc991
vendored
Normal file
2
internal/protocols/rtmp/amf0/testdata/fuzz/FuzzUnmarshal/13ba6ce8ecfbc991
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\b0000\x00\x000")
|
2
internal/protocols/rtmp/amf0/testdata/fuzz/FuzzUnmarshal/399a2041db7e1ff9
vendored
Normal file
2
internal/protocols/rtmp/amf0/testdata/fuzz/FuzzUnmarshal/399a2041db7e1ff9
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\x000")
|
2
internal/protocols/rtmp/amf0/testdata/fuzz/FuzzUnmarshal/582528ddfad69eb5
vendored
Normal file
2
internal/protocols/rtmp/amf0/testdata/fuzz/FuzzUnmarshal/582528ddfad69eb5
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("0")
|
2
internal/protocols/rtmp/amf0/testdata/fuzz/FuzzUnmarshal/6df12bd1a096b953
vendored
Normal file
2
internal/protocols/rtmp/amf0/testdata/fuzz/FuzzUnmarshal/6df12bd1a096b953
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\x03\x00\x0200")
|
2
internal/protocols/rtmp/amf0/testdata/fuzz/FuzzUnmarshal/6f9c29532ccb80bf
vendored
Normal file
2
internal/protocols/rtmp/amf0/testdata/fuzz/FuzzUnmarshal/6f9c29532ccb80bf
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\b")
|
2
internal/protocols/rtmp/amf0/testdata/fuzz/FuzzUnmarshal/7fbe967ee430d9f4
vendored
Normal file
2
internal/protocols/rtmp/amf0/testdata/fuzz/FuzzUnmarshal/7fbe967ee430d9f4
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\x0300")
|
2
internal/protocols/rtmp/amf0/testdata/fuzz/FuzzUnmarshal/97dc7172b48e6ffd
vendored
Normal file
2
internal/protocols/rtmp/amf0/testdata/fuzz/FuzzUnmarshal/97dc7172b48e6ffd
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\x01")
|
2
internal/protocols/rtmp/amf0/testdata/fuzz/FuzzUnmarshal/aba8f2fdaa5caedb
vendored
Normal file
2
internal/protocols/rtmp/amf0/testdata/fuzz/FuzzUnmarshal/aba8f2fdaa5caedb
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\x03\x00\x000")
|
2
internal/protocols/rtmp/amf0/testdata/fuzz/FuzzUnmarshal/b9ef082bd4d297b2
vendored
Normal file
2
internal/protocols/rtmp/amf0/testdata/fuzz/FuzzUnmarshal/b9ef082bd4d297b2
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\b0000\x00\x00")
|
2
internal/protocols/rtmp/amf0/testdata/fuzz/FuzzUnmarshal/df015416c2cf2dc6
vendored
Normal file
2
internal/protocols/rtmp/amf0/testdata/fuzz/FuzzUnmarshal/df015416c2cf2dc6
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\b0000")
|
2
internal/protocols/rtmp/amf0/testdata/fuzz/FuzzUnmarshal/e93e775e4de86c93
vendored
Normal file
2
internal/protocols/rtmp/amf0/testdata/fuzz/FuzzUnmarshal/e93e775e4de86c93
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\b0000\x00\x06000000")
|
2
internal/protocols/rtmp/amf0/testdata/fuzz/FuzzUnmarshal/f3b49d384cddc291
vendored
Normal file
2
internal/protocols/rtmp/amf0/testdata/fuzz/FuzzUnmarshal/f3b49d384cddc291
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\x03")
|
2
internal/protocols/rtmp/amf0/testdata/fuzz/FuzzUnmarshal/fb46839f39edbbd8
vendored
Normal file
2
internal/protocols/rtmp/amf0/testdata/fuzz/FuzzUnmarshal/fb46839f39edbbd8
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
go test fuzz v1
|
||||
[]byte("\x03\x00\x00")
|
189
internal/protocols/rtmp/amf0/unmarshal.go
Normal file
189
internal/protocols/rtmp/amf0/unmarshal.go
Normal file
@@ -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)
|
||||
}
|
||||
}
|
313
internal/protocols/rtmp/amf0/unmarshal_test.go
Normal file
313
internal/protocols/rtmp/amf0/unmarshal_test.go
Normal file
@@ -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
|
||||
})
|
||||
}
|
@@ -7,8 +7,7 @@ import (
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/notedit/rtmp/format/flv/flvio"
|
||||
|
||||
"github.com/bluenviron/mediamtx/internal/protocols/rtmp/amf0"
|
||||
"github.com/bluenviron/mediamtx/internal/protocols/rtmp/bytecounter"
|
||||
"github.com/bluenviron/mediamtx/internal/protocols/rtmp/handshake"
|
||||
"github.com/bluenviron/mediamtx/internal/protocols/rtmp/message"
|
||||
@@ -19,12 +18,12 @@ func resultIsOK1(res *message.CommandAMF0) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
ma, ok := res.Arguments[1].(flvio.AMFMap)
|
||||
ma, ok := res.Arguments[1].(amf0.Object)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
v, ok := ma.GetString("level")
|
||||
v, ok := ma.Get("level")
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
@@ -190,15 +189,15 @@ func (c *Conn) initializeClient(u *url.URL, publish bool) error {
|
||||
Name: "connect",
|
||||
CommandID: 1,
|
||||
Arguments: []interface{}{
|
||||
flvio.AMFMap{
|
||||
{K: "app", V: connectpath},
|
||||
{K: "flashVer", V: "LNX 9,0,124,2"},
|
||||
{K: "tcUrl", V: getTcURL(u)},
|
||||
{K: "fpad", V: false},
|
||||
{K: "capabilities", V: 15},
|
||||
{K: "audioCodecs", V: 4071},
|
||||
{K: "videoCodecs", V: 252},
|
||||
{K: "videoFunction", V: 1},
|
||||
amf0.Object{
|
||||
{Key: "app", Value: connectpath},
|
||||
{Key: "flashVer", Value: "LNX 9,0,124,2"},
|
||||
{Key: "tcUrl", Value: getTcURL(u)},
|
||||
{Key: "fpad", Value: false},
|
||||
{Key: "capabilities", Value: float64(15)},
|
||||
{Key: "audioCodecs", Value: float64(4071)},
|
||||
{Key: "videoCodecs", Value: float64(252)},
|
||||
{Key: "videoFunction", Value: float64(1)},
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -360,7 +359,7 @@ func (c *Conn) initializeServer() (*url.URL, bool, error) {
|
||||
return nil, false, fmt.Errorf("invalid connect command: %+v", cmd)
|
||||
}
|
||||
|
||||
ma, ok := cmd.Arguments[0].(flvio.AMFMap)
|
||||
ma, ok := cmd.Arguments[0].(amf0.Object)
|
||||
if !ok {
|
||||
return nil, false, fmt.Errorf("invalid connect command: %+v", cmd)
|
||||
}
|
||||
@@ -409,15 +408,15 @@ func (c *Conn) initializeServer() (*url.URL, bool, error) {
|
||||
Name: "_result",
|
||||
CommandID: cmd.CommandID,
|
||||
Arguments: []interface{}{
|
||||
flvio.AMFMap{
|
||||
{K: "fmsVer", V: "LNX 9,0,124,2"},
|
||||
{K: "capabilities", V: float64(31)},
|
||||
amf0.Object{
|
||||
{Key: "fmsVer", Value: "LNX 9,0,124,2"},
|
||||
{Key: "capabilities", Value: float64(31)},
|
||||
},
|
||||
flvio.AMFMap{
|
||||
{K: "level", V: "status"},
|
||||
{K: "code", V: "NetConnection.Connect.Success"},
|
||||
{K: "description", V: "Connection succeeded."},
|
||||
{K: "objectEncoding", V: oe},
|
||||
amf0.Object{
|
||||
{Key: "level", Value: "status"},
|
||||
{Key: "code", Value: "NetConnection.Connect.Success"},
|
||||
{Key: "description", Value: "Connection succeeded."},
|
||||
{Key: "objectEncoding", Value: oe},
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -482,10 +481,10 @@ func (c *Conn) initializeServer() (*url.URL, bool, error) {
|
||||
CommandID: cmd.CommandID,
|
||||
Arguments: []interface{}{
|
||||
nil,
|
||||
flvio.AMFMap{
|
||||
{K: "level", V: "status"},
|
||||
{K: "code", V: "NetStream.Play.Reset"},
|
||||
{K: "description", V: "play reset"},
|
||||
amf0.Object{
|
||||
{Key: "level", Value: "status"},
|
||||
{Key: "code", Value: "NetStream.Play.Reset"},
|
||||
{Key: "description", Value: "play reset"},
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -500,10 +499,10 @@ func (c *Conn) initializeServer() (*url.URL, bool, error) {
|
||||
CommandID: cmd.CommandID,
|
||||
Arguments: []interface{}{
|
||||
nil,
|
||||
flvio.AMFMap{
|
||||
{K: "level", V: "status"},
|
||||
{K: "code", V: "NetStream.Play.Start"},
|
||||
{K: "description", V: "play start"},
|
||||
amf0.Object{
|
||||
{Key: "level", Value: "status"},
|
||||
{Key: "code", Value: "NetStream.Play.Start"},
|
||||
{Key: "description", Value: "play start"},
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -518,10 +517,10 @@ func (c *Conn) initializeServer() (*url.URL, bool, error) {
|
||||
CommandID: cmd.CommandID,
|
||||
Arguments: []interface{}{
|
||||
nil,
|
||||
flvio.AMFMap{
|
||||
{K: "level", V: "status"},
|
||||
{K: "code", V: "NetStream.Data.Start"},
|
||||
{K: "description", V: "data start"},
|
||||
amf0.Object{
|
||||
{Key: "level", Value: "status"},
|
||||
{Key: "code", Value: "NetStream.Data.Start"},
|
||||
{Key: "description", Value: "data start"},
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -536,10 +535,10 @@ func (c *Conn) initializeServer() (*url.URL, bool, error) {
|
||||
CommandID: cmd.CommandID,
|
||||
Arguments: []interface{}{
|
||||
nil,
|
||||
flvio.AMFMap{
|
||||
{K: "level", V: "status"},
|
||||
{K: "code", V: "NetStream.Play.PublishNotify"},
|
||||
{K: "description", V: "publish notify"},
|
||||
amf0.Object{
|
||||
{Key: "level", Value: "status"},
|
||||
{Key: "code", Value: "NetStream.Play.PublishNotify"},
|
||||
{Key: "description", Value: "publish notify"},
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -571,10 +570,10 @@ func (c *Conn) initializeServer() (*url.URL, bool, error) {
|
||||
MessageStreamID: 0x1000000,
|
||||
Arguments: []interface{}{
|
||||
nil,
|
||||
flvio.AMFMap{
|
||||
{K: "level", V: "status"},
|
||||
{K: "code", V: "NetStream.Publish.Start"},
|
||||
{K: "description", V: "publish start"},
|
||||
amf0.Object{
|
||||
{Key: "level", Value: "status"},
|
||||
{Key: "code", Value: "NetStream.Publish.Start"},
|
||||
{Key: "description", Value: "publish start"},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@@ -6,9 +6,9 @@ import (
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/notedit/rtmp/format/flv/flvio"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/bluenviron/mediamtx/internal/protocols/rtmp/amf0"
|
||||
"github.com/bluenviron/mediamtx/internal/protocols/rtmp/bytecounter"
|
||||
"github.com/bluenviron/mediamtx/internal/protocols/rtmp/handshake"
|
||||
"github.com/bluenviron/mediamtx/internal/protocols/rtmp/message"
|
||||
@@ -64,15 +64,15 @@ func TestNewClientConn(t *testing.T) {
|
||||
Name: "connect",
|
||||
CommandID: 1,
|
||||
Arguments: []interface{}{
|
||||
flvio.AMFMap{
|
||||
{K: "app", V: "stream"},
|
||||
{K: "flashVer", V: "LNX 9,0,124,2"},
|
||||
{K: "tcUrl", V: "rtmp://127.0.0.1:9121/stream"},
|
||||
{K: "fpad", V: false},
|
||||
{K: "capabilities", V: float64(15)},
|
||||
{K: "audioCodecs", V: float64(4071)},
|
||||
{K: "videoCodecs", V: float64(252)},
|
||||
{K: "videoFunction", V: float64(1)},
|
||||
amf0.Object{
|
||||
{Key: "app", Value: "stream"},
|
||||
{Key: "flashVer", Value: "LNX 9,0,124,2"},
|
||||
{Key: "tcUrl", Value: "rtmp://127.0.0.1:9121/stream"},
|
||||
{Key: "fpad", Value: false},
|
||||
{Key: "capabilities", Value: float64(15)},
|
||||
{Key: "audioCodecs", Value: float64(4071)},
|
||||
{Key: "videoCodecs", Value: float64(252)},
|
||||
{Key: "videoFunction", Value: float64(1)},
|
||||
},
|
||||
},
|
||||
}, msg)
|
||||
@@ -82,15 +82,15 @@ func TestNewClientConn(t *testing.T) {
|
||||
Name: "_result",
|
||||
CommandID: 1,
|
||||
Arguments: []interface{}{
|
||||
flvio.AMFMap{
|
||||
{K: "fmsVer", V: "LNX 9,0,124,2"},
|
||||
{K: "capabilities", V: float64(31)},
|
||||
amf0.Object{
|
||||
{Key: "fmsVer", Value: "LNX 9,0,124,2"},
|
||||
{Key: "capabilities", Value: float64(31)},
|
||||
},
|
||||
flvio.AMFMap{
|
||||
{K: "level", V: "status"},
|
||||
{K: "code", V: "NetConnection.Connect.Success"},
|
||||
{K: "description", V: "Connection succeeded."},
|
||||
{K: "objectEncoding", V: float64(0)},
|
||||
amf0.Object{
|
||||
{Key: "level", Value: "status"},
|
||||
{Key: "code", Value: "NetConnection.Connect.Success"},
|
||||
{Key: "description", Value: "Connection succeeded."},
|
||||
{Key: "objectEncoding", Value: float64(0)},
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -151,10 +151,10 @@ func TestNewClientConn(t *testing.T) {
|
||||
}(),
|
||||
Arguments: []interface{}{
|
||||
nil,
|
||||
flvio.AMFMap{
|
||||
{K: "level", V: "status"},
|
||||
{K: "code", V: "NetStream.Play.Reset"},
|
||||
{K: "description", V: "play reset"},
|
||||
amf0.Object{
|
||||
{Key: "level", Value: "status"},
|
||||
{Key: "code", Value: "NetStream.Play.Reset"},
|
||||
{Key: "description", Value: "play reset"},
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -228,10 +228,10 @@ func TestNewClientConn(t *testing.T) {
|
||||
CommandID: 5,
|
||||
Arguments: []interface{}{
|
||||
nil,
|
||||
flvio.AMFMap{
|
||||
{K: "level", V: "status"},
|
||||
{K: "code", V: "NetStream.Publish.Start"},
|
||||
{K: "description", V: "publish start"},
|
||||
amf0.Object{
|
||||
{Key: "level", Value: "status"},
|
||||
{Key: "code", Value: "NetStream.Publish.Start"},
|
||||
{Key: "description", Value: "publish start"},
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -317,15 +317,15 @@ func TestNewServerConn(t *testing.T) {
|
||||
Name: "connect",
|
||||
CommandID: 1,
|
||||
Arguments: []interface{}{
|
||||
flvio.AMFMap{
|
||||
{K: "app", V: "/stream"},
|
||||
{K: "flashVer", V: "LNX 9,0,124,2"},
|
||||
{K: "tcUrl", V: tcURL},
|
||||
{K: "fpad", V: false},
|
||||
{K: "capabilities", V: 15},
|
||||
{K: "audioCodecs", V: 4071},
|
||||
{K: "videoCodecs", V: 252},
|
||||
{K: "videoFunction", V: 1},
|
||||
amf0.Object{
|
||||
{Key: "app", Value: "/stream"},
|
||||
{Key: "flashVer", Value: "LNX 9,0,124,2"},
|
||||
{Key: "tcUrl", Value: tcURL},
|
||||
{Key: "fpad", Value: false},
|
||||
{Key: "capabilities", Value: float64(15)},
|
||||
{Key: "audioCodecs", Value: float64(4071)},
|
||||
{Key: "videoCodecs", Value: float64(252)},
|
||||
{Key: "videoFunction", Value: float64(1)},
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -357,15 +357,15 @@ func TestNewServerConn(t *testing.T) {
|
||||
Name: "_result",
|
||||
CommandID: 1,
|
||||
Arguments: []interface{}{
|
||||
flvio.AMFMap{
|
||||
{K: "fmsVer", V: "LNX 9,0,124,2"},
|
||||
{K: "capabilities", V: float64(31)},
|
||||
amf0.Object{
|
||||
{Key: "fmsVer", Value: "LNX 9,0,124,2"},
|
||||
{Key: "capabilities", Value: float64(31)},
|
||||
},
|
||||
flvio.AMFMap{
|
||||
{K: "level", V: "status"},
|
||||
{K: "code", V: "NetConnection.Connect.Success"},
|
||||
{K: "description", V: "Connection succeeded."},
|
||||
{K: "objectEncoding", V: float64(0)},
|
||||
amf0.Object{
|
||||
{Key: "level", Value: "status"},
|
||||
{Key: "code", Value: "NetConnection.Connect.Success"},
|
||||
{Key: "description", Value: "Connection succeeded."},
|
||||
{Key: "objectEncoding", Value: float64(0)},
|
||||
},
|
||||
},
|
||||
}, msg)
|
||||
|
@@ -11,8 +11,7 @@ type Acknowledge struct {
|
||||
Value uint32
|
||||
}
|
||||
|
||||
// Unmarshal implements Message.
|
||||
func (m *Acknowledge) Unmarshal(raw *rawmessage.Message) error {
|
||||
func (m *Acknowledge) unmarshal(raw *rawmessage.Message) error {
|
||||
if raw.ChunkStreamID != ControlChunkStreamID {
|
||||
return fmt.Errorf("unexpected chunk stream ID")
|
||||
}
|
||||
@@ -26,8 +25,7 @@ func (m *Acknowledge) Unmarshal(raw *rawmessage.Message) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Marshal implements Message.
|
||||
func (m *Acknowledge) Marshal() (*rawmessage.Message, error) {
|
||||
func (m *Acknowledge) marshal() (*rawmessage.Message, error) {
|
||||
buf := make([]byte, 4)
|
||||
|
||||
buf[0] = byte(m.Value >> 24)
|
||||
|
@@ -57,8 +57,7 @@ type Audio struct {
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
// Unmarshal implements Message.
|
||||
func (m *Audio) Unmarshal(raw *rawmessage.Message) error {
|
||||
func (m *Audio) unmarshal(raw *rawmessage.Message) error {
|
||||
m.ChunkStreamID = raw.ChunkStreamID
|
||||
m.DTS = raw.Timestamp
|
||||
m.MessageStreamID = raw.MessageStreamID
|
||||
@@ -107,8 +106,7 @@ func (m Audio) marshalBodySize() int {
|
||||
return l
|
||||
}
|
||||
|
||||
// Marshal implements Message.
|
||||
func (m Audio) Marshal() (*rawmessage.Message, error) {
|
||||
func (m Audio) marshal() (*rawmessage.Message, error) {
|
||||
body := make([]byte, m.marshalBodySize())
|
||||
|
||||
body[0] = m.Codec<<4 | m.Rate<<2 | m.Depth<<1
|
||||
|
@@ -3,8 +3,7 @@ package message
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/notedit/rtmp/format/flv/flvio"
|
||||
|
||||
"github.com/bluenviron/mediamtx/internal/protocols/rtmp/amf0"
|
||||
"github.com/bluenviron/mediamtx/internal/protocols/rtmp/rawmessage"
|
||||
)
|
||||
|
||||
@@ -17,12 +16,11 @@ type CommandAMF0 struct {
|
||||
Arguments []interface{}
|
||||
}
|
||||
|
||||
// Unmarshal implements Message.
|
||||
func (m *CommandAMF0) Unmarshal(raw *rawmessage.Message) error {
|
||||
func (m *CommandAMF0) unmarshal(raw *rawmessage.Message) error {
|
||||
m.ChunkStreamID = raw.ChunkStreamID
|
||||
m.MessageStreamID = raw.MessageStreamID
|
||||
|
||||
payload, err := flvio.ParseAMFVals(raw.Body, false)
|
||||
payload, err := amf0.Unmarshal(raw.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -48,15 +46,21 @@ func (m *CommandAMF0) Unmarshal(raw *rawmessage.Message) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Marshal implements Message.
|
||||
func (m CommandAMF0) Marshal() (*rawmessage.Message, error) {
|
||||
func (m CommandAMF0) marshal() (*rawmessage.Message, error) {
|
||||
data := append([]interface{}{
|
||||
m.Name,
|
||||
float64(m.CommandID),
|
||||
}, m.Arguments...)
|
||||
|
||||
body, err := amf0.Marshal(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &rawmessage.Message{
|
||||
ChunkStreamID: m.ChunkStreamID,
|
||||
Type: uint8(TypeCommandAMF0),
|
||||
MessageStreamID: m.MessageStreamID,
|
||||
Body: flvio.FillAMF0ValsMalloc(append([]interface{}{
|
||||
m.Name,
|
||||
float64(m.CommandID),
|
||||
}, m.Arguments...)),
|
||||
Body: body,
|
||||
}, nil
|
||||
}
|
||||
|
73
internal/protocols/rtmp/message/command_amf0_test.go
Normal file
73
internal/protocols/rtmp/message/command_amf0_test.go
Normal file
@@ -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
|
||||
}
|
||||
}
|
@@ -1,8 +1,7 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"github.com/notedit/rtmp/format/flv/flvio"
|
||||
|
||||
"github.com/bluenviron/mediamtx/internal/protocols/rtmp/amf0"
|
||||
"github.com/bluenviron/mediamtx/internal/protocols/rtmp/rawmessage"
|
||||
)
|
||||
|
||||
@@ -13,12 +12,11 @@ type DataAMF0 struct {
|
||||
Payload []interface{}
|
||||
}
|
||||
|
||||
// Unmarshal implements Message.
|
||||
func (m *DataAMF0) Unmarshal(raw *rawmessage.Message) error {
|
||||
func (m *DataAMF0) unmarshal(raw *rawmessage.Message) error {
|
||||
m.ChunkStreamID = raw.ChunkStreamID
|
||||
m.MessageStreamID = raw.MessageStreamID
|
||||
|
||||
payload, err := flvio.ParseAMFVals(raw.Body, false)
|
||||
payload, err := amf0.Unmarshal(raw.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -27,12 +25,16 @@ func (m *DataAMF0) Unmarshal(raw *rawmessage.Message) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Marshal implements Message.
|
||||
func (m DataAMF0) Marshal() (*rawmessage.Message, error) {
|
||||
func (m DataAMF0) marshal() (*rawmessage.Message, error) {
|
||||
body, err := amf0.Marshal(m.Payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &rawmessage.Message{
|
||||
ChunkStreamID: m.ChunkStreamID,
|
||||
Type: uint8(TypeDataAMF0),
|
||||
MessageStreamID: m.MessageStreamID,
|
||||
Body: flvio.FillAMF0ValsMalloc(m.Payload),
|
||||
Body: body,
|
||||
}, nil
|
||||
}
|
||||
|
@@ -17,8 +17,7 @@ type ExtendedCodedFrames struct {
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
// Unmarshal implements Message.
|
||||
func (m *ExtendedCodedFrames) Unmarshal(raw *rawmessage.Message) error {
|
||||
func (m *ExtendedCodedFrames) unmarshal(raw *rawmessage.Message) error {
|
||||
if len(raw.Body) < 8 {
|
||||
return fmt.Errorf("not enough bytes")
|
||||
}
|
||||
@@ -48,8 +47,7 @@ func (m ExtendedCodedFrames) marshalBodySize() int {
|
||||
return l
|
||||
}
|
||||
|
||||
// Marshal implements Message.
|
||||
func (m ExtendedCodedFrames) Marshal() (*rawmessage.Message, error) {
|
||||
func (m ExtendedCodedFrames) marshal() (*rawmessage.Message, error) {
|
||||
body := make([]byte, m.marshalBodySize())
|
||||
|
||||
body[0] = 0b10000000 | byte(ExtendedTypeCodedFrames)
|
||||
|
@@ -16,8 +16,7 @@ type ExtendedFramesX struct {
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
// Unmarshal implements Message.
|
||||
func (m *ExtendedFramesX) Unmarshal(raw *rawmessage.Message) error {
|
||||
func (m *ExtendedFramesX) unmarshal(raw *rawmessage.Message) error {
|
||||
if len(raw.Body) < 6 {
|
||||
return fmt.Errorf("not enough bytes")
|
||||
}
|
||||
@@ -35,8 +34,7 @@ func (m ExtendedFramesX) marshalBodySize() int {
|
||||
return 5 + len(m.Payload)
|
||||
}
|
||||
|
||||
// Marshal implements Message.
|
||||
func (m ExtendedFramesX) Marshal() (*rawmessage.Message, error) {
|
||||
func (m ExtendedFramesX) marshal() (*rawmessage.Message, error) {
|
||||
body := make([]byte, m.marshalBodySize())
|
||||
|
||||
body[0] = 0b10000000 | byte(ExtendedTypeFramesX)
|
||||
|
@@ -11,8 +11,7 @@ type ExtendedMetadata struct {
|
||||
FourCC FourCC
|
||||
}
|
||||
|
||||
// Unmarshal implements Message.
|
||||
func (m *ExtendedMetadata) Unmarshal(raw *rawmessage.Message) error {
|
||||
func (m *ExtendedMetadata) unmarshal(raw *rawmessage.Message) error {
|
||||
if len(raw.Body) != 5 {
|
||||
return fmt.Errorf("invalid body size")
|
||||
}
|
||||
@@ -22,7 +21,6 @@ func (m *ExtendedMetadata) Unmarshal(raw *rawmessage.Message) error {
|
||||
return fmt.Errorf("ExtendedMetadata is not implemented yet")
|
||||
}
|
||||
|
||||
// Marshal implements Message.
|
||||
func (m ExtendedMetadata) Marshal() (*rawmessage.Message, error) {
|
||||
func (m ExtendedMetadata) marshal() (*rawmessage.Message, error) {
|
||||
return nil, fmt.Errorf("TODO")
|
||||
}
|
||||
|
@@ -11,8 +11,7 @@ type ExtendedMPEG2TSSequenceStart struct {
|
||||
FourCC FourCC
|
||||
}
|
||||
|
||||
// Unmarshal implements Message.
|
||||
func (m *ExtendedMPEG2TSSequenceStart) Unmarshal(raw *rawmessage.Message) error {
|
||||
func (m *ExtendedMPEG2TSSequenceStart) unmarshal(raw *rawmessage.Message) error {
|
||||
if len(raw.Body) != 5 {
|
||||
return fmt.Errorf("invalid body size")
|
||||
}
|
||||
@@ -22,7 +21,6 @@ func (m *ExtendedMPEG2TSSequenceStart) Unmarshal(raw *rawmessage.Message) error
|
||||
return fmt.Errorf("ExtendedMPEG2TSSequenceStart is not implemented yet")
|
||||
}
|
||||
|
||||
// Marshal implements Message.
|
||||
func (m ExtendedMPEG2TSSequenceStart) Marshal() (*rawmessage.Message, error) {
|
||||
func (m ExtendedMPEG2TSSequenceStart) marshal() (*rawmessage.Message, error) {
|
||||
return nil, fmt.Errorf("TODO")
|
||||
}
|
||||
|
@@ -11,8 +11,7 @@ type ExtendedSequenceEnd struct {
|
||||
FourCC FourCC
|
||||
}
|
||||
|
||||
// Unmarshal implements Message.
|
||||
func (m *ExtendedSequenceEnd) Unmarshal(raw *rawmessage.Message) error {
|
||||
func (m *ExtendedSequenceEnd) unmarshal(raw *rawmessage.Message) error {
|
||||
if len(raw.Body) != 5 {
|
||||
return fmt.Errorf("invalid body size")
|
||||
}
|
||||
@@ -22,7 +21,6 @@ func (m *ExtendedSequenceEnd) Unmarshal(raw *rawmessage.Message) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Marshal implements Message.
|
||||
func (m ExtendedSequenceEnd) Marshal() (*rawmessage.Message, error) {
|
||||
func (m ExtendedSequenceEnd) marshal() (*rawmessage.Message, error) {
|
||||
return nil, fmt.Errorf("TODO")
|
||||
}
|
||||
|
@@ -14,8 +14,7 @@ type ExtendedSequenceStart struct {
|
||||
Config []byte
|
||||
}
|
||||
|
||||
// Unmarshal implements Message.
|
||||
func (m *ExtendedSequenceStart) Unmarshal(raw *rawmessage.Message) error {
|
||||
func (m *ExtendedSequenceStart) unmarshal(raw *rawmessage.Message) error {
|
||||
if len(raw.Body) < 6 {
|
||||
return fmt.Errorf("not enough bytes")
|
||||
}
|
||||
@@ -32,8 +31,7 @@ func (m ExtendedSequenceStart) marshalBodySize() int {
|
||||
return 5 + len(m.Config)
|
||||
}
|
||||
|
||||
// Marshal implements Message.
|
||||
func (m ExtendedSequenceStart) Marshal() (*rawmessage.Message, error) {
|
||||
func (m ExtendedSequenceStart) marshal() (*rawmessage.Message, error) {
|
||||
body := make([]byte, m.marshalBodySize())
|
||||
|
||||
body[0] = 0b10000000 | byte(ExtendedTypeSequenceStart)
|
||||
|
@@ -72,6 +72,6 @@ var (
|
||||
|
||||
// Message is a message.
|
||||
type Message interface {
|
||||
Unmarshal(*rawmessage.Message) error
|
||||
Marshal() (*rawmessage.Message, error)
|
||||
unmarshal(*rawmessage.Message) error
|
||||
marshal() (*rawmessage.Message, error)
|
||||
}
|
||||
|
@@ -138,7 +138,7 @@ func (r *Reader) Read() (Message, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = msg.Unmarshal(raw)
|
||||
err = msg.unmarshal(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -5,9 +5,9 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/notedit/rtmp/format/flv/flvio"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/bluenviron/mediamtx/internal/protocols/rtmp/amf0"
|
||||
"github.com/bluenviron/mediamtx/internal/protocols/rtmp/bytecounter"
|
||||
)
|
||||
|
||||
@@ -70,9 +70,9 @@ var readWriterCases = []struct {
|
||||
Name: "i8yythrergre",
|
||||
CommandID: 56456,
|
||||
Arguments: []interface{}{
|
||||
flvio.AMFMap{
|
||||
{K: "k1", V: "v1"},
|
||||
{K: "k2", V: "v2"},
|
||||
amf0.Object{
|
||||
{Key: "k1", Value: "v1"},
|
||||
{Key: "k2", Value: "v2"},
|
||||
},
|
||||
nil,
|
||||
},
|
||||
|
@@ -11,8 +11,7 @@ type SetChunkSize struct {
|
||||
Value uint32
|
||||
}
|
||||
|
||||
// Unmarshal implements Message.
|
||||
func (m *SetChunkSize) Unmarshal(raw *rawmessage.Message) error {
|
||||
func (m *SetChunkSize) unmarshal(raw *rawmessage.Message) error {
|
||||
if raw.ChunkStreamID != ControlChunkStreamID {
|
||||
return fmt.Errorf("unexpected chunk stream ID")
|
||||
}
|
||||
@@ -26,8 +25,7 @@ func (m *SetChunkSize) Unmarshal(raw *rawmessage.Message) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Marshal implements Message.
|
||||
func (m *SetChunkSize) Marshal() (*rawmessage.Message, error) {
|
||||
func (m *SetChunkSize) marshal() (*rawmessage.Message, error) {
|
||||
buf := make([]byte, 4)
|
||||
|
||||
buf[0] = byte(m.Value >> 24)
|
||||
|
@@ -12,8 +12,7 @@ type SetPeerBandwidth struct {
|
||||
Type byte
|
||||
}
|
||||
|
||||
// Unmarshal implements Message.
|
||||
func (m *SetPeerBandwidth) Unmarshal(raw *rawmessage.Message) error {
|
||||
func (m *SetPeerBandwidth) unmarshal(raw *rawmessage.Message) error {
|
||||
if raw.ChunkStreamID != ControlChunkStreamID {
|
||||
return fmt.Errorf("unexpected chunk stream ID")
|
||||
}
|
||||
@@ -28,8 +27,7 @@ func (m *SetPeerBandwidth) Unmarshal(raw *rawmessage.Message) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Marshal implements Message.
|
||||
func (m *SetPeerBandwidth) Marshal() (*rawmessage.Message, error) {
|
||||
func (m *SetPeerBandwidth) marshal() (*rawmessage.Message, error) {
|
||||
buf := make([]byte, 5)
|
||||
|
||||
buf[0] = byte(m.Value >> 24)
|
||||
|
@@ -11,8 +11,7 @@ type SetWindowAckSize struct {
|
||||
Value uint32
|
||||
}
|
||||
|
||||
// Unmarshal implements Message.
|
||||
func (m *SetWindowAckSize) Unmarshal(raw *rawmessage.Message) error {
|
||||
func (m *SetWindowAckSize) unmarshal(raw *rawmessage.Message) error {
|
||||
if raw.ChunkStreamID != ControlChunkStreamID {
|
||||
return fmt.Errorf("unexpected chunk stream ID")
|
||||
}
|
||||
@@ -26,8 +25,7 @@ func (m *SetWindowAckSize) Unmarshal(raw *rawmessage.Message) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Marshal implements Message.
|
||||
func (m *SetWindowAckSize) Marshal() (*rawmessage.Message, error) {
|
||||
func (m *SetWindowAckSize) marshal() (*rawmessage.Message, error) {
|
||||
buf := make([]byte, 4)
|
||||
|
||||
buf[0] = byte(m.Value >> 24)
|
||||
|
@@ -11,8 +11,7 @@ type UserControlPingRequest struct {
|
||||
ServerTime uint32
|
||||
}
|
||||
|
||||
// Unmarshal implements Message.
|
||||
func (m *UserControlPingRequest) Unmarshal(raw *rawmessage.Message) error {
|
||||
func (m *UserControlPingRequest) unmarshal(raw *rawmessage.Message) error {
|
||||
if raw.ChunkStreamID != ControlChunkStreamID {
|
||||
return fmt.Errorf("unexpected chunk stream ID")
|
||||
}
|
||||
@@ -26,8 +25,7 @@ func (m *UserControlPingRequest) Unmarshal(raw *rawmessage.Message) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Marshal implements Message.
|
||||
func (m UserControlPingRequest) Marshal() (*rawmessage.Message, error) {
|
||||
func (m UserControlPingRequest) marshal() (*rawmessage.Message, error) {
|
||||
buf := make([]byte, 6)
|
||||
|
||||
buf[0] = byte(UserControlTypePingRequest >> 8)
|
||||
|
@@ -11,8 +11,7 @@ type UserControlPingResponse struct {
|
||||
ServerTime uint32
|
||||
}
|
||||
|
||||
// Unmarshal implements Message.
|
||||
func (m *UserControlPingResponse) Unmarshal(raw *rawmessage.Message) error {
|
||||
func (m *UserControlPingResponse) unmarshal(raw *rawmessage.Message) error {
|
||||
if raw.ChunkStreamID != ControlChunkStreamID {
|
||||
return fmt.Errorf("unexpected chunk stream ID")
|
||||
}
|
||||
@@ -26,8 +25,7 @@ func (m *UserControlPingResponse) Unmarshal(raw *rawmessage.Message) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Marshal implements Message.
|
||||
func (m UserControlPingResponse) Marshal() (*rawmessage.Message, error) {
|
||||
func (m UserControlPingResponse) marshal() (*rawmessage.Message, error) {
|
||||
buf := make([]byte, 6)
|
||||
|
||||
buf[0] = byte(UserControlTypePingResponse >> 8)
|
||||
|
@@ -12,8 +12,7 @@ type UserControlSetBufferLength struct {
|
||||
BufferLength uint32
|
||||
}
|
||||
|
||||
// Unmarshal implements Message.
|
||||
func (m *UserControlSetBufferLength) Unmarshal(raw *rawmessage.Message) error {
|
||||
func (m *UserControlSetBufferLength) unmarshal(raw *rawmessage.Message) error {
|
||||
if raw.ChunkStreamID != ControlChunkStreamID {
|
||||
return fmt.Errorf("unexpected chunk stream ID")
|
||||
}
|
||||
@@ -28,8 +27,7 @@ func (m *UserControlSetBufferLength) Unmarshal(raw *rawmessage.Message) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Marshal implements Message.
|
||||
func (m UserControlSetBufferLength) Marshal() (*rawmessage.Message, error) {
|
||||
func (m UserControlSetBufferLength) marshal() (*rawmessage.Message, error) {
|
||||
buf := make([]byte, 10)
|
||||
|
||||
buf[0] = byte(UserControlTypeSetBufferLength >> 8)
|
||||
|
@@ -11,8 +11,7 @@ type UserControlStreamBegin struct {
|
||||
StreamID uint32
|
||||
}
|
||||
|
||||
// Unmarshal implements Message.
|
||||
func (m *UserControlStreamBegin) Unmarshal(raw *rawmessage.Message) error {
|
||||
func (m *UserControlStreamBegin) unmarshal(raw *rawmessage.Message) error {
|
||||
if raw.ChunkStreamID != ControlChunkStreamID {
|
||||
return fmt.Errorf("unexpected chunk stream ID")
|
||||
}
|
||||
@@ -26,8 +25,7 @@ func (m *UserControlStreamBegin) Unmarshal(raw *rawmessage.Message) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Marshal implements Message.
|
||||
func (m UserControlStreamBegin) Marshal() (*rawmessage.Message, error) {
|
||||
func (m UserControlStreamBegin) marshal() (*rawmessage.Message, error) {
|
||||
buf := make([]byte, 6)
|
||||
|
||||
buf[0] = byte(UserControlTypeStreamBegin >> 8)
|
||||
|
@@ -11,8 +11,7 @@ type UserControlStreamDry struct {
|
||||
StreamID uint32
|
||||
}
|
||||
|
||||
// Unmarshal implements Message.
|
||||
func (m *UserControlStreamDry) Unmarshal(raw *rawmessage.Message) error {
|
||||
func (m *UserControlStreamDry) unmarshal(raw *rawmessage.Message) error {
|
||||
if raw.ChunkStreamID != ControlChunkStreamID {
|
||||
return fmt.Errorf("unexpected chunk stream ID")
|
||||
}
|
||||
@@ -26,8 +25,7 @@ func (m *UserControlStreamDry) Unmarshal(raw *rawmessage.Message) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Marshal implements Message.
|
||||
func (m UserControlStreamDry) Marshal() (*rawmessage.Message, error) {
|
||||
func (m UserControlStreamDry) marshal() (*rawmessage.Message, error) {
|
||||
buf := make([]byte, 6)
|
||||
|
||||
buf[0] = byte(UserControlTypeStreamDry >> 8)
|
||||
|
@@ -11,8 +11,7 @@ type UserControlStreamEOF struct {
|
||||
StreamID uint32
|
||||
}
|
||||
|
||||
// Unmarshal implements Message.
|
||||
func (m *UserControlStreamEOF) Unmarshal(raw *rawmessage.Message) error {
|
||||
func (m *UserControlStreamEOF) unmarshal(raw *rawmessage.Message) error {
|
||||
if raw.ChunkStreamID != ControlChunkStreamID {
|
||||
return fmt.Errorf("unexpected chunk stream ID")
|
||||
}
|
||||
@@ -26,8 +25,7 @@ func (m *UserControlStreamEOF) Unmarshal(raw *rawmessage.Message) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Marshal implements Message.
|
||||
func (m UserControlStreamEOF) Marshal() (*rawmessage.Message, error) {
|
||||
func (m UserControlStreamEOF) marshal() (*rawmessage.Message, error) {
|
||||
buf := make([]byte, 6)
|
||||
|
||||
buf[0] = byte(UserControlTypeStreamEOF >> 8)
|
||||
|
@@ -11,8 +11,7 @@ type UserControlStreamIsRecorded struct {
|
||||
StreamID uint32
|
||||
}
|
||||
|
||||
// Unmarshal implements Message.
|
||||
func (m *UserControlStreamIsRecorded) Unmarshal(raw *rawmessage.Message) error {
|
||||
func (m *UserControlStreamIsRecorded) unmarshal(raw *rawmessage.Message) error {
|
||||
if raw.ChunkStreamID != ControlChunkStreamID {
|
||||
return fmt.Errorf("unexpected chunk stream ID")
|
||||
}
|
||||
@@ -26,8 +25,7 @@ func (m *UserControlStreamIsRecorded) Unmarshal(raw *rawmessage.Message) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Marshal implements Message.
|
||||
func (m UserControlStreamIsRecorded) Marshal() (*rawmessage.Message, error) {
|
||||
func (m UserControlStreamIsRecorded) marshal() (*rawmessage.Message, error) {
|
||||
buf := make([]byte, 6)
|
||||
|
||||
buf[0] = byte(UserControlTypeStreamIsRecorded >> 8)
|
||||
|
@@ -39,8 +39,7 @@ type Video struct {
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
// Unmarshal implements Message.
|
||||
func (m *Video) Unmarshal(raw *rawmessage.Message) error {
|
||||
func (m *Video) unmarshal(raw *rawmessage.Message) error {
|
||||
m.ChunkStreamID = raw.ChunkStreamID
|
||||
m.DTS = raw.Timestamp
|
||||
m.MessageStreamID = raw.MessageStreamID
|
||||
@@ -76,8 +75,7 @@ func (m Video) marshalBodySize() int {
|
||||
return 5 + len(m.Payload)
|
||||
}
|
||||
|
||||
// Marshal implements Message.
|
||||
func (m Video) Marshal() (*rawmessage.Message, error) {
|
||||
func (m Video) marshal() (*rawmessage.Message, error) {
|
||||
body := make([]byte, m.marshalBodySize())
|
||||
|
||||
if m.IsKeyFrame {
|
||||
|
@@ -30,7 +30,7 @@ func (w *Writer) SetAcknowledgeValue(v uint32) {
|
||||
|
||||
// Write writes a message.
|
||||
func (w *Writer) Write(msg Message) error {
|
||||
raw, err := msg.Marshal()
|
||||
raw, err := msg.marshal()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@@ -12,8 +12,8 @@ import (
|
||||
"github.com/bluenviron/mediacommon/pkg/codecs/h264"
|
||||
"github.com/bluenviron/mediacommon/pkg/codecs/h265"
|
||||
"github.com/bluenviron/mediacommon/pkg/codecs/mpeg4audio"
|
||||
"github.com/notedit/rtmp/format/flv/flvio"
|
||||
|
||||
"github.com/bluenviron/mediamtx/internal/protocols/rtmp/amf0"
|
||||
"github.com/bluenviron/mediamtx/internal/protocols/rtmp/h264conf"
|
||||
"github.com/bluenviron/mediamtx/internal/protocols/rtmp/message"
|
||||
)
|
||||
@@ -43,11 +43,11 @@ type OnDataG711Func func(pts time.Duration, samples []byte)
|
||||
// OnDataLPCMFunc is the prototype of the callback passed to OnDataLPCM().
|
||||
type OnDataLPCMFunc func(pts time.Duration, samples []byte)
|
||||
|
||||
func hasVideo(md flvio.AMFMap) (bool, error) {
|
||||
v, ok := md.GetV("videocodecid")
|
||||
func hasVideo(md amf0.Object) (bool, error) {
|
||||
v, ok := md.Get("videocodecid")
|
||||
if !ok {
|
||||
// some Dahua cameras send width and height without videocodecid
|
||||
if v, ok := md.GetV("width"); ok {
|
||||
if v, ok := md.Get("width"); ok {
|
||||
if vf, ok := v.(float64); ok && vf != 0 {
|
||||
return true, nil
|
||||
}
|
||||
@@ -75,8 +75,8 @@ func hasVideo(md flvio.AMFMap) (bool, error) {
|
||||
return false, fmt.Errorf("unsupported video codec: %v", v)
|
||||
}
|
||||
|
||||
func hasAudio(md flvio.AMFMap, audioTrack *format.Format) (bool, error) {
|
||||
v, ok := md.GetV("audiocodecid")
|
||||
func hasAudio(md amf0.Object, audioTrack *format.Format) (bool, error) {
|
||||
v, ok := md.Get("audiocodecid")
|
||||
if !ok {
|
||||
return false, nil
|
||||
}
|
||||
@@ -156,7 +156,7 @@ func tracksFromMetadata(conn *Conn, payload []interface{}) (format.Format, forma
|
||||
return nil, nil, fmt.Errorf("invalid metadata")
|
||||
}
|
||||
|
||||
md, ok := payload[0].(flvio.AMFMap)
|
||||
md, ok := payload[0].(amf0.Object)
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("invalid metadata")
|
||||
}
|
||||
|
@@ -10,9 +10,9 @@ import (
|
||||
"github.com/bluenviron/mediacommon/pkg/codecs/h264"
|
||||
"github.com/bluenviron/mediacommon/pkg/codecs/h265"
|
||||
"github.com/bluenviron/mediacommon/pkg/codecs/mpeg4audio"
|
||||
"github.com/notedit/rtmp/format/flv/flvio"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/bluenviron/mediamtx/internal/protocols/rtmp/amf0"
|
||||
"github.com/bluenviron/mediamtx/internal/protocols/rtmp/bytecounter"
|
||||
"github.com/bluenviron/mediamtx/internal/protocols/rtmp/h264conf"
|
||||
"github.com/bluenviron/mediamtx/internal/protocols/rtmp/message"
|
||||
@@ -134,22 +134,22 @@ func TestReadTracks(t *testing.T) {
|
||||
Payload: []interface{}{
|
||||
"@setDataFrame",
|
||||
"onMetaData",
|
||||
flvio.AMFMap{
|
||||
amf0.Object{
|
||||
{
|
||||
K: "videodatarate",
|
||||
V: float64(0),
|
||||
Key: "videodatarate",
|
||||
Value: float64(0),
|
||||
},
|
||||
{
|
||||
K: "videocodecid",
|
||||
V: float64(message.CodecH264),
|
||||
Key: "videocodecid",
|
||||
Value: float64(message.CodecH264),
|
||||
},
|
||||
{
|
||||
K: "audiodatarate",
|
||||
V: float64(0),
|
||||
Key: "audiodatarate",
|
||||
Value: float64(0),
|
||||
},
|
||||
{
|
||||
K: "audiocodecid",
|
||||
V: float64(message.CodecMPEG4Audio),
|
||||
Key: "audiocodecid",
|
||||
Value: float64(message.CodecMPEG4Audio),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -204,22 +204,22 @@ func TestReadTracks(t *testing.T) {
|
||||
Payload: []interface{}{
|
||||
"@setDataFrame",
|
||||
"onMetaData",
|
||||
flvio.AMFMap{
|
||||
amf0.Object{
|
||||
{
|
||||
K: "videodatarate",
|
||||
V: float64(0),
|
||||
Key: "videodatarate",
|
||||
Value: float64(0),
|
||||
},
|
||||
{
|
||||
K: "videocodecid",
|
||||
V: float64(message.CodecH264),
|
||||
Key: "videocodecid",
|
||||
Value: float64(message.CodecH264),
|
||||
},
|
||||
{
|
||||
K: "audiodatarate",
|
||||
V: float64(0),
|
||||
Key: "audiodatarate",
|
||||
Value: float64(0),
|
||||
},
|
||||
{
|
||||
K: "audiocodecid",
|
||||
V: float64(0),
|
||||
Key: "audiocodecid",
|
||||
Value: float64(0),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -388,22 +388,22 @@ func TestReadTracks(t *testing.T) {
|
||||
Payload: []interface{}{
|
||||
"@setDataFrame",
|
||||
"onMetaData",
|
||||
flvio.AMFMap{
|
||||
amf0.Object{
|
||||
{
|
||||
K: "videodatarate",
|
||||
V: float64(0),
|
||||
Key: "videodatarate",
|
||||
Value: float64(0),
|
||||
},
|
||||
{
|
||||
K: "videocodecid",
|
||||
V: float64(message.CodecH264),
|
||||
Key: "videocodecid",
|
||||
Value: float64(message.CodecH264),
|
||||
},
|
||||
{
|
||||
K: "audiodatarate",
|
||||
V: float64(0),
|
||||
Key: "audiodatarate",
|
||||
Value: float64(0),
|
||||
},
|
||||
{
|
||||
K: "audiocodecid",
|
||||
V: float64(message.CodecMPEG4Audio),
|
||||
Key: "audiocodecid",
|
||||
Value: float64(message.CodecMPEG4Audio),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -460,22 +460,22 @@ func TestReadTracks(t *testing.T) {
|
||||
Payload: []interface{}{
|
||||
"@setDataFrame",
|
||||
"onMetaData",
|
||||
flvio.AMFMap{
|
||||
amf0.Object{
|
||||
{
|
||||
K: "videodatarate",
|
||||
V: float64(0),
|
||||
Key: "videodatarate",
|
||||
Value: float64(0),
|
||||
},
|
||||
{
|
||||
K: "videocodecid",
|
||||
V: "hvc1",
|
||||
Key: "videocodecid",
|
||||
Value: "hvc1",
|
||||
},
|
||||
{
|
||||
K: "audiodatarate",
|
||||
V: float64(0),
|
||||
Key: "audiodatarate",
|
||||
Value: float64(0),
|
||||
},
|
||||
{
|
||||
K: "audiocodecid",
|
||||
V: float64(0),
|
||||
Key: "audiocodecid",
|
||||
Value: float64(0),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -509,22 +509,22 @@ func TestReadTracks(t *testing.T) {
|
||||
Payload: []interface{}{
|
||||
"@setDataFrame",
|
||||
"onMetaData",
|
||||
flvio.AMFMap{
|
||||
amf0.Object{
|
||||
{
|
||||
K: "videodatarate",
|
||||
V: float64(0),
|
||||
Key: "videodatarate",
|
||||
Value: float64(0),
|
||||
},
|
||||
{
|
||||
K: "videocodecid",
|
||||
V: float64(message.FourCCHEVC),
|
||||
Key: "videocodecid",
|
||||
Value: float64(message.FourCCHEVC),
|
||||
},
|
||||
{
|
||||
K: "audiodatarate",
|
||||
V: float64(0),
|
||||
Key: "audiodatarate",
|
||||
Value: float64(0),
|
||||
},
|
||||
{
|
||||
K: "audiocodecid",
|
||||
V: float64(0),
|
||||
Key: "audiocodecid",
|
||||
Value: float64(0),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -555,38 +555,38 @@ func TestReadTracks(t *testing.T) {
|
||||
Payload: []interface{}{
|
||||
"@setDataFrame",
|
||||
"onMetaData",
|
||||
flvio.AMFMap{
|
||||
amf0.Object{
|
||||
{
|
||||
K: "duration",
|
||||
V: float64(0),
|
||||
Key: "duration",
|
||||
Value: float64(0),
|
||||
},
|
||||
{
|
||||
K: "width",
|
||||
V: float64(1920),
|
||||
Key: "width",
|
||||
Value: float64(1920),
|
||||
},
|
||||
{
|
||||
K: "height",
|
||||
V: float64(1080),
|
||||
Key: "height",
|
||||
Value: float64(1080),
|
||||
},
|
||||
{
|
||||
K: "videodatarate",
|
||||
V: float64(0),
|
||||
Key: "videodatarate",
|
||||
Value: float64(0),
|
||||
},
|
||||
{
|
||||
K: "framerate",
|
||||
V: float64(30),
|
||||
Key: "framerate",
|
||||
Value: float64(30),
|
||||
},
|
||||
{
|
||||
K: "videocodecid",
|
||||
V: float64(message.FourCCAV1),
|
||||
Key: "videocodecid",
|
||||
Value: float64(message.FourCCAV1),
|
||||
},
|
||||
{
|
||||
K: "encoder",
|
||||
V: "Lavf60.10.101",
|
||||
Key: "encoder",
|
||||
Value: "Lavf60.10.101",
|
||||
},
|
||||
{
|
||||
K: "filesize",
|
||||
V: float64(0),
|
||||
Key: "filesize",
|
||||
Value: float64(0),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -634,22 +634,22 @@ func TestReadTracks(t *testing.T) {
|
||||
Payload: []interface{}{
|
||||
"@setDataFrame",
|
||||
"onMetaData",
|
||||
flvio.AMFMap{
|
||||
amf0.Object{
|
||||
{
|
||||
K: "width",
|
||||
V: float64(1280),
|
||||
Key: "width",
|
||||
Value: float64(1280),
|
||||
},
|
||||
{
|
||||
K: "height",
|
||||
V: float64(720),
|
||||
Key: "height",
|
||||
Value: float64(720),
|
||||
},
|
||||
{
|
||||
K: "framerate",
|
||||
V: float64(30),
|
||||
Key: "framerate",
|
||||
Value: float64(30),
|
||||
},
|
||||
{
|
||||
K: "audiocodecid",
|
||||
V: float64(10),
|
||||
Key: "audiocodecid",
|
||||
Value: float64(10),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -695,34 +695,34 @@ func TestReadTracks(t *testing.T) {
|
||||
Payload: []interface{}{
|
||||
"@setDataFrame",
|
||||
"onMetaData",
|
||||
flvio.AMFMap{
|
||||
amf0.Object{
|
||||
{
|
||||
K: "audiodatarate",
|
||||
V: float64(128),
|
||||
Key: "audiodatarate",
|
||||
Value: float64(128),
|
||||
},
|
||||
{
|
||||
K: "framerate",
|
||||
V: float64(30),
|
||||
Key: "framerate",
|
||||
Value: float64(30),
|
||||
},
|
||||
{
|
||||
K: "videocodecid",
|
||||
V: float64(7),
|
||||
Key: "videocodecid",
|
||||
Value: float64(7),
|
||||
},
|
||||
{
|
||||
K: "videodatarate",
|
||||
V: float64(2500),
|
||||
Key: "videodatarate",
|
||||
Value: float64(2500),
|
||||
},
|
||||
{
|
||||
K: "audiocodecid",
|
||||
V: float64(10),
|
||||
Key: "audiocodecid",
|
||||
Value: float64(10),
|
||||
},
|
||||
{
|
||||
K: "height",
|
||||
V: float64(720),
|
||||
Key: "height",
|
||||
Value: float64(720),
|
||||
},
|
||||
{
|
||||
K: "width",
|
||||
V: float64(1280),
|
||||
Key: "width",
|
||||
Value: float64(1280),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -773,11 +773,11 @@ func TestReadTracks(t *testing.T) {
|
||||
Payload: []interface{}{
|
||||
"@setDataFrame",
|
||||
"onMetaData",
|
||||
flvio.AMFMap{
|
||||
{K: "duration", V: 0},
|
||||
{K: "audiocodecid", V: 2},
|
||||
{K: "encoder", V: "Lavf58.45.100"},
|
||||
{K: "filesize", V: 0},
|
||||
amf0.Object{
|
||||
{Key: "duration", Value: float64(0)},
|
||||
{Key: "audiocodecid", Value: float64(2)},
|
||||
{Key: "encoder", Value: "Lavf58.45.100"},
|
||||
{Key: "filesize", Value: float64(0)},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -799,11 +799,11 @@ func TestReadTracks(t *testing.T) {
|
||||
Payload: []interface{}{
|
||||
"@setDataFrame",
|
||||
"onMetaData",
|
||||
flvio.AMFMap{
|
||||
{K: "duration", V: 0},
|
||||
{K: "audiocodecid", V: 7},
|
||||
{K: "encoder", V: "Lavf58.45.100"},
|
||||
{K: "filesize", V: 0},
|
||||
amf0.Object{
|
||||
{Key: "duration", Value: float64(0)},
|
||||
{Key: "audiocodecid", Value: float64(7)},
|
||||
{Key: "encoder", Value: "Lavf58.45.100"},
|
||||
{Key: "filesize", Value: float64(0)},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -834,11 +834,11 @@ func TestReadTracks(t *testing.T) {
|
||||
Payload: []interface{}{
|
||||
"@setDataFrame",
|
||||
"onMetaData",
|
||||
flvio.AMFMap{
|
||||
{K: "duration", V: 0},
|
||||
{K: "audiocodecid", V: 8},
|
||||
{K: "encoder", V: "Lavf58.45.100"},
|
||||
{K: "filesize", V: 0},
|
||||
amf0.Object{
|
||||
{Key: "duration", Value: float64(0)},
|
||||
{Key: "audiocodecid", Value: float64(8)},
|
||||
{Key: "encoder", Value: "Lavf58.45.100"},
|
||||
{Key: "filesize", Value: float64(0)},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -869,10 +869,10 @@ func TestReadTracks(t *testing.T) {
|
||||
Payload: []interface{}{
|
||||
"@setDataFrame",
|
||||
"onMetaData",
|
||||
flvio.AMFMap{
|
||||
{K: "duration", V: 0},
|
||||
{K: "audiocodecid", V: 3},
|
||||
{K: "filesize", V: 0},
|
||||
amf0.Object{
|
||||
{Key: "duration", Value: float64(0)},
|
||||
{Key: "audiocodecid", Value: float64(3)},
|
||||
{Key: "filesize", Value: float64(0)},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@@ -7,8 +7,8 @@ import (
|
||||
"github.com/bluenviron/mediacommon/pkg/codecs/h264"
|
||||
"github.com/bluenviron/mediacommon/pkg/codecs/mpeg1audio"
|
||||
"github.com/bluenviron/mediacommon/pkg/codecs/mpeg4audio"
|
||||
"github.com/notedit/rtmp/format/flv/flvio"
|
||||
|
||||
"github.com/bluenviron/mediamtx/internal/protocols/rtmp/amf0"
|
||||
"github.com/bluenviron/mediamtx/internal/protocols/rtmp/h264conf"
|
||||
"github.com/bluenviron/mediamtx/internal/protocols/rtmp/message"
|
||||
)
|
||||
@@ -69,14 +69,14 @@ func (w *Writer) writeTracks(videoTrack format.Format, audioTrack format.Format)
|
||||
Payload: []interface{}{
|
||||
"@setDataFrame",
|
||||
"onMetaData",
|
||||
flvio.AMFMap{
|
||||
amf0.Object{
|
||||
{
|
||||
K: "videodatarate",
|
||||
V: float64(0),
|
||||
Key: "videodatarate",
|
||||
Value: float64(0),
|
||||
},
|
||||
{
|
||||
K: "videocodecid",
|
||||
V: func() float64 {
|
||||
Key: "videocodecid",
|
||||
Value: func() float64 {
|
||||
switch videoTrack.(type) {
|
||||
case *format.H264:
|
||||
return message.CodecH264
|
||||
@@ -87,12 +87,12 @@ func (w *Writer) writeTracks(videoTrack format.Format, audioTrack format.Format)
|
||||
}(),
|
||||
},
|
||||
{
|
||||
K: "audiodatarate",
|
||||
V: float64(0),
|
||||
Key: "audiodatarate",
|
||||
Value: float64(0),
|
||||
},
|
||||
{
|
||||
K: "audiocodecid",
|
||||
V: func() float64 {
|
||||
Key: "audiocodecid",
|
||||
Value: func() float64 {
|
||||
switch audioTrack.(type) {
|
||||
case *format.MPEG1Audio:
|
||||
return message.CodecMPEG1Audio
|
||||
|
@@ -6,9 +6,9 @@ import (
|
||||
|
||||
"github.com/bluenviron/gortsplib/v4/pkg/format"
|
||||
"github.com/bluenviron/mediacommon/pkg/codecs/mpeg4audio"
|
||||
"github.com/notedit/rtmp/format/flv/flvio"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/bluenviron/mediamtx/internal/protocols/rtmp/amf0"
|
||||
"github.com/bluenviron/mediamtx/internal/protocols/rtmp/bytecounter"
|
||||
"github.com/bluenviron/mediamtx/internal/protocols/rtmp/message"
|
||||
)
|
||||
@@ -56,11 +56,11 @@ func TestWriteTracks(t *testing.T) {
|
||||
Payload: []interface{}{
|
||||
"@setDataFrame",
|
||||
"onMetaData",
|
||||
flvio.AMFMap{
|
||||
{K: "videodatarate", V: float64(0)},
|
||||
{K: "videocodecid", V: float64(7)},
|
||||
{K: "audiodatarate", V: float64(0)},
|
||||
{K: "audiocodecid", V: float64(10)},
|
||||
amf0.Object{
|
||||
{Key: "videodatarate", Value: float64(0)},
|
||||
{Key: "videocodecid", Value: float64(7)},
|
||||
{Key: "audiodatarate", Value: float64(0)},
|
||||
{Key: "audiocodecid", Value: float64(10)},
|
||||
},
|
||||
},
|
||||
}, msg)
|
||||
|
Reference in New Issue
Block a user