Files
livekit/pkg/sfu/buffer/fps.go
T
Raja SubramanianandClaude Opus 5.5 9ddcc50767 sfu: parse VP8 and VP9 payload descriptors without an allocation (#4981)
* sfu: parse VP8 and VP9 payload descriptors without an allocation

Replace ExtPacket.Payload with typed VP8 and VP9 pointer fields that point
at backing structs inside the ExtPacket, so parsing does not allocate and a
wrong codec type is a compile error.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* sfu: copy the VP8 descriptor in the test packet helper

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 20:43:17 +05:30

746 lines
19 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 buffer
import (
"container/list"
"github.com/livekit/protocol/logger"
)
var minFramesForCalculation = [...]int{8, 15, 40, 60}
type frameInfo struct {
startSeq uint16
endSeq uint16
ts uint32
fn uint16
spatial int32
temporal int32
}
type FrameRateCalculator interface {
RecvPacket(ep *ExtPacket) bool
GetFrameRate() []float32
Completed() bool
}
// -----------------------------
// FrameRateCalculator based on PictureID in VPx
type frameRateCalculatorVPx struct {
frameRates [DefaultMaxLayerTemporal + 1]float32
clockRate uint32
logger logger.Logger
firstFrames [DefaultMaxLayerTemporal + 1]*frameInfo
secondFrames [DefaultMaxLayerTemporal + 1]*frameInfo
fnReceived [64]*frameInfo
baseFrame *frameInfo
completed bool
}
func newFrameRateCalculatorVPx(clockRate uint32, logger logger.Logger) *frameRateCalculatorVPx {
return &frameRateCalculatorVPx{
clockRate: clockRate,
logger: logger,
}
}
func (f *frameRateCalculatorVPx) Completed() bool {
return f.completed
}
func (f *frameRateCalculatorVPx) RecvPacket(ep *ExtPacket, fn uint16) bool {
if f.completed {
return true
}
if ep.Temporal >= int32(len(f.frameRates)) {
f.logger.Warnw("invalid temporal layer", nil, "temporal", ep.Temporal)
return false
}
temporal := ep.Temporal
if temporal < 0 {
temporal = 0
}
if f.baseFrame == nil {
f.baseFrame = &frameInfo{ts: ep.Packet.Timestamp, fn: fn}
f.fnReceived[0] = f.baseFrame
f.firstFrames[temporal] = f.baseFrame
return false
}
baseDiff := fn - f.baseFrame.fn
if baseDiff == 0 || baseDiff > 0x4000 {
return false
}
if baseDiff >= uint16(len(f.fnReceived)) {
// frame number is not continuous, reset
f.reset()
return false
}
if f.fnReceived[baseDiff] != nil {
return false
}
fi := &frameInfo{
ts: ep.Packet.Timestamp,
fn: fn,
temporal: temporal,
}
f.fnReceived[baseDiff] = fi
firstFrame := f.firstFrames[temporal]
secondFrame := f.secondFrames[temporal]
if firstFrame == nil {
f.firstFrames[temporal] = fi
firstFrame = fi
} else {
if (secondFrame == nil || secondFrame.fn < fn) && fn != firstFrame.fn && (fn-firstFrame.fn) < 0x4000 {
f.secondFrames[temporal] = fi
}
}
return f.calc()
}
func (f *frameRateCalculatorVPx) calc() bool {
var rateCounter int
for currentTemporal := int32(0); currentTemporal <= DefaultMaxLayerTemporal; currentTemporal++ {
if f.frameRates[currentTemporal] > 0 {
rateCounter++
continue
}
ff := f.firstFrames[currentTemporal]
sf := f.secondFrames[currentTemporal]
// lower temporal layer has been calculated, but higher layer has not received any frames, it should not exist
if rateCounter > 0 && ff == nil {
rateCounter++
continue
}
if ff == nil || sf == nil {
continue
}
var frameCount int
lastTs := ff.ts
for j := ff.fn - f.baseFrame.fn + 1; j < sf.fn-f.baseFrame.fn+1; j++ {
if f := f.fnReceived[j]; f == nil {
break
} else if f.temporal <= currentTemporal {
frameCount++
lastTs = f.ts
}
}
if frameCount >= minFramesForCalculation[currentTemporal] {
f.frameRates[currentTemporal] = float32(f.clockRate) / float32(lastTs-ff.ts) * float32(frameCount)
rateCounter++
}
}
if rateCounter == len(f.frameRates) {
f.completed = true
// normalize frame rates, Microsoft Edge use 3 temporal layers for vp8 but the middle layer has chance to
// get a very low frame rate, so we need to normalize the frame rate(use fixed ration 1:2 of highest layer for that layer)
if f.frameRates[2] > 0 && f.frameRates[2] > f.frameRates[1]*3 {
f.frameRates[1] = f.frameRates[2] / 2
}
f.reset()
return true
}
return false
}
func (f *frameRateCalculatorVPx) reset() {
for i := range f.firstFrames {
f.firstFrames[i] = nil
f.secondFrames[i] = nil
}
for i := range f.fnReceived {
f.fnReceived[i] = nil
}
f.baseFrame = nil
}
func (f *frameRateCalculatorVPx) GetFrameRate() []float32 {
return f.frameRates[:]
}
// -----------------------------
// FrameRateCalculator based on PictureID in VP8
type FrameRateCalculatorVP8 struct {
*frameRateCalculatorVPx
logger logger.Logger
}
func NewFrameRateCalculatorVP8(clockRate uint32, logger logger.Logger) *FrameRateCalculatorVP8 {
return &FrameRateCalculatorVP8{
frameRateCalculatorVPx: newFrameRateCalculatorVPx(clockRate, logger),
logger: logger,
}
}
func (f *FrameRateCalculatorVP8) RecvPacket(ep *ExtPacket) bool {
if f.frameRateCalculatorVPx.Completed() {
return true
}
vp8 := ep.VP8
if vp8 == nil {
f.logger.Debugw("no vp8 payload", "sn", ep.Packet.SequenceNumber)
return false
}
if !vp8.I {
f.logger.Debugw("no vp8 pictureID", "sn", ep.Packet.SequenceNumber)
return false
}
success := f.frameRateCalculatorVPx.RecvPacket(ep, vp8.PictureID)
if f.frameRateCalculatorVPx.Completed() {
f.logger.Debugw("frame rate calculated", "rate", f.frameRateCalculatorVPx.GetFrameRate())
}
return success
}
// -----------------------------
// FrameRateCalculator based on PictureID in VP9
type FrameRateCalculatorVP9 struct {
logger logger.Logger
completed bool
// VP9-TODO - this is assuming three spatial layers. As `completed` marker relies on all layers being finished, have to assume this. FIX.
// Maybe look at number of layers in livekit.TrackInfo and declare completed once advertised layers are measured
frameRateCalculatorsVPx [DefaultMaxLayerSpatial + 1]*frameRateCalculatorVPx
}
func NewFrameRateCalculatorVP9(clockRate uint32, logger logger.Logger) *FrameRateCalculatorVP9 {
f := &FrameRateCalculatorVP9{
logger: logger,
}
for i := range f.frameRateCalculatorsVPx {
f.frameRateCalculatorsVPx[i] = newFrameRateCalculatorVPx(clockRate, logger)
}
return f
}
func (f *FrameRateCalculatorVP9) Completed() bool {
return f.completed
}
func (f *FrameRateCalculatorVP9) RecvPacket(ep *ExtPacket) bool {
if f.completed {
return true
}
vp9 := ep.VP9
if vp9 == nil {
f.logger.Debugw("no vp9 payload", "sn", ep.Packet.SequenceNumber)
return false
}
if !vp9.I {
f.logger.Debugw("no vp9 pictureID", "sn", ep.Packet.SequenceNumber)
return false
}
if ep.Spatial < 0 || ep.Spatial >= int32(len(f.frameRateCalculatorsVPx)) || f.frameRateCalculatorsVPx[ep.Spatial] == nil {
f.logger.Debugw("invalid spatial layer", "sn", ep.Packet.SequenceNumber, "spatial", ep.Spatial)
return false
}
success := f.frameRateCalculatorsVPx[ep.Spatial].RecvPacket(ep, vp9.PictureID)
completed := true
for _, frc := range f.frameRateCalculatorsVPx {
if !frc.Completed() {
completed = false
break
}
}
if completed {
f.completed = true
var frameRates [DefaultMaxLayerSpatial + 1][]float32
for i := range f.frameRateCalculatorsVPx {
frameRates[i] = f.frameRateCalculatorsVPx[i].GetFrameRate()
}
f.logger.Debugw("frame rate calculated", "rate", frameRates)
}
return success
}
func (f *FrameRateCalculatorVP9) GetFrameRateForSpatial(spatial int32) []float32 {
if spatial < 0 || spatial >= int32(len(f.frameRateCalculatorsVPx)) || f.frameRateCalculatorsVPx[spatial] == nil {
return nil
}
return f.frameRateCalculatorsVPx[spatial].GetFrameRate()
}
func (f *FrameRateCalculatorVP9) GetFrameRateCalculatorForSpatial(spatial int32) *FrameRateCalculatorForVP9Layer {
return &FrameRateCalculatorForVP9Layer{
FrameRateCalculatorVP9: f,
spatial: spatial,
}
}
// -----------------------------
type FrameRateCalculatorForVP9Layer struct {
*FrameRateCalculatorVP9
spatial int32
}
func (f *FrameRateCalculatorForVP9Layer) GetFrameRate() []float32 {
return f.FrameRateCalculatorVP9.GetFrameRateForSpatial(f.spatial)
}
// -----------------------------------------------
// FrameRateCalculator based on Dependency descriptor
type FrameRateCalculatorDD struct {
frameRates [DefaultMaxLayerSpatial + 1][DefaultMaxLayerTemporal + 1]float32
clockRate uint32
logger logger.Logger
firstFrames [DefaultMaxLayerSpatial + 1][DefaultMaxLayerTemporal + 1]*frameInfo
secondFrames [DefaultMaxLayerSpatial + 1][DefaultMaxLayerTemporal + 1]*frameInfo
fnReceived [256]*frameInfo
baseFrame *frameInfo
completed bool
// frames for each decode target
targetFrames [DefaultMaxLayerSpatial + 1][DefaultMaxLayerTemporal + 1]list.List
maxSpatial, maxTemporal int32
}
func NewFrameRateCalculatorDD(clockRate uint32, logger logger.Logger) *FrameRateCalculatorDD {
return &FrameRateCalculatorDD{
clockRate: clockRate,
logger: logger,
maxSpatial: DefaultMaxLayerSpatial,
maxTemporal: DefaultMaxLayerTemporal,
}
}
func (f *FrameRateCalculatorDD) Completed() bool {
return f.completed
}
func (f *FrameRateCalculatorDD) SetMaxLayer(spatial, temporal int32) {
if spatial <= DefaultMaxLayerSpatial {
f.maxSpatial = spatial
} else {
f.logger.Warnw("max spatial layer out of range", nil, "spatial", spatial)
}
if temporal <= DefaultMaxLayerTemporal {
f.maxTemporal = temporal
} else {
f.logger.Warnw("max temporal layer out of range", nil, "temporal", temporal)
}
}
func (f *FrameRateCalculatorDD) RecvPacket(ep *ExtPacket) bool {
if f.completed {
return true
}
if ep.DependencyDescriptor == nil {
f.logger.Debugw("dependency descriptor is nil")
return false
}
spatial := ep.Spatial
// non-SVC codec will set spatial to -1
if spatial < 0 {
spatial = 0
}
temporal := ep.Temporal
if temporal < 0 || temporal > DefaultMaxLayerTemporal || spatial > DefaultMaxLayerSpatial {
f.logger.Warnw("invalid spatial or temporal", nil, "spatial", spatial, "temporal", temporal, "sn", ep.Packet.SequenceNumber)
return false
}
fn := ep.DependencyDescriptor.Descriptor.FrameNumber
if f.baseFrame == nil {
f.baseFrame = &frameInfo{ts: ep.Packet.Timestamp, fn: fn}
f.fnReceived[0] = f.baseFrame
f.firstFrames[spatial][temporal] = f.baseFrame
f.secondFrames[spatial][temporal] = f.baseFrame
return false
}
baseDiff := fn - f.baseFrame.fn
if baseDiff == 0 || baseDiff > 0x8000 {
return false
}
if baseDiff >= uint16(len(f.fnReceived)) {
// frame number is not continuous, reset
f.baseFrame = nil
for i := range f.firstFrames {
for j := range f.firstFrames[i] {
f.firstFrames[i][j] = nil
f.secondFrames[i][j] = nil
f.targetFrames[i][j].Init()
}
}
for i := range f.fnReceived {
f.fnReceived[i] = nil
}
return false
}
if f.fnReceived[baseDiff] != nil {
return false
}
fi := &frameInfo{
ts: ep.Packet.Timestamp,
fn: fn,
temporal: temporal,
spatial: spatial,
}
f.fnReceived[baseDiff] = fi
if f.firstFrames[spatial][temporal] == nil {
f.firstFrames[spatial][temporal] = fi
f.secondFrames[spatial][temporal] = fi
return false
}
chain := &f.targetFrames[spatial][temporal]
if chain.Len() == 0 {
chain.PushBack(fn)
}
for _, fdiff := range ep.DependencyDescriptor.Descriptor.FrameDependencies.FrameDiffs {
dependFrame := fn - uint16(fdiff)
// frame too old, ignore
if dependFrame-f.secondFrames[spatial][temporal].fn > 0x8000 {
continue
}
insertFrame:
for e := chain.Back(); e != nil; e = e.Prev() {
val := e.Value.(uint16)
switch {
case val == dependFrame:
break insertFrame
case sn16LT(val, dependFrame):
chain.InsertAfter(dependFrame, e)
break insertFrame
default:
if e == chain.Front() {
chain.PushFront(dependFrame)
break insertFrame
}
}
}
}
return f.calc()
}
func (f *FrameRateCalculatorDD) calc() bool {
var rateCounter int
for currentSpatial := int32(0); currentSpatial <= f.maxSpatial; currentSpatial++ {
var currentSpatialRateCounter int
for currentTemporal := int32(0); currentTemporal <= f.maxTemporal; currentTemporal++ {
if f.frameRates[currentSpatial][currentTemporal] > 0 {
rateCounter++
currentSpatialRateCounter++
continue
}
firstFrame := f.firstFrames[currentSpatial][currentTemporal]
// lower temporal layer has been calculated, but higher layer has not received any frames, it should not exist
if currentSpatialRateCounter > 0 && firstFrame == nil {
currentSpatialRateCounter++
rateCounter++
continue
}
chain := &f.targetFrames[currentSpatial][currentTemporal]
// find last decodable frame (no dependency frame is lost)
var lastFrame *frameInfo
for e := chain.Front(); e != nil; e = e.Next() {
diff := e.Value.(uint16) - f.baseFrame.fn
if diff >= uint16(len(f.fnReceived)) {
continue
}
fi := f.fnReceived[diff]
if fi == nil {
break
} else {
lastFrame = fi
if firstFrame == nil && fi.spatial == currentSpatial && fi.temporal == currentTemporal {
firstFrame = fi
}
}
}
if lastFrame != nil && lastFrame.fn > f.secondFrames[currentSpatial][currentTemporal].fn {
f.secondFrames[currentSpatial][currentTemporal] = lastFrame
} else {
continue
}
frameCount := 0
for i := firstFrame.fn - f.baseFrame.fn; i <= lastFrame.fn-f.baseFrame.fn; i++ {
fi := f.fnReceived[i]
if fi == nil {
continue
}
if fi.spatial == currentSpatial && fi.temporal <= currentTemporal {
frameCount++
}
}
if frameCount >= minFramesForCalculation[currentTemporal] && lastFrame.ts > firstFrame.ts {
f.frameRates[currentSpatial][currentTemporal] = float32(f.clockRate) / float32(lastFrame.ts-firstFrame.ts) * float32(frameCount)
rateCounter++
}
}
}
if rateCounter == int(f.maxSpatial+1)*int(f.maxTemporal+1) {
f.completed = true
f.close()
f.logger.Debugw("frame rate calculated", "rate", f.frameRates)
return true
}
return false
}
func (f *FrameRateCalculatorDD) GetFrameRateForSpatial(spatial int32) []float32 {
if spatial < 0 || spatial >= int32(len(f.frameRates)) {
return nil
}
return f.frameRates[spatial][:]
}
func (f *FrameRateCalculatorDD) close() {
f.baseFrame = nil
for i := range f.firstFrames {
for j := range f.firstFrames[i] {
f.firstFrames[i][j] = nil
f.secondFrames[i][j] = nil
}
}
for i := range f.fnReceived {
f.fnReceived[i] = nil
}
for i := range f.targetFrames {
for j := range f.targetFrames[i] {
f.targetFrames[i][j].Init()
}
}
}
func (f *FrameRateCalculatorDD) GetFrameRateCalculatorForSpatial(spatial int32) *FrameRateCalculatorForDDLayer {
return &FrameRateCalculatorForDDLayer{
FrameRateCalculatorDD: f,
spatial: spatial,
}
}
// -----------------------------------------------
type FrameRateCalculatorForDDLayer struct {
*FrameRateCalculatorDD
spatial int32
}
func (f *FrameRateCalculatorForDDLayer) GetFrameRate() []float32 {
return f.FrameRateCalculatorDD.GetFrameRateForSpatial(f.spatial)
}
// -----------------------------------------------
type FrameRateCalculatorH26x struct {
frameRates [DefaultMaxLayerTemporal + 1]float32
clockRate uint32
logger logger.Logger
fnReceived *list.List
baseFrame *frameInfo
completed bool
}
func NewFrameRateCalculatorH26x(clockRate uint32, logger logger.Logger) *FrameRateCalculatorH26x {
return &FrameRateCalculatorH26x{
clockRate: clockRate,
logger: logger,
}
}
func (f *FrameRateCalculatorH26x) Completed() bool {
return f.completed
}
func (f *FrameRateCalculatorH26x) RecvPacket(ep *ExtPacket) bool {
if f.completed {
return true
}
if ep.Temporal >= int32(len(f.frameRates)) {
f.logger.Warnw("invalid temporal layer", nil, "temporal", ep.Temporal)
return false
}
temporal := ep.Temporal
if temporal < 0 {
temporal = 0
}
if f.baseFrame == nil {
f.baseFrame = &frameInfo{
startSeq: ep.Packet.SequenceNumber,
endSeq: ep.Packet.SequenceNumber,
ts: ep.Packet.Timestamp,
temporal: temporal,
}
f.fnReceived = list.New()
f.fnReceived.PushBack(f.baseFrame)
return false
}
if sn16LTOrEqual(ep.Packet.SequenceNumber, f.baseFrame.startSeq) {
return false
}
insertFrame:
for e := f.fnReceived.Back(); e != nil; e = e.Prev() {
frame := e.Value.(*frameInfo)
switch {
case frame.ts == ep.Packet.Timestamp:
if sn16LT(frame.endSeq, ep.Packet.SequenceNumber) {
frame.endSeq = ep.Packet.SequenceNumber
}
if sn16LT(ep.Packet.SequenceNumber, frame.startSeq) {
frame.startSeq = ep.Packet.SequenceNumber
}
break insertFrame
case sn32LT(frame.ts, ep.Packet.Timestamp):
f.fnReceived.InsertAfter(&frameInfo{
startSeq: ep.Packet.SequenceNumber,
endSeq: ep.Packet.SequenceNumber,
ts: ep.Packet.Timestamp,
temporal: temporal,
}, e)
break insertFrame
default:
if e == f.fnReceived.Front() {
f.fnReceived.PushFront(&frameInfo{
startSeq: ep.Packet.SequenceNumber,
endSeq: ep.Packet.SequenceNumber,
ts: ep.Packet.Timestamp,
temporal: temporal,
})
break insertFrame
}
}
}
return f.calc()
}
func (f *FrameRateCalculatorH26x) calc() bool {
frameCounts := make([]int, DefaultMaxLayerTemporal+1)
var totalFrameCount int
var tsDuration int
cur := f.fnReceived.Front()
for {
next := cur.Next()
if next == nil {
break
}
ff := cur.Value.(*frameInfo)
nf := next.Value.(*frameInfo)
if nf.startSeq-ff.endSeq == 1 {
totalFrameCount++
tsDuration += int(nf.ts - ff.ts)
for i := int(nf.temporal); i < len(frameCounts); i++ {
frameCounts[i]++
}
} else {
// reset to find continuous frames
totalFrameCount = 0
for i := range frameCounts {
frameCounts[i] = 0
}
tsDuration = 0
}
// received enough continuous frames, calculate fps
if totalFrameCount >= minFramesForCalculation[DefaultMaxLayerTemporal] {
for currentTemporal := int32(0); currentTemporal <= DefaultMaxLayerTemporal; currentTemporal++ {
count := frameCounts[currentTemporal]
if currentTemporal > 0 && count == frameCounts[currentTemporal-1] {
// no frames for this temporal layer
f.frameRates[currentTemporal] = 0
} else {
f.frameRates[currentTemporal] = float32(f.clockRate) / float32(tsDuration) * float32(count)
}
}
f.logger.Debugw("fps changed", "fps", f.GetFrameRate())
f.completed = true
f.reset()
return true
}
cur = next
}
return false
}
func (f *FrameRateCalculatorH26x) reset() {
f.fnReceived.Init()
f.baseFrame = nil
}
func (f *FrameRateCalculatorH26x) GetFrameRate() []float32 {
return f.frameRates[:]
}
// -----------------------------------------------
func sn16LT(a, b uint16) bool {
return a-b > 0x8000
}
func sn16LTOrEqual(a, b uint16) bool {
return a == b || a-b > 0x8000
}
func sn32LT(a, b uint32) bool {
return a-b > 0x80000000
}