Expand upon playback metrics

This commit is contained in:
Gabe Kangas 2022-03-16 22:49:27 -07:00
parent d84da617b7
commit 6479220e78
No known key found for this signature in database
GPG Key ID: 9A56337728BC81EA
4 changed files with 227 additions and 44 deletions

View File

@ -15,9 +15,18 @@ func GetVideoPlaybackMetrics(w http.ResponseWriter, r *http.Request) {
type response struct {
Errors []metrics.TimestampedValue `json:"errors"`
QualityVariantChanges []metrics.TimestampedValue `json:"qualityVariantChanges"`
Latency []metrics.TimestampedValue `json:"latency"`
SegmentDownloadDuration []metrics.TimestampedValue `json:"segmentDownloadDuration"`
HighestLatency []metrics.TimestampedValue `json:"highestLatency"`
MedianLatency []metrics.TimestampedValue `json:"medianLatency"`
LowestLatency []metrics.TimestampedValue `json:"lowestLatency"`
MedianDownloadDuration []metrics.TimestampedValue `json:"medianSegmentDownloadDuration"`
MaximumDownloadDuration []metrics.TimestampedValue `json:"maximumSegmentDownloadDuration"`
MinimumDownloadDuration []metrics.TimestampedValue `json:"minimumSegmentDownloadDuration"`
SlowestDownloadRate []metrics.TimestampedValue `json:"minPlayerBitrate"`
MedianDownloadRate []metrics.TimestampedValue `json:"medianPlayerBitrate"`
HighestDownloadRater []metrics.TimestampedValue `json:"maxPlayerBitrate"`
AvailableBitrates []int `json:"availableBitrates"`
SegmentLength int `json:"segmentLength"`
}
@ -37,18 +46,32 @@ func GetVideoPlaybackMetrics(w http.ResponseWriter, r *http.Request) {
}
errors := metrics.GetPlaybackErrorCountOverTime()
latency := metrics.GetLatencyOverTime()
durations := metrics.GetDownloadDurationsOverTime()
medianLatency := metrics.GetMedianLatencyOverTime()
minimumLatency := metrics.GetMinimumLatencyOverTime()
maximumLatency := metrics.GetMaximumLatencyOverTime()
medianDurations := metrics.GetMedianDownloadDurationsOverTime()
maximumDurations := metrics.GetMaximumDownloadDurationsOverTime()
minimumDurations := metrics.GetMinimumDownloadDurationsOverTime()
minPlayerBitrate := metrics.GetSlowestDownloadRateOverTime()
medianPlayerBitrate := metrics.GetMedianDownloadRateOverTime()
maxPlayerBitrate := metrics.GetMaxDownloadRateOverTime()
qualityVariantChanges := metrics.GetQualityVariantChangesOverTime()
resp := response{
AvailableBitrates: availableBitrates,
Errors: errors,
Latency: latency,
MedianLatency: medianLatency,
HighestLatency: maximumLatency,
LowestLatency: minimumLatency,
SegmentLength: segmentLength,
SegmentDownloadDuration: durations,
MedianDownloadDuration: medianDurations,
MaximumDownloadDuration: maximumDurations,
MinimumDownloadDuration: minimumDurations,
SlowestDownloadRate: minPlayerBitrate,
MedianDownloadRate: medianPlayerBitrate,
HighestDownloadRater: maxPlayerBitrate,
QualityVariantChanges: qualityVariantChanges,
}

View File

@ -23,10 +23,20 @@ type CollectedMetrics struct {
CPUUtilizations []TimestampedValue `json:"cpu"`
RAMUtilizations []TimestampedValue `json:"memory"`
DiskUtilizations []TimestampedValue `json:"disk"`
errorCount []TimestampedValue `json:"-"`
lowestBitrate []TimestampedValue `json:"-"`
segmentDownloadSeconds []TimestampedValue `json:"-"`
averageLatency []TimestampedValue `json:"-"`
medianBitrate []TimestampedValue `json:"-"`
highestBitrate []TimestampedValue `json:"-"`
medianSegmentDownloadSeconds []TimestampedValue `json:"-"`
maximumSegmentDownloadSeconds []TimestampedValue `json:"-"`
minimumSegmentDownloadSeconds []TimestampedValue `json:"-"`
minimumLatency []TimestampedValue `json:"-"`
maximumLatency []TimestampedValue `json:"-"`
medianLatency []TimestampedValue `json:"-"`
qualityVariantChanges []TimestampedValue `json:"-"`
}

View File

@ -65,25 +65,57 @@ func collectPlaybackErrorCount() {
}
func collectSegmentDownloadDuration() {
val := 0.0
median := 0.0
max := 0.0
min := 0.0
if len(windowedDownloadDurations) > 0 {
val = utils.Avg(windowedDownloadDurations)
median = utils.Median(windowedDownloadDurations)
min, max = utils.MinMax(windowedDownloadDurations)
windowedDownloadDurations = []float64{}
}
metrics.segmentDownloadSeconds = append(metrics.segmentDownloadSeconds, TimestampedValue{
metrics.medianSegmentDownloadSeconds = append(metrics.medianSegmentDownloadSeconds, TimestampedValue{
Time: time.Now(),
Value: val,
Value: median,
})
if len(metrics.segmentDownloadSeconds) > maxCollectionValues {
metrics.segmentDownloadSeconds = metrics.segmentDownloadSeconds[1:]
if len(metrics.medianSegmentDownloadSeconds) > maxCollectionValues {
metrics.medianSegmentDownloadSeconds = metrics.medianSegmentDownloadSeconds[1:]
}
metrics.minimumSegmentDownloadSeconds = append(metrics.minimumSegmentDownloadSeconds, TimestampedValue{
Time: time.Now(),
Value: min,
})
if len(metrics.minimumSegmentDownloadSeconds) > maxCollectionValues {
metrics.minimumSegmentDownloadSeconds = metrics.minimumSegmentDownloadSeconds[1:]
}
metrics.maximumSegmentDownloadSeconds = append(metrics.maximumSegmentDownloadSeconds, TimestampedValue{
Time: time.Now(),
Value: max,
})
if len(metrics.maximumSegmentDownloadSeconds) > maxCollectionValues {
metrics.maximumSegmentDownloadSeconds = metrics.maximumSegmentDownloadSeconds[1:]
}
}
// GetDownloadDurationsOverTime will return a window of durations errors over time.
func GetDownloadDurationsOverTime() []TimestampedValue {
return metrics.segmentDownloadSeconds
// GetMedianDownloadDurationsOverTime will return a window of durations errors over time.
func GetMedianDownloadDurationsOverTime() []TimestampedValue {
return metrics.medianSegmentDownloadSeconds
}
// GetMaximumDownloadDurationsOverTime will return a maximum durations errors over time.
func GetMaximumDownloadDurationsOverTime() []TimestampedValue {
return metrics.maximumSegmentDownloadSeconds
}
// GetMinimumDownloadDurationsOverTime will return a maximum durations errors over time.
func GetMinimumDownloadDurationsOverTime() []TimestampedValue {
return metrics.minimumSegmentDownloadSeconds
}
// GetPlaybackErrorCountOverTime will return a window of playback errors over time.
@ -92,53 +124,113 @@ func GetPlaybackErrorCountOverTime() []TimestampedValue {
}
func collectLatencyValues() {
val := 0.0
median := 0.0
min := 0.0
max := 0.0
if len(windowedLatencies) > 0 {
val = utils.Avg(windowedLatencies)
val = math.Round(val)
median = utils.Median(windowedLatencies)
min, max = utils.MinMax(windowedLatencies)
windowedLatencies = []float64{}
}
metrics.averageLatency = append(metrics.averageLatency, TimestampedValue{
metrics.medianLatency = append(metrics.medianLatency, TimestampedValue{
Time: time.Now(),
Value: val,
Value: median,
})
if len(metrics.averageLatency) > maxCollectionValues {
metrics.averageLatency = metrics.averageLatency[1:]
if len(metrics.medianLatency) > maxCollectionValues {
metrics.medianLatency = metrics.medianLatency[1:]
}
metrics.minimumLatency = append(metrics.minimumLatency, TimestampedValue{
Time: time.Now(),
Value: min,
})
if len(metrics.minimumLatency) > maxCollectionValues {
metrics.minimumLatency = metrics.minimumLatency[1:]
}
metrics.maximumLatency = append(metrics.maximumLatency, TimestampedValue{
Time: time.Now(),
Value: max,
})
if len(metrics.maximumLatency) > maxCollectionValues {
metrics.maximumLatency = metrics.maximumLatency[1:]
}
}
// GetLatencyOverTime will return the min, max and avg latency values over time.
func GetLatencyOverTime() []TimestampedValue {
if len(metrics.averageLatency) == 0 {
// GetMedianLatencyOverTime will return the median latency values over time.
func GetMedianLatencyOverTime() []TimestampedValue {
if len(metrics.medianLatency) == 0 {
return []TimestampedValue{}
}
return metrics.averageLatency
return metrics.medianLatency
}
// collectLowestBandwidth will collect the lowest bandwidth currently collected
// GetMinimumLatencyOverTime will return the min latency values over time.
func GetMinimumLatencyOverTime() []TimestampedValue {
if len(metrics.minimumLatency) == 0 {
return []TimestampedValue{}
}
return metrics.minimumLatency
}
// GetMaximumLatencyOverTime will return the max latency values over time.
func GetMaximumLatencyOverTime() []TimestampedValue {
if len(metrics.maximumLatency) == 0 {
return []TimestampedValue{}
}
return metrics.maximumLatency
}
// collectLowestBandwidth will collect the bandwidth currently collected
// so we can report to the streamer the worst possible streaming condition
// being experienced.
func collectLowestBandwidth() {
val := 0.0
min := 0.0
median := 0.0
max := 0.0
if len(windowedBandwidths) > 0 {
val, _ = utils.MinMax(windowedBandwidths)
val = math.Round(val)
min, max = utils.MinMax(windowedBandwidths)
min = math.Round(min)
max = math.Round(max)
median = utils.Median(windowedBandwidths)
windowedBandwidths = []float64{}
}
metrics.lowestBitrate = append(metrics.lowestBitrate, TimestampedValue{
Time: time.Now(),
Value: math.Round(val),
Value: math.Round(min),
})
if len(metrics.lowestBitrate) > maxCollectionValues {
metrics.lowestBitrate = metrics.lowestBitrate[1:]
}
metrics.medianBitrate = append(metrics.medianBitrate, TimestampedValue{
Time: time.Now(),
Value: math.Round(median),
})
if len(metrics.medianBitrate) > maxCollectionValues {
metrics.medianBitrate = metrics.medianBitrate[1:]
}
metrics.highestBitrate = append(metrics.highestBitrate, TimestampedValue{
Time: time.Now(),
Value: math.Round(max),
})
if len(metrics.highestBitrate) > maxCollectionValues {
metrics.highestBitrate = metrics.highestBitrate[1:]
}
}
// GetSlowestDownloadRateOverTime will return the collected lowest bandwidth values
@ -151,6 +243,38 @@ func GetSlowestDownloadRateOverTime() []TimestampedValue {
return metrics.lowestBitrate
}
// GetMedianDownloadRateOverTime will return the collected median bandwidth values.
func GetMedianDownloadRateOverTime() []TimestampedValue {
if len(metrics.medianBitrate) == 0 {
return []TimestampedValue{}
}
return metrics.medianBitrate
}
// GetMaximumDownloadRateOverTime will return the collected maximum bandwidth values.
func GetMaximumDownloadRateOverTime() []TimestampedValue {
if len(metrics.maximumLatency) == 0 {
return []TimestampedValue{}
}
return metrics.maximumLatency
}
// GetMinimumDownloadRateOverTime will return the collected minimum bandwidth values.
func GetMinimumDownloadRateOverTime() []TimestampedValue {
if len(metrics.minimumLatency) == 0 {
return []TimestampedValue{}
}
return metrics.minimumLatency
}
// GetMaxDownloadRateOverTime will return the collected highest bandwidth values.
func GetMaxDownloadRateOverTime() []TimestampedValue {
if len(metrics.highestBitrate) == 0 {
return []TimestampedValue{}
}
return metrics.highestBitrate
}
func collectQualityVariantChanges() {
count := utils.Sum(windowedQualityVariantChanges)
windowedQualityVariantChanges = []float64{}

View File

@ -82,3 +82,29 @@ func MinMax(array []float64) (float64, float64) {
}
return min, max
}
func mean(input []float64) float64 {
sum := Sum(input)
return sum / float64(len(input))
}
// Median gets the median number in a slice of numbers.
func Median(input []float64) float64 {
if len(input) == 1 {
return input[0]
}
c := make([]float64, len(input))
copy(c, input)
var median float64
l := len(c)
if l%2 == 0 {
median = mean(c[l/2-1 : l/2+1])
} else {
median = c[l/2]
}
return median
}