arduino-audio-tools
Loading...
Searching...
No Matches
MimeDetector.h
Go to the documentation of this file.
1#pragma once
2
8
9namespace audio_tools {
10
26class MimeDetector : public MimeSource {
27 public:
28 MimeDetector(bool setupDefault = true) {
29 if (setupDefault) {
30 setCheck("audio/vnd.wave; codecs=ms-adpcm", checkWAV_ADPCM);
31 setCheck("audio/vnd.wave", checkWAV);
32 setCheck("audio/flac", checkFLAC);
33 setCheck("audio/ogg; codecs=flac", checkOggFLAC);
34 setCheck("audio/ogg; codecs=opus", checkOggOpus);
35 setCheck("audio/ogg; codec=vorbis", checkOggVorbis);
36 setCheck("audio/ogg", checkOGG);
37 setCheck("video/MP2T", checkMP2T);
38 setCheck("audio/prs.sid", checkSID);
39 setCheck("audio/m4a", checkM4A, false);
40 setCheck("audio/dsf", checkDSF);
41 setCheck("audio/mpeg", checkMP3Ext);
42 setCheck("audio/aac", checkAACExt);
43 }
44 }
45
47 bool begin() {
48 is_first = true;
49 return true;
50 }
51
53 void end() {
54 actual_mime = nullptr;
55 is_first = true;
56 }
57
59 size_t write(uint8_t* data, size_t len) {
61 determineMime(data, len);
62 return len;
63 }
64
66 void setCheck(const char* mime, bool (*check)(uint8_t* start, size_t len),
67 bool isActvie = true) {
68 StrView mime_str{mime};
69 for (int j = 0; j < checks.size(); j++) {
70 Check l_check = checks[j];
71 if (mime_str.equals(l_check.mime)) {
72 l_check.check = check;
73 l_check.is_active = isActvie;
74 return;
75 }
76 }
77 Check check_to_add{mime, check};
78 check_to_add.is_active = isActvie;
79 checks.push_back(check_to_add);
80 LOGI("MimeDetector for %s: %s", mime, isActvie ? "active" : "inactive");
81 }
82
83 // /// Define the callback that will notify about mime changes
84 void setMimeCallback(void (*callback)(const char*)) {
85 TRACED();
86 this->notifyMimeCallback = callback;
87 }
88
91 const char* mime() { return actual_mime; }
92
93 static bool checkAAC(uint8_t* start, size_t len) {
94 return start[0] == 0xFF &&
95 (start[1] == 0xF0 || start[1] == 0xF1 || start[1] == 0xF9);
96 }
97
98 static bool checkAACExt(uint8_t* start, size_t len) {
99 // checking logic for files
100 if (memcmp(start + 4, "ftypM4A", 7) == 0) {
101 return true;
102 }
103 // check for streaming: locate an ADTS synch word ...
104 HeaderParserAAC aac;
105 int pos = aac.findSyncWord((const uint8_t*)start, len);
106 if (pos == -1) {
107 return false;
108 }
109 // ... and confirm it is a valid, consistent ADTS header (this also
110 // rejects MP3 frames since ADTS requires the layer field to be 0, which
111 // real MP3 frames never have)
112 if (!aac.isValid(start + pos, len - pos)) {
113 return false;
114 }
115 // extra safety net: if the same data is unambiguously a valid MP3
116 // stream, it can not be AAC
117 HeaderParserMP3 mp3;
118 if (mp3.isValid(start, len)) {
119 return false;
120 }
121 return true;
122 }
123
124 static bool checkMP3(uint8_t* start, size_t len) {
125 return memcmp(start, "ID3", 3) == 0 ||
126 (start[0] == 0xFF && ((start[1] & 0xE0) == 0xE0));
127 }
128
129 static bool checkMP3Ext(uint8_t* start, size_t len) {
130 HeaderParserMP3 mp3;
131 return mp3.isValid(start, len);
132 }
133
134 static bool checkWAV_ADPCM(uint8_t* start, size_t len) {
135 if (memcmp(start, "RIFF", 4) != 0) return false;
136 WAVHeader header;
137 header.write(start, len);
138 if (!header.parse()) return false;
139 if (header.audioInfo().format == AudioFormat::ADPCM) {
140 return true;
141 }
142 return false;
143 }
144
145 static bool checkWAV(uint8_t* start, size_t len) {
146 return memcmp(start, "RIFF", 4) == 0;
147 }
148
149 static bool checkOGG(uint8_t* start, size_t len) {
150 return memcmp(start, "OggS", 4) == 0;
151 }
152
153 static bool checkFLAC(uint8_t* start, size_t len) {
154 if (len < 4) return false;
155
156 // Native FLAC streams start with "fLaC" (0x664C6143)
157 if (memcmp(start, "fLaC", 4) == 0) {
158 return true;
159 }
160 return false;
161 }
162
163 static bool checkOggFLAC(uint8_t* start, size_t len) {
164 // Check for OGG FLAC - OGG container with FLAC content
165 // OGG starts with "OggS" and may contain FLAC codec
166 if (len >= 32 && memcmp(start, "OggS", 4) == 0) {
167 // Look for FLAC signature within the first OGG page
168 // FLAC in OGG typically has "\x7FFLAC" or "FLAC" as codec identifier
169 for (size_t i = 4; i < len - 4 && i < 64; i++) {
170 if (memcmp(start + i, "FLAC", 4) == 0) {
171 return true;
172 }
173 // Also check for the more specific OGG FLAC header
174 if (i < len - 5 && start[i] == 0x7F &&
175 memcmp(start + i + 1, "FLAC", 4) == 0) {
176 return true;
177 }
178 }
179 }
180
181 return false;
182 }
183
195 static bool checkOggOpus(uint8_t* start, size_t len) {
196 // Check for OGG Opus - OGG container with Opus content
197 // OGG starts with "OggS" and contains Opus codec identifier
198 if (len >= 32 && memcmp(start, "OggS", 4) == 0) {
199 // Look for Opus signature within the first OGG page
200 // Opus in OGG typically has "OpusHead" as the codec identifier
201 for (size_t i = 4; i < len - 8 && i < 80; i++) {
202 if (memcmp(start + i, "OpusHead", 8) == 0) {
203 return true;
204 }
205 }
206 }
207
208 return false;
209 }
210
222 static bool checkOggVorbis(uint8_t* start, size_t len) {
223 // Check for OGG Vorbis - OGG container with Vorbis content
224 // OGG starts with "OggS" and contains Vorbis codec identifier
225 if (len >= 32 && memcmp(start, "OggS", 4) == 0) {
226 // Look for Vorbis signature within the first OGG page
227 // Vorbis in OGG has "\x01vorbis" as the codec identifier
228 for (size_t i = 4; i < len - 7 && i < 80; i++) {
229 if (start[i] == 0x01 && memcmp(start + i + 1, "vorbis", 6) == 0) {
230 return true;
231 }
232 }
233 }
234
235 return false;
236 }
237
239 static bool checkMP2T(uint8_t* start, size_t len) {
240 if (len < 189) return start[0] == 0x47;
241
242 return start[0] == 0x47 && start[188] == 0x47;
243 }
244
246 static bool checkDSF(uint8_t* start, size_t len) {
247 if (len < 4) return false;
248 return memcmp(start, "DSD ", 4) == 0;
249 }
250
252 static bool checkSID(uint8_t* start, size_t len) {
253 return memcmp(start, "PSID", 4) == 0 || memcmp(start, "RSID", 4) == 0;
254 }
255
256 static bool checkM4A(uint8_t* header, size_t len) {
257 if (len < 12) return false;
258
259 // prevent false detecton by mp3 files
260 if (memcmp(header, "ID3", 3) == 0) return false;
261
262 // Special hack when we position to start of mdat box
263 if (memcmp(header + 4, "mdat", 4) != 0) return true;
264
265 // Check for "ftyp" at offset 4
266 if (memcmp(header + 4, "ftyp", 4) != 0) return false;
267
268 // Check for "M4A " or similar major brand
269 if (memcmp(header + 8, "M4A ", 4) == 0 ||
270 memcmp(header + 8, "mp42", 4) == 0 ||
271 memcmp(header + 8, "isom", 4) == 0)
272 return true;
273
274 return false;
275 }
276
278 void setDefaultMime(const char* mime) { default_mime = mime; }
279
281 int setMimeActive(const char* mimePrefix, bool active) {
282 int result = 0;
283 for (auto& check : checks) {
284 if (StrView(check.mime).startsWith(mimePrefix)) {
285 check.is_active = active;
286 LOGI("MimeDetector for %s: %s", check.mime,
287 check.is_active ? "active" : "inactive");
288 result++;
289 }
290 }
291 return result;
292 }
293
295 void clear() {
296 checks.clear();
297 actual_mime = nullptr;
298 is_first = true;
299 }
300
301 protected:
302 struct Check {
303 const char* mime = nullptr;
304 bool (*check)(uint8_t* data, size_t len) = nullptr;
305 bool is_active = true;
306 Check(const char* mime, bool (*check)(uint8_t* data, size_t len)) {
307 this->mime = mime;
308 this->check = check;
309 }
310 Check() = default;
311 };
313 bool is_first = false;
314 const char* actual_mime = nullptr;
315 const char* default_mime = nullptr;
316 void (*notifyMimeCallback)(const char* mime) = nullptr;
317
319 void determineMime(void* data, size_t len) {
320 if (is_first) {
321 actual_mime = lookupMime((uint8_t*)data, len);
322 if (notifyMimeCallback != nullptr && actual_mime != nullptr) {
324 }
325 is_first = false;
326 }
327 }
328
330 const char* lookupMime(uint8_t* data, size_t len) {
331 for (int j = 0; j < checks.size(); j++) {
332 Check l_check = checks[j];
333 if (l_check.is_active && l_check.check(data, len)) {
334 return l_check.mime;
335 }
336 }
337 return default_mime;
338 }
339};
340
341} // namespace audio_tools
#define TRACED()
Definition AudioLoggerIDF.h:31
#define LOGI(...)
Definition AudioLoggerIDF.h:28
AAC header parser to check if the data is a valid ADTS aac which can extract some relevant audio info...
Definition HeaderParserAAC.h:16
int findSyncWord(const uint8_t *buf, int nBytes, int start=0)
Finds the mp3/aac sync word.
Definition HeaderParserAAC.h:41
bool isValid(const uint8_t *data, int len)
Definition HeaderParserAAC.h:20
MP3 header parser that processes MP3 data incrementally and extracts complete MP3 frames....
Definition HeaderParserMP3.h:25
bool isValid()
Returns true if a valid frame has been detected.
Definition HeaderParserMP3.h:215
Logic to detemine the mime type from the content. By default the following mime types are supported (...
Definition MimeDetector.h:26
static bool checkSID(uint8_t *start, size_t len)
Commodore 64 SID File.
Definition MimeDetector.h:252
const char * default_mime
Definition MimeDetector.h:315
void determineMime(void *data, size_t len)
Update the mime type.
Definition MimeDetector.h:319
static bool checkOggFLAC(uint8_t *start, size_t len)
Definition MimeDetector.h:163
bool is_first
Definition MimeDetector.h:313
const char * lookupMime(uint8_t *data, size_t len)
Default logic which supports aac, mp3, wav and ogg.
Definition MimeDetector.h:330
static bool checkFLAC(uint8_t *start, size_t len)
Definition MimeDetector.h:153
static bool checkDSF(uint8_t *start, size_t len)
DSF (DSD Stream File): starts with "DSD " magic.
Definition MimeDetector.h:246
static bool checkAACExt(uint8_t *start, size_t len)
Definition MimeDetector.h:98
bool begin()
Sets is_first to true.
Definition MimeDetector.h:47
size_t write(uint8_t *data, size_t len)
write the header to determine the mime
Definition MimeDetector.h:59
const char * mime()
Definition MimeDetector.h:91
static bool checkMP3(uint8_t *start, size_t len)
Definition MimeDetector.h:124
void(* notifyMimeCallback)(const char *mime)
Definition MimeDetector.h:316
static bool checkM4A(uint8_t *header, size_t len)
Definition MimeDetector.h:256
static bool checkOggOpus(uint8_t *start, size_t len)
Checks for OGG Opus format.
Definition MimeDetector.h:195
int setMimeActive(const char *mimePrefix, bool active)
sets the mime rules active or inactive
Definition MimeDetector.h:281
void setCheck(const char *mime, bool(*check)(uint8_t *start, size_t len), bool isActvie=true)
adds/updates the checking logic for the indicated mime
Definition MimeDetector.h:66
void setDefaultMime(const char *mime)
Provides the default mime type if no mime could be determined.
Definition MimeDetector.h:278
void end()
Clears the actual mime and resets the state.
Definition MimeDetector.h:53
void setMimeCallback(void(*callback)(const char *))
Definition MimeDetector.h:84
static bool checkAAC(uint8_t *start, size_t len)
Definition MimeDetector.h:93
Vector< Check > checks
Definition MimeDetector.h:312
void clear()
Clears all mime rules and resets the actual selection.
Definition MimeDetector.h:295
static bool checkOggVorbis(uint8_t *start, size_t len)
Checks for OGG Vorbis format.
Definition MimeDetector.h:222
static bool checkOGG(uint8_t *start, size_t len)
Definition MimeDetector.h:149
const char * actual_mime
Definition MimeDetector.h:314
static bool checkWAV_ADPCM(uint8_t *start, size_t len)
Definition MimeDetector.h:134
MimeDetector(bool setupDefault=true)
Definition MimeDetector.h:28
static bool checkMP2T(uint8_t *start, size_t len)
MPEG-2 TS Byte Stream Format.
Definition MimeDetector.h:239
static bool checkMP3Ext(uint8_t *start, size_t len)
Definition MimeDetector.h:129
static bool checkWAV(uint8_t *start, size_t len)
Definition MimeDetector.h:145
Abstract interface for classes that can provide MIME type information.
Definition AudioTypes.h:547
A simple wrapper to provide string functions on existing allocated char*. If the underlying char* is ...
Definition StrView.h:28
virtual bool startsWith(const char *str)
checks if the string starts with the indicated substring
Definition StrView.h:171
Vector implementation which provides the most important methods as defined by std::vector....
Definition Vector.h:21
Parser for Wav header data for details see https://de.wikipedia.org/wiki/RIFF_WAVE.
Definition CodecWAV.h:78
bool parse()
Call when header data write is complete to parse the data.
Definition CodecWAV.h:98
WAVAudioInfo & audioInfo()
provides the info from the header
Definition CodecWAV.h:152
int write(uint8_t *data, size_t data_len)
Definition CodecWAV.h:86
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition LMSEchoCancellationStream.h:6
Definition MimeDetector.h:302
bool is_active
Definition MimeDetector.h:305
Check(const char *mime, bool(*check)(uint8_t *data, size_t len))
Definition MimeDetector.h:306
bool(* check)(uint8_t *data, size_t len)
Definition MimeDetector.h:304
const char * mime
Definition MimeDetector.h:303
AudioFormat format
Definition CodecWAV.h:38