source_description.go 11 KB

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