OGG音频格式是一种开源的压缩音频格式,全称为Ogg Vorbis。OGG文件分为两个库,libogg和libvorbis;
- libogg :处理 Ogg 容器格式。它不关心内部数据是音频还是视频,只负责将数据打包成"页(Page)"和"流(Stream)",处理帧同步、交错和文件结构解析。
- libvorbis :处理 Vorbis 音频编码。它负责将 PCM 原始音频数据压缩成 Vorbis 比特流,或将 Vorbis 比特流解码回 PCM 数据,涉及心理声学模型等复杂算法
一、 OGG 格式
cpp
OGG 文件
├── Page 0 (BOS: 比特流开始)
│ ├── Header (27字节 )
│ └── Body (0~65307 字节,由Segments组成)
├── Page 1
│ ├── Header
│ └── Body
├── ...
└── Page N (EOS: 比特流结束)
1.1 页面头
| 偏移 | 大小(字节) | 字段 | 说明 |
|---|---|---|---|
| 0~3 | 4 | Capture Pattern同步 | "OggS" 固定标识 |
| 4 | 1 | Version 版本 | 版本号(当前为 0) |
| 5 | 1 | Header Type Flag标志位 | bit0=BOS, bit1=EOS, bit2=续页 3 个有效 bit:0x01(CONTINUED):页首 segment 是上一页未结束 Packet 的延续0x02(BOS):逻辑流第一页(流起始)0x04(EOS):逻辑流最后一页(流结束) |
| 6~13 | 8 | Granule Position 64位时间戳 | 时间戳/采样位置 |
| 14~17 | 4 | Bitstream Serial No 流序列号 | 流序列号(多路复用标识)随机唯一 ID,区分文件内多条逻辑流(音频 / 视频 / 字幕) |
| 18~21 | 4 | Page Sequence No | 页面序号,单条逻辑流内自增,检测丢页、乱序 |
| 22~25 | 4 | Checksum | CRC32 校验和,整页(头 + 分段表 + 负载)校验,多项式 0x04C11DB7;计算时先把本字段置 0 |
| 26 | 1 | Page Segments | 段表条目数(0~255) |
1.1.1 Header Type
0x01(CONTINUED):页首 segment 是上一页未结束 Packet 的延续
0x02( BOS):逻辑流第一页(流起始)
0x04(EOS):逻辑流最后一页(流结束)
1.2 body-segment_table (由Segments组成)
1.2.1 body的组成:
|----------|----------|----------|--------|
| Segment1 | Segment2 | Segment3 | ...... |
1.2.2 Segment组成:(字节)
|---------------|-------------|
| 1 | 0~255 |
| lacing values | Packet data |
1,lacing values:
- 数值范围:0 到 255(单字节无符号整数)。
- 基本规则 :
- 值 < 255 :表示当前段的长度,且标志着当前数据包的结束。
- 值 = 255 :表示当前段长度为 255 字节,且数据包未结束,后续紧跟下一个 lacing value 继续累加长度。
- 特殊终止 :若数据包长度恰好是 255 的倍数(如 255、510 字节),最后一个 lacing value 为 0,以此标记包结束
2,packet data
packetdata 可以为Vorbis/Opus/Theora 格式的音频或者视频数据。
Packetdata可以跨多个page, 按照lacing value规则组Packetdata包即可。
二、Vorbis 格式
cpp
OGG 文件
├── Page 0: BOS (Beginning of Stream)
│ └── Packet 0: Vorbis Identification Header
├── Page 1
│ └── Packet 1: Vorbis Comment Header
├── Page 2
│ └── Packet 2: Vorbis Setup Header
├── Page 3~N
│ └── Audio Packets (音频数据块)
└── Page N+1: EOS (End of Stream)
2.1 Vorbis Identification Header(标识头)
固定字节30个字节,用于识别码流版本、声道、采样率、码率范围、分块大小;播放器 / 解码器初始化音频参数全部依赖此字段。小端存储。
| 偏移 (字节) | 字段名称 | 位宽 | 类型 | 说明与约束 |
|---|---|---|---|---|
| 0 | packet_type | 8bit | u8 | 固定 0x01,标识这是 Identification Header |
| 1~6 | magic "vorbis" | 6×8bit | ASCII | 固定 v o r b i s,校验是否合法 Vorbis 头 |
| 7~10 | vorbis_version | 32bit | uint32_le | 标准仅支持 0 = Vorbis I;非 0 直接拒绝解码 |
| 11 | audio_channels | 8bit | uint8 | 声道数,≥1;常见 1 (单声道)、2 (立体声)、6 (5.1) |
| 12~15 | audio_sample_rate | 32bit | uint32_le | 采样率,必须 > 0;如 8000/16000/44100/48000 |
| 16~19 | bitrate_maximum | 32bit | int32_le | 最大瞬时比特率(bps);无限制填 -1 |
| 20~23 | bitrate_nominal | 32bit | int32_le | 平均标称码率;VBR 文件常填 -1 |
| 24~27 | bitrate_minimum | 32bit | int32_le | 最小瞬时比特率;无限制填 -1 |
| 28 | blocksize 混合字节 | 8bit | 复合 bit 域 | 高 4bit = blocksize_0,低 4bit = blocksize_1 |
| 29 | framing_flag | 1bit | bit | 低位,校验标志位;必须 = 1,否则码流非法 |
2.1.1 audio_channels 声道映射(标准定义)
- 1:Mono 单声道
- 2:Left / Right 立体声
- 3:L R Center
- 4:L R BackLeft BackRight
- 5:L R C BL BR
- 6:L R C LFE BL BR(5.1 环绕)
2.1.2 比特率 bitrate
单位:bit per second(bps),有符号 32 位:
- 值 >0:编码器给出参考码率上下限 / 目标码率
- 值 = -1:编码器未限制该参数(VBR 可变码率文件普遍三个全 - 1) 仅作播放器参考,不强制限制码流。
2.1.3 blocksize 混合字节拆解
1,bits 7~4(高 4 位):blocksize_0_exp,
2,bits 3~0(低 4 位):blocksize_1_exp
3,块计算
实际块大小 = 2^exp
强制约束:blocksize_0_exp ≤ blocksize_1_exp
4,标准合法取值:
blocksize0:256(2^8)、512(2^9)
blocksize1:1024(2^10)、2048(2^11) 常见组合:bs0=256, bs1=2048(exp0=8, exp1=11 → 字节值 0x8B)
短块(blocksize0):瞬态音频(打击乐、人声爆音),时域分辨率高
长块(blocksize1):稳态音乐,频域精度高、压缩率更好
2.2 Vorbis Comment Header(可选)
存储歌曲元数据(标题、歌手、专辑、封面、版权等标签),不参与音频解码,丢失仅无法读取标签,音频仍可播放。小端存储
| 偏移 | 字节 | 字段 | 固定值 |
|---|---|---|---|
| 0 | 1 | packet_type | 0x03(区分是 Comment 头) |
| 1~6 | 6 | magic 标识 | v o r b i s (0x76 6F 72 62 69 73) |
| 固定1 | 4 | vendor length | vendor 字符串长度 |
| 可选 | n | vendor string | 依赖vendor length ,编码器标识(如 "Xiph.Org libvorbis 1.3.7") |
| 固定2 | 4 | comments count | 用户评论数量 |
| 可变 | n | comments | 依赖comments count,多条用户评论(每条格式:长度 + 内容) |
| 固定3 | 1 | framing_bit | 实际只用1bit 校验标志位;必须 = 1,否则码流非法 |
官方约定的通用字段:
| 标签名 | 含义 |
|---|---|
| TITLE | 歌曲标题 |
| ARTIST | 主唱 / 艺人 |
| ALBUM | 专辑名 |
| TRACKNUMBER | 曲目序号,如 05 / 5/12 |
| DATE | 发行年份 / 日期 |
| GENRE | 曲风(Pop、Rock、Classical) |
| COPYRIGHT | 版权信息 |
| PERFORMER | 演奏者(古典乐专用) |
| COMPOSER | 作曲 |
| DISCNUMBER | 分碟序号(多碟专辑) |
| METADATA_BLOCK_PICTURE | 内嵌封面(Base64 编码图片二进制) |
2.3 Vorbis Setup Header
存储全部底层解码静态参数(码书、频谱映射、窗口模式、残差处理),没有它无法还原音频频谱;变长二进制比特流,纯位流读取(不是按字节对齐解析,大量位操作);仅作用于解码器初始化,全局静态数据,音频帧不再重复携带;
| 偏移字节 | 字节 | 字段 | 固定值 |
|---|---|---|---|
| 0 | 1 | packet_type | 0x05 |
| 1~6 | 6 | magic 串 | vorbis (0x76 6F 72 62 69 73) |
| 顺序1 | n | codebooks | 码定义-熵编码核心 |
| 顺序2 | n | time backend | 时间域后端设置(未使用) |
| 顺序3 | n | floor backend | 低频/中频包络解码参数 |
| 顺序4 | n | residue backend | 高频残差解码参数 |
| 顺序5 | n | map backend | 声道映射参数 |
| 顺序6 | n | mode | 编码模式定义 |
| 顺序7 | 1 | framing_bit | 实际只用1bit 校验标志位;必须 = 1,否则码流非法 |
2.3.1 codebooks
1, codebook_count 1字节; 存储的是N-1,因此在计算的时候需要+1
2,codebook 结构
cpp
比特流布局:
┌─────────────────────────────────────────────────────────┐
│ Magic (24b) │ dim (16b) │ entries (24b) │ ordered (1b)│
├─────────────────────────────────────────────────────────┤
│ lengthlist (可变) │
├─────────────────────────────────────────────────────────┤
│ maptype (4b) │ q_min (32b) │ q_delta (32b) │ ... │
└─────────────────────────────────────────────────────────┘
| 字段 | 比特数 | 说明 | |
|---|---|---|---|
| Magic | 24 | 魔数 0x564342("VCB") |
|
| dim | 16 | 码本维度(每个向量的元素数) | |
| entries | 24 | 码本条目总数 | |
| ordered | 1 | 码字是否有序(0=无序,1=有序) | |
| ordered = 1 | sparse(可选) | 1 | ordered= 0 仅无序时:unused是否有未使用条目 |
| lengthlist | 可变 | 码字长度列表(无序有序的时候,都需要计算该值) | |
| maptype | 4 | 值映射类型(0/1/2) | |
| maptype= 1或2 | q_min | 32 | 量化最小值(打包浮点) |
| maptype= 1或2 | q_delta | 32 | 量化步长(打包浮点) |
| maptype= 1或2 | q_quant | 4 | 量化精度(实际值 = 读取值 + 1) |
| maptype= 1或2 | q_sequencep | 1 | 是否为单调序列 |
| q_sequencep 和q_quant控制 | quantlist | 可变 | 量化值列表 |
1,maptype
值=1, 无填充
值= 2, 隐式填充映射(lattice VQ)
值=3,显示填充映射(tessellatedor 'foam'VQ)
2.3.2 time backend 、 floor backend、 residue backend、 map backend
| Backend | 数量读取 | 类型读取 | 实际参数 | 状态 |
|---|---|---|---|---|
| Time | 6 bits +1 | 16 bits | 无 | 未使用,预留的时间域处理接口 |
| Floor | 6 bits +1 | 16 bits | 有(floor_param) | 已实现 |
| Residue | 6 bits +1 | 16 bits | 有(residue_param) | 已实现 |
| Mapping | 6 bits +1 | 16 bits | 有(map_param) | 已实现 |
1,支持的 Floor 类型:
| 类型 | 名称 | 算法特点 |
|---|---|---|
| Type 0 | 线性分段 | 简单高效,适合低复杂度场景 |
| Type 1 | 对数分段 + 查表 | 更精确的频率分辨率 |
2,支持的 Residue 类型:
| 类型 | 名称 | 适用场景 |
|---|---|---|
| Type 0 | 常规残差编码 | 通用场景 |
| Type 1 | 混合残差编码 | 复杂频谱 |
| Type 2 | 脉冲编码 | 瞬态信号 |
| Type 3 | 稀疏编码 | 稀疏频谱 |
3,支持的 Mapping 类型:
| 类型 | 名称 | 说明 |
|---|---|---|
| Type 0 | 独立声道 | 各声道独立编码/解码 |
| Type 1 | 耦合立体声 | 使用 M/S(Mid/Side)编码 |
4,Backend 协作关系图
cpp
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Floor Backend │ │ Residue Backend │ │ Mapping Backend│
│ (低频/中频) │ │ (高频细节) │ │ (声道映射) │
└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
│ │ │
└───────────┬──────────┘ │
▼ │
┌─────────────┐ │
│ 完整频谱 │ │
└──────┬──────┘ │
│ │
▼ │
┌─────────────┐ │
│ MDCT │◄─────────────────────────┘
│ 逆变换 │
└──────┬──────┘
│
▼
┌─────────────┐
│ PCM 输出 │
└─────────────┘
2.3.3 mode
| 字段 | 比特数 | 范围 | 说明 | |
|---|---|---|---|---|
| blocksize | 6 | 值需要+1,因此存储只存储的N-1的值 | ||
| blocksize控制 | blockflag |
1 | 0/1 | 0=短块(1024采样), 1=长块(4096采样) |
| blocksize控制 | windowtype |
16 | 0~VI_WINDOWB-1 | 窗口函数类型 |
| blocksize控制 | transformtype |
16 | 0~VI_WINDOWB-1 | MDCT 变换类型 |
| blocksize控制 | mapping |
8 | 0~maps-1 | 声道映射索引,按标准索引声道即可 |
1,blockflag
| 值 | 块大小 | 适用场景 |
|---|---|---|
| 0 | 短块(通常 1024 采样) | 瞬态信号(鼓点、冲击声) |
| 1 | 长块(通常 4096 采样) | 稳态信号(持续音调) |
设计原理:
- 短块:时间分辨率高,频率分辨率低,适合捕捉快速变化的信号
- 长块:频率分辨率高,时间分辨率低,适合持续的稳态信号
2,windowtype
窗口函数作用:在 MDCT 变换前后应用,减少频谱泄漏
| 窗口类型 | 特点 |
|---|---|
| 0 | 正弦窗口 |
| 1 | 改进的正弦窗口 |
| 其他 | 预留扩展 |
3,transformtype
| 变换类型 | 说明 |
|---|---|
| 0 | 标准 MDCT |
| 其他 | 预留扩展 |
2.4 Audio Packets
本章节之介绍基础的格式,具体的音频数据格式涉及解码部分,本文不做详细介绍,有兴趣可以去阅读标准文档
cpp
音频数据包结构:
┌─────────────────────────────────────────────────────────┐
│ packet_type (1b) = 0 │ 标识这是音频数据包 │
├─────────────────────────────────────────────────────────┤
│ mode (modebits) │ 模式索引 │
├─────────────────────────────────────────────────────────┤
│ [lW (1b) + nW (1b)] │ 仅长块: 前后窗口类型 │
├─────────────────────────────────────────────────────────┤
│ mapping-specific data │ 映射相关数据 │
│ ├─ floor 编码数据 │ │
│ ├─ residue 编码数据 │ │
│ └─ coupling 编码数据
│ └─ MDCT 逆变换 → PCM │ │
└─────────────────────────────────────────────────────────┘
1,基础字段介绍:
| 字段 | 比特数 | 说明 |
|---|---|---|
packet_type |
1 | 必须为 0(头包为 1/3/5) |
mode |
modebits |
模式索引(通常 1-6 bits) |
lW |
1 | 仅长块:左窗口类型 |
nW |
1 | 仅长块:右窗口类型 |
mapping_data |
可变 | floor/residue/coupling /MDCT编码数据 |
1,mode
每个音频数据包开头包含模式索引,决定:
- 块大小(短块 1024 或长块 4096)
- 窗口类型(平滑过渡或陡峭过渡)
- 映射配置(声道耦合方式
2, 窗口类型(nw,lw)
| 值 | 窗口类型 | 适用场景 |
|---|---|---|
| 0 | 正弦窗口 | 平滑过渡,避免瞬态 |
| 1 | 调谐窗口 | 陡峭过渡,保留瞬态 |
3,与头包的区别
| 特性 | 头包 | 音频数据包 |
|---|---|---|
packet_type |
1/3/5 | 0 |
| 内容 | 编解码参数 | 音频数据 |
| 数量 | 固定 3 个 | 可变(取决于时长) |
| 大小 | 较大(含码本等) | 较小(一帧数据) |
b_o_s |
第一个头包为 1 | 0 |
e_o_s |
0 | 最后一包为 1 |