mirror of
https://github.com/aler9/gortsplib
synced 2025-10-05 23:26:54 +08:00

* 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
219 lines
5.0 KiB
Go
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
|
|
}
|