use std::sync::atomic::AtomicUsize;
use super::*;
use crate::api::media_engine::{MIME_TYPE_OPUS, MIME_TYPE_VP8, MIME_TYPE_VP9};
use crate::api::APIBuilder;
use crate::peer_connection::configuration::RTCConfiguration;
use crate::peer_connection::peer_connection_test::{close_pair_now, create_vnet_pair};
#[tokio::test]
async fn test_rtp_transceiver_set_codec_preferences() -> Result<()> {
let mut m = MediaEngine::default();
m.register_default_codecs()?;
m.push_codecs(m.video_codecs.clone(), RTPCodecType::Video)
.await;
m.push_codecs(m.audio_codecs.clone(), RTPCodecType::Audio)
.await;
let media_video_codecs = m.video_codecs.clone();
let api = APIBuilder::new().with_media_engine(m).build();
let tr = RTCRtpTransceiver::new(
None,
None,
RTCRtpTransceiverDirection::Unspecified,
RTPCodecType::Video,
media_video_codecs.clone(),
Arc::clone(&api.media_engine),
None,
)
.await;
assert_eq!(&media_video_codecs, &tr.get_codecs().await);
let fail_test_cases = vec![
vec![RTCRtpCodecParameters {
capability: RTCRtpCodecCapability {
mime_type: MIME_TYPE_OPUS.to_string(),
clock_rate: 48000,
channels: 2,
sdp_fmtp_line: "minptime=10;useinbandfec=1".to_string(),
rtcp_feedback: vec![],
},
payload_type: 111,
..Default::default()
}],
vec![
RTCRtpCodecParameters {
capability: RTCRtpCodecCapability {
mime_type: MIME_TYPE_VP8.to_string(),
clock_rate: 90000,
channels: 0,
sdp_fmtp_line: "".to_string(),
rtcp_feedback: vec![],
},
payload_type: 96,
..Default::default()
},
RTCRtpCodecParameters {
capability: RTCRtpCodecCapability {
mime_type: MIME_TYPE_OPUS.to_string(),
clock_rate: 48000,
channels: 2,
sdp_fmtp_line: "minptime=10;useinbandfec=1".to_string(),
rtcp_feedback: vec![],
},
payload_type: 111,
..Default::default()
},
],
];
for test_case in fail_test_cases {
if let Err(err) = tr.set_codec_preferences(test_case).await {
assert_eq!(Error::ErrRTPTransceiverCodecUnsupported, err);
} else {
assert!(false);
}
}
let success_test_cases = vec![
vec![RTCRtpCodecParameters {
capability: RTCRtpCodecCapability {
mime_type: MIME_TYPE_VP8.to_string(),
clock_rate: 90000,
channels: 0,
sdp_fmtp_line: "".to_string(),
rtcp_feedback: vec![],
},
payload_type: 96,
..Default::default()
}],
vec![
RTCRtpCodecParameters {
capability: RTCRtpCodecCapability {
mime_type: MIME_TYPE_VP8.to_string(),
clock_rate: 90000,
channels: 0,
sdp_fmtp_line: "".to_string(),
rtcp_feedback: vec![],
},
payload_type: 96,
..Default::default()
},
RTCRtpCodecParameters {
capability: RTCRtpCodecCapability {
mime_type: MIME_TYPE_VP9.to_string(),
clock_rate: 90000,
channels: 0,
sdp_fmtp_line: "profile-id=0".to_string(),
rtcp_feedback: vec![],
},
payload_type: 98,
..Default::default()
},
],
];
for test_case in success_test_cases {
tr.set_codec_preferences(test_case).await?;
}
tr.set_codec_preferences(vec![]).await?;
assert_ne!(0, tr.get_codecs().await.len());
Ok(())
}
#[tokio::test]
async fn test_rtp_transceiver_set_codec_preferences_payload_type() -> Result<()> {
let test_codec = RTCRtpCodecParameters {
capability: RTCRtpCodecCapability {
mime_type: "video/test_codec".to_string(),
clock_rate: 90000,
channels: 0,
sdp_fmtp_line: "".to_string(),
rtcp_feedback: vec![],
},
payload_type: 50,
..Default::default()
};
let mut m = MediaEngine::default();
m.register_default_codecs()?;
let api = APIBuilder::new().with_media_engine(m).build();
let offer_pc = api.new_peer_connection(RTCConfiguration::default()).await?;
let mut m = MediaEngine::default();
m.register_default_codecs()?;
m.register_codec(test_codec.clone(), RTPCodecType::Video)?;
let api = APIBuilder::new().with_media_engine(m).build();
let answer_pc = api.new_peer_connection(RTCConfiguration::default()).await?;
let _ = offer_pc
.add_transceiver_from_kind(RTPCodecType::Video, &[])
.await?;
let answer_transceiver = answer_pc
.add_transceiver_from_kind(RTPCodecType::Video, &[])
.await?;
answer_transceiver
.set_codec_preferences(vec![
test_codec,
RTCRtpCodecParameters {
capability: RTCRtpCodecCapability {
mime_type: MIME_TYPE_VP8.to_string(),
clock_rate: 90000,
channels: 0,
sdp_fmtp_line: "".to_string(),
rtcp_feedback: vec![],
},
payload_type: 51,
..Default::default()
},
])
.await?;
let offer = offer_pc.create_offer(None).await?;
offer_pc.set_local_description(offer.clone()).await?;
answer_pc.set_remote_description(offer).await?;
let answer = answer_pc.create_answer(None).await?;
assert!(
answer.sdp.contains("a=rtpmap:51 VP8/90000"),
"{}",
answer.sdp
);
assert!(!answer.sdp.contains("test_codec"));
close_pair_now(&offer_pc, &answer_pc).await;
Ok(())
}
#[tokio::test]
async fn test_rtp_transceiver_direction_change() -> Result<()> {
let (offer_pc, answer_pc, _) = create_vnet_pair().await?;
let offer_transceiver = offer_pc
.add_transceiver_from_kind(RTPCodecType::Video, &[])
.await?;
let _ = answer_pc
.add_transceiver_from_kind(RTPCodecType::Video, &[])
.await?;
let offer = offer_pc.create_offer(None).await?;
offer_pc.set_local_description(offer.clone()).await?;
answer_pc.set_remote_description(offer).await?;
let answer = answer_pc.create_answer(None).await?;
assert!(answer.sdp.contains("a=sendrecv"),);
answer_pc.set_local_description(answer.clone()).await?;
offer_pc.set_remote_description(answer).await?;
offer_transceiver
.set_direction(RTCRtpTransceiverDirection::Inactive)
.await;
let offer = offer_pc.create_offer(None).await?;
assert!(offer.sdp.contains("a=inactive"),);
offer_pc.set_local_description(offer.clone()).await?;
answer_pc.set_remote_description(offer).await?;
let answer = answer_pc.create_answer(None).await?;
assert!(answer.sdp.contains("a=inactive"),);
offer_pc.set_remote_description(answer).await?;
close_pair_now(&offer_pc, &answer_pc).await;
Ok(())
}
#[tokio::test]
async fn test_rtp_transceiver_set_direction_causing_negotiation() -> Result<()> {
let (offer_pc, answer_pc, _) = create_vnet_pair().await?;
let count = Arc::new(AtomicUsize::new(0));
{
let count = count.clone();
offer_pc.on_negotiation_needed(Box::new(move || {
let count = count.clone();
Box::pin(async move {
count.fetch_add(1, Ordering::SeqCst);
})
}));
}
let offer_transceiver = offer_pc
.add_transceiver_from_kind(RTPCodecType::Video, &[])
.await?;
let _ = answer_pc
.add_transceiver_from_kind(RTPCodecType::Video, &[])
.await?;
let offer = offer_pc.create_offer(None).await?;
offer_pc.set_local_description(offer.clone()).await?;
answer_pc.set_remote_description(offer).await?;
let answer = answer_pc.create_answer(None).await?;
answer_pc.set_local_description(answer.clone()).await?;
offer_pc.set_remote_description(answer).await?;
assert_eq!(count.load(Ordering::SeqCst), 0);
let offer = offer_pc.create_offer(None).await?;
offer_pc.set_local_description(offer.clone()).await?;
answer_pc.set_remote_description(offer).await?;
let answer = answer_pc.create_answer(None).await?;
answer_pc.set_local_description(answer.clone()).await?;
offer_pc.set_remote_description(answer).await?;
assert_eq!(count.load(Ordering::SeqCst), 0);
offer_transceiver
.set_direction(RTCRtpTransceiverDirection::Inactive)
.await;
offer_pc.internal.ops.done().await;
assert_eq!(count.load(Ordering::SeqCst), 1);
close_pair_now(&offer_pc, &answer_pc).await;
Ok(())
}
#[ignore]
#[tokio::test]
async fn test_rtp_transceiver_stopping() -> Result<()> {
let (offer_pc, answer_pc, _) = create_vnet_pair().await?;
let offer_transceiver = offer_pc
.add_transceiver_from_kind(RTPCodecType::Video, &[])
.await?;
let _ = answer_pc
.add_transceiver_from_kind(RTPCodecType::Video, &[])
.await?;
let offer = offer_pc.create_offer(None).await?;
offer_pc.set_local_description(offer.clone()).await?;
answer_pc.set_remote_description(offer).await?;
let answer = answer_pc.create_answer(None).await?;
assert!(answer.sdp.contains("a=sendrecv"),);
answer_pc.set_local_description(answer.clone()).await?;
offer_pc.set_remote_description(answer).await?;
assert!(
!offer_transceiver.mid().await.is_empty(),
"A mid should have been associated with the transceiver when applying the answer"
);
offer_transceiver.stop().await?;
let offer = offer_pc.create_offer(None).await?;
assert!(offer.sdp.contains("a=inactive"),);
let parsed = offer.parsed.unwrap();
let m = &parsed.media_descriptions[0];
assert_eq!(
m.media_name.port.value, 0,
"After stopping a transceiver it should be rejected in offers"
);
close_pair_now(&offer_pc, &answer_pc).await;
Ok(())
}