ogg-opus协议解析示例

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_segments01,表示有 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 010x0138 = 312 编码器在解码时应跳过的样本数
input_sample_rate 4 字节(小端) 80 BB 00 000x0000BB80 = 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 秒音频的结束位置