arduino-audio-tools
All Classes Namespaces Files Functions Variables Typedefs Enumerations Friends Macros Modules Pages
MimeDetector.h
1#pragma once
2
3#include "AudioTools/AudioCodecs/HeaderParserAAC.h"
4#include "AudioTools/AudioCodecs/HeaderParserMP3.h"
5#include "AudioTools/CoreAudio/AudioBasic/StrView.h"
6#include "AudioTools/AudioCodecs/CodecWAV.h"
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/mpeg", checkMP3Ext);
77 setCheck("audio/aac", checkAACExt);
78 }
79 }
80
82 bool begin() {
83 is_first = true;
84 return true;
85 }
86
88 void end() {
89 actual_mime = nullptr;
90 is_first = true;
91 }
92
94 size_t write(uint8_t* data, size_t len) {
95 actual_mime = default_mime;
96 determineMime(data, len);
97 return len;
98 }
99
101 void setCheck(const char* mime, bool (*check)(uint8_t* start, size_t len),
102 bool isActvie = true) {
103 StrView mime_str{mime};
104 for (int j = 0; j < checks.size(); j++) {
105 Check l_check = checks[j];
106 if (mime_str.equals(l_check.mime)) {
107 l_check.check = check;
108 l_check.is_active = isActvie;
109 return;
110 }
111 }
112 Check check_to_add{mime, check};
113 check_to_add.is_active = isActvie;
114 checks.push_back(check_to_add);
115 LOGI("MimeDetector for %s: %s", mime, isActvie ? "active" : "inactive");
116 }
117
118 // /// Define the callback that will notify about mime changes
119 void setMimeCallback(void (*callback)(const char*)) {
120 TRACED();
121 this->notifyMimeCallback = callback;
122 }
123
126 const char* mime() { return actual_mime; }
127
128 static bool checkAAC(uint8_t* start, size_t len) {
129 return start[0] == 0xFF &&
130 (start[1] == 0xF0 || start[1] == 0xF1 || start[1] == 0xF9);
131 }
132
133 static bool checkAACExt(uint8_t* start, size_t len) {
134 // checking logic for files
135 if (memcmp(start + 4, "ftypM4A", 7) == 0) {
136 return true;
137 }
138 // check for streaming
139 HeaderParserAAC aac;
140 // it should start with a synch word
141 int pos = aac.findSyncWord((const uint8_t*)start, len);
142 if (pos == -1) {
143 return false;
144 }
145 // make sure that it is not an mp3
146 if (aac.isValid(start + pos, len - pos)) {
147 return false;
148 }
149 return true;
150 }
151
152 static bool checkMP3(uint8_t* start, size_t len) {
153 return memcmp(start, "ID3", 3) == 0 ||
154 (start[0] == 0xFF && ((start[1] & 0xE0) == 0xE0));
155 }
156
157 static bool checkMP3Ext(uint8_t* start, size_t len) {
158 HeaderParserMP3 mp3;
159 return mp3.isValid(start, len);
160 }
161
162 static bool checkWAV_ADPCM(uint8_t* start, size_t len) {
163 if (memcmp(start, "RIFF", 4) != 0) return false;
164 WAVHeader header;
165 header.write(start, len);
166 if (!header.parse()) return false;
167 if (header.audioInfo().format == AudioFormat::ADPCM) {
168 return true;
169 }
170 return false;
171 }
172
173 static bool checkWAV(uint8_t* start, size_t len) {
174 return memcmp(start, "RIFF", 4) == 0;
175 }
176
177 static bool checkOGG(uint8_t* start, size_t len) {
178 return memcmp(start, "OggS", 4) == 0;
179 }
180
181 static bool checkFLAC(uint8_t* start, size_t len) {
182 if (len < 4) return false;
183
184 // Native FLAC streams start with "fLaC" (0x664C6143)
185 if (memcmp(start, "fLaC", 4) == 0) {
186 return true;
187 }
188 return false;
189 }
190
191 static bool checkOggFLAC(uint8_t* start, size_t len) {
192 // Check for OGG FLAC - OGG container with FLAC content
193 // OGG starts with "OggS" and may contain FLAC codec
194 if (len >= 32 && memcmp(start, "OggS", 4) == 0) {
195 // Look for FLAC signature within the first OGG page
196 // FLAC in OGG typically has "\x7FFLAC" or "FLAC" as codec identifier
197 for (size_t i = 4; i < len - 4 && i < 64; i++) {
198 if (memcmp(start + i, "FLAC", 4) == 0) {
199 return true;
200 }
201 // Also check for the more specific OGG FLAC header
202 if (i < len - 5 && start[i] == 0x7F &&
203 memcmp(start + i + 1, "FLAC", 4) == 0) {
204 return true;
205 }
206 }
207 }
208
209 return false;
210 }
211
223 static bool checkOggOpus(uint8_t* start, size_t len) {
224 // Check for OGG Opus - OGG container with Opus content
225 // OGG starts with "OggS" and contains Opus codec identifier
226 if (len >= 32 && memcmp(start, "OggS", 4) == 0) {
227 // Look for Opus signature within the first OGG page
228 // Opus in OGG typically has "OpusHead" as the codec identifier
229 for (size_t i = 4; i < len - 8 && i < 80; i++) {
230 if (memcmp(start + i, "OpusHead", 8) == 0) {
231 return true;
232 }
233 }
234 }
235
236 return false;
237 }
238
250 static bool checkOggVorbis(uint8_t* start, size_t len) {
251 // Check for OGG Vorbis - OGG container with Vorbis content
252 // OGG starts with "OggS" and contains Vorbis codec identifier
253 if (len >= 32 && memcmp(start, "OggS", 4) == 0) {
254 // Look for Vorbis signature within the first OGG page
255 // Vorbis in OGG has "\x01vorbis" as the codec identifier
256 for (size_t i = 4; i < len - 7 && i < 80; i++) {
257 if (start[i] == 0x01 && memcmp(start + i + 1, "vorbis", 6) == 0) {
258 return true;
259 }
260 }
261 }
262
263 return false;
264 }
265
267 static bool checkMP2T(uint8_t* start, size_t len) {
268 if (len < 189) return start[0] == 0x47;
269
270 return start[0] == 0x47 && start[188] == 0x47;
271 }
272
274 static bool checkSID(uint8_t* start, size_t len) {
275 return memcmp(start, "PSID", 4) == 0 || memcmp(start, "RSID", 4) == 0;
276 }
277
278 static bool checkM4A(uint8_t* header, size_t len) {
279 if (len < 12) return false;
280
281 // prevent false detecton by mp3 files
282 if (memcmp(header, "ID3", 3) == 0) return false;
283
284 // Special hack when we position to start of mdat box
285 if (memcmp(header + 4, "mdat", 4) != 0) return true;
286
287 // Check for "ftyp" at offset 4
288 if (memcmp(header + 4, "ftyp", 4) != 0) return false;
289
290 // Check for "M4A " or similar major brand
291 if (memcmp(header + 8, "M4A ", 4) == 0 ||
292 memcmp(header + 8, "mp42", 4) == 0 ||
293 memcmp(header + 8, "isom", 4) == 0)
294 return true;
295
296 return false;
297 }
298
300 void setDefaultMime(const char* mime) { default_mime = mime; }
301
303 int setMimeActive(const char* mimePrefix, bool active) {
304 int result = 0;
305 for (auto& check : checks) {
306 if (StrView(check.mime).startsWith(mimePrefix)) {
307 check.is_active = active;
308 LOGI("MimeDetector for %s: %s", check.mime,
309 check.is_active ? "active" : "inactive");
310 result++;
311 }
312 }
313 return result;
314 }
315
317 void clear() {
318 checks.clear();
319 actual_mime = nullptr;
320 is_first = true;
321 }
322
323 protected:
324 struct Check {
325 const char* mime = nullptr;
326 bool (*check)(uint8_t* data, size_t len) = nullptr;
327 bool is_active = true;
328 Check(const char* mime, bool (*check)(uint8_t* data, size_t len)) {
329 this->mime = mime;
330 this->check = check;
331 }
332 Check() = default;
333 };
334 Vector<Check> checks{0};
335 bool is_first = false;
336 const char* actual_mime = nullptr;
337 const char* default_mime = nullptr;
338 void (*notifyMimeCallback)(const char* mime) = nullptr;
339
341 void determineMime(void* data, size_t len) {
342 if (is_first) {
343 actual_mime = lookupMime((uint8_t*)data, len);
344 if (notifyMimeCallback != nullptr && actual_mime != nullptr) {
345 notifyMimeCallback(actual_mime);
346 }
347 is_first = false;
348 }
349 }
350
352 const char* lookupMime(uint8_t* data, size_t len) {
353 for (int j = 0; j < checks.size(); j++) {
354 Check l_check = checks[j];
355 if (l_check.is_active && l_check.check(data, len)) {
356 return l_check.mime;
357 }
358 }
359 return default_mime;
360 }
361};
362
363} // namespace audio_tools
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:274
void determineMime(void *data, size_t len)
Update the mime type.
Definition MimeDetector.h:341
const char * lookupMime(uint8_t *data, size_t len)
Default logic which supports aac, mp3, wav and ogg.
Definition MimeDetector.h:352
bool begin()
Sets is_first to true.
Definition MimeDetector.h:82
size_t write(uint8_t *data, size_t len)
write the header to determine the mime
Definition MimeDetector.h:94
const char * mime()
Definition MimeDetector.h:126
static bool checkOggOpus(uint8_t *start, size_t len)
Checks for OGG Opus format.
Definition MimeDetector.h:223
int setMimeActive(const char *mimePrefix, bool active)
sets the mime rules active or inactive
Definition MimeDetector.h:303
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:101
void setDefaultMime(const char *mime)
Provides the default mime type if no mime could be determined.
Definition MimeDetector.h:300
void end()
Clears the actual mime and resets the state.
Definition MimeDetector.h:88
void clear()
Clears all mime rules and resets the actual selection.
Definition MimeDetector.h:317
static bool checkOggVorbis(uint8_t *start, size_t len)
Checks for OGG Vorbis format.
Definition MimeDetector.h:250
static bool checkMP2T(uint8_t *start, size_t len)
MPEG-2 TS Byte Stream Format.
Definition MimeDetector.h:267
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
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10
Definition MimeDetector.h:324