Files
livekit/pkg/sfu/buffer.go
2020-11-15 22:11:52 -10:00

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,
}
}