mirror of
https://github.com/AlexxIT/go2rtc.git
synced 2025-10-21 15:29:51 +08:00
152 lines
3.2 KiB
Go
152 lines
3.2 KiB
Go
package srtp
|
|
|
|
import (
|
|
"github.com/AlexxIT/go2rtc/pkg/core"
|
|
"github.com/pion/rtcp"
|
|
"github.com/pion/rtp"
|
|
"github.com/pion/srtp/v2"
|
|
"time"
|
|
)
|
|
|
|
type Session struct {
|
|
LocalSSRC uint32 // outgoing SSRC
|
|
RemoteSSRC uint32 // incoming SSRC
|
|
|
|
localCtx *srtp.Context // write context
|
|
remoteCtx *srtp.Context // read context
|
|
|
|
Write func(b []byte) (int, error)
|
|
Track *core.Receiver
|
|
Recv uint32
|
|
|
|
lastSequence uint32
|
|
lastTimestamp uint32
|
|
//lastPacket *rtp.Packet
|
|
lastTime time.Time
|
|
jitter float64
|
|
//sequenceCycle uint16
|
|
totalLost uint32
|
|
}
|
|
|
|
func (s *Session) SetKeys(
|
|
localKey, localSalt, remoteKey, remoteSalt []byte,
|
|
) (err error) {
|
|
if s.localCtx, err = srtp.CreateContext(
|
|
localKey, localSalt, GuessProfile(localKey),
|
|
); err != nil {
|
|
return
|
|
}
|
|
s.remoteCtx, err = srtp.CreateContext(
|
|
remoteKey, remoteSalt, GuessProfile(remoteKey),
|
|
)
|
|
return
|
|
}
|
|
|
|
func (s *Session) HandleRTP(data []byte) (err error) {
|
|
if data, err = s.remoteCtx.DecryptRTP(nil, data, nil); err != nil {
|
|
return
|
|
}
|
|
|
|
if s.Track == nil {
|
|
return
|
|
}
|
|
|
|
packet := &rtp.Packet{}
|
|
if err = packet.Unmarshal(data); err != nil {
|
|
return
|
|
}
|
|
|
|
now := time.Now()
|
|
|
|
// https://www.ietf.org/rfc/rfc3550.txt
|
|
if s.lastTimestamp != 0 {
|
|
delta := packet.SequenceNumber - uint16(s.lastSequence)
|
|
|
|
// lost packet
|
|
if delta > 1 {
|
|
s.totalLost += uint32(delta - 1)
|
|
}
|
|
|
|
// D(i,j) = (Rj - Ri) - (Sj - Si) = (Rj - Sj) - (Ri - Si)
|
|
dTime := now.Sub(s.lastTime).Seconds()*float64(s.Track.Codec.ClockRate) -
|
|
float64(packet.Timestamp-s.lastTimestamp)
|
|
if dTime < 0 {
|
|
dTime = -dTime
|
|
}
|
|
// J(i) = J(i-1) + (|D(i-1,i)| - J(i-1))/16
|
|
s.jitter += (dTime - s.jitter) / 16
|
|
}
|
|
|
|
// keeping cycles (overflow)
|
|
s.lastSequence = s.lastSequence&0xFFFF0000 | uint32(packet.SequenceNumber)
|
|
s.lastTimestamp = packet.Timestamp
|
|
s.lastTime = now
|
|
|
|
s.Track.WriteRTP(packet)
|
|
|
|
return
|
|
}
|
|
|
|
func (s *Session) HandleRTCP(data []byte) (err error) {
|
|
header := &rtcp.Header{}
|
|
if data, err = s.remoteCtx.DecryptRTCP(nil, data, header); err != nil {
|
|
return
|
|
}
|
|
|
|
var packets []rtcp.Packet
|
|
if packets, err = rtcp.Unmarshal(data); err != nil {
|
|
return
|
|
}
|
|
|
|
_ = packets
|
|
|
|
if header.Type == rtcp.TypeSenderReport {
|
|
err = s.KeepAlive()
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (s *Session) KeepAlive() (err error) {
|
|
rep := rtcp.ReceiverReport{SSRC: s.LocalSSRC}
|
|
|
|
if s.lastTimestamp > 0 {
|
|
//log.Printf("[RTCP] ssrc=%d seq=%d lost=%d jit=%.2f", s.RemoteSSRC, s.lastSequence, s.totalLost, s.jitter)
|
|
|
|
rep.Reports = []rtcp.ReceptionReport{{
|
|
SSRC: s.RemoteSSRC,
|
|
LastSequenceNumber: s.lastSequence,
|
|
LastSenderReport: s.lastTimestamp,
|
|
FractionLost: 0, // TODO
|
|
TotalLost: s.totalLost,
|
|
Delay: 0, // send just after receive
|
|
Jitter: uint32(s.jitter),
|
|
}}
|
|
}
|
|
|
|
// we can send empty receiver response, but should send it to hold the connection
|
|
|
|
var data []byte
|
|
if data, err = rep.Marshal(); err != nil {
|
|
return
|
|
}
|
|
|
|
if data, err = s.localCtx.EncryptRTCP(nil, data, nil); err != nil {
|
|
return
|
|
}
|
|
|
|
_, err = s.Write(data)
|
|
|
|
return
|
|
}
|
|
|
|
func GuessProfile(masterKey []byte) srtp.ProtectionProfile {
|
|
switch len(masterKey) {
|
|
case 16:
|
|
return srtp.ProtectionProfileAes128CmHmacSha1_80
|
|
//case 32:
|
|
// return srtp.ProtectionProfileAes256CmHmacSha1_80
|
|
}
|
|
return 0
|
|
}
|