96 lines
2.8 KiB
Go
96 lines
2.8 KiB
Go
|
package metrics
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"sort"
|
||
|
|
||
|
"github.com/owncast/owncast/core/data"
|
||
|
"github.com/owncast/owncast/models"
|
||
|
)
|
||
|
|
||
|
var errorMessages = map[string]string{
|
||
|
"LOWSPEED": "%d of %d clients (%d%%) are consuming video slower than, or too close to your bitrate of %d kbps.",
|
||
|
"PLAYBACK_ERRORS": "%d of %d clients (%d%%) are experiencing different, unspecified, playback issues.",
|
||
|
}
|
||
|
|
||
|
// GetStreamHealthOverview will return the stream health overview.
|
||
|
func GetStreamHealthOverview() *models.StreamHealthOverview {
|
||
|
return metrics.streamHealthOverview
|
||
|
}
|
||
|
|
||
|
func generateStreamHealthOverview() {
|
||
|
overview := models.StreamHealthOverview{
|
||
|
Healthy: true,
|
||
|
HealthyPercentage: 100,
|
||
|
}
|
||
|
|
||
|
defer func() {
|
||
|
metrics.streamHealthOverview = &overview
|
||
|
}()
|
||
|
|
||
|
type singleVariant struct {
|
||
|
isVideoPassthrough bool
|
||
|
bitrate int
|
||
|
}
|
||
|
|
||
|
outputVariants := data.GetStreamOutputVariants()
|
||
|
|
||
|
streamSortVariants := make([]singleVariant, len(outputVariants))
|
||
|
for i, variant := range outputVariants {
|
||
|
variantSort := singleVariant{
|
||
|
bitrate: variant.VideoBitrate,
|
||
|
isVideoPassthrough: variant.IsVideoPassthrough,
|
||
|
}
|
||
|
streamSortVariants[i] = variantSort
|
||
|
}
|
||
|
|
||
|
sort.Slice(streamSortVariants, func(i, j int) bool {
|
||
|
if streamSortVariants[i].isVideoPassthrough && !streamSortVariants[j].isVideoPassthrough {
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
if !streamSortVariants[i].isVideoPassthrough && streamSortVariants[j].isVideoPassthrough {
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
return streamSortVariants[i].bitrate > streamSortVariants[j].bitrate
|
||
|
})
|
||
|
|
||
|
lowestSupportedBitrate := float64(streamSortVariants[0].bitrate)
|
||
|
totalNumberOfClients := len(windowedBandwidths)
|
||
|
|
||
|
if totalNumberOfClients == 0 {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Determine healthy status based on bandwidth speeds of clients.
|
||
|
unhealthyClientCount := 0
|
||
|
for _, speed := range windowedBandwidths {
|
||
|
if int(speed) < int(lowestSupportedBitrate*1.1) {
|
||
|
unhealthyClientCount++
|
||
|
}
|
||
|
}
|
||
|
if unhealthyClientCount > 0 {
|
||
|
overview.Message = fmt.Sprintf(errorMessages["LOWSPEED"], unhealthyClientCount, totalNumberOfClients, int((float64(unhealthyClientCount)/float64(totalNumberOfClients))*100), int(lowestSupportedBitrate))
|
||
|
}
|
||
|
|
||
|
// If bandwidth is ok, determine healthy status based on error
|
||
|
// counts of clients.
|
||
|
if unhealthyClientCount == 0 {
|
||
|
for _, errors := range windowedErrorCounts {
|
||
|
unhealthyClientCount += int(errors)
|
||
|
}
|
||
|
if unhealthyClientCount > 0 {
|
||
|
overview.Message = fmt.Sprintf(errorMessages["PLAYBACK_ERRORS"], unhealthyClientCount, totalNumberOfClients, int((float64(unhealthyClientCount)/float64(totalNumberOfClients))*100))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if unhealthyClientCount == 0 {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
percentUnhealthy := 100 - ((float64(unhealthyClientCount) / float64(totalNumberOfClients)) * 100)
|
||
|
overview.HealthyPercentage = int(percentUnhealthy)
|
||
|
overview.Healthy = overview.HealthyPercentage > 95
|
||
|
}
|