mediaengine.go 19 KB

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