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