mirror of
https://github.com/aler9/gortsplib
synced 2025-10-06 23:52:46 +08:00
92 lines
2.1 KiB
Go
92 lines
2.1 KiB
Go
// Package rtcpsender implements a utility to generate RTCP sender reports.
|
|
package rtcpsender
|
|
|
|
import (
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/pion/rtcp"
|
|
"github.com/pion/rtp"
|
|
|
|
"github.com/aler9/gortsplib/pkg/base"
|
|
)
|
|
|
|
// RtcpSender is a utility to generate RTCP sender reports.
|
|
type RtcpSender struct {
|
|
clockRate float64
|
|
mutex sync.Mutex
|
|
|
|
// data from rtp packets
|
|
firstRtpReceived bool
|
|
senderSSRC uint32
|
|
lastRtpTimeRtp uint32
|
|
lastRtpTimeTime time.Time
|
|
packetCount uint32
|
|
octetCount uint32
|
|
}
|
|
|
|
// New allocates a RtcpSender.
|
|
func New(clockRate int) *RtcpSender {
|
|
return &RtcpSender{
|
|
clockRate: float64(clockRate),
|
|
}
|
|
}
|
|
|
|
// ProcessFrame extracts the needed data from RTP or RTCP frames.
|
|
func (rs *RtcpSender) ProcessFrame(ts time.Time, streamType base.StreamType, buf []byte) {
|
|
rs.mutex.Lock()
|
|
defer rs.mutex.Unlock()
|
|
|
|
if streamType == base.StreamTypeRtp {
|
|
pkt := rtp.Packet{}
|
|
err := pkt.Unmarshal(buf)
|
|
if err == nil {
|
|
if !rs.firstRtpReceived {
|
|
rs.firstRtpReceived = true
|
|
rs.senderSSRC = pkt.SSRC
|
|
}
|
|
|
|
// always update time to minimize errors
|
|
rs.lastRtpTimeRtp = pkt.Timestamp
|
|
rs.lastRtpTimeTime = ts
|
|
|
|
rs.packetCount++
|
|
rs.octetCount += uint32(len(pkt.Payload))
|
|
}
|
|
}
|
|
}
|
|
|
|
// Report generates a RTCP sender report.
|
|
// It returns nil if no packets has been passed to ProcessFrame yet.
|
|
func (rs *RtcpSender) Report(ts time.Time) []byte {
|
|
rs.mutex.Lock()
|
|
defer rs.mutex.Unlock()
|
|
|
|
if !rs.firstRtpReceived {
|
|
return nil
|
|
}
|
|
|
|
report := &rtcp.SenderReport{
|
|
SSRC: rs.senderSSRC,
|
|
NTPTime: func() uint64 {
|
|
// seconds since 1st January 1900
|
|
s := (float64(ts.UnixNano()) / 1000000000) + 2208988800
|
|
|
|
// higher 32 bits are the integer part, lower 32 bits are the fractional part
|
|
integerPart := uint32(s)
|
|
fractionalPart := uint32((s - float64(integerPart)) * 0xFFFFFFFF)
|
|
return uint64(integerPart)<<32 | uint64(fractionalPart)
|
|
}(),
|
|
RTPTime: rs.lastRtpTimeRtp + uint32((ts.Sub(rs.lastRtpTimeTime)).Seconds()*float64(rs.clockRate)),
|
|
PacketCount: rs.packetCount,
|
|
OctetCount: rs.octetCount,
|
|
}
|
|
|
|
byts, err := report.Marshal()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
return byts
|
|
}
|