rtmp_rs/protocol/
enhanced.rs1use std::collections::HashMap;
9
10use crate::media::fourcc::{AudioFourCc, VideoFourCc};
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
16pub struct CapsEx(u32);
17
18impl CapsEx {
19 pub const RECONNECT: u32 = 0x01;
21 pub const MULTITRACK: u32 = 0x02;
23 pub const MODEX: u32 = 0x04;
25 pub const TIMESTAMP_NANO_OFFSET: u32 = 0x08;
27
28 pub const fn empty() -> Self {
30 Self(0)
31 }
32
33 pub const fn from_bits(bits: u32) -> Self {
35 Self(bits)
36 }
37
38 pub const fn bits(&self) -> u32 {
40 self.0
41 }
42
43 pub const fn contains(&self, flag: u32) -> bool {
45 (self.0 & flag) != 0
46 }
47
48 pub fn insert(&mut self, flag: u32) {
50 self.0 |= flag;
51 }
52
53 pub fn remove(&mut self, flag: u32) {
55 self.0 &= !flag;
56 }
57
58 pub const fn intersection(&self, other: &Self) -> Self {
60 Self(self.0 & other.0)
61 }
62
63 pub const fn supports_reconnect(&self) -> bool {
65 self.contains(Self::RECONNECT)
66 }
67
68 pub const fn supports_multitrack(&self) -> bool {
70 self.contains(Self::MULTITRACK)
71 }
72
73 pub const fn supports_modex(&self) -> bool {
75 self.contains(Self::MODEX)
76 }
77
78 pub const fn supports_timestamp_nano_offset(&self) -> bool {
80 self.contains(Self::TIMESTAMP_NANO_OFFSET)
81 }
82}
83
84#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
88pub struct FourCcCapability(u32);
89
90impl FourCcCapability {
91 pub const CAN_DECODE: u32 = 0x01;
93 pub const CAN_ENCODE: u32 = 0x02;
95 pub const CAN_FORWARD: u32 = 0x04;
97
98 pub const fn empty() -> Self {
100 Self(0)
101 }
102
103 pub const fn from_bits(bits: u32) -> Self {
105 Self(bits)
106 }
107
108 pub const fn bits(&self) -> u32 {
110 self.0
111 }
112
113 pub const fn decode() -> Self {
115 Self(Self::CAN_DECODE)
116 }
117
118 pub const fn encode() -> Self {
120 Self(Self::CAN_ENCODE)
121 }
122
123 pub const fn forward() -> Self {
125 Self(Self::CAN_FORWARD)
126 }
127
128 pub const fn full() -> Self {
130 Self(Self::CAN_DECODE | Self::CAN_ENCODE | Self::CAN_FORWARD)
131 }
132
133 pub const fn can_decode(&self) -> bool {
135 (self.0 & Self::CAN_DECODE) != 0
136 }
137
138 pub const fn can_encode(&self) -> bool {
140 (self.0 & Self::CAN_ENCODE) != 0
141 }
142
143 pub const fn can_forward(&self) -> bool {
145 (self.0 & Self::CAN_FORWARD) != 0
146 }
147}
148
149#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
153pub struct VideoFunctionFlags(u32);
154
155impl VideoFunctionFlags {
156 pub const CLIENT_SEEK: u32 = 1;
158
159 pub const fn empty() -> Self {
161 Self(0)
162 }
163
164 pub const fn from_bits(bits: u32) -> Self {
166 Self(bits)
167 }
168
169 pub const fn bits(&self) -> u32 {
171 self.0
172 }
173
174 pub const fn supports_client_seek(&self) -> bool {
176 (self.0 & Self::CLIENT_SEEK) != 0
177 }
178}
179
180#[derive(Debug, Clone, Default)]
185pub struct EnhancedCapabilities {
186 pub enabled: bool,
188
189 pub caps_ex: CapsEx,
191
192 pub video_codecs: HashMap<VideoFourCc, FourCcCapability>,
194
195 pub audio_codecs: HashMap<AudioFourCc, FourCcCapability>,
197
198 pub video_function: VideoFunctionFlags,
200}
201
202impl EnhancedCapabilities {
203 pub fn new() -> Self {
205 Self::default()
206 }
207
208 pub fn with_defaults() -> Self {
210 let mut caps = Self {
211 enabled: true,
212 caps_ex: CapsEx::from_bits(CapsEx::MODEX),
213 video_codecs: HashMap::new(),
214 audio_codecs: HashMap::new(),
215 video_function: VideoFunctionFlags::empty(),
216 };
217
218 caps.video_codecs
220 .insert(VideoFourCc::Avc, FourCcCapability::forward());
221 caps.video_codecs
222 .insert(VideoFourCc::Hevc, FourCcCapability::forward());
223 caps.video_codecs
224 .insert(VideoFourCc::Av1, FourCcCapability::forward());
225 caps.video_codecs
226 .insert(VideoFourCc::Vp9, FourCcCapability::forward());
227
228 caps.audio_codecs
230 .insert(AudioFourCc::Aac, FourCcCapability::forward());
231 caps.audio_codecs
232 .insert(AudioFourCc::Opus, FourCcCapability::forward());
233
234 caps
235 }
236
237 pub fn supports_video_codec(&self, codec: VideoFourCc) -> bool {
239 self.video_codecs.contains_key(&codec)
240 }
241
242 pub fn supports_audio_codec(&self, codec: AudioFourCc) -> bool {
244 self.audio_codecs.contains_key(&codec)
245 }
246
247 pub fn video_codec_capability(&self, codec: VideoFourCc) -> Option<FourCcCapability> {
249 self.video_codecs.get(&codec).copied()
250 }
251
252 pub fn audio_codec_capability(&self, codec: AudioFourCc) -> Option<FourCcCapability> {
254 self.audio_codecs.get(&codec).copied()
255 }
256
257 pub fn supports_multitrack(&self) -> bool {
259 self.enabled && self.caps_ex.supports_multitrack()
260 }
261
262 pub fn supports_reconnect(&self) -> bool {
264 self.enabled && self.caps_ex.supports_reconnect()
265 }
266
267 pub fn intersect(&self, other: &Self) -> Self {
271 if !self.enabled || !other.enabled {
272 return Self::new();
273 }
274
275 let mut result = Self {
276 enabled: true,
277 caps_ex: self.caps_ex.intersection(&other.caps_ex),
278 video_codecs: HashMap::new(),
279 audio_codecs: HashMap::new(),
280 video_function: VideoFunctionFlags::from_bits(
281 self.video_function.bits() & other.video_function.bits(),
282 ),
283 };
284
285 for (codec, self_cap) in &self.video_codecs {
287 if let Some(other_cap) = other.video_codecs.get(codec) {
288 let common = FourCcCapability::from_bits(self_cap.bits() & other_cap.bits());
289 if common.bits() != 0 {
290 result.video_codecs.insert(*codec, common);
291 }
292 }
293 }
294
295 for (codec, self_cap) in &self.audio_codecs {
297 if let Some(other_cap) = other.audio_codecs.get(codec) {
298 let common = FourCcCapability::from_bits(self_cap.bits() & other_cap.bits());
299 if common.bits() != 0 {
300 result.audio_codecs.insert(*codec, common);
301 }
302 }
303 }
304
305 result
306 }
307}
308
309#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
313pub enum EnhancedRtmpMode {
314 #[default]
316 Auto,
317
318 LegacyOnly,
320
321 EnhancedOnly,
323}
324
325#[cfg(test)]
326mod tests {
327 use super::*;
328
329 #[test]
330 fn test_caps_ex_empty() {
331 let caps = CapsEx::empty();
332 assert_eq!(caps.bits(), 0);
333 assert!(!caps.supports_reconnect());
334 assert!(!caps.supports_multitrack());
335 assert!(!caps.supports_modex());
336 assert!(!caps.supports_timestamp_nano_offset());
337 }
338
339 #[test]
340 fn test_caps_ex_flags() {
341 let caps = CapsEx::from_bits(CapsEx::RECONNECT | CapsEx::MULTITRACK);
342 assert!(caps.supports_reconnect());
343 assert!(caps.supports_multitrack());
344 assert!(!caps.supports_modex());
345 assert!(!caps.supports_timestamp_nano_offset());
346 }
347
348 #[test]
349 fn test_caps_ex_insert_remove() {
350 let mut caps = CapsEx::empty();
351
352 caps.insert(CapsEx::RECONNECT);
353 assert!(caps.supports_reconnect());
354
355 caps.insert(CapsEx::MODEX);
356 assert!(caps.supports_modex());
357
358 caps.remove(CapsEx::RECONNECT);
359 assert!(!caps.supports_reconnect());
360 assert!(caps.supports_modex());
361 }
362
363 #[test]
364 fn test_caps_ex_intersection() {
365 let client = CapsEx::from_bits(CapsEx::RECONNECT | CapsEx::MULTITRACK | CapsEx::MODEX);
366 let server = CapsEx::from_bits(CapsEx::MULTITRACK | CapsEx::MODEX);
367
368 let common = client.intersection(&server);
369 assert!(!common.supports_reconnect());
370 assert!(common.supports_multitrack());
371 assert!(common.supports_modex());
372 }
373
374 #[test]
375 fn test_fourcc_capability_flags() {
376 let cap = FourCcCapability::full();
377 assert!(cap.can_decode());
378 assert!(cap.can_encode());
379 assert!(cap.can_forward());
380
381 let forward_only = FourCcCapability::forward();
382 assert!(!forward_only.can_decode());
383 assert!(!forward_only.can_encode());
384 assert!(forward_only.can_forward());
385 }
386
387 #[test]
388 fn test_fourcc_capability_from_bits() {
389 let cap = FourCcCapability::from_bits(
390 FourCcCapability::CAN_DECODE | FourCcCapability::CAN_FORWARD,
391 );
392 assert!(cap.can_decode());
393 assert!(!cap.can_encode());
394 assert!(cap.can_forward());
395 }
396
397 #[test]
398 fn test_enhanced_capabilities_default() {
399 let caps = EnhancedCapabilities::new();
400 assert!(!caps.enabled);
401 assert!(caps.video_codecs.is_empty());
402 assert!(caps.audio_codecs.is_empty());
403 }
404
405 #[test]
406 fn test_enhanced_capabilities_with_defaults() {
407 let caps = EnhancedCapabilities::with_defaults();
408 assert!(caps.enabled);
409 assert!(caps.supports_video_codec(VideoFourCc::Avc));
410 assert!(caps.supports_video_codec(VideoFourCc::Hevc));
411 assert!(caps.supports_video_codec(VideoFourCc::Av1));
412 assert!(caps.supports_audio_codec(AudioFourCc::Aac));
413 assert!(caps.supports_audio_codec(AudioFourCc::Opus));
414
415 assert!(caps.supports_video_codec(VideoFourCc::Vp9));
417 }
418
419 #[test]
420 fn test_enhanced_capabilities_codec_lookup() {
421 let caps = EnhancedCapabilities::with_defaults();
422
423 let avc_cap = caps.video_codec_capability(VideoFourCc::Avc).unwrap();
424 assert!(avc_cap.can_forward());
425
426 let vp8_cap = caps.video_codec_capability(VideoFourCc::Vp8);
427 assert!(vp8_cap.is_none() || vp8_cap.unwrap().can_forward());
429 }
430
431 #[test]
432 fn test_enhanced_capabilities_intersect() {
433 let mut client = EnhancedCapabilities::with_defaults();
434 client.caps_ex = CapsEx::from_bits(CapsEx::RECONNECT | CapsEx::MODEX);
435 client
436 .video_codecs
437 .insert(VideoFourCc::Avc, FourCcCapability::full());
438 client
439 .video_codecs
440 .insert(VideoFourCc::Vp8, FourCcCapability::decode());
441
442 let mut server = EnhancedCapabilities::with_defaults();
443 server.caps_ex = CapsEx::from_bits(CapsEx::MODEX);
444 server
445 .video_codecs
446 .insert(VideoFourCc::Avc, FourCcCapability::forward());
447 let common = client.intersect(&server);
450 assert!(common.enabled);
451 assert!(!common.caps_ex.supports_reconnect()); assert!(common.caps_ex.supports_modex()); let avc_cap = common.video_codec_capability(VideoFourCc::Avc).unwrap();
456 assert!(avc_cap.can_forward());
457 assert!(!avc_cap.can_encode()); assert!(!common.supports_video_codec(VideoFourCc::Vp8));
461 }
462
463 #[test]
464 fn test_enhanced_capabilities_intersect_disabled() {
465 let client = EnhancedCapabilities::with_defaults();
466 let server = EnhancedCapabilities::new(); let common = client.intersect(&server);
469 assert!(!common.enabled);
470 }
471
472 #[test]
473 fn test_enhanced_rtmp_mode_default() {
474 let mode = EnhancedRtmpMode::default();
475 assert_eq!(mode, EnhancedRtmpMode::Auto);
476 }
477
478 #[test]
479 fn test_video_function_flags() {
480 let flags = VideoFunctionFlags::from_bits(VideoFunctionFlags::CLIENT_SEEK);
481 assert!(flags.supports_client_seek());
482
483 let empty = VideoFunctionFlags::empty();
484 assert!(!empty.supports_client_seek());
485 }
486
487 #[test]
488 fn test_multitrack_support() {
489 let mut caps = EnhancedCapabilities::with_defaults();
490 assert!(!caps.supports_multitrack()); caps.caps_ex.insert(CapsEx::MULTITRACK);
493 assert!(caps.supports_multitrack());
494
495 caps.enabled = false;
496 assert!(!caps.supports_multitrack()); }
498
499 #[test]
500 fn test_reconnect_support() {
501 let mut caps = EnhancedCapabilities::with_defaults();
502 assert!(!caps.supports_reconnect());
503
504 caps.caps_ex.insert(CapsEx::RECONNECT);
505 assert!(caps.supports_reconnect());
506 }
507}