【音视频 | opus】RFC7845:Opus音频编解码器的Ogg封装(Ogg Encapsulation for the Opus Audio Codec)

😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
🤣本文内容🤣:🍭介绍Opus音频编解码器的Ogg封装🍭
😎金句分享😎:🍭🍭

本文未经允许,不得转发!!!


🎄概述

本文档定义了Opus交互式语音和音频编解码器的Ogg封装。这允许以Opus格式编码的数据存储在Ogg逻辑比特流中。

文章从 Ogg Encapsulation for the Opus Audio Codec 摘取了笔者想了解的个别章节,为了方便对照,小节序号按照原文的。

在这里插入图片描述

🎄1、介绍

IETF Opus编解码器是一种针对语音和通用音频进行优化的低延迟音频编解码器。有关技术细节,请参见[RFC6716]。本文档定义了Opus在连续逻辑Ogg比特流中的封装[RFC3533]。Ogg封装为Opus提供了一种长期存储格式,支持所有基本功能,包括元数据、快速准确的查找、损坏检测、错误后重新捕获、低开销,以及以最小缓冲将Opus与其他编解码器(包括视频)多路复用的能力。它还提供了一种实时流媒体格式,能够通过可靠的面向流的传输进行传输,而不需要所有数据(甚至数据的总长度),其形式与磁盘存储格式相同。

Ogg比特流由一系列“页面”组成,每个页面都包含来自一个或多个“数据包”的数据。页面是Ogg流中多路复用的基本单元。每个页面都与一个特定的逻辑流相关联,包含一个捕获模式和校验和、标记逻辑流开始和结束的标志,以及表示流中绝对位置的“颗粒位置”,以帮助查找。单个页面可以包含来自多达255个不同分组的多达65025个八位字节的分组数据。数据包可以在页面之间任意分割,并从一个页面延续到下一个页面(允许数据包比单个页面大得多)。每个页面都包含指示如何将数据划分为数据包的“排列值”,从而允许解复用器(解复用器)在不检查编码数据的情况下恢复数据包边界。当页面包含与数据包对应的最终lacing value时,数据包在页面上被称为“完成”。

这种封装定义了数据包数据的内容,包括必要的报头、将这些数据包组织成逻辑流,以及对编解码器特定颗粒位置字段的解释。它不试图描述或指定现有的Ogg容器格式。鼓励不熟悉上述基本概念的读者查阅[RFC3533]中的详细信息。

在这里插入图片描述

🎄3、数据包结构

Ogg-Opus流的组织如下(示例见图1)。


        Page 0         Pages 1 ... n        Pages (n+1) ...
     +------------+ +---+ +---+ ... +---+ +-----------+ +---------+ +--
     |            | |   | |   |     |   | |           | |         | |
     |+----------+| |+-----------------+| |+-------------------+ +-----
     |||ID Header|| ||  Comment Header || ||Audio Data Packet 1| | ...
     |+----------+| |+-----------------+| |+-------------------+ +-----
     |            | |   | |   |     |   | |           | |         | |
     +------------+ +---+ +---+ ... +---+ +-----------+ +---------+ +--
     ^      ^                           ^
     |      |                           |
     |      |                           Mandatory Page Break
     |      |
     |      ID header is contained on a single page
     |
     'Beginning Of Stream'

    Figure 1: Example Packet Organization for a Logical Ogg Opus Stream

有两个必需的标头数据包。逻辑Ogg比特流中的第一个数据包必须包含标识(ID)的头部数据,该头部数据将流唯一标识为Opus音频。本头部数据的格式见第5.1节。它被 单独 放置在逻辑Ogg比特流的第一页上(没有任何其他分组数据),并在该页上完成。此页面设置了“流的开始”标志。

逻辑Ogg比特流中的第二个数据包必须包含注释头部数据,其中包含用户提供的元数据。本头部数据的格式见第5.2节。它可能跨越多个页面(page),从逻辑流的第二页开始。无论它跨越多少页,注释头数据包都必须结束在一个完整的页面。

所有后续页面都是音频数据页面,它们包含的Ogg数据包是音频数据包。每个音频数据包包含用于N个不同流中的每个流的一个Opus包,其中N通常是单声道或立体声的一个,但对于多声道音频可能大于一个。值N在ID头中指定(见第5.1.1节),并且在逻辑Ogg比特流的整个长度上是 固定 的。

第一个(N-1)Opus数据包,如果有的话,使用[RFC6716]附录B中的自定界帧,一个接一个地打包到Ogg数据包中。剩余的Opus数据包使用[RFC6716]第3节中的常规、未限制的帧被打包在Ogg数据包的末尾。单个Ogg数据包中的所有Opus数据包必须被限制为具有相同的持续时间。本规范的实现应该将持续时间与Ogg数据包中第一个Opus数据包的持续时间不同的任何Opus数据包包视为具有无效目录(TOC)序列的格式错误的Opus数据。

每个Opus数据包开头的TOC序列指示编码模式、音频带宽、信道计数、持续时间(帧大小)和每个数据包的帧数,如[RFC6716]第3.1节所述。编码模式是SILK、混合或约束能量重叠变换(CELT)之一。编码模式、音频带宽和帧大小的组合被称为Opus分组的配置。

