mirror of
https://github.com/livekit/livekit.git
synced 2026-04-01 00:05:40 +00:00
455 lines
12 KiB
Go
455 lines
12 KiB
Go
package sfu
|
|
|
|
// file from ion-sfu project
|
|
|
|
import (
|
|
"math"
|
|
"sort"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/gammazero/deque"
|
|
"github.com/pion/rtcp"
|
|
"github.com/pion/rtp"
|
|
"github.com/pion/webrtc/v3"
|
|
)
|
|
|
|
const (
|
|
maxSN = 1 << 16
|
|
// default buffer time by ms
|
|
defaultBufferTime = 1000
|
|
)
|
|
|
|
type rtpExtInfo struct {
|
|
ExtTSN uint32
|
|
Timestamp int64
|
|
}
|
|
|
|
// Buffer contains all packets
|
|
type Buffer struct {
|
|
mu sync.RWMutex
|
|
|
|
pktQueue queue
|
|
codecType webrtc.RTPCodecType
|
|
simulcast bool
|
|
clockRate uint32
|
|
maxBitrate uint64
|
|
|
|
// supported feedbacks
|
|
remb bool
|
|
nack bool
|
|
tcc bool
|
|
|
|
lastSRNTPTime uint64
|
|
lastSRRTPTime uint32
|
|
lastSRRecv int64 // Represents wall clock of the most recent sender report arrival
|
|
baseSN uint16
|
|
cycles uint32
|
|
lastExpected uint32
|
|
lastReceived uint32
|
|
lostRate float32
|
|
ssrc uint32
|
|
lastPacketTime int64 // Time the last RTP packet from this source was received
|
|
lastRtcpPacketTime int64 // Time the last RTCP packet was received.
|
|
lastRtcpSrTime int64 // Time the last RTCP SR was received. Required for DLSR computation.
|
|
packetCount uint32 // Number of packets received from this source.
|
|
lastTransit uint32
|
|
maxSeqNo uint16 // The highest sequence number received in an RTP data packet
|
|
jitter float64 // An estimate of the statistical variance of the RTP data packet inter-arrival time.
|
|
totalByte uint64
|
|
|
|
// remb
|
|
rembSteps uint8
|
|
|
|
// transport-cc
|
|
tccExt uint8
|
|
tccExtInfo []rtpExtInfo
|
|
tccCycles uint32
|
|
tccLastExtSN uint32
|
|
tccPktCtn uint8
|
|
tccLastSn uint16
|
|
lastExtInfo uint16
|
|
}
|
|
|
|
// BufferOptions provides configuration options for the buffer
|
|
type BufferOptions struct {
|
|
TCCExt int
|
|
BufferTime int
|
|
MaxBitRate uint64
|
|
}
|
|
|
|
// NewBuffer constructs a new Buffer
|
|
func NewBuffer(track *webrtc.Track, o BufferOptions) *Buffer {
|
|
b := &Buffer{
|
|
ssrc: track.SSRC(),
|
|
clockRate: track.Codec().ClockRate,
|
|
codecType: track.Codec().Type,
|
|
maxBitrate: o.MaxBitRate,
|
|
simulcast: len(track.RID()) > 0,
|
|
rembSteps: 4,
|
|
}
|
|
if o.BufferTime <= 0 {
|
|
o.BufferTime = defaultBufferTime
|
|
}
|
|
b.pktQueue.duration = uint32(o.BufferTime) * b.clockRate / 1000
|
|
b.pktQueue.ssrc = track.SSRC()
|
|
b.tccExt = uint8(o.TCCExt)
|
|
|
|
for _, fb := range track.Codec().RTCPFeedback {
|
|
switch fb.Type {
|
|
case webrtc.TypeRTCPFBGoogREMB:
|
|
//log.Debugf("Setting feedback %s", webrtc.TypeRTCPFBGoogREMB)
|
|
b.remb = true
|
|
case webrtc.TypeRTCPFBTransportCC:
|
|
//log.Debugf("Setting feedback %s", webrtc.TypeRTCPFBTransportCC)
|
|
b.tccExtInfo = make([]rtpExtInfo, 1<<8)
|
|
b.tcc = true
|
|
case webrtc.TypeRTCPFBNACK:
|
|
//log.Debugf("Setting feedback %s", webrtc.TypeRTCPFBNACK)
|
|
b.nack = true
|
|
}
|
|
}
|
|
//log.Debugf("NewBuffer BufferOptions=%v", o)
|
|
return b
|
|
}
|
|
|
|
// Push adds a RTP Packet, out of order, new packet may be arrived later
|
|
func (b *Buffer) Push(p *rtp.Packet) {
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
b.totalByte += uint64(p.MarshalSize())
|
|
if b.packetCount == 0 {
|
|
b.baseSN = p.SequenceNumber
|
|
b.maxSeqNo = p.SequenceNumber
|
|
b.pktQueue.headSN = p.SequenceNumber - 1
|
|
} else if snDiff(b.maxSeqNo, p.SequenceNumber) <= 0 {
|
|
if p.SequenceNumber < b.maxSeqNo {
|
|
b.cycles += maxSN
|
|
}
|
|
b.maxSeqNo = p.SequenceNumber
|
|
}
|
|
b.packetCount++
|
|
b.lastPacketTime = time.Now().UnixNano()
|
|
arrival := uint32(b.lastPacketTime / 1e6 * int64(b.clockRate/1e3))
|
|
transit := arrival - p.Timestamp
|
|
if b.lastTransit != 0 {
|
|
d := int32(transit - b.lastTransit)
|
|
if d < 0 {
|
|
d = -d
|
|
}
|
|
b.jitter += (float64(d) - b.jitter) / 16
|
|
}
|
|
b.lastTransit = transit
|
|
if b.codecType == webrtc.RTPCodecTypeVideo {
|
|
b.pktQueue.AddPacket(p, p.SequenceNumber == b.maxSeqNo)
|
|
}
|
|
|
|
if b.tcc {
|
|
rtpTCC := rtp.TransportCCExtension{}
|
|
if err := rtpTCC.Unmarshal(p.GetExtension(b.tccExt)); err == nil {
|
|
if rtpTCC.TransportSequence < 0x0fff && (b.tccLastSn&0xffff) > 0xf000 {
|
|
b.tccCycles += maxSN
|
|
}
|
|
b.tccExtInfo = append(b.tccExtInfo, rtpExtInfo{
|
|
ExtTSN: b.tccCycles | uint32(rtpTCC.TransportSequence),
|
|
Timestamp: b.lastPacketTime / 1e3,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
func (b *Buffer) buildREMBPacket() *rtcp.ReceiverEstimatedMaximumBitrate {
|
|
br := b.maxBitrate
|
|
if b.rembSteps > 0 {
|
|
br /= uint64(b.rembSteps)
|
|
b.rembSteps--
|
|
}
|
|
|
|
return &rtcp.ReceiverEstimatedMaximumBitrate{
|
|
SenderSSRC: b.ssrc,
|
|
Bitrate: br,
|
|
SSRCs: []uint32{b.ssrc},
|
|
}
|
|
}
|
|
|
|
func (b *Buffer) buildTransportCCPacket() *rtcp.TransportLayerCC {
|
|
if len(b.tccExtInfo) == 0 {
|
|
return nil
|
|
}
|
|
sort.Slice(b.tccExtInfo, func(i, j int) bool {
|
|
return b.tccExtInfo[i].ExtTSN < b.tccExtInfo[j].ExtTSN
|
|
})
|
|
tccPkts := make([]rtpExtInfo, 0, int(float64(len(b.tccExtInfo))*1.2))
|
|
for _, tccExtInfo := range b.tccExtInfo {
|
|
if tccExtInfo.ExtTSN < b.tccLastExtSN {
|
|
continue
|
|
}
|
|
if b.tccLastExtSN != 0 {
|
|
for j := b.tccLastExtSN + 1; j < tccExtInfo.ExtTSN; j++ {
|
|
tccPkts = append(tccPkts, rtpExtInfo{ExtTSN: j})
|
|
}
|
|
}
|
|
b.tccLastExtSN = tccExtInfo.ExtTSN
|
|
tccPkts = append(tccPkts, tccExtInfo)
|
|
}
|
|
b.tccExtInfo = b.tccExtInfo[:0]
|
|
|
|
rtcpTCC := &rtcp.TransportLayerCC{
|
|
Header: rtcp.Header{
|
|
Padding: true,
|
|
Count: rtcp.FormatTCC,
|
|
Type: rtcp.TypeTransportSpecificFeedback,
|
|
},
|
|
MediaSSRC: b.ssrc,
|
|
BaseSequenceNumber: uint16(tccPkts[0].ExtTSN),
|
|
PacketStatusCount: uint16(len(tccPkts)),
|
|
FbPktCount: b.tccPktCtn,
|
|
}
|
|
b.tccPktCtn++
|
|
|
|
firstRecv := false
|
|
allSame := true
|
|
timestamp := int64(0)
|
|
deltaLen := 0
|
|
lastStatus := rtcp.TypeTCCPacketReceivedWithoutDelta
|
|
maxStatus := rtcp.TypeTCCPacketNotReceived
|
|
|
|
var statusList deque.Deque
|
|
|
|
for _, stat := range tccPkts {
|
|
status := rtcp.TypeTCCPacketNotReceived
|
|
if stat.Timestamp != 0 {
|
|
var delta int64
|
|
if !firstRecv {
|
|
firstRecv = true
|
|
timestamp = stat.Timestamp
|
|
rtcpTCC.ReferenceTime = uint32(stat.Timestamp / 64000)
|
|
}
|
|
|
|
delta = (stat.Timestamp - timestamp) / 250
|
|
if delta < 0 || delta > 255 {
|
|
status = rtcp.TypeTCCPacketReceivedLargeDelta
|
|
rDelta := int16(delta)
|
|
if int64(rDelta) != delta {
|
|
if rDelta > 0 {
|
|
rDelta = math.MaxInt16
|
|
} else {
|
|
rDelta = math.MinInt16
|
|
}
|
|
}
|
|
rtcpTCC.RecvDeltas = append(rtcpTCC.RecvDeltas, &rtcp.RecvDelta{
|
|
Type: status,
|
|
Delta: int64(rDelta) * 250,
|
|
})
|
|
deltaLen += 2
|
|
} else {
|
|
status = rtcp.TypeTCCPacketReceivedSmallDelta
|
|
rtcpTCC.RecvDeltas = append(rtcpTCC.RecvDeltas, &rtcp.RecvDelta{
|
|
Type: status,
|
|
Delta: delta * 250,
|
|
})
|
|
deltaLen++
|
|
}
|
|
timestamp = stat.Timestamp
|
|
}
|
|
|
|
if allSame && lastStatus != rtcp.TypeTCCPacketReceivedWithoutDelta && status != lastStatus {
|
|
if statusList.Len() > 7 {
|
|
rtcpTCC.PacketChunks = append(rtcpTCC.PacketChunks, &rtcp.RunLengthChunk{
|
|
PacketStatusSymbol: lastStatus,
|
|
RunLength: uint16(statusList.Len()),
|
|
})
|
|
statusList.Clear()
|
|
lastStatus = rtcp.TypeTCCPacketReceivedWithoutDelta
|
|
maxStatus = rtcp.TypeTCCPacketNotReceived
|
|
allSame = true
|
|
} else {
|
|
allSame = false
|
|
}
|
|
}
|
|
statusList.PushBack(status)
|
|
if status > maxStatus {
|
|
maxStatus = status
|
|
}
|
|
lastStatus = status
|
|
|
|
if !allSame {
|
|
if maxStatus == rtcp.TypeTCCPacketReceivedLargeDelta && statusList.Len() > 6 {
|
|
symbolList := make([]uint16, 7)
|
|
for i := 0; i < 7; i++ {
|
|
symbolList[i] = statusList.PopFront().(uint16)
|
|
}
|
|
rtcpTCC.PacketChunks = append(rtcpTCC.PacketChunks, &rtcp.StatusVectorChunk{
|
|
SymbolSize: rtcp.TypeTCCSymbolSizeTwoBit,
|
|
SymbolList: symbolList,
|
|
})
|
|
lastStatus = rtcp.TypeTCCPacketReceivedWithoutDelta
|
|
maxStatus = rtcp.TypeTCCPacketNotReceived
|
|
allSame = true
|
|
|
|
for i := 0; i < statusList.Len(); i++ {
|
|
status = statusList.At(i).(uint16)
|
|
if status > maxStatus {
|
|
maxStatus = status
|
|
}
|
|
if allSame && lastStatus != rtcp.TypeTCCPacketReceivedWithoutDelta && status != lastStatus {
|
|
allSame = false
|
|
}
|
|
lastStatus = status
|
|
}
|
|
} else if statusList.Len() > 13 {
|
|
symbolList := make([]uint16, 14)
|
|
for i := 0; i < 14; i++ {
|
|
symbolList[i] = statusList.PopFront().(uint16)
|
|
}
|
|
rtcpTCC.PacketChunks = append(rtcpTCC.PacketChunks, &rtcp.StatusVectorChunk{
|
|
SymbolSize: rtcp.TypeTCCSymbolSizeOneBit,
|
|
SymbolList: symbolList,
|
|
})
|
|
lastStatus = rtcp.TypeTCCPacketReceivedWithoutDelta
|
|
maxStatus = rtcp.TypeTCCPacketNotReceived
|
|
allSame = true
|
|
}
|
|
}
|
|
}
|
|
|
|
if statusList.Len() > 0 {
|
|
if allSame {
|
|
rtcpTCC.PacketChunks = append(rtcpTCC.PacketChunks, &rtcp.RunLengthChunk{
|
|
PacketStatusSymbol: lastStatus,
|
|
RunLength: uint16(statusList.Len()),
|
|
})
|
|
} else if maxStatus == rtcp.TypeTCCPacketReceivedLargeDelta {
|
|
symbolList := make([]uint16, statusList.Len())
|
|
for i := 0; i < statusList.Len(); i++ {
|
|
symbolList[i] = statusList.PopFront().(uint16)
|
|
}
|
|
rtcpTCC.PacketChunks = append(rtcpTCC.PacketChunks, &rtcp.StatusVectorChunk{
|
|
SymbolSize: rtcp.TypeTCCSymbolSizeTwoBit,
|
|
SymbolList: symbolList,
|
|
})
|
|
} else {
|
|
symbolList := make([]uint16, statusList.Len())
|
|
for i := 0; i < statusList.Len(); i++ {
|
|
symbolList[i] = statusList.PopFront().(uint16)
|
|
}
|
|
rtcpTCC.PacketChunks = append(rtcpTCC.PacketChunks, &rtcp.StatusVectorChunk{
|
|
SymbolSize: rtcp.TypeTCCSymbolSizeOneBit,
|
|
SymbolList: symbolList,
|
|
})
|
|
}
|
|
}
|
|
|
|
pLen := uint16(20 + len(rtcpTCC.PacketChunks)*2 + deltaLen)
|
|
rtcpTCC.Header.Padding = pLen%4 != 0
|
|
for pLen%4 != 0 {
|
|
pLen++
|
|
}
|
|
rtcpTCC.Header.Length = (pLen / 4) - 1
|
|
return rtcpTCC
|
|
}
|
|
|
|
func (b *Buffer) BuildReceptionReport() rtcp.ReceptionReport {
|
|
extMaxSeq := b.cycles | uint32(b.maxSeqNo)
|
|
expected := extMaxSeq - uint32(b.baseSN) + 1
|
|
lost := expected - b.packetCount
|
|
if b.packetCount == 0 {
|
|
lost = 0
|
|
}
|
|
expectedInterval := expected - b.lastExpected
|
|
b.lastExpected = expected
|
|
|
|
receivedInterval := b.packetCount - b.lastReceived
|
|
b.lastReceived = b.packetCount
|
|
|
|
lostInterval := expectedInterval - receivedInterval
|
|
|
|
b.lostRate = float32(lostInterval) / float32(expectedInterval)
|
|
var fracLost uint8
|
|
if expectedInterval != 0 && lostInterval > 0 {
|
|
fracLost = uint8((lostInterval << 8) / expectedInterval)
|
|
}
|
|
var dlsr uint32
|
|
if b.lastSRRecv != 0 {
|
|
delayMS := uint32((time.Now().UnixNano() - b.lastSRRecv) / 1e6)
|
|
dlsr = (delayMS / 1e3) << 16
|
|
dlsr |= (delayMS % 1e3) * 65536 / 1000
|
|
}
|
|
|
|
rr := rtcp.ReceptionReport{
|
|
SSRC: b.ssrc,
|
|
FractionLost: fracLost,
|
|
TotalLost: lost,
|
|
LastSequenceNumber: extMaxSeq,
|
|
Jitter: uint32(b.jitter),
|
|
LastSenderReport: uint32(b.lastSRNTPTime >> 16),
|
|
Delay: dlsr,
|
|
}
|
|
return rr
|
|
}
|
|
|
|
func (b *Buffer) SetSenderReportData(rtpTime uint32, ntpTime uint64) {
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
b.lastSRRTPTime = rtpTime
|
|
b.lastSRNTPTime = ntpTime
|
|
b.lastSRRecv = time.Now().UnixNano()
|
|
}
|
|
|
|
func (b *Buffer) BuildRTCP() (rtcp.ReceptionReport, []rtcp.Packet) {
|
|
b.mu.RLock()
|
|
defer b.mu.RUnlock()
|
|
var pkts []rtcp.Packet
|
|
var report rtcp.ReceptionReport
|
|
|
|
report = b.BuildReceptionReport()
|
|
|
|
if b.remb {
|
|
pkts = append(pkts, b.buildREMBPacket())
|
|
}
|
|
|
|
if b.tcc {
|
|
if tccPkt := b.buildTransportCCPacket(); tccPkt != nil {
|
|
pkts = append(pkts, tccPkt)
|
|
}
|
|
}
|
|
|
|
return report, pkts
|
|
}
|
|
|
|
// WritePacket write buffer packet to requested track. and modify headers
|
|
func (b *Buffer) WritePacket(sn uint16, track *webrtc.Track, snOffset uint16, tsOffset, ssrc uint32) error {
|
|
b.mu.RLock()
|
|
defer b.mu.RUnlock()
|
|
if bufferPkt := b.pktQueue.GetPacket(sn); bufferPkt != nil {
|
|
bSsrc := bufferPkt.SSRC
|
|
bufferPkt.SequenceNumber -= snOffset
|
|
bufferPkt.Timestamp -= tsOffset
|
|
bufferPkt.SSRC = ssrc
|
|
err := track.WriteRTP(bufferPkt)
|
|
bufferPkt.Timestamp += tsOffset
|
|
bufferPkt.SequenceNumber += snOffset
|
|
bufferPkt.SSRC = bSsrc
|
|
return err
|
|
}
|
|
return ErrPacketNotFound
|
|
}
|
|
|
|
func (b *Buffer) onLostHandler(fn func(nack *rtcp.TransportLayerNack)) {
|
|
if b.nack {
|
|
b.pktQueue.onLost = fn
|
|
}
|
|
}
|
|
|
|
type BufferStats struct {
|
|
TotalBytes uint64
|
|
LastPacketTime int64
|
|
}
|
|
|
|
func (b *Buffer) Stats() BufferStats {
|
|
return BufferStats{
|
|
TotalBytes: b.totalByte,
|
|
LastPacketTime: b.lastPacketTime,
|
|
}
|
|
}
|