Commit 19a65318 authored by Juliusz Chroboczek's avatar Juliusz Chroboczek

Compute down track RTT.

parent 49bccda5
...@@ -281,6 +281,9 @@ type rtpDownTrack struct { ...@@ -281,6 +281,9 @@ type rtpDownTrack struct {
maxREMBBitrate *bitrate maxREMBBitrate *bitrate
rate *estimator.Estimator rate *estimator.Estimator
stats *receiverStats stats *receiverStats
srTime uint64
srNTPTime uint64
rtt uint64
} }
func (down *rtpDownTrack) WriteRTP(packet *rtp.Packet) error { func (down *rtpDownTrack) WriteRTP(packet *rtp.Packet) error {
......
...@@ -518,6 +518,7 @@ type trackStats struct { ...@@ -518,6 +518,7 @@ type trackStats struct {
bitrate uint64 bitrate uint64
maxBitrate uint64 maxBitrate uint64
loss uint8 loss uint8
rtt time.Duration
jitter time.Duration jitter time.Duration
} }
...@@ -589,6 +590,8 @@ func getClientStats(c *webClient) clientStats { ...@@ -589,6 +590,8 @@ func getClientStats(c *webClient) clientStats {
conns := connStats{id: down.id} conns := connStats{id: down.id}
for _, t := range down.tracks { for _, t := range down.tracks {
jiffies := rtptime.Jiffies() jiffies := rtptime.Jiffies()
rtt := rtptime.ToDuration(atomic.LoadUint64(&t.rtt),
rtptime.JiffiesPerSec)
loss, jitter := t.stats.Get(jiffies) loss, jitter := t.stats.Get(jiffies)
j := time.Duration(jitter) * time.Second / j := time.Duration(jitter) * time.Second /
time.Duration(t.track.Codec().ClockRate) time.Duration(t.track.Codec().ClockRate)
...@@ -596,6 +599,7 @@ func getClientStats(c *webClient) clientStats { ...@@ -596,6 +599,7 @@ func getClientStats(c *webClient) clientStats {
bitrate: uint64(t.rate.Estimate()) * 8, bitrate: uint64(t.rate.Estimate()) * 8,
maxBitrate: t.GetMaxBitrate(jiffies), maxBitrate: t.GetMaxBitrate(jiffies),
loss: uint8(uint32(loss) * 100 / 256), loss: uint8(uint32(loss) * 100 / 256),
rtt: rtt,
jitter: j, jitter: j,
}) })
} }
......
...@@ -10,7 +10,7 @@ func FromDuration(d time.Duration, hz uint32) uint64 { ...@@ -10,7 +10,7 @@ func FromDuration(d time.Duration, hz uint32) uint64 {
return uint64(d) * uint64(hz) / uint64(time.Second) return uint64(d) * uint64(hz) / uint64(time.Second)
} }
func toDuration(tm uint64, hz uint32) time.Duration { func ToDuration(tm uint64, hz uint32) time.Duration {
return time.Duration(tm * uint64(time.Second) / uint64(hz)) return time.Duration(tm * uint64(time.Second) / uint64(hz))
} }
...@@ -29,6 +29,10 @@ func Jiffies() uint64 { ...@@ -29,6 +29,10 @@ func Jiffies() uint64 {
return Now(JiffiesPerSec) return Now(JiffiesPerSec)
} }
func TimeToJiffies(tm time.Time) uint64 {
return FromDuration(tm.Sub(epoch), JiffiesPerSec)
}
var ntpEpoch = time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC) var ntpEpoch = time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC)
func NTPToTime(ntp uint64) time.Time { func NTPToTime(ntp uint64) time.Time {
......
...@@ -11,7 +11,7 @@ func TestDuration(t *testing.T) { ...@@ -11,7 +11,7 @@ func TestDuration(t *testing.T) {
t.Errorf("Expected 48000, got %v", a) t.Errorf("Expected 48000, got %v", a)
} }
b := toDuration(48000, 48000) b := ToDuration(48000, 48000)
if b != time.Second { if b != time.Second {
t.Errorf("Expected %v, got %v", time.Second, b) t.Errorf("Expected %v, got %v", time.Second, b)
} }
......
...@@ -20,8 +20,8 @@ import ( ...@@ -20,8 +20,8 @@ import (
"sfu/estimator" "sfu/estimator"
"sfu/jitter" "sfu/jitter"
"sfu/rtptime"
"sfu/packetcache" "sfu/packetcache"
"sfu/rtptime"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"github.com/pion/rtcp" "github.com/pion/rtcp"
...@@ -633,6 +633,7 @@ func sendSR(conn *rtpDownConnection) error { ...@@ -633,6 +633,7 @@ func sendSR(conn *rtpDownConnection) error {
now := time.Now() now := time.Now()
nowNTP := rtptime.TimeToNTP(now) nowNTP := rtptime.TimeToNTP(now)
jiffies := rtptime.TimeToJiffies(now)
for _, t := range conn.tracks { for _, t := range conn.tracks {
clockrate := t.track.Codec().ClockRate clockrate := t.track.Codec().ClockRate
...@@ -661,6 +662,8 @@ func sendSR(conn *rtpDownConnection) error { ...@@ -661,6 +662,8 @@ func sendSR(conn *rtpDownConnection) error {
PacketCount: p, PacketCount: p,
OctetCount: b, OctetCount: b,
}) })
atomic.StoreUint64(&t.srTime, jiffies)
atomic.StoreUint64(&t.srNTPTime, nowNTP)
} }
return conn.pc.WriteRTCP(packets) return conn.pc.WriteRTCP(packets)
...@@ -876,6 +879,7 @@ func rtcpDownListener(conn *rtpDownConnection, track *rtpDownTrack, s *webrtc.RT ...@@ -876,6 +879,7 @@ func rtcpDownListener(conn *rtpDownConnection, track *rtpDownTrack, s *webrtc.RT
} }
return return
} }
jiffies := rtptime.Jiffies()
for _, p := range ps { for _, p := range ps {
switch p := p.(type) { switch p := p.(type) {
...@@ -918,25 +922,21 @@ func rtcpDownListener(conn *rtpDownConnection, track *rtpDownTrack, s *webrtc.RT ...@@ -918,25 +922,21 @@ func rtcpDownListener(conn *rtpDownConnection, track *rtpDownTrack, s *webrtc.RT
log.Printf("sendFIR: %v", err) log.Printf("sendFIR: %v", err)
} }
case *rtcp.ReceiverEstimatedMaximumBitrate: case *rtcp.ReceiverEstimatedMaximumBitrate:
track.maxREMBBitrate.Set( track.maxREMBBitrate.Set(p.Bitrate, jiffies)
p.Bitrate, rtptime.Jiffies(),
)
case *rtcp.ReceiverReport: case *rtcp.ReceiverReport:
for _, r := range p.Reports { for _, r := range p.Reports {
if r.SSRC == track.track.SSRC() { if r.SSRC == track.track.SSRC() {
handleReport(track, r) handleReport(track, r, jiffies)
} }
} }
case *rtcp.SenderReport: case *rtcp.SenderReport:
for _, r := range p.Reports { for _, r := range p.Reports {
if r.SSRC == track.track.SSRC() { if r.SSRC == track.track.SSRC() {
handleReport(track, r) handleReport(track, r, jiffies)
} }
} }
case *rtcp.TransportLayerNack: case *rtcp.TransportLayerNack:
maxBitrate := track.GetMaxBitrate( maxBitrate := track.GetMaxBitrate(jiffies)
rtptime.Jiffies(),
)
bitrate := track.rate.Estimate() bitrate := track.rate.Estimate()
if uint64(bitrate)*7/8 < maxBitrate { if uint64(bitrate)*7/8 < maxBitrate {
sendRecovery(p, track) sendRecovery(p, track)
...@@ -946,10 +946,32 @@ func rtcpDownListener(conn *rtpDownConnection, track *rtpDownTrack, s *webrtc.RT ...@@ -946,10 +946,32 @@ func rtcpDownListener(conn *rtpDownConnection, track *rtpDownTrack, s *webrtc.RT
} }
} }
func handleReport(track *rtpDownTrack, report rtcp.ReceptionReport) { func handleReport(track *rtpDownTrack, report rtcp.ReceptionReport, jiffies uint64) {
jiffies := rtptime.Jiffies()
track.stats.Set(report.FractionLost, report.Jitter, jiffies) track.stats.Set(report.FractionLost, report.Jitter, jiffies)
track.updateRate(report.FractionLost, jiffies) track.updateRate(report.FractionLost, jiffies)
if report.LastSenderReport != 0 {
jiffies := rtptime.Jiffies()
srTime := atomic.LoadUint64(&track.srTime)
if jiffies < srTime || jiffies-srTime > 8*rtptime.JiffiesPerSec {
return
}
srNTPTime := atomic.LoadUint64(&track.srNTPTime)
if report.LastSenderReport == uint32(srNTPTime>>16) {
delay := uint64(report.Delay) *
(rtptime.JiffiesPerSec / 0x10000)
if delay > jiffies-srTime {
return
}
rtt := (jiffies - srTime) - delay
oldrtt := atomic.LoadUint64(&track.rtt)
newrtt := rtt
if oldrtt > 0 {
newrtt = (3*oldrtt + rtt) / 4
}
atomic.StoreUint64(&track.rtt, newrtt)
}
}
} }
func trackKinds(down *rtpDownConnection) (audio bool, video bool) { func trackKinds(down *rtpDownConnection) (audio bool, video bool) {
...@@ -1013,7 +1035,7 @@ func (up *upConnection) sendPLI(track *upTrack) error { ...@@ -1013,7 +1035,7 @@ func (up *upConnection) sendPLI(track *upTrack) error {
} }
last := atomic.LoadUint64(&track.lastPLI) last := atomic.LoadUint64(&track.lastPLI)
now := rtptime.Jiffies() now := rtptime.Jiffies()
if now >= last && now-last < rtptime.JiffiesPerSec / 5 { if now >= last && now-last < rtptime.JiffiesPerSec/5 {
return ErrRateLimited return ErrRateLimited
} }
atomic.StoreUint64(&track.lastPLI, now) atomic.StoreUint64(&track.lastPLI, now)
...@@ -1041,7 +1063,7 @@ func (up *upConnection) sendFIR(track *upTrack, increment bool) error { ...@@ -1041,7 +1063,7 @@ func (up *upConnection) sendFIR(track *upTrack, increment bool) error {
} }
last := atomic.LoadUint64(&track.lastFIR) last := atomic.LoadUint64(&track.lastFIR)
now := rtptime.Jiffies() now := rtptime.Jiffies()
if now >= last && now-last < rtptime.JiffiesPerSec / 5 { if now >= last && now-last < rtptime.JiffiesPerSec/5 {
return ErrRateLimited return ErrRateLimited
} }
atomic.StoreUint64(&track.lastFIR, now) atomic.StoreUint64(&track.lastFIR, now)
......
...@@ -154,11 +154,14 @@ func statsHandler(w http.ResponseWriter, r *http.Request) { ...@@ -154,11 +154,14 @@ func statsHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "<td>%d%%</td>", fmt.Fprintf(w, "<td>%d%%</td>",
t.loss, t.loss,
) )
fmt.Fprintf(w, "<td>")
if t.rtt > 0 {
fmt.Fprintf(w, "%v", t.rtt)
}
if t.jitter > 0 { if t.jitter > 0 {
fmt.Fprintf(w, "<td>%v</td>", t.jitter) fmt.Fprintf(w, "&#177;%v", t.jitter)
} else {
fmt.Fprintf(w, "<td></td>")
} }
fmt.Fprintf(w, "</td>")
fmt.Fprintf(w, "</tr>") fmt.Fprintf(w, "</tr>")
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment