oxide_browser/
audio_format.rs1pub const AUDIO_FORMAT_UNKNOWN: u32 = 0;
5pub const AUDIO_FORMAT_WAV: u32 = 1;
7pub const AUDIO_FORMAT_MP3: u32 = 2;
9pub const AUDIO_FORMAT_OGG: u32 = 3;
11pub const AUDIO_FORMAT_FLAC: u32 = 4;
13
14pub const AUDIO_HTTP_ACCEPT: &str = "audio/wav,audio/wave,audio/x-wav;q=0.9,audio/mpeg,audio/mp3;q=0.9,audio/ogg,audio/flac,audio/*;q=0.5,*/*;q=0.1";
16
17fn skip_id3_prefix(data: &[u8]) -> usize {
18 if data.len() >= 10 && data[0..3] == *b"ID3" {
19 let size = ((data[6] as usize) << 21)
20 | ((data[7] as usize) << 14)
21 | ((data[8] as usize) << 7)
22 | (data[9] as usize);
23 10 + size
24 } else {
25 0
26 }
27}
28
29fn mp3_sync_at(data: &[u8], offset: usize) -> bool {
30 if offset + 2 > data.len() {
31 return false;
32 }
33 let b0 = data[offset];
34 let b1 = data[offset + 1];
35 (b0 == 0xFF) && ((b1 & 0xE0) == 0xE0)
36}
37
38pub fn sniff_audio_format(data: &[u8]) -> u32 {
40 if data.len() < 4 {
41 return AUDIO_FORMAT_UNKNOWN;
42 }
43 if data.len() >= 12 && &data[0..4] == b"RIFF" && &data[8..12] == b"WAVE" {
44 return AUDIO_FORMAT_WAV;
45 }
46 if &data[0..4] == b"fLaC" {
47 return AUDIO_FORMAT_FLAC;
48 }
49 if &data[0..4] == b"OggS" {
50 return AUDIO_FORMAT_OGG;
51 }
52 let off = skip_id3_prefix(data);
53 if off < data.len() && mp3_sync_at(data, off) {
54 return AUDIO_FORMAT_MP3;
55 }
56 if mp3_sync_at(data, 0) {
57 return AUDIO_FORMAT_MP3;
58 }
59 AUDIO_FORMAT_UNKNOWN
60}
61
62pub fn mime_to_audio_format(mime: &str) -> u32 {
64 let s = mime
65 .split(';')
66 .next()
67 .unwrap_or(mime)
68 .trim()
69 .to_ascii_lowercase();
70 match s.as_str() {
71 "audio/wav" | "audio/wave" | "audio/x-wav" => AUDIO_FORMAT_WAV,
72 "audio/mpeg" | "audio/mp3" => AUDIO_FORMAT_MP3,
73 "audio/ogg" | "application/ogg" => AUDIO_FORMAT_OGG,
74 "audio/flac" | "audio/x-flac" => AUDIO_FORMAT_FLAC,
75 _ => AUDIO_FORMAT_UNKNOWN,
76 }
77}
78
79pub fn is_likely_non_audio_document(mime: &str) -> bool {
81 let s = mime
82 .split(';')
83 .next()
84 .unwrap_or(mime)
85 .trim()
86 .to_ascii_lowercase();
87 s.starts_with("text/html")
88 || s.starts_with("text/plain")
89 || s.starts_with("text/css")
90 || s == "application/json"
91 || s == "application/javascript"
92 || s.starts_with("application/xml")
93 || s.starts_with("text/")
94}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99
100 #[test]
101 fn sniff_wav() {
102 let mut b = vec![0u8; 12];
103 b[0..4].copy_from_slice(b"RIFF");
104 b[8..12].copy_from_slice(b"WAVE");
105 assert_eq!(sniff_audio_format(&b), AUDIO_FORMAT_WAV);
106 }
107
108 #[test]
109 fn sniff_flac() {
110 assert_eq!(sniff_audio_format(b"fLaCxxxx"), AUDIO_FORMAT_FLAC);
111 }
112
113 #[test]
114 fn sniff_ogg() {
115 assert_eq!(sniff_audio_format(b"OggSxxxx"), AUDIO_FORMAT_OGG);
116 }
117
118 #[test]
119 fn sniff_mp3_sync() {
120 let b = [0xFF, 0xFB, 0x90, 0x00];
121 assert_eq!(sniff_audio_format(&b), AUDIO_FORMAT_MP3);
122 }
123
124 #[test]
125 fn mime_maps() {
126 assert_eq!(mime_to_audio_format("audio/mpeg"), AUDIO_FORMAT_MP3);
127 assert_eq!(
128 mime_to_audio_format("audio/ogg; codecs=vorbis"),
129 AUDIO_FORMAT_OGG
130 );
131 assert!(is_likely_non_audio_document("text/html; charset=utf-8"));
132 }
133}