21 struct __attribute__((packed)) FrameHeader {
22 static const unsigned int SERIALIZED_SIZE = 4;
25 static const unsigned char FRAMESYNC_FIRST_BYTEMASK = 0b11111111;
27 static const unsigned char FRAMESYNC_SECOND_BYTEMASK = 0b1110000;
28 static const unsigned char AUDIO_VERSION_MASK = 0b00011000;
29 static const unsigned char LAYER_DESCRIPTION_MASK = 0b00000110;
30 static const unsigned char PROTECTION_BIT_MASK = 0b00000001;
32 static const unsigned char BITRATE_INDEX_MASK = 0b11110000;
33 static const unsigned char SAMPLERATE_INDEX_MASK = 0b00001100;
34 static const unsigned char PADDING_BIT_MASK = 0b00000010;
35 static const unsigned char PRIVATE_BIT_MASK = 0b00000001;
37 static const unsigned char CHANNEL_MODE_MASK = 0b11000000;
38 static const unsigned char MODE_EXTENTION_MASK = 0b00110000;
39 static const unsigned char COPYRIGHT_BIT_MASK = 0b00001000;
40 static const unsigned char ORIGINAL_BIT_MASK = 0b00000100;
41 static const unsigned char EMPHASIS_MASK = 0b00000011;
44 bool FrameSyncBits : 3;
47 enum class AudioVersionID :
unsigned {
55 enum class LayerID :
unsigned {
67 bool BitrateIndex : 4;
68 bool SampleRateIndex : 2;
77 enum class ChannelModeID :
unsigned {
86 bool ExtentionMode : 2;
97 enum class EmphasisID :
unsigned {
104 enum SpecialBitrate {
109 signed int getBitRate()
const {
111 static signed char rateTable[4][4][16] = {
115 {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
117 {0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 18, 20, -1},
119 {0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 18, 20, -1},
121 {0, 4, 6, 7, 8, 10, 12, 14, 16, 18, 20, 22, 24, 28, 32, -1},
126 {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
127 {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
128 {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
129 {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
135 {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
137 {0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 18, 20, -1},
139 {0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 18, 20, -1},
141 {0, 4, 6, 7, 8, 10, 12, 14, 16, 18, 20, 22, 24, 28, 32, -1},
147 {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
149 {0, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, -1},
151 {0, 4, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, -1},
153 {0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, -1},
156 signed char rate_byte = rateTable[AudioVersion][Layer][BitrateIndex];
157 if (rate_byte == -1) {
158 LOGE(
"Unsupported bitrate");
161 return rate_byte * 8000;
164 enum SpecialSampleRate {
168 unsigned short getSampleRate()
const {
170 static unsigned short rateTable[4][4] = {
172 {11025, 12000, 8000, 0},
176 {22050, 24000, 16000, 0},
178 {44100, 48000, 32000, 0},
181 return rateTable[AudioVersion][SampleRateIndex];
185 int sample_rate = getSampleRate();
186 if (sample_rate == 0)
return 0;
187 return int((144 * getBitRate() / sample_rate) + Padding);
194 memset(&header, 0,
sizeof(header));
197 if (memcmp(data,
"ID3", 3) == 0) {
202 int sync_pos = seekFrameSync(data, len);
203 if (sync_pos == -1) {
204 LOGE(
"Could not find FrameSync");
209 if (sync_pos >= 0 && contains(data,
"Xing", len)) {
215 if (sync_pos >= 0 && contains(data,
"Info", len)) {
216 LOGI(
"Xing Info found");
221 bool is_valid_mp3 =
false;
223 LOGI(
"checking header at %d", sync_pos);
224 int len_available = len - sync_pos;
227 if (len_available <
sizeof(header)) {
228 LOGE(
"Not enough data to determine mp3 header");
232 readFrameHeader(data + sync_pos);
233 is_valid_mp3 = validate(data + sync_pos, len_available);
237 if (is_valid_mp3 && frame_len > 0) {
239 int pos = seekFrameSync(data + expected_next_frame,
240 len - expected_next_frame);
241 LOGI(
"- end frame found: %s", pos == 0 ?
"yes" :
"no");
242 if (pos != 0) is_valid_mp3 =
false;
246 int pos = seekFrameSync(data + sync_pos + 2, len_available - 2);
248 if (pos == -1)
break;
250 sync_pos = pos + sync_pos + 2;
253 if (is_valid_mp3 && getSampleRate() != 0)
break;
256 LOGI(
"-------------------");
257 LOGI(
"is mp3: %s", is_valid_mp3 ?
"yes" :
"no");
259 LOGI(
"sample rate: %u", getSampleRate());
261 LOGI(
"bit rate: %d", getBitRate());
262 LOGI(
"Padding: %d", getFrameHeader().Padding);
263 LOGI(
"Layer: %s (0x%x)", getLayerStr(), (
int)getFrameHeader().Layer);
264 LOGI(
"Version: %s (0x%x)", getVersionStr(),
265 (
int)getFrameHeader().AudioVersion);
266 LOGI(
"-------------------");
271 uint16_t getSampleRate()
const {
return header.getSampleRate(); }
273 int getBitRate()
const {
return header.getBitRate(); }
281 int bitrate = getBitRate();
282 if (bitrate == 0)
return 0;
283 return fileSizeBytes / bitrate;
286 const char* getVersionStr()
const {
287 return header.AudioVersion == FrameHeader::AudioVersionID::MPEG_1 ?
"1"
288 : header.AudioVersion == FrameHeader::AudioVersionID::MPEG_2 ?
"2"
289 : header.AudioVersion == FrameHeader::AudioVersionID::MPEG_2_5
294 const char* getLayerStr()
const {
295 return header.Layer == FrameHeader::LayerID::LAYER_1 ?
"1"
296 : header.Layer == FrameHeader::LayerID::LAYER_2 ?
"2"
297 : header.Layer == FrameHeader::LayerID::LAYER_3 ?
"3"
302 FrameHeader getFrameHeader() {
return header; }
305 int findSyncWord(
const uint8_t* buf,
size_t nBytes, uint8_t synch = 0xFF,
306 uint8_t syncl = 0xF0) {
307 for (
int i = 0; i < nBytes - 1; i++) {
308 if ((buf[i + 0] & synch) == synch && (buf[i + 1] & syncl) == syncl)
305 int findSyncWord(
const uint8_t* buf,
size_t nBytes, uint8_t synch = 0xFF, {
…}
317 bool validate(
const uint8_t* data,
size_t len) {
318 assert(header.FrameSyncByte = 0xFF);
320 return FrameReason::VALID == validateFrameHeader(header);
323 bool contains(
const uint8_t* data,
const char* toFind,
size_t len) {
324 if (data ==
nullptr || len == 0)
return false;
325 int find_str_len = strlen(toFind);
326 for (
int j = 0; j < len - find_str_len; j++) {
327 if (memcmp(data + j, toFind, find_str_len) == 0)
return true;
335 int seekFrameSync(
const uint8_t* str,
size_t len) {
337 for (
int j = 0; j < len - 1; j++) {
340 if ((cur & 0b11111111) != 0b11111111)
continue;
342 if ((str[j + 1] & 0b11100000) != 0b11100000) {
354 void readFrameHeader(
const uint8_t* data) {
355 assert(data[0] == 0xFF);
356 assert((data[1] & 0b11100000) == 0b11100000);
358 memcpy(&header, data,
sizeof(header));
360 LOGI(
"- sample rate: %u", getSampleRate());
361 LOGI(
"- bit rate: %d", getBitRate());
364 enum class FrameReason {
366 INVALID_BITRATE_FOR_VERSION,
367 INVALID_SAMPLERATE_FOR_VERSION,
368 INVALID_MPEG_VERSION,
370 INVALID_LAYER_II_BITRATE_AND_MODE,
375 FrameReason validateFrameHeader(
const FrameHeader& header) {
376 if (header.AudioVersion == FrameHeader::AudioVersionID::INVALID) {
377 LOGI(
"invalid mpeg version");
378 return FrameReason::INVALID_MPEG_VERSION;
381 if (header.Layer == FrameHeader::LayerID::INVALID) {
382 LOGI(
"invalid layer");
383 return FrameReason::INVALID_LAYER;
386 if (header.getBitRate() == FrameHeader::SpecialBitrate::INVALID) {
387 LOGI(
"invalid bitrate");
388 return FrameReason::INVALID_BITRATE_FOR_VERSION;
391 if (header.getSampleRate() == FrameHeader::SpecialSampleRate::RESERVED) {
392 LOGI(
"invalid samplerate");
393 return FrameReason::INVALID_SAMPLERATE_FOR_VERSION;
398 if (header.Layer == FrameHeader::LayerID::LAYER_2) {
399 if (header.ChannelMode == FrameHeader::ChannelModeID::SINGLE) {
400 if (header.getBitRate() >= 224000) {
401 LOGI(
"invalid bitrate >224000");
402 return FrameReason::INVALID_LAYER_II_BITRATE_AND_MODE;
405 if (header.getBitRate() >= 32000 && header.getBitRate() <= 56000) {
406 LOGI(
"invalid bitrate >32000");
407 return FrameReason::INVALID_LAYER_II_BITRATE_AND_MODE;
410 if (header.getBitRate() == 80000) {
411 LOGI(
"invalid bitrate >80000");
412 return FrameReason::INVALID_LAYER_II_BITRATE_AND_MODE;
417 if (header.Emphasis == FrameHeader::EmphasisID::INVALID) {
418 LOGI(
"invalid Emphasis");
419 return FrameReason::INVALID_EMPHASIS;
422 return FrameReason::VALID;