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}