Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
4.5 kB · 127 lines
Go
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128package api
import ( "bytes" "context" "errors" "testing" "time"
"github.com/bluenviron/gortmplib" "github.com/bluenviron/gortmplib/pkg/bytecounter" "github.com/bluenviron/gortmplib/pkg/codecs" "github.com/bluenviron/gortmplib/pkg/message" "github.com/stretchr/testify/require" "stream.place/streamplace/pkg/media")
// testSPS is a real 352x288 High profile SPS; gortmplib's writer parses it.var testSPS = []byte{ 0x67, 0x64, 0x00, 0x0c, 0xac, 0x3b, 0x50, 0xb0, 0x4b, 0x42, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x3d, 0x08,}
func TestH264ConfigMessage(t *testing.T) { sps := testSPS pps := []byte{0x68, 0xce, 0x3c, 0x80} idr := []byte{0x65, 0x88, 0x84} slice := []byte{0x41, 0x9a}
msg, ok := h264ConfigMessage([][]byte{sps, pps}, 40*time.Millisecond) require.True(t, ok) require.Equal(t, message.VideoPacketTypeConfig, msg.PacketType) require.EqualValues(t, message.CodecH264, msg.Codec) require.Equal(t, 40*time.Millisecond, msg.DTS) requireAVCConfig(t, msg, sps, pps)
for name, au := range map[string][][]byte{ "keyframe with parameter sets": {sps, pps, idr}, "slice": {slice}, "SPS alone": {sps}, "two SPS": {sps, sps, pps}, "empty NALU": {sps, {}, pps}, "empty AU": nil, } { _, ok := h264ConfigMessage(au, 0) require.False(t, ok, name) }}
// recordingConn is a gortmplib.Conn that keeps what's written to it.type recordingConn struct { msgs []message.Message}
func (c *recordingConn) BytesReceived() uint64 { return 0 }func (c *recordingConn) BytesSent() uint64 { return 0 }func (c *recordingConn) Read() (message.Message, error) { return nil, errors.New("write-only") }func (c *recordingConn) Write(msg message.Message) error { c.msgs = append(c.msgs, msg) return nil}
// What the relay writes for a stream's start as gortmplib's reader hands it// on: the replayed sequence header as an SPS/PPS access unit, then the first// keyframe at the same timestamp. The header has to go out as a sequence// header, not a frame of its own, so the keyframe is the only frame at that// timestamp.func TestRelayRTMPSessionSequenceHeader(t *testing.T) { sps := testSPS pps := []byte{0x68, 0xce, 0x3c, 0x80} idr := []byte{0x65, 0x88, 0x84, 0x00} slice := []byte{0x41, 0x9a, 0x02}
session := &media.RTMPSession{ EventChan: make(chan any, 8), VideoTrack: &gortmplib.Track{Codec: &codecs.H264{SPS: sps, PPS: pps}}, } session.EventChan <- &media.RTMPH264Data{AU: [][]byte{sps, pps}} session.EventChan <- &media.RTMPH264Data{AU: [][]byte{idr}} session.EventChan <- &media.RTMPH264Data{AU: [][]byte{slice}, PTS: 33 * time.Millisecond, DTS: 33 * time.Millisecond} close(session.EventChan)
conn := &recordingConn{} err := relayRTMPSession(context.Background(), conn, session) require.ErrorContains(t, err, "session closed")
var video []*message.Video for _, msg := range conn.msgs { if v, ok := msg.(*message.Video); ok { video = append(video, v) } } // the writer's own header for the track, the relayed one, then frames require.Len(t, video, 4) for i, want := range []struct { typ message.VideoPacketType dts time.Duration frame message.VideoFrameType }{ {message.VideoPacketTypeConfig, 0, message.VideoFrameTypeKeyFrame}, {message.VideoPacketTypeConfig, 0, message.VideoFrameTypeKeyFrame}, {message.VideoPacketTypeAU, 0, message.VideoFrameTypeKeyFrame}, {message.VideoPacketTypeAU, 33 * time.Millisecond, message.VideoFrameTypeInterFrame}, } { require.Equal(t, want.typ, video[i].PacketType, "message %d", i) require.Equal(t, want.dts, video[i].DTS, "message %d", i) require.Equal(t, want.frame, video[i].FrameType, "message %d", i) } requireAVCConfig(t, video[1], sps, pps)}
// requireAVCConfig checks that msg's sequence header survives the wire: it// is marshalled and read back through gortmplib's own parser.func requireAVCConfig(t *testing.T, msg *message.Video, sps, pps []byte) { t.Helper() var buf bytes.Buffer rw := message.NewReadWriter(&buf, bytecounter.NewReadWriter(&buf), false) require.NoError(t, rw.Write(msg)) read, err := rw.Read() require.NoError(t, err) got, ok := read.(*message.Video) require.True(t, ok) require.NotNil(t, got.AVCConfig) require.Equal(t, sps, got.AVCConfig.SequenceParameterSets[0].NALUnit) require.Equal(t, pps, got.AVCConfig.PictureParameterSets[0].NALUnit)}