-
Notifications
You must be signed in to change notification settings - Fork 51
/
arrival_time_map.go
192 lines (167 loc) · 6.61 KB
/
arrival_time_map.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package twcc
const (
minCapacity = 128
maxNumberOfPackets = 1 << 15
)
// packetArrivalTimeMap is adapted from Chrome's implementation of TWCC, and keeps track
// of the arrival times of packets. It is used by the TWCC interceptor to build feedback
// packets.
// See https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:third_party/webrtc/modules/remote_bitrate_estimator/packet_arrival_map.h;drc=b5cd13bb6d5d157a5fbe3628b2dd1c1e106203c6
type packetArrivalTimeMap struct {
// arrivalTimes is a circular buffer, where the packet with sequence number sn is stored
// in slot sn % len(arrivalTimes)
arrivalTimes []int64
// The unwrapped sequence numbers for the range of valid sequence numbers in arrivalTimes.
// beginSequenceNumber is inclusive, and endSequenceNumber is exclusive.
beginSequenceNumber, endSequenceNumber int64
}
// AddPacket records the fact that the packet with sequence number sequenceNumber arrived
// at arrivalTime.
func (m *packetArrivalTimeMap) AddPacket(sequenceNumber int64, arrivalTime int64) {
if m.arrivalTimes == nil {
// First packet
m.reallocate(minCapacity)
m.beginSequenceNumber = sequenceNumber
m.endSequenceNumber = sequenceNumber + 1
m.arrivalTimes[m.index(sequenceNumber)] = arrivalTime
return
}
if sequenceNumber >= m.beginSequenceNumber && sequenceNumber < m.endSequenceNumber {
// The packet is within the buffer, no need to resize.
m.arrivalTimes[m.index(sequenceNumber)] = arrivalTime
return
}
if sequenceNumber < m.beginSequenceNumber {
// The packet goes before the current buffer. Expand to add packet,
// but only if it fits within the maximum number of packets.
newSize := int(m.endSequenceNumber - sequenceNumber)
if newSize > maxNumberOfPackets {
// Don't expand the buffer back for this packet, as it would remove newer received
// packets.
return
}
m.adjustToSize(newSize)
m.arrivalTimes[m.index(sequenceNumber)] = arrivalTime
m.setNotReceived(sequenceNumber+1, m.beginSequenceNumber)
m.beginSequenceNumber = sequenceNumber
return
}
// The packet goes after the buffer.
newEndSequenceNumber := sequenceNumber + 1
if newEndSequenceNumber >= m.endSequenceNumber+maxNumberOfPackets {
// All old packets have to be removed.
m.beginSequenceNumber = sequenceNumber
m.endSequenceNumber = newEndSequenceNumber
m.arrivalTimes[m.index(sequenceNumber)] = arrivalTime
return
}
if m.beginSequenceNumber < newEndSequenceNumber-maxNumberOfPackets {
// Remove oldest entries.
m.beginSequenceNumber = newEndSequenceNumber - maxNumberOfPackets
}
m.adjustToSize(int(newEndSequenceNumber - m.beginSequenceNumber))
// Packets can be received out of order. If this isn't the next expected packet,
// add enough placeholders to fill the gap.
m.setNotReceived(m.endSequenceNumber, sequenceNumber)
m.endSequenceNumber = newEndSequenceNumber
m.arrivalTimes[m.index(sequenceNumber)] = arrivalTime
}
func (m *packetArrivalTimeMap) setNotReceived(startInclusive, endExclusive int64) {
for sn := startInclusive; sn < endExclusive; sn++ {
m.arrivalTimes[m.index(sn)] = -1
}
}
// BeginSequenceNumber returns the first valid sequence number in the map.
func (m *packetArrivalTimeMap) BeginSequenceNumber() int64 {
return m.beginSequenceNumber
}
// EndSequenceNumber returns the first sequence number after the last valid sequence number in the map.
func (m *packetArrivalTimeMap) EndSequenceNumber() int64 {
return m.endSequenceNumber
}
// FindNextAtOrAfter returns the sequence number and timestamp of the first received packet that has a sequence number
// greator or equal to sequenceNumber.
func (m *packetArrivalTimeMap) FindNextAtOrAfter(sequenceNumber int64) (foundSequenceNumber int64, arrivalTime int64, ok bool) {
for sequenceNumber = m.Clamp(sequenceNumber); sequenceNumber < m.endSequenceNumber; sequenceNumber++ {
if t := m.get(sequenceNumber); t >= 0 {
return sequenceNumber, t, true
}
}
return -1, -1, false
}
// EraseTo erases all elements from the beginning of the map until sequenceNumber.
func (m *packetArrivalTimeMap) EraseTo(sequenceNumber int64) {
if sequenceNumber < m.beginSequenceNumber {
return
}
if sequenceNumber >= m.endSequenceNumber {
// Erase all.
m.beginSequenceNumber = m.endSequenceNumber
return
}
// Remove some
m.beginSequenceNumber = sequenceNumber
m.adjustToSize(int(m.endSequenceNumber - m.beginSequenceNumber))
}
// RemoveOldPackets removes packets from the beginning of the map as long as they are before
// sequenceNumber and with an age older than arrivalTimeLimit.
func (m *packetArrivalTimeMap) RemoveOldPackets(sequenceNumber int64, arrivalTimeLimit int64) {
checkTo := min64(sequenceNumber, m.endSequenceNumber)
for m.beginSequenceNumber < checkTo && m.get(m.beginSequenceNumber) <= arrivalTimeLimit {
m.beginSequenceNumber++
}
m.adjustToSize(int(m.endSequenceNumber - m.beginSequenceNumber))
}
// HasReceived returns whether a packet with the sequence number has been received.
func (m *packetArrivalTimeMap) HasReceived(sequenceNumber int64) bool {
return m.get(sequenceNumber) >= 0
}
// Clamp returns sequenceNumber clamped to [beginSequenceNumber, endSequenceNumber]
func (m *packetArrivalTimeMap) Clamp(sequenceNumber int64) int64 {
if sequenceNumber < m.beginSequenceNumber {
return m.beginSequenceNumber
}
if m.endSequenceNumber < sequenceNumber {
return m.endSequenceNumber
}
return sequenceNumber
}
func (m *packetArrivalTimeMap) get(sequenceNumber int64) int64 {
if sequenceNumber < m.beginSequenceNumber || sequenceNumber >= m.endSequenceNumber {
return -1
}
return m.arrivalTimes[m.index(sequenceNumber)]
}
func (m *packetArrivalTimeMap) index(sequenceNumber int64) int {
// Sequence number might be negative, and we always guarantee that arrivalTimes
// length is a power of 2, so it's easier to use "&" instead of "%"
return int(sequenceNumber & int64(m.capacity()-1))
}
func (m *packetArrivalTimeMap) adjustToSize(newSize int) {
if newSize > m.capacity() {
newCapacity := m.capacity()
for newCapacity < newSize {
newCapacity *= 2
}
m.reallocate(newCapacity)
}
if m.capacity() > max(minCapacity, newSize*4) {
newCapacity := m.capacity()
for newCapacity >= 2*max(newSize, minCapacity) {
newCapacity /= 2
}
m.reallocate(newCapacity)
}
}
func (m *packetArrivalTimeMap) capacity() int {
return len(m.arrivalTimes)
}
func (m *packetArrivalTimeMap) reallocate(newCapacity int) {
newBuffer := make([]int64, newCapacity)
for sn := m.beginSequenceNumber; sn < m.endSequenceNumber; sn++ {
newBuffer[int(sn&(int64(newCapacity-1)))] = m.get(sn)
}
m.arrivalTimes = newBuffer
}