rfc8888.go 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. // SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
  2. // SPDX-License-Identifier: MIT
  3. package rtcp
  4. import (
  5. "encoding/binary"
  6. "errors"
  7. "fmt"
  8. "math"
  9. )
  10. // https://www.rfc-editor.org/rfc/rfc8888.html#name-rtcp-congestion-control-fee
  11. // 0 1 2 3
  12. // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  13. // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  14. // |V=2|P| FMT=11 | PT = 205 | length |
  15. // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  16. // | SSRC of RTCP packet sender |
  17. // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  18. // | SSRC of 1st RTP Stream |
  19. // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  20. // | begin_seq | num_reports |
  21. // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  22. // |R|ECN| Arrival time offset | ... .
  23. // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  24. // . .
  25. // . .
  26. // . .
  27. // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  28. // | SSRC of nth RTP Stream |
  29. // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  30. // | begin_seq | num_reports |
  31. // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  32. // |R|ECN| Arrival time offset | ... |
  33. // . .
  34. // . .
  35. // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  36. // | Report Timestamp (32 bits) |
  37. // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  38. var (
  39. errReportBlockLength = errors.New("feedback report blocks must be at least 8 bytes")
  40. errIncorrectNumReports = errors.New("feedback report block contains less reports than num_reports")
  41. errMetricBlockLength = errors.New("feedback report metric blocks must be exactly 2 bytes")
  42. )
  43. // ECN represents the two ECN bits
  44. type ECN uint8
  45. const (
  46. //nolint:misspell
  47. // ECNNonECT signals Non ECN-Capable Transport, Non-ECT
  48. ECNNonECT ECN = iota // 00
  49. //nolint:misspell
  50. // ECNECT1 signals ECN Capable Transport, ECT(0)
  51. ECNECT1 // 01
  52. //nolint:misspell
  53. // ECNECT0 signals ECN Capable Transport, ECT(1)
  54. ECNECT0 // 10
  55. // ECNCE signals ECN Congestion Encountered, CE
  56. ECNCE // 11
  57. )
  58. const (
  59. reportTimestampLength = 4
  60. reportBlockOffset = 8
  61. )
  62. // CCFeedbackReport is a Congestion Control Feedback Report as defined in
  63. // https://www.rfc-editor.org/rfc/rfc8888.html#name-rtcp-congestion-control-fee
  64. type CCFeedbackReport struct {
  65. // SSRC of sender
  66. SenderSSRC uint32
  67. // Report Blocks
  68. ReportBlocks []CCFeedbackReportBlock
  69. // Basetime
  70. ReportTimestamp uint32
  71. }
  72. // DestinationSSRC returns an array of SSRC values that this packet refers to.
  73. func (b CCFeedbackReport) DestinationSSRC() []uint32 {
  74. ssrcs := make([]uint32, len(b.ReportBlocks))
  75. for i, block := range b.ReportBlocks {
  76. ssrcs[i] = block.MediaSSRC
  77. }
  78. return ssrcs
  79. }
  80. // Len returns the length of the report in bytes
  81. func (b *CCFeedbackReport) Len() int {
  82. n := 0
  83. for _, block := range b.ReportBlocks {
  84. n += block.len()
  85. }
  86. return reportBlockOffset + n + reportTimestampLength
  87. }
  88. // Header returns the Header associated with this packet.
  89. func (b *CCFeedbackReport) Header() Header {
  90. return Header{
  91. Padding: false,
  92. Count: FormatCCFB,
  93. Type: TypeTransportSpecificFeedback,
  94. Length: uint16(b.Len()/4 - 1),
  95. }
  96. }
  97. // Marshal encodes the Congestion Control Feedback Report in binary
  98. func (b CCFeedbackReport) Marshal() ([]byte, error) {
  99. header := b.Header()
  100. headerBuf, err := header.Marshal()
  101. if err != nil {
  102. return nil, err
  103. }
  104. length := 4 * (header.Length + 1)
  105. buf := make([]byte, length)
  106. copy(buf[:headerLength], headerBuf)
  107. binary.BigEndian.PutUint32(buf[headerLength:], b.SenderSSRC)
  108. offset := reportBlockOffset
  109. for _, block := range b.ReportBlocks {
  110. b, err := block.marshal()
  111. if err != nil {
  112. return nil, err
  113. }
  114. copy(buf[offset:], b)
  115. offset += block.len()
  116. }
  117. binary.BigEndian.PutUint32(buf[offset:], b.ReportTimestamp)
  118. return buf, nil
  119. }
  120. func (b CCFeedbackReport) String() string {
  121. out := fmt.Sprintf("CCFB:\n\tHeader %v\n", b.Header())
  122. out += fmt.Sprintf("CCFB:\n\tSender SSRC %d\n", b.SenderSSRC)
  123. out += fmt.Sprintf("\tReport Timestamp %d\n", b.ReportTimestamp)
  124. out += "\tFeedback Reports \n"
  125. for _, report := range b.ReportBlocks {
  126. out += fmt.Sprintf("%v ", report)
  127. }
  128. out += "\n"
  129. return out
  130. }
  131. // Unmarshal decodes the Congestion Control Feedback Report from binary
  132. func (b *CCFeedbackReport) Unmarshal(rawPacket []byte) error {
  133. if len(rawPacket) < headerLength+ssrcLength+reportTimestampLength {
  134. return errPacketTooShort
  135. }
  136. var h Header
  137. if err := h.Unmarshal(rawPacket); err != nil {
  138. return err
  139. }
  140. if h.Type != TypeTransportSpecificFeedback {
  141. return errWrongType
  142. }
  143. b.SenderSSRC = binary.BigEndian.Uint32(rawPacket[headerLength:])
  144. reportTimestampOffset := len(rawPacket) - reportTimestampLength
  145. b.ReportTimestamp = binary.BigEndian.Uint32(rawPacket[reportTimestampOffset:])
  146. offset := reportBlockOffset
  147. b.ReportBlocks = []CCFeedbackReportBlock{}
  148. for offset < reportTimestampOffset {
  149. var block CCFeedbackReportBlock
  150. if err := block.unmarshal(rawPacket[offset:]); err != nil {
  151. return err
  152. }
  153. b.ReportBlocks = append(b.ReportBlocks, block)
  154. offset += block.len()
  155. }
  156. return nil
  157. }
  158. const (
  159. ssrcOffset = 0
  160. beginSequenceOffset = 4
  161. numReportsOffset = 6
  162. reportsOffset = 8
  163. maxMetricBlocks = 16384
  164. )
  165. // CCFeedbackReportBlock is a Feedback Report Block
  166. type CCFeedbackReportBlock struct {
  167. // SSRC of the RTP stream on which this block is reporting
  168. MediaSSRC uint32
  169. BeginSequence uint16
  170. MetricBlocks []CCFeedbackMetricBlock
  171. }
  172. // len returns the length of the report block in bytes
  173. func (b *CCFeedbackReportBlock) len() int {
  174. n := len(b.MetricBlocks)
  175. if n%2 != 0 {
  176. n++
  177. }
  178. return reportsOffset + 2*n
  179. }
  180. func (b CCFeedbackReportBlock) String() string {
  181. out := fmt.Sprintf("\tReport Block Media SSRC %d\n", b.MediaSSRC)
  182. out += fmt.Sprintf("\tReport Begin Sequence Nr %d\n", b.BeginSequence)
  183. out += fmt.Sprintf("\tReport length %d\n\t", len(b.MetricBlocks))
  184. for i, block := range b.MetricBlocks {
  185. out += fmt.Sprintf("{nr: %d, rx: %v, ts: %v} ", b.BeginSequence+uint16(i), block.Received, block.ArrivalTimeOffset)
  186. }
  187. out += "\n"
  188. return out
  189. }
  190. // marshal encodes the Congestion Control Feedback Report Block in binary
  191. func (b CCFeedbackReportBlock) marshal() ([]byte, error) {
  192. if len(b.MetricBlocks) > maxMetricBlocks {
  193. return nil, errTooManyReports
  194. }
  195. buf := make([]byte, b.len())
  196. binary.BigEndian.PutUint32(buf[ssrcOffset:], b.MediaSSRC)
  197. binary.BigEndian.PutUint16(buf[beginSequenceOffset:], b.BeginSequence)
  198. length := uint16(len(b.MetricBlocks))
  199. if length > 0 {
  200. length--
  201. }
  202. binary.BigEndian.PutUint16(buf[numReportsOffset:], length)
  203. for i, block := range b.MetricBlocks {
  204. b, err := block.marshal()
  205. if err != nil {
  206. return nil, err
  207. }
  208. copy(buf[reportsOffset+i*2:], b)
  209. }
  210. return buf, nil
  211. }
  212. // Unmarshal decodes the Congestion Control Feedback Report Block from binary
  213. func (b *CCFeedbackReportBlock) unmarshal(rawPacket []byte) error {
  214. if len(rawPacket) < reportsOffset {
  215. return errReportBlockLength
  216. }
  217. b.MediaSSRC = binary.BigEndian.Uint32(rawPacket[:beginSequenceOffset])
  218. b.BeginSequence = binary.BigEndian.Uint16(rawPacket[beginSequenceOffset:numReportsOffset])
  219. numReportsField := binary.BigEndian.Uint16(rawPacket[numReportsOffset:])
  220. if numReportsField == 0 {
  221. return nil
  222. }
  223. if int(b.BeginSequence)+int(numReportsField) > math.MaxUint16 {
  224. return errIncorrectNumReports
  225. }
  226. endSequence := b.BeginSequence + numReportsField
  227. numReports := int(endSequence - b.BeginSequence + 1)
  228. if len(rawPacket) < reportsOffset+numReports*2 {
  229. return errIncorrectNumReports
  230. }
  231. b.MetricBlocks = make([]CCFeedbackMetricBlock, numReports)
  232. for i := int(0); i < numReports; i++ {
  233. var mb CCFeedbackMetricBlock
  234. offset := reportsOffset + 2*i
  235. if err := mb.unmarshal(rawPacket[offset : offset+2]); err != nil {
  236. return err
  237. }
  238. b.MetricBlocks[i] = mb
  239. }
  240. return nil
  241. }
  242. const (
  243. metricBlockLength = 2
  244. )
  245. // CCFeedbackMetricBlock is a Feedback Metric Block
  246. type CCFeedbackMetricBlock struct {
  247. Received bool
  248. ECN ECN
  249. // Offset in 1/1024 seconds before Report Timestamp
  250. ArrivalTimeOffset uint16
  251. }
  252. // Marshal encodes the Congestion Control Feedback Metric Block in binary
  253. func (b CCFeedbackMetricBlock) marshal() ([]byte, error) {
  254. buf := make([]byte, 2)
  255. r := uint16(0)
  256. if b.Received {
  257. r = 1
  258. }
  259. dst, err := setNBitsOfUint16(0, 1, 0, r)
  260. if err != nil {
  261. return nil, err
  262. }
  263. dst, err = setNBitsOfUint16(dst, 2, 1, uint16(b.ECN))
  264. if err != nil {
  265. return nil, err
  266. }
  267. dst, err = setNBitsOfUint16(dst, 13, 3, b.ArrivalTimeOffset)
  268. if err != nil {
  269. return nil, err
  270. }
  271. binary.BigEndian.PutUint16(buf, dst)
  272. return buf, nil
  273. }
  274. // Unmarshal decodes the Congestion Control Feedback Metric Block from binary
  275. func (b *CCFeedbackMetricBlock) unmarshal(rawPacket []byte) error {
  276. if len(rawPacket) != metricBlockLength {
  277. return errMetricBlockLength
  278. }
  279. b.Received = rawPacket[0]&0x80 != 0
  280. if !b.Received {
  281. b.ECN = ECNNonECT
  282. b.ArrivalTimeOffset = 0
  283. return nil
  284. }
  285. b.ECN = ECN(rawPacket[0] >> 5 & 0x03)
  286. b.ArrivalTimeOffset = binary.BigEndian.Uint16(rawPacket) & 0x1FFF
  287. return nil
  288. }