28 static const unsigned int SERIALIZED_SIZE = 4;
30 enum class MPEGVersionID :
unsigned {
37 enum class LayerID :
unsigned {
44 enum class ChannelModeID :
unsigned {
51 enum class EmphasisID :
unsigned {
58 enum SpecialBitrate { INVALID_BITRATE = -8000, ANY = 0 };
59 enum SpecialSampleRate { RESERVED = 0 };
62 MPEGVersionID audioVersion = MPEGVersionID::INVALID;
63 LayerID layer = LayerID::INVALID;
64 bool protection =
false;
68 bool isPrivate =
false;
69 ChannelModeID channelMode = ChannelModeID::STEREO;
71 bool copyright =
false;
72 bool original =
false;
73 EmphasisID emphasis = EmphasisID::NONE;
76 static bool decode(
const uint8_t*
b, FrameHeader& out) {
77 if (
b ==
nullptr)
return false;
78 if (!(
b[0] == 0xFF && (
b[1] & 0xE0) == 0xE0))
85 out.audioVersion =
static_cast<MPEGVersionID
>((
b1 >> 3) & 0x03);
86 out.layer =
static_cast<LayerID
>((
b1 >> 1) & 0x03);
87 out.protection = !(
b1 & 0x01);
89 out.bitrateIndex = (
b2 >> 4) & 0x0F;
90 out.sampleRateIndex = (
b2 >> 2) & 0x03;
91 out.padding = (
b2 >> 1) & 0x01;
92 out.isPrivate = (
b2 & 0x01) != 0;
94 out.channelMode =
static_cast<ChannelModeID
>((
b3 >> 6) & 0x03);
95 out.extensionMode = (
b3 >> 4) & 0x03;
96 out.copyright = (
b3 >> 3) & 0x01;
97 out.original = (
b3 >> 2) & 0x01;
98 out.emphasis =
static_cast<EmphasisID
>(
b3 & 0x03);
102 signed int getBitRate()
const {
104 static const signed char rateTable[4][4][16] = {
108 {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
110 {0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 18, 20, -1},
112 {0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 18, 20, -1},
114 {0, 4, 6, 7, 8, 10, 12, 14, 16, 18, 20, 22, 24, 28, 32, -1},
119 {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
120 {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
121 {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
122 {-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},
130 {0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 18, 20, -1},
132 {0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 18, 20, -1},
134 {0, 4, 6, 7, 8, 10, 12, 14, 16, 18, 20, 22, 24, 28, 32, -1},
140 {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
142 {0, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, -1},
144 {0, 4, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, -1},
146 {0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, -1},
152 LOGE(
"Unsupported bitrate");
158 unsigned short getSampleRate()
const {
160 static const unsigned short rateTable[4][4] = {
162 {11025, 12000, 8000, 0},
166 {22050, 24000, 16000, 0},
168 {44100, 48000, 32000, 0},
171 return rateTable[(
int)audioVersion][(
int)sampleRateIndex];
174 int getFrameLength()
const {
175 int sample_rate = getSampleRate();
176 if (sample_rate == 0)
return 0;
178 (audioVersion == FrameHeader::MPEGVersionID::MPEG_1) ? 144 : 72;
179 return int((value * getBitRate() / sample_rate) + (padding ? 1 : 0));
199 for (
int i = 0; i < len; i++) {
219 if (data ==
nullptr || len < 10) {
220 LOGE(
"Invalid input data or too small");
233 if (len >= 10 &&
memcmp(data,
"ID3", 3) == 0) {
234 LOGI(
"ID3v2 tag found");
236 int id3_size = ((data[6] & 0x7F) << 21) | ((data[7] & 0x7F) << 14) |
237 ((data[8] & 0x7F) << 7) | (data[9] & 0x7F);
256 LOGI(
"VBR header found (Xing/Info/VBRI)");
265 while (current_pos < len &&
271 LOGD(
"Not enough data for header at position %d", current_pos);
277 if (!FrameHeader::decode(data + current_pos,
temp_header) ||
279 LOGD(
"Invalid frame header at position %d", current_pos);
283 seekFrameSync(data + current_pos + 1, len - current_pos - 1);
292 LOGD(
"Invalid frame length %d at position %d",
frame_len, current_pos);
309 LOGD(
"Invalid audio parameters in frame at position %d",
319 (
temp_header.audioVersion == FrameHeader::MPEGVersionID::MPEG_1)
326 LOGD(
"Frame length %d doesn't match expected %d for bitrate",
340 LOGD(
"Frame parameters inconsistent at position %d", current_pos);
350 LOGD(
"Incomplete frame at position %d (need %d, have %d)", current_pos,
373 seekFrameSync(data + current_pos + 1, len - current_pos - 1);
375 current_pos = current_pos + 1 +
next_sync;
384 }
else if (len >= 1024) {
396 LOGI(
"-------------------");
397 LOGI(
"MP3 validation: VALID");
398 LOGI(
"Data size: %d bytes", len);
402 LOGI(
"Validation mode: STRICT (large buffer)");
403 }
else if (len >= 1024) {
404 LOGI(
"Validation mode: MODERATE (1KB+ buffer)");
406 LOGI(
"Validation mode: LENIENT (small buffer)");
415 LOGI(
"-------------------");
417 LOGI(
"MP3 validation: INVALID (frames: %d, consecutive: %d, size: %d)",
438 return (
header.channelMode == FrameHeader::ChannelModeID::SINGLE) ? 1 : 2;
450 if (bitrate == 0)
return 0;
456 return header.audioVersion == FrameHeader::MPEGVersionID::MPEG_1 ?
"1"
457 :
header.audioVersion == FrameHeader::MPEGVersionID::MPEG_2 ?
"2"
458 :
header.audioVersion == FrameHeader::MPEGVersionID::MPEG_2_5
465 return header.layer == FrameHeader::LayerID::LAYER_1 ?
"1"
466 :
header.layer == FrameHeader::LayerID::LAYER_2 ?
"2"
467 :
header.layer == FrameHeader::LayerID::LAYER_3 ?
"3"
473 if (
header.layer != FrameHeader::LayerID::LAYER_3)
return 0;
475 return header.audioVersion == FrameHeader::MPEGVersionID::MPEG_1 ? 1152
482 if (sample_rate == 0)
return 0;
511 for (
int i = 0; i <
nBytes - 1; i++) {
532 FrameHeader::SERIALIZED_SIZE) {
540 size_t to_remove = (available > 3) ? available - 3 : 0;
559 if (available < FrameHeader::SERIALIZED_SIZE) {
606 LOGE(
"Failed to write complete frame");
632 if (data ==
nullptr || len == 0)
return false;
646 if (str[
j] == 0xFF && (str[
j + 1] & 0xE0) == 0xE0) {
654 if (!FrameHeader::decode(data,
header))
return;
671 if (
header.audioVersion == FrameHeader::MPEGVersionID::INVALID) {
672 LOGI(
"invalid mpeg version");
676 if (
header.layer == FrameHeader::LayerID::INVALID) {
677 LOGI(
"invalid layer");
681 if (
header.getBitRate() <= 0) {
682 LOGI(
"invalid bitrate");
686 if (
header.getSampleRate() ==
687 (
unsigned short)FrameHeader::SpecialSampleRate::RESERVED) {
688 LOGI(
"invalid samplerate");
694 if (
header.layer == FrameHeader::LayerID::LAYER_2) {
695 if (
header.channelMode == FrameHeader::ChannelModeID::SINGLE) {
696 if (
header.getBitRate() >= 224000) {
697 LOGI(
"invalid bitrate >224000");
701 if (
header.getBitRate() >= 32000 &&
header.getBitRate() <= 56000) {
702 LOGI(
"invalid bitrate >32000");
706 if (
header.getBitRate() == 80000) {
707 LOGI(
"invalid bitrate >80000");
713 if (
header.emphasis == FrameHeader::EmphasisID::INVALID) {
714 LOGI(
"invalid Emphasis");