Files
lkm/jitterbuffer/jitter_buffer_test.go
2024-05-03 22:50:45 +08:00

239 lines
8.8 KiB
Go

// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package jitterbuffer
import (
"math"
"testing"
"github.com/pion/rtp"
"github.com/stretchr/testify/assert"
)
func TestJitterBuffer(t *testing.T) {
assert := assert.New(t)
t.Run("Appends packets in order", func(*testing.T) {
jb := New()
assert.Equal(jb.lastSequence, uint16(0))
jb.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 5000, Timestamp: 500}, Payload: []byte{0x02}})
jb.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 5001, Timestamp: 501}, Payload: []byte{0x02}})
jb.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 5002, Timestamp: 502}, Payload: []byte{0x02}})
assert.Equal(jb.lastSequence, uint16(5002))
jb.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 5012, Timestamp: 512}, Payload: []byte{0x02}})
assert.Equal(jb.stats.outOfOrderCount, uint32(1))
assert.Equal(jb.packets.Length(), uint16(4))
assert.Equal(jb.lastSequence, uint16(5012))
})
t.Run("Appends packets and begins playout", func(*testing.T) {
jb := New()
for i := 0; i < 100; i++ {
jb.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: uint16(5012 + i), Timestamp: uint32(512 + i)}, Payload: []byte{0x02}})
}
assert.Equal(jb.packets.Length(), uint16(100))
assert.Equal(jb.state, Emitting)
assert.Equal(jb.playoutHead, uint16(5012))
head, err := jb.Pop()
assert.Equal(head.SequenceNumber, uint16(5012))
assert.Equal(err, nil)
})
t.Run("Appends packets and begins playout", func(*testing.T) {
jb := New(WithMinimumPacketCount(1))
events := make([]Event, 0)
jb.Listen(BeginPlayback, func(event Event, _ *JitterBuffer) {
events = append(events, event)
})
for i := 0; i < 2; i++ {
jb.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: uint16(5012 + i), Timestamp: uint32(512 + i)}, Payload: []byte{0x02}})
}
assert.Equal(jb.packets.Length(), uint16(2))
assert.Equal(jb.state, Emitting)
assert.Equal(jb.playoutHead, uint16(5012))
head, err := jb.Pop()
assert.Equal(head.SequenceNumber, uint16(5012))
assert.Equal(err, nil)
assert.Equal(1, len(events))
assert.Equal(Event(BeginPlayback), events[0])
})
t.Run("Wraps playout correctly", func(*testing.T) {
jb := New()
for i := 0; i < 100; i++ {
sqnum := uint16((math.MaxUint16 - 32 + i) % math.MaxUint16)
jb.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: sqnum, Timestamp: uint32(512 + i)}, Payload: []byte{0x02}})
}
assert.Equal(jb.packets.Length(), uint16(100))
assert.Equal(jb.state, Emitting)
assert.Equal(jb.playoutHead, uint16(math.MaxUint16-32))
head, err := jb.Pop()
assert.Equal(head.SequenceNumber, uint16(math.MaxUint16-32))
assert.Equal(err, nil)
for i := 0; i < 100; i++ {
head, err := jb.Pop()
if i < 99 {
assert.Equal(head.SequenceNumber, uint16((math.MaxUint16-31+i)%math.MaxUint16))
assert.Equal(err, nil)
} else {
assert.Equal(head, (*rtp.Packet)(nil))
}
}
})
t.Run("Pops at timestamp correctly", func(*testing.T) {
jb := New()
for i := 0; i < 100; i++ {
sqnum := uint16((math.MaxUint16 - 32 + i) % math.MaxUint16)
jb.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: sqnum, Timestamp: uint32(512 + i)}, Payload: []byte{0x02}})
}
assert.Equal(jb.packets.Length(), uint16(100))
assert.Equal(jb.state, Emitting)
head, err := jb.PopAtTimestamp(uint32(513))
assert.Equal(head.SequenceNumber, uint16(math.MaxUint16-32+1))
assert.Equal(err, nil)
head, err = jb.PopAtTimestamp(uint32(513))
assert.Equal(head, (*rtp.Packet)(nil))
assert.NotEqual(err, nil)
head, err = jb.Pop()
assert.Equal(head.SequenceNumber, uint16(math.MaxUint16-32))
assert.Equal(err, nil)
})
t.Run("Can peek at a packet", func(*testing.T) {
jb := New()
jb.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 5000, Timestamp: 500}, Payload: []byte{0x02}})
jb.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 5001, Timestamp: 501}, Payload: []byte{0x02}})
jb.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 5002, Timestamp: 502}, Payload: []byte{0x02}})
pkt, err := jb.Peek(false)
assert.Equal(pkt.SequenceNumber, uint16(5002))
assert.Equal(err, nil)
for i := 0; i < 100; i++ {
sqnum := uint16((math.MaxUint16 - 32 + i) % math.MaxUint16)
jb.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: sqnum, Timestamp: uint32(512 + i)}, Payload: []byte{0x02}})
}
pkt, err = jb.Peek(true)
assert.Equal(pkt.SequenceNumber, uint16(5000))
assert.Equal(err, nil)
})
t.Run("Pops at sequence with an invalid sequence number", func(*testing.T) {
jb := New()
for i := 0; i < 50; i++ {
sqnum := uint16((math.MaxUint16 - 32 + i) % math.MaxUint16)
jb.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: sqnum, Timestamp: uint32(512 + i)}, Payload: []byte{0x02}})
}
jb.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 1019, Timestamp: uint32(9000)}, Payload: []byte{0x02}})
jb.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 1020, Timestamp: uint32(9000)}, Payload: []byte{0x02}})
assert.Equal(jb.packets.Length(), uint16(52))
assert.Equal(jb.state, Emitting)
head, err := jb.PopAtSequence(uint16(9000))
assert.Equal(head, (*rtp.Packet)(nil))
assert.NotEqual(err, nil)
})
t.Run("Pops at timestamp with multiple packets", func(*testing.T) {
jb := New()
for i := 0; i < 50; i++ {
sqnum := uint16((math.MaxUint16 - 32 + i) % math.MaxUint16)
jb.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: sqnum, Timestamp: uint32(512 + i)}, Payload: []byte{0x02}})
}
jb.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 1019, Timestamp: uint32(9000)}, Payload: []byte{0x02}})
jb.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 1020, Timestamp: uint32(9000)}, Payload: []byte{0x02}})
assert.Equal(jb.packets.Length(), uint16(52))
assert.Equal(jb.state, Emitting)
head, err := jb.PopAtTimestamp(uint32(9000))
assert.Equal(head.SequenceNumber, uint16(1019))
assert.Equal(err, nil)
head, err = jb.PopAtTimestamp(uint32(9000))
assert.Equal(head.SequenceNumber, uint16(1020))
assert.Equal(err, nil)
head, err = jb.Pop()
assert.Equal(head.SequenceNumber, uint16(math.MaxUint16-32))
assert.Equal(err, nil)
})
t.Run("Peeks at timestamp with multiple packets", func(*testing.T) {
jb := New()
for i := 0; i < 50; i++ {
sqnum := uint16((math.MaxUint16 - 32 + i) % math.MaxUint16)
jb.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: sqnum, Timestamp: uint32(512 + i)}, Payload: []byte{0x02}})
}
jb.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 1019, Timestamp: uint32(9000)}, Payload: []byte{0x02}})
jb.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 1020, Timestamp: uint32(9000)}, Payload: []byte{0x02}})
assert.Equal(jb.packets.Length(), uint16(52))
assert.Equal(jb.state, Emitting)
head, err := jb.PeekAtSequence(uint16(1019))
assert.Equal(head.SequenceNumber, uint16(1019))
assert.Equal(err, nil)
head, err = jb.PeekAtSequence(uint16(1020))
assert.Equal(head.SequenceNumber, uint16(1020))
assert.Equal(err, nil)
head, err = jb.PopAtSequence(uint16(math.MaxUint16 - 32))
assert.Equal(head.SequenceNumber, uint16(math.MaxUint16-32))
assert.Equal(err, nil)
})
t.Run("SetPlayoutHead", func(*testing.T) {
jb := New(WithMinimumPacketCount(1))
// Push packets 0-9, but no packet 4
for i := uint16(0); i < 10; i++ {
if i == 4 {
continue
}
jb.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: i, Timestamp: uint32(512 + i)}, Payload: []byte{0x00}})
}
// The first 3 packets will be able to popped
for i := 0; i < 4; i++ {
pkt, err := jb.Pop()
assert.NoError(err)
assert.NotNil(pkt)
}
// The next pop will fail because of gap
pkt, err := jb.Pop()
assert.ErrorIs(err, ErrNotFound)
assert.Nil(pkt)
assert.Equal(jb.PlayoutHead(), uint16(4))
// Assert that PlayoutHead isn't modified with pushing/popping again
jb.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 10, Timestamp: uint32(522)}, Payload: []byte{0x00}})
pkt, err = jb.Pop()
assert.ErrorIs(err, ErrNotFound)
assert.Nil(pkt)
assert.Equal(jb.PlayoutHead(), uint16(4))
// Increment the PlayoutHead and popping will work again
jb.SetPlayoutHead(jb.PlayoutHead() + 1)
for i := 0; i < 6; i++ {
pkt, err := jb.Pop()
assert.NoError(err)
assert.NotNil(pkt)
}
})
t.Run("Allows clearing the buffer", func(*testing.T) {
jb := New()
jb.Clear(false)
assert.Equal(jb.lastSequence, uint16(0))
jb.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 5000, Timestamp: 500}, Payload: []byte{0x02}})
jb.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 5001, Timestamp: 501}, Payload: []byte{0x02}})
jb.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 5002, Timestamp: 502}, Payload: []byte{0x02}})
assert.Equal(jb.lastSequence, uint16(5002))
jb.Clear(true)
assert.Equal(jb.lastSequence, uint16(0))
assert.Equal(jb.stats.outOfOrderCount, uint32(0))
assert.Equal(jb.packets.Length(), uint16(0))
})
}