File: stream_log.go

package info (click to toggle)
golang-github-pion-interceptor 0.1.12-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bookworm-backports, forky, sid, trixie
  • size: 764 kB
  • sloc: makefile: 8
file content (107 lines) | stat: -rw-r--r-- 2,962 bytes parent folder | download
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
}