mirror of
https://github.com/livekit/livekit.git
synced 2026-04-04 06:05:45 +00:00
* Do not restart on receiver side. Restart with wrap back causes issues in the forwarding path as the subscriber assumes the extended type from receiver side does not restart. Restart was an attempt to include as many packets as possible, but in practice is not super useful. So, taking it out. Can clean up a bit more stuff, but want to run this first and check for any oddities. * fix test
270 lines
8.0 KiB
Go
270 lines
8.0 KiB
Go
// Copyright 2023 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 buffer
|
|
|
|
import (
|
|
"fmt"
|
|
"math/rand"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/livekit/protocol/logger"
|
|
"github.com/pion/rtp"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func getPacket(sn uint16, ts uint32, payloadSize int) *rtp.Packet {
|
|
return &rtp.Packet{
|
|
Header: rtp.Header{
|
|
SequenceNumber: sn,
|
|
Timestamp: ts,
|
|
},
|
|
Payload: make([]byte, payloadSize),
|
|
}
|
|
}
|
|
|
|
func Test_RTPStatsReceiver(t *testing.T) {
|
|
clockRate := uint32(90000)
|
|
r := NewRTPStatsReceiver(RTPStatsParams{
|
|
ClockRate: clockRate,
|
|
Logger: logger.GetLogger(),
|
|
})
|
|
|
|
totalDuration := 5 * time.Second
|
|
bitrate := 1000000
|
|
packetSize := 1000
|
|
pps := (((bitrate + 7) / 8) + packetSize - 1) / packetSize
|
|
framerate := 30
|
|
sleep := 1000 / framerate
|
|
packetsPerFrame := (pps + framerate - 1) / framerate
|
|
|
|
sequenceNumber := uint16(rand.Float64() * float64(1<<16))
|
|
timestamp := uint32(rand.Float64() * float64(1<<32))
|
|
now := time.Now()
|
|
startTime := now
|
|
lastFrameTime := now
|
|
for now.Sub(startTime) < totalDuration {
|
|
timestamp += uint32(now.Sub(lastFrameTime).Seconds() * float64(clockRate))
|
|
for i := 0; i < packetsPerFrame; i++ {
|
|
packet := getPacket(sequenceNumber, timestamp, packetSize)
|
|
r.Update(
|
|
time.Now(),
|
|
packet.Header.SequenceNumber,
|
|
packet.Header.Timestamp,
|
|
packet.Header.Marker,
|
|
packet.Header.MarshalSize(),
|
|
len(packet.Payload),
|
|
0,
|
|
)
|
|
if (sequenceNumber % 100) == 0 {
|
|
jump := uint16(rand.Float64() * 120.0)
|
|
sequenceNumber += jump
|
|
} else {
|
|
sequenceNumber++
|
|
}
|
|
}
|
|
|
|
lastFrameTime = now
|
|
time.Sleep(time.Duration(sleep) * time.Millisecond)
|
|
now = time.Now()
|
|
}
|
|
|
|
r.Stop()
|
|
fmt.Printf("%s\n", r.ToString())
|
|
}
|
|
|
|
func Test_RTPStatsReceiver_Update(t *testing.T) {
|
|
clockRate := uint32(90000)
|
|
r := NewRTPStatsReceiver(RTPStatsParams{
|
|
ClockRate: clockRate,
|
|
Logger: logger.GetLogger(),
|
|
})
|
|
|
|
sequenceNumber := uint16(rand.Float64() * float64(1<<16))
|
|
timestamp := uint32(rand.Float64() * float64(1<<32))
|
|
packet := getPacket(sequenceNumber, timestamp, 1000)
|
|
flowState := r.Update(
|
|
time.Now(),
|
|
packet.Header.SequenceNumber,
|
|
packet.Header.Timestamp,
|
|
packet.Header.Marker,
|
|
packet.Header.MarshalSize(),
|
|
len(packet.Payload),
|
|
0,
|
|
)
|
|
require.False(t, flowState.HasLoss)
|
|
require.True(t, r.initialized)
|
|
require.Equal(t, sequenceNumber, r.sequenceNumber.GetHighest())
|
|
require.Equal(t, sequenceNumber, uint16(r.sequenceNumber.GetExtendedHighest()))
|
|
require.Equal(t, timestamp, r.timestamp.GetHighest())
|
|
require.Equal(t, timestamp, uint32(r.timestamp.GetExtendedHighest()))
|
|
|
|
// in-order, no loss
|
|
sequenceNumber++
|
|
timestamp += 3000
|
|
packet = getPacket(sequenceNumber, timestamp, 1000)
|
|
flowState = r.Update(
|
|
time.Now(),
|
|
packet.Header.SequenceNumber,
|
|
packet.Header.Timestamp,
|
|
packet.Header.Marker,
|
|
packet.Header.MarshalSize(),
|
|
len(packet.Payload),
|
|
0,
|
|
)
|
|
require.False(t, flowState.HasLoss)
|
|
require.Equal(t, sequenceNumber, r.sequenceNumber.GetHighest())
|
|
require.Equal(t, sequenceNumber, uint16(r.sequenceNumber.GetExtendedHighest()))
|
|
require.Equal(t, timestamp, r.timestamp.GetHighest())
|
|
require.Equal(t, timestamp, uint32(r.timestamp.GetExtendedHighest()))
|
|
|
|
// out-of-order, would cause a restart which is disallowed
|
|
packet = getPacket(sequenceNumber-10, timestamp-30000, 1000)
|
|
flowState = r.Update(
|
|
time.Now(),
|
|
packet.Header.SequenceNumber,
|
|
packet.Header.Timestamp,
|
|
packet.Header.Marker,
|
|
packet.Header.MarshalSize(),
|
|
len(packet.Payload),
|
|
0,
|
|
)
|
|
require.False(t, flowState.HasLoss)
|
|
require.True(t, flowState.IsNotHandled)
|
|
require.Equal(t, sequenceNumber, r.sequenceNumber.GetHighest())
|
|
require.Equal(t, sequenceNumber, uint16(r.sequenceNumber.GetExtendedHighest()))
|
|
require.Equal(t, timestamp, r.timestamp.GetHighest())
|
|
require.Equal(t, timestamp, uint32(r.timestamp.GetExtendedHighest()))
|
|
require.Equal(t, uint64(0), r.packetsOutOfOrder)
|
|
require.Equal(t, uint64(0), r.packetsDuplicate)
|
|
|
|
// duplicate of the above out-of-order packet, but would not be handled as it causes a restart
|
|
packet = getPacket(sequenceNumber-10, timestamp-30000, 1000)
|
|
flowState = r.Update(
|
|
time.Now(),
|
|
packet.Header.SequenceNumber,
|
|
packet.Header.Timestamp,
|
|
packet.Header.Marker,
|
|
packet.Header.MarshalSize(),
|
|
len(packet.Payload),
|
|
0,
|
|
)
|
|
require.False(t, flowState.HasLoss)
|
|
require.True(t, flowState.IsNotHandled)
|
|
require.Equal(t, sequenceNumber, r.sequenceNumber.GetHighest())
|
|
require.Equal(t, sequenceNumber, uint16(r.sequenceNumber.GetExtendedHighest()))
|
|
require.Equal(t, timestamp, r.timestamp.GetHighest())
|
|
require.Equal(t, timestamp, uint32(r.timestamp.GetExtendedHighest()))
|
|
require.Equal(t, uint64(0), r.packetsOutOfOrder)
|
|
require.Equal(t, uint64(0), r.packetsDuplicate)
|
|
|
|
// loss
|
|
sequenceNumber += 10
|
|
timestamp += 30000
|
|
packet = getPacket(sequenceNumber, timestamp, 1000)
|
|
flowState = r.Update(
|
|
time.Now(),
|
|
packet.Header.SequenceNumber,
|
|
packet.Header.Timestamp,
|
|
packet.Header.Marker,
|
|
packet.Header.MarshalSize(),
|
|
len(packet.Payload),
|
|
0,
|
|
)
|
|
require.True(t, flowState.HasLoss)
|
|
require.Equal(t, uint64(sequenceNumber-9), flowState.LossStartInclusive)
|
|
require.Equal(t, uint64(sequenceNumber), flowState.LossEndExclusive)
|
|
require.Equal(t, uint64(9), r.packetsLost)
|
|
|
|
// out-of-order should decrement number of lost packets
|
|
packet = getPacket(sequenceNumber-6, timestamp-45000, 1000)
|
|
flowState = r.Update(
|
|
time.Now(),
|
|
packet.Header.SequenceNumber,
|
|
packet.Header.Timestamp,
|
|
packet.Header.Marker,
|
|
packet.Header.MarshalSize(),
|
|
len(packet.Payload),
|
|
0,
|
|
)
|
|
require.False(t, flowState.HasLoss)
|
|
require.Equal(t, sequenceNumber, r.sequenceNumber.GetHighest())
|
|
require.Equal(t, sequenceNumber, uint16(r.sequenceNumber.GetExtendedHighest()))
|
|
require.Equal(t, timestamp, r.timestamp.GetHighest())
|
|
require.Equal(t, timestamp, uint32(r.timestamp.GetExtendedHighest()))
|
|
require.Equal(t, uint64(1), r.packetsOutOfOrder)
|
|
require.Equal(t, uint64(0), r.packetsDuplicate)
|
|
require.Equal(t, uint64(8), r.packetsLost)
|
|
|
|
// test sequence number history
|
|
// with a gap
|
|
sequenceNumber += 2
|
|
timestamp += 6000
|
|
packet = getPacket(sequenceNumber, timestamp, 1000)
|
|
flowState = r.Update(
|
|
time.Now(),
|
|
packet.Header.SequenceNumber,
|
|
packet.Header.Timestamp,
|
|
packet.Header.Marker,
|
|
packet.Header.MarshalSize(),
|
|
len(packet.Payload),
|
|
0,
|
|
)
|
|
require.True(t, flowState.HasLoss)
|
|
require.Equal(t, uint64(sequenceNumber-1), flowState.LossStartInclusive)
|
|
require.Equal(t, uint64(sequenceNumber), flowState.LossEndExclusive)
|
|
require.Equal(t, uint64(9), r.packetsLost)
|
|
require.False(t, r.history.IsSet(uint64(sequenceNumber)-1))
|
|
|
|
// out-of-order
|
|
sequenceNumber--
|
|
timestamp -= 3000
|
|
packet = getPacket(sequenceNumber, timestamp, 999)
|
|
flowState = r.Update(
|
|
time.Now(),
|
|
packet.Header.SequenceNumber,
|
|
packet.Header.Timestamp,
|
|
packet.Header.Marker,
|
|
packet.Header.MarshalSize(),
|
|
len(packet.Payload),
|
|
0,
|
|
)
|
|
require.False(t, flowState.HasLoss)
|
|
require.Equal(t, uint64(8), r.packetsLost)
|
|
require.Equal(t, uint64(2), r.packetsOutOfOrder)
|
|
require.True(t, r.history.IsSet(uint64(sequenceNumber)))
|
|
|
|
// padding only
|
|
sequenceNumber += 2
|
|
packet = getPacket(sequenceNumber, timestamp, 0)
|
|
flowState = r.Update(
|
|
time.Now(),
|
|
packet.Header.SequenceNumber,
|
|
packet.Header.Timestamp,
|
|
packet.Header.Marker,
|
|
packet.Header.MarshalSize(),
|
|
len(packet.Payload),
|
|
25,
|
|
)
|
|
require.False(t, flowState.HasLoss)
|
|
require.Equal(t, uint64(8), r.packetsLost)
|
|
require.Equal(t, uint64(2), r.packetsOutOfOrder)
|
|
require.True(t, r.history.IsSet(uint64(sequenceNumber)))
|
|
require.True(t, r.history.IsSet(uint64(sequenceNumber)-1))
|
|
require.True(t, r.history.IsSet(uint64(sequenceNumber)-2))
|
|
|
|
r.Stop()
|
|
}
|