OGG 头的示意图
这张图画的很好,借用一下,图片来源:https://chenliang.org/2020/03/14/ogg-container-format/
然后我们就一个具体的例子来解读,增强记忆
第一个页
第一部分:Ogg 页面头(Ogg Page Header)
4F 67 67 53 00 02 00 00 00 00 00 00 00 00 7B C0 CC 0B 00 00 00 00 01 BA D3 2F 01 13 4F 70 75 73 48 65 61 64 01 01 38 01 80 BB 00 00 00 00 00
前 27 字节是 Ogg 页面头(Ogg Page Header),标准长度为 27 字节。
Offset: 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
Bytes: 4F 67 67 53 00 02 00 00 00 00 00 00 00 00 7B C0 CC 0B 00 00 00 00 01 BA D3 2F 01
ASCII: O g g S
字段 | 字节 | 值 | 说明 |
---|---|---|---|
capture_pattern |
4F 67 67 53 |
"OggS" |
Ogg 文件标识符 |
version |
00 |
0 | Ogg 格式版本 |
header_type |
02 |
2 | 页面类型:0x02 表示这是 初始页(bos),通常是头信息 |
granule_position |
00 00 00 00 00 00 00 00 |
0 | 音频采样位置(此处为 0,因为是头页) |
bitstream_serialno |
7B C0 CC 0B |
0x0BCC C07B |
流的唯一 ID(小端序) |
page_sequence_no |
00 00 00 00 |
0 | 页面序号,头页为 0 |
CRC_checksum |
01 BA D3 2F |
CRC32 校验和 | 用于校验 |
page_segments |
01 |
1 | 该页包含 1 个 segment |
第二部分:Segment Table + OpusHead 数据
接下来是 segment table 和实际数据。
page_segments
是01
,表示有 1 个 segment。- segment table 是 1 字节:
13
→ 表示第一个 segment 长度为 0x13 = 19 字节
所以接下来的 19 字节是 OpusHead 数据:
Offset: 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
Bytes: 4F 70 75 73 48 65 61 64 01 01 38 01 80 BB 00 00 00 00 00
ASCII: O p u s H e a d
字段 | 字节 | 值 | 说明 |
---|---|---|---|
magic_signature |
8 字节 | 4F 70 75 73 48 65 61 64 |
"OpusHead" ASCII |
version |
1 字节 | 01 |
版本号,必须为 1 |
channel_count |
1 字节 | 01 |
声道数:1(单声道) |
pre_skip |
2 字节(小端) | 38 01 → 0x0138 = 312 |
编码器在解码时应跳过的样本数 |
input_sample_rate |
4 字节(小端) | 80 BB 00 00 → 0x0000BB80 = 48000 Hz |
输入采样率(仅作参考) |
output_gain |
2 字节(小端) | 00 00 |
增益(dB × 256),0 表示无增益 |
channel_mapping_family |
1 字节 | 00 |
通道映射族:0 表示无特殊映射(RTP 标准) |
详细解释:
- ✅
magic_signature
:"OpusHead"
,确认是 Opus 头。 - ✅
version = 1
:符合规范。 - 🎧
channel_count = 1
:单声道音频。 - ⏩
pre_skip = 312
:解码后应丢弃前 312 个样本,用于消除编码器引入的前导噪声。 - 📏
input_sample_rate = 48000
:原始输入采样率为 48 kHz(Opus 内部始终以 48kHz 运行,此字段仅作提示)。 - 🔊
output_gain = 0
:播放时无需调整音量。 - 🔗
channel_mapping_family = 0
:使用简单流,无复杂通道映射(适用于单声道或立体声)。
第二页
4F 67 67 53 00 00 00 00 00 00 00 00 00 00 7B C0 CC 0B 01 00 00 00 15 6E F0 0B 01 3D
4F 70 75 73 54 61 67 73 0C 00 00 00 4C 61 76 66 36 32 2E 34 2E 31 30 31 01 00 00 00 1D 00 00 00 65 6E 63 6F 64 65 72 3D 4C 61 76 63 36 32 2E 31 33 2E 31 30 31 20 6C 69 62 6F 70 75 73
Ogg Page Header 解析(前 27 字节)
偏移 | 字节 | 内容 | 说明 |
---|---|---|---|
0x00 | 4F 67 67 53 |
"OggS" |
Ogg 页头标识 |
0x04 | 00 |
版本号 | 0,标准版本 |
0x05 | 00 |
标志位 | 0x00:普通页,非开始、非结束 |
0x06 | 00 00 00 00 00 00 00 00 |
Granule Position | 64位,当前为 0 → 通常是首帧之后的数据页或注释页 |
0x0E | 00 00 00 00 |
Stream Serial Number | 流 ID = 0 |
0x12 | 7B C0 CC 0B |
Page Sequence No | 小端表示:0B CC C0 7B = 197,865,595 (十进制) |
0x16 | 15 6E F0 0B |
Checksum | 小端:0B F0 6E 15 = 210,562,581 (校验用) |
0x1A | 01 |
Segment 数量 | 1 个 segment |
0x1B | 3D |
Segment Table | 该 segment 长度为 0x3D = 61 字节 |
接下来的 61 字节是 Opus 的 注释头(Comment Header),结构如下:
1. "OpusTags" 标识(8 字节)
4F 70 75 73 54 61 67 73 → ASCII: "OpusTags"
✅ 表示这是 Opus 的 注释头(Comment Header),不是识别头(Identification Header)。
2. Vendor String Length(4 字节,小端)
0C 00 00 00 → 0x0000000C = 12
→ Vendor 字符串长度为 12 字节
3. Vendor String(12 字节)
4C 61 76 66 36 32 2E 34 2E 31 30 31 → "Lavf62.4.101"
Lavf
= Libavformat(FFmpeg 的封装库)62.4.101
= FFmpeg 的版本号
👉 表示这个文件是 用 FFmpeg 62.4.101 版本生成的。
4. User Comment List Length(4 字节,小端)
01 00 00 00 → 0x00000001 = 1
→ 有 1 条用户注释
5. 第一个注释:长度 + 内容
长度(4 字节,小端):
1D 00 00 00 → 0x1D = 29 字节
内容(29 字节):
65 6E 63 6F 64 65 72 3D 4C 61 76 63 36 32 2E 31 33 2E 31 30 31 20 6C 69 62 6F 70 75 73
ASCII 解码:
encoder=Lavc62.13.101 libopus
Lavc
= Libavcodec(FFmpeg 的编码库)62.13.101
= 编码器版本libopus
= 使用的 Opus 编码器后端
👉 表示音频是用 FFmpeg 的 libavcodec 调用 libopus 编码器完成的。
第三页(第一个数据包)
4F 67 67 53 00 00 80 BB 00 00 00 00 00 00 7B C0 CC 0B 02 00 00 00 D0 E4 54 E5 32 3A 36 3E 39 37 36 32 3D 40 39 38 3C 3A 38 38 2F 33 36 33 3B 3C 3F 49 43 40 38 3A 40 3C 3A 3E 3A 3E 35 34 33 31 31 2F 2E 33 2F 33 35 31 37 3A 36 3E 39 78 80 D3 60 D2 DA CB 7F 25 98 67 83 D8 E8 67 61 E1 3C
Ogg Page Header 解析(前 27 字节)
偏移 | 字节 | 内容 | 说明 |
---|---|---|---|
0x00 | 4F 67 67 53 |
"OggS" |
Ogg 标识 |
0x04 | 00 |
版本号 | 0 |
0x05 | 00 |
标志位 | 0x00 → 普通数据页 |
0x06 | 80 BB 00 00 00 00 00 00 |
Granule Position | 小端 64 位:0xBB80 = 48000 |
0x0E | 7B C0 CC 0B |
Stream Serial No | 小端:0B CC C0 7B = 197,865,595 |
0x12 | 02 00 00 00 |
Page Sequence No | 小端:2 → 第 3 页 |
0x16 | D0 E4 54 E5 |
Checksum | 0xE554E4D0 (用于校验) |
0x1A | 32 |
Segment Count | 0x32 = 50 → 有 50 个 segments |
Segment Table(Lacing Values)------接下来的 50 字节
从 offset 0x1B
开始,有 50 个字节,每个字节表示一个 segment 的长度(单位:字节),称为 lacing value。
这些值是:
3A 36 3E 39 37 36 32 3D 40 39 38 3C 3A 38 38 2F 33 36 33 3B 3C 3F 49 43 40 38 3A 40 3C 3A 3E 3A 3E 35 34 33 31 31 2F 2E 33 2F 33 35 31 37 3A 36 3E 39
我们将其转换为 十进制长度(每个是 1 字节):
58, 54, 62, 57, 55, 54, 50, 61, 64, 57, 56, 60, 58, 56, 56,
47, 51, 54, 51, 59, 60, 63, 73, 67, 64, 56, 58, 64, 60, 58,
62, 58, 62, 53, 52, 51, 49, 49, 47, 46, 51, 47, 51, 53, 49,
55, 58, 54, 62, 57
✅ 总共 50 个 Opus 数据包(packets),每个包的大小如上。
Granule Position = 48000
- Opus 使用 48 kHz 内部采样率
- Granule Position 表示"到本页为止累计的音频样本数"
48000
= 正好 1 秒(48000 个样本)
👉 表示这是 前 1 秒音频的结束位置