Squashed 'livekit-server/' content from commit 154b4d26

git-subtree-dir: livekit-server
git-subtree-split: 154b4d26b769c68a03c096124094b97bf61a996f
This commit is contained in:
2026-06-25 14:35:28 +09:00
commit 0da97ebd21
339 changed files with 114111 additions and 0 deletions
+127
View File
@@ -0,0 +1,127 @@
// 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 pacer
import (
"errors"
"io"
"time"
"github.com/livekit/livekit-server/pkg/sfu/bwe"
"github.com/livekit/protocol/logger"
"github.com/livekit/protocol/utils/mono"
"github.com/pion/rtp"
"go.uber.org/atomic"
)
type Base struct {
logger logger.Logger
bwe bwe.BWE
lastPacketSentAt atomic.Int64
*ProbeObserver
}
func NewBase(logger logger.Logger, bwe bwe.BWE) *Base {
return &Base{
logger: logger,
bwe: bwe,
ProbeObserver: NewProbeObserver(logger),
}
}
func (b *Base) SetInterval(_interval time.Duration) {
}
func (b *Base) SetBitrate(_bitrate int) {
}
func (b *Base) TimeSinceLastSentPacket() time.Duration {
return time.Duration(mono.UnixNano() - b.lastPacketSentAt.Load())
}
func (b *Base) SendPacket(p *Packet) (int, error) {
defer func() {
if p.Pool != nil && p.PoolEntity != nil {
p.Pool.Put(p.PoolEntity)
}
}()
err := b.patchRTPHeaderExtensions(p)
if err != nil {
b.logger.Errorw("patching rtp header extensions err", err)
return 0, err
}
var written int
written, err = p.WriteStream.WriteRTP(p.Header, p.Payload)
if err != nil {
if !errors.Is(err, io.ErrClosedPipe) {
b.logger.Errorw("write rtp packet failed", err)
}
return 0, err
}
return written, nil
}
// patch just abs-send-time and transport-cc extensions if applicable
func (b *Base) patchRTPHeaderExtensions(p *Packet) error {
sendingAt := mono.Now()
if p.AbsSendTimeExtID != 0 {
absSendTime := rtp.NewAbsSendTimeExtension(sendingAt)
absSendTimeBytes, err := absSendTime.Marshal()
if err != nil {
return err
}
if err = p.Header.SetExtension(p.AbsSendTimeExtID, absSendTimeBytes); err != nil {
return err
}
b.lastPacketSentAt.Store(sendingAt.UnixNano())
}
packetSize := p.HeaderSize + len(p.Payload)
if p.TransportWideExtID != 0 && b.bwe != nil {
twccSN := b.bwe.RecordPacketSendAndGetSequenceNumber(
sendingAt.UnixMicro(),
packetSize,
p.IsRTX,
p.ProbeClusterId,
p.IsProbe,
)
twccExt := rtp.TransportCCExtension{
TransportSequence: twccSN,
}
twccExtBytes, err := twccExt.Marshal()
if err != nil {
return err
}
if err = p.Header.SetExtension(p.TransportWideExtID, twccExtBytes); err != nil {
return err
}
b.lastPacketSentAt.Store(sendingAt.UnixNano())
}
b.ProbeObserver.RecordPacket(packetSize, p.IsRTX, p.ProbeClusterId, p.IsProbe)
return nil
}
// ------------------------------------------------
+142
View File
@@ -0,0 +1,142 @@
// 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 pacer
import (
"sync"
"time"
"github.com/frostbyte73/core"
"github.com/gammazero/deque"
"github.com/livekit/livekit-server/pkg/sfu/bwe"
"github.com/livekit/protocol/logger"
)
const (
maxOvershootFactor = 2.0
)
type LeakyBucket struct {
*Base
logger logger.Logger
lock sync.RWMutex
packets deque.Deque[*Packet]
interval time.Duration
bitrate int
stop core.Fuse
}
func NewLeakyBucket(logger logger.Logger, bwe bwe.BWE, interval time.Duration, bitrate int) *LeakyBucket {
l := &LeakyBucket{
Base: NewBase(logger, bwe),
logger: logger,
interval: interval,
bitrate: bitrate,
}
l.packets.SetBaseCap(512)
go l.sendWorker()
return l
}
func (l *LeakyBucket) SetInterval(interval time.Duration) {
l.lock.Lock()
defer l.lock.Unlock()
l.interval = interval
}
func (l *LeakyBucket) SetBitrate(bitrate int) {
l.lock.Lock()
defer l.lock.Unlock()
l.bitrate = bitrate
}
func (l *LeakyBucket) Stop() {
l.stop.Break()
}
func (l *LeakyBucket) Enqueue(p *Packet) {
l.lock.Lock()
l.packets.PushBack(p)
l.lock.Unlock()
}
func (l *LeakyBucket) sendWorker() {
l.lock.RLock()
interval := l.interval
bitrate := l.bitrate
l.lock.RUnlock()
timer := time.NewTimer(interval)
overage := 0
for {
<-timer.C
l.lock.RLock()
interval = l.interval
bitrate = l.bitrate
l.lock.RUnlock()
// calculate number of bytes that can be sent in this interval
// adjusting for overage.
intervalBytes := int(interval.Seconds() * float64(bitrate) / 8.0)
maxOvershootBytes := int(float64(intervalBytes) * maxOvershootFactor)
toSendBytes := intervalBytes - overage
if toSendBytes < 0 {
// too much overage, wait for next interval
overage = -toSendBytes
timer.Reset(interval)
continue
}
// do not allow too much overshoot in an interval
if toSendBytes > maxOvershootBytes {
toSendBytes = maxOvershootBytes
}
for {
if l.stop.IsBroken() {
return
}
l.lock.Lock()
if l.packets.Len() == 0 {
l.lock.Unlock()
// allow overshoot in next interval with shortage in this interval
overage = -toSendBytes
timer.Reset(interval)
break
}
p := l.packets.PopFront()
l.lock.Unlock()
written, _ := l.Base.SendPacket(p)
toSendBytes -= written
if toSendBytes < 0 {
// overage, wait for next interval
overage = -toSendBytes
timer.Reset(interval)
break
}
}
}
}
// ------------------------------------------------
+90
View File
@@ -0,0 +1,90 @@
// 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 pacer
import (
"sync"
"github.com/frostbyte73/core"
"github.com/gammazero/deque"
"github.com/livekit/livekit-server/pkg/sfu/bwe"
"github.com/livekit/protocol/logger"
)
type NoQueue struct {
*Base
logger logger.Logger
lock sync.RWMutex
packets deque.Deque[*Packet]
wake chan struct{}
stop core.Fuse
}
func NewNoQueue(logger logger.Logger, bwe bwe.BWE) *NoQueue {
n := &NoQueue{
Base: NewBase(logger, bwe),
logger: logger,
wake: make(chan struct{}, 1),
}
n.packets.SetBaseCap(512)
go n.sendWorker()
return n
}
func (n *NoQueue) Stop() {
n.stop.Break()
select {
case n.wake <- struct{}{}:
default:
}
}
func (n *NoQueue) Enqueue(p *Packet) {
n.lock.Lock()
n.packets.PushBack(p)
n.lock.Unlock()
select {
case n.wake <- struct{}{}:
default:
}
}
func (n *NoQueue) sendWorker() {
for {
<-n.wake
for {
if n.stop.IsBroken() {
return
}
n.lock.Lock()
if n.packets.Len() == 0 {
n.lock.Unlock()
break
}
p := n.packets.PopFront()
n.lock.Unlock()
n.Base.SendPacket(p)
}
}
}
// ------------------------------------------------
+58
View File
@@ -0,0 +1,58 @@
// 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 pacer
import (
"sync"
"time"
"github.com/livekit/livekit-server/pkg/sfu/ccutils"
"github.com/pion/rtp"
"github.com/pion/webrtc/v4"
)
type Packet struct {
Header *rtp.Header
HeaderSize int
Payload []byte
IsRTX bool
ProbeClusterId ccutils.ProbeClusterId
IsProbe bool
AbsSendTimeExtID uint8
TransportWideExtID uint8
WriteStream webrtc.TrackLocalWriter
Pool *sync.Pool
PoolEntity *[]byte
}
type Pacer interface {
Enqueue(p *Packet)
Stop()
SetInterval(interval time.Duration)
SetBitrate(bitrate int)
TimeSinceLastSentPacket() time.Duration
SetPacerProbeObserverListener(listener PacerProbeObserverListener)
StartProbeCluster(pci ccutils.ProbeClusterInfo)
EndProbeCluster(probeClusterId ccutils.ProbeClusterId) ccutils.ProbeClusterInfo
}
type PacerProbeObserverListener interface {
OnPacerProbeObserverClusterComplete(probeClusterId ccutils.ProbeClusterId)
}
// ------------------------------------------------
+39
View File
@@ -0,0 +1,39 @@
// 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 pacer
import (
"github.com/livekit/livekit-server/pkg/sfu/bwe"
"github.com/livekit/protocol/logger"
)
type PassThrough struct {
*Base
}
func NewPassThrough(logger logger.Logger, bwe bwe.BWE) *PassThrough {
return &PassThrough{
Base: NewBase(logger, bwe),
}
}
func (p *PassThrough) Stop() {
}
func (p *PassThrough) Enqueue(pkt *Packet) {
p.Base.SendPacket(pkt)
}
// ------------------------------------------------
+143
View File
@@ -0,0 +1,143 @@
// 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 pacer
import (
"sync"
"time"
"go.uber.org/atomic"
"github.com/livekit/livekit-server/pkg/sfu/ccutils"
"github.com/livekit/protocol/logger"
"github.com/livekit/protocol/utils/mono"
)
type ProbeObserver struct {
logger logger.Logger
listener PacerProbeObserverListener
isInProbe atomic.Bool
lock sync.Mutex
pci ccutils.ProbeClusterInfo
}
func NewProbeObserver(logger logger.Logger) *ProbeObserver {
return &ProbeObserver{
logger: logger,
}
}
func (po *ProbeObserver) SetPacerProbeObserverListener(listener PacerProbeObserverListener) {
po.listener = listener
}
func (po *ProbeObserver) StartProbeCluster(pci ccutils.ProbeClusterInfo) {
if po.isInProbe.Load() {
po.logger.Warnw(
"ignoring start of a new probe cluster when already active", nil,
"probeClusterInfo", pci,
)
return
}
po.lock.Lock()
defer po.lock.Unlock()
po.pci = pci
po.pci.Result = ccutils.ProbeClusterResult{
StartTime: mono.UnixNano(),
}
po.isInProbe.Store(true)
}
func (po *ProbeObserver) EndProbeCluster(probeClusterId ccutils.ProbeClusterId) ccutils.ProbeClusterInfo {
if !po.isInProbe.Load() {
// probe not active
if probeClusterId != ccutils.ProbeClusterIdInvalid {
po.logger.Debugw(
"ignoring end of a probe cluster when not active",
"probeClusterId", probeClusterId,
)
}
return ccutils.ProbeClusterInfoInvalid
}
po.lock.Lock()
defer po.lock.Unlock()
if po.pci.Id != probeClusterId {
// probe cluster id not active
po.logger.Warnw(
"ignoring end of a probe cluster of a non-active one", nil,
"probeClusterId", probeClusterId,
"active", po.pci.Id,
)
return ccutils.ProbeClusterInfoInvalid
}
if po.pci.Result.EndTime == 0 {
po.pci.Result.EndTime = mono.UnixNano()
}
po.isInProbe.Store(false)
return po.pci
}
func (po *ProbeObserver) RecordPacket(size int, isRTX bool, probeClusterId ccutils.ProbeClusterId, isProbe bool) {
if !po.isInProbe.Load() {
return
}
po.lock.Lock()
if probeClusterId != po.pci.Id || po.pci.Result.EndTime != 0 {
po.lock.Unlock()
return
}
if isProbe {
po.pci.Result.PacketsProbe++
po.pci.Result.BytesProbe += size
} else {
if isRTX {
po.pci.Result.PacketsNonProbeRTX++
po.pci.Result.BytesNonProbeRTX += size
} else {
po.pci.Result.PacketsNonProbePrimary++
po.pci.Result.BytesNonProbePrimary += size
}
}
notify := false
var clusterId ccutils.ProbeClusterId
if po.pci.Result.EndTime == 0 && ((po.pci.Result.Bytes() >= po.pci.Goal.DesiredBytes) && time.Duration(mono.UnixNano()-po.pci.Result.StartTime) >= po.pci.Goal.Duration) {
po.pci.Result.EndTime = mono.UnixNano()
po.pci.Result.IsCompleted = true
notify = true
clusterId = po.pci.Id
}
po.lock.Unlock()
if notify && po.listener != nil {
po.listener.OnPacerProbeObserverClusterComplete(clusterId)
}
}
// ------------------------------------------------