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
}
|