Squashed 'livekit-server/' content from commit 154b4d26
git-subtree-dir: livekit-server git-subtree-split: 154b4d26b769c68a03c096124094b97bf61a996f
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
// ------------------------------------------------
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
// ------------------------------------------------
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
// ------------------------------------------------
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------
|
||||
Reference in New Issue
Block a user