streaming

Media streaming and broadcast systems in Go
Log | Files | Refs | README | LICENSE

commit 65e214248fc8e29f5c4c74075b2a74c9d10232a5
parent 0cf1dfb386ded8d8b966ebeca50bbca10bb82d11
Author: Oliver Lowe <o@olowe.co>
Date:   Sun, 16 Jun 2024 16:09:31 +1000

rtp: add header decoding

Not complete or properly tested but it's a start!

Diffstat:
Artp/rtp.go | 112+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 112 insertions(+), 0 deletions(-)

diff --git a/rtp/rtp.go b/rtp/rtp.go @@ -0,0 +1,112 @@ +// Package rtp implements the Real-Time Transport Protocol as +// specified in RFC 3550. +package rtp + +import ( + "encoding/binary" + "errors" + "fmt" +) + +type Packet struct { + Header Header + Payload []byte +} + +type Header struct { + Version uint8 + // TODO(otl): do we store padding bytes? how many? + padding bool + Marker bool + Type PayloadType + Sequence uint16 + Timestamp uint32 + SyncSource uint32 + ContribSource []uint32 + Extension *Extension +} + +const VersionRFC3550 uint8 = 2 +const VersionDraft uint8 = 1 + +type PayloadType uint8 + +const ( + PayloadMP2T PayloadType = 33 + // ... +) + +type Extension struct { + Profile [2]byte + Data []byte +} + +var ErrNoPayload = errors.New("no payload") + +func Unmarshal(data []byte, p *Packet) error { + // version (2), padding (1), ext (1), contribcount (4) + // marker (1), type (7) + // sequence (16) + // timestamp (32) + // syncsource (32) + need := 1 + 1 + 2 + 4 + 4 + if len(data) < need { + return fmt.Errorf("need %d bytes, have %d", need, len(data)) + } + + p.Header.Version = uint8(data[0] & 0b11000000) + p.Header.padding = data[0]&0b00100000 > 0 + // extension bit, ignore til later, 0b00010000 + cc := data[0] & 0b00001111 + if cc > 0 { + p.Header.ContribSource = make([]uint32, cc) + } + + // m t t t t t t t + p.Header.Marker = data[1]&0x80 > 0 + p.Header.Type = PayloadType(uint8(data[1] & 0x7f)) + + p.Header.Sequence = binary.BigEndian.Uint16(data[2:4]) + p.Header.Timestamp = binary.BigEndian.Uint32(data[4:8]) + p.Header.SyncSource = binary.BigEndian.Uint32(data[8:12]) + + if len(data) == 12 { + return ErrNoPayload + } + data = data[12:] + + if data[0]&0b00010000 > 0 { + if len(data) < 4 { + return fmt.Errorf("header extension: %d bytes after header, need %d", len(data), 4) + } + ext := &Extension{} + copy(ext.Profile[:], data[:2]) + length := int(binary.BigEndian.Uint16(data[2:4])) + if len(data) < length { + return fmt.Errorf("header extension: reports length %d bytes, only have %d", length, len(data)) + } + if length > 0 { + ext.Data = data[4 : 4+length] + data = data[4+length:] + } else { + data = data[4:] + } + p.Header.Extension = ext + } + + need = len(p.Header.ContribSource) * 4 // 32-bit uints * 4 for number of bytes needed + if len(data) < need { + return fmt.Errorf("contribution sources: need %d bytes, only have %d", need, len(data)) + } + var n int + for i := range p.Header.ContribSource { + p.Header.ContribSource[i] = binary.BigEndian.Uint32(data[n : n+4]) + n += 4 + } + + if len(data[n:]) == 0 { + return ErrNoPayload + } + p.Payload = data[n:] + return nil +}