MP3 文件格式详解¶
一、概述¶
MPEG(Moving Picture Experts Group,活动图像专家组)音频文件是 MPEG-1 标准中的声音部分,根据压缩质量和编码复杂程度划分为三层:
| 层级 | 文件扩展名 | 压缩率 | 复杂度 |
|---|---|---|---|
| Layer 1 | .mp1 | 4:1 | 低 |
| Layer 2 | .mp2 | 6:1-8:1 | 中 |
| Layer 3 | .mp3 | 10:1-12:1 | 高 |
MP3 压缩原理¶
MP3 采用 有损压缩 方式,使用 感官编码技术:
- 频谱分析:对音频文件进行频谱分析
- 噪声过滤:滤掉人耳不敏感的噪音电平
- 量化编码:将剩余数据量化并重新排列
- 高压缩比:达到 10:1-12:1 的压缩率
示例:1 分钟 CD 音质音乐(44.1kHz/16bit/立体声)
- 未压缩:约 10MB
- MP3 压缩:约 1MB
二、MP3 文件整体结构¶
┌─────────────────────┐
│ ID3V2 标签 │ ← 文件开始,长度可变
├─────────────────────┤
│ │
│ 音频数据帧序列 │ ← 文件主体,多个帧组成
│ │
├─────────────────────┤
│ ID3V1 标签 │ ← 文件结尾,128字节固定
└─────────────────────┘
- ID3V2:位于文件开始,存储详细的元数据(作者、专辑等),长度可变
- 音频数据帧:文件主体,包含实际的音频数据,每个帧独立编码
- ID3V1:位于文件结尾,存储基本元数据,128字节固定长度
三、ID3V2 标签详解¶
3.1 标签头结构(10 字节)¶
| 偏移 | 长度 | 字段 | 描述 | 示例值 |
|---|---|---|---|---|
| 0 | 3 | Header | 必须为 "ID3" | 49 44 33 |
| 3 | 1 | Ver | 版本号(ID3v2.3 = 3) | 03 |
| 4 | 1 | Revision | 副版本号(通常为 0) | 00 |
| 5 | 1 | Flag | 标志字节 | 00 |
| 6 | 4 | Size | 标签大小(含帧头) | 见下文 |
标志字节详解(Flag):
a b c 0 0 0 0 0
├─┬─┬─┴───────── 保留位(总为0)
│ │ └─ c:测试标签标志(通常0)
│ └─ b:扩展头标志(通常0)
└─ a:Unsynchronisation标志(通常0)
标签大小计算(Size):
四个字节每个只使用低 7 位,最高位恒为 0:
计算公式:
total_size = (Size[0] & 0x7F) << 21 |
(Size[1] & 0x7F) << 14 |
(Size[2] & 0x7F) << 7 |
(Size[3] & 0x7F);
示例计算:
Size[0..3] = 00 00 2F 76
total_size = (0x00 & 0x7F) * 0x200000 +
(0x00 & 0x7F) * 0x400 +
(0x2F & 0x7F) * 0x80 +
(0x76 & 0x7F)
= 0x872 = 2162 字节
3.2 标签帧结构¶
每个标签帧由 10 字节帧头 + 数据内容 组成:
| 偏移 | 长度 | 字段 | 描述 |
|---|---|---|---|
| 0 | 4 | Frame ID | 帧标识符(如 "TIT2") |
| 4 | 4 | Size | 数据内容大小 |
| 8 | 2 | Flags | 帧标志 |
常用帧标识符:
| 标识符 | 含义 | 说明 |
|---|---|---|
| TIT2 | 标题 | 歌曲标题 |
| TPE1 | 艺术家 | 表演者/艺术家 |
| TALB | 专辑 | 所属专辑 |
| TRCK | 音轨号 | 格式:"N/M"(第N首/共M首) |
| TYER | 年份 | 发行年份 |
| TCON | 流派 | 音乐类型 |
| COMM | 注释 | 格式:"语言代码\0注释内容" |
帧大小计算:
帧标志(Flags):
a b c 0 0 0 0 0 i j k 0 0 0 0 0
├─┬─┬─┴─────────┼─┬─┬─┴───────── 保留位
│ │ │ │ │ └─ k:组标志
│ │ │ │ └─ j:加密标志
│ │ │ └─ i:压缩标志
│ │ └─ c:只读标志
│ └─ b:文件保护标志
└─ a:标签保护标志
四、音频数据帧详解¶
4.1 帧头结构(4 字节)¶
每个音频帧以 4 字节帧头开始:
typedef struct {
unsigned int sync:11; // 同步字(全1)
unsigned int version:2; // MPEG版本
unsigned int layer:2; // 层级
unsigned int crc_protect:1; // CRC保护标志
unsigned int bitrate_index:4; // 比特率索引
unsigned int sampling_rate:2; // 采样率索引
unsigned int padding:1; // 填充位
unsigned int private_bit:1; // 私有位
unsigned int channel_mode:2; // 声道模式
unsigned int mode_ext:2; // 模式扩展
unsigned int copyright:1; // 版权标志
unsigned int original:1; // 原版标志
unsigned int emphasis:2; // 强调方式
} MP3FrameHeader;
4.2 帧头解析示例¶
帧头:FF FB 90 04(二进制:11111111 11111011 10010000 00000100)
| 位域 | 位数 | 值 | 含义 |
|---|---|---|---|
| 0-10 | 11 | 11111111111 |
同步字 |
| 11-12 | 2 | 11 |
MPEG版本:MPEG-1 |
| 13-14 | 2 | 01 |
层级:Layer 3 |
| 15 | 1 | 1 |
CRC保护:无 |
| 16-19 | 4 | 1001 |
比特率索引:128 kbps |
| 20-21 | 2 | 00 |
采样率索引:44.1 kHz |
| 22 | 1 | 0 |
填充:无 |
| 23 | 1 | 0 |
私有位:未使用 |
| 24-25 | 2 | 00 |
声道模式:立体声 |
| 26-27 | 2 | 00 |
模式扩展:未使用 |
| 28 | 1 | 0 |
版权:无 |
| 29 | 1 | 1 |
原版:是 |
| 30-31 | 2 | 00 |
强调方式:无 |
4.3 帧长度计算¶
每帧采样数:
| 层级 | MPEG-1 | MPEG-2/LSF |
|---|---|---|
| Layer 1 | 384 | 384 |
| Layer 2 | 1152 | 1152 |
| Layer 3 | 1152 | 576 |
帧长度计算公式:
// Layer I
frame_length = ((采样数 / 8 × 比特率) / 采样率) + 填充 × 4
// Layer II & III
frame_length = ((采样数 / 8 × 比特率) / 采样率) + 填充
示例计算(MPEG-1 Layer III):
- 比特率:128 kbps = 128,000 bps
- 采样率:44.1 kHz = 44,100 Hz
- 每帧采样数:1152
- 填充:0
4.4 帧持续时间¶
示例:
4.5 音频数据帧组成¶
┌─────────────────────────────────┐
│ 帧头(4字节) │
├─────────────────────────────────┤
│ CRC校验(可选,2字节) │ ← 仅当crc_protect=0时存在
├─────────────────────────────────┤
│ Side Info(通道信息) │ ← Layer III:32字节(立体声)
├─────────────────────────────────┤
│ Scale Factor(增益因子) │
├─────────────────────────────────┤
│ Huffman编码数据(主数据) │
└─────────────────────────────────┘
4.6 CBR vs VBR¶
CBR(恒定比特率)
- 所有帧使用相同比特率
- 帧长度固定(除可能有填充外)
- 易于计算文件总时长
VBR(可变比特率)
- 根据音频复杂度动态调整比特率
- 文件开头包含特殊帧标识:
- XING Header:包含 "Xing" 或 "Info"
- VBRI Header:包含 "VBRI"(较少见)
XING Header 位置:
| 条件 | 起始偏移 |
|---|---|
| MPEG-1,非单声道 | 帧头后 36 字节 |
| MPEG-1,单声道 | 帧头后 21 字节 |
| MPEG-2,非单声道 | 帧头后 21 字节 |
| MPEG-2,单声道 | 帧头后 13 字节 |
XING Header 包含:
- 总帧数
- 文件总大小
- 100个时间段的帧索引(用于快速定位)
- 编码器信息
五、ID3V1 标签详解¶
5.1 结构(128 字节固定)¶
typedef struct {
char header[3]; // 必须为 "TAG"
char title[30]; // 标题
char artist[30]; // 艺术家
char album[30]; // 专辑
char year[4]; // 年份(YYYY)
char comment[28]; // 注释
char zero_byte; // 保留(通常为0)
char track; // 音轨号(ID3v1.1新增)
char genre; // 流派编号
} ID3V1_Tag;
5.2 内存布局¶
字节 长度 内容
0-2 3 "TAG"(标识)
3-32 30 标题(不足补\0)
33-62 30 艺术家(不足补\0)
63-92 30 专辑(不足补\0)
93-96 4 年份(YYYY)
97-124 28 注释
125 1 保留(ID3v1.1:音轨号,0=无音轨号)
126 1 音轨号(ID3v1.1,0=无音轨号)
127 1 流派编号(0-147)
5.3 常用流派编号(部分)¶
| 编号 | 流派 | 编号 | 流派 |
|---|---|---|---|
| 0 | Blues | 1 | Classic Rock |
| 2 | Country | 3 | Dance |
| 4 | Disco | 5 | Funk |
| 6 | Grunge | 7 | Hip-Hop |
| 8 | Jazz | 9 | Metal |
| 10 | New Age | 11 | Oldies |
| 12 | Other | 13 | Pop |
| 14 | R&B | 15 | Rap |
| 16 | Reggae | 17 | Rock |
| 18 | Techno | 19 | Industrial |
| 20 | Alternative | ... | ... |
六、MP3 文件解析示例¶
6.1 文件结构分析¶
偏移 内容 说明
0x0000 ID3V2 标签头 "ID3"标识
0x000A ID3V2 标签帧 多个帧组成
... 音频数据帧 从ID3V2后开始
文件尾-128 ID3V1 标签 固定128字节
6.2 音频帧识别流程¶
int find_mp3_frames(FILE *fp) {
unsigned char header[4];
// 查找第一个有效帧头(同步字11位全1)
while (!feof(fp)) {
fread(header, 1, 4, fp);
if ((header[0] == 0xFF) && ((header[1] & 0xE0) == 0xE0)) {
// 找到有效帧头
return ftell(fp) - 4;
}
// 未找到,回退3字节继续搜索
fseek(fp, -3, SEEK_CUR);
}
return -1;
}
6.3 帧类型判断¶
int get_mpeg_version(unsigned char byte2) {
int version_bits = (byte2 >> 3) & 0x03;
switch (version_bits) {
case 0: return 2; // MPEG-2.5
case 2: return 2; // MPEG-2
case 3: return 1; // MPEG-1
default: return 0; // 保留
}
}
int get_layer(unsigned char byte2) {
int layer_bits = (byte2 >> 1) & 0x03;
switch (layer_bits) {
case 1: return 3; // Layer III
case 2: return 2; // Layer II
case 3: return 1; // Layer I
default: return 0; // 保留
}
}