1use std::net::SocketAddr;
4use std::time::Duration;
5
6use crate::media::fourcc::{AudioFourCc, VideoFourCc};
7use crate::protocol::constants::*;
8use crate::protocol::enhanced::{CapsEx, EnhancedRtmpMode, FourCcCapability};
9
10#[derive(Debug, Clone)]
12pub struct ServerConfig {
13 pub bind_addr: SocketAddr,
15
16 pub max_connections: usize,
18
19 pub chunk_size: u32,
21
22 pub window_ack_size: u32,
24
25 pub peer_bandwidth: u32,
27
28 pub connection_timeout: Duration,
30
31 pub idle_timeout: Duration,
33
34 pub tcp_nodelay: bool,
36
37 pub tcp_recv_buffer: usize,
39
40 pub tcp_send_buffer: usize,
42
43 pub read_buffer_size: usize,
45
46 pub write_buffer_size: usize,
48
49 pub gop_buffer_enabled: bool,
51
52 pub gop_buffer_max_size: usize,
54
55 pub stats_interval: Duration,
57
58 pub enhanced_rtmp: EnhancedRtmpMode,
60
61 pub enhanced_capabilities: EnhancedServerCapabilities,
63}
64
65#[derive(Debug, Clone)]
69pub struct EnhancedServerCapabilities {
70 pub reconnect: bool,
72
73 pub multitrack: bool,
75
76 pub modex: bool,
78
79 pub video_codecs: Vec<(VideoFourCc, FourCcCapability)>,
81
82 pub audio_codecs: Vec<(AudioFourCc, FourCcCapability)>,
84}
85
86impl Default for EnhancedServerCapabilities {
87 fn default() -> Self {
88 Self {
89 reconnect: false,
90 multitrack: false,
91 modex: true, video_codecs: vec![
93 (VideoFourCc::Avc, FourCcCapability::forward()),
94 (VideoFourCc::Hevc, FourCcCapability::forward()),
95 (VideoFourCc::Av1, FourCcCapability::forward()),
96 (VideoFourCc::Vp9, FourCcCapability::forward()),
97 ],
98 audio_codecs: vec![
99 (AudioFourCc::Aac, FourCcCapability::forward()),
100 (AudioFourCc::Opus, FourCcCapability::forward()),
101 ],
102 }
103 }
104}
105
106impl EnhancedServerCapabilities {
107 pub fn minimal() -> Self {
109 Self {
110 reconnect: false,
111 multitrack: false,
112 modex: true,
113 video_codecs: vec![],
114 audio_codecs: vec![],
115 }
116 }
117
118 pub fn with_video_codec(mut self, codec: VideoFourCc, cap: FourCcCapability) -> Self {
120 self.video_codecs.push((codec, cap));
121 self
122 }
123
124 pub fn with_audio_codec(mut self, codec: AudioFourCc, cap: FourCcCapability) -> Self {
126 self.audio_codecs.push((codec, cap));
127 self
128 }
129
130 pub fn with_reconnect(mut self) -> Self {
132 self.reconnect = true;
133 self
134 }
135
136 pub fn with_multitrack(mut self) -> Self {
138 self.multitrack = true;
139 self
140 }
141
142 pub fn to_caps_ex(&self) -> CapsEx {
144 let mut caps = CapsEx::empty();
145 if self.reconnect {
146 caps.insert(CapsEx::RECONNECT);
147 }
148 if self.multitrack {
149 caps.insert(CapsEx::MULTITRACK);
150 }
151 if self.modex {
152 caps.insert(CapsEx::MODEX);
153 }
154 caps
155 }
156
157 pub fn to_enhanced_capabilities(&self) -> crate::protocol::enhanced::EnhancedCapabilities {
159 use crate::protocol::enhanced::EnhancedCapabilities;
160
161 let mut caps = EnhancedCapabilities {
162 enabled: true,
163 caps_ex: self.to_caps_ex(),
164 video_codecs: std::collections::HashMap::new(),
165 audio_codecs: std::collections::HashMap::new(),
166 video_function: crate::protocol::enhanced::VideoFunctionFlags::empty(),
167 };
168
169 for (codec, capability) in &self.video_codecs {
170 caps.video_codecs.insert(*codec, *capability);
171 }
172
173 for (codec, capability) in &self.audio_codecs {
174 caps.audio_codecs.insert(*codec, *capability);
175 }
176
177 caps
178 }
179}
180
181impl Default for ServerConfig {
182 fn default() -> Self {
183 Self {
184 bind_addr: "0.0.0.0:1935".parse().unwrap(),
185 max_connections: 0, chunk_size: RECOMMENDED_CHUNK_SIZE,
187 window_ack_size: DEFAULT_WINDOW_ACK_SIZE,
188 peer_bandwidth: DEFAULT_PEER_BANDWIDTH,
189 connection_timeout: Duration::from_secs(10),
190 idle_timeout: Duration::from_secs(60),
191 tcp_nodelay: true, tcp_recv_buffer: 0,
193 tcp_send_buffer: 0,
194 read_buffer_size: 64 * 1024, write_buffer_size: 64 * 1024,
196 gop_buffer_enabled: true,
197 gop_buffer_max_size: 4 * 1024 * 1024, stats_interval: Duration::from_secs(5),
199 enhanced_rtmp: EnhancedRtmpMode::Auto,
200 enhanced_capabilities: EnhancedServerCapabilities::default(),
201 }
202 }
203}
204
205impl ServerConfig {
206 pub fn with_addr(addr: SocketAddr) -> Self {
208 Self {
209 bind_addr: addr,
210 ..Default::default()
211 }
212 }
213
214 pub fn bind(mut self, addr: SocketAddr) -> Self {
216 self.bind_addr = addr;
217 self
218 }
219
220 pub fn max_connections(mut self, max: usize) -> Self {
222 self.max_connections = max;
223 self
224 }
225
226 pub fn chunk_size(mut self, size: u32) -> Self {
228 self.chunk_size = size.min(MAX_CHUNK_SIZE);
229 self
230 }
231
232 pub fn disable_gop_buffer(mut self) -> Self {
234 self.gop_buffer_enabled = false;
235 self
236 }
237
238 pub fn connection_timeout(mut self, timeout: Duration) -> Self {
240 self.connection_timeout = timeout;
241 self
242 }
243
244 pub fn idle_timeout(mut self, timeout: Duration) -> Self {
246 self.idle_timeout = timeout;
247 self
248 }
249
250 pub fn enhanced_rtmp(mut self, mode: EnhancedRtmpMode) -> Self {
256 self.enhanced_rtmp = mode;
257 self
258 }
259
260 pub fn enhanced_capabilities(mut self, caps: EnhancedServerCapabilities) -> Self {
262 self.enhanced_capabilities = caps;
263 self
264 }
265}
266
267#[cfg(test)]
268mod tests {
269 use super::*;
270
271 #[test]
272 fn test_default_config() {
273 let config = ServerConfig::default();
274
275 assert_eq!(config.bind_addr.port(), 1935);
276 assert_eq!(config.max_connections, 0);
277 assert_eq!(config.chunk_size, RECOMMENDED_CHUNK_SIZE);
278 assert_eq!(config.window_ack_size, DEFAULT_WINDOW_ACK_SIZE);
279 assert_eq!(config.peer_bandwidth, DEFAULT_PEER_BANDWIDTH);
280 assert!(config.tcp_nodelay);
281 assert!(config.gop_buffer_enabled);
282 assert_eq!(config.enhanced_rtmp, EnhancedRtmpMode::Auto);
283 }
284
285 #[test]
286 fn test_with_addr() {
287 let addr: SocketAddr = "127.0.0.1:1936".parse().unwrap();
288 let config = ServerConfig::with_addr(addr);
289
290 assert_eq!(config.bind_addr.port(), 1936);
291 }
292
293 #[test]
294 fn test_builder_bind() {
295 let addr: SocketAddr = "0.0.0.0:8080".parse().unwrap();
296 let config = ServerConfig::default().bind(addr);
297
298 assert_eq!(config.bind_addr, addr);
299 }
300
301 #[test]
302 fn test_builder_max_connections() {
303 let config = ServerConfig::default().max_connections(100);
304
305 assert_eq!(config.max_connections, 100);
306 }
307
308 #[test]
309 fn test_builder_chunk_size() {
310 let config = ServerConfig::default().chunk_size(8192);
311
312 assert_eq!(config.chunk_size, 8192);
313 }
314
315 #[test]
316 fn test_builder_chunk_size_capped() {
317 let config = ServerConfig::default().chunk_size(u32::MAX);
319
320 assert_eq!(config.chunk_size, MAX_CHUNK_SIZE);
321 }
322
323 #[test]
324 fn test_builder_disable_gop_buffer() {
325 let config = ServerConfig::default().disable_gop_buffer();
326
327 assert!(!config.gop_buffer_enabled);
328 }
329
330 #[test]
331 fn test_builder_connection_timeout() {
332 let config = ServerConfig::default().connection_timeout(Duration::from_secs(30));
333
334 assert_eq!(config.connection_timeout, Duration::from_secs(30));
335 }
336
337 #[test]
338 fn test_builder_idle_timeout() {
339 let config = ServerConfig::default().idle_timeout(Duration::from_secs(120));
340
341 assert_eq!(config.idle_timeout, Duration::from_secs(120));
342 }
343
344 #[test]
345 fn test_builder_chaining() {
346 let addr: SocketAddr = "127.0.0.1:1935".parse().unwrap();
347 let config = ServerConfig::default()
348 .bind(addr)
349 .max_connections(50)
350 .chunk_size(4096)
351 .connection_timeout(Duration::from_secs(5))
352 .idle_timeout(Duration::from_secs(30))
353 .disable_gop_buffer();
354
355 assert_eq!(config.bind_addr, addr);
356 assert_eq!(config.max_connections, 50);
357 assert_eq!(config.chunk_size, 4096);
358 assert_eq!(config.connection_timeout, Duration::from_secs(5));
359 assert_eq!(config.idle_timeout, Duration::from_secs(30));
360 assert!(!config.gop_buffer_enabled);
361 }
362
363 #[test]
364 fn test_enhanced_rtmp_mode() {
365 let config = ServerConfig::default().enhanced_rtmp(EnhancedRtmpMode::EnhancedOnly);
366 assert_eq!(config.enhanced_rtmp, EnhancedRtmpMode::EnhancedOnly);
367
368 let config = ServerConfig::default().enhanced_rtmp(EnhancedRtmpMode::LegacyOnly);
369 assert_eq!(config.enhanced_rtmp, EnhancedRtmpMode::LegacyOnly);
370 }
371
372 #[test]
373 fn test_enhanced_server_capabilities_default() {
374 let caps = EnhancedServerCapabilities::default();
375
376 assert!(!caps.reconnect);
377 assert!(!caps.multitrack);
378 assert!(caps.modex);
379 assert!(!caps.video_codecs.is_empty());
380 assert!(!caps.audio_codecs.is_empty());
381
382 assert!(caps
384 .video_codecs
385 .iter()
386 .any(|(c, _)| *c == VideoFourCc::Avc));
387 assert!(caps
388 .video_codecs
389 .iter()
390 .any(|(c, _)| *c == VideoFourCc::Hevc));
391 assert!(caps
392 .video_codecs
393 .iter()
394 .any(|(c, _)| *c == VideoFourCc::Av1));
395
396 assert!(caps
398 .audio_codecs
399 .iter()
400 .any(|(c, _)| *c == AudioFourCc::Aac));
401 assert!(caps
402 .audio_codecs
403 .iter()
404 .any(|(c, _)| *c == AudioFourCc::Opus));
405 }
406
407 #[test]
408 fn test_enhanced_server_capabilities_minimal() {
409 let caps = EnhancedServerCapabilities::minimal();
410
411 assert!(!caps.reconnect);
412 assert!(!caps.multitrack);
413 assert!(caps.modex);
414 assert!(caps.video_codecs.is_empty());
415 assert!(caps.audio_codecs.is_empty());
416 }
417
418 #[test]
419 fn test_enhanced_server_capabilities_builder() {
420 let caps = EnhancedServerCapabilities::minimal()
421 .with_video_codec(VideoFourCc::Hevc, FourCcCapability::full())
422 .with_audio_codec(AudioFourCc::Opus, FourCcCapability::decode())
423 .with_reconnect()
424 .with_multitrack();
425
426 assert!(caps.reconnect);
427 assert!(caps.multitrack);
428 assert_eq!(caps.video_codecs.len(), 1);
429 assert_eq!(caps.audio_codecs.len(), 1);
430
431 let (codec, cap) = &caps.video_codecs[0];
432 assert_eq!(*codec, VideoFourCc::Hevc);
433 assert!(cap.can_decode());
434 assert!(cap.can_encode());
435 assert!(cap.can_forward());
436 }
437
438 #[test]
439 fn test_enhanced_server_capabilities_to_caps_ex() {
440 let caps = EnhancedServerCapabilities::default();
441 let caps_ex = caps.to_caps_ex();
442
443 assert!(!caps_ex.supports_reconnect());
444 assert!(!caps_ex.supports_multitrack());
445 assert!(caps_ex.supports_modex());
446
447 let caps_full = EnhancedServerCapabilities::default()
448 .with_reconnect()
449 .with_multitrack();
450 let caps_ex = caps_full.to_caps_ex();
451
452 assert!(caps_ex.supports_reconnect());
453 assert!(caps_ex.supports_multitrack());
454 assert!(caps_ex.supports_modex());
455 }
456
457 #[test]
458 fn test_config_with_enhanced_capabilities() {
459 let caps = EnhancedServerCapabilities::minimal()
460 .with_video_codec(VideoFourCc::Av1, FourCcCapability::forward());
461
462 let config = ServerConfig::default()
463 .enhanced_rtmp(EnhancedRtmpMode::Auto)
464 .enhanced_capabilities(caps);
465
466 assert_eq!(config.enhanced_rtmp, EnhancedRtmpMode::Auto);
467 assert_eq!(config.enhanced_capabilities.video_codecs.len(), 1);
468 }
469}