source_description.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. // SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
  2. // SPDX-License-Identifier: MIT
  3. package rtcp
  4. import (
  5. "encoding/binary"
  6. "fmt"
  7. )
  8. // SDESType is the item type used in the RTCP SDES control packet.
  9. type SDESType uint8
  10. // RTP SDES item types registered with IANA. See: https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml#rtp-parameters-5
  11. const (
  12. SDESEnd SDESType = iota // end of SDES list RFC 3550, 6.5
  13. SDESCNAME // canonical name RFC 3550, 6.5.1
  14. SDESName // user name RFC 3550, 6.5.2
  15. SDESEmail // user's electronic mail address RFC 3550, 6.5.3
  16. SDESPhone // user's phone number RFC 3550, 6.5.4
  17. SDESLocation // geographic user location RFC 3550, 6.5.5
  18. SDESTool // name of application or tool RFC 3550, 6.5.6
  19. SDESNote // notice about the source RFC 3550, 6.5.7
  20. SDESPrivate // private extensions RFC 3550, 6.5.8 (not implemented)
  21. )
  22. func (s SDESType) String() string {
  23. switch s {
  24. case SDESEnd:
  25. return "END"
  26. case SDESCNAME:
  27. return "CNAME"
  28. case SDESName:
  29. return "NAME"
  30. case SDESEmail:
  31. return "EMAIL"
  32. case SDESPhone:
  33. return "PHONE"
  34. case SDESLocation:
  35. return "LOC"
  36. case SDESTool:
  37. return "TOOL"
  38. case SDESNote:
  39. return "NOTE"
  40. case SDESPrivate:
  41. return "PRIV"
  42. default:
  43. return string(s)
  44. }
  45. }
  46. const (
  47. sdesSourceLen = 4
  48. sdesTypeLen = 1
  49. sdesTypeOffset = 0
  50. sdesOctetCountLen = 1
  51. sdesOctetCountOffset = 1
  52. sdesMaxOctetCount = (1 << 8) - 1
  53. sdesTextOffset = 2
  54. )
  55. // A SourceDescription (SDES) packet describes the sources in an RTP stream.
  56. type SourceDescription struct {
  57. Chunks []SourceDescriptionChunk
  58. }
  59. // NewCNAMESourceDescription creates a new SourceDescription with a single CNAME item.
  60. func NewCNAMESourceDescription(ssrc uint32, cname string) *SourceDescription {
  61. return &SourceDescription{
  62. Chunks: []SourceDescriptionChunk{{
  63. Source: ssrc,
  64. Items: []SourceDescriptionItem{{
  65. Type: SDESCNAME,
  66. Text: cname,
  67. }},
  68. }},
  69. }
  70. }
  71. // Marshal encodes the SourceDescription in binary
  72. func (s SourceDescription) Marshal() ([]byte, error) {
  73. /*
  74. * 0 1 2 3
  75. * 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
  76. * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  77. * header |V=2|P| SC | PT=SDES=202 | length |
  78. * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
  79. * chunk | SSRC/CSRC_1 |
  80. * 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  81. * | SDES items |
  82. * | ... |
  83. * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
  84. * chunk | SSRC/CSRC_2 |
  85. * 2 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  86. * | SDES items |
  87. * | ... |
  88. * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
  89. */
  90. rawPacket := make([]byte, s.len())
  91. packetBody := rawPacket[headerLength:]
  92. chunkOffset := 0
  93. for _, c := range s.Chunks {
  94. data, err := c.Marshal()
  95. if err != nil {
  96. return nil, err
  97. }
  98. copy(packetBody[chunkOffset:], data)
  99. chunkOffset += len(data)
  100. }
  101. if len(s.Chunks) > countMax {
  102. return nil, errTooManyChunks
  103. }
  104. hData, err := s.Header().Marshal()
  105. if err != nil {
  106. return nil, err
  107. }
  108. copy(rawPacket, hData)
  109. return rawPacket, nil
  110. }
  111. // Unmarshal decodes the SourceDescription from binary
  112. func (s *SourceDescription) Unmarshal(rawPacket []byte) error {
  113. /*
  114. * 0 1 2 3
  115. * 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
  116. * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  117. * header |V=2|P| SC | PT=SDES=202 | length |
  118. * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
  119. * chunk | SSRC/CSRC_1 |
  120. * 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  121. * | SDES items |
  122. * | ... |
  123. * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
  124. * chunk | SSRC/CSRC_2 |
  125. * 2 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  126. * | SDES items |
  127. * | ... |
  128. * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
  129. */
  130. var h Header
  131. if err := h.Unmarshal(rawPacket); err != nil {
  132. return err
  133. }
  134. if h.Type != TypeSourceDescription {
  135. return errWrongType
  136. }
  137. for i := headerLength; i < len(rawPacket); {
  138. var chunk SourceDescriptionChunk
  139. if err := chunk.Unmarshal(rawPacket[i:]); err != nil {
  140. return err
  141. }
  142. s.Chunks = append(s.Chunks, chunk)
  143. i += chunk.len()
  144. }
  145. if len(s.Chunks) != int(h.Count) {
  146. return errInvalidHeader
  147. }
  148. return nil
  149. }
  150. func (s *SourceDescription) len() int {
  151. chunksLength := 0
  152. for _, c := range s.Chunks {
  153. chunksLength += c.len()
  154. }
  155. return headerLength + chunksLength
  156. }
  157. // Header returns the Header associated with this packet.
  158. func (s *SourceDescription) Header() Header {
  159. return Header{
  160. Count: uint8(len(s.Chunks)),
  161. Type: TypeSourceDescription,
  162. Length: uint16((s.len() / 4) - 1),
  163. }
  164. }
  165. // A SourceDescriptionChunk contains items describing a single RTP source
  166. type SourceDescriptionChunk struct {
  167. // The source (ssrc) or contributing source (csrc) identifier this packet describes
  168. Source uint32
  169. Items []SourceDescriptionItem
  170. }
  171. // Marshal encodes the SourceDescriptionChunk in binary
  172. func (s SourceDescriptionChunk) Marshal() ([]byte, error) {
  173. /*
  174. * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
  175. * | SSRC/CSRC_1 |
  176. * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  177. * | SDES items |
  178. * | ... |
  179. * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
  180. */
  181. rawPacket := make([]byte, sdesSourceLen)
  182. binary.BigEndian.PutUint32(rawPacket, s.Source)
  183. for _, it := range s.Items {
  184. data, err := it.Marshal()
  185. if err != nil {
  186. return nil, err
  187. }
  188. rawPacket = append(rawPacket, data...)
  189. }
  190. // The list of items in each chunk MUST be terminated by one or more null octets
  191. rawPacket = append(rawPacket, uint8(SDESEnd))
  192. // additional null octets MUST be included if needed to pad until the next 32-bit boundary
  193. rawPacket = append(rawPacket, make([]byte, getPadding(len(rawPacket)))...)
  194. return rawPacket, nil
  195. }
  196. // Unmarshal decodes the SourceDescriptionChunk from binary
  197. func (s *SourceDescriptionChunk) Unmarshal(rawPacket []byte) error {
  198. /*
  199. * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
  200. * | SSRC/CSRC_1 |
  201. * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  202. * | SDES items |
  203. * | ... |
  204. * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
  205. */
  206. if len(rawPacket) < (sdesSourceLen + sdesTypeLen) {
  207. return errPacketTooShort
  208. }
  209. s.Source = binary.BigEndian.Uint32(rawPacket)
  210. for i := 4; i < len(rawPacket); {
  211. if pktType := SDESType(rawPacket[i]); pktType == SDESEnd {
  212. return nil
  213. }
  214. var it SourceDescriptionItem
  215. if err := it.Unmarshal(rawPacket[i:]); err != nil {
  216. return err
  217. }
  218. s.Items = append(s.Items, it)
  219. i += it.len()
  220. }
  221. return errPacketTooShort
  222. }
  223. func (s SourceDescriptionChunk) len() int {
  224. chunkLen := sdesSourceLen
  225. for _, it := range s.Items {
  226. chunkLen += it.len()
  227. }
  228. chunkLen += sdesTypeLen // for terminating null octet
  229. // align to 32-bit boundary
  230. chunkLen += getPadding(chunkLen)
  231. return chunkLen
  232. }
  233. // A SourceDescriptionItem is a part of a SourceDescription that describes a stream.
  234. type SourceDescriptionItem struct {
  235. // The type identifier for this item. eg, SDESCNAME for canonical name description.
  236. //
  237. // Type zero or SDESEnd is interpreted as the end of an item list and cannot be used.
  238. Type SDESType
  239. // Text is a unicode text blob associated with the item. Its meaning varies based on the item's Type.
  240. Text string
  241. }
  242. func (s SourceDescriptionItem) len() int {
  243. /*
  244. * 0 1 2 3
  245. * 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
  246. * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  247. * | CNAME=1 | length | user and domain name ...
  248. * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  249. */
  250. return sdesTypeLen + sdesOctetCountLen + len([]byte(s.Text))
  251. }
  252. // Marshal encodes the SourceDescriptionItem in binary
  253. func (s SourceDescriptionItem) Marshal() ([]byte, error) {
  254. /*
  255. * 0 1 2 3
  256. * 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
  257. * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  258. * | CNAME=1 | length | user and domain name ...
  259. * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  260. */
  261. if s.Type == SDESEnd {
  262. return nil, errSDESMissingType
  263. }
  264. rawPacket := make([]byte, sdesTypeLen+sdesOctetCountLen)
  265. rawPacket[sdesTypeOffset] = uint8(s.Type)
  266. txtBytes := []byte(s.Text)
  267. octetCount := len(txtBytes)
  268. if octetCount > sdesMaxOctetCount {
  269. return nil, errSDESTextTooLong
  270. }
  271. rawPacket[sdesOctetCountOffset] = uint8(octetCount)
  272. rawPacket = append(rawPacket, txtBytes...)
  273. return rawPacket, nil
  274. }
  275. // Unmarshal decodes the SourceDescriptionItem from binary
  276. func (s *SourceDescriptionItem) Unmarshal(rawPacket []byte) error {
  277. /*
  278. * 0 1 2 3
  279. * 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
  280. * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  281. * | CNAME=1 | length | user and domain name ...
  282. * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  283. */
  284. if len(rawPacket) < (sdesTypeLen + sdesOctetCountLen) {
  285. return errPacketTooShort
  286. }
  287. s.Type = SDESType(rawPacket[sdesTypeOffset])
  288. octetCount := int(rawPacket[sdesOctetCountOffset])
  289. if sdesTextOffset+octetCount > len(rawPacket) {
  290. return errPacketTooShort
  291. }
  292. txtBytes := rawPacket[sdesTextOffset : sdesTextOffset+octetCount]
  293. s.Text = string(txtBytes)
  294. return nil
  295. }
  296. // DestinationSSRC returns an array of SSRC values that this packet refers to.
  297. func (s *SourceDescription) DestinationSSRC() []uint32 {
  298. out := make([]uint32, len(s.Chunks))
  299. for i, v := range s.Chunks {
  300. out[i] = v.Source
  301. }
  302. return out
  303. }
  304. func (s *SourceDescription) String() string {
  305. out := "Source Description:\n"
  306. for _, c := range s.Chunks {
  307. out += fmt.Sprintf("\t%x: %s\n", c.Source, c.Items)
  308. }
  309. return out
  310. }