rtpdump.go 4.0 KB

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