snapshot of https://github.com/livekit/livekit.git @ 08c5433449c106943231037d0b892895f6e92d3b (2026-09-15)
Some checks failed
Release to Docker / docker (push) Failing after 2s
Release Test Server to Docker / docker (push) Failing after 3s
Test / test (push) Failing after 16s

This commit is contained in:
upstream-mirror
2026-09-15 16:14:22 +05:00
commit 78704103fc
422 changed files with 154805 additions and 0 deletions

View File

@@ -0,0 +1,202 @@
// 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 (
"fmt"
"time"
"go.uber.org/zap/zapcore"
"github.com/livekit/livekit-server/pkg/sfu/ccutils"
"github.com/livekit/protocol/logger"
)
// ------------------------------------------------
type channelTrend int
const (
channelTrendInconclusive channelTrend = iota
channelTrendClearing
channelTrendCongesting
)
func (c channelTrend) String() string {
switch c {
case channelTrendInconclusive:
return "INCONCLUSIVE"
case channelTrendClearing:
return "CLEARING"
case channelTrendCongesting:
return "CONGESTING"
default:
return fmt.Sprintf("%d", int(c))
}
}
// ------------------------------------------------
type channelCongestionReason int
const (
channelCongestionReasonNone channelCongestionReason = iota
channelCongestionReasonEstimate
channelCongestionReasonLoss
)
func (c channelCongestionReason) String() string {
switch c {
case channelCongestionReasonNone:
return "NONE"
case channelCongestionReasonEstimate:
return "ESTIMATE"
case channelCongestionReasonLoss:
return "LOSS"
default:
return fmt.Sprintf("%d", int(c))
}
}
// ------------------------------------------------
type ChannelObserverConfig struct {
Estimate ccutils.TrendDetectorConfig `yaml:"estimate,omitempty"`
Nack NackTrackerConfig `yaml:"nack,omitempty"`
}
var (
defaultTrendDetectorConfigProbe = ccutils.TrendDetectorConfig{
RequiredSamples: 3,
RequiredSamplesMin: 3,
DownwardTrendThreshold: 0.0,
DownwardTrendMaxWait: 5 * time.Second,
CollapseThreshold: 0,
ValidityWindow: 10 * time.Second,
}
defaultChannelObserverConfigProbe = ChannelObserverConfig{
Estimate: defaultTrendDetectorConfigProbe,
Nack: defaultNackTrackerConfigProbe,
}
defaultTrendDetectorConfigNonProbe = ccutils.TrendDetectorConfig{
RequiredSamples: 12,
RequiredSamplesMin: 8,
DownwardTrendThreshold: -0.6,
DownwardTrendMaxWait: 5 * time.Second,
CollapseThreshold: 500 * time.Millisecond,
ValidityWindow: 10 * time.Second,
}
defaultChannelObserverConfigNonProbe = ChannelObserverConfig{
Estimate: defaultTrendDetectorConfigNonProbe,
Nack: defaultNackTrackerConfigNonProbe,
}
)
// ------------------------------------------------
type channelObserverParams struct {
Name string
Config ChannelObserverConfig
}
type channelObserver struct {
params channelObserverParams
logger logger.Logger
estimateTrend *ccutils.TrendDetector[int64]
nackTracker *nackTracker
}
func newChannelObserver(params channelObserverParams, logger logger.Logger) *channelObserver {
return &channelObserver{
params: params,
logger: logger,
estimateTrend: ccutils.NewTrendDetector[int64](ccutils.TrendDetectorParams{
Name: params.Name + "-estimate",
Logger: logger,
Config: params.Config.Estimate,
}),
nackTracker: newNackTracker(nackTrackerParams{
Name: params.Name + "-nack",
Logger: logger,
Config: params.Config.Nack,
}),
}
}
func (c *channelObserver) SeedEstimate(estimate int64) {
c.estimateTrend.Seed(estimate)
}
func (c *channelObserver) AddEstimate(estimate int64) {
c.estimateTrend.AddValue(estimate)
}
func (c *channelObserver) AddNack(packets uint32, repeatedNacks uint32) {
c.nackTracker.Add(packets, repeatedNacks)
}
func (c *channelObserver) GetLowestEstimate() int64 {
return c.estimateTrend.GetLowest()
}
func (c *channelObserver) GetHighestEstimate() int64 {
return c.estimateTrend.GetHighest()
}
func (c *channelObserver) HasEnoughEstimateSamples() bool {
return c.estimateTrend.HasEnoughSamples()
}
func (c *channelObserver) GetNackRatio() float64 {
return c.nackTracker.GetRatio()
}
func (c *channelObserver) GetTrend() (channelTrend, channelCongestionReason) {
estimateDirection := c.estimateTrend.GetDirection()
switch {
case estimateDirection == ccutils.TrendDirectionDownward:
return channelTrendCongesting, channelCongestionReasonEstimate
case c.nackTracker.IsTriggered():
return channelTrendCongesting, channelCongestionReasonLoss
case estimateDirection == ccutils.TrendDirectionUpward:
return channelTrendClearing, channelCongestionReasonNone
}
return channelTrendInconclusive, channelCongestionReasonNone
}
func (c *channelObserver) MarshalLogObject(e zapcore.ObjectEncoder) error {
if c == nil {
return nil
}
e.AddString("name", c.params.Name)
e.AddObject("estimate", c.estimateTrend)
e.AddObject("nack", c.nackTracker)
channelTrend, channelCongestionReason := c.GetTrend()
e.AddString("channelTrend", channelTrend.String())
e.AddString("channelCongestionReason", channelCongestionReason.String())
return nil
}
// ------------------------------------------------

