Files
livekit/pkg/sfu/buffer/buffer_test.go
Raja Subramanian 9db2bd22df LK logger with context in SFU (#391)
* LK logger with context in SFU

* Move buff.SetLogger into sfu.WebRTCReceiver
2022-02-01 08:57:09 +05:30

308 lines
7.1 KiB
Go

package buffer
import (
"sync"
"testing"
"time"
"github.com/pion/rtcp"
"github.com/pion/rtp"
"github.com/pion/webrtc/v3"
"github.com/stretchr/testify/assert"
)
var vp8Codec = webrtc.RTPCodecParameters{
RTPCodecCapability: webrtc.RTPCodecCapability{
MimeType: "video/vp8",
ClockRate: 90000,
RTCPFeedback: []webrtc.RTCPFeedback{{
Type: "nack",
}},
},
PayloadType: 96,
}
var opusCodec = webrtc.RTPCodecParameters{
RTPCodecCapability: webrtc.RTPCodecCapability{
MimeType: "audio/opus",
ClockRate: 48000,
},
PayloadType: 96,
}
func TestNack(t *testing.T) {
pool := &sync.Pool{
New: func() interface{} {
b := make([]byte, 1500)
return &b
},
}
t.Run("nack normal", func(t *testing.T) {
buff := NewBuffer(123, pool, pool)
buff.codecType = webrtc.RTPCodecTypeVideo
assert.NotNil(t, buff)
var wg sync.WaitGroup
// 3 nacks 1 Pli
wg.Add(4)
buff.OnFeedback(func(fb []rtcp.Packet) {
for _, pkt := range fb {
switch p := pkt.(type) {
case *rtcp.TransportLayerNack:
if p.Nacks[0].PacketList()[0] == 1 && p.MediaSSRC == 123 {
wg.Done()
}
case *rtcp.PictureLossIndication:
if p.MediaSSRC == 123 {
wg.Done()
}
}
}
})
buff.Bind(webrtc.RTPParameters{
HeaderExtensions: nil,
Codecs: []webrtc.RTPCodecParameters{vp8Codec},
}, vp8Codec.RTPCodecCapability, Options{})
for i := 0; i < 15; i++ {
if i == 1 {
continue
}
pkt := rtp.Packet{
Header: rtp.Header{SequenceNumber: uint16(i), Timestamp: uint32(i)},
Payload: []byte{0xff, 0xff, 0xff, 0xfd, 0xb4, 0x9f, 0x94, 0x1},
}
b, err := pkt.Marshal()
assert.NoError(t, err)
_, err = buff.Write(b)
assert.NoError(t, err)
}
wg.Wait()
})
t.Run("nack with seq wrap", func(t *testing.T) {
buff := NewBuffer(123, pool, pool)
buff.codecType = webrtc.RTPCodecTypeVideo
assert.NotNil(t, buff)
var wg sync.WaitGroup
expects := map[uint16]int{
65534: 0,
65535: 0,
0: 0,
1: 0,
}
wg.Add(3 * len(expects)) // retry 3 times
buff.OnFeedback(func(fb []rtcp.Packet) {
for _, pkt := range fb {
switch p := pkt.(type) {
case *rtcp.TransportLayerNack:
if p.MediaSSRC == 123 {
for _, v := range p.Nacks {
v.Range(func(seq uint16) bool {
if _, ok := expects[seq]; ok {
wg.Done()
} else {
assert.Fail(t, "unexpected nack seq ", seq)
}
return true
})
}
}
case *rtcp.PictureLossIndication:
if p.MediaSSRC == 123 {
// wg.Done()
}
}
}
})
buff.Bind(webrtc.RTPParameters{
HeaderExtensions: nil,
Codecs: []webrtc.RTPCodecParameters{vp8Codec},
}, vp8Codec.RTPCodecCapability, Options{})
for i := 0; i < 15; i++ {
if i > 0 && i < 5 {
continue
}
pkt := rtp.Packet{
Header: rtp.Header{SequenceNumber: uint16(i + 65533), Timestamp: uint32(i)},
Payload: []byte{0xff, 0xff, 0xff, 0xfd, 0xb4, 0x9f, 0x94, 0x1},
}
b, err := pkt.Marshal()
assert.NoError(t, err)
_, err = buff.Write(b)
assert.NoError(t, err)
}
wg.Wait()
})
}
func TestNewBuffer(t *testing.T) {
type args struct {
options Options
ssrc uint32
}
tests := []struct {
name string
args args
}{
{
name: "Must not be nil and add packets in sequence",
args: args{
options: Options{
MaxBitRate: 1e6,
},
},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
var TestPackets = []*rtp.Packet{
{
Header: rtp.Header{
SequenceNumber: 65533,
},
},
{
Header: rtp.Header{
SequenceNumber: 65534,
},
},
{
Header: rtp.Header{
SequenceNumber: 2,
},
},
{
Header: rtp.Header{
SequenceNumber: 65535,
},
},
}
pool := &sync.Pool{
New: func() interface{} {
b := make([]byte, 1500)
return &b
},
}
buff := NewBuffer(123, pool, pool)
buff.codecType = webrtc.RTPCodecTypeVideo
assert.NotNil(t, buff)
assert.NotNil(t, TestPackets)
buff.OnFeedback(func(_ []rtcp.Packet) {
})
buff.Bind(webrtc.RTPParameters{
HeaderExtensions: nil,
Codecs: []webrtc.RTPCodecParameters{vp8Codec},
}, vp8Codec.RTPCodecCapability, Options{})
for _, p := range TestPackets {
buf, _ := p.Marshal()
_, _ = buff.Write(buf)
}
// assert.Equal(t, 6, buff.PacketQueue.size)
assert.Equal(t, uint32(1<<16), buff.seqHdlr.Cycles())
assert.Equal(t, uint16(2), uint16(buff.seqHdlr.MaxSeqNo()))
})
}
}
func TestFractionLostReport(t *testing.T) {
pool := &sync.Pool{
New: func() interface{} {
b := make([]byte, 1500)
return &b
},
}
buff := NewBuffer(123, pool, pool)
buff.codecType = webrtc.RTPCodecTypeVideo
assert.NotNil(t, buff)
var wg sync.WaitGroup
wg.Add(1)
buff.SetLastFractionLostReport(55)
buff.OnFeedback(func(fb []rtcp.Packet) {
for _, pkt := range fb {
switch p := pkt.(type) {
case *rtcp.ReceiverReport:
for _, v := range p.Reports {
assert.EqualValues(t, 55, v.FractionLost)
}
wg.Done()
}
}
})
buff.Bind(webrtc.RTPParameters{
HeaderExtensions: nil,
Codecs: []webrtc.RTPCodecParameters{opusCodec},
}, opusCodec.RTPCodecCapability, Options{})
for i := 0; i < 15; i++ {
pkt := rtp.Packet{
Header: rtp.Header{SequenceNumber: uint16(i), Timestamp: uint32(i)},
Payload: []byte{0xff, 0xff, 0xff, 0xfd, 0xb4, 0x9f, 0x94, 0x1},
}
b, err := pkt.Marshal()
assert.NoError(t, err)
if i == 1 {
time.Sleep(1 * time.Second)
}
_, err = buff.Write(b)
assert.NoError(t, err)
}
wg.Wait()
}
func TestSeqWrapHandler(t *testing.T) {
s := SeqWrapHandler{}
s.UpdateMaxSeq(1)
assert.Equal(t, uint32(1), s.MaxSeqNo())
type caseInfo struct {
seqs []uint32 // {seq1, seq2, unwrap of seq2}
newer bool // seq2 is newer than seq1
}
// test normal case, name -> {seq1, seq2, unwrap of seq2}
cases := map[string]caseInfo{
"no wrap": {[]uint32{1, 4, 4}, true},
"no wrap backward": {[]uint32{4, 1, 1}, false},
"wrap around forward to zero": {[]uint32{65534, 0, 65536}, true},
"wrap around forward": {[]uint32{65534, 10, 65546}, true},
"wrap around forward 2": {[]uint32{65535 + 65536*2, 1, 1 + 65536*3}, true},
"wrap around backward ": {[]uint32{5, 65534, 65534}, false},
"wrap around backward less than zero": {[]uint32{5, 65534, 65534}, false},
}
for k, v := range cases {
t.Run(k, func(t *testing.T) {
s := SeqWrapHandler{}
s.UpdateMaxSeq(v.seqs[0])
extsn, newer := s.Unwrap(uint16(v.seqs[1]))
assert.Equal(t, v.newer, newer)
assert.Equal(t, v.seqs[2], extsn)
})
}
}
func TestIsTimestampWrap(t *testing.T) {
type caseInfo struct {
name string
ts1 uint32
ts2 uint32
later bool
}
cases := []caseInfo{
{"normal case 1 timestamp later ", 2, 1, true},
{"normal case 2 timestamp later", 0x1c000000, 0x10000000, true},
{"wrap case timestamp later", 0xffff, 0xfc000000, true},
{"wrap case timestamp early", 0xfc000000, 0xffff, false},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
assert.Equal(t, c.later, IsLaterTimestamp(c.ts1, c.ts2))
})
}
}