1use bytes::Bytes;
2
3pub mod aac;
4pub mod chunk;
5pub mod flac;
6pub mod h264;
7pub mod mp3;
8pub mod mp4;
9pub mod webm;
10
11pub const PSI_STREAM_MP3: u8 = 0x04; pub const PSI_STREAM_PRIVATE_DATA: u8 = 0x06;
13pub const PSI_STREAM_H264: u8 = 0x1b; pub const PSI_STREAM_AAC: u8 = 0x0f;
15pub const PSI_STREAM_MPEG4_AAC: u8 = 0x1c;
16pub const PSI_STREAM_AUDIO_OPUS: u8 = 0x9c;
17
18#[derive(Debug, Clone, Copy, PartialEq)]
19pub enum AudioType {
20 Unknown,
21 AAC, M4A, FLAC,
24 MP3,
25 OggOpus,
26 Opus,
27 Wav,
28 WebM,
29}
30
31#[derive(Debug, Clone)]
32pub struct Fmp4 {
33 pub init: Option<Bytes>,
34 pub key: bool,
35 pub data: Bytes,
36 pub duration: u32,
37}
38
39#[derive(Debug, Clone)]
40pub struct AccessUnit {
41 pub key: bool,
42 pub pts: u64,
43 pub dts: u64,
44 pub data: Bytes,
45 pub stream_type: u8,
46 pub id: u64,
47}
48
49pub fn detect_audio(data: &[u8]) -> AudioType {
50 if mp4::is_m4a(data) {
52 return AudioType::M4A;
53 }
54 if let Some(audio_type) = mp4::detect_audio_track(data) {
56 return audio_type;
57 }
58 if flac::is_flac(data) {
59 AudioType::FLAC
60 } else if aac::is_aac(data) {
61 AudioType::AAC
62 } else if webm::is_webm(data) {
63 AudioType::WebM
64 } else if is_ogg_opus(data) {
65 AudioType::OggOpus
66 } else if is_opus(data) {
67 AudioType::Opus
68 } else if is_wav(data) {
69 AudioType::Wav
70 } else if mp3::is_mp3(data) {
71 AudioType::MP3
72 } else {
73 AudioType::Unknown
74 }
75}
76
77pub fn is_mp4(data: &[u8]) -> bool {
78 mp4::is_mp4(data)
79}
80
81pub fn is_webm(data: &[u8]) -> bool {
82 webm::is_webm(data)
83}
84
85fn is_wav(data: &[u8]) -> bool {
86 data.len() >= 12 && &data[0..4] == b"RIFF" && &data[8..12] == b"WAVE"
87}
88
89fn is_opus(data: &[u8]) -> bool {
90 if data.starts_with(b"OggS") {
91 return false;
92 }
93
94 let search_len = data.len().min(64);
95 data[..search_len]
96 .windows(b"OpusHead".len())
97 .any(|w| w == b"OpusHead")
98}
99
100fn is_ogg_opus(data: &[u8]) -> bool {
101 if data.len() < 36 || !data.starts_with(b"OggS") {
102 return false;
103 }
104 let search_len = data.len().min(256);
106 data[..search_len]
107 .windows(b"OpusHead".len())
108 .any(|w| w == b"OpusHead")
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114 use std::fs;
115
116 fn read(path: &str) -> Vec<u8> {
117 fs::read(path).unwrap_or_else(|err| panic!("read {}: {}", path, err))
118 }
119
120 #[test]
121 fn detect_aac_from_testdata() {
122 let data = read("testdata/wav_stereo/A_Tusk_is_used_to_make_costly_gifts.wav.aac");
123 assert_eq!(detect_audio(&data), AudioType::AAC);
124 }
125
126 #[test]
127 fn detect_flac_from_testdata() {
128 let data = read("testdata/flac/A_Tusk_is_used_to_make_costly_gifts.flac");
129 assert_eq!(detect_audio(&data), AudioType::FLAC);
130 }
131
132 #[test]
133 fn detect_mp3_from_testdata() {
134 let data = read("testdata/mp3/A_Tusk_is_used_to_make_costly_gifts.mp3");
135 assert_eq!(detect_audio(&data), AudioType::MP3);
136
137 let (offset, header) = mp3::find_frame(&data).expect("mp3 frame");
138 assert!(offset < data.len());
139 assert!(header.frame_length > 0);
140 }
141
142 #[test]
143 fn detect_mp4_audio_from_testdata() {
144 let data = read("testdata/mp4/heat.mp4");
145 assert_eq!(detect_audio(&data), AudioType::AAC);
146 }
147
148 #[test]
149 fn detect_wav_from_testdata() {
150 let data = read("testdata/wav_stereo/A_Tusk_is_used_to_make_costly_gifts.wav");
151 assert_eq!(detect_audio(&data), AudioType::Wav);
152 }
153
154 #[test]
155 fn detect_opus_from_testdata() {
156 let data = read("testdata/opus/A_Tusk_is_used_to_make_costly_gifts.opus");
157 assert_eq!(detect_audio(&data), AudioType::Opus);
158 }
159
160 #[test]
161 fn detect_ogg_opus_from_testdata() {
162 let data = read("testdata/ogg_opus/A_Tusk_is_used_to_make_costly_gifts.ogg");
163 assert_eq!(detect_audio(&data), AudioType::OggOpus);
164 }
165
166 #[test]
167 fn detect_webm_from_testdata() {
168 let data = read("testdata/webm/A_Tusk_is_used_to_make_costly_gifts.webm");
169 assert_eq!(detect_audio(&data), AudioType::WebM);
170 }
171}