View File

@@ -0,0 +1,130 @@
// 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
}
// ------------------------------------------------

View File

@@ -0,0 +1,184 @@
// 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 (
"fmt"
"time"
"github.com/livekit/livekit-server/pkg/sfu/bwe"
"github.com/livekit/livekit-server/pkg/sfu/ccutils"
"github.com/livekit/protocol/logger"
"github.com/livekit/protocol/utils/mono"
)
// ---------------------------------------------------------------------------
type probeControllerState int
const (
probeControllerStateNone probeControllerState = iota
probeControllerStateProbing
probeControllerStateHangover
)
func (p probeControllerState) String() string {
switch p {
case probeControllerStateNone:
return "NONE"
case probeControllerStateProbing:
return "PROBING"
case probeControllerStateHangover:
return "HANGOVER"
default:
return fmt.Sprintf("%d", int(p))
}
}
// ------------------------------------------------
type ProbeControllerConfig struct {
ProbeRegulator ccutils.ProbeRegulatorConfig `yaml:"probe_regulator,omitempty"`
SettleWaitNumRTT uint32 `yaml:"settle_wait_num_rtt,omitempty"`
SettleWaitMin time.Duration `yaml:"settle_wait_min,omitempty"`
SettleWaitMax time.Duration `yaml:"settle_wait_max,omitempty"`
}
var (
DefaultProbeControllerConfig = ProbeControllerConfig{
ProbeRegulator: ccutils.DefaultProbeRegulatorConfig,
SettleWaitNumRTT: 5,
SettleWaitMin: 250 * time.Millisecond,
SettleWaitMax: 5 * time.Second,
}
)
// ---------------------------------------------------------------------------
type probeControllerParams struct {
Config ProbeControllerConfig
Logger logger.Logger
}
type probeController struct {
params probeControllerParams
state probeControllerState
stateSwitchedAt time.Time
pci ccutils.ProbeClusterInfo
rtt float64
*ccutils.ProbeRegulator
}
func newProbeController(params probeControllerParams) *probeController {
return &probeController{
params: params,
state: probeControllerStateNone,
stateSwitchedAt: mono.Now(),
pci: ccutils.ProbeClusterInfoInvalid,
rtt: bwe.DefaultRTT,
ProbeRegulator: ccutils.NewProbeRegulator(
ccutils.ProbeRegulatorParams{
Config: params.Config.ProbeRegulator,
Logger: params.Logger,
},
),
}
}
func (p *probeController) UpdateRTT(rtt float64) {
if rtt == 0 {
p.rtt = bwe.DefaultRTT
} else {
if p.rtt == 0 {
p.rtt = rtt
} else {
p.rtt = bwe.RTTSmoothingFactor*rtt + (1.0-bwe.RTTSmoothingFactor)*p.rtt
}
}
}
func (p *probeController) GetRTT() float64 {
return p.rtt
}
func (p *probeController) CanProbe() bool {
return p.state == probeControllerStateNone && p.ProbeRegulator.CanProbe()
}
func (p *probeController) IsInProbe() bool {
return p.state != probeControllerStateNone
}
func (p *probeController) ProbeClusterStarting(pci ccutils.ProbeClusterInfo) {
if p.state != probeControllerStateNone {
p.params.Logger.Warnw("unexpected probe controller state", nil, "state", p.state)
}
p.setState(probeControllerStateProbing)
p.pci = pci
}
func (p *probeController) ProbeClusterDone(pci ccutils.ProbeClusterInfo) {
if p.pci.Id != pci.Id {
return
}
p.pci.Result = pci.Result
p.setState(probeControllerStateHangover)
}
func (p *probeController) ProbeClusterIsGoalReached(estimate int64) bool {
if p.pci.Id == ccutils.ProbeClusterIdInvalid {
return false
}
return estimate > int64(p.pci.Goal.DesiredBps)
}
func (p *probeController) MaybeFinalizeProbe() (ccutils.ProbeClusterInfo, bool) {
if p.state != probeControllerStateHangover {
return ccutils.ProbeClusterInfoInvalid, false
}
settleWait := time.Duration(float64(p.params.Config.SettleWaitNumRTT) * p.rtt * float64(time.Second))
if settleWait < p.params.Config.SettleWaitMin {
settleWait = p.params.Config.SettleWaitMin
}
if settleWait > p.params.Config.SettleWaitMax {
settleWait = p.params.Config.SettleWaitMax
}
if time.Since(p.stateSwitchedAt) < settleWait {
return ccutils.ProbeClusterInfoInvalid, false
}
p.setState(probeControllerStateNone)
return p.pci, true
}
func (p *probeController) setState(state probeControllerState) {
if state == p.state {
return
}
p.state = state
p.stateSwitchedAt = mono.Now()
}
// ------------------------------------------------

