use anyhow::Result;
use rustrtc::media::frame::{MediaSample, VideoFrame};
use rustrtc::{PeerConnection, RtcConfiguration, RtpCodecParameters, SdpType, TransportMode};
use std::sync::Arc;
use std::time::Duration;
use tokio::net::UdpSocket;
#[tokio::test]
async fn test_rtp_mode_no_default_extensions() -> Result<()> {
let _ = env_logger::builder().is_test(true).try_init();
let mut config = RtcConfiguration::default();
config.transport_mode = TransportMode::Rtp;
config.bind_ip = Some("127.0.0.1".to_string());
config.rtp_start_port = Some(40200);
config.rtp_end_port = Some(40300);
let pc = PeerConnection::new(config);
let (source, track_video, _) =
rustrtc::media::track::sample_track(rustrtc::media::frame::MediaKind::Video, 90000);
let source = Arc::new(source);
let params_video = RtpCodecParameters {
payload_type: 96,
clock_rate: 90000,
channels: 0,
};
pc.add_track(track_video, params_video)?;
let offer = pc.create_offer().await?;
for section in &offer.media_sections {
for attr in §ion.attributes {
if attr.key == "extmap" {
if let Some(val) = &attr.value {
println!("Found extmap: {}", val);
if val.contains("http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time") {
panic!("Found abs-send-time extension in RTP mode offer!");
}
if val.contains("urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id") {
panic!("Found rid extension in RTP mode offer!");
}
if val.contains("urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id") {
panic!("Found repaired-rid extension in RTP mode offer!");
}
}
}
}
}
pc.set_local_description(offer.clone())?;
let socket = UdpSocket::bind("127.0.0.1:0").await?;
let remote_addr = socket.local_addr()?;
let mut answer = offer.clone();
answer.sdp_type = SdpType::Answer;
let ip_str = remote_addr.ip().to_string();
for section in &mut answer.media_sections {
section.connection = Some(format!("IN IP4 {}", ip_str));
section.port = remote_addr.port();
section.attributes.retain(|a| a.key != "candidate");
}
pc.set_remote_description(answer).await?;
let connected = pc.wait_for_connected();
match tokio::time::timeout(Duration::from_secs(5), connected).await {
Ok(_) => println!("PC API reports Connected"),
Err(_) => panic!("PC failed to connect"),
}
let source_clone = source.clone();
tokio::spawn(async move {
for i in 0..5 {
let frame = VideoFrame {
rtp_timestamp: i * 3000,
data: bytes::Bytes::from(vec![0u8; 100]),
is_last_packet: true,
..Default::default()
};
if source_clone.send(MediaSample::Video(frame)).await.is_err() {
break;
}
tokio::time::sleep(Duration::from_millis(20)).await;
}
});
let mut buf = [0u8; 1500];
let (len, _) = tokio::time::timeout(Duration::from_secs(2), socket.recv_from(&mut buf))
.await
.expect("Timeout waiting for packet")?;
println!("Received packet len {}", len);
let b0 = buf[0];
let x_bit = b0 & 0x10;
println!("Byte0: {:08b} (0x{:02x}), X-bit: {}", b0, b0, x_bit > 0);
if x_bit != 0 {
panic!(
"RTP Header Extension bit (X) is SET! Packet header Dump: {:02x?}",
&buf[0..12]
);
}
println!("SUCCESS: RTP Header Extension bit is NOT set.");
Ok(())
}