github.com/pion/webrtc/v4@v4.0.1/pkg/media/rtpdump/rtpdump.go (about) 1 // SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly> 2 // SPDX-License-Identifier: MIT 3 4 // Package rtpdump implements the RTPDump file format documented at 5 // https://www.cs.columbia.edu/irt/software/rtptools/ 6 package rtpdump 7 8 import ( 9 "encoding/binary" 10 "errors" 11 "net" 12 "time" 13 ) 14 15 const ( 16 pktHeaderLen = 8 17 headerLen = 16 18 preambleLen = 36 19 ) 20 21 var errMalformed = errors.New("malformed rtpdump") 22 23 // Header is the binary header at the top of the RTPDump file. It contains 24 // information about the source and start time of the packet stream included 25 // in the file. 26 type Header struct { 27 // start of recording (GMT) 28 Start time.Time 29 // network source (multicast address) 30 Source net.IP 31 // UDP port 32 Port uint16 33 } 34 35 // Marshal encodes the Header as binary. 36 func (h Header) Marshal() ([]byte, error) { 37 d := make([]byte, headerLen) 38 39 startNano := h.Start.UnixNano() 40 startSec := uint32(startNano / int64(time.Second)) 41 startUsec := uint32( 42 (startNano % int64(time.Second)) / int64(time.Microsecond), 43 ) 44 binary.BigEndian.PutUint32(d[0:], startSec) 45 binary.BigEndian.PutUint32(d[4:], startUsec) 46 47 source := h.Source.To4() 48 copy(d[8:], source) 49 50 binary.BigEndian.PutUint16(d[12:], h.Port) 51 52 return d, nil 53 } 54 55 // Unmarshal decodes the Header from binary. 56 func (h *Header) Unmarshal(d []byte) error { 57 if len(d) < headerLen { 58 return errMalformed 59 } 60 61 // time as a `struct timeval` 62 startSec := binary.BigEndian.Uint32(d[0:]) 63 startUsec := binary.BigEndian.Uint32(d[4:]) 64 h.Start = time.Unix(int64(startSec), int64(startUsec)*1e3).UTC() 65 66 // ipv4 address 67 h.Source = net.IPv4(d[8], d[9], d[10], d[11]) 68 69 h.Port = binary.BigEndian.Uint16(d[12:]) 70 71 // 2 bytes of padding (ignored) 72 73 return nil 74 } 75 76 // Packet contains an RTP or RTCP packet along a time offset when it was logged 77 // (relative to the Start of the recording in Header). The Payload may contain 78 // truncated packets to support logging just the headers of RTP/RTCP packets. 79 type Packet struct { 80 // Offset is the time since the start of recording in milliseconds 81 Offset time.Duration 82 // IsRTCP is true if the payload is RTCP, false if the payload is RTP 83 IsRTCP bool 84 // Payload is the binary RTP or RTCP payload. The contents may not parse 85 // as a valid packet if the contents have been truncated. 86 Payload []byte 87 } 88 89 // Marshal encodes the Packet as binary. 90 func (p Packet) Marshal() ([]byte, error) { 91 packetLength := len(p.Payload) 92 if p.IsRTCP { 93 packetLength = 0 94 } 95 96 hdr := packetHeader{ 97 Length: uint16(len(p.Payload)) + 8, 98 PacketLength: uint16(packetLength), 99 Offset: p.offsetMs(), 100 } 101 hdrData, err := hdr.Marshal() 102 if err != nil { 103 return nil, err 104 } 105 106 return append(hdrData, p.Payload...), nil 107 } 108 109 // Unmarshal decodes the Packet from binary. 110 func (p *Packet) Unmarshal(d []byte) error { 111 var hdr packetHeader 112 if err := hdr.Unmarshal(d); err != nil { 113 return err 114 } 115 116 p.Offset = hdr.offset() 117 p.IsRTCP = hdr.Length != 0 && hdr.PacketLength == 0 118 119 if hdr.Length < 8 { 120 return errMalformed 121 } 122 if len(d) < int(hdr.Length) { 123 return errMalformed 124 } 125 p.Payload = d[8:hdr.Length] 126 127 return nil 128 } 129 130 func (p *Packet) offsetMs() uint32 { 131 return uint32(p.Offset / time.Millisecond) 132 } 133 134 type packetHeader struct { 135 // length of packet, including this header (may be smaller than 136 // plen if not whole packet recorded) 137 Length uint16 138 // Actual header+payload length for RTP, 0 for RTCP 139 PacketLength uint16 140 // milliseconds since the start of recording 141 Offset uint32 142 } 143 144 func (p packetHeader) Marshal() ([]byte, error) { 145 d := make([]byte, pktHeaderLen) 146 147 binary.BigEndian.PutUint16(d[0:], p.Length) 148 binary.BigEndian.PutUint16(d[2:], p.PacketLength) 149 binary.BigEndian.PutUint32(d[4:], p.Offset) 150 151 return d, nil 152 } 153 154 func (p *packetHeader) Unmarshal(d []byte) error { 155 if len(d) < pktHeaderLen { 156 return errMalformed 157 } 158 159 p.Length = binary.BigEndian.Uint16(d[0:]) 160 p.PacketLength = binary.BigEndian.Uint16(d[2:]) 161 p.Offset = binary.BigEndian.Uint32(d[4:]) 162 163 return nil 164 } 165 166 func (p packetHeader) offset() time.Duration { 167 return time.Duration(p.Offset) * time.Millisecond 168 }