数据包按顺序放入Ogg页面,直到流结束。音频数据包可能跨越页面边界。例如,如果第一个音频数据页是在广播中途加入的直播流,并且标题粘贴在前面,则它可以设置“连续数据包”标志(指示第一个音频信息包从上一个页面继续)。如果页面设置了“continued packet”标志,并且以下条件之一也为真:

  • 具有数据包数据的前一页没有以连续的数据包结束(没有以255的换行值结束)或
  • 页面序列号不是连续的,

则解复用器决不能试图对页面上的第一个分组的数据进行解码,除非解复用器具有一些特殊知识。实现必须将零八位字节音频数据包视为[RFC6716]第3.4节中描述的格式错误的Opus数据包。

逻辑流以设置了“end of stream”标志的页结束,但需要准备好实现来处理没有标记为“end of stream”的页的截断流。最后一页上的最后一个数据包没有理由是连续的数据包,例如:最终的lacing value为255。然而,解复用器可能会遇到这样的流,这可能是由于传输未完成或损坏的结果。如果数据包继续到下一页(即,当该页以255的lacing value结束时),并且以下条件之一也成立:

  • 具有数据包数据的下一页没有设置“继续数据包”标志,或者
  • 没有包含数据包的下一页,或者
  • 页面序列号不是连续的,

则解复用器决不能试图解码来自该分组的数据,除非解复用器具有允许其解释该数据的某些特殊知识。在标记为“流结束”的页面之后,Opus逻辑位流中不得有更多页面。

在这里插入图片描述

🎄4、Granule Position

ID头部数据页和注释头部数据完成的页面的颗粒位置(Granule Position)必须为零。也就是说,逻辑流中的第一页和第一音频数据页之前的最后一个头页都具有零的粒度位置(Granule Position)。

音频数据页的颗粒位置(Granule Position)可以翻译成该页的PCM样本总数(直到并包括在该页上完成的最后一个数据包的最后完全可解码样本)。第一个音频数据页的颗粒位置(Granule Position)通常会大于零,如第4.5节所述。

完全由单个数据包跨越的页面(在随后的页面上完成)没有颗粒位置(Granule Position),并且颗粒位置字段设置为二进制补码中的特殊值“-1”。

音频数据页的颗粒位置(Granule Position)是以固定速率为48 kHz的PCM音频样本为单位的(意思是48000的倍数),(每个通道;立体声流的颗粒位置不会以单声道流的两倍速度递增)。可以以其他采样率运行Opus解码器,但所有Opus分组都以平均划分48kHz的采样率对样本进行编码。因此,假设48kHz解码速率,颗粒位置(Granule Position)字段中的值总是对样本进行计数,而本说明书的其余部分也做出了相同的假设。

在[RFC6716]中定义的Opus包的持续时间可以是2.5ms的任意倍数,最大可达120ms。该持续时间在每个数据包的开头被编码在TOC序列中。解码器返回的样本数量恰好对应于该持续时间,即使对于最初的几个分组也是如此。例如,馈送到以48kHz运行的解码器的20ms分组将始终返回960个样本。解复用器可以解析每个Ogg分组开始时的TOC序列,以从具有已知颗粒位置(Granule Position)的分组(即,在某个页面上完成的最后一个分组)向后或向前工作,以便将颗粒位置(Granule Position)分配给每个分组,甚至每个单独的样本。一个例外是流中的最后一页,如下所述。

在第一页之后有完整数据包的所有其他页面的颗粒位置(Granule Position)必须等于该页面上完成的数据包中包含的样本数量加上最近一页有完成数据包的颗粒位置(Granule Position)。这保证了解复用器在向前工作时和向后工作时可以为单个数据包分配相同的粒度位置(Granule Position)。要做到这一点,就不能有任何间隙(gaps)。

✨4.1 修复实时流中的间隙

为了支持捕获丢失或未传输数据包的实时流,多路复用器(muxer)应该发出明确请求使用数据包丢失隐藏(PLC)代替丢失数据包的数据包。未能做到这一点的实现仍然不能将页面的颗粒位置增加除该页面上实际完成的数据包中包含的样本数之外的任何其他值。

只有2.5毫秒倍数的间隙是可修复的,因为这是数据包丢失或不连续传输可能产生的唯一持续时间。复用器不需要处理其他间隙大小。创建必要的数据包包括合成一个TOC字节(在[RFC6716]的第3.1节中定义)——以及需要的任何额外的内部帧——以指示每个流的数据包持续时间。数据包内每个丢失的Opus帧的实际长度为零字节,如[RFC6716]第3.2.1节所定义。

可以使用代码0、1、2或3中的任何一个将零字节帧打包成分组。当连续的帧具有相同的配置时,较高的代码封装减少了开销。同样,如果TOC配置匹配,则复用器可以进一步将空帧与先前或随后的非零长度帧组合(使用代码2或可变比特率(VBR)代码3)。

[RFC6716]没有对PLC提出任何要求,但本节概述了预计会对大多数PLC实施产生积极影响的选择,包括参考实施。合成的TOC序列应该保持与前一个数据包(如果有的话)相同的模式、音频带宽、信道计数和帧大小。这是PLC处理的最简单且通常测试最充分的情况,它涵盖了不包括配置开关的所有损失,如[RFC6716]第4.5节所定义。

