Commit 7ce61a11 authored by Juliusz Chroboczek's avatar Juliusz Chroboczek

Move stats code into its own module.

Move RTP-specific code into its own file.
parent 813d89b6
...@@ -5,78 +5,21 @@ import ( ...@@ -5,78 +5,21 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"sfu/group"
"sfu/rtptime" "sfu/rtptime"
"sfu/stats"
) )
type groupStats struct { func (c *webClient) GetStats() *stats.Client {
name string
clients []clientStats
}
type clientStats struct {
id string
up, down []connStats
}
type connStats struct {
id string
maxBitrate uint64
tracks []trackStats
}
type trackStats struct {
bitrate uint64
maxBitrate uint64
loss uint8
rtt time.Duration
jitter time.Duration
}
func getGroupStats() []groupStats {
names := group.GetNames()
gs := make([]groupStats, 0, len(names))
for _, name := range names {
g := group.Get(name)
if g == nil {
continue
}
clients := g.GetClients(nil)
stats := groupStats{
name: name,
clients: make([]clientStats, 0, len(clients)),
}
for _, c := range clients {
c, ok := c.(*webClient)
if ok {
cs := getClientStats(c)
stats.clients = append(stats.clients, cs)
}
}
sort.Slice(stats.clients, func(i, j int) bool {
return stats.clients[i].id < stats.clients[j].id
})
gs = append(gs, stats)
}
sort.Slice(gs, func(i, j int) bool {
return gs[i].name < gs[j].name
})
return gs
}
func getClientStats(c *webClient) clientStats {
c.mu.Lock() c.mu.Lock()
defer c.mu.Unlock() defer c.mu.Unlock()
cs := clientStats{ cs := stats.Client{
id: c.id, Id: c.id,
} }
for _, up := range c.up { for _, up := range c.up {
conns := connStats{ conns := stats.Conn{
id: up.id, Id: up.id,
} }
tracks := up.getTracks() tracks := up.getTracks()
for _, t := range tracks { for _, t := range tracks {
...@@ -88,23 +31,23 @@ func getClientStats(c *webClient) clientStats { ...@@ -88,23 +31,23 @@ func getClientStats(c *webClient) clientStats {
jitter := time.Duration(t.jitter.Jitter()) * jitter := time.Duration(t.jitter.Jitter()) *
(time.Second / time.Duration(t.jitter.HZ())) (time.Second / time.Duration(t.jitter.HZ()))
rate, _ := t.rate.Estimate() rate, _ := t.rate.Estimate()
conns.tracks = append(conns.tracks, trackStats{ conns.Tracks = append(conns.Tracks, stats.Track{
bitrate: uint64(rate) * 8, Bitrate: uint64(rate) * 8,
loss: loss, Loss: loss,
jitter: jitter, Jitter: jitter,
}) })
} }
cs.up = append(cs.up, conns) cs.Up = append(cs.Up, conns)
} }
sort.Slice(cs.up, func(i, j int) bool { sort.Slice(cs.Up, func(i, j int) bool {
return cs.up[i].id < cs.up[j].id return cs.Up[i].Id < cs.Up[j].Id
}) })
jiffies := rtptime.Jiffies() jiffies := rtptime.Jiffies()
for _, down := range c.down { for _, down := range c.down {
conns := connStats{ conns := stats.Conn{
id: down.id, Id: down.id,
maxBitrate: down.GetMaxBitrate(jiffies), MaxBitrate: down.GetMaxBitrate(jiffies),
} }
for _, t := range down.tracks { for _, t := range down.tracks {
rate, _ := t.rate.Estimate() rate, _ := t.rate.Estimate()
...@@ -113,19 +56,19 @@ func getClientStats(c *webClient) clientStats { ...@@ -113,19 +56,19 @@ func getClientStats(c *webClient) clientStats {
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)
conns.tracks = append(conns.tracks, trackStats{ conns.Tracks = append(conns.Tracks, stats.Track{
bitrate: uint64(rate) * 8, Bitrate: uint64(rate) * 8,
maxBitrate: t.maxBitrate.Get(jiffies), MaxBitrate: t.maxBitrate.Get(jiffies),
loss: uint8(uint32(loss) * 100 / 256), Loss: uint8(uint32(loss) * 100 / 256),
rtt: rtt, Rtt: rtt,
jitter: j, Jitter: j,
}) })
} }
cs.down = append(cs.down, conns) cs.Down = append(cs.Down, conns)
} }
sort.Slice(cs.down, func(i, j int) bool { sort.Slice(cs.Down, func(i, j int) bool {
return cs.down[i].id < cs.down[j].id return cs.Down[i].Id < cs.Down[j].Id
}) })
return cs return &cs
} }
package stats
import (
"sort"
"time"
"sfu/group"
)
type GroupStats struct {
Name string
Clients []*Client
}
type Client struct {
Id string
Up, Down []Conn
}
type Statable interface {
GetStats() *Client
}
type Conn struct {
Id string
MaxBitrate uint64
Tracks []Track
}
type Track struct {
Bitrate uint64
MaxBitrate uint64
Loss uint8
Rtt time.Duration
Jitter time.Duration
}
func GetGroups() []GroupStats {
names := group.GetNames()
gs := make([]GroupStats, 0, len(names))
for _, name := range names {
g := group.Get(name)
if g == nil {
continue
}
clients := g.GetClients(nil)
stats := GroupStats{
Name: name,
Clients: make([]*Client, 0, len(clients)),
}
for _, c := range clients {
s, ok := c.(Statable)
if ok {
cs := s.GetStats()
stats.Clients = append(stats.Clients, cs)
} else {
stats.Clients = append(stats.Clients,
&Client{Id: c.Id()},
)
}
}
sort.Slice(stats.Clients, func(i, j int) bool {
return stats.Clients[i].Id < stats.Clients[j].Id
})
gs = append(gs, stats)
}
sort.Slice(gs, func(i, j int) bool {
return gs[i].Name < gs[j].Name
})
return gs
}
...@@ -21,6 +21,7 @@ import ( ...@@ -21,6 +21,7 @@ import (
"sfu/disk" "sfu/disk"
"sfu/group" "sfu/group"
"sfu/stats"
) )
var server *http.Server var server *http.Server
...@@ -50,7 +51,7 @@ func webserver() { ...@@ -50,7 +51,7 @@ func webserver() {
IdleTimeout: 120 * time.Second, IdleTimeout: 120 * time.Second,
} }
server.RegisterOnShutdown(func() { server.RegisterOnShutdown(func() {
group.Range(func (g *group.Group) bool { group.Range(func(g *group.Group) bool {
go g.Shutdown("server is shutting down") go g.Shutdown("server is shutting down")
return true return true
}) })
...@@ -225,7 +226,7 @@ func statsHandler(w http.ResponseWriter, r *http.Request) { ...@@ -225,7 +226,7 @@ func statsHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
stats := getGroupStats() ss := stats.GetGroups()
fmt.Fprintf(w, "<!DOCTYPE html>\n<html><head>\n") fmt.Fprintf(w, "<!DOCTYPE html>\n<html><head>\n")
fmt.Fprintf(w, "<title>Stats</title>\n") fmt.Fprintf(w, "<title>Stats</title>\n")
...@@ -242,51 +243,51 @@ func statsHandler(w http.ResponseWriter, r *http.Request) { ...@@ -242,51 +243,51 @@ func statsHandler(w http.ResponseWriter, r *http.Request) {
return err return err
} }
printTrack := func(w io.Writer, t trackStats) { printTrack := func(w io.Writer, t stats.Track) {
fmt.Fprintf(w, "<tr><td></td><td></td><td></td>") fmt.Fprintf(w, "<tr><td></td><td></td><td></td>")
fmt.Fprintf(w, "<td>") fmt.Fprintf(w, "<td>")
printBitrate(w, t.bitrate, t.maxBitrate) printBitrate(w, t.Bitrate, t.MaxBitrate)
fmt.Fprintf(w, "</td>") fmt.Fprintf(w, "</td>")
fmt.Fprintf(w, "<td>%d%%</td>", fmt.Fprintf(w, "<td>%d%%</td>",
t.loss, t.Loss,
) )
fmt.Fprintf(w, "<td>") fmt.Fprintf(w, "<td>")
if t.rtt > 0 { if t.Rtt > 0 {
fmt.Fprintf(w, "%v", t.rtt) fmt.Fprintf(w, "%v", t.Rtt)
} }
if t.jitter > 0 { if t.Jitter > 0 {
fmt.Fprintf(w, "&#177;%v", t.jitter) fmt.Fprintf(w, "&#177;%v", t.Jitter)
} }
fmt.Fprintf(w, "</td>") fmt.Fprintf(w, "</td>")
fmt.Fprintf(w, "</tr>") fmt.Fprintf(w, "</tr>")
} }
for _, gs := range stats { for _, gs := range ss {
fmt.Fprintf(w, "<p>%v</p>\n", html.EscapeString(gs.name)) fmt.Fprintf(w, "<p>%v</p>\n", html.EscapeString(gs.Name))
fmt.Fprintf(w, "<table>") fmt.Fprintf(w, "<table>")
for _, cs := range gs.clients { for _, cs := range gs.Clients {
fmt.Fprintf(w, "<tr><td>%v</td></tr>\n", cs.id) fmt.Fprintf(w, "<tr><td>%v</td></tr>\n", cs.Id)
for _, up := range cs.up { for _, up := range cs.Up {
fmt.Fprintf(w, "<tr><td></td><td>Up</td><td>%v</td>", fmt.Fprintf(w, "<tr><td></td><td>Up</td><td>%v</td>",
up.id) up.Id)
if up.maxBitrate > 0 { if up.MaxBitrate > 0 {
fmt.Fprintf(w, "<td>%v</td>", fmt.Fprintf(w, "<td>%v</td>",
up.maxBitrate) up.MaxBitrate)
} }
fmt.Fprintf(w, "</tr>\n") fmt.Fprintf(w, "</tr>\n")
for _, t := range up.tracks { for _, t := range up.Tracks {
printTrack(w, t) printTrack(w, t)
} }
} }
for _, down := range cs.down { for _, down := range cs.Down {
fmt.Fprintf(w, "<tr><td></td><td>Down</td><td> %v</td>", fmt.Fprintf(w, "<tr><td></td><td>Down</td><td> %v</td>",
down.id) down.Id)
if down.maxBitrate > 0 { if down.MaxBitrate > 0 {
fmt.Fprintf(w, "<td>%v</td>", fmt.Fprintf(w, "<td>%v</td>",
down.maxBitrate) down.MaxBitrate)
} }
fmt.Fprintf(w, "</tr>\n") fmt.Fprintf(w, "</tr>\n")
for _, t := range down.tracks { for _, t := range down.Tracks {
printTrack(w, t) printTrack(w, t)
} }
} }
......
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