arduino-audio-tools
Loading...
Searching...
No Matches
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
7namespace audio_tools {
8
29 public:
43 virtual const char* mime() = 0;
44};
45
61class MimeDetector : public MimeSource {
62 public:
63 MimeDetector(bool setupDefault = true) {
64 if (setupDefault) {
65 setCheck("audio/vnd.wave", checkWAV);
66 setCheck("audio/flac", checkFLAC);
67 setCheck("audio/ogg; codecs=flac", checkOggFLAC);
68 setCheck("audio/ogg; codecs=opus", checkOggOpus);
69 setCheck("audio/ogg; codec=vorbis", checkOggVorbis);
70 setCheck("audio/ogg", checkOGG);
71 setCheck("video/MP2T", checkMP2T);
72 setCheck("audio/prs.sid", checkSID);
73 setCheck("audio/m4a", checkM4A, false);
74 setCheck("audio/mpeg", checkMP3Ext);
75 setCheck("audio/aac", checkAACExt);
76 }
77 }
78
80 bool begin() {
81 is_first = true;
82 return true;
83 }
84
86 void end(){
87 actual_mime = nullptr;
88 is_first = true;
89 }
90
91
93 size_t write(uint8_t* data, size_t len) {
94 actual_mime = default_mime;
95 determineMime(data, len);
96 return len;
97 }
98
100 void setCheck(const char* mime, bool (*check)(uint8_t* start, size_t len), bool isActvie = true) {
101 StrView mime_str{mime};
102 for (int j = 0; j < checks.size(); j++) {
103 Check l_check = checks[j];
104 if (mime_str.equals(l_check.mime)) {
105 l_check.check = check;
106 l_check.is_active = isActvie;
107 return;
108 }
109 }
110 Check check_to_add{mime, check};
111 check_to_add.is_active = isActvie;
112 checks.push_back(check_to_add);
113 LOGI("MimeDetector for %s: %s", mime, isActvie ? "active" : "inactive");
114 }
115
116 // /// Define the callback that will notify about mime changes
117 void setMimeCallback(void (*callback)(const char*)) {
118 TRACED();
119 this->notifyMimeCallback = callback;
120 }
121
124 const char* mime() { return actual_mime; }
125
126 static bool checkAAC(uint8_t* start, size_t len) {
127 return start[0] == 0xFF &&
128 (start[1] == 0xF0 || start[1] == 0xF1 || start[1] == 0xF9);
129 }
130
131 static bool checkAACExt(uint8_t* start, size_t len) {
132 // checking logic for files
133 if (memcmp(start + 4, "ftypM4A", 7) == 0) {
134 return true;
135 }
136 // check for streaming
137 HeaderParserAAC aac;
138 // it should start with a synch word
139 int pos = aac.findSyncWord((const uint8_t*)start, len);
140 if (pos == -1) {
141 return false;
142 }
143 // make sure that it is not an mp3
144 if (aac.isValid(start + pos, len - pos)) {
145 return false;
146 }
147 return true;
148 }
149
150 static bool checkMP3(uint8_t* start, size_t len) {
151 return memcmp(start, "ID3", 3) == 0 ||
152 (start[0] == 0xFF && ((start[1] & 0xE0) == 0xE0));
153 }
154
155 static bool checkMP3Ext(uint8_t* start, size_t len) {
156 HeaderParserMP3 mp3;
157 return mp3.isValid(start, len);
158 }
159
160 static bool checkWAV(uint8_t* start, size_t len) {
161 return memcmp(start, "RIFF", 4) == 0;
162 }
163
164 static bool checkOGG(uint8_t* start, size_t len) {
165 return memcmp(start, "OggS", 4) == 0;
166 }
167
168 static bool checkFLAC(uint8_t* start, size_t len) {
169 if (len < 4) return false;
170
171 // Native FLAC streams start with "fLaC" (0x664C6143)
172 if (memcmp(start, "fLaC", 4) == 0) {
173 return true;
174 }
175 return false;
176 }
177
178 static bool checkOggFLAC(uint8_t* start, size_t len) {
179 // Check for OGG FLAC - OGG container with FLAC content
180 // OGG starts with "OggS" and may contain FLAC codec
181 if (len >= 32 && memcmp(start, "OggS", 4) == 0) {
182 // Look for FLAC signature within the first OGG page
183 // FLAC in OGG typically has "\x7FFLAC" or "FLAC" as codec identifier
184 for (size_t i = 4; i < len - 4 && i < 64; i++) {
185 if (memcmp(start + i, "FLAC", 4) == 0) {
186 return true;
187 }
188 // Also check for the more specific OGG FLAC header
189 if (i < len - 5 && start[i] == 0x7F &&
190 memcmp(start + i + 1, "FLAC", 4) == 0) {
191 return true;
192 }
193 }
194 }
195
196 return false;
197 }
198
210 static bool checkOggOpus(uint8_t* start, size_t len) {
211 // Check for OGG Opus - OGG container with Opus content
212 // OGG starts with "OggS" and contains Opus codec identifier
213 if (len >= 32 && memcmp(start, "OggS", 4) == 0) {
214 // Look for Opus signature within the first OGG page
215 // Opus in OGG typically has "OpusHead" as the codec identifier
216 for (size_t i = 4; i < len - 8 && i < 80; i++) {
217 if (memcmp(start + i, "OpusHead", 8) == 0) {
218 return true;
219 }
220 }
221 }
222
223 return false;
224 }
225
237 static bool checkOggVorbis(uint8_t* start, size_t len) {
238 // Check for OGG Vorbis - OGG container with Vorbis content
239 // OGG starts with "OggS" and contains Vorbis codec identifier
240 if (len >= 32 && memcmp(start, "OggS", 4) == 0) {
241 // Look for Vorbis signature within the first OGG page
242 // Vorbis in OGG has "\x01vorbis" as the codec identifier
243 for (size_t i = 4; i < len - 7 && i < 80; i++) {
244 if (start[i] == 0x01 && memcmp(start + i + 1, "vorbis", 6) == 0) {
245 return true;
246 }
247 }
248 }
249
250 return false;
251 }
252
254 static bool checkMP2T(uint8_t* start, size_t len) {
255 if (len < 189) return start[0] == 0x47;
256
257 return start[0] == 0x47 && start[188] == 0x47;
258 }
259
261 static bool checkSID(uint8_t* start, size_t len) {
262 return memcmp(start, "PSID", 4) == 0 || memcmp(start, "RSID", 4) == 0;
263 }
264
265 static bool checkM4A(uint8_t* header, size_t len) {
266 if (len < 12) return false;
267
268 // prevent false detecton by mp3 files
269 if (memcmp(header, "ID3", 3) == 0) return false;
270
271 // Special hack when we position to start of mdat box
272 if (memcmp(header + 4, "mdat", 4) != 0) return true;
273
274 // Check for "ftyp" at offset 4
275 if (memcmp(header + 4, "ftyp", 4) != 0) return false;
276
277 // Check for "M4A " or similar major brand
278 if (memcmp(header + 8, "M4A ", 4) == 0 ||
279 memcmp(header + 8, "mp42", 4) == 0 ||
280 memcmp(header + 8, "isom", 4) == 0)
281 return true;
282
283 return false;
284 }
285
287 void setDefaultMime(const char* mime) { default_mime = mime; }
288
290 int setMimeActive(const char* mimePrefix, bool active) {
291 int result = 0;
292 for (auto& check : checks) {
293 if (StrView(check.mime).startsWith(mimePrefix)) {
294 check.is_active = active;
295 LOGI("MimeDetector for %s: %s", check.mime, check.is_active ? "active" : "inactive");
296 result++;
297 }
298 }
299 return result;
300 }
301
303 void clear() {
304 checks.clear();
305 actual_mime = nullptr;
306 is_first = true;
307 }
308
309 protected:
310 struct Check {
311 const char* mime = nullptr;
312 bool (*check)(uint8_t* data, size_t len) = nullptr;
313 bool is_active = true;
314 Check(const char* mime, bool (*check)(uint8_t* data, size_t len)) {
315 this->mime = mime;
316 this->check = check;
317 }
318 Check() = default;
319 };
320 Vector<Check> checks{0};
321 bool is_first = false;
322 const char* actual_mime = nullptr;
323 const char* default_mime = nullptr;
324 void (*notifyMimeCallback)(const char* mime) = nullptr;
325
327 void determineMime(void* data, size_t len) {
328 if (is_first) {
329 actual_mime = lookupMime((uint8_t*)data, len);
330 if (notifyMimeCallback != nullptr && actual_mime != nullptr) {
331 notifyMimeCallback(actual_mime);
332 }
333 is_first = false;
334 }
335 }
336
338 const char* lookupMime(uint8_t* data, size_t len) {
339 for (int j = 0; j < checks.size(); j++) {
340 Check l_check = checks[j];
341 if (l_check.is_active && l_check.check(data, len)) {
342 return l_check.mime;
343 }
344 }
345 return default_mime;
346 }
347};
348
349} // namespace audio_tools
Logic to detemine the mime type from the content. By default the following mime types are supported (...
Definition MimeDetector.h:61
static bool checkSID(uint8_t *start, size_t len)
Commodore 64 SID File.
Definition MimeDetector.h:261
void determineMime(void *data, size_t len)
Update the mime type.
Definition MimeDetector.h:327
const char * lookupMime(uint8_t *data, size_t len)
Default logic which supports aac, mp3, wav and ogg.
Definition MimeDetector.h:338
bool begin()
Sets is_first to true.
Definition MimeDetector.h:80
size_t write(uint8_t *data, size_t len)
write the header to determine the mime
Definition MimeDetector.h:93
const char * mime()
Definition MimeDetector.h:124
static bool checkOggOpus(uint8_t *start, size_t len)
Checks for OGG Opus format.
Definition MimeDetector.h:210
int setMimeActive(const char *mimePrefix, bool active)
sets the mime rules active or inactive
Definition MimeDetector.h:290
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:100
void setDefaultMime(const char *mime)
Provides the default mime type if no mime could be determined.
Definition MimeDetector.h:287
void end()
Clears the actual mime and resets the state.
Definition MimeDetector.h:86
void clear()
Clears all mime rules and resets the actual selection.
Definition MimeDetector.h:303
static bool checkOggVorbis(uint8_t *start, size_t len)
Checks for OGG Vorbis format.
Definition MimeDetector.h:237
static bool checkMP2T(uint8_t *start, size_t len)
MPEG-2 TS Byte Stream Format.
Definition MimeDetector.h:254
Abstract interface for classes that can provide MIME type information.
Definition MimeDetector.h:28
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:310