commit f97b41d3edf722a016b54ea138e5f27129d66cf1
parent 65e214248fc8e29f5c4c74075b2a74c9d10232a5
Author: Oliver Lowe <o@olowe.co>
Date: Sun, 16 Jun 2024 18:36:07 +1000
rtp: implement marshalling packets
No reproducible tests yet. Tested by:
1. streaming the first MPEG-TS segment of Big Buck Bunny 480p with ffmpeg
2. via RTP to loopback interface, unmarshal then marshal a packet one by one
3. check md5 checksum of each packet
4. send marshalled packet to VLC
And it played ok!
We may be able to generate our own text payloads in line with RFC
4103. That'll come later.
Diffstat:
| M | rtp/rtp.go | | | 65 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- |
1 file changed, 62 insertions(+), 3 deletions(-)
diff --git a/rtp/rtp.go b/rtp/rtp.go
@@ -26,8 +26,11 @@ type Header struct {
Extension *Extension
}
-const VersionRFC3550 uint8 = 2
-const VersionDraft uint8 = 1
+const (
+ versionPreSpec uint8 = 0
+ VersionDraft uint8 = 1 << 6
+ VersionRFC3550 = 1 << 7
+)
type PayloadType uint8
@@ -36,6 +39,14 @@ const (
// ...
)
+func (t PayloadType) String() string {
+ switch t {
+ case PayloadMP2T:
+ return "MP2T"
+ }
+ return "unknown"
+}
+
type Extension struct {
Profile [2]byte
Data []byte
@@ -49,6 +60,7 @@ func Unmarshal(data []byte, p *Packet) error {
// sequence (16)
// timestamp (32)
// syncsource (32)
+ // TODO(otl): set this as a constant - headerSize? minPacketSize?
need := 1 + 1 + 2 + 4 + 4
if len(data) < need {
return fmt.Errorf("need %d bytes, have %d", need, len(data))
@@ -64,7 +76,7 @@ func Unmarshal(data []byte, p *Packet) error {
// 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.Type = PayloadType(data[1] & 0x7f)
p.Header.Sequence = binary.BigEndian.Uint16(data[2:4])
p.Header.Timestamp = binary.BigEndian.Uint32(data[4:8])
@@ -75,6 +87,7 @@ func Unmarshal(data []byte, p *Packet) error {
}
data = data[12:]
+ // is the extension bit set?
if data[0]&0b00010000 > 0 {
if len(data) < 4 {
return fmt.Errorf("header extension: %d bytes after header, need %d", len(data), 4)
@@ -110,3 +123,49 @@ func Unmarshal(data []byte, p *Packet) error {
p.Payload = data[n:]
return nil
}
+
+func Marshal(p *Packet) ([]byte, error) {
+ if p.Header.Version > VersionRFC3550 {
+ return nil, fmt.Errorf("bad version %v", p.Header.Version)
+ }
+ buf := make([]byte, 12) // see Unmarshal() for size
+ buf[0] |= p.Header.Version
+ if p.Header.padding {
+ buf[0] |= 0b00100000
+ }
+ if p.Header.Extension != nil {
+ buf[0] |= 0b00010000
+ }
+ maxContribCount := 0x0f // max 4-bit integer
+ if len(p.Header.ContribSource) > maxContribCount {
+ return nil, fmt.Errorf("contribution source count %d greater than max %d", len(p.Header.ContribSource), maxContribCount)
+ }
+ buf[0] |= uint8(len(p.Header.ContribSource))
+
+ if p.Header.Marker {
+ buf[1] |= 0b10000000
+ }
+ if p.Header.Type > 0x7f {
+ return nil, fmt.Errorf("payload type %s (%d) greater than max %d", p.Header.Type, p.Header.Type, 0x7f)
+ }
+ buf[1] |= byte(p.Header.Type)
+
+ binary.BigEndian.PutUint16(buf[2:4], p.Header.Sequence)
+ binary.BigEndian.PutUint32(buf[4:8], p.Header.Timestamp)
+ binary.BigEndian.PutUint32(buf[8:12], p.Header.SyncSource)
+
+ if p.Header.Extension != nil {
+ buf = append(buf, p.Header.Extension.Profile[:]...)
+ if len(p.Header.Extension.Data) > 0xffff { // max uint16
+ return buf, fmt.Errorf("extension data length %d greater than max %d", len(p.Header.Extension.Data), 0xffff)
+ }
+ buf = binary.BigEndian.AppendUint16(buf, uint16(len(p.Header.Extension.Data)))
+ buf = append(buf, p.Header.Extension.Data...)
+ }
+
+ for _, src := range p.Header.ContribSource {
+ buf = binary.BigEndian.AppendUint32(buf, src)
+ }
+
+ return append(buf, p.Payload...), nil
+}