音频格式之OGG

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