mirror of
https://github.com/giongto35/cloud-game.git
synced 2026-07-23 01:57:22 +00:00
275 lines
8.1 KiB
Go
275 lines
8.1 KiB
Go
package rtp
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"io"
|
|
)
|
|
|
|
// TODO(@kixelated) Remove Header.PayloadOffset and Packet.Raw
|
|
|
|
// Header represents an RTP packet header
|
|
// NOTE: PayloadOffset is populated by Marshal/Unmarshal and should not be modified
|
|
type Header struct {
|
|
Version uint8
|
|
Padding bool
|
|
Extension bool
|
|
Marker bool
|
|
PayloadOffset int
|
|
PayloadType uint8
|
|
SequenceNumber uint16
|
|
Timestamp uint32
|
|
SSRC uint32
|
|
CSRC []uint32
|
|
ExtensionProfile uint16
|
|
ExtensionPayload []byte
|
|
}
|
|
|
|
// Packet represents an RTP Packet
|
|
// NOTE: Raw is populated by Marshal/Unmarshal and should not be modified
|
|
type Packet struct {
|
|
Header
|
|
Raw []byte
|
|
Payload []byte
|
|
}
|
|
|
|
const (
|
|
headerLength = 4
|
|
versionShift = 6
|
|
versionMask = 0x3
|
|
paddingShift = 5
|
|
paddingMask = 0x1
|
|
extensionShift = 4
|
|
extensionMask = 0x1
|
|
ccMask = 0xF
|
|
markerShift = 7
|
|
markerMask = 0x1
|
|
ptMask = 0x7F
|
|
seqNumOffset = 2
|
|
seqNumLength = 2
|
|
timestampOffset = 4
|
|
timestampLength = 4
|
|
ssrcOffset = 8
|
|
ssrcLength = 4
|
|
csrcOffset = 12
|
|
csrcLength = 4
|
|
)
|
|
|
|
// String helps with debugging by printing packet information in a readable way
|
|
func (p Packet) String() string {
|
|
out := "RTP PACKET:\n"
|
|
|
|
out += fmt.Sprintf("\tVersion: %v\n", p.Version)
|
|
out += fmt.Sprintf("\tMarker: %v\n", p.Marker)
|
|
out += fmt.Sprintf("\tPayload Type: %d\n", p.PayloadType)
|
|
out += fmt.Sprintf("\tSequence Number: %d\n", p.SequenceNumber)
|
|
out += fmt.Sprintf("\tTimestamp: %d\n", p.Timestamp)
|
|
out += fmt.Sprintf("\tSSRC: %d (%x)\n", p.SSRC, p.SSRC)
|
|
out += fmt.Sprintf("\tPayload Length: %d\n", len(p.Payload))
|
|
|
|
return out
|
|
}
|
|
|
|
// Unmarshal parses the passed byte slice and stores the result in the Header this method is called upon
|
|
func (h *Header) Unmarshal(rawPacket []byte) error {
|
|
if len(rawPacket) < headerLength {
|
|
return fmt.Errorf("RTP header size insufficient; %d < %d", len(rawPacket), headerLength)
|
|
}
|
|
|
|
/*
|
|
* 0 1 2 3
|
|
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
* |V=2|P|X| CC |M| PT | sequence number |
|
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
* | timestamp |
|
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
* | synchronization source (SSRC) identifier |
|
|
* +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
|
* | contributing source (CSRC) identifiers |
|
|
* | .... |
|
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
*/
|
|
|
|
h.Version = rawPacket[0] >> versionShift & versionMask
|
|
h.Padding = (rawPacket[0] >> paddingShift & paddingMask) > 0
|
|
h.Extension = (rawPacket[0] >> extensionShift & extensionMask) > 0
|
|
h.CSRC = make([]uint32, rawPacket[0]&ccMask)
|
|
|
|
h.Marker = (rawPacket[1] >> markerShift & markerMask) > 0
|
|
h.PayloadType = rawPacket[1] & ptMask
|
|
|
|
h.SequenceNumber = binary.BigEndian.Uint16(rawPacket[seqNumOffset : seqNumOffset+seqNumLength])
|
|
h.Timestamp = binary.BigEndian.Uint32(rawPacket[timestampOffset : timestampOffset+timestampLength])
|
|
h.SSRC = binary.BigEndian.Uint32(rawPacket[ssrcOffset : ssrcOffset+ssrcLength])
|
|
|
|
currOffset := csrcOffset + (len(h.CSRC) * csrcLength)
|
|
if len(rawPacket) < currOffset {
|
|
return fmt.Errorf("RTP header size insufficient; %d < %d", len(rawPacket), currOffset)
|
|
}
|
|
|
|
for i := range h.CSRC {
|
|
offset := csrcOffset + (i * csrcLength)
|
|
h.CSRC[i] = binary.BigEndian.Uint32(rawPacket[offset:])
|
|
}
|
|
|
|
if h.Extension {
|
|
if len(rawPacket) < currOffset+4 {
|
|
return fmt.Errorf("RTP header size insufficient for extension; %d < %d", len(rawPacket), currOffset)
|
|
}
|
|
|
|
h.ExtensionProfile = binary.BigEndian.Uint16(rawPacket[currOffset:])
|
|
currOffset += 2
|
|
extensionLength := int(binary.BigEndian.Uint16(rawPacket[currOffset:])) * 4
|
|
currOffset += 2
|
|
|
|
if len(rawPacket) < currOffset+extensionLength {
|
|
return fmt.Errorf("RTP header size insufficient for extension length; %d < %d", len(rawPacket), currOffset+extensionLength)
|
|
}
|
|
|
|
h.ExtensionPayload = rawPacket[currOffset : currOffset+extensionLength]
|
|
currOffset += len(h.ExtensionPayload)
|
|
}
|
|
h.PayloadOffset = currOffset
|
|
|
|
return nil
|
|
}
|
|
|
|
// Unmarshal parses the passed byte slice and stores the result in the Packet this method is called upon
|
|
func (p *Packet) Unmarshal(rawPacket []byte) error {
|
|
if err := p.Header.Unmarshal(rawPacket); err != nil {
|
|
return err
|
|
}
|
|
|
|
p.Payload = rawPacket[p.PayloadOffset:]
|
|
p.Raw = rawPacket
|
|
return nil
|
|
}
|
|
|
|
// Marshal serializes the header into bytes.
|
|
func (h *Header) Marshal() (buf []byte, err error) {
|
|
buf = make([]byte, h.MarshalSize())
|
|
|
|
n, err := h.MarshalTo(buf)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return buf[:n], nil
|
|
}
|
|
|
|
// MarshalTo serializes the header and writes to the buffer.
|
|
func (h *Header) MarshalTo(buf []byte) (n int, err error) {
|
|
/*
|
|
* 0 1 2 3
|
|
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
* |V=2|P|X| CC |M| PT | sequence number |
|
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
* | timestamp |
|
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
* | synchronization source (SSRC) identifier |
|
|
* +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
|
* | contributing source (CSRC) identifiers |
|
|
* | .... |
|
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
*/
|
|
|
|
size := h.MarshalSize()
|
|
if size > len(buf) {
|
|
return 0, io.ErrShortBuffer
|
|
}
|
|
|
|
// The first byte contains the version, padding bit, extension bit, and csrc size
|
|
buf[0] = (h.Version << versionShift) | uint8(len(h.CSRC))
|
|
if h.Padding {
|
|
buf[0] |= 1 << paddingShift
|
|
}
|
|
|
|
if h.Extension {
|
|
buf[0] |= 1 << extensionShift
|
|
}
|
|
|
|
// The second byte contains the marker bit and payload type.
|
|
buf[1] = h.PayloadType
|
|
if h.Marker {
|
|
buf[1] |= 1 << markerShift
|
|
}
|
|
|
|
binary.BigEndian.PutUint16(buf[2:4], h.SequenceNumber)
|
|
binary.BigEndian.PutUint32(buf[4:8], h.Timestamp)
|
|
binary.BigEndian.PutUint32(buf[8:12], h.SSRC)
|
|
|
|
n = 12
|
|
for _, csrc := range h.CSRC {
|
|
binary.BigEndian.PutUint32(buf[n:n+4], csrc)
|
|
n += 4
|
|
}
|
|
|
|
// Calculate the size of the header by seeing how many bytes we're written.
|
|
// TODO This is a BUG but fixing it causes more issues.
|
|
h.PayloadOffset = n
|
|
|
|
if h.Extension {
|
|
if len(h.ExtensionPayload)%4 != 0 {
|
|
//the payload must be in 32-bit words.
|
|
return 0, io.ErrShortBuffer
|
|
}
|
|
extSize := uint16(len(h.ExtensionPayload) / 4)
|
|
|
|
binary.BigEndian.PutUint16(buf[n+0:n+2], h.ExtensionProfile)
|
|
binary.BigEndian.PutUint16(buf[n+2:n+4], extSize)
|
|
n += 4
|
|
|
|
n += copy(buf[n:], h.ExtensionPayload)
|
|
}
|
|
|
|
return n, nil
|
|
}
|
|
|
|
// MarshalSize returns the size of the header once marshaled.
|
|
func (h *Header) MarshalSize() int {
|
|
// NOTE: Be careful to match the MarshalTo() method.
|
|
size := 12 + (len(h.CSRC) * csrcLength)
|
|
|
|
if h.Extension {
|
|
size += 4 + len(h.ExtensionPayload)
|
|
}
|
|
|
|
return size
|
|
}
|
|
|
|
// Marshal serializes the packet into bytes.
|
|
func (p *Packet) Marshal() (buf []byte, err error) {
|
|
buf = make([]byte, p.MarshalSize())
|
|
|
|
n, err := p.MarshalTo(buf)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return buf[:n], nil
|
|
}
|
|
|
|
// MarshalTo serializes the packet and writes to the buffer.
|
|
func (p *Packet) MarshalTo(buf []byte) (n int, err error) {
|
|
n, err = p.Header.MarshalTo(buf)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
// Make sure the buffer is large enough to hold the packet.
|
|
if n+len(p.Payload) > len(buf) {
|
|
return 0, io.ErrShortBuffer
|
|
}
|
|
|
|
m := copy(buf[n:], p.Payload)
|
|
p.Raw = buf[n : n+m]
|
|
|
|
return n + m, nil
|
|
}
|
|
|
|
// MarshalSize returns the size of the packet once marshaled.
|
|
func (p *Packet) MarshalSize() int {
|
|
return p.Header.MarshalSize() + len(p.Payload)
|
|
}
|