Expand upon playback metrics
This commit is contained in:
parent
d84da617b7
commit
6479220e78
@ -13,13 +13,22 @@ import (
|
|||||||
// GetVideoPlaybackMetrics returns video playback metrics.
|
// GetVideoPlaybackMetrics returns video playback metrics.
|
||||||
func GetVideoPlaybackMetrics(w http.ResponseWriter, r *http.Request) {
|
func GetVideoPlaybackMetrics(w http.ResponseWriter, r *http.Request) {
|
||||||
type response struct {
|
type response struct {
|
||||||
Errors []metrics.TimestampedValue `json:"errors"`
|
Errors []metrics.TimestampedValue `json:"errors"`
|
||||||
QualityVariantChanges []metrics.TimestampedValue `json:"qualityVariantChanges"`
|
QualityVariantChanges []metrics.TimestampedValue `json:"qualityVariantChanges"`
|
||||||
Latency []metrics.TimestampedValue `json:"latency"`
|
|
||||||
SegmentDownloadDuration []metrics.TimestampedValue `json:"segmentDownloadDuration"`
|
HighestLatency []metrics.TimestampedValue `json:"highestLatency"`
|
||||||
SlowestDownloadRate []metrics.TimestampedValue `json:"minPlayerBitrate"`
|
MedianLatency []metrics.TimestampedValue `json:"medianLatency"`
|
||||||
AvailableBitrates []int `json:"availableBitrates"`
|
LowestLatency []metrics.TimestampedValue `json:"lowestLatency"`
|
||||||
SegmentLength int `json:"segmentLength"`
|
|
||||||
|
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"`
|
||||||
}
|
}
|
||||||
|
|
||||||
availableBitrates := []int{}
|
availableBitrates := []int{}
|
||||||
@ -37,18 +46,32 @@ func GetVideoPlaybackMetrics(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
errors := metrics.GetPlaybackErrorCountOverTime()
|
errors := metrics.GetPlaybackErrorCountOverTime()
|
||||||
latency := metrics.GetLatencyOverTime()
|
medianLatency := metrics.GetMedianLatencyOverTime()
|
||||||
durations := metrics.GetDownloadDurationsOverTime()
|
minimumLatency := metrics.GetMinimumLatencyOverTime()
|
||||||
|
maximumLatency := metrics.GetMaximumLatencyOverTime()
|
||||||
|
|
||||||
|
medianDurations := metrics.GetMedianDownloadDurationsOverTime()
|
||||||
|
maximumDurations := metrics.GetMaximumDownloadDurationsOverTime()
|
||||||
|
minimumDurations := metrics.GetMinimumDownloadDurationsOverTime()
|
||||||
|
|
||||||
minPlayerBitrate := metrics.GetSlowestDownloadRateOverTime()
|
minPlayerBitrate := metrics.GetSlowestDownloadRateOverTime()
|
||||||
|
medianPlayerBitrate := metrics.GetMedianDownloadRateOverTime()
|
||||||
|
maxPlayerBitrate := metrics.GetMaxDownloadRateOverTime()
|
||||||
qualityVariantChanges := metrics.GetQualityVariantChangesOverTime()
|
qualityVariantChanges := metrics.GetQualityVariantChangesOverTime()
|
||||||
|
|
||||||
resp := response{
|
resp := response{
|
||||||
AvailableBitrates: availableBitrates,
|
AvailableBitrates: availableBitrates,
|
||||||
Errors: errors,
|
Errors: errors,
|
||||||
Latency: latency,
|
MedianLatency: medianLatency,
|
||||||
|
HighestLatency: maximumLatency,
|
||||||
|
LowestLatency: minimumLatency,
|
||||||
SegmentLength: segmentLength,
|
SegmentLength: segmentLength,
|
||||||
SegmentDownloadDuration: durations,
|
MedianDownloadDuration: medianDurations,
|
||||||
|
MaximumDownloadDuration: maximumDurations,
|
||||||
|
MinimumDownloadDuration: minimumDurations,
|
||||||
SlowestDownloadRate: minPlayerBitrate,
|
SlowestDownloadRate: minPlayerBitrate,
|
||||||
|
MedianDownloadRate: medianPlayerBitrate,
|
||||||
|
HighestDownloadRater: maxPlayerBitrate,
|
||||||
QualityVariantChanges: qualityVariantChanges,
|
QualityVariantChanges: qualityVariantChanges,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,14 +20,24 @@ const (
|
|||||||
|
|
||||||
// CollectedMetrics stores different collected + timestamped values.
|
// CollectedMetrics stores different collected + timestamped values.
|
||||||
type CollectedMetrics struct {
|
type CollectedMetrics struct {
|
||||||
CPUUtilizations []TimestampedValue `json:"cpu"`
|
CPUUtilizations []TimestampedValue `json:"cpu"`
|
||||||
RAMUtilizations []TimestampedValue `json:"memory"`
|
RAMUtilizations []TimestampedValue `json:"memory"`
|
||||||
DiskUtilizations []TimestampedValue `json:"disk"`
|
DiskUtilizations []TimestampedValue `json:"disk"`
|
||||||
errorCount []TimestampedValue `json:"-"`
|
|
||||||
lowestBitrate []TimestampedValue `json:"-"`
|
errorCount []TimestampedValue `json:"-"`
|
||||||
segmentDownloadSeconds []TimestampedValue `json:"-"`
|
lowestBitrate []TimestampedValue `json:"-"`
|
||||||
averageLatency []TimestampedValue `json:"-"`
|
medianBitrate []TimestampedValue `json:"-"`
|
||||||
qualityVariantChanges []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:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Metrics is the shared Metrics instance.
|
// Metrics is the shared Metrics instance.
|
||||||
|
@ -65,25 +65,57 @@ func collectPlaybackErrorCount() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func collectSegmentDownloadDuration() {
|
func collectSegmentDownloadDuration() {
|
||||||
val := 0.0
|
median := 0.0
|
||||||
|
max := 0.0
|
||||||
|
min := 0.0
|
||||||
|
|
||||||
if len(windowedDownloadDurations) > 0 {
|
if len(windowedDownloadDurations) > 0 {
|
||||||
val = utils.Avg(windowedDownloadDurations)
|
median = utils.Median(windowedDownloadDurations)
|
||||||
|
min, max = utils.MinMax(windowedDownloadDurations)
|
||||||
windowedDownloadDurations = []float64{}
|
windowedDownloadDurations = []float64{}
|
||||||
}
|
}
|
||||||
metrics.segmentDownloadSeconds = append(metrics.segmentDownloadSeconds, TimestampedValue{
|
|
||||||
|
metrics.medianSegmentDownloadSeconds = append(metrics.medianSegmentDownloadSeconds, TimestampedValue{
|
||||||
Time: time.Now(),
|
Time: time.Now(),
|
||||||
Value: val,
|
Value: median,
|
||||||
})
|
})
|
||||||
|
|
||||||
if len(metrics.segmentDownloadSeconds) > maxCollectionValues {
|
if len(metrics.medianSegmentDownloadSeconds) > maxCollectionValues {
|
||||||
metrics.segmentDownloadSeconds = metrics.segmentDownloadSeconds[1:]
|
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.
|
// GetMedianDownloadDurationsOverTime will return a window of durations errors over time.
|
||||||
func GetDownloadDurationsOverTime() []TimestampedValue {
|
func GetMedianDownloadDurationsOverTime() []TimestampedValue {
|
||||||
return metrics.segmentDownloadSeconds
|
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.
|
// GetPlaybackErrorCountOverTime will return a window of playback errors over time.
|
||||||
@ -92,53 +124,113 @@ func GetPlaybackErrorCountOverTime() []TimestampedValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func collectLatencyValues() {
|
func collectLatencyValues() {
|
||||||
val := 0.0
|
median := 0.0
|
||||||
|
min := 0.0
|
||||||
|
max := 0.0
|
||||||
|
|
||||||
if len(windowedLatencies) > 0 {
|
if len(windowedLatencies) > 0 {
|
||||||
val = utils.Avg(windowedLatencies)
|
median = utils.Median(windowedLatencies)
|
||||||
val = math.Round(val)
|
min, max = utils.MinMax(windowedLatencies)
|
||||||
windowedLatencies = []float64{}
|
windowedLatencies = []float64{}
|
||||||
}
|
}
|
||||||
|
|
||||||
metrics.averageLatency = append(metrics.averageLatency, TimestampedValue{
|
metrics.medianLatency = append(metrics.medianLatency, TimestampedValue{
|
||||||
Time: time.Now(),
|
Time: time.Now(),
|
||||||
Value: val,
|
Value: median,
|
||||||
})
|
})
|
||||||
|
|
||||||
if len(metrics.averageLatency) > maxCollectionValues {
|
if len(metrics.medianLatency) > maxCollectionValues {
|
||||||
metrics.averageLatency = metrics.averageLatency[1:]
|
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.
|
// GetMedianLatencyOverTime will return the median latency values over time.
|
||||||
func GetLatencyOverTime() []TimestampedValue {
|
func GetMedianLatencyOverTime() []TimestampedValue {
|
||||||
if len(metrics.averageLatency) == 0 {
|
if len(metrics.medianLatency) == 0 {
|
||||||
return []TimestampedValue{}
|
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
|
// so we can report to the streamer the worst possible streaming condition
|
||||||
// being experienced.
|
// being experienced.
|
||||||
func collectLowestBandwidth() {
|
func collectLowestBandwidth() {
|
||||||
val := 0.0
|
min := 0.0
|
||||||
|
median := 0.0
|
||||||
|
max := 0.0
|
||||||
|
|
||||||
if len(windowedBandwidths) > 0 {
|
if len(windowedBandwidths) > 0 {
|
||||||
val, _ = utils.MinMax(windowedBandwidths)
|
min, max = utils.MinMax(windowedBandwidths)
|
||||||
val = math.Round(val)
|
min = math.Round(min)
|
||||||
|
max = math.Round(max)
|
||||||
|
median = utils.Median(windowedBandwidths)
|
||||||
windowedBandwidths = []float64{}
|
windowedBandwidths = []float64{}
|
||||||
}
|
}
|
||||||
|
|
||||||
metrics.lowestBitrate = append(metrics.lowestBitrate, TimestampedValue{
|
metrics.lowestBitrate = append(metrics.lowestBitrate, TimestampedValue{
|
||||||
Time: time.Now(),
|
Time: time.Now(),
|
||||||
Value: math.Round(val),
|
Value: math.Round(min),
|
||||||
})
|
})
|
||||||
|
|
||||||
if len(metrics.lowestBitrate) > maxCollectionValues {
|
if len(metrics.lowestBitrate) > maxCollectionValues {
|
||||||
metrics.lowestBitrate = metrics.lowestBitrate[1:]
|
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
|
// GetSlowestDownloadRateOverTime will return the collected lowest bandwidth values
|
||||||
@ -151,6 +243,38 @@ func GetSlowestDownloadRateOverTime() []TimestampedValue {
|
|||||||
return metrics.lowestBitrate
|
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() {
|
func collectQualityVariantChanges() {
|
||||||
count := utils.Sum(windowedQualityVariantChanges)
|
count := utils.Sum(windowedQualityVariantChanges)
|
||||||
windowedQualityVariantChanges = []float64{}
|
windowedQualityVariantChanges = []float64{}
|
||||||
|
@ -82,3 +82,29 @@ func MinMax(array []float64) (float64, float64) {
|
|||||||
}
|
}
|
||||||
return min, max
|
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
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user