rvoip_client_core/client/config.rs
1//! Client configuration structures and presets
2//!
3//! This module provides comprehensive configuration structures for the VoIP client, including
4//! media settings, codec preferences, network parameters, and security options. It offers
5//! both fine-grained control and convenient presets for common use cases.
6//!
7//! # Key Components
8//!
9//! - **ClientConfig** - Main client configuration with network and session settings
10//! - **MediaConfig** - Media-specific settings including codecs and audio processing
11//! - **MediaPreset** - Predefined media configuration templates for common scenarios
12//!
13//! # Architecture
14//!
15//! ```text
16//! ┌─────────────────────────┐
17//! │ ClientConfig │
18//! │ ┌─────────────────────┐ │
19//! │ │ Network Settings │ │ • SIP & Media addresses
20//! │ │ Session Settings │ │ • Timeouts & Limits
21//! │ │ MediaConfig ─┼─┼─ • Codec preferences
22//! │ └─────────────────────┘ │ • Audio processing
23//! └─────────────────────────┘ • Security settings
24//! ```
25//!
26//! # Usage Examples
27//!
28//! ## Basic Client Configuration
29//!
30//! ```rust
31//! use rvoip_client_core::client::config::{ClientConfig, MediaPreset};
32//! use std::net::SocketAddr;
33//!
34//! let config = ClientConfig::new()
35//! .with_sip_addr("127.0.0.1:5060".parse().unwrap())
36//! .with_media_addr("127.0.0.1:0".parse().unwrap())
37//! .with_user_agent("MyApp/1.0".to_string())
38//! .with_max_calls(5);
39//!
40//! assert_eq!(config.max_concurrent_calls, 5);
41//! assert_eq!(config.user_agent, "MyApp/1.0");
42//! ```
43//!
44//! ## Advanced Media Configuration
45//!
46//! ```rust
47//! use rvoip_client_core::client::config::{ClientConfig, MediaConfig};
48//! use std::collections::HashMap;
49//!
50//! let mut custom_attributes = HashMap::new();
51//! custom_attributes.insert("a".to_string(), "sendrecv".to_string());
52//!
53//! let media_config = MediaConfig {
54//! preferred_codecs: vec!["opus".to_string(), "G722".to_string()],
55//! echo_cancellation: true,
56//! noise_suppression: true,
57//! auto_gain_control: true,
58//! max_bandwidth_kbps: Some(128),
59//! require_srtp: true,
60//! srtp_profiles: vec!["AES_CM_128_HMAC_SHA1_80".to_string()],
61//! rtp_port_start: 12000,
62//! rtp_port_end: 15000,
63//! preferred_ptime: Some(20),
64//! custom_sdp_attributes: custom_attributes,
65//! dtmf_enabled: true,
66//! };
67//!
68//! let config = ClientConfig::new().with_media(media_config);
69//! assert!(config.media.require_srtp);
70//! assert_eq!(config.media.rtp_port_start, 12000);
71//! ```
72//!
73//! ## Using Media Presets
74//!
75//! ```rust
76//! use rvoip_client_core::client::config::{ClientConfig, MediaPreset, MediaConfig};
77//!
78//! // Secure configuration for enterprise use
79//! let secure_config = ClientConfig::new()
80//! .with_media_preset(MediaPreset::Secure)
81//! .with_user_agent("Enterprise-Phone/1.0".to_string());
82//!
83//! assert!(secure_config.media.require_srtp);
84//! assert!(!secure_config.media.srtp_profiles.is_empty());
85//!
86//! // Low bandwidth configuration for mobile
87//! let mobile_config = ClientConfig::new()
88//! .with_media_preset(MediaPreset::LowBandwidth);
89//!
90//! assert!(mobile_config.media.max_bandwidth_kbps.unwrap() <= 32);
91//!
92//! // Voice-optimized configuration
93//! let voice_config = MediaConfig::from_preset(MediaPreset::VoiceOptimized);
94//! assert!(voice_config.echo_cancellation);
95//! assert!(voice_config.noise_suppression);
96//! ```
97//!
98//! ## Configuration Validation
99//!
100//! ```rust
101//! use rvoip_client_core::client::config::ClientConfig;
102//!
103//! let config = ClientConfig::new()
104//! .with_sip_addr("0.0.0.0:5060".parse().unwrap())
105//! .with_media_addr("0.0.0.0:0".parse().unwrap())
106//! .with_max_calls(100);
107//!
108//! // Validate configuration settings
109//! assert!(config.max_concurrent_calls > 0);
110//! assert!(config.session_timeout_secs > 0);
111//! assert!(config.media.rtp_port_start < config.media.rtp_port_end);
112//! assert!(config.enable_audio); // Default is true
113//! ```
114//!
115//! # Common Patterns
116//!
117//! ## Enterprise VoIP Setup
118//!
119//! ```rust
120//! use rvoip_client_core::client::config::{ClientConfig, MediaPreset};
121//!
122//! let enterprise_config = ClientConfig::new()
123//! .with_sip_addr("192.168.1.100:5060".parse().unwrap())
124//! .with_media_preset(MediaPreset::Secure)
125//! .with_user_agent("CorporatePhone/2.1".to_string())
126//! .with_max_calls(20);
127//!
128//! assert_eq!(enterprise_config.max_concurrent_calls, 20);
129//! ```
130//!
131//! ## Residential VoIP Setup
132//!
133//! ```rust
134//! use rvoip_client_core::client::config::{ClientConfig, MediaPreset};
135//!
136//! let home_config = ClientConfig::new()
137//! .with_media_preset(MediaPreset::VoiceOptimized)
138//! .with_max_calls(3);
139//!
140//! assert_eq!(home_config.max_concurrent_calls, 3);
141//! assert!(home_config.media.echo_cancellation);
142//! ```
143//!
144//! ## Mobile VoIP Setup
145//!
146//! ```rust
147//! use rvoip_client_core::client::config::{ClientConfig, MediaPreset};
148//!
149//! let mobile_config = ClientConfig::new()
150//! .with_media_preset(MediaPreset::LowBandwidth)
151//! .with_user_agent("MobileVoIP/1.0".to_string())
152//! .with_max_calls(2);
153//!
154//! assert!(mobile_config.media.max_bandwidth_kbps.is_some());
155//! assert!(mobile_config.media.max_bandwidth_kbps.unwrap() <= 32);
156//! ```
157
158use std::net::SocketAddr;
159use std::collections::HashMap;
160use serde::{Deserialize, Serialize};
161
162/// Media configuration preferences
163///
164/// Defines comprehensive media-related settings including codec preferences, audio processing
165/// options, security requirements, network parameters, and SDP customization. This structure
166/// provides fine-grained control over all aspects of media handling in VoIP calls.
167///
168/// # Audio Processing Features
169///
170/// - **Echo Cancellation**: Removes acoustic echo during calls
171/// - **Noise Suppression**: Filters background noise from audio
172/// - **Auto Gain Control**: Automatically adjusts microphone levels
173/// - **DTMF Support**: Dual-tone multi-frequency signaling for dial tones
174///
175/// # Security Features
176///
177/// - **SRTP Encryption**: Secure Real-time Transport Protocol for media encryption
178/// - **Profile Selection**: Choose from multiple SRTP encryption profiles
179/// - **Mandatory Encryption**: Option to require SRTP for all calls
180///
181/// # Network Configuration
182///
183/// - **Port Range Control**: Specify RTP port ranges for NAT/firewall traversal
184/// - **Bandwidth Management**: Set maximum bandwidth limits
185/// - **Packetization Time**: Control audio packet timing for latency/quality trade-offs
186///
187/// # Examples
188///
189/// ## Basic Media Configuration
190///
191/// ```rust
192/// use rvoip_client_core::client::config::MediaConfig;
193/// use std::collections::HashMap;
194///
195/// let media_config = MediaConfig {
196/// preferred_codecs: vec!["opus".to_string(), "PCMU".to_string()],
197/// dtmf_enabled: true,
198/// echo_cancellation: true,
199/// noise_suppression: true,
200/// auto_gain_control: true,
201/// max_bandwidth_kbps: Some(128),
202/// require_srtp: false,
203/// srtp_profiles: vec![],
204/// rtp_port_start: 10000,
205/// rtp_port_end: 20000,
206/// preferred_ptime: Some(20),
207/// custom_sdp_attributes: HashMap::new(),
208/// };
209///
210/// assert_eq!(media_config.preferred_codecs[0], "opus");
211/// assert!(media_config.echo_cancellation);
212/// assert_eq!(media_config.max_bandwidth_kbps, Some(128));
213/// ```
214///
215/// ## Secure Media Configuration
216///
217/// ```rust
218/// use rvoip_client_core::client::config::MediaConfig;
219/// use std::collections::HashMap;
220///
221/// let secure_config = MediaConfig {
222/// preferred_codecs: vec!["opus".to_string(), "G722".to_string()],
223/// require_srtp: true,
224/// srtp_profiles: vec![
225/// "AES_CM_128_HMAC_SHA1_80".to_string(),
226/// "AES_CM_128_HMAC_SHA1_32".to_string(),
227/// ],
228/// dtmf_enabled: true,
229/// echo_cancellation: true,
230/// noise_suppression: true,
231/// auto_gain_control: true,
232/// max_bandwidth_kbps: Some(256),
233/// rtp_port_start: 16384,
234/// rtp_port_end: 32767,
235/// preferred_ptime: Some(20),
236/// custom_sdp_attributes: HashMap::new(),
237/// };
238///
239/// assert!(secure_config.require_srtp);
240/// assert_eq!(secure_config.srtp_profiles.len(), 2);
241/// assert!(secure_config.srtp_profiles.contains(&"AES_CM_128_HMAC_SHA1_80".to_string()));
242/// ```
243///
244/// ## Low Bandwidth Configuration
245///
246/// ```rust
247/// use rvoip_client_core::client::config::MediaConfig;
248/// use std::collections::HashMap;
249///
250/// let low_bandwidth_config = MediaConfig {
251/// preferred_codecs: vec!["G729".to_string(), "GSM".to_string()],
252/// max_bandwidth_kbps: Some(32),
253/// preferred_ptime: Some(30), // Larger packets for efficiency
254/// echo_cancellation: true,
255/// noise_suppression: true,
256/// auto_gain_control: true,
257/// dtmf_enabled: true,
258/// require_srtp: false,
259/// srtp_profiles: vec![],
260/// rtp_port_start: 10000,
261/// rtp_port_end: 20000,
262/// custom_sdp_attributes: HashMap::new(),
263/// };
264///
265/// assert_eq!(low_bandwidth_config.max_bandwidth_kbps, Some(32));
266/// assert_eq!(low_bandwidth_config.preferred_ptime, Some(30));
267/// assert!(low_bandwidth_config.preferred_codecs.contains(&"G729".to_string()));
268/// ```
269///
270/// ## Custom SDP Attributes
271///
272/// ```rust
273/// use rvoip_client_core::client::config::MediaConfig;
274/// use std::collections::HashMap;
275///
276/// let mut custom_attrs = HashMap::new();
277/// custom_attrs.insert("a".to_string(), "sendrecv".to_string());
278/// custom_attrs.insert("a".to_string(), "rtcp-mux".to_string());
279///
280/// let custom_config = MediaConfig {
281/// preferred_codecs: vec!["opus".to_string()],
282/// custom_sdp_attributes: custom_attrs,
283/// dtmf_enabled: true,
284/// echo_cancellation: true,
285/// noise_suppression: true,
286/// auto_gain_control: true,
287/// max_bandwidth_kbps: None,
288/// require_srtp: false,
289/// srtp_profiles: vec![],
290/// rtp_port_start: 10000,
291/// rtp_port_end: 20000,
292/// preferred_ptime: Some(20),
293/// };
294///
295/// assert!(!custom_config.custom_sdp_attributes.is_empty());
296/// ```
297#[derive(Debug, Clone, Serialize, Deserialize)]
298pub struct MediaConfig {
299 /// Preferred codecs in order of preference
300 pub preferred_codecs: Vec<String>,
301
302 /// Whether DTMF (Dual-Tone Multi-Frequency) signaling is enabled
303 pub dtmf_enabled: bool,
304 /// Whether echo cancellation audio processing is enabled
305 pub echo_cancellation: bool,
306 /// Whether noise suppression audio processing is enabled
307 pub noise_suppression: bool,
308 /// Whether automatic gain control audio processing is enabled
309 pub auto_gain_control: bool,
310
311 /// Maximum bandwidth in kilobits per second (None for unlimited)
312 pub max_bandwidth_kbps: Option<u32>,
313
314 /// Whether SRTP (Secure RTP) encryption is required
315 pub require_srtp: bool,
316 /// List of supported SRTP encryption profiles
317 pub srtp_profiles: Vec<String>,
318
319 /// Starting port number for RTP media streams
320 pub rtp_port_start: u16,
321 /// Ending port number for RTP media streams
322 pub rtp_port_end: u16,
323
324 /// Preferred packetization time in milliseconds
325 pub preferred_ptime: Option<u8>,
326
327 /// Additional custom SDP (Session Description Protocol) attributes
328 pub custom_sdp_attributes: HashMap<String, String>,
329}
330
331impl Default for MediaConfig {
332 fn default() -> Self {
333 Self {
334 preferred_codecs: vec!["opus".to_string(), "PCMU".to_string(), "PCMA".to_string()],
335 dtmf_enabled: true,
336 echo_cancellation: true,
337 noise_suppression: true,
338 auto_gain_control: true,
339 max_bandwidth_kbps: None,
340 require_srtp: false,
341 srtp_profiles: vec![],
342 rtp_port_start: 10000,
343 rtp_port_end: 20000,
344 preferred_ptime: Some(20),
345 custom_sdp_attributes: HashMap::new(),
346 }
347 }
348}
349
350/// Predefined media configuration presets for common use cases
351///
352/// These presets provide optimized configurations for different scenarios,
353/// allowing users to quickly configure appropriate settings without manual tuning.
354/// Each preset can be further customized after application.
355///
356/// # Preset Characteristics
357///
358/// | Preset | Primary Use | Codecs | Bandwidth | Security | Audio Processing |
359/// |--------|-------------|--------|-----------|----------|------------------|
360/// | VoiceOptimized | Phone calls | Opus, PCMU | Standard | None | Full (AEC, NS, AGC) |
361/// | MusicOptimized | Music streaming | Opus | High | None | Minimal |
362/// | LowBandwidth | Mobile/poor networks | G.729, GSM | Low | None | Full |
363/// | Secure | Enterprise | Default | Standard | SRTP | Full |
364/// | Legacy | Compatibility | G.711 | Standard | None | Minimal |
365///
366/// # Examples
367///
368/// ## Voice Calling (Recommended for Phone Calls)
369///
370/// ```rust
371/// use rvoip_client_core::client::config::{MediaPreset, MediaConfig};
372///
373/// let voice_config = MediaConfig::from_preset(MediaPreset::VoiceOptimized);
374///
375/// // Optimized for voice with full audio processing
376/// assert!(voice_config.echo_cancellation);
377/// assert!(voice_config.noise_suppression);
378/// assert!(voice_config.auto_gain_control);
379/// assert_eq!(voice_config.preferred_ptime, Some(20));
380/// assert!(voice_config.preferred_codecs.contains(&"opus".to_string()));
381/// ```
382///
383/// ## Music Streaming (High Quality Audio)
384///
385/// ```rust
386/// use rvoip_client_core::client::config::{MediaPreset, MediaConfig};
387///
388/// let music_config = MediaConfig::from_preset(MediaPreset::MusicOptimized);
389///
390/// // Optimized for music with minimal processing
391/// assert!(!music_config.echo_cancellation); // No echo cancellation for music
392/// assert!(!music_config.noise_suppression); // Preserve audio fidelity
393/// assert!(!music_config.auto_gain_control); // No gain adjustment
394/// assert_eq!(music_config.max_bandwidth_kbps, Some(256)); // Higher bandwidth
395/// assert!(music_config.preferred_codecs.contains(&"opus".to_string()));
396/// ```
397///
398/// ## Low Bandwidth (Mobile/Constrained Networks)
399///
400/// ```rust
401/// use rvoip_client_core::client::config::{MediaPreset, MediaConfig};
402///
403/// let mobile_config = MediaConfig::from_preset(MediaPreset::LowBandwidth);
404///
405/// // Optimized for low bandwidth connections
406/// assert_eq!(mobile_config.max_bandwidth_kbps, Some(32));
407/// assert_eq!(mobile_config.preferred_ptime, Some(30)); // Larger packets
408/// assert!(mobile_config.preferred_codecs.iter().any(|c| c == "G.729"));
409/// assert!(mobile_config.echo_cancellation); // Still enabled for quality
410/// ```
411///
412/// ## Secure Communications (Enterprise)
413///
414/// ```rust
415/// use rvoip_client_core::client::config::{MediaPreset, MediaConfig};
416///
417/// let secure_config = MediaConfig::from_preset(MediaPreset::Secure);
418///
419/// // Requires encryption for all calls
420/// assert!(secure_config.require_srtp);
421/// assert!(!secure_config.srtp_profiles.is_empty());
422/// assert!(secure_config.srtp_profiles.contains(&"AES_CM_128_HMAC_SHA1_80".to_string()));
423/// assert!(secure_config.echo_cancellation); // Full audio processing
424/// ```
425///
426/// ## Legacy Compatibility (Older Systems)
427///
428/// ```rust
429/// use rvoip_client_core::client::config::{MediaPreset, MediaConfig};
430///
431/// let legacy_config = MediaConfig::from_preset(MediaPreset::Legacy);
432///
433/// // Compatible with older SIP systems
434/// assert!(legacy_config.preferred_codecs.contains(&"PCMU".to_string()));
435/// assert!(legacy_config.preferred_codecs.contains(&"PCMA".to_string()));
436/// assert!(!legacy_config.echo_cancellation); // Minimal processing
437/// assert!(!legacy_config.require_srtp); // No encryption requirement
438/// ```
439///
440/// ## Preset Comparison
441///
442/// ```rust
443/// use rvoip_client_core::client::config::{MediaPreset, MediaConfig};
444///
445/// let voice = MediaConfig::from_preset(MediaPreset::VoiceOptimized);
446/// let music = MediaConfig::from_preset(MediaPreset::MusicOptimized);
447/// let mobile = MediaConfig::from_preset(MediaPreset::LowBandwidth);
448///
449/// // Voice has audio processing, music doesn't
450/// assert!(voice.echo_cancellation);
451/// assert!(!music.echo_cancellation);
452///
453/// // Mobile has lower bandwidth than music
454/// assert!(mobile.max_bandwidth_kbps.unwrap() < music.max_bandwidth_kbps.unwrap());
455///
456/// // All have DTMF enabled by default
457/// assert!(voice.dtmf_enabled);
458/// assert!(music.dtmf_enabled);
459/// assert!(mobile.dtmf_enabled);
460/// ```
461#[derive(Debug, Clone, Copy, PartialEq, Eq)]
462pub enum MediaPreset {
463 /// Optimized for voice calls with full audio processing
464 ///
465 /// **Use Case**: Standard phone calls, conferencing, customer service
466 ///
467 /// **Features**:
468 /// - Opus and PCMU codecs for voice clarity
469 /// - Full audio processing (echo cancellation, noise suppression, AGC)
470 /// - 20ms packetization for low latency
471 /// - Standard bandwidth usage
472 ///
473 /// **Best For**: Business phones, softphones, call centers
474 VoiceOptimized,
475
476 /// Optimized for high-quality music streaming
477 ///
478 /// **Use Case**: Music streaming, audio conferencing, broadcast
479 ///
480 /// **Features**:
481 /// - Opus codec for high fidelity
482 /// - Minimal audio processing to preserve quality
483 /// - Higher bandwidth allocation (256 kbps)
484 /// - No echo cancellation or gain control
485 ///
486 /// **Best For**: Music apps, audio streaming, podcast recording
487 MusicOptimized,
488
489 /// Optimized for constrained bandwidth connections
490 ///
491 /// **Use Case**: Mobile networks, satellite links, poor connectivity
492 ///
493 /// **Features**:
494 /// - Low bitrate codecs (G.729, GSM)
495 /// - 32 kbps maximum bandwidth
496 /// - 30ms packetization for efficiency
497 /// - Audio processing enabled for quality
498 ///
499 /// **Best For**: Mobile VoIP, rural networks, international calling
500 LowBandwidth,
501
502 /// Requires SRTP encryption for secure communications
503 ///
504 /// **Use Case**: Enterprise communications, sensitive data, compliance
505 ///
506 /// **Features**:
507 /// - Mandatory SRTP encryption
508 /// - Multiple SRTP profiles supported
509 /// - Full audio processing enabled
510 /// - Standard codec selection
511 ///
512 /// **Best For**: Corporate phones, government, healthcare, legal
513 Secure,
514
515 /// Basic G.711 compatibility for legacy systems
516 ///
517 /// **Use Case**: Interoperability with older PBX systems
518 ///
519 /// **Features**:
520 /// - G.711 (PCMU/PCMA) codecs only
521 /// - Minimal audio processing
522 /// - No encryption requirements
523 /// - Maximum compatibility
524 ///
525 /// **Best For**: Legacy PBX integration, older VoIP systems
526 Legacy,
527}
528
529impl MediaConfig {
530 /// Create a MediaConfig from a predefined preset
531 ///
532 /// This convenience method creates a MediaConfig with optimized settings for
533 /// specific use cases. The resulting configuration can be further customized
534 /// by modifying individual fields after creation.
535 ///
536 /// # Arguments
537 ///
538 /// * `preset` - The MediaPreset to use as a template
539 ///
540 /// # Returns
541 ///
542 /// A MediaConfig configured for the specified use case
543 ///
544 /// # Examples
545 ///
546 /// ## Voice-Optimized Configuration
547 ///
548 /// ```rust
549 /// use rvoip_client_core::client::config::{MediaConfig, MediaPreset};
550 ///
551 /// let config = MediaConfig::from_preset(MediaPreset::VoiceOptimized);
552 ///
553 /// // Verify voice optimization settings
554 /// assert!(config.echo_cancellation);
555 /// assert!(config.noise_suppression);
556 /// assert!(config.auto_gain_control);
557 /// assert_eq!(config.preferred_ptime, Some(20));
558 /// assert!(config.preferred_codecs.contains(&"opus".to_string()));
559 /// ```
560 ///
561 /// ## Secure Configuration for Enterprise
562 ///
563 /// ```rust
564 /// use rvoip_client_core::client::config::{MediaConfig, MediaPreset};
565 ///
566 /// let mut config = MediaConfig::from_preset(MediaPreset::Secure);
567 ///
568 /// // Verify security settings
569 /// assert!(config.require_srtp);
570 /// assert!(!config.srtp_profiles.is_empty());
571 ///
572 /// // Customize for specific enterprise needs
573 /// config.preferred_codecs = vec!["opus".to_string(), "G722".to_string()];
574 /// config.max_bandwidth_kbps = Some(128);
575 ///
576 /// assert_eq!(config.preferred_codecs.len(), 2);
577 /// ```
578 ///
579 /// ## Mobile-Optimized Configuration
580 ///
581 /// ```rust
582 /// use rvoip_client_core::client::config::{MediaConfig, MediaPreset};
583 ///
584 /// let config = MediaConfig::from_preset(MediaPreset::LowBandwidth);
585 ///
586 /// // Verify mobile optimization
587 /// assert_eq!(config.max_bandwidth_kbps, Some(32));
588 /// assert_eq!(config.preferred_ptime, Some(30)); // Efficient packetization
589 /// assert!(config.preferred_codecs.iter().any(|c| c == "G.729"));
590 /// ```
591 ///
592 /// ## Customizing After Preset Application
593 ///
594 /// ```rust
595 /// use rvoip_client_core::client::config::{MediaConfig, MediaPreset};
596 /// use std::collections::HashMap;
597 ///
598 /// let mut config = MediaConfig::from_preset(MediaPreset::VoiceOptimized);
599 ///
600 /// // Add custom SDP attributes
601 /// let mut custom_attrs = HashMap::new();
602 /// custom_attrs.insert("a".to_string(), "rtcp-mux".to_string());
603 /// config.custom_sdp_attributes = custom_attrs;
604 ///
605 /// // Adjust port range for firewall
606 /// config.rtp_port_start = 16384;
607 /// config.rtp_port_end = 32767;
608 ///
609 /// // Verify customizations
610 /// assert!(!config.custom_sdp_attributes.is_empty());
611 /// assert_eq!(config.rtp_port_start, 16384);
612 /// ```
613 pub fn from_preset(preset: MediaPreset) -> Self {
614 match preset {
615 MediaPreset::VoiceOptimized => Self {
616 preferred_codecs: vec!["opus".to_string(), "PCMU".to_string()],
617 echo_cancellation: true,
618 noise_suppression: true,
619 auto_gain_control: true,
620 preferred_ptime: Some(20),
621 ..Default::default()
622 },
623 MediaPreset::MusicOptimized => Self {
624 preferred_codecs: vec!["opus".to_string()],
625 echo_cancellation: false,
626 noise_suppression: false,
627 auto_gain_control: false,
628 max_bandwidth_kbps: Some(256),
629 ..Default::default()
630 },
631 MediaPreset::LowBandwidth => Self {
632 preferred_codecs: vec!["G.729".to_string(), "GSM".to_string(), "PCMU".to_string()],
633 max_bandwidth_kbps: Some(32),
634 preferred_ptime: Some(30),
635 ..Default::default()
636 },
637 MediaPreset::Secure => Self {
638 require_srtp: true,
639 srtp_profiles: vec![
640 "AES_CM_128_HMAC_SHA1_80".to_string(),
641 "AES_CM_128_HMAC_SHA1_32".to_string(),
642 ],
643 ..Default::default()
644 },
645 MediaPreset::Legacy => Self {
646 preferred_codecs: vec!["PCMU".to_string(), "PCMA".to_string()],
647 dtmf_enabled: true,
648 echo_cancellation: false,
649 noise_suppression: false,
650 auto_gain_control: false,
651 ..Default::default()
652 },
653 }
654 }
655}
656
657/// Comprehensive configuration for the SIP client
658///
659/// This structure contains all the configuration needed to set up and run a VoIP client,
660/// including network settings, media configuration, session parameters, and feature flags.
661/// It uses the builder pattern for easy configuration and provides sensible defaults.
662///
663/// # Configuration Categories
664///
665/// ## Network Configuration
666/// - **SIP Address**: Local address for SIP signaling
667/// - **Media Address**: Local address for RTP media streams
668/// - **Domain**: Optional SIP domain for routing
669///
670/// ## Session Management
671/// - **Concurrent Calls**: Maximum number of simultaneous calls
672/// - **Timeouts**: Session and registration timeout values
673/// - **User Agent**: Client identification string
674///
675/// ## Media Settings
676/// - **Audio/Video**: Enable/disable media types
677/// - **MediaConfig**: Detailed codec and processing settings
678///
679/// # Examples
680///
681/// ## Basic Configuration
682///
683/// ```rust
684/// use rvoip_client_core::client::config::ClientConfig;
685///
686/// let config = ClientConfig::new()
687/// .with_sip_addr("127.0.0.1:5060".parse().unwrap())
688/// .with_user_agent("MyApp/1.0".to_string())
689/// .with_max_calls(5);
690///
691/// assert_eq!(config.max_concurrent_calls, 5);
692/// assert_eq!(config.user_agent, "MyApp/1.0");
693/// assert!(config.enable_audio);
694/// ```
695///
696/// ## Enterprise Configuration
697///
698/// ```rust
699/// use rvoip_client_core::client::config::{ClientConfig, MediaPreset};
700///
701/// let enterprise_config = ClientConfig::new()
702/// .with_sip_addr("192.168.1.100:5060".parse().unwrap())
703/// .with_media_addr("192.168.1.100:0".parse().unwrap())
704/// .with_user_agent("EnterprisePhone/2.1".to_string())
705/// .with_media_preset(MediaPreset::Secure)
706/// .with_max_calls(20);
707///
708/// assert_eq!(enterprise_config.max_concurrent_calls, 20);
709/// assert!(enterprise_config.media.require_srtp);
710/// assert_eq!(enterprise_config.local_sip_addr.ip().to_string(), "192.168.1.100");
711/// ```
712///
713/// ## Mobile Configuration
714///
715/// ```rust
716/// use rvoip_client_core::client::config::{ClientConfig, MediaPreset};
717///
718/// let mobile_config = ClientConfig::new()
719/// .with_media_preset(MediaPreset::LowBandwidth)
720/// .with_user_agent("MobileVoIP/1.0".to_string())
721/// .with_max_calls(2);
722///
723/// assert_eq!(mobile_config.max_concurrent_calls, 2);
724/// assert_eq!(mobile_config.media.max_bandwidth_kbps, Some(32));
725/// assert!(mobile_config.media.preferred_codecs.iter().any(|c| c == "G.729"));
726/// ```
727///
728/// ## Custom Media Configuration
729///
730/// ```rust
731/// use rvoip_client_core::client::config::{ClientConfig, MediaConfig};
732/// use std::collections::HashMap;
733///
734/// let custom_media = MediaConfig {
735/// preferred_codecs: vec!["opus".to_string(), "G722".to_string()],
736/// echo_cancellation: true,
737/// noise_suppression: true,
738/// auto_gain_control: false, // Disable AGC
739/// max_bandwidth_kbps: Some(128),
740/// require_srtp: true,
741/// srtp_profiles: vec!["AES_CM_128_HMAC_SHA1_80".to_string()],
742/// rtp_port_start: 16384,
743/// rtp_port_end: 32767,
744/// preferred_ptime: Some(20),
745/// custom_sdp_attributes: HashMap::new(),
746/// dtmf_enabled: true,
747/// };
748///
749/// let config = ClientConfig::new()
750/// .with_media(custom_media)
751/// .with_user_agent("CustomApp/1.0".to_string());
752///
753/// assert!(!config.media.auto_gain_control);
754/// assert!(config.media.require_srtp);
755/// assert_eq!(config.media.rtp_port_start, 16384);
756/// ```
757///
758/// ## Configuration Validation
759///
760/// ```rust
761/// use rvoip_client_core::client::config::ClientConfig;
762///
763/// let config = ClientConfig::new()
764/// .with_sip_addr("0.0.0.0:5060".parse().unwrap())
765/// .with_media_addr("0.0.0.0:0".parse().unwrap());
766///
767/// // Verify default settings
768/// assert!(config.max_concurrent_calls > 0);
769/// assert!(config.session_timeout_secs > 0);
770/// assert!(config.enable_audio);
771/// assert!(!config.enable_video); // Default: video disabled
772/// assert!(config.domain.is_none()); // Default: no domain
773///
774/// // Verify media defaults
775/// assert_eq!(config.media.rtp_port_start, 10000);
776/// assert_eq!(config.media.rtp_port_end, 20000);
777/// assert!(config.media.rtp_port_start < config.media.rtp_port_end);
778/// ```
779#[derive(Debug, Clone, Serialize, Deserialize)]
780pub struct ClientConfig {
781 /// Local SIP bind address
782 pub local_sip_addr: SocketAddr,
783 /// Local media bind address
784 pub local_media_addr: SocketAddr,
785 /// User agent string
786 pub user_agent: String,
787 /// Media configuration
788 pub media: MediaConfig,
789 /// Maximum number of concurrent calls
790 pub max_concurrent_calls: usize,
791 /// Session timeout in seconds
792 pub session_timeout_secs: u64,
793 /// Enable audio processing
794 pub enable_audio: bool,
795 /// Enable video processing (future)
796 pub enable_video: bool,
797 /// SIP domain (optional)
798 pub domain: Option<String>,
799}
800
801impl ClientConfig {
802 /// Create a new client configuration with sensible defaults
803 ///
804 /// Initializes a ClientConfig with default values suitable for most applications.
805 /// The configuration can be customized using the builder pattern methods.
806 ///
807 /// # Default Values
808 ///
809 /// - **SIP Address**: `127.0.0.1:5060` (bind to localhost, standard SIP port)
810 /// - **Media Address**: `127.0.0.1:0` (port 0 = automatic allocation when media session is created)
811 /// - **User Agent**: `rvoip-client-core/0.1.0`
812 /// - **Max Calls**: 10 concurrent calls
813 /// - **Timeout**: 300 seconds (5 minutes)
814 /// - **Audio**: Enabled
815 /// - **Video**: Disabled
816 /// - **Media**: Default MediaConfig with standard codecs
817 ///
818 /// # Returns
819 ///
820 /// A new ClientConfig with default settings
821 ///
822 /// # Examples
823 ///
824 /// ## Basic Usage
825 ///
826 /// ```rust
827 /// use rvoip_client_core::client::config::ClientConfig;
828 ///
829 /// let config = ClientConfig::new();
830 ///
831 /// // Verify defaults
832 /// assert_eq!(config.local_sip_addr.ip().to_string(), "127.0.0.1");
833 /// assert_eq!(config.local_sip_addr.port(), 5060); // Standard SIP port
834 /// assert_eq!(config.max_concurrent_calls, 10);
835 /// assert_eq!(config.session_timeout_secs, 300);
836 /// assert!(config.enable_audio);
837 /// assert!(!config.enable_video);
838 /// assert!(config.domain.is_none());
839 /// ```
840 ///
841 /// ## Immediate Customization
842 ///
843 /// ```rust
844 /// use rvoip_client_core::client::config::ClientConfig;
845 ///
846 /// let config = ClientConfig::new()
847 /// .with_sip_addr("192.168.1.100:5060".parse().unwrap())
848 /// .with_user_agent("MyApp/1.0".to_string())
849 /// .with_max_calls(5);
850 ///
851 /// assert_eq!(config.local_sip_addr.ip().to_string(), "192.168.1.100");
852 /// assert_eq!(config.local_sip_addr.port(), 5060);
853 /// assert_eq!(config.user_agent, "MyApp/1.0");
854 /// assert_eq!(config.max_concurrent_calls, 5);
855 /// ```
856 pub fn new() -> Self {
857 Self {
858 local_sip_addr: "127.0.0.1:5060".parse().unwrap(),
859 local_media_addr: "127.0.0.1:0".parse().unwrap(), // Port 0 = automatic allocation via GlobalPortAllocator
860 user_agent: "rvoip-client-core/0.1.0".to_string(),
861 media: MediaConfig::default(),
862 max_concurrent_calls: 10,
863 session_timeout_secs: 300,
864 enable_audio: true,
865 enable_video: false,
866 domain: None,
867 }
868 }
869
870 /// Set the local SIP bind address for signaling
871 ///
872 /// Configures the local address and port that the SIP client will bind to
873 /// for receiving SIP messages. Use `0.0.0.0` to bind to all interfaces
874 /// and port `0` to let the OS assign an available port.
875 ///
876 /// # Arguments
877 ///
878 /// * `addr` - The socket address to bind SIP signaling to
879 ///
880 /// # Examples
881 ///
882 /// ```rust
883 /// use rvoip_client_core::client::config::ClientConfig;
884 /// use std::net::SocketAddr;
885 ///
886 /// // Bind to specific address and port
887 /// let config = ClientConfig::new()
888 /// .with_sip_addr("192.168.1.100:5060".parse().unwrap());
889 ///
890 /// assert_eq!(config.local_sip_addr.ip().to_string(), "192.168.1.100");
891 /// assert_eq!(config.local_sip_addr.port(), 5060);
892 ///
893 /// // Bind to all interfaces with OS-assigned port
894 /// let config2 = ClientConfig::new()
895 /// .with_sip_addr("0.0.0.0:0".parse().unwrap());
896 ///
897 /// assert_eq!(config2.local_sip_addr.ip().to_string(), "0.0.0.0");
898 /// assert_eq!(config2.local_sip_addr.port(), 0);
899 /// ```
900 pub fn with_sip_addr(mut self, addr: SocketAddr) -> Self {
901 self.local_sip_addr = addr;
902 self
903 }
904
905 /// Set the local media bind address for RTP streams
906 ///
907 /// Configures the local address that will be used for RTP media streams.
908 /// This is typically the same as the SIP address but can be different
909 /// for advanced network configurations.
910 ///
911 /// # Arguments
912 ///
913 /// * `addr` - The socket address to bind RTP media to
914 ///
915 /// # Examples
916 ///
917 /// ```rust
918 /// use rvoip_client_core::client::config::ClientConfig;
919 ///
920 /// // Use same address for SIP and media
921 /// let addr = "192.168.1.100:0".parse().unwrap();
922 /// let config = ClientConfig::new()
923 /// .with_sip_addr(addr)
924 /// .with_media_addr(addr);
925 ///
926 /// assert_eq!(config.local_sip_addr.ip(), config.local_media_addr.ip());
927 ///
928 /// // Use different addresses (advanced networking)
929 /// let config2 = ClientConfig::new()
930 /// .with_sip_addr("10.0.0.100:5060".parse().unwrap())
931 /// .with_media_addr("192.168.1.100:0".parse().unwrap());
932 ///
933 /// assert_ne!(config2.local_sip_addr.ip(), config2.local_media_addr.ip());
934 /// ```
935 pub fn with_media_addr(mut self, addr: SocketAddr) -> Self {
936 self.local_media_addr = addr;
937 self
938 }
939
940 /// Set the User-Agent string for SIP identification
941 ///
942 /// The User-Agent header identifies the client software in SIP messages.
943 /// It's helpful for debugging and server-side logging.
944 ///
945 /// # Arguments
946 ///
947 /// * `user_agent` - String identifying the client application
948 ///
949 /// # Examples
950 ///
951 /// ```rust
952 /// use rvoip_client_core::client::config::ClientConfig;
953 ///
954 /// let config = ClientConfig::new()
955 /// .with_user_agent("MyVoIPApp/2.1.0".to_string());
956 ///
957 /// assert_eq!(config.user_agent, "MyVoIPApp/2.1.0");
958 ///
959 /// // Enterprise naming convention
960 /// let enterprise_config = ClientConfig::new()
961 /// .with_user_agent("CorporatePhone/1.0 (Build 12345)".to_string());
962 ///
963 /// assert!(enterprise_config.user_agent.contains("CorporatePhone"));
964 /// assert!(enterprise_config.user_agent.contains("Build"));
965 /// ```
966 pub fn with_user_agent(mut self, user_agent: String) -> Self {
967 self.user_agent = user_agent;
968 self
969 }
970
971 /// Set preferred audio codecs (convenience method)
972 ///
973 /// This is a convenience method that sets the preferred codec list
974 /// in the media configuration. Codecs are tried in the order specified.
975 ///
976 /// # Arguments
977 ///
978 /// * `codecs` - Vector of codec names in order of preference
979 ///
980 /// # Examples
981 ///
982 /// ```rust
983 /// use rvoip_client_core::client::config::ClientConfig;
984 ///
985 /// let config = ClientConfig::new()
986 /// .with_codecs(vec![
987 /// "opus".to_string(),
988 /// "G722".to_string(),
989 /// "PCMU".to_string()
990 /// ]);
991 ///
992 /// assert_eq!(config.media.preferred_codecs[0], "opus");
993 /// assert_eq!(config.media.preferred_codecs[1], "G722");
994 /// assert_eq!(config.media.preferred_codecs[2], "PCMU");
995 /// assert_eq!(config.media.preferred_codecs.len(), 3);
996 /// ```
997 pub fn with_codecs(mut self, codecs: Vec<String>) -> Self {
998 self.media.preferred_codecs = codecs;
999 self
1000 }
1001
1002 /// Set complete media configuration
1003 ///
1004 /// Replaces the entire media configuration with a custom MediaConfig.
1005 /// This provides full control over all media-related settings.
1006 ///
1007 /// # Arguments
1008 ///
1009 /// * `media` - Custom MediaConfig with desired settings
1010 ///
1011 /// # Examples
1012 ///
1013 /// ```rust
1014 /// use rvoip_client_core::client::config::{ClientConfig, MediaConfig};
1015 /// use std::collections::HashMap;
1016 ///
1017 /// let custom_media = MediaConfig {
1018 /// preferred_codecs: vec!["opus".to_string()],
1019 /// echo_cancellation: false,
1020 /// noise_suppression: true,
1021 /// auto_gain_control: true,
1022 /// max_bandwidth_kbps: Some(64),
1023 /// require_srtp: true,
1024 /// srtp_profiles: vec!["AES_CM_128_HMAC_SHA1_80".to_string()],
1025 /// rtp_port_start: 20000,
1026 /// rtp_port_end: 30000,
1027 /// preferred_ptime: Some(40),
1028 /// custom_sdp_attributes: HashMap::new(),
1029 /// dtmf_enabled: true,
1030 /// };
1031 ///
1032 /// let config = ClientConfig::new().with_media(custom_media);
1033 ///
1034 /// assert!(!config.media.echo_cancellation);
1035 /// assert!(config.media.require_srtp);
1036 /// assert_eq!(config.media.max_bandwidth_kbps, Some(64));
1037 /// assert_eq!(config.media.rtp_port_start, 20000);
1038 /// ```
1039 pub fn with_media(mut self, media: MediaConfig) -> Self {
1040 self.media = media;
1041 self
1042 }
1043
1044 /// Set media configuration using a preset
1045 ///
1046 /// Applies a predefined media configuration optimized for specific use cases.
1047 /// This is a convenience method that replaces the current media config
1048 /// with one generated from the specified preset.
1049 ///
1050 /// # Arguments
1051 ///
1052 /// * `preset` - MediaPreset to use for configuration
1053 ///
1054 /// # Examples
1055 ///
1056 /// ```rust
1057 /// use rvoip_client_core::client::config::{ClientConfig, MediaPreset};
1058 ///
1059 /// // Voice-optimized for phone calls
1060 /// let voice_config = ClientConfig::new()
1061 /// .with_media_preset(MediaPreset::VoiceOptimized);
1062 ///
1063 /// assert!(voice_config.media.echo_cancellation);
1064 /// assert!(voice_config.media.noise_suppression);
1065 ///
1066 /// // Low bandwidth for mobile
1067 /// let mobile_config = ClientConfig::new()
1068 /// .with_media_preset(MediaPreset::LowBandwidth);
1069 ///
1070 /// assert_eq!(mobile_config.media.max_bandwidth_kbps, Some(32));
1071 ///
1072 /// // Secure for enterprise
1073 /// let secure_config = ClientConfig::new()
1074 /// .with_media_preset(MediaPreset::Secure);
1075 ///
1076 /// assert!(secure_config.media.require_srtp);
1077 /// ```
1078 pub fn with_media_preset(mut self, preset: MediaPreset) -> Self {
1079 self.media = MediaConfig::from_preset(preset);
1080 self
1081 }
1082
1083 /// Set maximum number of concurrent calls
1084 ///
1085 /// Configures the maximum number of calls that can be active simultaneously.
1086 /// This helps with resource management and prevents overloading the client.
1087 ///
1088 /// # Arguments
1089 ///
1090 /// * `max_calls` - Maximum number of concurrent calls (must be > 0)
1091 ///
1092 /// # Examples
1093 ///
1094 /// ```rust
1095 /// use rvoip_client_core::client::config::ClientConfig;
1096 ///
1097 /// // Single-line phone
1098 /// let single_line = ClientConfig::new().with_max_calls(1);
1099 /// assert_eq!(single_line.max_concurrent_calls, 1);
1100 ///
1101 /// // Small office setup
1102 /// let office_phone = ClientConfig::new().with_max_calls(5);
1103 /// assert_eq!(office_phone.max_concurrent_calls, 5);
1104 ///
1105 /// // Call center agent
1106 /// let call_center = ClientConfig::new().with_max_calls(20);
1107 /// assert_eq!(call_center.max_concurrent_calls, 20);
1108 ///
1109 /// // Enterprise server
1110 /// let enterprise = ClientConfig::new().with_max_calls(100);
1111 /// assert_eq!(enterprise.max_concurrent_calls, 100);
1112 /// ```
1113 pub fn with_max_calls(mut self, max_calls: usize) -> Self {
1114 self.max_concurrent_calls = max_calls;
1115 self
1116 }
1117
1118 /// Get the preferred codecs list (backwards compatibility)
1119 ///
1120 /// Returns a slice of the preferred codec names in order of preference.
1121 /// This is a convenience method that provides direct access to the
1122 /// codec list without going through the media configuration.
1123 ///
1124 /// # Returns
1125 ///
1126 /// A slice containing codec names in preference order
1127 ///
1128 /// # Examples
1129 ///
1130 /// ```rust
1131 /// use rvoip_client_core::client::config::ClientConfig;
1132 ///
1133 /// let config = ClientConfig::new()
1134 /// .with_codecs(vec![
1135 /// "opus".to_string(),
1136 /// "G722".to_string(),
1137 /// "PCMU".to_string()
1138 /// ]);
1139 ///
1140 /// let codecs = config.preferred_codecs();
1141 /// assert_eq!(codecs.len(), 3);
1142 /// assert_eq!(codecs[0], "opus");
1143 /// assert_eq!(codecs[1], "G722");
1144 /// assert_eq!(codecs[2], "PCMU");
1145 ///
1146 /// // Check for specific codec support
1147 /// assert!(codecs.contains(&"opus".to_string()));
1148 /// assert!(!codecs.contains(&"G729".to_string()));
1149 /// ```
1150 pub fn preferred_codecs(&self) -> &[String] {
1151 &self.media.preferred_codecs
1152 }
1153}
1154
1155impl Default for ClientConfig {
1156 fn default() -> Self {
1157 Self::new()
1158 }
1159}