scuffle_flv/audio/header/
enhanced.rs

1//! Enhanced audio header types and functions.
2
3use 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    /// Different types of audio packets.
15    ///
16    /// Defined by:
17    /// - Enhanced RTMP spec, page 20-21, Enhanced Audio
18    pub enum AudioPacketType(u8) {
19        /// Sequence start.
20        SequenceStart = 0,
21        /// Coded frames.
22        CodedFrames = 1,
23        /// Sequence end.
24        SequenceEnd = 2,
25        /// Multichannel configuration.
26        MultichannelConfig = 4,
27        /// Turns on audio multitrack mode.
28        Multitrack = 5,
29        /// Modifier extension.
30        ModEx = 7,
31    }
32}
33
34nutype_enum! {
35    /// Different types of audio packet modifier extensions.
36    pub enum AudioPacketModExType(u8) {
37        /// Timestamp offset in nanoseconds.
38        TimestampOffsetNano = 0,
39    }
40}
41
42/// This is a helper enum to represent the different types of audio packet modifier extensions.
43#[derive(Debug, Clone, PartialEq)]
44pub enum AudioPacketModEx {
45    /// Timestamp offset in nanoseconds.
46    TimestampOffsetNano {
47        /// The timestamp offset in nanoseconds.
48        audio_timestamp_nano_offset: u32,
49    },
50    /// Any other modifier extension.
51    Other {
52        /// The type of the modifier extension.
53        audio_packet_mod_ex_type: AudioPacketModExType,
54        /// The data of the modifier extension.
55        mod_ex_data: Bytes,
56    },
57}
58
59impl AudioPacketModEx {
60    /// Demux a [`AudioPacketModEx`] from the given reader.
61    ///
62    /// Returns the demuxed [`AudioPacketModEx`] and the next [`AudioPacketType`], if successful.
63    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); // 0b1111_0000
73        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                // too few data bytes for the timestamp offset
78                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    /// Valid FOURCC values for signaling support of audio codecs in the enhanced FourCC pipeline.
103    ///
104    /// Defined by:
105    /// - Enhanced RTMP spec, page 21-22, Enhanced Audio
106    pub enum AudioFourCc([u8; 4]) {
107        /// Dolby AC-3
108        ///
109        /// <https://en.wikipedia.org/wiki/Dolby_Digital>
110        Ac3 = *b"ac-3",
111        /// Dolby Digital Plus (E-AC-3)
112        ///
113        /// <https://en.wikipedia.org/wiki/Dolby_Digital>
114        Eac3 = *b"ec-3",
115        /// Opus audio
116        ///
117        /// <https://opus-codec.org/>
118        Opus = *b"Opus",
119        /// Mp3 audio
120        ///
121        /// <https://en.wikipedia.org/wiki/MP3>
122        Mp3 = *b".mp3",
123        /// Free Lossless Audio Codec
124        ///
125        /// <https://xiph.org/flac/format.html>
126        Flac = *b"fLaC",
127        /// Advanced Audio Coding
128        ///
129        /// <https://en.wikipedia.org/wiki/Advanced_Audio_Coding>
130        Aac = *b"mp4a",
131    }
132}
133
134/// This is a helper enum to represent the different types of multitrack audio.
135#[derive(Debug, Clone, PartialEq)]
136pub enum ExAudioTagHeaderContent {
137    /// Not multitrack.
138    NoMultiTrack(AudioFourCc),
139    /// Multirack with one track.
140    OneTrack(AudioFourCc),
141    /// Multitrack with many tracks of the same codec.
142    ManyTracks(AudioFourCc),
143    /// Multitrack with many tracks of different codecs.
144    ManyTracksManyCodecs,
145    /// Unknown multitrack type.
146    Unknown {
147        /// The type of the multitrack audio.
148        audio_multitrack_type: AvMultitrackType,
149        /// The FOURCC of the audio codec.
150        audio_four_cc: AudioFourCc,
151    },
152}
153
154/// `ExAudioTagHeader`
155///
156/// Defined by:
157/// - Enhanced RTMP spec, page 20-22, Enhanced Audio
158#[derive(Debug, Clone, PartialEq)]
159pub struct ExAudioTagHeader {
160    /// The modifier extensions of the audio packet.
161    ///
162    /// This can be empty if there are no modifier extensions.
163    pub audio_packet_mod_exs: Vec<AudioPacketModEx>,
164    /// The type of the audio packet.
165    pub audio_packet_type: AudioPacketType,
166    /// The content of the audio packet which contains more information about the multitrack configuration.
167    pub content: ExAudioTagHeaderContent,
168}
169
170impl ExAudioTagHeader {
171    /// Demux an [`ExAudioTagHeader`] from the given reader.
172    ///
173    /// This is implemented as per Enhanced RTMP spec, page 20-21, ExAudioTagHeader.
174    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); // 0b1111_0000
188            audio_packet_type = AudioPacketType::from(byte & 0b0000_1111);
189
190            if audio_packet_type == AudioPacketType::Multitrack {
191                // nested multitracks are not allowed
192                return Err(FlvError::NestedMultitracks);
193            }
194
195            let mut audio_four_cc = [0; 4];
196            // Only read the FOURCC if it's not ManyTracksManyCodecs
197            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,  // size 2
246            42, // data
247            42,
248            0b0001_0001, // type 1, next packet 1
249        ];
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, // size 3
267            0, // data
268            0,
269            1,
270            0b0000_0000, // type 0, next packet 0
271        ];
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, // size 2
288            0,
289            1,
290            42, // data
291            42,
292            0b0001_0001, // type 1, next packet 1
293        ];
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, // size 1
311            42,
312            0b0000_0010, // type 0, next packet 2
313        ];
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, // type 0
324            b'm',        // four cc
325            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, // type 7
341            1,           // modex size 2
342            42,          // modex data
343            42,
344            0b0001_0001, // type 1, next packet 1
345            b'm',        // four cc
346            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, // type 5
369            0b0000_0000, // one track, type 0
370            b'm',        // four cc
371            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, // type 5
387            0b0001_0000, // many tracks, type 0
388            b'm',        // four cc
389            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, // type 5
405            0b0010_0000, // many tracks many codecs, type 0
406            b'm',        // four cc, should be ignored
407            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, // type 5
423            0b0011_0000, // unknown, type 0
424            b'm',        // four cc
425            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, // type 5
447            0b0000_0101, // one track, type 5
448        ];
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}