View File

@@ -0,0 +1,359 @@
// 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 (
"sync"
"time"
"github.com/livekit/livekit-server/pkg/sfu/bwe"
"github.com/livekit/livekit-server/pkg/sfu/ccutils"
"github.com/livekit/protocol/logger"
"github.com/livekit/protocol/utils/mono"
)
var _ bwe.BWE = (*RemoteBWE)(nil)
// ---------------------------------------------------------------------------
type RemoteBWEConfig struct {
NackRatioAttenuator float64 `yaml:"nack_ratio_attenuator,omitempty"`
ExpectedUsageThreshold float64 `yaml:"expected_usage_threshold,omitempty"`
ChannelObserverProbe ChannelObserverConfig `yaml:"channel_observer_probe,omitempty"`
ChannelObserverNonProbe ChannelObserverConfig `yaml:"channel_observer_non_probe,omitempty"`
ProbeController ProbeControllerConfig `yaml:"probe_controller,omitempty"`
}
var (
DefaultRemoteBWEConfig = RemoteBWEConfig{
NackRatioAttenuator: 0.4,
ExpectedUsageThreshold: 0.95,
ChannelObserverProbe: defaultChannelObserverConfigProbe,
ChannelObserverNonProbe: defaultChannelObserverConfigNonProbe,
ProbeController: DefaultProbeControllerConfig,
}
)
// ---------------------------------------------------------------------------
type RemoteBWEParams struct {
Config RemoteBWEConfig
Logger logger.Logger
}
type RemoteBWE struct {
bwe.NullBWE
params RemoteBWEParams
lock sync.RWMutex
lastReceivedEstimate int64
lastExpectedBandwidthUsage int64
committedChannelCapacity int64
probeController *probeController
channelObserver *channelObserver
congestionState bwe.CongestionState
congestionStateSwitchedAt time.Time
bweListener bwe.BWEListener
}
func NewRemoteBWE(params RemoteBWEParams) *RemoteBWE {
r := &RemoteBWE{
params: params,
}
r.Reset()
return r
}
func (r *RemoteBWE) Type() bwe.BWEType {
return bwe.BWETypeRemote
}
func (r *RemoteBWE) SetBWEListener(bweListener bwe.BWEListener) {
r.lock.Lock()
defer r.lock.Unlock()
r.bweListener = bweListener
}
func (r *RemoteBWE) getBWEListener() bwe.BWEListener {
r.lock.RLock()
defer r.lock.RUnlock()
return r.bweListener
}
func (r *RemoteBWE) Reset() {
r.lock.Lock()
defer r.lock.Unlock()
r.lastReceivedEstimate = 0
r.lastExpectedBandwidthUsage = 0
r.committedChannelCapacity = 100_000_000
r.congestionState = bwe.CongestionStateNone
r.congestionStateSwitchedAt = mono.Now()
r.probeController = newProbeController(probeControllerParams{
Config: r.params.Config.ProbeController,
Logger: r.params.Logger,
})
r.newChannelObserver()
}
func (r *RemoteBWE) HandleREMB(
receivedEstimate int64,
expectedBandwidthUsage int64,
sentPackets uint32,
repeatedNacks uint32,
) {
r.lock.Lock()
r.lastReceivedEstimate = receivedEstimate
r.lastExpectedBandwidthUsage = expectedBandwidthUsage
// in probe, freeze channel observer state if probe causes congestion till the probe is done,
// this is to ensure that probe result is not marked a success,
// an unsuccessful probe will not up allocate any tracks
if r.congestionState != bwe.CongestionStateNone && r.probeController.IsInProbe() {
r.lock.Unlock()
return
}
r.channelObserver.AddEstimate(r.lastReceivedEstimate)
r.channelObserver.AddNack(sentPackets, repeatedNacks)
shouldNotify, fromState, toState, committedChannelCapacity := r.congestionDetectionStateMachine()
r.lock.Unlock()
if shouldNotify {
if bweListener := r.getBWEListener(); bweListener != nil {
bweListener.OnCongestionStateChange(fromState, toState, committedChannelCapacity)
}
}
}
func (r *RemoteBWE) UpdateRTT(rtt float64) {
r.lock.Lock()
defer r.lock.Unlock()
r.probeController.UpdateRTT(rtt)
}
func (r *RemoteBWE) congestionDetectionStateMachine() (bool, bwe.CongestionState, bwe.CongestionState, int64) {
fromState := r.congestionState
toState := r.congestionState
update := false
trend, reason := r.channelObserver.GetTrend()
switch fromState {
case bwe.CongestionStateNone:
if trend == channelTrendCongesting {
if r.probeController.IsInProbe() || r.estimateAvailableChannelCapacity(reason) {
// when in probe, if congested, stays there till probe is done,
// the estimate stays at pre-probe level
toState = bwe.CongestionStateCongested
}
}
case bwe.CongestionStateCongested:
if trend == channelTrendCongesting {
if r.estimateAvailableChannelCapacity(reason) {
// update state as this needs to reset switch time to wait for congestion min duration again
update = true
}
} else {
toState = bwe.CongestionStateNone
}
}
shouldNotify := false
if toState != fromState || update {
fromState, toState = r.updateCongestionState(toState, reason)
shouldNotify = true
}
return shouldNotify, fromState, toState, r.committedChannelCapacity
}
func (r *RemoteBWE) estimateAvailableChannelCapacity(reason channelCongestionReason) bool {
var estimateToCommit int64
switch reason {
case channelCongestionReasonLoss:
estimateToCommit = int64(float64(r.lastExpectedBandwidthUsage) * (1.0 - r.params.Config.NackRatioAttenuator*r.channelObserver.GetNackRatio()))
default:
estimateToCommit = r.lastReceivedEstimate
}
if estimateToCommit > r.lastReceivedEstimate {
estimateToCommit = r.lastReceivedEstimate
}
commitThreshold := int64(r.params.Config.ExpectedUsageThreshold * float64(r.lastExpectedBandwidthUsage))
if estimateToCommit > commitThreshold || r.committedChannelCapacity == estimateToCommit {
return false
}
r.params.Logger.Infow(
"remote bwe: channel congestion detected, applying channel capacity update",
"reason", reason,
"old(bps)", r.committedChannelCapacity,
"new(bps)", estimateToCommit,
"lastReceived(bps)", r.lastReceivedEstimate,
"expectedUsage(bps)", r.lastExpectedBandwidthUsage,
"commitThreshold(bps)", commitThreshold,
"channel", r.channelObserver,
)
r.committedChannelCapacity = estimateToCommit
return true
}
func (r *RemoteBWE) updateCongestionState(state bwe.CongestionState, reason channelCongestionReason) (bwe.CongestionState, bwe.CongestionState) {
r.params.Logger.Debugw(
"remote bwe: congestion state change",
"from", r.congestionState,
"to", state,
"reason", reason,
"committedChannelCapacity", r.committedChannelCapacity,
)
fromState := r.congestionState
r.congestionState = state
r.congestionStateSwitchedAt = mono.Now()
return fromState, r.congestionState
}
func (r *RemoteBWE) CongestionState() bwe.CongestionState {
r.lock.Lock()
defer r.lock.Unlock()
return r.congestionState
}
func (r *RemoteBWE) CanProbe() bool {
r.lock.Lock()
defer r.lock.Unlock()
return r.congestionState == bwe.CongestionStateNone && r.probeController.CanProbe()
}
func (r *RemoteBWE) ProbeDuration() time.Duration {
r.lock.Lock()
defer r.lock.Unlock()
return r.probeController.ProbeDuration()
}
func (r *RemoteBWE) ProbeClusterStarting(pci ccutils.ProbeClusterInfo) {
r.lock.Lock()
defer r.lock.Unlock()
r.lastExpectedBandwidthUsage = int64(pci.Goal.ExpectedUsageBps)
r.params.Logger.Debugw(
"remote bwe: starting probe",
"lastReceived", r.lastReceivedEstimate,
"expectedBandwidthUsage", r.lastExpectedBandwidthUsage,
"channel", r.channelObserver,
)
r.probeController.ProbeClusterStarting(pci)
r.newChannelObserver()
}
func (r *RemoteBWE) ProbeClusterDone(pci ccutils.ProbeClusterInfo) {
r.lock.Lock()
defer r.lock.Unlock()
r.probeController.ProbeClusterDone(pci)
}
func (r *RemoteBWE) ProbeClusterIsGoalReached() bool {
r.lock.Lock()
defer r.lock.Unlock()
if !r.probeController.IsInProbe() ||
r.congestionState != bwe.CongestionStateNone ||
!r.channelObserver.HasEnoughEstimateSamples() {
return false
}
return r.probeController.ProbeClusterIsGoalReached(r.channelObserver.GetHighestEstimate())
}
func (r *RemoteBWE) ProbeClusterFinalize() (ccutils.ProbeSignal, int64, bool) {
r.lock.Lock()
defer r.lock.Unlock()
pci, isFinalized := r.probeController.MaybeFinalizeProbe()
if !isFinalized {
return ccutils.ProbeSignalInconclusive, 0, isFinalized
}
// switch to a non-probe channel observer on probe end,
// reset congestion state to get a fresh trend
pco := r.channelObserver
probeCongestionState := r.congestionState
r.congestionState = bwe.CongestionStateNone
r.newChannelObserver()
r.params.Logger.Infow(
"remote bwe: probe finalized",
"lastReceived", r.lastReceivedEstimate,
"expectedBandwidthUsage", r.lastExpectedBandwidthUsage,
"channel", pco,
"isSignalValid", pco.HasEnoughEstimateSamples(),
"probeClusterInfo", pci,
"rtt", r.probeController.GetRTT(),
)
probeSignal := ccutils.ProbeSignalNotCongesting
if probeCongestionState != bwe.CongestionStateNone {
probeSignal = ccutils.ProbeSignalCongesting
} else if !pco.HasEnoughEstimateSamples() {
probeSignal = ccutils.ProbeSignalInconclusive
} else {
highestEstimate := pco.GetHighestEstimate()
if highestEstimate > r.committedChannelCapacity {
r.committedChannelCapacity = highestEstimate
}
}
r.probeController.ProbeSignal(probeSignal, pci.CreatedAt)
return probeSignal, r.committedChannelCapacity, true
}
func (r *RemoteBWE) newChannelObserver() {
var params channelObserverParams
if r.probeController.IsInProbe() {
params = channelObserverParams{
Name: "probe",
Config: r.params.Config.ChannelObserverProbe,
}
} else {
params = channelObserverParams{
Name: "non-probe",
Config: r.params.Config.ChannelObserverNonProbe,
}
}
r.channelObserver = newChannelObserver(params, r.params.Logger)
}