1#[cfg(test)]
2mod media_engine_test;
3
4use std::collections::HashMap;
5use std::ops::Range;
6use std::sync::atomic::Ordering;
7use std::time::{SystemTime, UNIX_EPOCH};
8
9use portable_atomic::AtomicBool;
10use sdp::description::session::SessionDescription;
11use util::sync::Mutex as SyncMutex;
12
13use crate::error::{Error, Result};
14use crate::peer_connection::sdp::{
15 codecs_from_media_description, rtp_extensions_from_media_description,
16};
17use crate::rtp_transceiver::rtp_codec::{
18 codec_parameters_fuzzy_search, CodecMatch, RTCRtpCodecCapability, RTCRtpCodecParameters,
19 RTCRtpHeaderExtensionCapability, RTCRtpHeaderExtensionParameters, RTCRtpParameters,
20 RTPCodecType,
21};
22use crate::rtp_transceiver::rtp_transceiver_direction::RTCRtpTransceiverDirection;
23use crate::rtp_transceiver::{fmtp, PayloadType, RTCPFeedback};
24use crate::stats::stats_collector::StatsCollector;
25use crate::stats::CodecStats;
26use crate::stats::StatsReportType::Codec;
27
28pub const MIME_TYPE_H264: &str = "video/H264";
31pub const MIME_TYPE_HEVC: &str = "video/H265";
34pub const MIME_TYPE_OPUS: &str = "audio/opus";
37pub const MIME_TYPE_VP8: &str = "video/VP8";
40pub const MIME_TYPE_VP9: &str = "video/VP9";
43pub const MIME_TYPE_AV1: &str = "video/AV1";
46pub const MIME_TYPE_G722: &str = "audio/G722";
49pub const MIME_TYPE_PCMU: &str = "audio/PCMU";
52pub const MIME_TYPE_PCMA: &str = "audio/PCMA";
55pub const MIME_TYPE_TELEPHONE_EVENT: &str = "audio/telephone-event";
58
59const VALID_EXT_IDS: Range<isize> = 1..15;
60
61#[derive(Default, Clone)]
62pub(crate) struct MediaEngineHeaderExtension {
63 pub(crate) uri: String,
64 pub(crate) is_audio: bool,
65 pub(crate) is_video: bool,
66 pub(crate) allowed_direction: Option<RTCRtpTransceiverDirection>,
67}
68
69impl MediaEngineHeaderExtension {
70 pub fn is_matching_direction(&self, dir: RTCRtpTransceiverDirection) -> bool {
71 if let Some(allowed_direction) = self.allowed_direction {
72 use RTCRtpTransceiverDirection::*;
73 allowed_direction == Inactive && dir == Inactive
74 || allowed_direction.has_send() && dir.has_send()
75 || allowed_direction.has_recv() && dir.has_recv()
76 } else {
77 true
79 }
80 }
81}
82
83#[derive(Default)]
87pub struct MediaEngine {
88 pub(crate) negotiated_video: AtomicBool,
90 pub(crate) negotiated_audio: AtomicBool,
91
92 pub(crate) video_codecs: Vec<RTCRtpCodecParameters>,
93 pub(crate) audio_codecs: Vec<RTCRtpCodecParameters>,
94 pub(crate) negotiated_video_codecs: SyncMutex<Vec<RTCRtpCodecParameters>>,
95 pub(crate) negotiated_audio_codecs: SyncMutex<Vec<RTCRtpCodecParameters>>,
96
97 header_extensions: Vec<MediaEngineHeaderExtension>,
98 proposed_header_extensions: SyncMutex<HashMap<isize, MediaEngineHeaderExtension>>,
99 pub(crate) negotiated_header_extensions: SyncMutex<HashMap<isize, MediaEngineHeaderExtension>>,
100}
101
102impl MediaEngine {
103 pub fn register_default_codecs(&mut self) -> Result<()> {
106 for codec in vec![
108 RTCRtpCodecParameters {
109 capability: RTCRtpCodecCapability {
110 mime_type: MIME_TYPE_OPUS.to_owned(),
111 clock_rate: 48000,
112 channels: 2,
113 sdp_fmtp_line: "minptime=10;useinbandfec=1".to_owned(),
114 rtcp_feedback: vec![],
115 },
116 payload_type: 111,
117 ..Default::default()
118 },
119 RTCRtpCodecParameters {
120 capability: RTCRtpCodecCapability {
121 mime_type: MIME_TYPE_G722.to_owned(),
122 clock_rate: 8000,
123 channels: 0,
124 sdp_fmtp_line: "".to_owned(),
125 rtcp_feedback: vec![],
126 },
127 payload_type: 9,
128 ..Default::default()
129 },
130 RTCRtpCodecParameters {
131 capability: RTCRtpCodecCapability {
132 mime_type: MIME_TYPE_PCMU.to_owned(),
133 clock_rate: 8000,
134 channels: 0,
135 sdp_fmtp_line: "".to_owned(),
136 rtcp_feedback: vec![],
137 },
138 payload_type: 0,
139 ..Default::default()
140 },
141 RTCRtpCodecParameters {
142 capability: RTCRtpCodecCapability {
143 mime_type: MIME_TYPE_PCMA.to_owned(),
144 clock_rate: 8000,
145 channels: 0,
146 sdp_fmtp_line: "".to_owned(),
147 rtcp_feedback: vec![],
148 },
149 payload_type: 8,
150 ..Default::default()
151 },
152 ] {
153 self.register_codec(codec, RTPCodecType::Audio)?;
154 }
155
156 let video_rtcp_feedback = vec![
157 RTCPFeedback {
158 typ: "goog-remb".to_owned(),
159 parameter: "".to_owned(),
160 },
161 RTCPFeedback {
162 typ: "ccm".to_owned(),
163 parameter: "fir".to_owned(),
164 },
165 RTCPFeedback {
166 typ: "nack".to_owned(),
167 parameter: "".to_owned(),
168 },
169 RTCPFeedback {
170 typ: "nack".to_owned(),
171 parameter: "pli".to_owned(),
172 },
173 ];
174 for codec in vec![
175 RTCRtpCodecParameters {
176 capability: RTCRtpCodecCapability {
177 mime_type: MIME_TYPE_VP8.to_owned(),
178 clock_rate: 90000,
179 channels: 0,
180 sdp_fmtp_line: "".to_owned(),
181 rtcp_feedback: video_rtcp_feedback.clone(),
182 },
183 payload_type: 96,
184 ..Default::default()
185 },
186 RTCRtpCodecParameters {
187 capability: RTCRtpCodecCapability {
188 mime_type: MIME_TYPE_VP9.to_owned(),
189 clock_rate: 90000,
190 channels: 0,
191 sdp_fmtp_line: "profile-id=0".to_owned(),
192 rtcp_feedback: video_rtcp_feedback.clone(),
193 },
194 payload_type: 98,
195 ..Default::default()
196 },
197 RTCRtpCodecParameters {
198 capability: RTCRtpCodecCapability {
199 mime_type: MIME_TYPE_VP9.to_owned(),
200 clock_rate: 90000,
201 channels: 0,
202 sdp_fmtp_line: "profile-id=1".to_owned(),
203 rtcp_feedback: video_rtcp_feedback.clone(),
204 },
205 payload_type: 100,
206 ..Default::default()
207 },
208 RTCRtpCodecParameters {
209 capability: RTCRtpCodecCapability {
210 mime_type: MIME_TYPE_H264.to_owned(),
211 clock_rate: 90000,
212 channels: 0,
213 sdp_fmtp_line:
214 "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f"
215 .to_owned(),
216 rtcp_feedback: video_rtcp_feedback.clone(),
217 },
218 payload_type: 102,
219 ..Default::default()
220 },
221 RTCRtpCodecParameters {
222 capability: RTCRtpCodecCapability {
223 mime_type: MIME_TYPE_H264.to_owned(),
224 clock_rate: 90000,
225 channels: 0,
226 sdp_fmtp_line:
227 "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f"
228 .to_owned(),
229 rtcp_feedback: video_rtcp_feedback.clone(),
230 },
231 payload_type: 127,
232 ..Default::default()
233 },
234 RTCRtpCodecParameters {
235 capability: RTCRtpCodecCapability {
236 mime_type: MIME_TYPE_H264.to_owned(),
237 clock_rate: 90000,
238 channels: 0,
239 sdp_fmtp_line:
240 "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f"
241 .to_owned(),
242 rtcp_feedback: video_rtcp_feedback.clone(),
243 },
244 payload_type: 125,
245 ..Default::default()
246 },
247 RTCRtpCodecParameters {
248 capability: RTCRtpCodecCapability {
249 mime_type: MIME_TYPE_H264.to_owned(),
250 clock_rate: 90000,
251 channels: 0,
252 sdp_fmtp_line:
253 "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f"
254 .to_owned(),
255 rtcp_feedback: video_rtcp_feedback.clone(),
256 },
257 payload_type: 108,
258 ..Default::default()
259 },
260 RTCRtpCodecParameters {
261 capability: RTCRtpCodecCapability {
262 mime_type: MIME_TYPE_H264.to_owned(),
263 clock_rate: 90000,
264 channels: 0,
265 sdp_fmtp_line:
266 "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f"
267 .to_owned(),
268 rtcp_feedback: video_rtcp_feedback.clone(),
269 },
270 payload_type: 127,
271 ..Default::default()
272 },
273 RTCRtpCodecParameters {
274 capability: RTCRtpCodecCapability {
275 mime_type: MIME_TYPE_H264.to_owned(),
276 clock_rate: 90000,
277 channels: 0,
278 sdp_fmtp_line:
279 "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=640032"
280 .to_owned(),
281 rtcp_feedback: video_rtcp_feedback.clone(),
282 },
283 payload_type: 123,
284 ..Default::default()
285 },
286 RTCRtpCodecParameters {
287 capability: RTCRtpCodecCapability {
288 mime_type: MIME_TYPE_AV1.to_owned(),
289 clock_rate: 90000,
290 channels: 0,
291 sdp_fmtp_line: "profile-id=0".to_owned(),
292 rtcp_feedback: video_rtcp_feedback.clone(),
293 },
294 payload_type: 41,
295 ..Default::default()
296 },
297 RTCRtpCodecParameters {
298 capability: RTCRtpCodecCapability {
299 mime_type: MIME_TYPE_HEVC.to_owned(),
300 clock_rate: 90000,
301 channels: 0,
302 sdp_fmtp_line: "".to_owned(),
303 rtcp_feedback: video_rtcp_feedback,
304 },
305 payload_type: 126,
306 ..Default::default()
307 },
308 RTCRtpCodecParameters {
309 capability: RTCRtpCodecCapability {
310 mime_type: "video/ulpfec".to_owned(),
311 clock_rate: 90000,
312 channels: 0,
313 sdp_fmtp_line: "".to_owned(),
314 rtcp_feedback: vec![],
315 },
316 payload_type: 116,
317 ..Default::default()
318 },
319 ] {
320 self.register_codec(codec, RTPCodecType::Video)?;
321 }
322
323 Ok(())
324 }
325
326 fn add_codec(codecs: &mut Vec<RTCRtpCodecParameters>, codec: RTCRtpCodecParameters) {
328 for c in codecs.iter() {
329 if c.capability.mime_type == codec.capability.mime_type
330 && c.payload_type == codec.payload_type
331 {
332 return;
333 }
334 }
335 codecs.push(codec);
336 }
337
338 pub fn register_codec(
342 &mut self,
343 mut codec: RTCRtpCodecParameters,
344 typ: RTPCodecType,
345 ) -> Result<()> {
346 codec.stats_id = format!(
347 "RTPCodec-{}",
348 SystemTime::now()
349 .duration_since(UNIX_EPOCH)
350 .unwrap()
351 .as_nanos()
352 );
353 match typ {
354 RTPCodecType::Audio => {
355 MediaEngine::add_codec(&mut self.audio_codecs, codec);
356 Ok(())
357 }
358 RTPCodecType::Video => {
359 MediaEngine::add_codec(&mut self.video_codecs, codec);
360 Ok(())
361 }
362 _ => Err(Error::ErrUnknownType),
363 }
364 }
365
366 pub fn register_header_extension(
373 &mut self,
374 extension: RTCRtpHeaderExtensionCapability,
375 typ: RTPCodecType,
376 allowed_direction: Option<RTCRtpTransceiverDirection>,
377 ) -> Result<()> {
378 let ext = {
379 match self
380 .header_extensions
381 .iter_mut()
382 .find(|ext| ext.uri == extension.uri)
383 {
384 Some(ext) => ext,
385 None => {
386 if self.header_extensions.len() > VALID_EXT_IDS.end as usize {
388 return Err(Error::ErrRegisterHeaderExtensionNoFreeID);
389 }
390 self.header_extensions.push(MediaEngineHeaderExtension {
391 allowed_direction,
392 ..Default::default()
393 });
394
395 self.header_extensions.last_mut().unwrap()
397 }
398 }
399 };
400
401 if typ == RTPCodecType::Audio {
402 ext.is_audio = true;
403 } else if typ == RTPCodecType::Video {
404 ext.is_video = true;
405 }
406
407 ext.uri = extension.uri;
408
409 if ext.allowed_direction != allowed_direction {
410 return Err(Error::ErrRegisterHeaderExtensionInvalidDirection);
411 }
412
413 Ok(())
414 }
415
416 pub fn register_feedback(&mut self, feedback: RTCPFeedback, typ: RTPCodecType) {
418 match typ {
419 RTPCodecType::Video => {
420 for v in &mut self.video_codecs {
421 v.capability.rtcp_feedback.push(feedback.clone());
422 }
423 }
424 RTPCodecType::Audio => {
425 for a in &mut self.audio_codecs {
426 a.capability.rtcp_feedback.push(feedback.clone());
427 }
428 }
429 _ => {}
430 }
431 }
432
433 pub async fn get_header_extension_id(
436 &self,
437 extension: RTCRtpHeaderExtensionCapability,
438 ) -> (isize, bool, bool) {
439 let negotiated_header_extensions = self.negotiated_header_extensions.lock();
440 if negotiated_header_extensions.is_empty() {
441 return (0, false, false);
442 }
443
444 for (id, h) in &*negotiated_header_extensions {
445 if extension.uri == h.uri {
446 return (*id, h.is_audio, h.is_video);
447 }
448 }
449
450 (0, false, false)
451 }
452
453 pub(crate) fn clone_to(&self) -> Self {
456 MediaEngine {
457 video_codecs: self.video_codecs.clone(),
458 audio_codecs: self.audio_codecs.clone(),
459 header_extensions: self.header_extensions.clone(),
460 ..Default::default()
461 }
462 }
463
464 pub(crate) async fn get_codec_by_payload(
465 &self,
466 payload_type: PayloadType,
467 ) -> Result<(RTCRtpCodecParameters, RTPCodecType)> {
468 if self.negotiated_video.load(Ordering::SeqCst) {
469 let negotiated_video_codecs = self.negotiated_video_codecs.lock();
470 for codec in &*negotiated_video_codecs {
471 if codec.payload_type == payload_type {
472 return Ok((codec.clone(), RTPCodecType::Video));
473 }
474 }
475 }
476 if self.negotiated_audio.load(Ordering::SeqCst) {
477 let negotiated_audio_codecs = self.negotiated_audio_codecs.lock();
478 for codec in &*negotiated_audio_codecs {
479 if codec.payload_type == payload_type {
480 return Ok((codec.clone(), RTPCodecType::Audio));
481 }
482 }
483 }
484 if !self.negotiated_video.load(Ordering::SeqCst) {
485 for codec in &self.video_codecs {
486 if codec.payload_type == payload_type {
487 return Ok((codec.clone(), RTPCodecType::Video));
488 }
489 }
490 }
491 if !self.negotiated_audio.load(Ordering::SeqCst) {
492 for codec in &self.audio_codecs {
493 if codec.payload_type == payload_type {
494 return Ok((codec.clone(), RTPCodecType::Audio));
495 }
496 }
497 }
498
499 Err(Error::ErrCodecNotFound)
500 }
501
502 pub(crate) async fn collect_stats(&self, collector: &StatsCollector) {
503 let mut reports = HashMap::new();
504
505 for codec in &self.video_codecs {
506 reports.insert(codec.stats_id.clone(), Codec(CodecStats::from(codec)));
507 }
508
509 for codec in &self.audio_codecs {
510 reports.insert(codec.stats_id.clone(), Codec(CodecStats::from(codec)));
511 }
512
513 collector.merge(reports);
514 }
515
516 pub(crate) fn match_remote_codec(
518 &self,
519 remote_codec: &RTCRtpCodecParameters,
520 typ: RTPCodecType,
521 exact_matches: &[RTCRtpCodecParameters],
522 partial_matches: &[RTCRtpCodecParameters],
523 ) -> Result<CodecMatch> {
524 let codecs = if typ == RTPCodecType::Audio {
525 &self.audio_codecs
526 } else {
527 &self.video_codecs
528 };
529
530 let remote_fmtp = fmtp::parse(
531 &remote_codec.capability.mime_type,
532 remote_codec.capability.sdp_fmtp_line.as_str(),
533 );
534 if let Some(apt) = remote_fmtp.parameter("apt") {
535 let payload_type = apt.parse::<u8>()?;
536
537 let mut apt_match = CodecMatch::None;
538 let mut apt_codec = None;
539 for codec in exact_matches {
540 if codec.payload_type == payload_type {
541 apt_match = CodecMatch::Exact;
542 apt_codec = Some(codec);
543 break;
544 }
545 }
546
547 if apt_match == CodecMatch::None {
548 for codec in partial_matches {
549 if codec.payload_type == payload_type {
550 apt_match = CodecMatch::Partial;
551 apt_codec = Some(codec);
552 break;
553 }
554 }
555 }
556
557 if apt_match == CodecMatch::None {
558 return Ok(CodecMatch::None); }
560
561 let mut to_match_codec = remote_codec.clone();
563 if let Some(apt_codec) = apt_codec {
564 let (apt_matched, mt) = codec_parameters_fuzzy_search(apt_codec, codecs);
565 if mt == apt_match {
566 to_match_codec.capability.sdp_fmtp_line =
567 to_match_codec.capability.sdp_fmtp_line.replacen(
568 &format!("apt={payload_type}"),
569 &format!("apt={}", apt_matched.payload_type),
570 1,
571 );
572 }
573 }
574
575 let (_, mut match_type) = codec_parameters_fuzzy_search(&to_match_codec, codecs);
577 if match_type == CodecMatch::Exact && apt_match == CodecMatch::Partial {
578 match_type = CodecMatch::Partial;
579 }
580 return Ok(match_type);
581 }
582
583 let (_, match_type) = codec_parameters_fuzzy_search(remote_codec, codecs);
584 Ok(match_type)
585 }
586
587 pub(crate) async fn update_header_extension(
589 &self,
590 id: isize,
591 extension: &str,
592 typ: RTPCodecType,
593 ) -> Result<()> {
594 let mut negotiated_header_extensions = self.negotiated_header_extensions.lock();
595 let mut proposed_header_extensions = self.proposed_header_extensions.lock();
596
597 for local_extension in &self.header_extensions {
598 if local_extension.uri != extension {
599 continue;
600 }
601
602 let negotiated_ext = negotiated_header_extensions
603 .iter_mut()
604 .find(|(_, ext)| ext.uri == extension);
605
606 if let Some(n_ext) = negotiated_ext {
607 if *n_ext.0 == id {
608 n_ext.1.is_video |= typ == RTPCodecType::Video;
609 n_ext.1.is_audio |= typ == RTPCodecType::Audio;
610 } else {
611 let nid = n_ext.0;
612 log::warn!("Invalid ext id mapping in update_header_extension. {} was negotiated as {}, but was {} in call", extension, nid, id);
613 }
614 } else {
615 if let Some(prev_ext) = negotiated_header_extensions.get(&id) {
619 let prev_uri = &prev_ext.uri;
620 log::warn!("Assigning {} to {} would override previous assignment to {}, no action taken", id, extension, prev_uri);
621 } else {
622 let h = MediaEngineHeaderExtension {
623 uri: extension.to_owned(),
624 is_audio: local_extension.is_audio && typ == RTPCodecType::Audio,
625 is_video: local_extension.is_video && typ == RTPCodecType::Video,
626 allowed_direction: local_extension.allowed_direction,
627 };
628 negotiated_header_extensions.insert(id, h);
629 }
630 }
631
632 proposed_header_extensions.remove(&id);
634 }
635 Ok(())
636 }
637
638 pub(crate) async fn push_codecs(&self, codecs: Vec<RTCRtpCodecParameters>, typ: RTPCodecType) {
639 for codec in codecs {
640 if typ == RTPCodecType::Audio {
641 let mut negotiated_audio_codecs = self.negotiated_audio_codecs.lock();
642 MediaEngine::add_codec(&mut negotiated_audio_codecs, codec);
643 } else if typ == RTPCodecType::Video {
644 let mut negotiated_video_codecs = self.negotiated_video_codecs.lock();
645 MediaEngine::add_codec(&mut negotiated_video_codecs, codec);
646 }
647 }
648 }
649
650 pub(crate) async fn update_from_remote_description(
652 &self,
653 desc: &SessionDescription,
654 ) -> Result<()> {
655 for media in &desc.media_descriptions {
656 let typ = if !self.negotiated_audio.load(Ordering::SeqCst)
657 && media.media_name.media.to_lowercase() == "audio"
658 {
659 self.negotiated_audio.store(true, Ordering::SeqCst);
660 RTPCodecType::Audio
661 } else if !self.negotiated_video.load(Ordering::SeqCst)
662 && media.media_name.media.to_lowercase() == "video"
663 {
664 self.negotiated_video.store(true, Ordering::SeqCst);
665 RTPCodecType::Video
666 } else {
667 continue;
668 };
669
670 let codecs = codecs_from_media_description(media)?;
671
672 let mut exact_matches = vec![]; let mut partial_matches = vec![]; for codec in codecs {
676 let match_type =
677 self.match_remote_codec(&codec, typ, &exact_matches, &partial_matches)?;
678
679 if match_type == CodecMatch::Exact {
680 exact_matches.push(codec);
681 } else if match_type == CodecMatch::Partial {
682 partial_matches.push(codec);
683 }
684 }
685
686 if !exact_matches.is_empty() {
688 self.push_codecs(exact_matches, typ).await;
689 } else if !partial_matches.is_empty() {
690 self.push_codecs(partial_matches, typ).await;
691 } else {
692 continue;
694 }
695
696 let extensions = rtp_extensions_from_media_description(media)?;
697
698 for (extension, id) in extensions {
699 self.update_header_extension(id, &extension, typ).await?;
700 }
701 }
702
703 Ok(())
704 }
705
706 pub(crate) fn get_codecs_by_kind(&self, typ: RTPCodecType) -> Vec<RTCRtpCodecParameters> {
707 if typ == RTPCodecType::Video {
708 if self.negotiated_video.load(Ordering::SeqCst) {
709 let negotiated_video_codecs = self.negotiated_video_codecs.lock();
710 negotiated_video_codecs.clone()
711 } else {
712 self.video_codecs.clone()
713 }
714 } else if typ == RTPCodecType::Audio {
715 if self.negotiated_audio.load(Ordering::SeqCst) {
716 let negotiated_audio_codecs = self.negotiated_audio_codecs.lock();
717 negotiated_audio_codecs.clone()
718 } else {
719 self.audio_codecs.clone()
720 }
721 } else {
722 vec![]
723 }
724 }
725
726 pub(crate) fn get_rtp_parameters_by_kind(
727 &self,
728 typ: RTPCodecType,
729 direction: RTCRtpTransceiverDirection,
730 ) -> RTCRtpParameters {
731 let mut header_extensions = vec![];
732
733 if self.negotiated_video.load(Ordering::SeqCst) && typ == RTPCodecType::Video
734 || self.negotiated_audio.load(Ordering::SeqCst) && typ == RTPCodecType::Audio
735 {
736 let negotiated_header_extensions = self.negotiated_header_extensions.lock();
737 for (id, e) in &*negotiated_header_extensions {
738 if e.is_matching_direction(direction)
739 && (e.is_audio && typ == RTPCodecType::Audio
740 || e.is_video && typ == RTPCodecType::Video)
741 {
742 header_extensions.push(RTCRtpHeaderExtensionParameters {
743 id: *id,
744 uri: e.uri.clone(),
745 });
746 }
747 }
748 } else {
749 let mut proposed_header_extensions = self.proposed_header_extensions.lock();
750 let mut negotiated_header_extensions = self.negotiated_header_extensions.lock();
751
752 for local_extension in &self.header_extensions {
753 let relevant = local_extension.is_matching_direction(direction)
754 && (local_extension.is_audio && typ == RTPCodecType::Audio
755 || local_extension.is_video && typ == RTPCodecType::Video);
756
757 if !relevant {
758 continue;
759 }
760
761 if let Some((id, negotiated_extension)) = negotiated_header_extensions
762 .iter_mut()
763 .find(|(_, e)| e.uri == local_extension.uri)
764 {
765 negotiated_extension.is_audio |= typ == RTPCodecType::Audio;
768 negotiated_extension.is_video |= typ == RTPCodecType::Video;
769
770 header_extensions.push(RTCRtpHeaderExtensionParameters {
771 id: *id,
772 uri: negotiated_extension.uri.clone(),
773 });
774
775 continue;
776 }
777
778 if let Some((id, negotiated_extension)) = proposed_header_extensions
779 .iter_mut()
780 .find(|(_, e)| e.uri == local_extension.uri)
781 {
782 header_extensions.push(RTCRtpHeaderExtensionParameters {
784 id: *id,
785 uri: negotiated_extension.uri.clone(),
786 });
787
788 continue;
789 }
790
791 let id = VALID_EXT_IDS.clone().find(|id| {
793 !negotiated_header_extensions.keys().any(|nid| nid == id)
794 && !proposed_header_extensions.keys().any(|pid| pid == id)
795 });
796
797 if let Some(id) = id {
798 proposed_header_extensions.insert(
799 id,
800 MediaEngineHeaderExtension {
801 uri: local_extension.uri.clone(),
802 is_audio: local_extension.is_audio,
803 is_video: local_extension.is_video,
804 allowed_direction: local_extension.allowed_direction,
805 },
806 );
807
808 header_extensions.push(RTCRtpHeaderExtensionParameters {
809 id,
810 uri: local_extension.uri.clone(),
811 });
812 } else {
813 log::warn!("No available RTP extension ID for {}", local_extension.uri);
814 }
815 }
816 }
817
818 RTCRtpParameters {
819 header_extensions,
820 codecs: self.get_codecs_by_kind(typ),
821 }
822 }
823
824 pub(crate) async fn get_rtp_parameters_by_payload_type(
825 &self,
826 payload_type: PayloadType,
827 ) -> Result<RTCRtpParameters> {
828 let (codec, typ) = self.get_codec_by_payload(payload_type).await?;
829
830 let mut header_extensions = vec![];
831 {
832 let negotiated_header_extensions = self.negotiated_header_extensions.lock();
833 for (id, e) in &*negotiated_header_extensions {
834 if e.is_audio && typ == RTPCodecType::Audio
835 || e.is_video && typ == RTPCodecType::Video
836 {
837 header_extensions.push(RTCRtpHeaderExtensionParameters {
838 uri: e.uri.clone(),
839 id: *id,
840 });
841 }
842 }
843 }
844
845 Ok(RTCRtpParameters {
846 header_extensions,
847 codecs: vec![codec],
848 })
849 }
850}