当前一个数据包可用时,保持音频带宽和通道计数相同允许PLC在其生成的隐藏数据中提供最大的连续性。然而,如果间隙的大小不是最近帧大小的倍数,那么至少对于某些帧,帧大小将不得不改变。此类更改应尽可能长时间延迟,以简化PLC实施过程。

例如,95ms的间隙可以被编码为具有单个恒定比特率(CBR)码3分组的两个字节中的十九个5ms帧。如果之前的帧大小是20ms,那么使用四个20ms帧然后使用三个5ms帧需要4个字节(加上额外的Ogg系带开销字节),但允许PLC尽可能长时间地使用其经过良好测试的稳态行为。后一种方法的总比特率(包括Ogg开销)约为0.4kbps,因此对文件大小的影响最小。

不鼓励更改模式,因为这会导致一些解码器实现重置其PLC状态。但是,SILK和Hybrid模式帧无法填充不是10ms倍数的间隙。如果需要切换到CELT模式以匹配间隙大小,则复用器应在间隙结束时进行切换,以使PLC尽可能长时间地工作。

在上面的示例中,如果前一帧是20ms SILK模式帧,则更好的解决方案是合成描述四个20ms SILK帧的分组,然后是具有单个10ms SILK帧的分组、最后是具有5ms CELT帧的分组以填充95ms的间隙。这也需要四个字节来描述合成的分组数据(两个字节用于CBR代码3,每个字节用于两个代码0分组),但是需要三个字节的Ogg鞋带开销来标记分组边界。在0.6kbps的速率下,这仍然是一个对原始低质量解决方案的最小比特率影响。

由于中频音频仅在SILK模式下是一个选项,因此如果从该配置切换到CELT模式,则应生成宽带帧,以确保任何试图在模式之间迁移状态的PLC实现都能够保留所有可用的音频带宽。

✨4.2 Pre-skip

在解码过程中引入了一定量的延迟,以允许CELT模式中的重叠、SILK模式中的立体声混合以及重新采样。编码器可能通过自己的重新采样和分析引入了额外的延迟(尽管没有指定确切的数量)。因此,解码器产生的前几个样本并不对应于真实的输入音频,而是由编码器插入的填充组成,以补偿这种延迟。这些样本需要存储和解码,因为Opus是一种渐近收敛的预测编解码器,这意味着每帧的解码内容取决于解码器输入的最近历史。然而,玩家在对这些样本进行解码后会希望跳过这些样本。

ID标头中的“Pre-skip”字段(见第5.1节)表示在流开始时应该跳过(解码但丢弃)的样本数量,尽管某些特定应用程序可能有查看该数据的原因。该量不需要是2.5ms的倍数,可以小于单个数据包,或者可以跨越多个数据包的内容。这些样本不是有效的音频。

例如,如果第一个Opus帧使用CELT模式,它将始终产生120个窗口重叠相加数据样本。然而,重叠数据最初都是零(因为没有先前帧),这意味着这通常不能准确地表示原始音频。SILK模式需要额外的延迟来考虑其分析和重新采样延迟。编码器延迟原始音频以避免此问题。

“Pre-skip”字段也可用于对已编码的流执行采样精确裁剪。在这种情况下,至少3840个样本(80ms)的值为解码器提供了足够的历史,即它将在流的输出开始之前收敛。

✨4.3 PCM样本位置

PCM样品位置由颗粒位置(granule position)确定,使用以下公式:

'PCM sample position' = 'granule position' - 'pre-skip'

例如,如果第一音频数据页的颗粒位置(granule position)是59971,并且预跳过(Pre-skip)是11971,则来自该页的最后解码样本的PCM样本位置是48000。

可以使用以下公式将其转换为播放时间:

                                    'PCM sample position'
                  'playback time' = ---------------------
                                           48000.0

播放任何采样之前的初始PCM采样位置通常为“0”。在这种情况下,要播放的第一个音频样本的PCM样本位置从“1”开始,因为它在时钟_after_上标记了该样本已经播放的时间,并且正好一秒长的流的最终PCM样本位置为“48000”,如这里的示例中所示。

Vorbis流使用小于第一音频数据页中包含的音频样本数量的颗粒位置来指示这些样本中的一些样本是从输出中修剪的(参见[FORBIS-TRIM])。然而,为了做到这一点,Vorbis要求第一音频数据页恰好包含两个分组,以便允许解码器在需要返回任何PCM数据之前执行PCM位置调整。Opus使用预跳过机制来实现这一目的,因为编码器可能会引入超过单个数据包的延迟,并且由于具有大量通道的流中的非常大的数据包可能无法容纳在单个页面上。

✨4.4 端部修整

设置了“end of stream”标志的页面可能具有一个颗粒位置,该位置指示该页面包含的音频数据比通常通过解码最终数据包返回的音频数据更少。这用于在偶数帧边界之外的其他地方结束流。具有已完成数据包的最近音频数据页的颗粒位置用于进行此确定,或者如果以前没有具有已完成的数据包的音频数据页,则使用“0”。这些颗粒位置之间的差异指示在解码最后一页上完成的数据包后要保留多少样本。剩余的样本将被丢弃。丢弃样本的数量应不大于从上一个数据包解码的数量。

