Skip to content

Commit 1ebbe49

Browse files
committed
WIP: Draft for NADA BandwidthEstimator
Implements a simple adapter to satisfy the BandwidthEstimator interface to allow using nada implementation in interceptor.
1 parent 3759563 commit 1ebbe49

File tree

1 file changed

+102
-0
lines changed

1 file changed

+102
-0
lines changed

pkg/nada/bandwidth_estimator.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package nada
2+
3+
import (
4+
"math"
5+
"time"
6+
7+
"github.com/pion/interceptor"
8+
"github.com/pion/interceptor/internal/cc"
9+
"github.com/pion/interceptor/internal/ntp"
10+
"github.com/pion/rtcp"
11+
"github.com/pion/rtp"
12+
)
13+
14+
type NadaBandwidthEstimator struct {
15+
*cc.FeedbackAdapter
16+
*Sender
17+
*Receiver
18+
}
19+
20+
func (e *NadaBandwidthEstimator) AddStream(_ *interceptor.StreamInfo, writer interceptor.RTPWriter) interceptor.RTPWriter {
21+
return interceptor.RTPWriterFunc(func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
22+
now := time.Now()
23+
if err := e.OnSent(now, header, len(payload), attributes); err != nil {
24+
return 0, err
25+
}
26+
return writer.Write(header, payload, attributes)
27+
})
28+
}
29+
30+
func (e *NadaBandwidthEstimator) WriteRTCP(pkts []rtcp.Packet, _ interceptor.Attributes) error {
31+
now := time.Now()
32+
var acks []cc.Acknowledgment
33+
for _, pkt := range pkts {
34+
var feedbackSentTime time.Time
35+
switch fb := pkt.(type) {
36+
case *rtcp.TransportLayerCC:
37+
newAcks, err := e.OnTransportCCFeedback(now, fb)
38+
if err != nil {
39+
return err
40+
}
41+
for i, ack := range acks {
42+
if i == 0 {
43+
feedbackSentTime = ack.Arrival
44+
continue
45+
}
46+
if ack.Arrival.After(feedbackSentTime) {
47+
feedbackSentTime = ack.Arrival
48+
}
49+
}
50+
acks = append(acks, newAcks...)
51+
case *rtcp.CCFeedbackReport:
52+
acks = e.OnRFC8888Feedback(now, fb)
53+
feedbackSentTime = ntp.ToTime(uint64(fb.ReportTimestamp) << 16)
54+
default:
55+
continue
56+
}
57+
58+
feedbackMinRTT := time.Duration(math.MaxInt)
59+
for _, ack := range acks {
60+
if ack.Arrival.IsZero() {
61+
continue
62+
}
63+
pendingTime := feedbackSentTime.Sub(ack.Arrival)
64+
rtt := now.Sub(ack.Departure) - pendingTime
65+
feedbackMinRTT = time.Duration(minInt(int(rtt), int(feedbackMinRTT)))
66+
}
67+
if feedbackMinRTT < math.MaxInt {
68+
e.UpdateEstimatedRoundTripTime(feedbackMinRTT)
69+
}
70+
}
71+
72+
for _, ack := range acks {
73+
e.OnReceiveMediaPacket(ack.Arrival, ack.Departure, ack.SequenceNumber, ack.ECN == rtcp.ECNCE, 8*Bits(ack.Size))
74+
}
75+
76+
e.OnReceiveFeedbackReport(now, e.BuildFeedbackReport())
77+
return nil
78+
}
79+
80+
func (e *NadaBandwidthEstimator) GetTargetBitrate() int {
81+
// TODO: What is the buffer len parameter of GetTargetBitrate?
82+
return int(e.GetTargetRate(0))
83+
}
84+
85+
func (e *NadaBandwidthEstimator) OnTargetBitrateChange(f func(bitrate int)) {
86+
panic("not implemented")
87+
}
88+
89+
func (e *NadaBandwidthEstimator) GetStats() map[string]interface{} {
90+
panic("not implemented")
91+
}
92+
93+
func (e *NadaBandwidthEstimator) Close() error {
94+
panic("not implemented")
95+
}
96+
97+
func minInt(a, b int) int {
98+
if a < b {
99+
return a
100+
}
101+
return b
102+
}

0 commit comments

Comments
 (0)