博客主页:https://blog.csdn.net/wkd_007
博客内容:嵌入式开发、Linux、C语言、C++、数据结构、音视频
本文内容:介绍 opus 编码的 Ogg 封装文件
金句分享:
本文未经允许,不得转发!!!
前面的文章 Ogg封装格式详解 介绍了Ogg格式的封装,介绍了封装过程、Ogg的页结构、Ogg的段,但没涉及到每个段的内容,因为这跟编码有关系。本文就介绍 opus 编码在 Ogg 封装文件中是怎样存储的,同样地,从一个 opus 编码的Ogg文件去查看十六进制,最终了解 opus 编码 Ogg 封装文件的整个结构,这样就可以轻易将这类文件解码为PCM了。
opus 编码的Ogg文件结构如下图:
opus编码的逻辑Ogg比特流中,第一页必须包含ID头部数据
,其结构如下图,包括8个字段,各个字段代表的含义如下:
1、Magic Signature(8字节):
这是一个8位字节(64位)字段,允许编解码器识别,并且是人类可读的。它按顺序包含以下的字母:
0x4F 'O'
0x70 'p'
0x75 'u'
0x73 's'
0x48 'H'
0x65 'e'
0x61 'a'
0x64 'd'
从“Op”开始有助于将其与音频数据包区分开来,因为这是一个无效的TOC序列。
2、Version (1字节, unsigned):
对于此版本的封装规范,版本号必须始终为“1”。实现应该将版本号的前四位与已识别规范的版本号匹配的流视为与该规范向后兼容。也就是说,版本号可以分为“主要”和“次要”版本子字段,次要子字段的变化(在较低的四位中)表示兼容的变化。例如,本规范的实现应该接受版本号为“15”或更低的任何流,并且应该假设版本号为”16“或更高的任何流不兼容。选择初始版本“1”是为了防止实现依赖此八位字节作为“OpusHead”字符串的null终止符。
3、Output Channel Count ‘C’ (1字节, unsigned)
这是输出通道的数量。这可能与编码信道的数量不同,编码信道的数目可以在逐包的基础上改变。此值不得为零。最大允许值取决于通道映射族,可能大到255。详见[RFC7845]
文档的第5.1.1节。
4、Pre-skip (2字节, unsigned, little endian):
这是开始播放时要从解码器输出中丢弃的采样数(48 kHz),也是要从页面的颗粒位置中减去以计算其PCM采样位置的数量。当裁剪现有Ogg Opus流的开头时,建议预跳过至少3840个样本(80ms),以确保解码器完全收敛。
5、Input Sample Rate (4字节, unsigned, little endian):
这是原始输入(编码前)的采样率,单位为Hz。该字段不是用于播放编码数据的采样率。
Opus可以在4、6、8、12和20kHz的内部音频带宽之间切换。流中的每个数据包可以具有不同的音频带宽。不管音频带宽如何,参考解码器都支持以8、12、16、24或48kHz的采样率对任何流进行解码。传递到编码器的音频的原始采样率不被有损压缩所保留。
Ogg Opus播放器应根据以下程序选择播放采样率:
1.如果硬件支持48 kHz播放,则以48 kHz进行解码。
2.否则,如果硬件的最高可用采样率是支持的速率,则以该采样率进行解码。
3.否则,如果硬件的最高可用采样率小于48kHz,则以高于最高可用硬件速率的下一个更高的Opus支持速率进行解码并重新采样。
4.否则,以48 kHz解码并重新采样。
然而,“输入采样率”字段允许复用器将原始输入流的采样率作为元数据传递。当用户需要输出采样率来匹配输入采样率时,这是有用的。例如,当不播放输出时,将PCM格式样本写入磁盘的实现可能会选择将音频重新采样回原始输入采样率,以减少用户的意外,因为用户可能会合理地期望以相同的采样率返回文件。
零值表示“未指定”。复用器应该写入实际输入采样率或零,但使用该字段执行某些操作的实现应该注意,如果给定疯狂的值(例如,如果请求,不要实际将输出上采样到10MHz),则行为要理智。实现应支持 8kHz 和 192kHz(包括8kHz)之间的输入采样率。此范围之外的速率可以通过回落到默认速率 48kHz 来忽略。
6、Output Gain (2字节, signed, little endian):
这是解码时要应用的增益。它是对解码器输出进行缩放以获得所需播放量的因子的20*log10,存储在具有8个小数比特的16比特、带符号的2的补码定点值中(即Q7.8[Q-NOTATION])。
为了应用增益,实现可以使用以下内容:
sample *= pow(10, output_gain/(20.0*256))
其中“output_gain”是来自标头的原始16位值。
玩家和媒体框架应该默认应用它。如果播放器选择应用任何音量调整或增益修改,如R128_TRACK_AIN(见第5.2节),则除了此输出增益外,还必须应用该调整,以实现标准化音量的播放。
复用器应该将该字段设置为零,而不是在编码之前应用任何增益,只要这是可能的,并且不会与用户的愿望相冲突。非零输出增益表示在编码之后调整了增益,或者用户希望在保持恢复原始信号幅度的能力的同时调整增益以进行回放。
尽管输出增益具有巨大的范围(+/-128 dB,足以将听不见的声音放大到身体疼痛的阈值),但大多数应用程序只能合理地使用零附近这个范围的一小部分。大范围的部分作用是确保增益始终可以在OpusHead和R128增益标签之间无损传输(见下文)而不会饱和。
7、Channel Mapping Family (1字节, unsigned):
这个八位字节表示输出通道的顺序和语义。
该八位位组的每个当前指定值都指示一个映射族,该映射族定义了一组允许的通道计数,以及每个允许的通道数的有序通道名称集。详见[RFC7845]
文档的第5.1.1节。
8、Channel Mapping Table:
此表定义了从编码流到输出通道的映射。其内容见[RFC7845]
文档的第5.1.1节。ID标头中的所有字段都是必需的,但“通道映射表”除外,当通道映射族为0时,必须省略该字段,否则为必需字段。如果流包含的ID标头没有足够的数据用于这些字段,即使它包含有效的“魔术签名”,实现也应该将其视为无效。该规范的未来版本,甚至是向后兼容的版本,可能会在ID头中包含额外的字段。如果ID标头具有兼容的主版本,但具有较大的次版本,则实现不得将其视为无效,因为它包含此处未指定的其他数据,前提是它仍然在第一页上完成。
现在按照上面的字段,分析opus编码的Ogg文件,用Notepad打开 48000Hz-s16le-1ch-ChengDu.opus 并查看十六进制模式:
OpuaHead
;1
;1
,表示单声道;0x0138
;0xbb80
,对应十进制 48000
;0
,代表使用映射族0,允许的通道数:1或2,且省略通道映射表(Channel Mapping Table);opus编码的逻辑Ogg比特流中,第二页必须包含注释头部数据
。它可能跨越多个页面(page),从逻辑流的第二页开始。无论它跨越多少页,注释头数据包都必须结束在一个完整的页面,其结构如下图,包括6个字段,各个字段代表的含义如下:
1、Magic Signature(8个字节):
这是一个8位字节(64位)字段,允许编解码器识别,并且是人类可读的。它按顺序包含以下神奇数字:
0x4F 'O'
0x70 'p'
0x75 'u'
0x73 's'
0x54 'T'
0x61 'a'
0x67 'g'
0x73 's'
从“Op”开始有助于将其与音频数据包区分开来,因为这是一个无效的TOC序列。
2、Vendor String Length (4个字节, unsigned, little endian):
此字段给出下个字段Vendor String
的长度,单位为八位字节。它不得指示供应商字符串比数据包的其余部分长。
3、Vendor String (variable length, UTF-8 vector):
这是一个用于供应商信息的简单可读标签,编码为UTF-8字符串[RFC3629]。不需要终止空八位字节。
此标签旨在识别编解码器编码器和封装实现,以跟踪技术行为的差异。面向用户的应用程序可以使用“ENCODER”用户注释标记来标识自己。
4、User Comment List Length (4个字节, unsigned, little endian):
此字段指示用户提供的注释个数。它可能表示用户提供的注释为零,在这种情况下,数据包中没有其他字段。它不能表明有太多的注释,以至于注释字符串长度需要比数据包其余部分更多的数据。
5、User Comment #i String Length (4个字节, unsigned, little endian):
此字段给出下个字段User Comment #i String
的长度,单位为八位字节。“用户评论列表长度”字段指示的每个用户评论都有一个。它不能指示字符串比数据包的其余部分长。
6、User Comment #i String (variable length, UTF-8 vector):
此字段包含编码为UTF-8字符串的单个用户注释[RFC3629]。“用户评论列表长度”字段指示的每个用户评论都有一个。
如果文章有帮助的话,点赞、收藏⭐,支持一波,谢谢