scuffle_flv/audio/header/
enhanced.rs1use std::io::{self, Read};
4
5use byteorder::{BigEndian, ReadBytesExt};
6use bytes::Bytes;
7use nutype_enum::nutype_enum;
8use scuffle_bytes_util::BytesCursorExt;
9
10use crate::common::AvMultitrackType;
11use crate::error::FlvError;
12
13nutype_enum! {
14 pub enum AudioPacketType(u8) {
19 SequenceStart = 0,
21 CodedFrames = 1,
23 SequenceEnd = 2,
25 MultichannelConfig = 4,
27 Multitrack = 5,
29 ModEx = 7,
31 }
32}
33
34nutype_enum! {
35 pub enum AudioPacketModExType(u8) {
37 TimestampOffsetNano = 0,
39 }
40}
41
42#[derive(Debug, Clone, PartialEq)]
44pub enum AudioPacketModEx {
45 TimestampOffsetNano {
47 audio_timestamp_nano_offset: u32,
49 },
50 Other {
52 audio_packet_mod_ex_type: AudioPacketModExType,
54 mod_ex_data: Bytes,
56 },
57}
58
59impl AudioPacketModEx {
60 pub fn demux(reader: &mut io::Cursor<Bytes>) -> Result<(Self, AudioPacketType), FlvError> {
64 let mut mod_ex_data_size = reader.read_u8()? as usize + 1;
65 if mod_ex_data_size == 256 {
66 mod_ex_data_size = reader.read_u16::<BigEndian>()? as usize + 1;
67 }
68
69 let mod_ex_data = reader.extract_bytes(mod_ex_data_size)?;
70
71 let next_byte = reader.read_u8()?;
72 let audio_packet_mod_ex_type = AudioPacketModExType::from(next_byte >> 4); let audio_packet_type = AudioPacketType::from(next_byte & 0b0000_1111);
74
75 if audio_packet_mod_ex_type == AudioPacketModExType::TimestampOffsetNano {
76 if mod_ex_data_size < 3 {
77 return Err(FlvError::InvalidModExData { expected_bytes: 3 });
79 }
80
81 let mod_ex_data = &mut io::Cursor::new(mod_ex_data);
82
83 Ok((
84 Self::TimestampOffsetNano {
85 audio_timestamp_nano_offset: mod_ex_data.read_u24::<BigEndian>()?,
86 },
87 audio_packet_type,
88 ))
89 } else {
90 Ok((
91 Self::Other {
92 audio_packet_mod_ex_type,
93 mod_ex_data,
94 },
95 audio_packet_type,
96 ))
97 }
98 }
99}
100
101nutype_enum! {
102 pub enum AudioFourCc([u8; 4]) {
107 Ac3 = *b"ac-3",
111 Eac3 = *b"ec-3",
115 Opus = *b"Opus",
119 Mp3 = *b".mp3",
123 Flac = *b"fLaC",
127 Aac = *b"mp4a",
131 }
132}
133
134#[derive(Debug, Clone, PartialEq)]
136pub enum ExAudioTagHeaderContent {
137 NoMultiTrack(AudioFourCc),
139 OneTrack(AudioFourCc),
141 ManyTracks(AudioFourCc),
143 ManyTracksManyCodecs,
145 Unknown {
147 audio_multitrack_type: AvMultitrackType,
149 audio_four_cc: AudioFourCc,
151 },
152}
153
154#[derive(Debug, Clone, PartialEq)]
159pub struct ExAudioTagHeader {
160 pub audio_packet_mod_exs: Vec<AudioPacketModEx>,
164 pub audio_packet_type: AudioPacketType,
166 pub content: ExAudioTagHeaderContent,
168}
169
170impl ExAudioTagHeader {
171 pub fn demux(reader: &mut io::Cursor<Bytes>) -> Result<Self, FlvError> {
175 let mut audio_packet_type = AudioPacketType::from(reader.read_u8()? & 0b0000_1111);
176
177 let mut audio_packet_mod_exs = Vec::new();
178
179 while audio_packet_type == AudioPacketType::ModEx {
180 let (mod_ex, next_audio_packet_type) = AudioPacketModEx::demux(reader)?;
181 audio_packet_mod_exs.push(mod_ex);
182 audio_packet_type = next_audio_packet_type;
183 }
184
185 if audio_packet_type == AudioPacketType::Multitrack {
186 let byte = reader.read_u8()?;
187 let audio_multitrack_type = AvMultitrackType::from(byte >> 4); audio_packet_type = AudioPacketType::from(byte & 0b0000_1111);
189
190 if audio_packet_type == AudioPacketType::Multitrack {
191 return Err(FlvError::NestedMultitracks);
193 }
194
195 let mut audio_four_cc = [0; 4];
196 if audio_multitrack_type != AvMultitrackType::ManyTracksManyCodecs {
198 reader.read_exact(&mut audio_four_cc)?;
199 }
200
201 let content = match audio_multitrack_type {
202 AvMultitrackType::OneTrack => ExAudioTagHeaderContent::OneTrack(AudioFourCc::from(audio_four_cc)),
203 AvMultitrackType::ManyTracks => ExAudioTagHeaderContent::ManyTracks(AudioFourCc::from(audio_four_cc)),
204 AvMultitrackType::ManyTracksManyCodecs => ExAudioTagHeaderContent::ManyTracksManyCodecs,
205 _ => ExAudioTagHeaderContent::Unknown {
206 audio_multitrack_type,
207 audio_four_cc: AudioFourCc::from(audio_four_cc),
208 },
209 };
210
211 Ok(Self {
212 audio_packet_mod_exs,
213 audio_packet_type,
214 content,
215 })
216 } else {
217 let mut audio_four_cc = [0; 4];
218 reader.read_exact(&mut audio_four_cc)?;
219 let audio_four_cc = AudioFourCc::from(audio_four_cc);
220
221 Ok(Self {
222 audio_packet_mod_exs,
223 audio_packet_type,
224 content: ExAudioTagHeaderContent::NoMultiTrack(audio_four_cc),
225 })
226 }
227 }
228}
229
230#[cfg(test)]
231#[cfg_attr(all(test, coverage_nightly), coverage(off))]
232mod tests {
233 use bytes::Bytes;
234
235 use super::AudioPacketModEx;
236 use crate::audio::header::enhanced::{
237 AudioFourCc, AudioPacketModExType, AudioPacketType, ExAudioTagHeader, ExAudioTagHeaderContent,
238 };
239 use crate::common::AvMultitrackType;
240 use crate::error::FlvError;
241
242 #[test]
243 fn small_mod_ex_demux() {
244 let data = &[
245 1, 42, 42,
248 0b0001_0001, ];
250
251 let (mod_ex, next_packet) = AudioPacketModEx::demux(&mut std::io::Cursor::new(Bytes::from_static(data))).unwrap();
252
253 assert_eq!(
254 mod_ex,
255 AudioPacketModEx::Other {
256 audio_packet_mod_ex_type: AudioPacketModExType(1),
257 mod_ex_data: Bytes::from_static(&[42, 42])
258 }
259 );
260 assert_eq!(next_packet, AudioPacketType::CodedFrames);
261 }
262
263 #[test]
264 fn timestamp_offset_mod_ex_demux() {
265 let data = &[
266 2, 0, 0,
269 1,
270 0b0000_0000, ];
272
273 let (mod_ex, next_packet) = AudioPacketModEx::demux(&mut std::io::Cursor::new(Bytes::from_static(data))).unwrap();
274
275 assert_eq!(
276 mod_ex,
277 AudioPacketModEx::TimestampOffsetNano {
278 audio_timestamp_nano_offset: 1
279 },
280 );
281 assert_eq!(next_packet, AudioPacketType::SequenceStart);
282 }
283
284 #[test]
285 fn big_mod_ex_demux() {
286 let data = &[
287 255, 0,
289 1,
290 42, 42,
292 0b0001_0001, ];
294
295 let (mod_ex, next_packet) = AudioPacketModEx::demux(&mut std::io::Cursor::new(Bytes::from_static(data))).unwrap();
296
297 assert_eq!(
298 mod_ex,
299 AudioPacketModEx::Other {
300 audio_packet_mod_ex_type: AudioPacketModExType(1),
301 mod_ex_data: Bytes::from_static(&[42, 42])
302 }
303 );
304 assert_eq!(next_packet, AudioPacketType::CodedFrames);
305 }
306
307 #[test]
308 fn mod_ex_demux_error() {
309 let data = &[
310 0, 42,
312 0b0000_0010, ];
314
315 let err = AudioPacketModEx::demux(&mut std::io::Cursor::new(Bytes::from_static(data))).unwrap_err();
316
317 assert!(matches!(err, FlvError::InvalidModExData { expected_bytes: 3 },));
318 }
319
320 #[test]
321 fn minimal_header() {
322 let data = &[
323 0b0000_0000, b'm', b'p',
326 b'4',
327 b'a',
328 ];
329
330 let header = ExAudioTagHeader::demux(&mut std::io::Cursor::new(Bytes::from_static(data))).unwrap();
331
332 assert_eq!(header.audio_packet_mod_exs.len(), 0);
333 assert_eq!(header.audio_packet_type, AudioPacketType::SequenceStart);
334 assert_eq!(header.content, ExAudioTagHeaderContent::NoMultiTrack(AudioFourCc::Aac));
335 }
336
337 #[test]
338 fn header_small_mod_ex() {
339 let data = &[
340 0b0000_0111, 1, 42, 42,
344 0b0001_0001, b'm', b'p',
347 b'4',
348 b'a',
349 ];
350
351 let header = ExAudioTagHeader::demux(&mut std::io::Cursor::new(Bytes::from_static(data))).unwrap();
352
353 assert_eq!(header.audio_packet_mod_exs.len(), 1);
354 assert_eq!(
355 header.audio_packet_mod_exs[0],
356 AudioPacketModEx::Other {
357 audio_packet_mod_ex_type: AudioPacketModExType(1),
358 mod_ex_data: Bytes::from_static(&[42, 42])
359 }
360 );
361 assert_eq!(header.audio_packet_type, AudioPacketType::CodedFrames);
362 assert_eq!(header.content, ExAudioTagHeaderContent::NoMultiTrack(AudioFourCc::Aac));
363 }
364
365 #[test]
366 fn header_multitrack_one_track() {
367 let data = &[
368 0b0000_0101, 0b0000_0000, b'm', b'p',
372 b'4',
373 b'a',
374 ];
375
376 let header = ExAudioTagHeader::demux(&mut std::io::Cursor::new(Bytes::from_static(data))).unwrap();
377
378 assert_eq!(header.audio_packet_mod_exs.len(), 0);
379 assert_eq!(header.audio_packet_type, AudioPacketType::SequenceStart);
380 assert_eq!(header.content, ExAudioTagHeaderContent::OneTrack(AudioFourCc::Aac));
381 }
382
383 #[test]
384 fn header_multitrack_many_tracks() {
385 let data = &[
386 0b0000_0101, 0b0001_0000, b'm', b'p',
390 b'4',
391 b'a',
392 ];
393
394 let header = ExAudioTagHeader::demux(&mut std::io::Cursor::new(Bytes::from_static(data))).unwrap();
395
396 assert_eq!(header.audio_packet_mod_exs.len(), 0);
397 assert_eq!(header.audio_packet_type, AudioPacketType::SequenceStart);
398 assert_eq!(header.content, ExAudioTagHeaderContent::ManyTracks(AudioFourCc::Aac));
399 }
400
401 #[test]
402 fn header_multitrack_many_tracks_many_codecs() {
403 let data = &[
404 0b0000_0101, 0b0010_0000, b'm', b'p',
408 b'4',
409 b'a',
410 ];
411
412 let header = ExAudioTagHeader::demux(&mut std::io::Cursor::new(Bytes::from_static(data))).unwrap();
413
414 assert_eq!(header.audio_packet_mod_exs.len(), 0);
415 assert_eq!(header.audio_packet_type, AudioPacketType::SequenceStart);
416 assert_eq!(header.content, ExAudioTagHeaderContent::ManyTracksManyCodecs);
417 }
418
419 #[test]
420 fn header_multitrack_unknown() {
421 let data = &[
422 0b0000_0101, 0b0011_0000, b'm', b'p',
426 b'4',
427 b'a',
428 ];
429
430 let header = ExAudioTagHeader::demux(&mut std::io::Cursor::new(Bytes::from_static(data))).unwrap();
431
432 assert_eq!(header.audio_packet_mod_exs.len(), 0);
433 assert_eq!(header.audio_packet_type, AudioPacketType::SequenceStart);
434 assert_eq!(
435 header.content,
436 ExAudioTagHeaderContent::Unknown {
437 audio_multitrack_type: AvMultitrackType(3),
438 audio_four_cc: AudioFourCc::Aac
439 }
440 );
441 }
442
443 #[test]
444 fn nested_multitrack_error() {
445 let data = &[
446 0b0000_0101, 0b0000_0101, ];
449
450 let err = ExAudioTagHeader::demux(&mut std::io::Cursor::new(Bytes::from_static(data))).unwrap_err();
451 assert!(matches!(err, FlvError::NestedMultitracks));
452 }
453}