✨4.5 对初始颗粒位置(Granule Position)的限制

具有完成的数据包的第一音频数据页的颗粒位置(Granule Position)可能大于在该页上完成的数据包包含的样本数量。但是,它不能更小,除非该页面设置了“流结束”标志。允许大于样本数的颗粒位置(Granule Position)允许裁剪流的开头或加入直播流,而无需重写所有剩余页面的颗粒位置(Granule Position)。这意味着就在要播放的第一个采样之前的PCM采样位置可能大于“0”。与其他逻辑流多路复用时的同步仍然使用相对于“0”的PCM采样位置来计算采样时间。这不会影响预跳过的行为:即使初始PCM样本位置大于零,也应该从解码输出的开始跳过“预跳过”样本。

另一方面,由于颗粒位置是非负的,因此小于解码样本数的颗粒位置防止解复用器向后工作以为每个数据包或每个单独样本分配有效的颗粒位置。如果颗粒位置小于在具有已完成数据包的第一个音频数据页上完成的数据包中包含的样本数,则实现必须将任何流视为无效,除非该页设置了“流结束”标志。它可能会推迟此操作,直到它对该页面上完成的最后一个数据包进行解码。

如果该页面设置了“流结束”标志,则解复用器必须将粒度位置小于“预跳过”量的任何流视为无效。这表明从初始解码输出中跳过的样本数比流中存在的样本数多。如果粒度位置小于在该页面上完成的包所生成的解码样本数,则解复用器必须使用初始粒度位置“0”,并可以从“0”开始向前处理到时间戳单个包。如果粒度位置大于可用的解码样本数,则解复用器仍必须如上所述向后处理,即使设置了“流结束”标志,以确定初始粒度位置,从而确定初始PCM样本位置。在这种情况下,这两个位置都将大于“0”。

Ogg文件中的搜索最好使用对页面的平分搜索来执行,该页面的颗粒位置对应于搜索目标处或之前的PCM位置。通过适当的加权平分,即使在千兆字节的文件中,平均只需一到两个平分就可以进行精确的搜索。有关一般实施指南的示例,请参见[SSEEKING]。

当在Ogg Opus流中搜索时,实现应该在搜索目标之前至少3840个样本(80ms)开始解码(并丢弃输出),以确保输出音频在到达搜索目标时是正确的。这种“预滚动”与流开始时使用的预跳过是分开的,并且与之无关。如果在寻道目标之前80ms的点在初始PCM采样位置之前,则实现应该从流的开头开始解码,正常应用预跳过,无论预跳过是否大于或小于80ms,然后继续丢弃采样以到达寻道目标(如果有)。

在这里插入图片描述

🎄5、头部数据包

Ogg-Opus逻辑流正好包含两个必需的头数据包:一个标识头和一个注释头。

