util.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. package sdp
  2. import (
  3. "errors"
  4. "fmt"
  5. "io"
  6. "sort"
  7. "strconv"
  8. "strings"
  9. "github.com/pion/randutil"
  10. )
  11. const (
  12. attributeKey = "a="
  13. )
  14. var (
  15. errExtractCodecRtpmap = errors.New("could not extract codec from rtpmap")
  16. errExtractCodecFmtp = errors.New("could not extract codec from fmtp")
  17. errExtractCodecRtcpFb = errors.New("could not extract codec from rtcp-fb")
  18. errPayloadTypeNotFound = errors.New("payload type not found")
  19. errCodecNotFound = errors.New("codec not found")
  20. errSyntaxError = errors.New("SyntaxError")
  21. )
  22. // ConnectionRole indicates which of the end points should initiate the connection establishment
  23. type ConnectionRole int
  24. const (
  25. // ConnectionRoleActive indicates the endpoint will initiate an outgoing connection.
  26. ConnectionRoleActive ConnectionRole = iota + 1
  27. // ConnectionRolePassive indicates the endpoint will accept an incoming connection.
  28. ConnectionRolePassive
  29. // ConnectionRoleActpass indicates the endpoint is willing to accept an incoming connection or to initiate an outgoing connection.
  30. ConnectionRoleActpass
  31. // ConnectionRoleHoldconn indicates the endpoint does not want the connection to be established for the time being.
  32. ConnectionRoleHoldconn
  33. )
  34. func (t ConnectionRole) String() string {
  35. switch t {
  36. case ConnectionRoleActive:
  37. return "active"
  38. case ConnectionRolePassive:
  39. return "passive"
  40. case ConnectionRoleActpass:
  41. return "actpass"
  42. case ConnectionRoleHoldconn:
  43. return "holdconn"
  44. default:
  45. return "Unknown"
  46. }
  47. }
  48. func newSessionID() (uint64, error) {
  49. // https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-26#section-5.2.1
  50. // Session ID is recommended to be constructed by generating a 64-bit
  51. // quantity with the highest bit set to zero and the remaining 63-bits
  52. // being cryptographically random.
  53. id, err := randutil.CryptoUint64()
  54. return id & (^(uint64(1) << 63)), err
  55. }
  56. // Codec represents a codec
  57. type Codec struct {
  58. PayloadType uint8
  59. Name string
  60. ClockRate uint32
  61. EncodingParameters string
  62. Fmtp string
  63. RTCPFeedback []string
  64. }
  65. const (
  66. unknown = iota
  67. )
  68. func (c Codec) String() string {
  69. return fmt.Sprintf("%d %s/%d/%s (%s) [%s]", c.PayloadType, c.Name, c.ClockRate, c.EncodingParameters, c.Fmtp, strings.Join(c.RTCPFeedback, ", "))
  70. }
  71. func parseRtpmap(rtpmap string) (Codec, error) {
  72. var codec Codec
  73. parsingFailed := errExtractCodecRtpmap
  74. // a=rtpmap:<payload type> <encoding name>/<clock rate>[/<encoding parameters>]
  75. split := strings.Split(rtpmap, " ")
  76. if len(split) != 2 {
  77. return codec, parsingFailed
  78. }
  79. ptSplit := strings.Split(split[0], ":")
  80. if len(ptSplit) != 2 {
  81. return codec, parsingFailed
  82. }
  83. ptInt, err := strconv.ParseUint(ptSplit[1], 10, 8)
  84. if err != nil {
  85. return codec, parsingFailed
  86. }
  87. codec.PayloadType = uint8(ptInt)
  88. split = strings.Split(split[1], "/")
  89. codec.Name = split[0]
  90. parts := len(split)
  91. if parts > 1 {
  92. rate, err := strconv.ParseUint(split[1], 10, 32)
  93. if err != nil {
  94. return codec, parsingFailed
  95. }
  96. codec.ClockRate = uint32(rate)
  97. }
  98. if parts > 2 {
  99. codec.EncodingParameters = split[2]
  100. }
  101. return codec, nil
  102. }
  103. func parseFmtp(fmtp string) (Codec, error) {
  104. var codec Codec
  105. parsingFailed := errExtractCodecFmtp
  106. // a=fmtp:<format> <format specific parameters>
  107. split := strings.Split(fmtp, " ")
  108. if len(split) != 2 {
  109. return codec, parsingFailed
  110. }
  111. formatParams := split[1]
  112. split = strings.Split(split[0], ":")
  113. if len(split) != 2 {
  114. return codec, parsingFailed
  115. }
  116. ptInt, err := strconv.ParseUint(split[1], 10, 8)
  117. if err != nil {
  118. return codec, parsingFailed
  119. }
  120. codec.PayloadType = uint8(ptInt)
  121. codec.Fmtp = formatParams
  122. return codec, nil
  123. }
  124. func parseRtcpFb(rtcpFb string) (Codec, error) {
  125. var codec Codec
  126. parsingFailed := errExtractCodecRtcpFb
  127. // a=ftcp-fb:<payload type> <RTCP feedback type> [<RTCP feedback parameter>]
  128. split := strings.SplitN(rtcpFb, " ", 2)
  129. if len(split) != 2 {
  130. return codec, parsingFailed
  131. }
  132. ptSplit := strings.Split(split[0], ":")
  133. if len(ptSplit) != 2 {
  134. return codec, parsingFailed
  135. }
  136. ptInt, err := strconv.ParseUint(ptSplit[1], 10, 8)
  137. if err != nil {
  138. return codec, parsingFailed
  139. }
  140. codec.PayloadType = uint8(ptInt)
  141. codec.RTCPFeedback = append(codec.RTCPFeedback, split[1])
  142. return codec, nil
  143. }
  144. func mergeCodecs(codec Codec, codecs map[uint8]Codec) {
  145. savedCodec := codecs[codec.PayloadType]
  146. if savedCodec.PayloadType == 0 {
  147. savedCodec.PayloadType = codec.PayloadType
  148. }
  149. if savedCodec.Name == "" {
  150. savedCodec.Name = codec.Name
  151. }
  152. if savedCodec.ClockRate == 0 {
  153. savedCodec.ClockRate = codec.ClockRate
  154. }
  155. if savedCodec.EncodingParameters == "" {
  156. savedCodec.EncodingParameters = codec.EncodingParameters
  157. }
  158. if savedCodec.Fmtp == "" {
  159. savedCodec.Fmtp = codec.Fmtp
  160. }
  161. savedCodec.RTCPFeedback = append(savedCodec.RTCPFeedback, codec.RTCPFeedback...)
  162. codecs[savedCodec.PayloadType] = savedCodec
  163. }
  164. func (s *SessionDescription) buildCodecMap() map[uint8]Codec {
  165. codecs := make(map[uint8]Codec)
  166. for _, m := range s.MediaDescriptions {
  167. for _, a := range m.Attributes {
  168. attr := a.String()
  169. switch {
  170. case strings.HasPrefix(attr, "rtpmap:"):
  171. codec, err := parseRtpmap(attr)
  172. if err == nil {
  173. mergeCodecs(codec, codecs)
  174. }
  175. case strings.HasPrefix(attr, "fmtp:"):
  176. codec, err := parseFmtp(attr)
  177. if err == nil {
  178. mergeCodecs(codec, codecs)
  179. }
  180. case strings.HasPrefix(attr, "rtcp-fb:"):
  181. codec, err := parseRtcpFb(attr)
  182. if err == nil {
  183. mergeCodecs(codec, codecs)
  184. }
  185. }
  186. }
  187. }
  188. return codecs
  189. }
  190. func equivalentFmtp(want, got string) bool {
  191. wantSplit := strings.Split(want, ";")
  192. gotSplit := strings.Split(got, ";")
  193. if len(wantSplit) != len(gotSplit) {
  194. return false
  195. }
  196. sort.Strings(wantSplit)
  197. sort.Strings(gotSplit)
  198. for i, wantPart := range wantSplit {
  199. wantPart = strings.TrimSpace(wantPart)
  200. gotPart := strings.TrimSpace(gotSplit[i])
  201. if gotPart != wantPart {
  202. return false
  203. }
  204. }
  205. return true
  206. }
  207. func codecsMatch(wanted, got Codec) bool {
  208. if wanted.Name != "" && !strings.EqualFold(wanted.Name, got.Name) {
  209. return false
  210. }
  211. if wanted.ClockRate != 0 && wanted.ClockRate != got.ClockRate {
  212. return false
  213. }
  214. if wanted.EncodingParameters != "" && wanted.EncodingParameters != got.EncodingParameters {
  215. return false
  216. }
  217. if wanted.Fmtp != "" && !equivalentFmtp(wanted.Fmtp, got.Fmtp) {
  218. return false
  219. }
  220. return true
  221. }
  222. // GetCodecForPayloadType scans the SessionDescription for the given payload type and returns the codec
  223. func (s *SessionDescription) GetCodecForPayloadType(payloadType uint8) (Codec, error) {
  224. codecs := s.buildCodecMap()
  225. codec, ok := codecs[payloadType]
  226. if ok {
  227. return codec, nil
  228. }
  229. return codec, errPayloadTypeNotFound
  230. }
  231. // GetPayloadTypeForCodec scans the SessionDescription for a codec that matches the provided codec
  232. // as closely as possible and returns its payload type
  233. func (s *SessionDescription) GetPayloadTypeForCodec(wanted Codec) (uint8, error) {
  234. codecs := s.buildCodecMap()
  235. for payloadType, codec := range codecs {
  236. if codecsMatch(wanted, codec) {
  237. return payloadType, nil
  238. }
  239. }
  240. return 0, errCodecNotFound
  241. }
  242. type stateFn func(*lexer) (stateFn, error)
  243. type lexer struct {
  244. desc *SessionDescription
  245. baseLexer
  246. }
  247. type keyToState func(key string) stateFn
  248. func (l *lexer) handleType(fn keyToState) (stateFn, error) {
  249. key, err := l.readType()
  250. if errors.Is(err, io.EOF) && key == "" {
  251. return nil, nil //nolint:nilnil
  252. } else if err != nil {
  253. return nil, err
  254. }
  255. if res := fn(key); res != nil {
  256. return res, nil
  257. }
  258. return nil, l.syntaxError()
  259. }