mediaengine.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651
  1. //go:build !js
  2. // +build !js
  3. package webrtc
  4. import (
  5. "fmt"
  6. "strconv"
  7. "strings"
  8. "sync"
  9. "time"
  10. "github.com/pion/rtp"
  11. "github.com/pion/rtp/codecs"
  12. "github.com/pion/sdp/v3"
  13. "github.com/pion/webrtc/v3/internal/fmtp"
  14. )
  15. const (
  16. // MimeTypeH264 H264 MIME type.
  17. // Note: Matching should be case insensitive.
  18. MimeTypeH264 = "video/H264"
  19. // MimeTypeH265 H265 MIME type
  20. // Note: Matching should be case insensitive.
  21. MimeTypeH265 = "video/H265"
  22. // MimeTypeOpus Opus MIME type
  23. // Note: Matching should be case insensitive.
  24. MimeTypeOpus = "audio/opus"
  25. // MimeTypeVP8 VP8 MIME type
  26. // Note: Matching should be case insensitive.
  27. MimeTypeVP8 = "video/VP8"
  28. // MimeTypeVP9 VP9 MIME type
  29. // Note: Matching should be case insensitive.
  30. MimeTypeVP9 = "video/VP9"
  31. // MimeTypeAV1 AV1 MIME type
  32. // Note: Matching should be case insensitive.
  33. MimeTypeAV1 = "video/AV1"
  34. // MimeTypeG722 G722 MIME type
  35. // Note: Matching should be case insensitive.
  36. MimeTypeG722 = "audio/G722"
  37. // MimeTypePCMU PCMU MIME type
  38. // Note: Matching should be case insensitive.
  39. MimeTypePCMU = "audio/PCMU"
  40. // MimeTypePCMA PCMA MIME type
  41. // Note: Matching should be case insensitive.
  42. MimeTypePCMA = "audio/PCMA"
  43. )
  44. type mediaEngineHeaderExtension struct {
  45. uri string
  46. isAudio, isVideo bool
  47. // If set only Transceivers of this direction are allowed
  48. allowedDirections []RTPTransceiverDirection
  49. }
  50. // A MediaEngine defines the codecs supported by a PeerConnection, and the
  51. // configuration of those codecs. A MediaEngine must not be shared between
  52. // PeerConnections.
  53. type MediaEngine struct {
  54. // If we have attempted to negotiate a codec type yet.
  55. negotiatedVideo, negotiatedAudio bool
  56. videoCodecs, audioCodecs []RTPCodecParameters
  57. negotiatedVideoCodecs, negotiatedAudioCodecs []RTPCodecParameters
  58. headerExtensions []mediaEngineHeaderExtension
  59. negotiatedHeaderExtensions map[int]mediaEngineHeaderExtension
  60. mu sync.RWMutex
  61. }
  62. // RegisterDefaultCodecs registers the default codecs supported by Pion WebRTC.
  63. // RegisterDefaultCodecs is not safe for concurrent use.
  64. func (m *MediaEngine) RegisterDefaultCodecs() error {
  65. // Default Pion Audio Codecs
  66. for _, codec := range []RTPCodecParameters{
  67. {
  68. RTPCodecCapability: RTPCodecCapability{MimeTypeOpus, 48000, 2, "minptime=10;useinbandfec=1", nil},
  69. PayloadType: 111,
  70. },
  71. {
  72. RTPCodecCapability: RTPCodecCapability{MimeTypeG722, 8000, 0, "", nil},
  73. PayloadType: 9,
  74. },
  75. {
  76. RTPCodecCapability: RTPCodecCapability{MimeTypePCMU, 8000, 0, "", nil},
  77. PayloadType: 0,
  78. },
  79. {
  80. RTPCodecCapability: RTPCodecCapability{MimeTypePCMA, 8000, 0, "", nil},
  81. PayloadType: 8,
  82. },
  83. } {
  84. if err := m.RegisterCodec(codec, RTPCodecTypeAudio); err != nil {
  85. return err
  86. }
  87. }
  88. videoRTCPFeedback := []RTCPFeedback{{"goog-remb", ""}, {"ccm", "fir"}, {"nack", ""}, {"nack", "pli"}}
  89. for _, codec := range []RTPCodecParameters{
  90. {
  91. RTPCodecCapability: RTPCodecCapability{MimeTypeVP8, 90000, 0, "", videoRTCPFeedback},
  92. PayloadType: 96,
  93. },
  94. {
  95. RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=96", nil},
  96. PayloadType: 97,
  97. },
  98. {
  99. RTPCodecCapability: RTPCodecCapability{MimeTypeVP9, 90000, 0, "profile-id=0", videoRTCPFeedback},
  100. PayloadType: 98,
  101. },
  102. {
  103. RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=98", nil},
  104. PayloadType: 99,
  105. },
  106. {
  107. RTPCodecCapability: RTPCodecCapability{MimeTypeVP9, 90000, 0, "profile-id=1", videoRTCPFeedback},
  108. PayloadType: 100,
  109. },
  110. {
  111. RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=100", nil},
  112. PayloadType: 101,
  113. },
  114. {
  115. RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f", videoRTCPFeedback},
  116. PayloadType: 102,
  117. },
  118. {
  119. RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=102", nil},
  120. PayloadType: 121,
  121. },
  122. {
  123. RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f", videoRTCPFeedback},
  124. PayloadType: 127,
  125. },
  126. {
  127. RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=127", nil},
  128. PayloadType: 120,
  129. },
  130. {
  131. RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f", videoRTCPFeedback},
  132. PayloadType: 125,
  133. },
  134. {
  135. RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=125", nil},
  136. PayloadType: 107,
  137. },
  138. {
  139. RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f", videoRTCPFeedback},
  140. PayloadType: 108,
  141. },
  142. {
  143. RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=108", nil},
  144. PayloadType: 109,
  145. },
  146. {
  147. RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f", videoRTCPFeedback},
  148. PayloadType: 127,
  149. },
  150. {
  151. RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=127", nil},
  152. PayloadType: 120,
  153. },
  154. {
  155. RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=640032", videoRTCPFeedback},
  156. PayloadType: 123,
  157. },
  158. {
  159. RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=123", nil},
  160. PayloadType: 118,
  161. },
  162. {
  163. RTPCodecCapability: RTPCodecCapability{"video/ulpfec", 90000, 0, "", nil},
  164. PayloadType: 116,
  165. },
  166. } {
  167. if err := m.RegisterCodec(codec, RTPCodecTypeVideo); err != nil {
  168. return err
  169. }
  170. }
  171. return nil
  172. }
  173. // addCodec will append codec if it not exists
  174. func (m *MediaEngine) addCodec(codecs []RTPCodecParameters, codec RTPCodecParameters) []RTPCodecParameters {
  175. for _, c := range codecs {
  176. if c.MimeType == codec.MimeType && c.PayloadType == codec.PayloadType {
  177. return codecs
  178. }
  179. }
  180. return append(codecs, codec)
  181. }
  182. // RegisterCodec adds codec to the MediaEngine
  183. // These are the list of codecs supported by this PeerConnection.
  184. // RegisterCodec is not safe for concurrent use.
  185. func (m *MediaEngine) RegisterCodec(codec RTPCodecParameters, typ RTPCodecType) error {
  186. m.mu.Lock()
  187. defer m.mu.Unlock()
  188. codec.statsID = fmt.Sprintf("RTPCodec-%d", time.Now().UnixNano())
  189. switch typ {
  190. case RTPCodecTypeAudio:
  191. m.audioCodecs = m.addCodec(m.audioCodecs, codec)
  192. case RTPCodecTypeVideo:
  193. m.videoCodecs = m.addCodec(m.videoCodecs, codec)
  194. default:
  195. return ErrUnknownType
  196. }
  197. return nil
  198. }
  199. // RegisterHeaderExtension adds a header extension to the MediaEngine
  200. // To determine the negotiated value use `GetHeaderExtensionID` after signaling is complete
  201. func (m *MediaEngine) RegisterHeaderExtension(extension RTPHeaderExtensionCapability, typ RTPCodecType, allowedDirections ...RTPTransceiverDirection) error {
  202. m.mu.Lock()
  203. defer m.mu.Unlock()
  204. if m.negotiatedHeaderExtensions == nil {
  205. m.negotiatedHeaderExtensions = map[int]mediaEngineHeaderExtension{}
  206. }
  207. if len(allowedDirections) == 0 {
  208. allowedDirections = []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly, RTPTransceiverDirectionSendonly}
  209. }
  210. for _, direction := range allowedDirections {
  211. if direction != RTPTransceiverDirectionRecvonly && direction != RTPTransceiverDirectionSendonly {
  212. return ErrRegisterHeaderExtensionInvalidDirection
  213. }
  214. }
  215. extensionIndex := -1
  216. for i := range m.headerExtensions {
  217. if extension.URI == m.headerExtensions[i].uri {
  218. extensionIndex = i
  219. }
  220. }
  221. if extensionIndex == -1 {
  222. m.headerExtensions = append(m.headerExtensions, mediaEngineHeaderExtension{})
  223. extensionIndex = len(m.headerExtensions) - 1
  224. }
  225. if typ == RTPCodecTypeAudio {
  226. m.headerExtensions[extensionIndex].isAudio = true
  227. } else if typ == RTPCodecTypeVideo {
  228. m.headerExtensions[extensionIndex].isVideo = true
  229. }
  230. m.headerExtensions[extensionIndex].uri = extension.URI
  231. m.headerExtensions[extensionIndex].allowedDirections = allowedDirections
  232. return nil
  233. }
  234. // RegisterFeedback adds feedback mechanism to already registered codecs.
  235. func (m *MediaEngine) RegisterFeedback(feedback RTCPFeedback, typ RTPCodecType) {
  236. m.mu.Lock()
  237. defer m.mu.Unlock()
  238. switch typ {
  239. case RTPCodecTypeVideo:
  240. for i, v := range m.videoCodecs {
  241. v.RTCPFeedback = append(v.RTCPFeedback, feedback)
  242. m.videoCodecs[i] = v
  243. }
  244. case RTPCodecTypeAudio:
  245. for i, v := range m.audioCodecs {
  246. v.RTCPFeedback = append(v.RTCPFeedback, feedback)
  247. m.audioCodecs[i] = v
  248. }
  249. }
  250. }
  251. // getHeaderExtensionID returns the negotiated ID for a header extension.
  252. // If the Header Extension isn't enabled ok will be false
  253. func (m *MediaEngine) getHeaderExtensionID(extension RTPHeaderExtensionCapability) (val int, audioNegotiated, videoNegotiated bool) {
  254. m.mu.RLock()
  255. defer m.mu.RUnlock()
  256. if m.negotiatedHeaderExtensions == nil {
  257. return 0, false, false
  258. }
  259. for id, h := range m.negotiatedHeaderExtensions {
  260. if extension.URI == h.uri {
  261. return id, h.isAudio, h.isVideo
  262. }
  263. }
  264. return
  265. }
  266. // copy copies any user modifiable state of the MediaEngine
  267. // all internal state is reset
  268. func (m *MediaEngine) copy() *MediaEngine {
  269. m.mu.Lock()
  270. defer m.mu.Unlock()
  271. cloned := &MediaEngine{
  272. videoCodecs: append([]RTPCodecParameters{}, m.videoCodecs...),
  273. audioCodecs: append([]RTPCodecParameters{}, m.audioCodecs...),
  274. headerExtensions: append([]mediaEngineHeaderExtension{}, m.headerExtensions...),
  275. }
  276. if len(m.headerExtensions) > 0 {
  277. cloned.negotiatedHeaderExtensions = map[int]mediaEngineHeaderExtension{}
  278. }
  279. return cloned
  280. }
  281. func findCodecByPayload(codecs []RTPCodecParameters, payloadType PayloadType) *RTPCodecParameters {
  282. for _, codec := range codecs {
  283. if codec.PayloadType == payloadType {
  284. return &codec
  285. }
  286. }
  287. return nil
  288. }
  289. func (m *MediaEngine) getCodecByPayload(payloadType PayloadType) (RTPCodecParameters, RTPCodecType, error) {
  290. m.mu.RLock()
  291. defer m.mu.RUnlock()
  292. // if we've negotiated audio or video, check the negotiated types before our
  293. // built-in payload types, to ensure we pick the codec the other side wants.
  294. if m.negotiatedVideo {
  295. if codec := findCodecByPayload(m.negotiatedVideoCodecs, payloadType); codec != nil {
  296. return *codec, RTPCodecTypeVideo, nil
  297. }
  298. }
  299. if m.negotiatedAudio {
  300. if codec := findCodecByPayload(m.negotiatedAudioCodecs, payloadType); codec != nil {
  301. return *codec, RTPCodecTypeAudio, nil
  302. }
  303. }
  304. if !m.negotiatedVideo {
  305. if codec := findCodecByPayload(m.videoCodecs, payloadType); codec != nil {
  306. return *codec, RTPCodecTypeVideo, nil
  307. }
  308. }
  309. if !m.negotiatedAudio {
  310. if codec := findCodecByPayload(m.audioCodecs, payloadType); codec != nil {
  311. return *codec, RTPCodecTypeAudio, nil
  312. }
  313. }
  314. return RTPCodecParameters{}, 0, ErrCodecNotFound
  315. }
  316. func (m *MediaEngine) collectStats(collector *statsReportCollector) {
  317. m.mu.RLock()
  318. defer m.mu.RUnlock()
  319. statsLoop := func(codecs []RTPCodecParameters) {
  320. for _, codec := range codecs {
  321. collector.Collecting()
  322. stats := CodecStats{
  323. Timestamp: statsTimestampFrom(time.Now()),
  324. Type: StatsTypeCodec,
  325. ID: codec.statsID,
  326. PayloadType: codec.PayloadType,
  327. MimeType: codec.MimeType,
  328. ClockRate: codec.ClockRate,
  329. Channels: uint8(codec.Channels),
  330. SDPFmtpLine: codec.SDPFmtpLine,
  331. }
  332. collector.Collect(stats.ID, stats)
  333. }
  334. }
  335. statsLoop(m.videoCodecs)
  336. statsLoop(m.audioCodecs)
  337. }
  338. // Look up a codec and enable if it exists
  339. func (m *MediaEngine) matchRemoteCodec(remoteCodec RTPCodecParameters, typ RTPCodecType, exactMatches, partialMatches []RTPCodecParameters) (codecMatchType, error) {
  340. codecs := m.videoCodecs
  341. if typ == RTPCodecTypeAudio {
  342. codecs = m.audioCodecs
  343. }
  344. remoteFmtp := fmtp.Parse(remoteCodec.RTPCodecCapability.MimeType, remoteCodec.RTPCodecCapability.SDPFmtpLine)
  345. if apt, hasApt := remoteFmtp.Parameter("apt"); hasApt {
  346. payloadType, err := strconv.ParseUint(apt, 10, 8)
  347. if err != nil {
  348. return codecMatchNone, err
  349. }
  350. aptMatch := codecMatchNone
  351. for _, codec := range exactMatches {
  352. if codec.PayloadType == PayloadType(payloadType) {
  353. aptMatch = codecMatchExact
  354. break
  355. }
  356. }
  357. if aptMatch == codecMatchNone {
  358. for _, codec := range partialMatches {
  359. if codec.PayloadType == PayloadType(payloadType) {
  360. aptMatch = codecMatchPartial
  361. break
  362. }
  363. }
  364. }
  365. if aptMatch == codecMatchNone {
  366. return codecMatchNone, nil // not an error, we just ignore this codec we don't support
  367. }
  368. // if apt's media codec is partial match, then apt codec must be partial match too
  369. _, matchType := codecParametersFuzzySearch(remoteCodec, codecs)
  370. if matchType == codecMatchExact && aptMatch == codecMatchPartial {
  371. matchType = codecMatchPartial
  372. }
  373. return matchType, nil
  374. }
  375. _, matchType := codecParametersFuzzySearch(remoteCodec, codecs)
  376. return matchType, nil
  377. }
  378. // Look up a header extension and enable if it exists
  379. func (m *MediaEngine) updateHeaderExtension(id int, extension string, typ RTPCodecType) error {
  380. if m.negotiatedHeaderExtensions == nil {
  381. return nil
  382. }
  383. for _, localExtension := range m.headerExtensions {
  384. if localExtension.uri == extension {
  385. h := mediaEngineHeaderExtension{uri: extension, allowedDirections: localExtension.allowedDirections}
  386. if existingValue, ok := m.negotiatedHeaderExtensions[id]; ok {
  387. h = existingValue
  388. }
  389. switch {
  390. case localExtension.isAudio && typ == RTPCodecTypeAudio:
  391. h.isAudio = true
  392. case localExtension.isVideo && typ == RTPCodecTypeVideo:
  393. h.isVideo = true
  394. }
  395. m.negotiatedHeaderExtensions[id] = h
  396. }
  397. }
  398. return nil
  399. }
  400. func (m *MediaEngine) pushCodecs(codecs []RTPCodecParameters, typ RTPCodecType) {
  401. for _, codec := range codecs {
  402. if typ == RTPCodecTypeAudio {
  403. m.negotiatedAudioCodecs = m.addCodec(m.negotiatedAudioCodecs, codec)
  404. } else if typ == RTPCodecTypeVideo {
  405. m.negotiatedVideoCodecs = m.addCodec(m.negotiatedVideoCodecs, codec)
  406. }
  407. }
  408. }
  409. // Update the MediaEngine from a remote description
  410. func (m *MediaEngine) updateFromRemoteDescription(desc sdp.SessionDescription) error {
  411. m.mu.Lock()
  412. defer m.mu.Unlock()
  413. for _, media := range desc.MediaDescriptions {
  414. var typ RTPCodecType
  415. switch {
  416. case !m.negotiatedAudio && strings.EqualFold(media.MediaName.Media, "audio"):
  417. m.negotiatedAudio = true
  418. typ = RTPCodecTypeAudio
  419. case !m.negotiatedVideo && strings.EqualFold(media.MediaName.Media, "video"):
  420. m.negotiatedVideo = true
  421. typ = RTPCodecTypeVideo
  422. default:
  423. continue
  424. }
  425. codecs, err := codecsFromMediaDescription(media)
  426. if err != nil {
  427. return err
  428. }
  429. exactMatches := make([]RTPCodecParameters, 0, len(codecs))
  430. partialMatches := make([]RTPCodecParameters, 0, len(codecs))
  431. for _, codec := range codecs {
  432. matchType, mErr := m.matchRemoteCodec(codec, typ, exactMatches, partialMatches)
  433. if mErr != nil {
  434. return mErr
  435. }
  436. if matchType == codecMatchExact {
  437. exactMatches = append(exactMatches, codec)
  438. } else if matchType == codecMatchPartial {
  439. partialMatches = append(partialMatches, codec)
  440. }
  441. }
  442. // use exact matches when they exist, otherwise fall back to partial
  443. switch {
  444. case len(exactMatches) > 0:
  445. m.pushCodecs(exactMatches, typ)
  446. case len(partialMatches) > 0:
  447. m.pushCodecs(partialMatches, typ)
  448. default:
  449. // no match, not negotiated
  450. continue
  451. }
  452. extensions, err := rtpExtensionsFromMediaDescription(media)
  453. if err != nil {
  454. return err
  455. }
  456. for extension, id := range extensions {
  457. if err = m.updateHeaderExtension(id, extension, typ); err != nil {
  458. return err
  459. }
  460. }
  461. }
  462. return nil
  463. }
  464. func (m *MediaEngine) getCodecsByKind(typ RTPCodecType) []RTPCodecParameters {
  465. m.mu.RLock()
  466. defer m.mu.RUnlock()
  467. if typ == RTPCodecTypeVideo {
  468. if m.negotiatedVideo {
  469. return m.negotiatedVideoCodecs
  470. }
  471. return m.videoCodecs
  472. } else if typ == RTPCodecTypeAudio {
  473. if m.negotiatedAudio {
  474. return m.negotiatedAudioCodecs
  475. }
  476. return m.audioCodecs
  477. }
  478. return nil
  479. }
  480. func (m *MediaEngine) getRTPParametersByKind(typ RTPCodecType, directions []RTPTransceiverDirection) RTPParameters { //nolint:gocognit
  481. headerExtensions := make([]RTPHeaderExtensionParameter, 0)
  482. // perform before locking to prevent recursive RLocks
  483. foundCodecs := m.getCodecsByKind(typ)
  484. m.mu.RLock()
  485. defer m.mu.RUnlock()
  486. if m.negotiatedVideo && typ == RTPCodecTypeVideo ||
  487. m.negotiatedAudio && typ == RTPCodecTypeAudio {
  488. for id, e := range m.negotiatedHeaderExtensions {
  489. if haveRTPTransceiverDirectionIntersection(e.allowedDirections, directions) && (e.isAudio && typ == RTPCodecTypeAudio || e.isVideo && typ == RTPCodecTypeVideo) {
  490. headerExtensions = append(headerExtensions, RTPHeaderExtensionParameter{ID: id, URI: e.uri})
  491. }
  492. }
  493. } else {
  494. mediaHeaderExtensions := make(map[int]mediaEngineHeaderExtension)
  495. for _, e := range m.headerExtensions {
  496. usingNegotiatedID := false
  497. for id := range m.negotiatedHeaderExtensions {
  498. if m.negotiatedHeaderExtensions[id].uri == e.uri {
  499. usingNegotiatedID = true
  500. mediaHeaderExtensions[id] = e
  501. break
  502. }
  503. }
  504. if !usingNegotiatedID {
  505. for id := 1; id < 15; id++ {
  506. idAvailable := true
  507. if _, ok := mediaHeaderExtensions[id]; ok {
  508. idAvailable = false
  509. }
  510. if _, taken := m.negotiatedHeaderExtensions[id]; idAvailable && !taken {
  511. mediaHeaderExtensions[id] = e
  512. break
  513. }
  514. }
  515. }
  516. }
  517. for id, e := range mediaHeaderExtensions {
  518. if haveRTPTransceiverDirectionIntersection(e.allowedDirections, directions) && (e.isAudio && typ == RTPCodecTypeAudio || e.isVideo && typ == RTPCodecTypeVideo) {
  519. headerExtensions = append(headerExtensions, RTPHeaderExtensionParameter{ID: id, URI: e.uri})
  520. }
  521. }
  522. }
  523. return RTPParameters{
  524. HeaderExtensions: headerExtensions,
  525. Codecs: foundCodecs,
  526. }
  527. }
  528. func (m *MediaEngine) getRTPParametersByPayloadType(payloadType PayloadType) (RTPParameters, error) {
  529. codec, typ, err := m.getCodecByPayload(payloadType)
  530. if err != nil {
  531. return RTPParameters{}, err
  532. }
  533. m.mu.RLock()
  534. defer m.mu.RUnlock()
  535. headerExtensions := make([]RTPHeaderExtensionParameter, 0)
  536. for id, e := range m.negotiatedHeaderExtensions {
  537. if e.isAudio && typ == RTPCodecTypeAudio || e.isVideo && typ == RTPCodecTypeVideo {
  538. headerExtensions = append(headerExtensions, RTPHeaderExtensionParameter{ID: id, URI: e.uri})
  539. }
  540. }
  541. return RTPParameters{
  542. HeaderExtensions: headerExtensions,
  543. Codecs: []RTPCodecParameters{codec},
  544. }, nil
  545. }
  546. func payloaderForCodec(codec RTPCodecCapability) (rtp.Payloader, error) {
  547. switch strings.ToLower(codec.MimeType) {
  548. case strings.ToLower(MimeTypeH264):
  549. return &codecs.H264Payloader{}, nil
  550. case strings.ToLower(MimeTypeOpus):
  551. return &codecs.OpusPayloader{}, nil
  552. case strings.ToLower(MimeTypeVP8):
  553. return &codecs.VP8Payloader{
  554. EnablePictureID: true,
  555. }, nil
  556. case strings.ToLower(MimeTypeVP9):
  557. return &codecs.VP9Payloader{}, nil
  558. case strings.ToLower(MimeTypeAV1):
  559. return &codecs.AV1Payloader{}, nil
  560. case strings.ToLower(MimeTypeG722):
  561. return &codecs.G722Payloader{}, nil
  562. case strings.ToLower(MimeTypePCMU), strings.ToLower(MimeTypePCMA):
  563. return &codecs.G711Payloader{}, nil
  564. default:
  565. return nil, ErrNoPayloaderForCodec
  566. }
  567. }