use super::*;
use crate::rtp_parameters::{MimeTypeAudio, RtpHeaderExtension};
use std::iter;
#[test]
fn generate_router_rtp_capabilities_succeeds() {
let media_codecs = vec![
RtpCodecCapability::Audio {
mime_type: MimeTypeAudio::Opus,
preferred_payload_type: None,
clock_rate: NonZeroU32::new(48000).unwrap(),
channels: NonZeroU8::new(2).unwrap(),
parameters: RtpCodecParametersParameters::from([
("useinbandfec", 1_u32.into()),
("foo", "bar".into()),
]),
rtcp_feedback: vec![],
},
RtpCodecCapability::Video {
mime_type: MimeTypeVideo::Vp8,
preferred_payload_type: Some(125), clock_rate: NonZeroU32::new(90000).unwrap(),
parameters: RtpCodecParametersParameters::default(),
rtcp_feedback: vec![],
},
RtpCodecCapability::Video {
mime_type: MimeTypeVideo::H264,
preferred_payload_type: None,
clock_rate: NonZeroU32::new(90000).unwrap(),
parameters: RtpCodecParametersParameters::from([
("level-asymmetry-allowed", 1_u32.into()),
("profile-level-id", "42e01f".into()),
("foo", "bar".into()),
]),
rtcp_feedback: vec![], },
];
let rtp_capabilities = generate_router_rtp_capabilities(media_codecs)
.expect("Failed to generate router RTP capabilities");
assert_eq!(
rtp_capabilities.codecs,
vec![
RtpCodecCapabilityFinalized::Audio {
mime_type: MimeTypeAudio::Opus,
preferred_payload_type: 100, clock_rate: NonZeroU32::new(48000).unwrap(),
channels: NonZeroU8::new(2).unwrap(),
parameters: RtpCodecParametersParameters::from([
("useinbandfec", 1_u32.into()),
("foo", "bar".into()),
]),
rtcp_feedback: vec![RtcpFeedback::TransportCc],
},
RtpCodecCapabilityFinalized::Video {
mime_type: MimeTypeVideo::Vp8,
preferred_payload_type: 125,
clock_rate: NonZeroU32::new(90000).unwrap(),
parameters: RtpCodecParametersParameters::default(),
rtcp_feedback: vec![
RtcpFeedback::Nack,
RtcpFeedback::NackPli,
RtcpFeedback::CcmFir,
RtcpFeedback::GoogRemb,
RtcpFeedback::TransportCc
],
},
RtpCodecCapabilityFinalized::Video {
mime_type: MimeTypeVideo::Rtx,
preferred_payload_type: 101, clock_rate: NonZeroU32::new(90000).unwrap(),
parameters: RtpCodecParametersParameters::from([("apt", 125u32.into())]),
rtcp_feedback: vec![],
},
RtpCodecCapabilityFinalized::Video {
mime_type: MimeTypeVideo::H264,
preferred_payload_type: 102, clock_rate: NonZeroU32::new(90000).unwrap(),
parameters: RtpCodecParametersParameters::from([
("packetization-mode", 0_u32.into()),
("level-asymmetry-allowed", 1_u32.into()),
("profile-level-id", "42e01f".into()),
("foo", "bar".into()),
]),
rtcp_feedback: vec![
RtcpFeedback::Nack,
RtcpFeedback::NackPli,
RtcpFeedback::CcmFir,
RtcpFeedback::GoogRemb,
RtcpFeedback::TransportCc,
],
},
RtpCodecCapabilityFinalized::Video {
mime_type: MimeTypeVideo::Rtx,
preferred_payload_type: 103,
clock_rate: NonZeroU32::new(90000).unwrap(),
parameters: RtpCodecParametersParameters::from([("apt", 102u32.into())]),
rtcp_feedback: vec![],
},
]
);
}
#[test]
fn generate_router_rtp_capabilities_unsupported() {
assert!(matches!(
generate_router_rtp_capabilities(vec![RtpCodecCapability::Audio {
mime_type: MimeTypeAudio::Opus,
preferred_payload_type: None,
clock_rate: NonZeroU32::new(48000).unwrap(),
channels: NonZeroU8::new(1).unwrap(),
parameters: RtpCodecParametersParameters::default(),
rtcp_feedback: vec![],
}]),
Err(RtpCapabilitiesError::UnsupportedCodec { .. })
));
assert!(matches!(
generate_router_rtp_capabilities(vec![RtpCodecCapability::Video {
mime_type: MimeTypeVideo::H264,
preferred_payload_type: None,
clock_rate: NonZeroU32::new(90000).unwrap(),
parameters: RtpCodecParametersParameters::from([("packetization-mode", 5u32.into())]),
rtcp_feedback: vec![],
}]),
Err(RtpCapabilitiesError::UnsupportedCodec { .. })
));
}
#[test]
fn generate_router_rtp_capabilities_too_many_codecs() {
assert!(matches!(
generate_router_rtp_capabilities(
iter::repeat(RtpCodecCapability::Audio {
mime_type: MimeTypeAudio::Opus,
preferred_payload_type: None,
clock_rate: NonZeroU32::new(48000).unwrap(),
channels: NonZeroU8::new(2).unwrap(),
parameters: RtpCodecParametersParameters::default(),
rtcp_feedback: vec![],
})
.take(100)
.collect::<Vec<_>>()
),
Err(RtpCapabilitiesError::CannotAllocate)
));
}
#[test]
fn get_producer_rtp_parameters_mapping_get_consumable_rtp_parameters_get_consumer_rtp_parameters_get_pipe_consumer_rtp_parameters_succeeds(
) {
let media_codecs = vec![
RtpCodecCapability::Audio {
mime_type: MimeTypeAudio::Opus,
preferred_payload_type: None,
clock_rate: NonZeroU32::new(48000).unwrap(),
channels: NonZeroU8::new(2).unwrap(),
parameters: RtpCodecParametersParameters::from([
("useinbandfec", 1_u32.into()),
("foo", "bar".into()),
]),
rtcp_feedback: vec![],
},
RtpCodecCapability::Video {
mime_type: MimeTypeVideo::H264,
preferred_payload_type: None,
clock_rate: NonZeroU32::new(90000).unwrap(),
parameters: RtpCodecParametersParameters::from([
("level-asymmetry-allowed", 1_u32.into()),
("packetization-mode", 1_u32.into()),
("profile-level-id", "4d0032".into()),
("foo", "lalala".into()),
]),
rtcp_feedback: vec![],
},
];
let router_rtp_capabilities = generate_router_rtp_capabilities(media_codecs)
.expect("Failed to generate router RTP capabilities");
let rtp_parameters = RtpParameters {
mid: None,
codecs: vec![
RtpCodecParameters::Video {
mime_type: MimeTypeVideo::H264,
payload_type: 111,
clock_rate: NonZeroU32::new(90000).unwrap(),
parameters: RtpCodecParametersParameters::from([
("foo", 1234u32.into()),
("packetization-mode", 1_u32.into()),
("profile-level-id", "4d0032".into()),
]),
rtcp_feedback: vec![
RtcpFeedback::Nack,
RtcpFeedback::NackPli,
RtcpFeedback::GoogRemb,
],
},
RtpCodecParameters::Video {
mime_type: MimeTypeVideo::Rtx,
payload_type: 112,
clock_rate: NonZeroU32::new(90000).unwrap(),
parameters: RtpCodecParametersParameters::from([("apt", 111_u32.into())]),
rtcp_feedback: vec![],
},
],
header_extensions: vec![
RtpHeaderExtensionParameters {
uri: RtpHeaderExtensionUri::Mid,
id: 1,
encrypt: false,
},
RtpHeaderExtensionParameters {
uri: RtpHeaderExtensionUri::VideoOrientation,
id: 2,
encrypt: false,
},
],
encodings: vec![
RtpEncodingParameters {
ssrc: Some(11111111),
rtx: Some(RtpEncodingParametersRtx { ssrc: 11111112 }),
scalability_mode: ScalabilityMode::L1T3,
max_bitrate: Some(111111),
..RtpEncodingParameters::default()
},
RtpEncodingParameters {
ssrc: Some(21111111),
rtx: Some(RtpEncodingParametersRtx { ssrc: 21111112 }),
scalability_mode: ScalabilityMode::L1T3,
max_bitrate: Some(222222),
..RtpEncodingParameters::default()
},
RtpEncodingParameters {
rid: Some("high".to_string()),
scalability_mode: ScalabilityMode::L1T3,
max_bitrate: Some(333333),
..RtpEncodingParameters::default()
},
],
rtcp: RtcpParameters {
cname: Some("qwerty1234".to_string()),
..RtcpParameters::default()
},
};
let rtp_mapping =
get_producer_rtp_parameters_mapping(&rtp_parameters, &router_rtp_capabilities)
.expect("Failed to get producer RTP parameters mapping");
assert_eq!(
rtp_mapping.codecs,
vec![
RtpMappingCodec {
payload_type: 111,
mapped_payload_type: 101
},
RtpMappingCodec {
payload_type: 112,
mapped_payload_type: 102
},
]
);
assert_eq!(rtp_mapping.encodings.get(0).unwrap().ssrc, Some(11111111));
assert_eq!(rtp_mapping.encodings.get(0).unwrap().rid, None);
assert_eq!(rtp_mapping.encodings.get(1).unwrap().ssrc, Some(21111111));
assert_eq!(rtp_mapping.encodings.get(1).unwrap().rid, None);
assert_eq!(rtp_mapping.encodings.get(2).unwrap().ssrc, None);
assert_eq!(
rtp_mapping.encodings.get(2).unwrap().rid,
Some("high".to_string())
);
let consumable_rtp_parameters = get_consumable_rtp_parameters(
MediaKind::Video,
&rtp_parameters,
&router_rtp_capabilities,
&rtp_mapping,
);
assert_eq!(
consumable_rtp_parameters.codecs,
vec![
RtpCodecParameters::Video {
mime_type: MimeTypeVideo::H264,
payload_type: 101,
clock_rate: NonZeroU32::new(90000).unwrap(),
parameters: RtpCodecParametersParameters::from([
("foo", 1234u32.into()),
("packetization-mode", 1_u32.into()),
("profile-level-id", "4d0032".into()),
]),
rtcp_feedback: vec![
RtcpFeedback::Nack,
RtcpFeedback::NackPli,
RtcpFeedback::CcmFir,
RtcpFeedback::GoogRemb,
RtcpFeedback::TransportCc,
],
},
RtpCodecParameters::Video {
mime_type: MimeTypeVideo::Rtx,
payload_type: 102,
clock_rate: NonZeroU32::new(90000).unwrap(),
parameters: RtpCodecParametersParameters::from([("apt", 101_u32.into())]),
rtcp_feedback: vec![],
},
]
);
assert_eq!(
consumable_rtp_parameters.encodings.get(0).unwrap().ssrc,
Some(rtp_mapping.encodings.get(0).unwrap().mapped_ssrc),
);
assert_eq!(
consumable_rtp_parameters
.encodings
.get(0)
.unwrap()
.max_bitrate,
Some(111111),
);
assert_eq!(
consumable_rtp_parameters
.encodings
.get(0)
.unwrap()
.scalability_mode,
ScalabilityMode::L1T3,
);
assert_eq!(
consumable_rtp_parameters.encodings.get(1).unwrap().ssrc,
Some(rtp_mapping.encodings.get(1).unwrap().mapped_ssrc),
);
assert_eq!(
consumable_rtp_parameters
.encodings
.get(1)
.unwrap()
.max_bitrate,
Some(222222),
);
assert_eq!(
consumable_rtp_parameters
.encodings
.get(1)
.unwrap()
.scalability_mode,
ScalabilityMode::L1T3,
);
assert_eq!(
consumable_rtp_parameters.encodings.get(2).unwrap().ssrc,
Some(rtp_mapping.encodings.get(2).unwrap().mapped_ssrc),
);
assert_eq!(
consumable_rtp_parameters
.encodings
.get(2)
.unwrap()
.max_bitrate,
Some(333333),
);
assert_eq!(
consumable_rtp_parameters
.encodings
.get(2)
.unwrap()
.scalability_mode,
ScalabilityMode::L1T3,
);
assert_eq!(
consumable_rtp_parameters.rtcp,
RtcpParameters {
cname: rtp_parameters.rtcp.cname.clone(),
reduced_size: true,
mux: Some(true),
}
);
let remote_rtp_capabilities = RtpCapabilities {
codecs: vec![
RtpCodecCapability::Audio {
mime_type: MimeTypeAudio::Opus,
preferred_payload_type: Some(100),
clock_rate: NonZeroU32::new(48000).unwrap(),
channels: NonZeroU8::new(2).unwrap(),
parameters: RtpCodecParametersParameters::default(),
rtcp_feedback: vec![],
},
RtpCodecCapability::Video {
mime_type: MimeTypeVideo::H264,
preferred_payload_type: Some(101),
clock_rate: NonZeroU32::new(90000).unwrap(),
parameters: RtpCodecParametersParameters::from([
("packetization-mode", 1_u32.into()),
("profile-level-id", "4d0032".into()),
("baz", "LOLOLO".into()),
]),
rtcp_feedback: vec![
RtcpFeedback::Nack,
RtcpFeedback::NackPli,
RtcpFeedback::Unsupported,
],
},
RtpCodecCapability::Video {
mime_type: MimeTypeVideo::Rtx,
preferred_payload_type: Some(102),
clock_rate: NonZeroU32::new(90000).unwrap(),
parameters: RtpCodecParametersParameters::from([("apt", 101_u32.into())]),
rtcp_feedback: vec![],
},
],
header_extensions: vec![
RtpHeaderExtension {
kind: MediaKind::Audio,
uri: RtpHeaderExtensionUri::Mid,
preferred_id: 1,
preferred_encrypt: false,
direction: RtpHeaderExtensionDirection::SendRecv,
},
RtpHeaderExtension {
kind: MediaKind::Video,
uri: RtpHeaderExtensionUri::Mid,
preferred_id: 1,
preferred_encrypt: false,
direction: RtpHeaderExtensionDirection::SendRecv,
},
RtpHeaderExtension {
kind: MediaKind::Video,
uri: RtpHeaderExtensionUri::RtpStreamId,
preferred_id: 2,
preferred_encrypt: false,
direction: RtpHeaderExtensionDirection::SendRecv,
},
RtpHeaderExtension {
kind: MediaKind::Audio,
uri: RtpHeaderExtensionUri::AudioLevel,
preferred_id: 8,
preferred_encrypt: false,
direction: RtpHeaderExtensionDirection::SendRecv,
},
RtpHeaderExtension {
kind: MediaKind::Video,
uri: RtpHeaderExtensionUri::VideoOrientation,
preferred_id: 11,
preferred_encrypt: false,
direction: RtpHeaderExtensionDirection::SendRecv,
},
RtpHeaderExtension {
kind: MediaKind::Video,
uri: RtpHeaderExtensionUri::TimeOffset,
preferred_id: 12,
preferred_encrypt: false,
direction: RtpHeaderExtensionDirection::SendRecv,
},
],
};
let consumer_rtp_parameters =
get_consumer_rtp_parameters(&consumable_rtp_parameters, &remote_rtp_capabilities, false)
.expect("Failed to get consumer RTP parameters");
assert_eq!(
consumer_rtp_parameters.codecs,
vec![
RtpCodecParameters::Video {
mime_type: MimeTypeVideo::H264,
payload_type: 101,
clock_rate: NonZeroU32::new(90000).unwrap(),
parameters: RtpCodecParametersParameters::from([
("foo", 1234u32.into()),
("packetization-mode", 1_u32.into()),
("profile-level-id", "4d0032".into()),
]),
rtcp_feedback: vec![
RtcpFeedback::Nack,
RtcpFeedback::NackPli,
RtcpFeedback::Unsupported,
],
},
RtpCodecParameters::Video {
mime_type: MimeTypeVideo::Rtx,
payload_type: 102,
clock_rate: NonZeroU32::new(90000).unwrap(),
parameters: RtpCodecParametersParameters::from([("apt", 101_u32.into())]),
rtcp_feedback: vec![],
},
]
);
assert_eq!(consumer_rtp_parameters.encodings.len(), 1);
assert!(consumer_rtp_parameters
.encodings
.get(0)
.unwrap()
.ssrc
.is_some());
assert!(consumer_rtp_parameters
.encodings
.get(0)
.unwrap()
.rtx
.is_some());
assert_eq!(
consumer_rtp_parameters
.encodings
.get(0)
.unwrap()
.scalability_mode,
ScalabilityMode::S3T3,
);
assert_eq!(
consumer_rtp_parameters
.encodings
.get(0)
.unwrap()
.max_bitrate,
Some(333333),
);
assert_eq!(
consumer_rtp_parameters.header_extensions,
vec![
RtpHeaderExtensionParameters {
uri: RtpHeaderExtensionUri::Mid,
id: 1,
encrypt: false,
},
RtpHeaderExtensionParameters {
uri: RtpHeaderExtensionUri::VideoOrientation,
id: 11,
encrypt: false,
},
RtpHeaderExtensionParameters {
uri: RtpHeaderExtensionUri::TimeOffset,
id: 12,
encrypt: false,
},
],
);
assert_eq!(
consumer_rtp_parameters.rtcp,
RtcpParameters {
cname: rtp_parameters.rtcp.cname.clone(),
reduced_size: true,
mux: Some(true),
},
);
let pipe_consumer_rtp_parameters =
get_pipe_consumer_rtp_parameters(&consumable_rtp_parameters, false);
assert_eq!(
pipe_consumer_rtp_parameters.codecs,
vec![RtpCodecParameters::Video {
mime_type: MimeTypeVideo::H264,
payload_type: 101,
clock_rate: NonZeroU32::new(90000).unwrap(),
parameters: RtpCodecParametersParameters::from([
("foo", 1234u32.into()),
("packetization-mode", 1_u32.into()),
("profile-level-id", "4d0032".into()),
]),
rtcp_feedback: vec![RtcpFeedback::NackPli, RtcpFeedback::CcmFir],
}],
);
assert_eq!(pipe_consumer_rtp_parameters.encodings.len(), 3);
assert!(pipe_consumer_rtp_parameters
.encodings
.get(0)
.unwrap()
.ssrc
.is_some());
assert!(pipe_consumer_rtp_parameters
.encodings
.get(0)
.unwrap()
.rtx
.is_none());
assert!(pipe_consumer_rtp_parameters
.encodings
.get(0)
.unwrap()
.max_bitrate
.is_some());
assert_eq!(
pipe_consumer_rtp_parameters
.encodings
.get(0)
.unwrap()
.scalability_mode,
ScalabilityMode::L1T3,
);
assert!(pipe_consumer_rtp_parameters
.encodings
.get(1)
.unwrap()
.ssrc
.is_some());
assert!(pipe_consumer_rtp_parameters
.encodings
.get(1)
.unwrap()
.rtx
.is_none());
assert!(pipe_consumer_rtp_parameters
.encodings
.get(1)
.unwrap()
.max_bitrate
.is_some());
assert_eq!(
pipe_consumer_rtp_parameters
.encodings
.get(1)
.unwrap()
.scalability_mode,
ScalabilityMode::L1T3,
);
assert!(pipe_consumer_rtp_parameters
.encodings
.get(2)
.unwrap()
.ssrc
.is_some());
assert!(pipe_consumer_rtp_parameters
.encodings
.get(2)
.unwrap()
.rtx
.is_none());
assert!(pipe_consumer_rtp_parameters
.encodings
.get(2)
.unwrap()
.max_bitrate
.is_some());
assert_eq!(
pipe_consumer_rtp_parameters
.encodings
.get(2)
.unwrap()
.scalability_mode,
ScalabilityMode::L1T3,
);
assert_eq!(
pipe_consumer_rtp_parameters.rtcp,
RtcpParameters {
cname: rtp_parameters.rtcp.cname.clone(),
reduced_size: true,
mux: Some(true),
},
);
}
#[test]
fn get_producer_rtp_parameters_mapping_unsupported() {
let media_codecs = vec![
RtpCodecCapability::Audio {
mime_type: MimeTypeAudio::Opus,
preferred_payload_type: None,
clock_rate: NonZeroU32::new(48000).unwrap(),
channels: NonZeroU8::new(2).unwrap(),
parameters: RtpCodecParametersParameters::default(),
rtcp_feedback: vec![],
},
RtpCodecCapability::Video {
mime_type: MimeTypeVideo::H264,
preferred_payload_type: None,
clock_rate: NonZeroU32::new(90000).unwrap(),
parameters: RtpCodecParametersParameters::from([
("packetization-mode", 1_u32.into()),
("profile-level-id", "640032".into()),
]),
rtcp_feedback: vec![],
},
];
let router_rtp_capabilities = generate_router_rtp_capabilities(media_codecs)
.expect("Failed to generate router RTP capabilities");
let rtp_parameters = RtpParameters {
mid: None,
codecs: vec![RtpCodecParameters::Video {
mime_type: MimeTypeVideo::Vp8,
payload_type: 120,
clock_rate: NonZeroU32::new(90000).unwrap(),
parameters: RtpCodecParametersParameters::default(),
rtcp_feedback: vec![RtcpFeedback::Nack, RtcpFeedback::Unsupported],
}],
header_extensions: vec![],
encodings: vec![RtpEncodingParameters {
ssrc: Some(11111111),
..RtpEncodingParameters::default()
}],
rtcp: RtcpParameters {
cname: Some("qwerty1234".to_string()),
..RtcpParameters::default()
},
};
assert!(matches!(
get_producer_rtp_parameters_mapping(&rtp_parameters, &router_rtp_capabilities),
Err(RtpParametersMappingError::UnsupportedCodec { .. }),
));
}