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);
40 setCheck("audio/dsf", checkDSF);
41 setCheck("audio/eac3", checkEAC3);
42 setCheck("audio/ac3", checkAC3);
43 setCheck("audio/mpeg", checkMP3Ext);
44 setCheck("audio/aac", checkAACExt);
45 }
46 }
47
49 bool begin() {
50 is_first = true;
51 return true;
52 }
53
55 void end() {
56 actual_mime = nullptr;
57 is_first = true;
58 }
59
61 size_t write(uint8_t* data, size_t len) {
63 determineMime(data, len);
64 return len;
65 }
66
68 void setCheck(const char* mime, bool (*check)(uint8_t* start, size_t len),
69 bool isActvie = true) {
70 StrView mime_str{mime};
71 for (int j = 0; j < checks.size(); j++) {
72 Check l_check = checks[j];
73 if (mime_str.equals(l_check.mime)) {
74 l_check.check = check;
75 l_check.is_active = isActvie;
76 return;
77 }
78 }
79 Check check_to_add{mime, check};
80 check_to_add.is_active = isActvie;
81 checks.push_back(check_to_add);
82 LOGI("MimeDetector for %s: %s", mime, isActvie ? "active" : "inactive");
83 }
84
85 // /// Define the callback that will notify about mime changes
86 void setMimeCallback(void (*callback)(const char*)) {
87 TRACED();
88 this->notifyMimeCallback = callback;
89 }
90
93 const char* mime() { return actual_mime; }
94
95 static bool checkAAC(uint8_t* start, size_t len) {
96 return start[0] == 0xFF &&
97 (start[1] == 0xF0 || start[1] == 0xF1 || start[1] == 0xF9);
98 }
99
100 static bool checkAACExt(uint8_t* start, size_t len) {
101 // checking logic for files
102 if (memcmp(start + 4, "ftypM4A", 7) == 0) {
103 return true;
104 }
105 // check for streaming: locate an ADTS synch word ...
106 HeaderParserAAC aac;
107 int pos = aac.findSyncWord((const uint8_t*)start, len);
108 if (pos == -1) {
109 return false;
110 }
111 // ... and confirm it is a valid, consistent ADTS header (this also
112 // rejects MP3 frames since ADTS requires the layer field to be 0, which
113 // real MP3 frames never have)
114 if (!aac.isValid(start + pos, len - pos)) {
115 return false;
116 }
117 // extra safety net: if the same data is unambiguously a valid MP3
118 // stream, it can not be AAC
119 HeaderParserMP3 mp3;
120 if (mp3.isValid(start, len)) {
121 return false;
122 }
123 return true;
124 }
125
126 static bool checkMP3(uint8_t* start, size_t len) {
127 return memcmp(start, "ID3", 3) == 0 ||
128 (start[0] == 0xFF && ((start[1] & 0xE0) == 0xE0));
129 }
130
131 static bool checkMP3Ext(uint8_t* start, size_t len) {
132 HeaderParserMP3 mp3;
133 return mp3.isValid(start, len);
134 }
135
136 static bool checkWAV_ADPCM(uint8_t* start, size_t len) {
137 if (memcmp(start, "RIFF", 4) != 0) return false;
138 WAVHeader header;
139 header.write(start, len);
140 if (!header.parse()) return false;
141 if (header.audioInfo().format == AudioFormat::ADPCM) {
142 return true;
143 }
144 return false;
145 }
146
147 static bool checkWAV(uint8_t* start, size_t len) {
148 return memcmp(start, "RIFF", 4) == 0;
149 }
150
151 static bool checkOGG(uint8_t* start, size_t len) {
152 return memcmp(start, "OggS", 4) == 0;
153 }
154
155 static bool checkFLAC(uint8_t* start, size_t len) {
156 if (len < 4) return false;
157
158 // Native FLAC streams start with "fLaC" (0x664C6143)
159 if (memcmp(start, "fLaC", 4) == 0) {
160 return true;
161 }
162 return false;
163 }
164
165 static bool checkOggFLAC(uint8_t* start, size_t len) {
166 // Check for OGG FLAC - OGG container with FLAC content
167 // OGG starts with "OggS" and may contain FLAC codec
168 if (len >= 32 && memcmp(start, "OggS", 4) == 0) {
169 // Look for FLAC signature within the first OGG page
170 // FLAC in OGG typically has "\x7FFLAC" or "FLAC" as codec identifier
171 for (size_t i = 4; i < len - 4 && i < 64; i++) {
172 if (memcmp(start + i, "FLAC", 4) == 0) {
173 return true;
174 }
175 // Also check for the more specific OGG FLAC header
176 if (i < len - 5 && start[i] == 0x7F &&
177 memcmp(start + i + 1, "FLAC", 4) == 0) {
178 return true;
179 }
180 }
181 }
182
183 return false;
184 }
185
197 static bool checkOggOpus(uint8_t* start, size_t len) {
198 // Check for OGG Opus - OGG container with Opus content
199 // OGG starts with "OggS" and contains Opus codec identifier
200 if (len >= 32 && memcmp(start, "OggS", 4) == 0) {
201 // Look for Opus signature within the first OGG page
202 // Opus in OGG typically has "OpusHead" as the codec identifier
203 for (size_t i = 4; i < len - 8 && i < 80; i++) {
204 if (memcmp(start + i, "OpusHead", 8) == 0) {
205 return true;
206 }
207 }
208 }
209
210 return false;
211 }
212
224 static bool checkOggVorbis(uint8_t* start, size_t len) {
225 // Check for OGG Vorbis - OGG container with Vorbis content
226 // OGG starts with "OggS" and contains Vorbis codec identifier
227 if (len >= 32 && memcmp(start, "OggS", 4) == 0) {
228 // Look for Vorbis signature within the first OGG page
229 // Vorbis in OGG has "\x01vorbis" as the codec identifier
230 for (size_t i = 4; i < len - 7 && i < 80; i++) {
231 if (start[i] == 0x01 && memcmp(start + i + 1, "vorbis", 6) == 0) {
232 return true;
233 }
234 }
235 }
236
237 return false;
238 }
239
241 static bool checkMP2T(uint8_t* start, size_t len) {
242 if (len < 189) return start[0] == 0x47;
243
244 return start[0] == 0x47 && start[188] == 0x47;
245 }
246
248 static bool checkDSF(uint8_t* start, size_t len) {
249 if (len < 4) return false;
250 return memcmp(start, "DSD ", 4) == 0;
251 }
252
264 static bool checkAC3(uint8_t* start, size_t len) {
265 if (len < 7) return false;
266 if (start[0] != 0x0B || start[1] != 0x77) return false;
267 uint8_t bsid = start[5] >> 3;
268 return bsid <= 8;
269 }
270
281 static bool checkEAC3(uint8_t* start, size_t len) {
282 if (len < 7) return false;
283 if (start[0] != 0x0B || start[1] != 0x77) return false;
284 uint8_t bsid = start[5] >> 3;
285 return bsid >= 11 && bsid <= 16;
286 }
287
289 static bool checkSID(uint8_t* start, size_t len) {
290 return memcmp(start, "PSID", 4) == 0 || memcmp(start, "RSID", 4) == 0;
291 }
292
293 static bool checkM4A(uint8_t* header, size_t len) {
294 if (len < 12) return false;
295
296 // prevent false detecton by mp3 files
297 if (memcmp(header, "ID3", 3) == 0) return false;
298
299 // Check for "ftyp" at offset 4
300 if (memcmp(header + 4, "ftyp", 4) != 0) return false;
301
302 // Check for "M4A " or similar major brand
303 if (memcmp(header + 8, "M4A ", 4) == 0 ||
304 memcmp(header + 8, "mp42", 4) == 0 ||
305 memcmp(header + 8, "isom", 4) == 0)
306 return true;
307
308 return false;
309 }
310
312 void setDefaultMime(const char* mime) { default_mime = mime; }
313
315 int setMimeActive(const char* mimePrefix, bool active) {
316 int result = 0;
317 for (auto& check : checks) {
318 if (StrView(check.mime).startsWith(mimePrefix)) {
319 check.is_active = active;
320 LOGI("MimeDetector for %s: %s", check.mime,
321 check.is_active ? "active" : "inactive");
322 result++;
323 }
324 }
325 return result;
326 }
327
329 void clear() {
330 checks.clear();
331 actual_mime = nullptr;
332 is_first = true;
333 }
334
335 protected:
336 struct Check {
337 const char* mime = nullptr;
338 bool (*check)(uint8_t* data, size_t len) = nullptr;
339 bool is_active = true;
340 Check(const char* mime, bool (*check)(uint8_t* data, size_t len)) {
341 this->mime = mime;
342 this->check = check;
343 }
344 Check() = default;
345 };
347 bool is_first = false;
348 const char* actual_mime = nullptr;
349 const char* default_mime = nullptr;
350 void (*notifyMimeCallback)(const char* mime) = nullptr;
351
353 void determineMime(void* data, size_t len) {
354 if (is_first) {
355 actual_mime = lookupMime((uint8_t*)data, len);
356 if (notifyMimeCallback != nullptr && actual_mime != nullptr) {
358 }
359 is_first = false;
360 }
361 }
362
364 const char* lookupMime(uint8_t* data, size_t len) {
365 for (int j = 0; j < checks.size(); j++) {
366 Check l_check = checks[j];
367 if (l_check.is_active && l_check.check(data, len)) {
368 return l_check.mime;
369 }
370 }
371 return default_mime;
372 }
373};
374
375} // 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:289
const char * default_mime
Definition MimeDetector.h:349
void determineMime(void *data, size_t len)
Update the mime type.
Definition MimeDetector.h:353
static bool checkOggFLAC(uint8_t *start, size_t len)
Definition MimeDetector.h:165
static bool checkEAC3(uint8_t *start, size_t len)
Checks for Enhanced AC-3 (E-AC-3 / Dolby Digital Plus) format.
Definition MimeDetector.h:281
bool is_first
Definition MimeDetector.h:347
const char * lookupMime(uint8_t *data, size_t len)
Default logic which supports aac, mp3, wav and ogg.
Definition MimeDetector.h:364
static bool checkFLAC(uint8_t *start, size_t len)
Definition MimeDetector.h:155
static bool checkDSF(uint8_t *start, size_t len)
DSF (DSD Stream File): starts with "DSD " magic.
Definition MimeDetector.h:248
static bool checkAACExt(uint8_t *start, size_t len)
Definition MimeDetector.h:100
bool begin()
Sets is_first to true.
Definition MimeDetector.h:49
size_t write(uint8_t *data, size_t len)
write the header to determine the mime
Definition MimeDetector.h:61
const char * mime()
Definition MimeDetector.h:93
static bool checkMP3(uint8_t *start, size_t len)
Definition MimeDetector.h:126
void(* notifyMimeCallback)(const char *mime)
Definition MimeDetector.h:350
static bool checkM4A(uint8_t *header, size_t len)
Definition MimeDetector.h:293
static bool checkOggOpus(uint8_t *start, size_t len)
Checks for OGG Opus format.
Definition MimeDetector.h:197
int setMimeActive(const char *mimePrefix, bool active)
sets the mime rules active or inactive
Definition MimeDetector.h:315
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:68
void setDefaultMime(const char *mime)
Provides the default mime type if no mime could be determined.
Definition MimeDetector.h:312
void end()
Clears the actual mime and resets the state.
Definition MimeDetector.h:55
void setMimeCallback(void(*callback)(const char *))
Definition MimeDetector.h:86
static bool checkAAC(uint8_t *start, size_t len)
Definition MimeDetector.h:95
Vector< Check > checks
Definition MimeDetector.h:346
static bool checkAC3(uint8_t *start, size_t len)
Checks for AC-3 (Dolby Digital) format.
Definition MimeDetector.h:264
void clear()
Clears all mime rules and resets the actual selection.
Definition MimeDetector.h:329
static bool checkOggVorbis(uint8_t *start, size_t len)
Checks for OGG Vorbis format.
Definition MimeDetector.h:224
static bool checkOGG(uint8_t *start, size_t len)
Definition MimeDetector.h:151
const char * actual_mime
Definition MimeDetector.h:348
static bool checkWAV_ADPCM(uint8_t *start, size_t len)
Definition MimeDetector.h:136
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:241
static bool checkMP3Ext(uint8_t *start, size_t len)
Definition MimeDetector.h:131
static bool checkWAV(uint8_t *start, size_t len)
Definition MimeDetector.h:147
Abstract interface for classes that can provide MIME type information.
Definition AudioTypes.h:552
A simple wrapper to provide string functions on existing allocated char*. If the underlying char* is ...
Definition StrView.h:29
virtual bool startsWith(const char *str)
checks if the string starts with the indicated substring
Definition StrView.h:203
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:89
bool parse()
Call when header data write is complete to parse the data.
Definition CodecWAV.h:109
WAVAudioInfo & audioInfo()
provides the info from the header
Definition CodecWAV.h:163
int write(uint8_t *data, size_t data_len)
Definition CodecWAV.h:97
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition LMSEchoCancellationStream.h:6
Definition MimeDetector.h:336
bool is_active
Definition MimeDetector.h:339
Check(const char *mime, bool(*check)(uint8_t *data, size_t len))
Definition MimeDetector.h:340
bool(* check)(uint8_t *data, size_t len)
Definition MimeDetector.h:338
const char * mime
Definition MimeDetector.h:337
AudioFormat format
Definition CodecWAV.h:38