Files
livekit/pkg/sfu/bwe/remotebwe/nack_tracker.go
Raja Subramanian c172ba13e6 Cleaning up unused stream allocator experiments. (#3237)
Not sure if we will ever use it. Can bring it back if needed.
2024-12-08 13:00:58 +05:30

131 lines
3.3 KiB
Go

// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package remotebwe
import (
"time"
"go.uber.org/zap/zapcore"
"github.com/livekit/protocol/logger"
)
// ------------------------------------------------
type NackTrackerConfig struct {
WindowMinDuration time.Duration `yaml:"window_min_duration,omitempty"`
WindowMaxDuration time.Duration `yaml:"window_max_duration,omitempty"`
RatioThreshold float64 `yaml:"ratio_threshold,omitempty"`
}
var (
defaultNackTrackerConfigProbe = NackTrackerConfig{
WindowMinDuration: 500 * time.Millisecond,
WindowMaxDuration: 1 * time.Second,
RatioThreshold: 0.04,
}
defaultNackTrackerConfigNonProbe = NackTrackerConfig{
WindowMinDuration: 2 * time.Second,
WindowMaxDuration: 3 * time.Second,
RatioThreshold: 0.08,
}
)
// ------------------------------------------------
type nackTrackerParams struct {
Name string
Logger logger.Logger
Config NackTrackerConfig
}
type nackTracker struct {
params nackTrackerParams
windowStartTime time.Time
packets uint32
repeatedNacks uint32
}
func newNackTracker(params nackTrackerParams) *nackTracker {
return &nackTracker{
params: params,
}
}
func (n *nackTracker) Add(packets uint32, repeatedNacks uint32) {
if n.params.Config.WindowMaxDuration != 0 && !n.windowStartTime.IsZero() && time.Since(n.windowStartTime) > n.params.Config.WindowMaxDuration {
n.windowStartTime = time.Time{}
n.packets = 0
n.repeatedNacks = 0
}
//
// Start NACK monitoring window only when a repeated NACK happens.
// This allows locking tightly to when NACKs start happening and
// check if the NACKs keep adding up (potentially a sign of congestion)
// or isolated losses
//
if n.repeatedNacks == 0 && repeatedNacks != 0 {
n.windowStartTime = time.Now()
}
if !n.windowStartTime.IsZero() {
n.packets += packets
n.repeatedNacks += repeatedNacks
}
}
func (n *nackTracker) GetRatio() float64 {
ratio := 0.0
if n.packets != 0 {
ratio = float64(n.repeatedNacks) / float64(n.packets)
if ratio > 1.0 {
ratio = 1.0
}
}
return ratio
}
func (n *nackTracker) IsTriggered() bool {
if n.params.Config.WindowMinDuration != 0 && !n.windowStartTime.IsZero() && time.Since(n.windowStartTime) > n.params.Config.WindowMinDuration {
return n.GetRatio() > n.params.Config.RatioThreshold
}
return false
}
func (n *nackTracker) MarshalLogObject(e zapcore.ObjectEncoder) error {
if n == nil {
return nil
}
e.AddString("name", n.params.Name)
if n.windowStartTime.IsZero() {
e.AddString("window", "inactive")
} else {
e.AddTime("windowStartTime", n.windowStartTime)
e.AddDuration("windowDuration", time.Since(n.windowStartTime))
e.AddUint32("packets", n.packets)
e.AddUint32("repeatedNacks", n.repeatedNacks)
e.AddFloat64("nackRatio", n.GetRatio())
}
return nil
}
// ------------------------------------------------