use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct IceServer {
pub urls: Vec<String>,
pub username: Option<String>,
pub credential: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
pub enum IceTransportPolicy {
#[default]
All,
Relay,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
pub enum DtlsRole {
#[default]
Auto,
Client,
Server,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum VideoCodec {
Vp8,
Vp9,
H264,
Av1,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum AudioCodec {
Opus,
G711Ulaw,
G711Alaw,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct CodecPreferences {
pub video: Vec<VideoCodec>,
pub audio: Vec<AudioCodec>,
}
impl CodecPreferences {
pub fn default_webrtc() -> Self {
Self {
video: vec![VideoCodec::Vp8, VideoCodec::Vp9, VideoCodec::H264],
audio: vec![AudioCodec::Opus],
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BandwidthConstraints {
pub min_bps: u32,
pub start_bps: u32,
pub max_bps: u32,
}
impl Default for BandwidthConstraints {
fn default() -> Self {
Self {
min_bps: 30_000,
start_bps: 300_000,
max_bps: 2_500_000,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WebRtcConfig {
pub ice_servers: Vec<IceServer>,
pub ice_transport_policy: IceTransportPolicy,
pub codec_preferences: CodecPreferences,
pub bandwidth: BandwidthConstraints,
pub dtls_role: DtlsRole,
pub mdns_enabled: bool,
pub tcp_candidates_enabled: bool,
pub bind_addresses: Vec<String>,
}
impl Default for WebRtcConfig {
fn default() -> Self {
Self {
ice_servers: vec![IceServer {
urls: vec!["stun:stun.l.google.com:19302".to_string()],
username: None,
credential: None,
}],
ice_transport_policy: IceTransportPolicy::All,
codec_preferences: CodecPreferences::default_webrtc(),
bandwidth: BandwidthConstraints::default(),
dtls_role: DtlsRole::Auto,
mdns_enabled: false,
tcp_candidates_enabled: true,
bind_addresses: vec!["0.0.0.0:0".to_string()],
}
}
}
impl WebRtcConfig {
pub fn to_rtc_configuration(&self) -> webrtc::peer_connection::RTCConfiguration {
use webrtc::peer_connection::{
RTCConfigurationBuilder, RTCIceServer, RTCIceTransportPolicy,
};
let ice_servers: Vec<RTCIceServer> = self
.ice_servers
.iter()
.map(|s| RTCIceServer {
urls: s.urls.clone(),
username: s.username.clone().unwrap_or_default(),
credential: s.credential.clone().unwrap_or_default(),
..Default::default()
})
.collect();
RTCConfigurationBuilder::new()
.with_ice_servers(ice_servers)
.with_ice_transport_policy(match self.ice_transport_policy {
IceTransportPolicy::All => RTCIceTransportPolicy::All,
IceTransportPolicy::Relay => RTCIceTransportPolicy::Relay,
})
.build()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn webrtc_config_serde_roundtrip() {
let config = WebRtcConfig {
ice_servers: vec![
IceServer {
urls: vec!["stun:stun.example.com:3478".to_string()],
username: None,
credential: None,
},
IceServer {
urls: vec!["turn:turn.example.com:3478".to_string()],
username: Some("user".to_string()),
credential: Some("pass".to_string()),
},
],
ice_transport_policy: IceTransportPolicy::Relay,
codec_preferences: CodecPreferences::default_webrtc(),
bandwidth: BandwidthConstraints {
min_bps: 50_000,
start_bps: 500_000,
max_bps: 5_000_000,
},
dtls_role: DtlsRole::Client,
mdns_enabled: true,
tcp_candidates_enabled: false,
bind_addresses: vec!["192.168.1.1:0".to_string()],
};
let json = serde_json::to_string(&config).unwrap();
let rt: WebRtcConfig = serde_json::from_str(&json).unwrap();
assert_eq!(rt.ice_servers.len(), 2);
assert_eq!(rt.ice_transport_policy, IceTransportPolicy::Relay);
assert_eq!(rt.dtls_role, DtlsRole::Client);
assert!(rt.mdns_enabled);
assert!(!rt.tcp_candidates_enabled);
assert_eq!(rt.bandwidth.min_bps, 50_000);
assert_eq!(rt.bind_addresses, vec!["192.168.1.1:0"]);
}
#[test]
fn bandwidth_constraints_defaults() {
let bw = BandwidthConstraints::default();
assert_eq!(bw.min_bps, 30_000);
assert_eq!(bw.start_bps, 300_000);
assert_eq!(bw.max_bps, 2_500_000);
}
#[test]
fn codec_preferences_default_webrtc() {
let prefs = CodecPreferences::default_webrtc();
assert_eq!(prefs.video[0], VideoCodec::Vp8);
assert_eq!(prefs.audio[0], AudioCodec::Opus);
}
}