1use bytes::Bytes;
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub enum FlvTagType {
34 Audio,
35 Video,
36 Script,
37}
38
39#[derive(Debug, Clone)]
41pub struct FlvTag {
42 pub tag_type: FlvTagType,
44 pub timestamp: u32,
46 pub data: Bytes,
48}
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52pub enum VideoFrameType {
53 Keyframe = 1,
55 InterFrame = 2,
57 DisposableInterFrame = 3,
59 GeneratedKeyframe = 4,
61 VideoInfoFrame = 5,
63}
64
65impl VideoFrameType {
66 pub fn from_byte(b: u8) -> Option<Self> {
67 match (b >> 4) & 0x0F {
68 1 => Some(VideoFrameType::Keyframe),
69 2 => Some(VideoFrameType::InterFrame),
70 3 => Some(VideoFrameType::DisposableInterFrame),
71 4 => Some(VideoFrameType::GeneratedKeyframe),
72 5 => Some(VideoFrameType::VideoInfoFrame),
73 _ => None,
74 }
75 }
76
77 pub fn is_keyframe(&self) -> bool {
78 matches!(
79 self,
80 VideoFrameType::Keyframe | VideoFrameType::GeneratedKeyframe
81 )
82 }
83}
84
85#[derive(Debug, Clone, Copy, PartialEq, Eq)]
87pub enum VideoCodec {
88 SorensonH263 = 2,
90 ScreenVideo = 3,
92 Vp6 = 4,
94 Vp6Alpha = 5,
96 ScreenVideoV2 = 6,
98 Avc = 7,
100 Hevc = 12,
102 Av1 = 13,
104}
105
106impl VideoCodec {
107 pub fn from_byte(b: u8) -> Option<Self> {
108 match b & 0x0F {
109 2 => Some(VideoCodec::SorensonH263),
110 3 => Some(VideoCodec::ScreenVideo),
111 4 => Some(VideoCodec::Vp6),
112 5 => Some(VideoCodec::Vp6Alpha),
113 6 => Some(VideoCodec::ScreenVideoV2),
114 7 => Some(VideoCodec::Avc),
115 12 => Some(VideoCodec::Hevc),
116 13 => Some(VideoCodec::Av1),
117 _ => None,
118 }
119 }
120}
121
122#[derive(Debug, Clone, Copy, PartialEq, Eq)]
124pub enum AudioFormat {
125 LinearPcmPlatform = 0,
127 Adpcm = 1,
129 Mp3 = 2,
131 LinearPcmLe = 3,
133 Nellymoser16kMono = 4,
135 Nellymoser8kMono = 5,
137 Nellymoser = 6,
139 G711ALaw = 7,
141 G711MuLaw = 8,
143 Aac = 10,
145 Speex = 11,
147 Mp38k = 14,
149 DeviceSpecific = 15,
151}
152
153impl AudioFormat {
154 pub fn from_byte(b: u8) -> Option<Self> {
155 match (b >> 4) & 0x0F {
156 0 => Some(AudioFormat::LinearPcmPlatform),
157 1 => Some(AudioFormat::Adpcm),
158 2 => Some(AudioFormat::Mp3),
159 3 => Some(AudioFormat::LinearPcmLe),
160 4 => Some(AudioFormat::Nellymoser16kMono),
161 5 => Some(AudioFormat::Nellymoser8kMono),
162 6 => Some(AudioFormat::Nellymoser),
163 7 => Some(AudioFormat::G711ALaw),
164 8 => Some(AudioFormat::G711MuLaw),
165 10 => Some(AudioFormat::Aac),
166 11 => Some(AudioFormat::Speex),
167 14 => Some(AudioFormat::Mp38k),
168 15 => Some(AudioFormat::DeviceSpecific),
169 _ => None,
170 }
171 }
172}
173
174#[derive(Debug, Clone, Copy, PartialEq, Eq)]
176pub enum AudioSampleRate {
177 Rate5512 = 0,
178 Rate11025 = 1,
179 Rate22050 = 2,
180 Rate44100 = 3,
181}
182
183impl AudioSampleRate {
184 pub fn from_byte(b: u8) -> Self {
185 match (b >> 2) & 0x03 {
186 0 => AudioSampleRate::Rate5512,
187 1 => AudioSampleRate::Rate11025,
188 2 => AudioSampleRate::Rate22050,
189 _ => AudioSampleRate::Rate44100,
190 }
191 }
192
193 pub fn to_hz(&self) -> u32 {
194 match self {
195 AudioSampleRate::Rate5512 => 5512,
196 AudioSampleRate::Rate11025 => 11025,
197 AudioSampleRate::Rate22050 => 22050,
198 AudioSampleRate::Rate44100 => 44100,
199 }
200 }
201}
202
203impl FlvTag {
204 pub fn video(timestamp: u32, data: Bytes) -> Self {
206 Self {
207 tag_type: FlvTagType::Video,
208 timestamp,
209 data,
210 }
211 }
212
213 pub fn audio(timestamp: u32, data: Bytes) -> Self {
215 Self {
216 tag_type: FlvTagType::Audio,
217 timestamp,
218 data,
219 }
220 }
221
222 pub fn is_video(&self) -> bool {
224 self.tag_type == FlvTagType::Video
225 }
226
227 pub fn is_audio(&self) -> bool {
229 self.tag_type == FlvTagType::Audio
230 }
231
232 pub fn video_frame_type(&self) -> Option<VideoFrameType> {
234 if self.is_video() && !self.data.is_empty() {
235 VideoFrameType::from_byte(self.data[0])
236 } else {
237 None
238 }
239 }
240
241 pub fn video_codec(&self) -> Option<VideoCodec> {
243 if self.is_video() && !self.data.is_empty() {
244 VideoCodec::from_byte(self.data[0])
245 } else {
246 None
247 }
248 }
249
250 pub fn audio_format(&self) -> Option<AudioFormat> {
252 if self.is_audio() && !self.data.is_empty() {
253 AudioFormat::from_byte(self.data[0])
254 } else {
255 None
256 }
257 }
258
259 pub fn is_keyframe(&self) -> bool {
261 self.video_frame_type()
262 .map(|ft| ft.is_keyframe())
263 .unwrap_or(false)
264 }
265
266 pub fn is_avc_sequence_header(&self) -> bool {
268 if self.is_video() && self.data.len() >= 2 {
269 let codec = VideoCodec::from_byte(self.data[0]);
270 codec == Some(VideoCodec::Avc) && self.data[1] == 0
271 } else {
272 false
273 }
274 }
275
276 pub fn is_aac_sequence_header(&self) -> bool {
278 if self.is_audio() && self.data.len() >= 2 {
279 let format = AudioFormat::from_byte(self.data[0]);
280 format == Some(AudioFormat::Aac) && self.data[1] == 0
281 } else {
282 false
283 }
284 }
285
286 pub fn size(&self) -> usize {
288 self.data.len()
289 }
290}
291
292#[cfg(test)]
293mod tests {
294 use super::*;
295
296 #[test]
297 fn test_video_frame_type() {
298 assert_eq!(
300 VideoFrameType::from_byte(0x17),
301 Some(VideoFrameType::Keyframe)
302 );
303 assert_eq!(VideoCodec::from_byte(0x17), Some(VideoCodec::Avc));
304
305 assert_eq!(
307 VideoFrameType::from_byte(0x27),
308 Some(VideoFrameType::InterFrame)
309 );
310 }
311
312 #[test]
313 fn test_avc_sequence_header() {
314 let header = FlvTag::video(0, Bytes::from_static(&[0x17, 0x00, 0x00, 0x00, 0x00]));
315 assert!(header.is_avc_sequence_header());
316 assert!(header.is_keyframe());
317
318 let frame = FlvTag::video(0, Bytes::from_static(&[0x17, 0x01, 0x00, 0x00, 0x00]));
319 assert!(!frame.is_avc_sequence_header());
320 }
321
322 #[test]
323 fn test_aac_sequence_header() {
324 let header = FlvTag::audio(0, Bytes::from_static(&[0xAF, 0x00, 0x12, 0x10]));
325 assert!(header.is_aac_sequence_header());
326
327 let frame = FlvTag::audio(0, Bytes::from_static(&[0xAF, 0x01, 0x21, 0x00]));
328 assert!(!frame.is_aac_sequence_header());
329 }
330
331 #[test]
332 fn test_video_frame_type_all_values() {
333 assert_eq!(
334 VideoFrameType::from_byte(0x10),
335 Some(VideoFrameType::Keyframe)
336 );
337 assert_eq!(
338 VideoFrameType::from_byte(0x20),
339 Some(VideoFrameType::InterFrame)
340 );
341 assert_eq!(
342 VideoFrameType::from_byte(0x30),
343 Some(VideoFrameType::DisposableInterFrame)
344 );
345 assert_eq!(
346 VideoFrameType::from_byte(0x40),
347 Some(VideoFrameType::GeneratedKeyframe)
348 );
349 assert_eq!(
350 VideoFrameType::from_byte(0x50),
351 Some(VideoFrameType::VideoInfoFrame)
352 );
353 assert_eq!(VideoFrameType::from_byte(0x00), None);
354 assert_eq!(VideoFrameType::from_byte(0x60), None);
355 }
356
357 #[test]
358 fn test_video_frame_type_is_keyframe() {
359 assert!(VideoFrameType::Keyframe.is_keyframe());
360 assert!(VideoFrameType::GeneratedKeyframe.is_keyframe());
361 assert!(!VideoFrameType::InterFrame.is_keyframe());
362 assert!(!VideoFrameType::DisposableInterFrame.is_keyframe());
363 assert!(!VideoFrameType::VideoInfoFrame.is_keyframe());
364 }
365
366 #[test]
367 fn test_video_codec_all_values() {
368 assert_eq!(VideoCodec::from_byte(0x02), Some(VideoCodec::SorensonH263));
369 assert_eq!(VideoCodec::from_byte(0x03), Some(VideoCodec::ScreenVideo));
370 assert_eq!(VideoCodec::from_byte(0x04), Some(VideoCodec::Vp6));
371 assert_eq!(VideoCodec::from_byte(0x05), Some(VideoCodec::Vp6Alpha));
372 assert_eq!(VideoCodec::from_byte(0x06), Some(VideoCodec::ScreenVideoV2));
373 assert_eq!(VideoCodec::from_byte(0x07), Some(VideoCodec::Avc));
374 assert_eq!(VideoCodec::from_byte(0x0C), Some(VideoCodec::Hevc));
375 assert_eq!(VideoCodec::from_byte(0x0D), Some(VideoCodec::Av1));
376 assert_eq!(VideoCodec::from_byte(0x00), None);
377 assert_eq!(VideoCodec::from_byte(0x01), None);
378 assert_eq!(VideoCodec::from_byte(0x08), None);
379 }
380
381 #[test]
382 fn test_audio_format_all_values() {
383 assert_eq!(
384 AudioFormat::from_byte(0x00),
385 Some(AudioFormat::LinearPcmPlatform)
386 );
387 assert_eq!(AudioFormat::from_byte(0x10), Some(AudioFormat::Adpcm));
388 assert_eq!(AudioFormat::from_byte(0x20), Some(AudioFormat::Mp3));
389 assert_eq!(AudioFormat::from_byte(0x30), Some(AudioFormat::LinearPcmLe));
390 assert_eq!(
391 AudioFormat::from_byte(0x40),
392 Some(AudioFormat::Nellymoser16kMono)
393 );
394 assert_eq!(
395 AudioFormat::from_byte(0x50),
396 Some(AudioFormat::Nellymoser8kMono)
397 );
398 assert_eq!(AudioFormat::from_byte(0x60), Some(AudioFormat::Nellymoser));
399 assert_eq!(AudioFormat::from_byte(0x70), Some(AudioFormat::G711ALaw));
400 assert_eq!(AudioFormat::from_byte(0x80), Some(AudioFormat::G711MuLaw));
401 assert_eq!(AudioFormat::from_byte(0xA0), Some(AudioFormat::Aac));
402 assert_eq!(AudioFormat::from_byte(0xB0), Some(AudioFormat::Speex));
403 assert_eq!(AudioFormat::from_byte(0xE0), Some(AudioFormat::Mp38k));
404 assert_eq!(
405 AudioFormat::from_byte(0xF0),
406 Some(AudioFormat::DeviceSpecific)
407 );
408 assert_eq!(AudioFormat::from_byte(0x90), None); }
410
411 #[test]
412 fn test_audio_sample_rate() {
413 assert_eq!(AudioSampleRate::from_byte(0x00).to_hz(), 5512);
414 assert_eq!(AudioSampleRate::from_byte(0x04).to_hz(), 11025);
415 assert_eq!(AudioSampleRate::from_byte(0x08).to_hz(), 22050);
416 assert_eq!(AudioSampleRate::from_byte(0x0C).to_hz(), 44100);
417 assert_eq!(AudioSampleRate::from_byte(0xFF).to_hz(), 44100);
419 }
420
421 #[test]
422 fn test_flv_tag_video_construction() {
423 let tag = FlvTag::video(1000, Bytes::from_static(&[0x17, 0x01]));
424 assert!(tag.is_video());
425 assert!(!tag.is_audio());
426 assert_eq!(tag.tag_type, FlvTagType::Video);
427 assert_eq!(tag.timestamp, 1000);
428 }
429
430 #[test]
431 fn test_flv_tag_audio_construction() {
432 let tag = FlvTag::audio(2000, Bytes::from_static(&[0xAF, 0x01]));
433 assert!(tag.is_audio());
434 assert!(!tag.is_video());
435 assert_eq!(tag.tag_type, FlvTagType::Audio);
436 assert_eq!(tag.timestamp, 2000);
437 }
438
439 #[test]
440 fn test_flv_tag_video_frame_type() {
441 let keyframe = FlvTag::video(0, Bytes::from_static(&[0x17, 0x01]));
443 assert_eq!(keyframe.video_frame_type(), Some(VideoFrameType::Keyframe));
444 assert!(keyframe.is_keyframe());
445
446 let interframe = FlvTag::video(0, Bytes::from_static(&[0x27, 0x01]));
448 assert_eq!(
449 interframe.video_frame_type(),
450 Some(VideoFrameType::InterFrame)
451 );
452 assert!(!interframe.is_keyframe());
453
454 let audio = FlvTag::audio(0, Bytes::from_static(&[0xAF, 0x01]));
456 assert!(audio.video_frame_type().is_none());
457 }
458
459 #[test]
460 fn test_flv_tag_video_codec() {
461 let avc = FlvTag::video(0, Bytes::from_static(&[0x17, 0x01]));
463 assert_eq!(avc.video_codec(), Some(VideoCodec::Avc));
464
465 let hevc = FlvTag::video(0, Bytes::from_static(&[0x1C, 0x01]));
467 assert_eq!(hevc.video_codec(), Some(VideoCodec::Hevc));
468
469 let audio = FlvTag::audio(0, Bytes::from_static(&[0xAF]));
471 assert!(audio.video_codec().is_none());
472 }
473
474 #[test]
475 fn test_flv_tag_audio_format() {
476 let aac = FlvTag::audio(0, Bytes::from_static(&[0xAF, 0x01]));
478 assert_eq!(aac.audio_format(), Some(AudioFormat::Aac));
479
480 let mp3 = FlvTag::audio(0, Bytes::from_static(&[0x2F]));
482 assert_eq!(mp3.audio_format(), Some(AudioFormat::Mp3));
483
484 let video = FlvTag::video(0, Bytes::from_static(&[0x17]));
486 assert!(video.audio_format().is_none());
487 }
488
489 #[test]
490 fn test_flv_tag_empty_data() {
491 let empty_video = FlvTag::video(0, Bytes::new());
492 assert!(empty_video.video_frame_type().is_none());
493 assert!(empty_video.video_codec().is_none());
494 assert!(!empty_video.is_keyframe());
495 assert!(!empty_video.is_avc_sequence_header());
496
497 let empty_audio = FlvTag::audio(0, Bytes::new());
498 assert!(empty_audio.audio_format().is_none());
499 assert!(!empty_audio.is_aac_sequence_header());
500 }
501
502 #[test]
503 fn test_flv_tag_size() {
504 let tag = FlvTag::video(0, Bytes::from_static(&[0x17, 0x00, 0x00, 0x00, 0x00]));
505 assert_eq!(tag.size(), 5);
506
507 let empty_tag = FlvTag::audio(0, Bytes::new());
508 assert_eq!(empty_tag.size(), 0);
509 }
510
511 #[test]
512 fn test_is_avc_sequence_header_non_avc() {
513 let hevc = FlvTag::video(0, Bytes::from_static(&[0x1C, 0x00, 0x00, 0x00, 0x00]));
515 assert!(!hevc.is_avc_sequence_header());
516
517 let avc_nalu = FlvTag::video(0, Bytes::from_static(&[0x17, 0x01, 0x00, 0x00, 0x00]));
519 assert!(!avc_nalu.is_avc_sequence_header());
520 }
521
522 #[test]
523 fn test_is_aac_sequence_header_non_aac() {
524 let mp3 = FlvTag::audio(0, Bytes::from_static(&[0x2F, 0x00]));
526 assert!(!mp3.is_aac_sequence_header());
527
528 let aac_raw = FlvTag::audio(0, Bytes::from_static(&[0xAF, 0x01]));
530 assert!(!aac_raw.is_aac_sequence_header());
531 }
532
533 #[test]
534 fn test_flv_tag_type_enum() {
535 assert_eq!(FlvTagType::Audio, FlvTagType::Audio);
536 assert_ne!(FlvTagType::Audio, FlvTagType::Video);
537 assert_ne!(FlvTagType::Video, FlvTagType::Script);
538 }
539
540 #[test]
541 fn test_combined_video_byte() {
542 let tag = FlvTag::video(0, Bytes::from_static(&[0x17]));
545 assert_eq!(tag.video_frame_type(), Some(VideoFrameType::Keyframe));
546 assert_eq!(tag.video_codec(), Some(VideoCodec::Avc));
547
548 let tag = FlvTag::video(0, Bytes::from_static(&[0x27]));
550 assert_eq!(tag.video_frame_type(), Some(VideoFrameType::InterFrame));
551 assert_eq!(tag.video_codec(), Some(VideoCodec::Avc));
552
553 let tag = FlvTag::video(0, Bytes::from_static(&[0x14]));
555 assert_eq!(tag.video_frame_type(), Some(VideoFrameType::Keyframe));
556 assert_eq!(tag.video_codec(), Some(VideoCodec::Vp6));
557 }
558
559 #[test]
560 fn test_short_video_data() {
561 let tag = FlvTag::video(0, Bytes::from_static(&[0x17]));
563 assert!(tag.video_frame_type().is_some());
564 assert!(tag.video_codec().is_some());
565
566 assert!(!tag.is_avc_sequence_header());
568 }
569
570 #[test]
571 fn test_short_audio_data() {
572 let tag = FlvTag::audio(0, Bytes::from_static(&[0xAF]));
574 assert!(tag.audio_format().is_some());
575
576 assert!(!tag.is_aac_sequence_header());
578 }
579}