arduino-audio-tools
Loading...
Searching...
No Matches
MimeDetector.h
Go to the documentation of this file.
1#pragma once
2
7
8namespace audio_tools {
9
30 public:
44 virtual const char* mime() = 0;
45};
46
62class MimeDetector : public MimeSource {
63 public:
64 MimeDetector(bool setupDefault = true) {
65 if (setupDefault) {
66 setCheck("audio/vnd.wave; codecs=ms-adpcm", checkWAV_ADPCM);
67 setCheck("audio/vnd.wave", checkWAV);
68 setCheck("audio/flac", checkFLAC);
69 setCheck("audio/ogg; codecs=flac", checkOggFLAC);
70 setCheck("audio/ogg; codecs=opus", checkOggOpus);
71 setCheck("audio/ogg; codec=vorbis", checkOggVorbis);
72 setCheck("audio/ogg", checkOGG);
73 setCheck("video/MP2T", checkMP2T);
74 setCheck("audio/prs.sid", checkSID);
75 setCheck("audio/m4a", checkM4A, false);
76 setCheck("audio/dsf", checkDSF);
77 setCheck("audio/mpeg", checkMP3Ext);
78 setCheck("audio/aac", checkAACExt);
79 }
80 }
81
83 bool begin() {
84 is_first = true;
85 return true;
86 }
87
89 void end() {
90 actual_mime = nullptr;
91 is_first = true;
92 }
93
95 size_t write(uint8_t* data, size_t len) {
97 determineMime(data, len);
98 return len;
99 }
100
102 void setCheck(const char* mime, bool (*check)(uint8_t* start, size_t len),
103 bool isActvie = true) {
104 StrView mime_str{mime};
105 for (int j = 0; j < checks.size(); j++) {
106 Check l_check = checks[j];
107 if (mime_str.equals(l_check.mime)) {
108 l_check.check = check;
109 l_check.is_active = isActvie;
110 return;
111 }
112 }
113 Check check_to_add{mime, check};
114 check_to_add.is_active = isActvie;
115 checks.push_back(check_to_add);
116 LOGI("MimeDetector for %s: %s", mime, isActvie ? "active" : "inactive");
117 }
118
119 // /// Define the callback that will notify about mime changes
120 void setMimeCallback(void (*callback)(const char*)) {
121 TRACED();
122 this->notifyMimeCallback = callback;
123 }
124
127 const char* mime() { return actual_mime; }
128
129 static bool checkAAC(uint8_t* start, size_t len) {
130 return start[0] == 0xFF &&
131 (start[1] == 0xF0 || start[1] == 0xF1 || start[1] == 0xF9);
132 }
133
134 static bool checkAACExt(uint8_t* start, size_t len) {
135 // checking logic for files
136 if (memcmp(start + 4, "ftypM4A", 7) == 0) {
137 return true;
138 }
139 // check for streaming
140 HeaderParserAAC aac;
141 // it should start with a synch word
142 int pos = aac.findSyncWord((const uint8_t*)start, len);
143 if (pos == -1) {
144 return false;
145 }
146 // make sure that it is not an mp3
147 if (aac.isValid(start + pos, len - pos)) {
148 return false;
149 }
150 return true;
151 }
152
153 static bool checkMP3(uint8_t* start, size_t len) {
154 return memcmp(start, "ID3", 3) == 0 ||
155 (start[0] == 0xFF && ((start[1] & 0xE0) == 0xE0));
156 }
157
158 static bool checkMP3Ext(uint8_t* start, size_t len) {
159 HeaderParserMP3 mp3;
160 return mp3.isValid(start, len);
161 }
162
163 static bool checkWAV_ADPCM(uint8_t* start, size_t len) {
164 if (memcmp(start, "RIFF", 4) != 0) return false;
165 WAVHeader header;
166 header.write(start, len);
167 if (!header.parse()) return false;
168 if (header.audioInfo().format == AudioFormat::ADPCM) {
169 return true;
170 }
171 return false;
172 }
173
174 static bool checkWAV(uint8_t* start, size_t len) {
175 return memcmp(start, "RIFF", 4) == 0;
176 }
177
178 static bool checkOGG(uint8_t* start, size_t len) {
179 return memcmp(start, "OggS", 4) == 0;
180 }
181
182 static bool checkFLAC(uint8_t* start, size_t len) {
183 if (len < 4) return false;
184
185 // Native FLAC streams start with "fLaC" (0x664C6143)
186 if (memcmp(start, "fLaC", 4) == 0) {
187 return true;
188 }
189 return false;
190 }
191
192 static bool checkOggFLAC(uint8_t* start, size_t len) {
193 // Check for OGG FLAC - OGG container with FLAC content
194 // OGG starts with "OggS" and may contain FLAC codec
195 if (len >= 32 && memcmp(start, "OggS", 4) == 0) {
196 // Look for FLAC signature within the first OGG page
197 // FLAC in OGG typically has "\x7FFLAC" or "FLAC" as codec identifier
198 for (size_t i = 4; i < len - 4 && i < 64; i++) {
199 if (memcmp(start + i, "FLAC", 4) == 0) {
200 return true;
201 }
202 // Also check for the more specific OGG FLAC header
203 if (i < len - 5 && start[i] == 0x7F &&
204 memcmp(start + i + 1, "FLAC", 4) == 0) {
205 return true;
206 }
207 }
208 }
209
210 return false;
211 }
212
224 static bool checkOggOpus(uint8_t* start, size_t len) {
225 // Check for OGG Opus - OGG container with Opus content
226 // OGG starts with "OggS" and contains Opus codec identifier
227 if (len >= 32 && memcmp(start, "OggS", 4) == 0) {
228 // Look for Opus signature within the first OGG page
229 // Opus in OGG typically has "OpusHead" as the codec identifier
230 for (size_t i = 4; i < len - 8 && i < 80; i++) {
231 if (memcmp(start + i, "OpusHead", 8) == 0) {
232 return true;
233 }
234 }
235 }
236
237 return false;
238 }
239
251 static bool checkOggVorbis(uint8_t* start, size_t len) {
252 // Check for OGG Vorbis - OGG container with Vorbis content
253 // OGG starts with "OggS" and contains Vorbis codec identifier
254 if (len >= 32 && memcmp(start, "OggS", 4) == 0) {
255 // Look for Vorbis signature within the first OGG page
256 // Vorbis in OGG has "\x01vorbis" as the codec identifier
257 for (size_t i = 4; i < len - 7 && i < 80; i++) {
258 if (start[i] == 0x01 && memcmp(start + i + 1, "vorbis", 6) == 0) {
259 return true;
260 }
261 }
262 }
263
264 return false;
265 }
266
268 static bool checkMP2T(uint8_t* start, size_t len) {
269 if (len < 189) return start[0] == 0x47;
270
271 return start[0] == 0x47 && start[188] == 0x47;
272 }
273
275 static bool checkDSF(uint8_t* start, size_t len) {
276 if (len < 4) return false;
277 return memcmp(start, "DSD ", 4) == 0;
278 }
279
281 static bool checkSID(uint8_t* start, size_t len) {
282 return memcmp(start, "PSID", 4) == 0 || memcmp(start, "RSID", 4) == 0;
283 }
284
285 static bool checkM4A(uint8_t* header, size_t len) {
286 if (len < 12) return false;
287
288 // prevent false detecton by mp3 files
289 if (memcmp(header, "ID3", 3) == 0) return false;
290
291 // Special hack when we position to start of mdat box
292 if (memcmp(header + 4, "mdat", 4) != 0) return true;
293
294 // Check for "ftyp" at offset 4
295 if (memcmp(header + 4, "ftyp", 4) != 0) return false;
296
297 // Check for "M4A " or similar major brand
298 if (memcmp(header + 8, "M4A ", 4) == 0 ||
299 memcmp(header + 8, "mp42", 4) == 0 ||
300 memcmp(header + 8, "isom", 4) == 0)
301 return true;
302
303 return false;
304 }
305
307 void setDefaultMime(const char* mime) { default_mime = mime; }
308
310 int setMimeActive(const char* mimePrefix, bool active) {
311 int result = 0;
312 for (auto& check : checks) {
313 if (StrView(check.mime).startsWith(mimePrefix)) {
314 check.is_active = active;
315 LOGI("MimeDetector for %s: %s", check.mime,
316 check.is_active ? "active" : "inactive");
317 result++;
318 }
319 }
320 return result;
321 }
322
324 void clear() {
325 checks.clear();
326 actual_mime = nullptr;
327 is_first = true;
328 }
329
330 protected:
331 struct Check {
332 const char* mime = nullptr;
333 bool (*check)(uint8_t* data, size_t len) = nullptr;
334 bool is_active = true;
335 Check(const char* mime, bool (*check)(uint8_t* data, size_t len)) {
336 this->mime = mime;
337 this->check = check;
338 }
339 Check() = default;
340 };
342 bool is_first = false;
343 const char* actual_mime = nullptr;
344 const char* default_mime = nullptr;
345 void (*notifyMimeCallback)(const char* mime) = nullptr;
346
348 void determineMime(void* data, size_t len) {
349 if (is_first) {
350 actual_mime = lookupMime((uint8_t*)data, len);
351 if (notifyMimeCallback != nullptr && actual_mime != nullptr) {
353 }
354 is_first = false;
355 }
356 }
357
359 const char* lookupMime(uint8_t* data, size_t len) {
360 for (int j = 0; j < checks.size(); j++) {
361 Check l_check = checks[j];
362 if (l_check.is_active && l_check.check(data, len)) {
363 return l_check.mime;
364 }
365 }
366 return default_mime;
367 }
368};
369
370} // 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:62
static bool checkSID(uint8_t *start, size_t len)
Commodore 64 SID File.
Definition MimeDetector.h:281
const char * default_mime
Definition MimeDetector.h:344
void determineMime(void *data, size_t len)
Update the mime type.
Definition MimeDetector.h:348
static bool checkOggFLAC(uint8_t *start, size_t len)
Definition MimeDetector.h:192
bool is_first
Definition MimeDetector.h:342
const char * lookupMime(uint8_t *data, size_t len)
Default logic which supports aac, mp3, wav and ogg.
Definition MimeDetector.h:359
static bool checkFLAC(uint8_t *start, size_t len)
Definition MimeDetector.h:182
static bool checkDSF(uint8_t *start, size_t len)
DSF (DSD Stream File): starts with "DSD " magic.
Definition MimeDetector.h:275
static bool checkAACExt(uint8_t *start, size_t len)
Definition MimeDetector.h:134
bool begin()
Sets is_first to true.
Definition MimeDetector.h:83
size_t write(uint8_t *data, size_t len)
write the header to determine the mime
Definition MimeDetector.h:95
const char * mime()
Definition MimeDetector.h:127
static bool checkMP3(uint8_t *start, size_t len)
Definition MimeDetector.h:153
void(* notifyMimeCallback)(const char *mime)
Definition MimeDetector.h:345
static bool checkM4A(uint8_t *header, size_t len)
Definition MimeDetector.h:285
static bool checkOggOpus(uint8_t *start, size_t len)
Checks for OGG Opus format.
Definition MimeDetector.h:224
int setMimeActive(const char *mimePrefix, bool active)
sets the mime rules active or inactive
Definition MimeDetector.h:310
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:102
void setDefaultMime(const char *mime)
Provides the default mime type if no mime could be determined.
Definition MimeDetector.h:307
void end()
Clears the actual mime and resets the state.
Definition MimeDetector.h:89
void setMimeCallback(void(*callback)(const char *))
Definition MimeDetector.h:120
static bool checkAAC(uint8_t *start, size_t len)
Definition MimeDetector.h:129
Vector< Check > checks
Definition MimeDetector.h:341
void clear()
Clears all mime rules and resets the actual selection.
Definition MimeDetector.h:324
static bool checkOggVorbis(uint8_t *start, size_t len)
Checks for OGG Vorbis format.
Definition MimeDetector.h:251
static bool checkOGG(uint8_t *start, size_t len)
Definition MimeDetector.h:178
const char * actual_mime
Definition MimeDetector.h:343
static bool checkWAV_ADPCM(uint8_t *start, size_t len)
Definition MimeDetector.h:163
MimeDetector(bool setupDefault=true)
Definition MimeDetector.h:64
static bool checkMP2T(uint8_t *start, size_t len)
MPEG-2 TS Byte Stream Format.
Definition MimeDetector.h:268
static bool checkMP3Ext(uint8_t *start, size_t len)
Definition MimeDetector.h:158
static bool checkWAV(uint8_t *start, size_t len)
Definition MimeDetector.h:174
Abstract interface for classes that can provide MIME type information.
Definition MimeDetector.h:29
virtual const char * mime()=0
Get the MIME type string.
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:67
bool parse()
Call when header data write is complete to parse the data.
Definition CodecWAV.h:78
WAVAudioInfo & audioInfo()
provides the info from the header
Definition CodecWAV.h:125
int write(uint8_t *data, size_t data_len)
Definition CodecWAV.h:73
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition LMSEchoCancellationStream.h:6
Definition MimeDetector.h:331
bool is_active
Definition MimeDetector.h:334
Check(const char *mime, bool(*check)(uint8_t *data, size_t len))
Definition MimeDetector.h:335
bool(* check)(uint8_t *data, size_t len)
Definition MimeDetector.h:333
const char * mime
Definition MimeDetector.h:332
AudioFormat format
Definition CodecWAV.h:27