mirror of
https://github.com/livekit/livekit.git
synced 2026-05-24 14:35:34 +00:00
52c28a938d
* Log a bit more details of out-of-order TWCC feedback report. * Revert to mono.Now()
128 lines
3.7 KiB
Go
128 lines
3.7 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 sendsidebwe
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/livekit/protocol/logger"
|
|
"github.com/pion/rtcp"
|
|
"go.uber.org/zap/zapcore"
|
|
)
|
|
|
|
// ------------------------------------------------------
|
|
|
|
const (
|
|
cOutlierReportFactor = 3
|
|
cEstimatedFeedbackIntervalAlpha = float64(0.9)
|
|
|
|
cReferenceTimeMask = (1 << 24) - 1
|
|
cReferenceTimeResolution = 64 // 64 ms
|
|
)
|
|
|
|
// ------------------------------------------------------
|
|
|
|
type twccFeedbackParams struct {
|
|
Logger logger.Logger
|
|
}
|
|
|
|
type twccFeedback struct {
|
|
params twccFeedbackParams
|
|
|
|
lastFeedbackTime time.Time
|
|
estimatedFeedbackInterval time.Duration
|
|
numReports int
|
|
numReportsOutOfOrder int
|
|
|
|
highestFeedbackCount uint8
|
|
|
|
cycles int64
|
|
highestReferenceTime uint32
|
|
}
|
|
|
|
func newTWCCFeedback(params twccFeedbackParams) *twccFeedback {
|
|
return &twccFeedback{
|
|
params: params,
|
|
}
|
|
}
|
|
|
|
func (t *twccFeedback) ProcessReport(report *rtcp.TransportLayerCC, at time.Time) (int64, bool) {
|
|
t.numReports++
|
|
if t.lastFeedbackTime.IsZero() {
|
|
t.lastFeedbackTime = at
|
|
t.highestReferenceTime = report.ReferenceTime
|
|
t.highestFeedbackCount = report.FbPktCount
|
|
return (t.cycles + int64(report.ReferenceTime)) * cReferenceTimeResolution * 1000, false
|
|
}
|
|
|
|
isOutOfOrder := false
|
|
if (report.FbPktCount - t.highestFeedbackCount) > (1 << 7) {
|
|
t.numReportsOutOfOrder++
|
|
isOutOfOrder = true
|
|
t.params.Logger.Infow(
|
|
"send side bwe: received out-of-order feedback report",
|
|
"highestFeedbackCount", t.highestFeedbackCount,
|
|
"fbPktCount", report.FbPktCount,
|
|
"numReportsOutOfOrder", t.numReportsOutOfOrder,
|
|
)
|
|
}
|
|
|
|
// reference time wrap around handling
|
|
var referenceTime int64
|
|
if (report.ReferenceTime-t.highestReferenceTime)&cReferenceTimeMask < (1 << 23) {
|
|
if report.ReferenceTime < t.highestReferenceTime {
|
|
t.cycles += (1 << 24)
|
|
}
|
|
t.highestReferenceTime = report.ReferenceTime
|
|
referenceTime = t.cycles + int64(report.ReferenceTime)
|
|
} else {
|
|
cycles := t.cycles
|
|
if report.ReferenceTime > t.highestReferenceTime && cycles >= (1<<24) {
|
|
cycles -= (1 << 24)
|
|
}
|
|
referenceTime = cycles + int64(report.ReferenceTime)
|
|
}
|
|
|
|
if !isOutOfOrder {
|
|
sinceLast := at.Sub(t.lastFeedbackTime)
|
|
if t.estimatedFeedbackInterval == 0 {
|
|
t.estimatedFeedbackInterval = sinceLast
|
|
} else {
|
|
// filter out outliers from estimate
|
|
if sinceLast > t.estimatedFeedbackInterval/cOutlierReportFactor && sinceLast < cOutlierReportFactor*t.estimatedFeedbackInterval {
|
|
// smoothed version of inter feedback interval
|
|
t.estimatedFeedbackInterval = time.Duration(cEstimatedFeedbackIntervalAlpha*float64(t.estimatedFeedbackInterval) + (1.0-cEstimatedFeedbackIntervalAlpha)*float64(sinceLast))
|
|
}
|
|
}
|
|
t.lastFeedbackTime = at
|
|
t.highestFeedbackCount = report.FbPktCount
|
|
}
|
|
|
|
return referenceTime * cReferenceTimeResolution * 1000, isOutOfOrder
|
|
}
|
|
|
|
func (t *twccFeedback) MarshalLogObject(e zapcore.ObjectEncoder) error {
|
|
if t == nil {
|
|
return nil
|
|
}
|
|
|
|
e.AddTime("lastFeedbackTime", t.lastFeedbackTime)
|
|
e.AddDuration("estimatedFeedbackInterval", t.estimatedFeedbackInterval)
|
|
e.AddInt("numReports", t.numReports)
|
|
e.AddInt("numReportsOutOfOrder", t.numReportsOutOfOrder)
|
|
e.AddInt64("cycles", t.cycles/(1<<24))
|
|
return nil
|
|
}
|