Files
gortsplib/pkg/rtcpreceiver/rtcpreceiver.go
Alessandro Ros a1396206b5 convert Tracks into Medias and Formats (#155)
* split tracks from medias

* move tracks into dedicated package

* move media into dedicated package

* edit Medias.Marshal() in order to return SDP

* add medias.Find() and simplify examples

* improve coverage

* fix rebase errors

* replace TrackIDs with MediaIDs

* implement media-specific and track-specific callbacks for reading RTCP and RTP packets

* rename publish into record, read into play

* add v2 tag

* rename tracks into formats
2022-12-11 22:03:22 +01:00

219 lines
5.0 KiB
Go

// Package rtcpreceiver contains a utility to generate RTCP receiver reports.
package rtcpreceiver
import (
"crypto/rand"
"sync"
"time"
"github.com/pion/rtcp"
"github.com/pion/rtp"
)
func randUint32() uint32 {
var b [4]byte
rand.Read(b[:])
return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3])
}
var now = time.Now
// RTCPReceiver is a utility to generate RTCP receiver reports.
type RTCPReceiver struct {
period time.Duration
receiverSSRC uint32
clockRate float64
writePacketRTCP func(rtcp.Packet)
mutex sync.Mutex
// data from RTP packets
initialized bool
timeInitialized bool
sequenceNumberCycles uint16
lastSSRC uint32
lastSequenceNumber uint16
lastTimeRTP uint32
lastTimeNTP time.Time
totalLost uint32
totalLostSinceReport uint32
totalSinceReport uint32
jitter float64
// data from RTCP packets
senderInitialized bool
lastSenderReportNTP uint32
lastSenderReportTime time.Time
terminate chan struct{}
done chan struct{}
}
// New allocates a RTCPReceiver.
func New(
period time.Duration,
receiverSSRC *uint32,
clockRate int,
writePacketRTCP func(rtcp.Packet),
) *RTCPReceiver {
rr := &RTCPReceiver{
period: period,
receiverSSRC: func() uint32 {
if receiverSSRC == nil {
return randUint32()
}
return *receiverSSRC
}(),
clockRate: float64(clockRate),
writePacketRTCP: writePacketRTCP,
terminate: make(chan struct{}),
done: make(chan struct{}),
}
go rr.run()
return rr
}
// Close closes the RTCPReceiver.
func (rr *RTCPReceiver) Close() {
close(rr.terminate)
<-rr.done
}
func (rr *RTCPReceiver) run() {
defer close(rr.done)
t := time.NewTicker(rr.period)
defer t.Stop()
for {
select {
case <-t.C:
report := rr.report(now())
if report != nil {
rr.writePacketRTCP(report)
}
case <-rr.terminate:
return
}
}
}
func (rr *RTCPReceiver) report(ts time.Time) rtcp.Packet {
rr.mutex.Lock()
defer rr.mutex.Unlock()
if !rr.senderInitialized || !rr.initialized || rr.clockRate == 0 {
return nil
}
report := &rtcp.ReceiverReport{
SSRC: rr.receiverSSRC,
Reports: []rtcp.ReceptionReport{
{
SSRC: rr.lastSSRC,
LastSequenceNumber: uint32(rr.sequenceNumberCycles)<<16 | uint32(rr.lastSequenceNumber),
// middle 32 bits out of 64 in the NTP timestamp of last sender report
LastSenderReport: rr.lastSenderReportNTP,
// equivalent to taking the integer part after multiplying the
// loss fraction by 256
FractionLost: uint8(float64(rr.totalLostSinceReport*256) / float64(rr.totalSinceReport)),
TotalLost: rr.totalLost,
// delay, expressed in units of 1/65536 seconds, between
// receiving the last SR packet from source SSRC_n and sending this
// reception report block
Delay: uint32(ts.Sub(rr.lastSenderReportTime).Seconds() * 65536),
Jitter: uint32(rr.jitter),
},
},
}
rr.totalLostSinceReport = 0
rr.totalSinceReport = 0
return report
}
// ProcessPacket extracts the needed data from RTP packets.
func (rr *RTCPReceiver) ProcessPacket(pkt *rtp.Packet, ntp time.Time, ptsEqualsDTS bool) {
rr.mutex.Lock()
defer rr.mutex.Unlock()
// first packet
if !rr.initialized {
rr.initialized = true
rr.totalSinceReport = 1
rr.lastSSRC = pkt.SSRC
rr.lastSequenceNumber = pkt.SequenceNumber
if ptsEqualsDTS {
rr.timeInitialized = true
rr.lastTimeRTP = pkt.Timestamp
rr.lastTimeNTP = ntp
}
// subsequent packets
} else {
diff := int32(pkt.SequenceNumber) - int32(rr.lastSequenceNumber)
// overflow
if diff < -0x0FFF {
rr.sequenceNumberCycles++
}
// detect lost packets
if pkt.SequenceNumber != (rr.lastSequenceNumber + 1) {
rr.totalLost += uint32(uint16(diff) - 1)
rr.totalLostSinceReport += uint32(uint16(diff) - 1)
// allow up to 24 bits
if rr.totalLost > 0xFFFFFF {
rr.totalLost = 0xFFFFFF
}
if rr.totalLostSinceReport > 0xFFFFFF {
rr.totalLostSinceReport = 0xFFFFFF
}
}
rr.totalSinceReport += uint32(uint16(diff))
rr.lastSSRC = pkt.SSRC
rr.lastSequenceNumber = pkt.SequenceNumber
if ptsEqualsDTS {
if rr.timeInitialized {
// update jitter
// https://tools.ietf.org/html/rfc3550#page-39
D := ntp.Sub(rr.lastTimeNTP).Seconds()*rr.clockRate -
(float64(pkt.Timestamp) - float64(rr.lastTimeRTP))
if D < 0 {
D = -D
}
rr.jitter += (D - rr.jitter) / 16
}
rr.timeInitialized = true
rr.lastTimeRTP = pkt.Timestamp
rr.lastTimeNTP = ntp
rr.lastSSRC = pkt.SSRC
}
}
}
// ProcessSenderReport extracts the needed data from RTCP sender reports.
func (rr *RTCPReceiver) ProcessSenderReport(sr *rtcp.SenderReport, ts time.Time) {
rr.mutex.Lock()
defer rr.mutex.Unlock()
rr.senderInitialized = true
rr.lastSenderReportNTP = uint32(sr.NTPTime >> 16)
rr.lastSenderReportTime = ts
}
// LastSSRC returns the SSRC of the last RTP packet.
func (rr *RTCPReceiver) LastSSRC() (uint32, bool) {
rr.mutex.Lock()
defer rr.mutex.Unlock()
return rr.lastSSRC, rr.initialized
}