✨5.1. ID头部数据

      0                   1                   2                   3
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |      'O'      |      'p'      |      'u'      |      's'      |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |      'H'      |      'e'      |      'a'      |      'd'      |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |  Version = 1  | Channel Count |           Pre-skip            |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                     Input Sample Rate (Hz)                    |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |   Output Gain (Q7.8 in dB)    | Mapping Family|               |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+               :
     |                                                               |
     :               Optional Channel Mapping Table...               :
     |                                                               |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

                        Figure 2: ID Header Packet

   The fields in the identification (ID) header have the following
   meaning:
  • 1、Magic Signature(64-bit):

    这是一个8位字节(64位)字段,允许编解码器识别,并且是人类可读的。它按顺序包含以下神奇数字:

    0x4F 'O'
    0x70 'p'
    0x75 'u'
    0x73 's'
    0x48 'H'
    0x65 'e'
    0x61 'a'
    0x64 'd'
    

    从“Op”开始有助于将其与音频数据包区分开来,因为这是一个无效的TOC序列。

  • 2、Version (8 bits, unsigned):

    对于此版本的封装规范,版本号必须始终为“1”。实现应该将版本号的前四位与已识别规范的版本号匹配的流视为与该规范向后兼容。也就是说,版本号可以分为“主要”和“次要”版本子字段,次要子字段的变化(在较低的四位中)表示兼容的变化。例如,本规范的实现应该接受版本号为“15”或更低的任何流,并且应该假设版本号为”16“或更高的任何流不兼容。选择初始版本“1”是为了防止实现依赖此八位字节作为“OpusHead”字符串的null终止符。

  • 3、Output Channel Count ‘C’ (8 bits, unsigned)

    这是输出通道的数量。这可能与编码信道的数量不同,编码信道的数目可以在逐包的基础上改变。此值不得为零。最大允许值取决于通道映射族,可能大到255。详见第5.1.1节。

  • 4、Pre-skip (16 bits, unsigned, little endian):

    这是开始播放时要从解码器输出中丢弃的采样数(48 kHz),也是要从页面的颗粒位置中减去以计算其PCM采样位置的数量。当裁剪现有Ogg Opus流的开头时,建议预跳过至少3840个样本(80ms),以确保解码器完全收敛。

  • 5、Input Sample Rate (32 bits, 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 (16 bits, 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 (8 bits, unsigned):

    这个八位字节表示输出通道的顺序和语义。

    该八位位组的每个当前指定值都指示一个映射族,该映射族定义了一组允许的通道计数,以及每个允许的通道数的有序通道名称集。详见第5.1.1节。

  • 8、Channel Mapping Table:

    此表定义了从编码流到输出通道的映射。其内容见第5.1.1节。ID标头中的所有字段都是必需的,但“通道映射表”除外,当通道映射族为0时,必须省略该字段,否则为必需字段。如果流包含的ID标头没有足够的数据用于这些字段,即使它包含有效的“魔术签名”,实现也应该将其视为无效。该规范的未来版本,甚至是向后兼容的版本,可能会在ID头中包含额外的字段。如果ID标头具有兼容的主版本,但具有较大的次版本,则实现不得将其视为无效,因为它包含此处未指定的其他数据,前提是它仍然在第一页上完成。

🎈5.1.1. Channel Mapping

Ogg Opus流允许将一个数量的Opus流(N)映射到可能更大数量的解码信道(M+N),再映射到另一数量的输出信道(C),其可能大于或小于解码信道的数量。这些通道的顺序和含义由通道映射定义,该映射由“通道映射族”八位位组组成,对于0族以外的通道映射族,则由“通道映像表”组成,如图3所示。

      0                   1                   2                   3
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
                                                     +-+-+-+-+-+-+-+-+
                                                     | Stream Count  |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     | Coupled Count |              Channel Mapping...               :
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

                      Figure 3: Channel Mapping Table

通道道映射表中的字段具有以下含义:

  • 1、Stream Count ‘N’ (8 bits, unsigned):

    这是在每个Ogg数据包中编码的流的总数。该值对于正确解析Ogg数据包中的打包Opus数据包是必要的,如第3节所述。该值不得为零,因为如果没有至少一个具有有效TOC序列的Opus数据包,解复用器将无法恢复Ogg数据包的持续时间。

    对于通道映射族0,该值默认为1,并且未进行编码。

  • 2、Coupled Stream Count ‘M’ (8 bits, unsigned):

    这是其解码器将被配置为产生两个声道(立体声)的流的数量。这必须不大于流的总数N。

    Opus流中的每个数据包都有一个1或2的内部信道计数,它可以在不同的数据包之间变化。这是由编码器根据比特率和正在编码的音频来选择的。传递到编码器的音频的原始通道计数不一定由有损压缩来保留。

    不管内部信道计数如何,通过解码器的适当初始化,任何Opus流都可以被解码为单声道(单个信道)或立体声(两个信道)。“耦合流计数”字段表示前M个Opus流的解码器将被初始化为立体声(双声道)输出,其余(N-M)解码器将仅被初始化为单声道(单声道)。解码通道的总数(M+N)必须不大于255,因为没有办法索引比通道映射中更多的通道。

    对于通道映射族0,此值默认为(C-1)(即,0表示单声道,1表示立体声),并且不进行编码。

  • 3、Channel Mapping (8*C bits):

    每个输出通道包含一个八位字节,指示每个通道要使用哪个解码通道。设“index”为特定输出通道的此八位位组的值。该值必须小于(M+N)或为特殊值255。如果“index”小于2*M,则必须将流(“index”/2)解码为立体声,并在“index”为偶数的情况下选择左声道,在“index”为奇数的情况下,选择右声道。如果“index”是2*M或更大,但小于255,则输出必须从解码流(“index”-M)中提取为单声道。如果“index”为255,则相应的输出通道必须包含纯静音。

    输出通道的数量C不被约束为与解码通道的数量(M+N)相匹配。单个索引值可能出现多次,即同一解码信道可能被映射到多个输出信道。一些解码的通道可能也没有分配给任何输出通道。对于通道映射族0,第一个索引默认为0,如果C==2,第二个索引则默认为1。两个索引都没有编码。

在生成输出通道之后,通道映射族确定每个通道的语义。本规范中有三个已定义的映射族。

5.1.1.1 Channel Mapping Family 0

允许的通道数:1或2。RTP映射。这与[RFC7587]的通道解释相同。

  • 1声道:单声道(单声道)。

  • 2声道:立体声(左、右)。

特殊映射:该通道映射族还指示内容由当且仅当C==2时为立体声的单个Opus流组成,流索引0映射到输出通道0(单声道或左声道),流索引1映射到输出频道1(右声道)(如果为立体声)。当“信道映射族”八位位组具有此值时,必须从ID头数据包中省略信道映射表。

5.1.1.2 Channel Mapping Family 1

允许的通道数:1…8。Vorbis通道顺序(见下文)。

在传统环绕布置中,每个声道都被分配给扬声器位置。具体位置取决于信道的数量,下面按照相应信道索引的顺序给出。

  • 1个声道:单声道(单声道)。
  • 2个声道:立体声(左、右)。
  • 3个声道:线性环绕(左、中、右)。
  • 4个声道:四声道(左前、右前、左后、右后)。
  • 5个声道:5.0环绕(左前、中前、右前、左后、右后)。
  • 6个声道:5.1环绕(左前、中前、右前、左后、右后、LFE)。
  • 7个声道:6.1环绕(左前、中前、右前、左侧、右侧、中后、LFE)。
  • 8个声道:7.1环绕(左前、中前、右前、左侧、右侧、左后、右后、LFE)。

这组环绕选项和扬声器位置顺序与Vorbis编解码器[Vorbis-MAPPING]使用的相同。排序不同于WAVE[WAVE-MULTICANNEL]和Free Lossless Audio Codec(FLAC)[FLAC]格式所使用的排序,因此在解码到这些格式或从这些格式编码时,正确的排序需要对输出通道进行排列。这里的“LFE”指的是低频效果通道,通常映射到没有特定空间位置的低音炮。当与喜欢该术语的音频格式或系统接口时,实施应酌情用“环绕”和“背面”识别“侧面”或“背面”扬声器位置。

5.1.1.3 Channel Mapping Family 255

允许的通道数:1…255。没有定义的通道含义。

通道未识别。通用播放器不应尝试播放这些流。离线实施可能会将输出解交错到单独的PCM文件中,每个通道一个。实现不应该为映射到流索引255(纯静音)的通道产生输出,除非它们没有其他方式来指示非静音通道的索引。

5.1.1.4 Undefined Channel Mappings

保留剩余的通道映射族(2…254)。遇到保留的“通道映射族”值的解复用器实现应该表现为该值为255。

5.1.1.5 Downmixing

Ogg Opus播放器必须支持任何有效的通道映射,通道映射族为0或1,即使通道数量与物理连接的音频硬件不匹配。玩家应该根据需要进行频道混合以增加或减少频道数量。

实现可以使用图4至图9中的矩阵,使用通道映射族1(第5.1.1.2节)从多通道文件中实现下混合,已知该族可以为立体声提供可接受的结果。对3个和4个通道的矩阵进行归一化,以便每个系数行求和为1以避免削波。对于5个或更多通道,它们被标准化为2,作为剪裁和动态范围缩小之间的折衷。

在这些矩阵中,左前通道和右前通道通常直接通过。当环绕声道在左右立体声声道之间分割时,选择系数,使其平方和为1,这有助于保持感知强度。后声道混合得更为漫射或衰减,以保持对前声道的关注。

   L output = ( 0.585786 * left + 0.414214 * center                    )
   R output = (                   0.414214 * center + 0.585786 * right )

   Exact coefficient values are 1 and 1/sqrt(2), multiplied by
   1/(1 + 1/sqrt(2)) for normalization.

                  Figure 4: Stereo Downmix Matrix for the
                      Linear Surround Channel Mapping
       /          \   /                                     \ / FL \
       | L output |   | 0.422650 0.000000 0.366025 0.211325 | | FR |
       | R output | = | 0.000000 0.422650 0.211325 0.366025 | | RL |
       \          /   \                                     / \ RR /

   Exact coefficient values are 1, sqrt(3)/2 and 1/2, multiplied by
   1/(1 + sqrt(3)/2 + 1/2) for normalization.

   Figure 5: Stereo Downmix Matrix for the Quadraphonic Channel Mapping
      /   \   /                                              \ | FC |
      | L |   | 0.650802 0.460186 0.000000 0.563611 0.325401 | | FR |
      | R | = | 0.000000 0.460186 0.650802 0.325401 0.563611 | | RL |
      \   /   \                                              / | RR |
                                                               \    /

   Exact coefficient values are 1, 1/sqrt(2), sqrt(3)/2 and 1/2,
   multiplied by 2/(1 + 1/sqrt(2) + sqrt(3)/2 + 1/2) for normalization.

       Figure 6: Stereo Downmix Matrix for the 5.0 Surround Mapping
   / \   /                                                       \ |FC |
   |L|   | 0.529067 0.374107 0.000000 0.458186 0.264534 0.374107 | |FR |
   |R| = | 0.000000 0.374107 0.529067 0.264534 0.458186 0.374107 | |RL |
   \ /   \                                                       / |RR |
                                                                   \LFE/
   Exact coefficient values are 1, 1/sqrt(2), sqrt(3)/2 and 1/2,
   multiplied by 2/(1 + 1/sqrt(2) + sqrt(3)/2 + 1/2 + 1/sqrt(2)) for
   normalization.

       Figure 7: Stereo Downmix Matrix for the 5.1 Surround Mapping
     /                                                                \
     | 0.455310 0.321953 0.000000 0.394310 0.227655 0.278819 0.321953 |
     | 0.000000 0.321953 0.455310 0.227655 0.394310 0.278819 0.321953 |
     \                                                                /

   Exact coefficient values are 1, 1/sqrt(2), sqrt(3)/2, 1/2 and
   sqrt(3)/2/sqrt(2), multiplied by 2/(1 + 1/sqrt(2) + sqrt(3)/2 + 1/2 +
   sqrt(3)/2/sqrt(2) + 1/sqrt(2)) for normalization.  The coefficients
   are in the same order as in Section 5.1.1.2 and the matrices above.

       Figure 8: Stereo Downmix Matrix for the 6.1 Surround Mapping
    /                                                                 \
    | .388631 .274804 .000000 .336565 .194316 .336565 .194316 .274804 |
    | .000000 .274804 .388631 .194316 .336565 .194316 .336565 .274804 |
    \                                                                 /

   Exact coefficient values are 1, 1/sqrt(2), sqrt(3)/2 and 1/2,
   multiplied by 2/(2 + 2/sqrt(2) + sqrt(3)) for normalization.  The
   coefficients are in the same order as in Section 5.1.1.2 and the
   matrices above.

       Figure 9: Stereo Downmix Matrix for the 7.1 Surround Mapping

✨5.2 注释头部数据(Comment Header)

      0                   1                   2                   3
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |      'O'      |      'p'      |      'u'      |      's'      |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |      'T'      |      'a'      |      'g'      |      's'      |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                     Vendor String Length                      |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                                                               |
     :                        Vendor String...                       :
     |                                                               |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                   User Comment List Length                    |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                 User Comment #0 String Length                 |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                                                               |
     :                   User Comment #0 String...                   :
     |                                                               |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                 User Comment #1 String Length                 |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     :                                                               :

                     Figure 10: Comment Header Packet

注释标头由一个64位的“魔术签名”字段组成,后面是与Ogg VORBIS中使用的[VORBIS-comment]标头格式相同的数据,但(与Ogg Theora和Speex一样)VORBIS规范中指定的最终“成帧位”不存在。

  • 1、Magic Signature:

    这是一个8位字节(64位)字段,允许编解码器识别,并且是人类可读的。它按顺序包含以下神奇数字:

    0x4F 'O'
    0x70 'p'
    0x75 'u'
    0x73 's'
    0x54 'T'
    0x61 'a'
    0x67 'g'
    0x73 's'
    

    从“Op”开始有助于将其与音频数据包区分开来,因为这是一个无效的TOC序列。

  • 2、Vendor String Length (32 bits, unsigned, little endian):

    此字段给出下个字段Vendor String的长度,单位为八位字节。它不得指示供应商字符串比数据包的其余部分长。

  • 3、Vendor String (variable length, UTF-8 vector):

    这是一个用于供应商信息的简单可读标签,编码为UTF-8字符串[RFC3629]。不需要终止空八位字节。

    此标签旨在识别编解码器编码器和封装实现,以跟踪技术行为的差异。面向用户的应用程序可以使用“ENCODER”用户注释标记来标识自己。

  • 4、User Comment List Length (32 bits, unsigned, little endian):

    此字段指示用户提供的注释个数。它可能表示用户提供的注释为零,在这种情况下,数据包中没有其他字段。它不能表明有太多的注释,以至于注释字符串长度需要比数据包其余部分更多的数据。

  • 5、User Comment #i String Length (32 bits, unsigned, little endian):

    此字段给出下个字段User Comment #i String的长度,单位为八位字节。“用户评论列表长度”字段指示的每个用户评论都有一个。它不能指示字符串比数据包的其余部分长。

  • 6、User Comment #i String (variable length, UTF-8 vector):

    此字段包含编码为UTF-8字符串的单个用户注释[RFC3629]。“用户评论列表长度”字段指示的每个用户评论都有一个。

“vendor string length”和“user comment list length”字段是必需的,如果流包含的注释头没有足够的数据用于这些字段,或者没有包含足够的数据来用于相应的vendor string或用户注释,则实现应将流视为无效。在分配相关内存以包含数据之前进行此检查有助于防止小评论标头可能发起的拒绝服务(DoS)攻击,这些小评论标头声称包含比整个数据包更长的字符串,或者包含比数据包中可能容纳的用户评论更多的用户评论。

紧接在用户注释列表之后,注释头可能包含零填充或此处未指定的其他二进制数据。如果此数据的第一个字节的最低有效位为1,则编辑器在更新标签时应保留此数据的内容,但如果此位为0,则所有此类数据都可能被视为填充,并根据需要进行截断或丢弃。这允许对该二进制数据的格式进行非正式的实验,直到以后可以指定为止。

注释头可以任意大,并且可能分布在大量的Ogg页面上。实现必须避免在出现非常大的注释头时试图分配过多的内存。为了实现这一点,如果流具有大于125829120个八位位组(120MB)的注释报头,则实现可以将其视为无效,并且可以忽略未完全包含在注释报头的前61440个八位位位组内的各个注释。

🎈5.2.1 Tag Definitions

用户评论字符串遵循[VORBIS-comment]描述的NAME=值格式,并具有相同的推荐标记名称:ARTIST、TITLE、DATE、ALBUM等。

这里引入了两个新的注释标签:

第一,轨迹归一化的可选增益:

R128_TRACK_GAIN=-573

表示在隔离播放、随机混洗等过程中规范音轨音量所需的音量偏移。增益是以dB为单位的Q7.8定点数字,如ID标头的“输出增益”字段中所示。该标记类似于Vorbis[REPLAY-GAIN]中的REPLAYGAIN_TRACK_GAIN标记,不同之处在于正常体积参考为[EBU-R128]标准。

第二,合集标准化的可选增益:

R128_ALBUM_GAIN=111

表示当作为曲目的特定集合的一部分播放时归一化总音量所需的音量偏移。增益也是以dB为单位的Q7.8定点数字,如ID标题的“输出增益”字段中所示。此处给出的值“-573”和“111”只是示例。

Ogg Opus流中的每一个标签都不能超过一个,并且,如果存在,它们的值必须是-32768到32767之间的整数,包括-32768和32767,以ASCII表示,以10为基数,没有空格。前导字符“+”或“-”是有效的。前导零也是允许的,但该值必须由不超过6个字符表示。其他非数字字符不得存在。

如果存在,R128_TRACK_AIN和R128_ALBUM_GAIN必须正确表示相对于ID标头中指定的“输出增益”字段的R128归一化增益。如果玩家选择使用R128_TRACK_AIN标记或R128_ALBUM_GAIN标记,则必须将这些增益_in addition_应用于“输出增益”值。如果工具修改ID标题的“输出增益”字段,则还必须更新或删除R128_TRACK_gain和R128_ALBUM_gain注释标记(如果存在)。复用器应该将它希望其他工具默认使用的增益放在“输出增益”字段中,而不是注释标记中。

为了避免与多个规范化方案混淆,Opus注释标头不应包含任何REPLAYGAIN_TRACK_GAIN、REPLAYGAIN _TRACK_PEAK、REPLAYGAIN_ALBUM_GAIN或REPLAYGAINC_ALBUM_PEAK标记,除非它们仅用于保证不会出现这种混淆的某些上下文中。[EBU-R128]规范化比早期的REPLAYGAIN方案更受欢迎,因为它的定义和行业采用都很明确。对于有损编解码器,由于解码器差异导致偏移高度的变化,因此难以可靠地计算峰值归一化。在作者的调查中,它们的应用不够一致或广泛,不值得纳入本文。

在这里插入图片描述

🎄6、数据包大小限制

从技术上讲,由于填充格式的原因,有效的Opus数据包可以任意大,尽管它们可以包含的非填充数据量是有界的。这些数据包可能分布在同样数量巨大的Ogg页面上。编码时,实现应该将音频数据包中填充的使用限制在不超过制作VBR流CBR所需的范围内,除非它们没有合理的方法来确定什么是必要的。如果每个Opus流的音频数据包大于61440个八位字节,则多路复用器应将音频数据包视为无效(将其视为具有无效TOC序列的格式错误的Opus数据包),除非它们有允许额外填充的特定原因。这样的数据包必然包含比制作流CBR所需的更多的填充。当出现非常大的数据包时,多路复用器必须避免试图分配过多的内存。如果音频数据包在信道映射族为0或1的Ogg Opus流中大于61440个八位字节,则多路复用器可以将其视为无效或部分处理。如果音频数据包大于61440个八位位组并且每个Opus流也大于7680个八位位位组,则多路复用器可以将音频数据包视为无效,或者在任何Ogg Opus流中对其进行部分处理。流中存在超大数据包可能表示内存耗尽攻击或流损坏。

在Ogg-Opus流中,不使用填充的最大可能有效数据包的大小为(61298*N-2)个八位字节。对于255个流,这是15630988个八位字节,最多可以跨越61298个Ogg页面,除一个外,所有页面的颗粒位置都为-1。当然,这是一个非常极端的分组,由255个流组成,每个流包含编码为2.5ms帧的120ms音频,每个帧使用最大可能数量的八位字节(1275),并以允许的最低效的方式存储(VBR代码3 Opus分组)。即使在这样的分组中,由于2.5ms帧实际上不能使用所有1275个八位字节,所以大多数数据将是零。

由完全有用的数据组成的最大数据包是(15326*N-2)个八位字节。这对应于在SILK或Hybrid模式下编码为10ms帧的120ms音频,但数据速率超过1Mbps,这对所实现的质量没有什么意义。

更合理的限制是(7664*N-2)个八位字节。这对应于编码为20ms立体声CELT模式帧的120ms音频,总比特率略低于511kbps(不包括Ogg封装开销)。对于信道映射族1,N=8提供了合理的上限,因为它允许从单独的立体声Opus流解码8个可能的输出信道中的每一个。这给出了61310个八位字节的大小,将其四舍五入为1024个八位比特的倍数,以产生61440个八位位组的音频数据分组大小,期望任何实现都能够成功处理该音频数据分组。

在这里插入图片描述

🎄9、Content Type

一个“Ogg Opus文件”由一个或多个顺序复用的段组成,每个段恰好包含一个Ogg Opu流。Ogg Opus文件的推荐mime类型为“audio/Ogg”。

如果需要更多的特异性,可以使用[RFC6381]和[RFC5334]中定义的编解码器参数来指示Opus流的存在,例如。,
audio/ogg; codecs=opus给到一个Ogg Opus 文件。

Ogg Opus文件的推荐文件扩展名是.opus

当Opus与Ogg容器中的其他流同时复用时,应使用[RFC5334]中定义的“audio/Ogg”、“video/Ogg” 或 “application/Ogg” mime类型之一。这样的流并不是如上所述的严格的“Ogg Opus文件”,因为它们在每个顺序复用的片段(例如,视频或多个音轨)中包含多于一个Opus流。在这种情况下,不建议使用“.opus”文件扩展名。

在任何一种情况下,本文档都会更新[RFC5334],将“opus”添加为编解码器参数值,并将char[8]:“OpusHead”添加为编码解码器标识符。

在这里插入图片描述
如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁

参考资料:
Ogg Encapsulation for the Opus Audio Codec

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wkd_007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值