From 2863bad6bba361c8bbc38400c8cad078c81a68e3 Mon Sep 17 00:00:00 2001 From: David Chen Date: Tue, 22 Sep 2026 15:08:08 -0700 Subject: [PATCH] refactor packet generation for tests into util --- pkg/sfu/buffer/buffer_fec_test.go | 50 +++++++---------------------- pkg/sfu/testutils/rtptest/packet.go | 46 ++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 39 deletions(-) create mode 100644 pkg/sfu/testutils/rtptest/packet.go diff --git a/pkg/sfu/buffer/buffer_fec_test.go b/pkg/sfu/buffer/buffer_fec_test.go index 8d859b4dd..9f3e1e42e 100644 --- a/pkg/sfu/buffer/buffer_fec_test.go +++ b/pkg/sfu/buffer/buffer_fec_test.go @@ -18,7 +18,6 @@ import ( "crypto/aes" "crypto/cipher" "encoding/binary" - "math/rand" "testing" "time" @@ -29,6 +28,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/livekit/livekit-server/pkg/sfu/testutils/rtptest" "github.com/livekit/mediatransportutil/pkg/twcc" ) @@ -47,34 +47,6 @@ var flexFECCodec = webrtc.RTPCodecParameters{ PayloadType: webrtc.PayloadType(fecTestFECPT), } -func fecTestMediaPackets(t *testing.T, baseSN uint16, count int) []rtp.Packet { - t.Helper() - rng := rand.New(rand.NewSource(int64(baseSN))) - pkts := make([]rtp.Packet, 0, count) - for i := 0; i < count; i++ { - payload := make([]byte, 50+rng.Intn(200)) - rng.Read(payload) - // valid VP8 payload descriptor (S=1, no extensions) so the video - // packet processing in the buffer accepts the packet - payload[0] = 0x10 - sn := baseSN + uint16(i) - pkts = append(pkts, rtp.Packet{ - Header: rtp.Header{ - Version: 2, - PayloadType: uint8(vp8Codec.PayloadType), - SequenceNumber: sn, - // derive timestamp from the sequence number so windows - // generated separately stay monotonic - Timestamp: 90000 + 3000*uint32(sn), - SSRC: fecTestMediaSSRC, - Marker: i == count-1, - }, - Payload: payload, - }) - } - return pkts -} - func bindFECTestBuffer(t *testing.T, buff *Buffer) { t.Helper() buff.codecType = webrtc.RTPCodecTypeVideo @@ -193,7 +165,7 @@ func TestBufferFECRecoversDroppedPacket(t *testing.T) { receivedDelta += received }) - media := fecTestMediaPackets(t, 100, 10) + media := rtptest.GenerateVP8Packets(100, 10, uint8(vp8Codec.PayloadType), fecTestMediaSSRC) encoder := pionflexfec.NewFlexEncoder03(fecTestFECPT, fecTestFECSSRC) fecPackets := encoder.EncodeFec(media, 2) require.NotEmpty(t, fecPackets) @@ -246,7 +218,7 @@ func TestBufferFECRecoversEncryptedPayload(t *testing.T) { aead, err := cipher.NewGCM(block) require.NoError(t, err) - media := fecTestMediaPackets(t, 120, 5) + media := rtptest.GenerateVP8Packets(120, 5, uint8(vp8Codec.PayloadType), fecTestMediaSSRC) plaintexts := make([][]byte, len(media)) for i := range media { plaintexts[i] = append([]byte("encrypted-video-frame-"), byte(i)) @@ -302,7 +274,7 @@ func TestBufferFECRecoveryCallbackCanReenterBuffer(t *testing.T) { callbackDone <- struct{}{} }) - media := fecTestMediaPackets(t, 150, 5) + media := rtptest.GenerateVP8Packets(150, 5, uint8(vp8Codec.PayloadType), fecTestMediaSSRC) for i := range media { writePacket(t, primary, &media[i]) } @@ -341,7 +313,7 @@ func TestBufferFECPairAfterPackets(t *testing.T) { bindFECTestBuffer(t, primary) encoder := pionflexfec.NewFlexEncoder03(fecTestFECPT, fecTestFECSSRC) - media := fecTestMediaPackets(t, 200, 10) + media := rtptest.GenerateVP8Packets(200, 10, uint8(vp8Codec.PayloadType), fecTestMediaSSRC) fecPackets := encoder.EncodeFec(media, 2) require.NotEmpty(t, fecPackets) @@ -370,7 +342,7 @@ func TestBufferFECPairAfterPackets(t *testing.T) { assert.EqualValues(t, 1, stats.PacketsRecovered) // the next window recovers normally - media2 := fecTestMediaPackets(t, 210, 10) + media2 := rtptest.GenerateVP8Packets(210, 10, uint8(vp8Codec.PayloadType), fecTestMediaSSRC) fecPackets2 := encoder.EncodeFec(media2, 2) require.NotEmpty(t, fecPackets2) @@ -402,7 +374,7 @@ func TestBufferFECCoupledBeforeBuffersExist(t *testing.T) { fecBuff := factory.GetOrNew(packetio.RTPBufferPacket, fecTestFECSSRC).(*Buffer) bindFECTestBuffer(t, primary) - media := fecTestMediaPackets(t, 300, 5) + media := rtptest.GenerateVP8Packets(300, 5, uint8(vp8Codec.PayloadType), fecTestMediaSSRC) encoder := pionflexfec.NewFlexEncoder03(fecTestFECPT, fecTestFECSSRC) fecPackets := encoder.EncodeFec(media, 1) require.NotEmpty(t, fecPackets) @@ -433,7 +405,7 @@ func TestBufferFECSequenceNumberWrap(t *testing.T) { fecBuff := factory.GetOrNew(packetio.RTPBufferPacket, fecTestFECSSRC).(*Buffer) bindFECTestBuffer(t, primary) - media := fecTestMediaPackets(t, 65533, 5) + media := rtptest.GenerateVP8Packets(65533, 5, uint8(vp8Codec.PayloadType), fecTestMediaSSRC) for i := range media { media[i].Timestamp = 90000 + 3000*uint32(i) } @@ -463,7 +435,7 @@ func TestBufferFECRecoveryAfterPaddingRemoval(t *testing.T) { fecBuff := factory.GetOrNew(packetio.RTPBufferPacket, fecTestFECSSRC).(*Buffer) bindFECTestBuffer(t, primary) - media := fecTestMediaPackets(t, 800, 5) + media := rtptest.GenerateVP8Packets(800, 5, uint8(vp8Codec.PayloadType), fecTestMediaSSRC) fecPackets := pionflexfec.NewFlexEncoder03(fecTestFECPT, fecTestFECSSRC).EncodeFec(media, 1) require.Len(t, fecPackets, 1) @@ -532,7 +504,7 @@ func TestBufferFECIgnoresUnexpectedPayloadType(t *testing.T) { Codecs: []webrtc.RTPCodecParameters{vp8Codec}, }, vp8Codec.RTPCodecCapability, 0)) - media := fecTestMediaPackets(t, 400, 5) + media := rtptest.GenerateVP8Packets(400, 5, uint8(vp8Codec.PayloadType), fecTestMediaSSRC) encoder := pionflexfec.NewFlexEncoder03(fecTestFECPT, fecTestFECSSRC) fecPackets := encoder.EncodeFec(media, 1) require.NotEmpty(t, fecPackets) @@ -585,7 +557,7 @@ func TestBufferFECNACKSuppression(t *testing.T) { factory.SetFECPair(fecTestFECSSRC, fecTestMediaSSRC) bindFECTestBuffer(t, primary) - media := fecTestMediaPackets(t, 700, 10) + media := rtptest.GenerateVP8Packets(700, 10, uint8(vp8Codec.PayloadType), fecTestMediaSSRC) encoder := pionflexfec.NewFlexEncoder03(fecTestFECPT, fecTestFECSSRC) fecPackets := encoder.EncodeFec(media, 2) require.NotEmpty(t, fecPackets) diff --git a/pkg/sfu/testutils/rtptest/packet.go b/pkg/sfu/testutils/rtptest/packet.go new file mode 100644 index 000000000..9be018254 --- /dev/null +++ b/pkg/sfu/testutils/rtptest/packet.go @@ -0,0 +1,46 @@ +// Copyright 2026 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package rtptest + +import ( + "math/rand" + + "github.com/pion/rtp" +) + +// GenerateVP8Packets returns deterministic RTP packets with valid VP8 payload descriptors. +func GenerateVP8Packets(baseSN uint16, count int, payloadType uint8, ssrc uint32) []rtp.Packet { + rng := rand.New(rand.NewSource(int64(baseSN))) + packets := make([]rtp.Packet, 0, count) + for i := 0; i < count; i++ { + payload := make([]byte, 50+rng.Intn(200)) + rng.Read(payload) + payload[0] = 0x10 + + sequenceNumber := baseSN + uint16(i) + packets = append(packets, rtp.Packet{ + Header: rtp.Header{ + Version: 2, + PayloadType: payloadType, + SequenceNumber: sequenceNumber, + Timestamp: 90000 + 3000*uint32(sequenceNumber), + SSRC: ssrc, + Marker: i == count-1, + }, + Payload: payload, + }) + } + return packets +}