/* SPDX-License-Identifier: MIT
*
* Phobos
*/
package phobos
import (
"encoding/binary"
"net/netip"
"time"
)
const rtpHeaderSize = 12
type mediaPreset struct {
payloadType uint8
timestampStep uint16
}
var mediaPresets = [...]mediaPreset{
{96, 3000}, {96, 3600}, {96, 3750}, {96, 1500}, {96, 6000},
{97, 3000}, {97, 3600}, {97, 4500}, {97, 9000}, {97, 18000},
{98, 3000}, {98, 3600}, {98, 3750}, {98, 1500}, {98, 6000},
{99, 3000}, {99, 3600}, {100, 3000}, {100, 1500}, {100, 3750},
{102, 3000}, {102, 1500}, {102, 3600}, {104, 3000}, {104, 1500},
{106, 3000}, {106, 3750}, {108, 3000}, {108, 1500}, {110, 3000},
{112, 3000}, {112, 3600}, {114, 3000}, {114, 1500}, {116, 3000},
{118, 3000}, {119, 3000}, {120, 3000}, {122, 3000}, {123, 3000},
{124, 3000}, {125, 3000}, {125, 1500}, {126, 3000}, {127, 3000},
{127, 1500}, {127, 3750}, {96, 1502}, {96, 3003}, {97, 3753},
}
type rtpStream struct {
sequence uint16
timestamp uint32
ssrc uint32
timestampStep uint16
payloadType uint8
initialized bool
}
func (s *rtpStream) init(params MediaParams, rng *rng32) {
s.sequence = uint16(rng.next())
s.timestamp = rng.next()
if params.SSRC != 0 {
s.ssrc = params.SSRC
} else if s.ssrc = rng.next(); s.ssrc == 0 {
s.ssrc = 1
}
preset := mediaPresets[rng.below(len(mediaPresets))]
s.payloadType = preset.payloadType
if params.PayloadType != 0 {
s.payloadType = params.PayloadType
}
s.timestampStep = preset.timestampStep
if params.TimestampStep != 0 {
s.timestampStep = params.TimestampStep
}
s.initialized = true
}
func (s *rtpStream) writeHeader(buf []byte) {
buf[0] = 0x80
buf[1] = 0x80 | (s.payloadType & 0x7F)
binary.BigEndian.PutUint16(buf[2:], s.sequence)
binary.BigEndian.PutUint32(buf[4:], s.timestamp)
binary.BigEndian.PutUint32(buf[8:], s.ssrc)
s.sequence++
s.timestamp += uint32(s.timestampStep)
}
type maskerMedia struct {
rng rng32
params MediaParams
stream rtpStream
}
func (m *maskerMedia) TimerInterval() time.Duration {
return 5 * time.Second
}
func (m *maskerMedia) OnHandshakeRequest(sendForward SendFunc) {
sendBindingRequest(sendForward, &m.rng)
}
func (m *maskerMedia) OnDataUnwrap(buf []byte, length int, src netip.AddrPort, sendBack SendFunc) int {
if stunHasMagic(buf[:length]) {
return stunHandleIncoming(buf, length, src, sendBack)
}
if length < rtpHeaderSize+4 {
return -1
}
if buf[0]&0xC0 != 0x80 {
return -1
}
if m.params.PayloadType != 0 && buf[1]&0x7F != m.params.PayloadType {
return -1
}
if m.params.SSRC != 0 && binary.BigEndian.Uint32(buf[8:]) != m.params.SSRC {
return -1
}
payloadLength := length - rtpHeaderSize
copy(buf[:payloadLength], buf[rtpHeaderSize:length])
return payloadLength
}
func (m *maskerMedia) OnDataWrap(buf []byte, length int) int {
if length+rtpHeaderSize > len(buf) {
return -1
}
if !m.stream.initialized {
m.stream.init(m.params, &m.rng)
}
copy(buf[rtpHeaderSize:rtpHeaderSize+length], buf[:length])
m.stream.writeHeader(buf)
return length + rtpHeaderSize
}
func (m *maskerMedia) OnTimer(sendToServer SendFunc) {
sendBindingRequest(sendToServer, &m.rng)
}