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