xref: /webrtc/webrtc/src/api/media_engine/mod.rs (revision 65fc4df2)
1 #[cfg(test)]
2 mod media_engine_test;
3 
4 use crate::error::{Error, Result};
5 use crate::peer_connection::sdp::{
6     codecs_from_media_description, rtp_extensions_from_media_description,
7 };
8 use crate::rtp_transceiver::fmtp;
9 use crate::rtp_transceiver::rtp_codec::{
10     codec_parameters_fuzzy_search, CodecMatch, RTCRtpCodecCapability, RTCRtpCodecParameters,
11     RTCRtpHeaderExtensionCapability, RTCRtpHeaderExtensionParameters, RTCRtpParameters,
12     RTPCodecType,
13 };
14 use crate::rtp_transceiver::rtp_transceiver_direction::RTCRtpTransceiverDirection;
15 use crate::rtp_transceiver::{PayloadType, RTCPFeedback};
16 use crate::stats::stats_collector::StatsCollector;
17 use crate::stats::CodecStats;
18 use crate::stats::StatsReportType::Codec;
19 
20 use sdp::description::session::SessionDescription;
21 use std::collections::HashMap;
22 use std::ops::Range;
23 use std::sync::atomic::{AtomicBool, Ordering};
24 use std::time::{SystemTime, UNIX_EPOCH};
25 use util::sync::Mutex as SyncMutex;
26 
27 /// MIME_TYPE_H264 H264 MIME type.
28 /// Note: Matching should be case insensitive.
29 pub const MIME_TYPE_H264: &str = "video/H264";
30 /// MIME_TYPE_OPUS Opus MIME type
31 /// Note: Matching should be case insensitive.
32 pub const MIME_TYPE_OPUS: &str = "audio/opus";
33 /// MIME_TYPE_VP8 VP8 MIME type
34 /// Note: Matching should be case insensitive.
35 pub const MIME_TYPE_VP8: &str = "video/VP8";
36 /// MIME_TYPE_VP9 VP9 MIME type
37 /// Note: Matching should be case insensitive.
38 pub const MIME_TYPE_VP9: &str = "video/VP9";
39 /// MIME_TYPE_AV1 AV1 MIME type
40 /// Note: Matching should be case insensitive.
41 pub const MIME_TYPE_AV1: &str = "video/AV1";
42 /// MIME_TYPE_G722 G722 MIME type
43 /// Note: Matching should be case insensitive.
44 pub const MIME_TYPE_G722: &str = "audio/G722";
45 /// MIME_TYPE_PCMU PCMU MIME type
46 /// Note: Matching should be case insensitive.
47 pub const MIME_TYPE_PCMU: &str = "audio/PCMU";
48 /// MIME_TYPE_PCMA PCMA MIME type
49 /// Note: Matching should be case insensitive.
50 pub const MIME_TYPE_PCMA: &str = "audio/PCMA";
51 /// MIME_TYPE_TELEPHONE_EVENT telephone-event MIME type
52 /// Note: Matching should be case insensitive.
53 pub const MIME_TYPE_TELEPHONE_EVENT: &str = "audio/telephone-event";
54 
55 const VALID_EXT_IDS: Range<isize> = 1..15;
56 
57 #[derive(Default, Clone)]
58 pub(crate) struct MediaEngineHeaderExtension {
59     pub(crate) uri: String,
60     pub(crate) is_audio: bool,
61     pub(crate) is_video: bool,
62     pub(crate) allowed_direction: Option<RTCRtpTransceiverDirection>,
63 }
64 
65 impl MediaEngineHeaderExtension {
is_matching_direction(&self, dir: RTCRtpTransceiverDirection) -> bool66     pub fn is_matching_direction(&self, dir: RTCRtpTransceiverDirection) -> bool {
67         if let Some(allowed_direction) = self.allowed_direction {
68             use RTCRtpTransceiverDirection::*;
69             allowed_direction == Inactive && dir == Inactive
70                 || allowed_direction.has_send() && dir.has_send()
71                 || allowed_direction.has_recv() && dir.has_recv()
72         } else {
73             // None means all directions matches.
74             true
75         }
76     }
77 }
78 
79 /// A MediaEngine defines the codecs supported by a PeerConnection, and the
80 /// configuration of those codecs. A MediaEngine must not be shared between
81 /// PeerConnections.
82 #[derive(Default)]
83 pub struct MediaEngine {
84     // If we have attempted to negotiate a codec type yet.
85     pub(crate) negotiated_video: AtomicBool,
86     pub(crate) negotiated_audio: AtomicBool,
87 
88     pub(crate) video_codecs: Vec<RTCRtpCodecParameters>,
89     pub(crate) audio_codecs: Vec<RTCRtpCodecParameters>,
90     pub(crate) negotiated_video_codecs: SyncMutex<Vec<RTCRtpCodecParameters>>,
91     pub(crate) negotiated_audio_codecs: SyncMutex<Vec<RTCRtpCodecParameters>>,
92 
93     header_extensions: Vec<MediaEngineHeaderExtension>,
94     proposed_header_extensions: SyncMutex<HashMap<isize, MediaEngineHeaderExtension>>,
95     pub(crate) negotiated_header_extensions: SyncMutex<HashMap<isize, MediaEngineHeaderExtension>>,
96 }
97 
98 impl MediaEngine {
99     /// register_default_codecs registers the default codecs supported by Pion WebRTC.
100     /// register_default_codecs is not safe for concurrent use.
register_default_codecs(&mut self) -> Result<()>101     pub fn register_default_codecs(&mut self) -> Result<()> {
102         // Default Audio Codecs
103         for codec in vec![
104             RTCRtpCodecParameters {
105                 capability: RTCRtpCodecCapability {
106                     mime_type: MIME_TYPE_OPUS.to_owned(),
107                     clock_rate: 48000,
108                     channels: 2,
109                     sdp_fmtp_line: "minptime=10;useinbandfec=1".to_owned(),
110                     rtcp_feedback: vec![],
111                 },
112                 payload_type: 111,
113                 ..Default::default()
114             },
115             RTCRtpCodecParameters {
116                 capability: RTCRtpCodecCapability {
117                     mime_type: MIME_TYPE_G722.to_owned(),
118                     clock_rate: 8000,
119                     channels: 0,
120                     sdp_fmtp_line: "".to_owned(),
121                     rtcp_feedback: vec![],
122                 },
123                 payload_type: 9,
124                 ..Default::default()
125             },
126             RTCRtpCodecParameters {
127                 capability: RTCRtpCodecCapability {
128                     mime_type: MIME_TYPE_PCMU.to_owned(),
129                     clock_rate: 8000,
130                     channels: 0,
131                     sdp_fmtp_line: "".to_owned(),
132                     rtcp_feedback: vec![],
133                 },
134                 payload_type: 0,
135                 ..Default::default()
136             },
137             RTCRtpCodecParameters {
138                 capability: RTCRtpCodecCapability {
139                     mime_type: MIME_TYPE_PCMA.to_owned(),
140                     clock_rate: 8000,
141                     channels: 0,
142                     sdp_fmtp_line: "".to_owned(),
143                     rtcp_feedback: vec![],
144                 },
145                 payload_type: 8,
146                 ..Default::default()
147             },
148         ] {
149             self.register_codec(codec, RTPCodecType::Audio)?;
150         }
151 
152         let video_rtcp_feedback = vec![
153             RTCPFeedback {
154                 typ: "goog-remb".to_owned(),
155                 parameter: "".to_owned(),
156             },
157             RTCPFeedback {
158                 typ: "ccm".to_owned(),
159                 parameter: "fir".to_owned(),
160             },
161             RTCPFeedback {
162                 typ: "nack".to_owned(),
163                 parameter: "".to_owned(),
164             },
165             RTCPFeedback {
166                 typ: "nack".to_owned(),
167                 parameter: "pli".to_owned(),
168             },
169         ];
170         for codec in vec![
171             RTCRtpCodecParameters {
172                 capability: RTCRtpCodecCapability {
173                     mime_type: MIME_TYPE_VP8.to_owned(),
174                     clock_rate: 90000,
175                     channels: 0,
176                     sdp_fmtp_line: "".to_owned(),
177                     rtcp_feedback: video_rtcp_feedback.clone(),
178                 },
179                 payload_type: 96,
180                 ..Default::default()
181             },
182             RTCRtpCodecParameters {
183                 capability: RTCRtpCodecCapability {
184                     mime_type: MIME_TYPE_VP9.to_owned(),
185                     clock_rate: 90000,
186                     channels: 0,
187                     sdp_fmtp_line: "profile-id=0".to_owned(),
188                     rtcp_feedback: video_rtcp_feedback.clone(),
189                 },
190                 payload_type: 98,
191                 ..Default::default()
192             },
193             RTCRtpCodecParameters {
194                 capability: RTCRtpCodecCapability {
195                     mime_type: MIME_TYPE_VP9.to_owned(),
196                     clock_rate: 90000,
197                     channels: 0,
198                     sdp_fmtp_line: "profile-id=1".to_owned(),
199                     rtcp_feedback: video_rtcp_feedback.clone(),
200                 },
201                 payload_type: 100,
202                 ..Default::default()
203             },
204             RTCRtpCodecParameters {
205                 capability: RTCRtpCodecCapability {
206                     mime_type: MIME_TYPE_H264.to_owned(),
207                     clock_rate: 90000,
208                     channels: 0,
209                     sdp_fmtp_line:
210                         "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f"
211                             .to_owned(),
212                     rtcp_feedback: video_rtcp_feedback.clone(),
213                 },
214                 payload_type: 102,
215                 ..Default::default()
216             },
217             RTCRtpCodecParameters {
218                 capability: RTCRtpCodecCapability {
219                     mime_type: MIME_TYPE_H264.to_owned(),
220                     clock_rate: 90000,
221                     channels: 0,
222                     sdp_fmtp_line:
223                         "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f"
224                             .to_owned(),
225                     rtcp_feedback: video_rtcp_feedback.clone(),
226                 },
227                 payload_type: 127,
228                 ..Default::default()
229             },
230             RTCRtpCodecParameters {
231                 capability: RTCRtpCodecCapability {
232                     mime_type: MIME_TYPE_H264.to_owned(),
233                     clock_rate: 90000,
234                     channels: 0,
235                     sdp_fmtp_line:
236                         "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f"
237                             .to_owned(),
238                     rtcp_feedback: video_rtcp_feedback.clone(),
239                 },
240                 payload_type: 125,
241                 ..Default::default()
242             },
243             RTCRtpCodecParameters {
244                 capability: RTCRtpCodecCapability {
245                     mime_type: MIME_TYPE_H264.to_owned(),
246                     clock_rate: 90000,
247                     channels: 0,
248                     sdp_fmtp_line:
249                         "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f"
250                             .to_owned(),
251                     rtcp_feedback: video_rtcp_feedback.clone(),
252                 },
253                 payload_type: 108,
254                 ..Default::default()
255             },
256             RTCRtpCodecParameters {
257                 capability: RTCRtpCodecCapability {
258                     mime_type: MIME_TYPE_H264.to_owned(),
259                     clock_rate: 90000,
260                     channels: 0,
261                     sdp_fmtp_line:
262                         "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f"
263                             .to_owned(),
264                     rtcp_feedback: video_rtcp_feedback.clone(),
265                 },
266                 payload_type: 127,
267                 ..Default::default()
268             },
269             RTCRtpCodecParameters {
270                 capability: RTCRtpCodecCapability {
271                     mime_type: MIME_TYPE_H264.to_owned(),
272                     clock_rate: 90000,
273                     channels: 0,
274                     sdp_fmtp_line:
275                         "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=640032"
276                             .to_owned(),
277                     rtcp_feedback: video_rtcp_feedback,
278                 },
279                 payload_type: 123,
280                 ..Default::default()
281             },
282             RTCRtpCodecParameters {
283                 capability: RTCRtpCodecCapability {
284                     mime_type: "video/ulpfec".to_owned(),
285                     clock_rate: 90000,
286                     channels: 0,
287                     sdp_fmtp_line: "".to_owned(),
288                     rtcp_feedback: vec![],
289                 },
290                 payload_type: 116,
291                 ..Default::default()
292             },
293         ] {
294             self.register_codec(codec, RTPCodecType::Video)?;
295         }
296 
297         Ok(())
298     }
299 
300     /// add_codec will append codec if it not exists
add_codec(codecs: &mut Vec<RTCRtpCodecParameters>, codec: RTCRtpCodecParameters)301     fn add_codec(codecs: &mut Vec<RTCRtpCodecParameters>, codec: RTCRtpCodecParameters) {
302         for c in codecs.iter() {
303             if c.capability.mime_type == codec.capability.mime_type
304                 && c.payload_type == codec.payload_type
305             {
306                 return;
307             }
308         }
309         codecs.push(codec);
310     }
311 
312     /// register_codec adds codec to the MediaEngine
313     /// These are the list of codecs supported by this PeerConnection.
314     /// register_codec is not safe for concurrent use.
register_codec( &mut self, mut codec: RTCRtpCodecParameters, typ: RTPCodecType, ) -> Result<()>315     pub fn register_codec(
316         &mut self,
317         mut codec: RTCRtpCodecParameters,
318         typ: RTPCodecType,
319     ) -> Result<()> {
320         codec.stats_id = format!(
321             "RTPCodec-{}",
322             SystemTime::now()
323                 .duration_since(UNIX_EPOCH)
324                 .unwrap()
325                 .as_nanos()
326         );
327         match typ {
328             RTPCodecType::Audio => {
329                 MediaEngine::add_codec(&mut self.audio_codecs, codec);
330                 Ok(())
331             }
332             RTPCodecType::Video => {
333                 MediaEngine::add_codec(&mut self.video_codecs, codec);
334                 Ok(())
335             }
336             _ => Err(Error::ErrUnknownType),
337         }
338     }
339 
340     /// Adds a header extension to the MediaEngine
341     /// To determine the negotiated value use [`get_header_extension_id`] after signaling is complete.
342     ///
343     /// The `allowed_direction` controls for which transceiver directions the extension matches. If
344     /// set to `None` it matches all directions. The `SendRecv` direction would match all transceiver
345     /// directions apart from `Inactive`. Inactive ony matches inactive.
register_header_extension( &mut self, extension: RTCRtpHeaderExtensionCapability, typ: RTPCodecType, allowed_direction: Option<RTCRtpTransceiverDirection>, ) -> Result<()>346     pub fn register_header_extension(
347         &mut self,
348         extension: RTCRtpHeaderExtensionCapability,
349         typ: RTPCodecType,
350         allowed_direction: Option<RTCRtpTransceiverDirection>,
351     ) -> Result<()> {
352         let ext = {
353             match self
354                 .header_extensions
355                 .iter_mut()
356                 .find(|ext| ext.uri == extension.uri)
357             {
358                 Some(ext) => ext,
359                 None => {
360                     // We have registered too many extensions
361                     if self.header_extensions.len() > VALID_EXT_IDS.end as usize {
362                         return Err(Error::ErrRegisterHeaderExtensionNoFreeID);
363                     }
364                     self.header_extensions.push(MediaEngineHeaderExtension {
365                         allowed_direction,
366                         ..Default::default()
367                     });
368 
369                     // Unwrap is fine because we just pushed
370                     self.header_extensions.last_mut().unwrap()
371                 }
372             }
373         };
374 
375         if typ == RTPCodecType::Audio {
376             ext.is_audio = true;
377         } else if typ == RTPCodecType::Video {
378             ext.is_video = true;
379         }
380 
381         ext.uri = extension.uri;
382 
383         if ext.allowed_direction != allowed_direction {
384             return Err(Error::ErrRegisterHeaderExtensionInvalidDirection);
385         }
386 
387         Ok(())
388     }
389 
390     /// register_feedback adds feedback mechanism to already registered codecs.
register_feedback(&mut self, feedback: RTCPFeedback, typ: RTPCodecType)391     pub fn register_feedback(&mut self, feedback: RTCPFeedback, typ: RTPCodecType) {
392         match typ {
393             RTPCodecType::Video => {
394                 for v in &mut self.video_codecs {
395                     v.capability.rtcp_feedback.push(feedback.clone());
396                 }
397             }
398             RTPCodecType::Audio => {
399                 for a in &mut self.audio_codecs {
400                     a.capability.rtcp_feedback.push(feedback.clone());
401                 }
402             }
403             _ => {}
404         }
405     }
406 
407     /// get_header_extension_id returns the negotiated ID for a header extension.
408     /// If the Header Extension isn't enabled ok will be false
get_header_extension_id( &self, extension: RTCRtpHeaderExtensionCapability, ) -> (isize, bool, bool)409     pub(crate) async fn get_header_extension_id(
410         &self,
411         extension: RTCRtpHeaderExtensionCapability,
412     ) -> (isize, bool, bool) {
413         let negotiated_header_extensions = self.negotiated_header_extensions.lock();
414         if negotiated_header_extensions.is_empty() {
415             return (0, false, false);
416         }
417 
418         for (id, h) in &*negotiated_header_extensions {
419             if extension.uri == h.uri {
420                 return (*id, h.is_audio, h.is_video);
421             }
422         }
423 
424         (0, false, false)
425     }
426 
427     /// clone_to copies any user modifiable state of the MediaEngine
428     /// all internal state is reset
clone_to(&self) -> Self429     pub(crate) fn clone_to(&self) -> Self {
430         MediaEngine {
431             video_codecs: self.video_codecs.clone(),
432             audio_codecs: self.audio_codecs.clone(),
433             header_extensions: self.header_extensions.clone(),
434             ..Default::default()
435         }
436     }
437 
get_codec_by_payload( &self, payload_type: PayloadType, ) -> Result<(RTCRtpCodecParameters, RTPCodecType)>438     pub(crate) async fn get_codec_by_payload(
439         &self,
440         payload_type: PayloadType,
441     ) -> Result<(RTCRtpCodecParameters, RTPCodecType)> {
442         {
443             let negotiated_video_codecs = self.negotiated_video_codecs.lock();
444             for codec in &*negotiated_video_codecs {
445                 if codec.payload_type == payload_type {
446                     return Ok((codec.clone(), RTPCodecType::Video));
447                 }
448             }
449         }
450         {
451             let negotiated_audio_codecs = self.negotiated_audio_codecs.lock();
452             for codec in &*negotiated_audio_codecs {
453                 if codec.payload_type == payload_type {
454                     return Ok((codec.clone(), RTPCodecType::Audio));
455                 }
456             }
457         }
458 
459         Err(Error::ErrCodecNotFound)
460     }
461 
collect_stats(&self, collector: &StatsCollector)462     pub(crate) async fn collect_stats(&self, collector: &StatsCollector) {
463         let mut reports = HashMap::new();
464 
465         for codec in &self.video_codecs {
466             reports.insert(codec.stats_id.clone(), Codec(CodecStats::from(codec)));
467         }
468 
469         for codec in &self.audio_codecs {
470             reports.insert(codec.stats_id.clone(), Codec(CodecStats::from(codec)));
471         }
472 
473         collector.merge(reports);
474     }
475 
476     /// Look up a codec and enable if it exists
match_remote_codec( &self, remote_codec: &RTCRtpCodecParameters, typ: RTPCodecType, exact_matches: &[RTCRtpCodecParameters], partial_matches: &[RTCRtpCodecParameters], ) -> Result<CodecMatch>477     pub(crate) fn match_remote_codec(
478         &self,
479         remote_codec: &RTCRtpCodecParameters,
480         typ: RTPCodecType,
481         exact_matches: &[RTCRtpCodecParameters],
482         partial_matches: &[RTCRtpCodecParameters],
483     ) -> Result<CodecMatch> {
484         let codecs = if typ == RTPCodecType::Audio {
485             &self.audio_codecs
486         } else {
487             &self.video_codecs
488         };
489 
490         let remote_fmtp = fmtp::parse(
491             &remote_codec.capability.mime_type,
492             remote_codec.capability.sdp_fmtp_line.as_str(),
493         );
494         if let Some(apt) = remote_fmtp.parameter("apt") {
495             let payload_type = apt.parse::<u8>()?;
496 
497             let mut apt_match = CodecMatch::None;
498             for codec in exact_matches {
499                 if codec.payload_type == payload_type {
500                     apt_match = CodecMatch::Exact;
501                     break;
502                 }
503             }
504 
505             if apt_match == CodecMatch::None {
506                 for codec in partial_matches {
507                     if codec.payload_type == payload_type {
508                         apt_match = CodecMatch::Partial;
509                         break;
510                     }
511                 }
512             }
513 
514             if apt_match == CodecMatch::None {
515                 return Ok(CodecMatch::None); // not an error, we just ignore this codec we don't support
516             }
517 
518             // if apt's media codec is partial match, then apt codec must be partial match too
519             let (_, mut match_type) = codec_parameters_fuzzy_search(remote_codec, codecs);
520             if match_type == CodecMatch::Exact && apt_match == CodecMatch::Partial {
521                 match_type = CodecMatch::Partial;
522             }
523             return Ok(match_type);
524         }
525 
526         let (_, match_type) = codec_parameters_fuzzy_search(remote_codec, codecs);
527         Ok(match_type)
528     }
529 
530     /// Look up a header extension and enable if it exists
update_header_extension( &self, id: isize, extension: &str, typ: RTPCodecType, ) -> Result<()>531     pub(crate) async fn update_header_extension(
532         &self,
533         id: isize,
534         extension: &str,
535         typ: RTPCodecType,
536     ) -> Result<()> {
537         let mut negotiated_header_extensions = self.negotiated_header_extensions.lock();
538         let mut propsed_header_extensions = self.proposed_header_extensions.lock();
539 
540         for local_extension in &self.header_extensions {
541             if local_extension.uri != extension {
542                 continue;
543             }
544 
545             let negotiated_ext = negotiated_header_extensions
546                 .iter_mut()
547                 .find(|(_, ext)| ext.uri == extension);
548 
549             if let Some(n_ext) = negotiated_ext {
550                 if *n_ext.0 == id {
551                     n_ext.1.is_video |= typ == RTPCodecType::Video;
552                     n_ext.1.is_audio |= typ == RTPCodecType::Audio;
553                 } else {
554                     let nid = n_ext.0;
555                     log::warn!("Invalid ext id mapping in update_header_extension. {} was negotiated as {}, but was {} in call", extension, nid, id);
556                 }
557             } else {
558                 // We either only have a proposal or we have neither proposal nor a negotiated id
559                 // Accept whatevers the peer suggests
560 
561                 if let Some(prev_ext) = negotiated_header_extensions.get(&id) {
562                     let prev_uri = &prev_ext.uri;
563                     log::warn!("Assigning {} to {} would override previous assignment to {}, no action taken", id, extension, prev_uri);
564                 } else {
565                     let h = MediaEngineHeaderExtension {
566                         uri: extension.to_owned(),
567                         is_audio: local_extension.is_audio && typ == RTPCodecType::Audio,
568                         is_video: local_extension.is_video && typ == RTPCodecType::Video,
569                         allowed_direction: local_extension.allowed_direction,
570                     };
571                     negotiated_header_extensions.insert(id, h);
572                 }
573             }
574 
575             // Clear any proposals we had for this id
576             propsed_header_extensions.remove(&id);
577         }
578         Ok(())
579     }
580 
push_codecs(&self, codecs: Vec<RTCRtpCodecParameters>, typ: RTPCodecType)581     pub(crate) async fn push_codecs(&self, codecs: Vec<RTCRtpCodecParameters>, typ: RTPCodecType) {
582         for codec in codecs {
583             if typ == RTPCodecType::Audio {
584                 let mut negotiated_audio_codecs = self.negotiated_audio_codecs.lock();
585                 MediaEngine::add_codec(&mut negotiated_audio_codecs, codec);
586             } else if typ == RTPCodecType::Video {
587                 let mut negotiated_video_codecs = self.negotiated_video_codecs.lock();
588                 MediaEngine::add_codec(&mut negotiated_video_codecs, codec);
589             }
590         }
591     }
592 
593     /// Update the MediaEngine from a remote description
update_from_remote_description( &self, desc: &SessionDescription, ) -> Result<()>594     pub(crate) async fn update_from_remote_description(
595         &self,
596         desc: &SessionDescription,
597     ) -> Result<()> {
598         for media in &desc.media_descriptions {
599             let typ = if !self.negotiated_audio.load(Ordering::SeqCst)
600                 && media.media_name.media.to_lowercase() == "audio"
601             {
602                 self.negotiated_audio.store(true, Ordering::SeqCst);
603                 RTPCodecType::Audio
604             } else if !self.negotiated_video.load(Ordering::SeqCst)
605                 && media.media_name.media.to_lowercase() == "video"
606             {
607                 self.negotiated_video.store(true, Ordering::SeqCst);
608                 RTPCodecType::Video
609             } else {
610                 continue;
611             };
612 
613             let codecs = codecs_from_media_description(media)?;
614 
615             let mut exact_matches = vec![]; //make([]RTPCodecParameters, 0, len(codecs))
616             let mut partial_matches = vec![]; //make([]RTPCodecParameters, 0, len(codecs))
617 
618             for codec in codecs {
619                 let match_type =
620                     self.match_remote_codec(&codec, typ, &exact_matches, &partial_matches)?;
621 
622                 if match_type == CodecMatch::Exact {
623                     exact_matches.push(codec);
624                 } else if match_type == CodecMatch::Partial {
625                     partial_matches.push(codec);
626                 }
627             }
628 
629             // use exact matches when they exist, otherwise fall back to partial
630             if !exact_matches.is_empty() {
631                 self.push_codecs(exact_matches, typ).await;
632             } else if !partial_matches.is_empty() {
633                 self.push_codecs(partial_matches, typ).await;
634             } else {
635                 // no match, not negotiated
636                 continue;
637             }
638 
639             let extensions = rtp_extensions_from_media_description(media)?;
640 
641             for (extension, id) in extensions {
642                 self.update_header_extension(id, &extension, typ).await?;
643             }
644         }
645 
646         Ok(())
647     }
648 
get_codecs_by_kind(&self, typ: RTPCodecType) -> Vec<RTCRtpCodecParameters>649     pub(crate) fn get_codecs_by_kind(&self, typ: RTPCodecType) -> Vec<RTCRtpCodecParameters> {
650         if typ == RTPCodecType::Video {
651             if self.negotiated_video.load(Ordering::SeqCst) {
652                 let negotiated_video_codecs = self.negotiated_video_codecs.lock();
653                 negotiated_video_codecs.clone()
654             } else {
655                 self.video_codecs.clone()
656             }
657         } else if typ == RTPCodecType::Audio {
658             if self.negotiated_audio.load(Ordering::SeqCst) {
659                 let negotiated_audio_codecs = self.negotiated_audio_codecs.lock();
660                 negotiated_audio_codecs.clone()
661             } else {
662                 self.audio_codecs.clone()
663             }
664         } else {
665             vec![]
666         }
667     }
668 
get_rtp_parameters_by_kind( &self, typ: RTPCodecType, direction: RTCRtpTransceiverDirection, ) -> RTCRtpParameters669     pub(crate) fn get_rtp_parameters_by_kind(
670         &self,
671         typ: RTPCodecType,
672         direction: RTCRtpTransceiverDirection,
673     ) -> RTCRtpParameters {
674         let mut header_extensions = vec![];
675 
676         if self.negotiated_video.load(Ordering::SeqCst) && typ == RTPCodecType::Video
677             || self.negotiated_audio.load(Ordering::SeqCst) && typ == RTPCodecType::Audio
678         {
679             let negotiated_header_extensions = self.negotiated_header_extensions.lock();
680             for (id, e) in &*negotiated_header_extensions {
681                 if e.is_matching_direction(direction)
682                     && (e.is_audio && typ == RTPCodecType::Audio
683                         || e.is_video && typ == RTPCodecType::Video)
684                 {
685                     header_extensions.push(RTCRtpHeaderExtensionParameters {
686                         id: *id,
687                         uri: e.uri.clone(),
688                     });
689                 }
690             }
691         } else {
692             let mut proposed_header_extensions = self.proposed_header_extensions.lock();
693             let mut negotiated_header_extensions = self.negotiated_header_extensions.lock();
694 
695             for local_extension in &self.header_extensions {
696                 let relevant = local_extension.is_matching_direction(direction)
697                     && (local_extension.is_audio && typ == RTPCodecType::Audio
698                         || local_extension.is_video && typ == RTPCodecType::Video);
699 
700                 if !relevant {
701                     continue;
702                 }
703 
704                 if let Some((id, negotiated_extension)) = negotiated_header_extensions
705                     .iter_mut()
706                     .find(|(_, e)| e.uri == local_extension.uri)
707                 {
708                     // We have previously negotiated this extension, make sure to record it as
709                     // active for the current type
710                     negotiated_extension.is_audio |= typ == RTPCodecType::Audio;
711                     negotiated_extension.is_video |= typ == RTPCodecType::Video;
712 
713                     header_extensions.push(RTCRtpHeaderExtensionParameters {
714                         id: *id,
715                         uri: negotiated_extension.uri.clone(),
716                     });
717 
718                     continue;
719                 }
720 
721                 if let Some((id, negotiated_extension)) = proposed_header_extensions
722                     .iter_mut()
723                     .find(|(_, e)| e.uri == local_extension.uri)
724                 {
725                     // We have previously proposed this extension, re-use it
726                     header_extensions.push(RTCRtpHeaderExtensionParameters {
727                         id: *id,
728                         uri: negotiated_extension.uri.clone(),
729                     });
730 
731                     continue;
732                 }
733 
734                 // Figure out which (unused id) to propose.
735                 let id = VALID_EXT_IDS.clone().find(|id| {
736                     !negotiated_header_extensions.keys().any(|nid| nid == id)
737                         && !proposed_header_extensions.keys().any(|pid| pid == id)
738                 });
739 
740                 if let Some(id) = id {
741                     proposed_header_extensions.insert(
742                         id,
743                         MediaEngineHeaderExtension {
744                             uri: local_extension.uri.clone(),
745                             is_audio: local_extension.is_audio,
746                             is_video: local_extension.is_video,
747                             allowed_direction: local_extension.allowed_direction,
748                         },
749                     );
750 
751                     header_extensions.push(RTCRtpHeaderExtensionParameters {
752                         id,
753                         uri: local_extension.uri.clone(),
754                     });
755                 } else {
756                     log::warn!("No available RTP extension ID for {}", local_extension.uri);
757                 }
758             }
759         }
760 
761         RTCRtpParameters {
762             header_extensions,
763             codecs: self.get_codecs_by_kind(typ),
764         }
765     }
766 
get_rtp_parameters_by_payload_type( &self, payload_type: PayloadType, ) -> Result<RTCRtpParameters>767     pub(crate) async fn get_rtp_parameters_by_payload_type(
768         &self,
769         payload_type: PayloadType,
770     ) -> Result<RTCRtpParameters> {
771         let (codec, typ) = self.get_codec_by_payload(payload_type).await?;
772 
773         let mut header_extensions = vec![];
774         {
775             let negotiated_header_extensions = self.negotiated_header_extensions.lock();
776             for (id, e) in &*negotiated_header_extensions {
777                 if e.is_audio && typ == RTPCodecType::Audio
778                     || e.is_video && typ == RTPCodecType::Video
779                 {
780                     header_extensions.push(RTCRtpHeaderExtensionParameters {
781                         uri: e.uri.clone(),
782                         id: *id,
783                     });
784                 }
785             }
786         }
787 
788         Ok(RTCRtpParameters {
789             header_extensions,
790             codecs: vec![codec],
791         })
792     }
793 }
794