Browse Source

rtmp: support clients that publish with empty metadata or no metadata (#386) (#769)

pull/846/head
aler9 4 years ago
parent
commit
983469a1f9
  1. 4
      internal/core/rtmp_conn.go
  2. 2
      internal/core/rtmp_server_test.go
  3. 2
      internal/core/rtmp_source.go
  4. 111
      internal/rtmp/conn.go
  5. 739
      internal/rtmp/conn_test.go

4
internal/core/rtmp_conn.go

@ -283,7 +283,7 @@ func (c *rtmpConn) runRead(ctx context.Context) error { @@ -283,7 +283,7 @@ func (c *rtmpConn) runRead(ctx context.Context) error {
}
c.conn.SetWriteDeadline(time.Now().Add(time.Duration(c.writeTimeout)))
err := c.conn.WriteMetadata(videoTrack, audioTrack)
err := c.conn.WriteTracks(videoTrack, audioTrack)
if err != nil {
return err
}
@ -441,7 +441,7 @@ func (c *rtmpConn) runRead(ctx context.Context) error { @@ -441,7 +441,7 @@ func (c *rtmpConn) runRead(ctx context.Context) error {
func (c *rtmpConn) runPublish(ctx context.Context) error {
c.conn.SetReadDeadline(time.Now().Add(time.Duration(c.readTimeout)))
videoTrack, audioTrack, err := c.conn.ReadMetadata()
videoTrack, audioTrack, err := c.conn.ReadTracks()
if err != nil {
return err
}

2
internal/core/rtmp_server_test.go

@ -142,7 +142,7 @@ func TestRTMPServerAuth(t *testing.T) { @@ -142,7 +142,7 @@ func TestRTMPServerAuth(t *testing.T) {
err = conn.ClientHandshake()
require.NoError(t, err)
_, _, err = conn.ReadMetadata()
_, _, err = conn.ReadTracks()
require.NoError(t, err)
})
}

2
internal/core/rtmp_source.go

@ -124,7 +124,7 @@ func (s *rtmpSource) runInner() bool { @@ -124,7 +124,7 @@ func (s *rtmpSource) runInner() bool {
conn.SetWriteDeadline(time.Time{})
conn.SetReadDeadline(time.Now().Add(time.Duration(s.readTimeout)))
videoTrack, audioTrack, err := conn.ReadMetadata()
videoTrack, audioTrack, err := conn.ReadTracks()
if err != nil {
return err
}

111
internal/rtmp/conn.go

@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
package rtmp
import (
"errors"
"fmt"
"net"
"net/url"
@ -81,41 +82,32 @@ func (c *Conn) WritePacket(pkt av.Packet) error { @@ -81,41 +82,32 @@ func (c *Conn) WritePacket(pkt av.Packet) error {
return c.rconn.FlushWrite()
}
// ReadMetadata reads track informations.
func (c *Conn) ReadMetadata() (*gortsplib.TrackH264, *gortsplib.TrackAAC, error) {
var videoTrack *gortsplib.TrackH264
var audioTrack *gortsplib.TrackAAC
md, err := func() (flvio.AMFMap, error) {
pkt, err := c.ReadPacket()
if err != nil {
return nil, err
}
if pkt.Type != av.Metadata {
return nil, fmt.Errorf("first packet must be metadata")
}
arr, err := flvio.ParseAMFVals(pkt.Data, false)
if err != nil {
return nil, err
}
func trackFromH264DecoderConfig(data []byte) (*gortsplib.TrackH264, error) {
codec, err := nh264.FromDecoderConfig(data)
if err != nil {
return nil, err
}
if len(arr) != 1 {
return nil, fmt.Errorf("invalid metadata")
}
return gortsplib.NewTrackH264(96, codec.SPS[0], codec.PPS[0], nil)
}
ma, ok := arr[0].(flvio.AMFMap)
if !ok {
return nil, fmt.Errorf("invalid metadata")
}
var errEmptyMetadata = errors.New("metadata is empty")
return ma, nil
}()
func (c *Conn) readTracksFromMetadata(pkt av.Packet) (*gortsplib.TrackH264, *gortsplib.TrackAAC, error) {
arr, err := flvio.ParseAMFVals(pkt.Data, false)
if err != nil {
return nil, nil, err
}
if len(arr) != 1 {
return nil, nil, fmt.Errorf("invalid metadata")
}
md, ok := arr[0].(flvio.AMFMap)
if !ok {
return nil, nil, fmt.Errorf("invalid metadata")
}
hasVideo, err := func() (bool, error) {
v, ok := md.GetV("videocodecid")
if !ok {
@ -173,9 +165,12 @@ func (c *Conn) ReadMetadata() (*gortsplib.TrackH264, *gortsplib.TrackAAC, error) @@ -173,9 +165,12 @@ func (c *Conn) ReadMetadata() (*gortsplib.TrackH264, *gortsplib.TrackAAC, error)
}
if !hasVideo && !hasAudio {
return nil, nil, fmt.Errorf("stream doesn't contain tracks with supported codecs (H264 or AAC)")
return nil, nil, errEmptyMetadata
}
var videoTrack *gortsplib.TrackH264
var audioTrack *gortsplib.TrackAAC
for {
var pkt av.Packet
pkt, err = c.ReadPacket()
@ -193,12 +188,7 @@ func (c *Conn) ReadMetadata() (*gortsplib.TrackH264, *gortsplib.TrackAAC, error) @@ -193,12 +188,7 @@ func (c *Conn) ReadMetadata() (*gortsplib.TrackH264, *gortsplib.TrackAAC, error)
return nil, nil, fmt.Errorf("video track setupped twice")
}
codec, err := nh264.FromDecoderConfig(pkt.Data)
if err != nil {
return nil, nil, err
}
videoTrack, err = gortsplib.NewTrackH264(96, codec.SPS[0], codec.PPS[0], nil)
videoTrack, err = trackFromH264DecoderConfig(pkt.Data)
if err != nil {
return nil, nil, err
}
@ -232,8 +222,55 @@ func (c *Conn) ReadMetadata() (*gortsplib.TrackH264, *gortsplib.TrackAAC, error) @@ -232,8 +222,55 @@ func (c *Conn) ReadMetadata() (*gortsplib.TrackH264, *gortsplib.TrackAAC, error)
}
}
// WriteMetadata writes track informations.
func (c *Conn) WriteMetadata(videoTrack *gortsplib.TrackH264, audioTrack *gortsplib.TrackAAC) error {
// ReadTracks reads track informations.
func (c *Conn) ReadTracks() (*gortsplib.TrackH264, *gortsplib.TrackAAC, error) {
pkt, err := c.ReadPacket()
if err != nil {
return nil, nil, err
}
switch pkt.Type {
case av.Metadata:
videoTrack, audioTrack, err := c.readTracksFromMetadata(pkt)
if err != nil {
if err == errEmptyMetadata {
pkt, err := c.ReadPacket()
if err != nil {
return nil, nil, err
}
if pkt.Type != av.H264DecoderConfig {
return nil, nil, fmt.Errorf("unexpected packet (%v)", pkt.Type)
}
videoTrack, err := trackFromH264DecoderConfig(pkt.Data)
if err != nil {
return nil, nil, err
}
return videoTrack, nil, nil
}
return nil, nil, err
}
return videoTrack, audioTrack, nil
case av.H264DecoderConfig:
videoTrack, err := trackFromH264DecoderConfig(pkt.Data)
if err != nil {
return nil, nil, err
}
return videoTrack, nil, nil
default:
return nil, nil, fmt.Errorf("unexpected packet (%v)", pkt.Type)
}
}
// WriteTracks writes track informations.
func (c *Conn) WriteTracks(videoTrack *gortsplib.TrackH264, audioTrack *gortsplib.TrackAAC) error {
err := c.WritePacket(av.Packet{
Type: av.Metadata,
Data: flvio.FillAMF0ValMalloc(flvio.AMFMap{

739
internal/rtmp/conn_test.go

@ -237,321 +237,434 @@ func (m chunk3) write(w io.Writer) error { @@ -237,321 +237,434 @@ func (m chunk3) write(w io.Writer) error {
return err
}
func TestReadMetadata(t *testing.T) {
ln, err := net.Listen("tcp", "127.0.0.1:9121")
require.NoError(t, err)
defer ln.Close()
done := make(chan struct{})
go func() {
conn, err := ln.Accept()
require.NoError(t, err)
defer conn.Close()
rconn := NewServerConn(conn)
err = rconn.ServerHandshake()
require.NoError(t, err)
videoTrack, audioTrack, err := rconn.ReadMetadata()
require.NoError(t, err)
videoTrack2, err := gortsplib.NewTrackH264(96,
[]byte{
0x67, 0x64, 0x00, 0x0c, 0xac, 0x3b, 0x50, 0xb0,
0x4b, 0x42, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00,
0x00, 0x03, 0x00, 0x3d, 0x08,
},
[]byte{
0x68, 0xee, 0x3c, 0x80,
},
nil)
require.NoError(t, err)
require.Equal(t, videoTrack2, videoTrack)
audioTrack2, err := gortsplib.NewTrackAAC(96, 2, 44100, 2, nil)
require.NoError(t, err)
require.Equal(t, audioTrack2, audioTrack)
close(done)
}()
conn, err := net.Dial("tcp", "127.0.0.1:9121")
require.NoError(t, err)
defer conn.Close()
// C->S handshake C0+C1
err = writeHandshakeC0C1(conn)
require.NoError(t, err)
// S->C handshake S0+S1+S2
s0s1s2 := make([]byte, 1536*2+1)
_, err = conn.Read(s0s1s2)
require.NoError(t, err)
// C->S handshake C2
err = writeHandshakeC2(conn, s0s1s2)
require.NoError(t, err)
// C->S connect
byts := flvio.FillAMF0ValsMalloc([]interface{}{
"connect",
1,
flvio.AMFMap{
{K: "app", V: "/stream"},
{K: "flashVer", V: "LNX 9,0,124,2"},
{K: "tcUrl", V: getTcURL("rtmp://127.0.0.1:9121/stream")},
{K: "fpad", V: false},
{K: "capabilities", V: 15},
{K: "audioCodecs", V: 4071},
{K: "videoCodecs", V: 252},
{K: "videoFunction", V: 1},
},
})
err = chunk0{
chunkStreamID: 3,
typ: 0x14,
bodyLen: uint32(len(byts)),
body: byts[:128],
}.write(conn)
require.NoError(t, err)
err = chunk3{
chunkStreamID: 3,
body: byts[128:],
}.write(conn)
require.NoError(t, err)
// S->C window acknowledgement size
var c0 chunk0
err = c0.read(conn, 128)
require.NoError(t, err)
require.Equal(t, chunk0{
chunkStreamID: 2,
typ: 5,
bodyLen: 4,
body: []byte{0x00, 38, 37, 160},
}, c0)
// S->C set peer bandwidth
err = c0.read(conn, 128)
require.NoError(t, err)
require.Equal(t, chunk0{
chunkStreamID: 2,
typ: 6,
bodyLen: 5,
body: []byte{0x00, 0x26, 0x25, 0xa0, 0x02},
}, c0)
// S->C set chunk size
err = c0.read(conn, 128)
require.NoError(t, err)
require.Equal(t, chunk0{
chunkStreamID: 2,
typ: 1,
bodyLen: 4,
body: []byte{0x00, 0x01, 0x00, 0x00},
}, c0)
// S->C result
err = c0.read(conn, 65536)
require.NoError(t, err)
require.Equal(t, uint8(3), c0.chunkStreamID)
require.Equal(t, uint8(0x14), c0.typ)
arr, err := flvio.ParseAMFVals(c0.body, false)
require.NoError(t, err)
require.Equal(t, []interface{}{
"_result",
float64(1),
flvio.AMFMap{
{K: "fmsVer", V: "LNX 9,0,124,2"},
{K: "capabilities", V: float64(31)},
},
flvio.AMFMap{
{K: "level", V: "status"},
{K: "code", V: "NetConnection.Connect.Success"},
{K: "description", V: "Connection succeeded."},
{K: "objectEncoding", V: float64(0)},
},
}, arr)
// C->S set chunk size
err = chunk0{
chunkStreamID: 2,
typ: 1,
bodyLen: 4,
body: []byte{0x00, 0x01, 0x00, 0x00},
}.write(conn)
require.NoError(t, err)
// C->S releaseStream
err = chunk1{
chunkStreamID: 3,
typ: 0x14,
body: flvio.FillAMF0ValsMalloc([]interface{}{
"releaseStream",
float64(2),
nil,
"",
}),
}.write(conn)
require.NoError(t, err)
// C->S FCPublish
err = chunk1{
chunkStreamID: 3,
typ: 0x14,
body: flvio.FillAMF0ValsMalloc([]interface{}{
"FCPublish",
float64(3),
nil,
"",
}),
}.write(conn)
require.NoError(t, err)
// C->S createStream
err = chunk3{
chunkStreamID: 3,
body: flvio.FillAMF0ValsMalloc([]interface{}{
"createStream",
float64(4),
nil,
}),
}.write(conn)
require.NoError(t, err)
// S->C result
err = c0.read(conn, 65536)
require.NoError(t, err)
require.Equal(t, uint8(3), c0.chunkStreamID)
require.Equal(t, uint8(0x14), c0.typ)
arr, err = flvio.ParseAMFVals(c0.body, false)
require.NoError(t, err)
require.Equal(t, []interface{}{
"_result",
float64(4),
nil,
float64(1),
}, arr)
// C->S publish
byts = flvio.FillAMF0ValsMalloc([]interface{}{
"publish",
float64(5),
nil,
"",
"live",
})
err = chunk0{
chunkStreamID: 8,
typ: 0x14,
streamID: 1,
bodyLen: uint32(len(byts)),
body: byts,
}.write(conn)
require.NoError(t, err)
// S->C onStatus
err = c0.read(conn, 65536)
require.NoError(t, err)
require.Equal(t, uint8(5), c0.chunkStreamID)
require.Equal(t, uint8(0x14), c0.typ)
arr, err = flvio.ParseAMFVals(c0.body, false)
require.NoError(t, err)
require.Equal(t, []interface{}{
"onStatus",
float64(5),
nil,
flvio.AMFMap{
{K: "level", V: "status"},
{K: "code", V: "NetStream.Publish.Start"},
{K: "description", V: "publish start"},
},
}, arr)
// C->S metadata
byts = flvio.FillAMF0ValsMalloc([]interface{}{
"@setDataFrame",
"onMetaData",
flvio.AMFMap{
{
K: "videodatarate",
V: float64(0),
},
{
K: "videocodecid",
V: float64(codecH264),
},
{
K: "audiodatarate",
V: float64(0),
},
{
K: "audiocodecid",
V: float64(codecAAC),
},
},
})
err = chunk0{
chunkStreamID: 4,
typ: 0x12,
streamID: 1,
bodyLen: uint32(len(byts)),
body: byts,
}.write(conn)
require.NoError(t, err)
// C->S H264 decoder config
codec := nh264.Codec{
SPS: map[int][]byte{
0: {
0x67, 0x64, 0x00, 0x0c, 0xac, 0x3b, 0x50, 0xb0,
0x4b, 0x42, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00,
0x00, 0x03, 0x00, 0x3d, 0x08,
},
},
PPS: map[int][]byte{
0: {
0x68, 0xee, 0x3c, 0x80,
},
},
func TestReadTracks(t *testing.T) {
for _, ca := range []string{
"standard",
"empty metadata",
"no metadata",
} {
t.Run(ca, func(t *testing.T) {
ln, err := net.Listen("tcp", "127.0.0.1:9121")
require.NoError(t, err)
defer ln.Close()
done := make(chan struct{})
go func() {
conn, err := ln.Accept()
require.NoError(t, err)
defer conn.Close()
rconn := NewServerConn(conn)
err = rconn.ServerHandshake()
require.NoError(t, err)
videoTrack, audioTrack, err := rconn.ReadTracks()
require.NoError(t, err)
switch ca {
case "standard":
videoTrack2, err := gortsplib.NewTrackH264(96,
[]byte{
0x67, 0x64, 0x00, 0x0c, 0xac, 0x3b, 0x50, 0xb0,
0x4b, 0x42, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00,
0x00, 0x03, 0x00, 0x3d, 0x08,
},
[]byte{
0x68, 0xee, 0x3c, 0x80,
},
nil)
require.NoError(t, err)
require.Equal(t, videoTrack2, videoTrack)
audioTrack2, err := gortsplib.NewTrackAAC(96, 2, 44100, 2, nil)
require.NoError(t, err)
require.Equal(t, audioTrack2, audioTrack)
case "empty metadata":
videoTrack2, err := gortsplib.NewTrackH264(96,
[]byte{
0x67, 0x64, 0x00, 0x32, 0xac, 0x2c, 0x6a, 0x80,
0xa8, 0x02, 0xfe, 0x9b, 0x82, 0x80, 0x82, 0xa0,
0x00, 0x00, 0x03, 0x00, 0x20, 0x00, 0x00, 0x06,
0x50, 0x80,
},
[]byte{
0x68, 0xee, 0x31, 0xb2, 0x1b,
},
nil)
require.NoError(t, err)
require.Equal(t, videoTrack2, videoTrack)
require.Equal(t, (*gortsplib.TrackAAC)(nil), audioTrack)
case "no metadata":
videoTrack2, err := gortsplib.NewTrackH264(96,
[]byte{
0x27, 0x42, 0xe0, 0x1f, 0x8d, 0x68, 0x05, 0x00,
0x5b, 0xa1, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00,
0x00, 0x03, 0x00, 0x1e, 0x0f, 0x10, 0x7a, 0x80,
},
[]byte{
0x28, 0xce, 0x32, 0x48,
},
nil)
require.NoError(t, err)
require.Equal(t, videoTrack2, videoTrack)
require.Equal(t, (*gortsplib.TrackAAC)(nil), audioTrack)
}
close(done)
}()
conn, err := net.Dial("tcp", "127.0.0.1:9121")
require.NoError(t, err)
defer conn.Close()
// C->S handshake C0+C1
err = writeHandshakeC0C1(conn)
require.NoError(t, err)
// S->C handshake S0+S1+S2
s0s1s2 := make([]byte, 1536*2+1)
_, err = conn.Read(s0s1s2)
require.NoError(t, err)
// C->S handshake C2
err = writeHandshakeC2(conn, s0s1s2)
require.NoError(t, err)
// C->S connect
byts := flvio.FillAMF0ValsMalloc([]interface{}{
"connect",
1,
flvio.AMFMap{
{K: "app", V: "/stream"},
{K: "flashVer", V: "LNX 9,0,124,2"},
{K: "tcUrl", V: getTcURL("rtmp://127.0.0.1:9121/stream")},
{K: "fpad", V: false},
{K: "capabilities", V: 15},
{K: "audioCodecs", V: 4071},
{K: "videoCodecs", V: 252},
{K: "videoFunction", V: 1},
},
})
err = chunk0{
chunkStreamID: 3,
typ: 0x14,
bodyLen: uint32(len(byts)),
body: byts[:128],
}.write(conn)
require.NoError(t, err)
err = chunk3{
chunkStreamID: 3,
body: byts[128:],
}.write(conn)
require.NoError(t, err)
// S->C window acknowledgement size
var c0 chunk0
err = c0.read(conn, 128)
require.NoError(t, err)
require.Equal(t, chunk0{
chunkStreamID: 2,
typ: 5,
bodyLen: 4,
body: []byte{0x00, 38, 37, 160},
}, c0)
// S->C set peer bandwidth
err = c0.read(conn, 128)
require.NoError(t, err)
require.Equal(t, chunk0{
chunkStreamID: 2,
typ: 6,
bodyLen: 5,
body: []byte{0x00, 0x26, 0x25, 0xa0, 0x02},
}, c0)
// S->C set chunk size
err = c0.read(conn, 128)
require.NoError(t, err)
require.Equal(t, chunk0{
chunkStreamID: 2,
typ: 1,
bodyLen: 4,
body: []byte{0x00, 0x01, 0x00, 0x00},
}, c0)
// S->C result
err = c0.read(conn, 65536)
require.NoError(t, err)
require.Equal(t, uint8(3), c0.chunkStreamID)
require.Equal(t, uint8(0x14), c0.typ)
arr, err := flvio.ParseAMFVals(c0.body, false)
require.NoError(t, err)
require.Equal(t, []interface{}{
"_result",
float64(1),
flvio.AMFMap{
{K: "fmsVer", V: "LNX 9,0,124,2"},
{K: "capabilities", V: float64(31)},
},
flvio.AMFMap{
{K: "level", V: "status"},
{K: "code", V: "NetConnection.Connect.Success"},
{K: "description", V: "Connection succeeded."},
{K: "objectEncoding", V: float64(0)},
},
}, arr)
// C->S set chunk size
err = chunk0{
chunkStreamID: 2,
typ: 1,
bodyLen: 4,
body: []byte{0x00, 0x01, 0x00, 0x00},
}.write(conn)
require.NoError(t, err)
// C->S releaseStream
err = chunk1{
chunkStreamID: 3,
typ: 0x14,
body: flvio.FillAMF0ValsMalloc([]interface{}{
"releaseStream",
float64(2),
nil,
"",
}),
}.write(conn)
require.NoError(t, err)
// C->S FCPublish
err = chunk1{
chunkStreamID: 3,
typ: 0x14,
body: flvio.FillAMF0ValsMalloc([]interface{}{
"FCPublish",
float64(3),
nil,
"",
}),
}.write(conn)
require.NoError(t, err)
// C->S createStream
err = chunk3{
chunkStreamID: 3,
body: flvio.FillAMF0ValsMalloc([]interface{}{
"createStream",
float64(4),
nil,
}),
}.write(conn)
require.NoError(t, err)
// S->C result
err = c0.read(conn, 65536)
require.NoError(t, err)
require.Equal(t, uint8(3), c0.chunkStreamID)
require.Equal(t, uint8(0x14), c0.typ)
arr, err = flvio.ParseAMFVals(c0.body, false)
require.NoError(t, err)
require.Equal(t, []interface{}{
"_result",
float64(4),
nil,
float64(1),
}, arr)
// C->S publish
byts = flvio.FillAMF0ValsMalloc([]interface{}{
"publish",
float64(5),
nil,
"",
"live",
})
err = chunk0{
chunkStreamID: 8,
typ: 0x14,
streamID: 1,
bodyLen: uint32(len(byts)),
body: byts,
}.write(conn)
require.NoError(t, err)
// S->C onStatus
err = c0.read(conn, 65536)
require.NoError(t, err)
require.Equal(t, uint8(5), c0.chunkStreamID)
require.Equal(t, uint8(0x14), c0.typ)
arr, err = flvio.ParseAMFVals(c0.body, false)
require.NoError(t, err)
require.Equal(t, []interface{}{
"onStatus",
float64(5),
nil,
flvio.AMFMap{
{K: "level", V: "status"},
{K: "code", V: "NetStream.Publish.Start"},
{K: "description", V: "publish start"},
},
}, arr)
switch ca {
case "standard":
// C->S metadata
byts = flvio.FillAMF0ValsMalloc([]interface{}{
"@setDataFrame",
"onMetaData",
flvio.AMFMap{
{
K: "videodatarate",
V: float64(0),
},
{
K: "videocodecid",
V: float64(codecH264),
},
{
K: "audiodatarate",
V: float64(0),
},
{
K: "audiocodecid",
V: float64(codecAAC),
},
},
})
err = chunk0{
chunkStreamID: 4,
typ: 0x12,
streamID: 1,
bodyLen: uint32(len(byts)),
body: byts,
}.write(conn)
require.NoError(t, err)
// C->S H264 decoder config
codec := nh264.Codec{
SPS: map[int][]byte{
0: {
0x67, 0x64, 0x00, 0x0c, 0xac, 0x3b, 0x50, 0xb0,
0x4b, 0x42, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00,
0x00, 0x03, 0x00, 0x3d, 0x08,
},
},
PPS: map[int][]byte{
0: {
0x68, 0xee, 0x3c, 0x80,
},
},
}
b := make([]byte, 128)
var n int
codec.ToConfig(b, &n)
decConfig := b[:n]
err = chunk0{
chunkStreamID: 6,
typ: flvio.TAG_VIDEO,
streamID: 1,
bodyLen: uint32(len(decConfig) + 5),
body: append([]byte{flvio.FRAME_KEY<<4 | flvio.VIDEO_H264, 0, 0, 0, 0}, decConfig...),
}.write(conn)
require.NoError(t, err)
// C->S AAC decoder config
enc, err := aac.MPEG4AudioConfig{
Type: 2,
SampleRate: 44100,
ChannelCount: 2,
}.Encode()
require.NoError(t, err)
err = chunk0{
chunkStreamID: 4,
typ: flvio.TAG_AUDIO,
streamID: 1,
bodyLen: uint32(len(enc) + 2),
body: append([]byte{
flvio.SOUND_AAC<<4 | flvio.SOUND_44Khz<<2 | flvio.SOUND_16BIT<<1 | flvio.SOUND_STEREO,
flvio.AAC_SEQHDR,
}, enc...),
}.write(conn)
require.NoError(t, err)
case "empty metadata":
// C->S metadata
byts = flvio.FillAMF0ValsMalloc([]interface{}{
"@setDataFrame",
"onMetaData",
flvio.AMFMap{
{
K: "width",
V: float64(2688),
},
{
K: "height",
V: float64(1520),
},
{
K: "framerate",
V: float64(0o25),
},
},
})
err = chunk0{
chunkStreamID: 4,
typ: 0x12,
streamID: 1,
bodyLen: uint32(len(byts)),
body: byts,
}.write(conn)
require.NoError(t, err)
// C->S H264 decoder config
byts := []byte{
0x17, 0x00, 0x00, 0x00, 0x00, 0x01, 0x64, 0x00,
0x32, 0xff, 0xe1, 0x00, 0x1a, 0x67, 0x64, 0x00,
0x32, 0xac, 0x2c, 0x6a, 0x80, 0xa8, 0x02, 0xfe,
0x9b, 0x82, 0x80, 0x82, 0xa0, 0x00, 0x00, 0x03,
0x00, 0x20, 0x00, 0x00, 0x06, 0x50, 0x80, 0x01,
0x00, 0x05, 0x68, 0xee, 0x31, 0xb2, 0x1b,
}
err = chunk0{
chunkStreamID: 21,
typ: 0x09,
streamID: 1,
bodyLen: uint32(len(byts)),
body: byts,
}.write(conn)
require.NoError(t, err)
case "no metadata":
// C->S H264 decoder config
byts := []byte{
0x17, 0x00, 0x00, 0x00, 0x00, 0x01, 0x42, 0xe0,
0x1f, 0xff, 0xe1, 0x00, 0x18, 0x27, 0x42, 0xe0,
0x1f, 0x8d, 0x68, 0x05, 0x00, 0x5b, 0xa1, 0x00,
0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x03, 0x00,
0x1e, 0x0f, 0x10, 0x7a, 0x80, 0x01, 0x00, 0x04,
0x28, 0xce, 0x32, 0x48,
}
err = chunk0{
chunkStreamID: 4,
typ: 0x09,
streamID: 1,
bodyLen: uint32(len(byts)),
body: byts,
}.write(conn)
require.NoError(t, err)
}
<-done
})
}
b := make([]byte, 128)
var n int
codec.ToConfig(b, &n)
decConfig := b[:n]
err = chunk0{
chunkStreamID: 6,
typ: flvio.TAG_VIDEO,
streamID: 1,
bodyLen: uint32(len(decConfig) + 5),
body: append([]byte{flvio.FRAME_KEY<<4 | flvio.VIDEO_H264, 0, 0, 0, 0}, decConfig...),
}.write(conn)
require.NoError(t, err)
// C->S AAC decoder config
enc, err := aac.MPEG4AudioConfig{
Type: 2,
SampleRate: 44100,
ChannelCount: 2,
}.Encode()
require.NoError(t, err)
err = chunk0{
chunkStreamID: 4,
typ: flvio.TAG_AUDIO,
streamID: 1,
bodyLen: uint32(len(enc) + 2),
body: append([]byte{
flvio.SOUND_AAC<<4 | flvio.SOUND_44Khz<<2 | flvio.SOUND_16BIT<<1 | flvio.SOUND_STEREO,
flvio.AAC_SEQHDR,
}, enc...),
}.write(conn)
require.NoError(t, err)
<-done
}
func TestWriteMetadata(t *testing.T) {
func TestWriteTracks(t *testing.T) {
ln, err := net.Listen("tcp", "127.0.0.1:9121")
require.NoError(t, err)
defer ln.Close()
@ -580,7 +693,7 @@ func TestWriteMetadata(t *testing.T) { @@ -580,7 +693,7 @@ func TestWriteMetadata(t *testing.T) {
audioTrack, err := gortsplib.NewTrackAAC(96, 2, 44100, 2, nil)
require.NoError(t, err)
err = rconn.WriteMetadata(videoTrack, audioTrack)
err = rconn.WriteTracks(videoTrack, audioTrack)
require.NoError(t, err)
}()

Loading…
Cancel
Save