Rewrite HomeKit client
This commit is contained in:
+60
-44
@@ -3,80 +3,96 @@ package srtp
|
||||
import (
|
||||
"encoding/binary"
|
||||
"net"
|
||||
"sync/atomic"
|
||||
"strconv"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Server using same UDP port for SRTP and for SRTCP as the iPhone does
|
||||
// this is not really necessary but anyway
|
||||
type Server struct {
|
||||
address string
|
||||
conn net.PacketConn
|
||||
sessions map[uint32]*Session
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func (s *Server) Port() uint16 {
|
||||
addr := s.conn.LocalAddr().(*net.UDPAddr)
|
||||
return uint16(addr.Port)
|
||||
func NewServer(address string) *Server {
|
||||
return &Server{address: address}
|
||||
}
|
||||
|
||||
func (s *Server) Close() error {
|
||||
return s.conn.Close()
|
||||
func (s *Server) Port() int {
|
||||
if s.conn != nil {
|
||||
return s.conn.LocalAddr().(*net.UDPAddr).Port
|
||||
}
|
||||
|
||||
_, a, _ := net.SplitHostPort(s.address)
|
||||
i, _ := strconv.Atoi(a)
|
||||
return i
|
||||
}
|
||||
|
||||
func (s *Server) AddSession(session *Session) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
if err := session.Local.Init(); err != nil {
|
||||
return
|
||||
}
|
||||
if err := session.Remote.Init(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(s.sessions) == 0 {
|
||||
var err error
|
||||
if s.conn, err = net.ListenPacket("udp", s.address); err != nil {
|
||||
return
|
||||
}
|
||||
go s.handle()
|
||||
}
|
||||
|
||||
session.conn = s.conn
|
||||
|
||||
if s.sessions == nil {
|
||||
s.sessions = map[uint32]*Session{}
|
||||
}
|
||||
s.sessions[session.RemoteSSRC] = session
|
||||
|
||||
s.sessions[session.Remote.SSRC] = session
|
||||
}
|
||||
|
||||
func (s *Server) RemoveSession(session *Session) {
|
||||
delete(s.sessions, session.RemoteSSRC)
|
||||
func (s *Server) DelSession(session *Session) {
|
||||
s.mu.Lock()
|
||||
|
||||
delete(s.sessions, session.Remote.SSRC)
|
||||
|
||||
if len(s.sessions) == 0 {
|
||||
_ = s.conn.Close()
|
||||
}
|
||||
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
func (s *Server) Serve(conn net.PacketConn) error {
|
||||
s.conn = conn
|
||||
|
||||
buf := make([]byte, 2048)
|
||||
func (s *Server) handle() error {
|
||||
b := make([]byte, 2048)
|
||||
for {
|
||||
n, addr, err := conn.ReadFrom(buf)
|
||||
n, _, err := s.conn.ReadFrom(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if s.sessions == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Multiplexing RTP Data and Control Packets on a Single Port
|
||||
// https://datatracker.ietf.org/doc/html/rfc5761
|
||||
|
||||
var handle func([]byte) error
|
||||
switch packetType := b[1]; packetType {
|
||||
case 99, 110, 0x80 | 99, 0x80 | 110:
|
||||
// this is default position for SSRC in RTP packet
|
||||
ssrc := binary.BigEndian.Uint32(b[8:])
|
||||
if session, ok := s.sessions[ssrc]; ok {
|
||||
session.ReadRTP(b[:n])
|
||||
}
|
||||
|
||||
// this is default position for SSRC in RTP packet
|
||||
ssrc := binary.BigEndian.Uint32(buf[8:])
|
||||
session, ok := s.sessions[ssrc]
|
||||
if ok {
|
||||
handle = session.HandleRTP
|
||||
} else {
|
||||
case 200, 201, 202, 203, 204, 205, 206, 207:
|
||||
// this is default position for SSRC in RTCP packet
|
||||
ssrc = binary.BigEndian.Uint32(buf[4:])
|
||||
if session, ok = s.sessions[ssrc]; !ok {
|
||||
continue // skip unknown ssrc
|
||||
ssrc := binary.BigEndian.Uint32(b[4:])
|
||||
if session, ok := s.sessions[ssrc]; ok {
|
||||
session.ReadRTCP(b[:n])
|
||||
}
|
||||
|
||||
handle = session.HandleRTCP
|
||||
}
|
||||
|
||||
if session.Write == nil {
|
||||
session.Write = func(b []byte) (int, error) {
|
||||
return conn.WriteTo(b, addr)
|
||||
}
|
||||
}
|
||||
|
||||
atomic.AddUint32(&session.Recv, uint32(n))
|
||||
|
||||
if err = handle(buf[:n]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+70
-116
@@ -1,154 +1,108 @@
|
||||
package srtp
|
||||
|
||||
import (
|
||||
"time"
|
||||
"net"
|
||||
|
||||
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||
"github.com/pion/rtcp"
|
||||
"github.com/pion/rtp"
|
||||
"github.com/pion/srtp/v2"
|
||||
)
|
||||
|
||||
type Session struct {
|
||||
LocalSSRC uint32 // outgoing SSRC
|
||||
RemoteSSRC uint32 // incoming SSRC
|
||||
Local *Endpoint
|
||||
Remote *Endpoint
|
||||
|
||||
localCtx *srtp.Context // write context
|
||||
remoteCtx *srtp.Context // read context
|
||||
OnReadRTP func(packet *rtp.Packet)
|
||||
|
||||
Write func(b []byte) (int, error)
|
||||
Track *core.Receiver
|
||||
Recv uint32
|
||||
Recv int // bytes recv
|
||||
Send int // bytes send
|
||||
|
||||
Deadline *time.Timer
|
||||
|
||||
lastSequence uint32
|
||||
lastTimestamp uint32
|
||||
//lastPacket *rtp.Packet
|
||||
lastTime time.Time
|
||||
jitter float64
|
||||
//sequenceCycle uint16
|
||||
totalLost uint32
|
||||
conn net.PacketConn // local conn endpoint
|
||||
addr net.Addr // remote addr
|
||||
}
|
||||
|
||||
func (s *Session) LastTime() time.Time {
|
||||
return s.lastTime
|
||||
type Endpoint struct {
|
||||
Addr string
|
||||
Port uint16
|
||||
MasterKey []byte
|
||||
MasterSalt []byte
|
||||
SSRC uint32
|
||||
|
||||
srtp *srtp.Context
|
||||
}
|
||||
|
||||
func (s *Session) SetKeys(localKey, localSalt, remoteKey, remoteSalt []byte) (err error) {
|
||||
s.localCtx, err = srtp.CreateContext(localKey, localSalt, GuessProfile(localKey))
|
||||
func (e *Endpoint) Init() error {
|
||||
var profile srtp.ProtectionProfile
|
||||
|
||||
switch len(e.MasterKey) {
|
||||
case 16:
|
||||
profile = srtp.ProtectionProfileAes128CmHmacSha1_80
|
||||
//case 32:
|
||||
// return srtp.ProtectionProfileAes256CmHmacSha1_80
|
||||
}
|
||||
|
||||
var err error
|
||||
e.srtp, err = srtp.CreateContext(e.MasterKey, e.MasterSalt, profile)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Session) WriteRTP(packet *rtp.Packet) (int, error) {
|
||||
b, err := packet.Marshal()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if b, err = s.Local.srtp.EncryptRTP(nil, b, nil); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return s.conn.WriteTo(b, s.addr)
|
||||
}
|
||||
|
||||
func (s *Session) ReadRTP(b []byte) {
|
||||
packet := &rtp.Packet{}
|
||||
|
||||
b, err := s.Remote.srtp.DecryptRTP(nil, b, &packet.Header)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s.remoteCtx, err = srtp.CreateContext(remoteKey, remoteSalt, GuessProfile(remoteKey))
|
||||
return
|
||||
|
||||
if err = packet.Unmarshal(b); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if s.OnReadRTP != nil {
|
||||
s.OnReadRTP(packet)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
if s.Deadline != nil {
|
||||
s.Deadline.Reset(core.ConnDeadline)
|
||||
}
|
||||
|
||||
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) {
|
||||
func (s *Session) ReadRTCP(b []byte) {
|
||||
header := &rtcp.Header{}
|
||||
if data, err = s.remoteCtx.DecryptRTCP(nil, data, header); err != nil {
|
||||
b, err := s.Remote.srtp.DecryptRTCP(nil, b, header)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = rtcp.Unmarshal(data); err != nil {
|
||||
if _, err = rtcp.Unmarshal(b); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if header.Type == rtcp.TypeSenderReport {
|
||||
err = s.KeepAlive()
|
||||
_ = 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),
|
||||
}}
|
||||
func (s *Session) KeepAlive() error {
|
||||
rep := rtcp.ReceiverReport{SSRC: s.Local.SSRC}
|
||||
b, err := rep.Marshal()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 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 b, err = s.Local.srtp.EncryptRTCP(nil, b, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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
|
||||
_, err = s.conn.WriteTo(b, s.addr)
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user