-
Notifications
You must be signed in to change notification settings - Fork 52
/
stream_log.go
107 lines (98 loc) · 2.89 KB
/
stream_log.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
package rfc8888
import (
"time"
"github.com/pion/rtcp"
)
const maxReportsPerReportBlock = 16384
type streamLog struct {
ssrc uint32
sequence unwrapper
init bool
nextSequenceNumberToReport int64 // next to report
lastSequenceNumberReceived int64 // highest received
log map[int64]*packetReport
}
func newStreamLog(ssrc uint32) *streamLog {
return &streamLog{
ssrc: ssrc,
sequence: unwrapper{},
init: false,
nextSequenceNumberToReport: 0,
lastSequenceNumberReceived: 0,
log: map[int64]*packetReport{},
}
}
func (l *streamLog) add(ts time.Time, sequenceNumber uint16, ecn uint8) {
unwrappedSequenceNumber := l.sequence.unwrap(sequenceNumber)
if !l.init {
l.init = true
l.nextSequenceNumberToReport = unwrappedSequenceNumber
}
l.log[unwrappedSequenceNumber] = &packetReport{
arrivalTime: ts,
ecn: ecn,
}
if l.lastSequenceNumberReceived < unwrappedSequenceNumber {
l.lastSequenceNumberReceived = unwrappedSequenceNumber
}
}
// metricsAfter iterates over all packets order of their sequence number.
// Packets are removed until the first loss is detected.
func (l *streamLog) metricsAfter(reference time.Time, maxReportBlocks int64) rtcp.CCFeedbackReportBlock {
if len(l.log) == 0 {
return rtcp.CCFeedbackReportBlock{
MediaSSRC: l.ssrc,
BeginSequence: uint16(l.nextSequenceNumberToReport),
MetricBlocks: []rtcp.CCFeedbackMetricBlock{},
}
}
numReports := l.lastSequenceNumberReceived - l.nextSequenceNumberToReport + 1
if numReports > maxReportBlocks {
numReports = maxReportBlocks
l.nextSequenceNumberToReport = l.lastSequenceNumberReceived - maxReportBlocks + 1
}
metricBlocks := make([]rtcp.CCFeedbackMetricBlock, numReports)
offset := l.nextSequenceNumberToReport
lastReceived := l.nextSequenceNumberToReport
gapDetected := false
for i := offset; i <= l.lastSequenceNumberReceived; i++ {
received := false
ecn := uint8(0)
ato := uint16(0)
if report, ok := l.log[i]; ok {
received = true
ecn = report.ecn
ato = getArrivalTimeOffset(reference, report.arrivalTime)
}
metricBlocks[i-offset] = rtcp.CCFeedbackMetricBlock{
Received: received,
ECN: rtcp.ECN(ecn),
ArrivalTimeOffset: ato,
}
if !gapDetected {
if received && i == l.nextSequenceNumberToReport {
delete(l.log, i)
l.nextSequenceNumberToReport++
lastReceived = i
}
if i > lastReceived+1 {
gapDetected = true
}
}
}
return rtcp.CCFeedbackReportBlock{
MediaSSRC: l.ssrc,
BeginSequence: uint16(offset),
MetricBlocks: metricBlocks,
}
}
func getArrivalTimeOffset(base time.Time, arrival time.Time) uint16 {
if base.Before(arrival) {
return 0x1FFF
}
ato := uint16(base.Sub(arrival).Seconds() * 1024.0)
if ato > 0x1FFD {
return 0x1FFE
}
return ato
}