rfc8888.go 9.4 KB

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