前面我們介紹了Ogg格式, Opus也使用格式Ogg封裝,其框架符合Ogg,只是每個Page中的內容有其自己的定義。規格書見RFC7845
https://www.rfc-editor.org/rfc/rfc7845 簡而言之Ogg就是將編碼器的輸出數據包Packet, 以Page為單位進行封裝,所以核心就是Page格式。本文就來分享下Ogg封裝Opus時的Page格式。
當然一般我們不需要手動去解析,借助opus官方開源的工具項目,即可實現編解碼,推流等應用,相關開源項目可以構建直接使用,也可集成到自己的項目中。但是了解其格式有利于調試分析。
Opus編碼后音頻使用Ogg封裝,其整體結構圖下:
第一個Page為ID頭部數據,其內容用于識別Opus。必須是BOS Page。
第二個Page開始為注釋頭數據,可能有多個Page, 存儲一些廠商字符串,注釋字符串。
后續Page存儲音頻數據。

第一個Page為ID頭部數據,如下, 注意這里是segment中有效數據,去掉了Ogg HDR頭部內容。

1.Magic Signature:
固定8字節0x4F ’O’ 0x70 ’p’ 0x75 ’u’ 0x73 ’s’ 0x48 ’H’ 0x65 ’e’ 0x61 ’a’ 0x64 ’d’
2.Version
1字節,目前版本為1
3.Output Channel Count
1字節, 輸出通道數 用C表示
4.Pre-skip
2字節,小端, 播放時前面需要丟棄的采樣點數。
5.Input Sample Rate
4字節,小端,編碼前數據采樣率
6.Output Gain
2字節,小端,輸出增益
Q7.8格式
根據增益還原原始大小
sample *= pow(10, output_gain/(20.0*256))
7.Channel Mapping Family
1字節,含義如下
Family 0
該模式支持1(單聲道) 或者2(立體聲).和RTP.
Family 1
該模式支持1...8聲道. Vorbis channel order
1 channel: monophonic (mono).
2 channels: stereo (left, right).
3 channels: linear surround (left, center, right).
4 channels: quadraphonic (front left, front right, rear left,
rear right).
5 channels: 5.0 surround (front left, front center, front right,
rear left, rear right).
6 channels: 5.1 surround (front left, front center, front right,
rear left, rear right, LFE).
7 channels: 6.1 surround (front left, front center, front right,
side left, side right, rear center, LFE).
8 channels: 7.1 surround (front left, front center, front right,
side left, side right, rear left, rear right, LFE).
Family 255
支持channels: 1...255.
families (2...254) 保留
8.Channel Mapping Table
非family0才有

Stream Count :1字節 用N表示
Coupled Stream Count :1字節 用M表示
Channel Mapping :大小為Output Channel Count指定的字節數C
如下是一個實例
Magic Signature:”OpusHead”
Version:0x01
Output Channel Count:0x02
Pre-skip: 0x0138=312
Input Sample Rate:0xBB80=48000
Output Gain: 0x0000
Channel Mapping Family: 0即Family 0
Family0 所以后面沒有8.Channel Mapping Table

第二個PAG開始為注釋頭部數據,如下

1.Magic Signature:
8字節固定為0x4F ’O’ 0x70 ’p’ 0x75 ’u’ 0x73 ’s’ 0x54 ’T’ 0x61 ’a’ 0x67 ’g’ 0x73 ’s’
2.Vendor String Length
4字節,小端,后續的廠商字符串的字節長度
3.Vendor String
Vendor String Length指定的字節數UTF-8
4.User Comment List Length
4字節小端,后續user-supplied comments的個數,即后面5和6的個數
5.User Comment #i String Length
4字節小端,后續User Comment #i String的長度
6.User Comment #i String
User Comment #i String Length指定的字節數,UTF-8
實例如下
Magic Signature:”OpusTags”
Vendor String Length:0x0D 13字節
Vendor String:”Lavf57.83.100”
User Comment List Length:0x01 后面一組注釋字符串
User Comment #1 String Length:0x1E=30
User Comment #1 String:”encoder=Lavc57.107.100 libopus”

后續就是opus編碼音頻數據包
我們來連續看3個Page,以及最后一個Page





header_type分別是 00 00 00 04 即最后一個是eos
granule_position分別是0x00BB80 0x017700 0x023280 ... 0x4D79A6
每個Page間隔都是0x017700-0x00BB80=0xBB80 48000個采樣點。
bitstream_serial_number:都是0x63E98BF0
page_sequence_number:0x02 0x03 0x04 ... 0x6B 因為前面ID頭占用一個Page0 , 注釋頭占用以一個Page1 所以數據從Page 2開始。
page_segments:分別是0x3C 0x3E 0x3F ... 0x29
page_segments后的page_segments個字節,表示page_segments個段,每個段的長度。
我們以最后一個Page來看
0x29個段,每個段的長度分別是
FF 05 DC DF E0 DE E4 E9 E8 FF 3B D5 DB DF E4 E9
EB E9 DB C3 CD D9 DE ED D6 E1 EF DB E7 DC E3 DF
E6 CF BC C9 CA D3 D6 CE F0
累加和是0x221C

從0x12D8D1

到0x12FAED

0x12FAED-0x12D8D1=0x220C
和前面計算的段累加和對應。
上述各個segment就是存儲編碼之后的Packet,解碼需要先獲取Packet數據,然后以Packet為單位解碼,以上述為例子,如果segment的長度是0xFF則說明該segment后面的segment也屬于同一個Packet,也就是連續的0xFF...到最后不足0xFF的都是一個Packet。 如果Segment的長度不是0xFF且前面的segment不是0xFF,則說明該Segment獨自存了一個Packet。
如下FF 05和FF 3B處就是兩個segment存儲一個packet.
Packet長度就是0xFF+0x05和0xFF+0x3B,其他的都是一個segment存一個Packet。
FF 05 DC DF E0 DE E4 E9 E8 FF 3B D5 DB DF E4 E9
EB E9 DB C3 CD D9 DE ED D6 E1 EF DB E7 DC E3 DF
E6 CF BC C9 CA D3 D6 CE F0
可以直接使用工具opusinfo查看
和上面手動解析的對比
opusinfo ../sample3.opusProcessing file "../sample3.opus"...New logical stream (#1, serial: 63e98bf0): type opusEncoded with Lavf57.83.100User comments section follows...encoder=Lavc57.107.100 libopusOpus stream 1:Pre-skip: 312Playback gain: 0 dBChannels: 2Original sample rate: 48000 HzPacket duration: 20.0ms (max), 20.0ms (avg), 20.0ms (min)Page duration: 1000.0ms (max), 997.9ms (avg), 780.0ms (min)Total data length: 1243885 bytes (overhead: 0.707%)Playback length: 1m:45.772sAverage bitrate: 94.08 kbit/s, w/o overhead: 93.41 kbit/sLogical stream 1 ended
其他計算采樣點位置等詳細信息參考規格書
’PCM sample position’ = ’granule position’ - ’pre-skip’
’playback time’ = ’PCM sample position’ / 48000.0
可以直接apt install opus-tools安裝
這里通過帶啊嗎倉庫手動構建
sudo apt install libogg-dev libvorbis-dev手動構建安裝flac
wget https://downloads.xiph.org/releases/flac/flac-1.3.3.tar.xzxz -d flac-1.3.3.tar.xztar -xvf flac-1.3.3.tarcd flac-1.3.3/./configuremakemake install
sudo apt install libpcap-devgit clone https://gitlab.xiph.org/xiph/opus.gitcd opus./autogen.sh./configuremakemake install
git clone https://gitlab.xiph.org/xiph/opusfile.gitcd opusfile/./autogen.sh./configuremakemake install
git clone https://gitlab.xiph.org/xiph/libopusenc.gitcd libopusenc/./autogen.sh./configuremakemake install
git clone https://gitlab.xiph.org/xiph/opus-tools.gitcd opus-tools/./autogen.sh./configuremakemake install
opusinfo ../sample3.opusProcessing file "../sample3.opus"...New logical stream (#1, serial: 63e98bf0): type opusEncoded with Lavf57.83.100User comments section follows...encoder=Lavc57.107.100 libopusOpus stream 1:Pre-skip: 312Playback gain: 0 dBChannels: 2Original sample rate: 48000 HzPacket duration: 20.0ms (max), 20.0ms (avg), 20.0ms (min)Page duration: 1000.0ms (max), 997.9ms (avg), 780.0ms (min)Total data length: 1243885 bytes (overhead: 0.707%)Playback length: 1m:45.772sAverage bitrate: 94.08 kbit/s, w/o overhead: 93.41 kbit/sLogical stream 1 ended
如果提示
opusdec: error while loading shared libraries: libopusurl.so.0: cannot open shared object file: No such file or directory查找
find /usr -name libopusurl.so.0/usr/local/lib/libopusurl.so.0
設置
sudo nano ~/.bashrcexport LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib/source ~/.bashrc
opusdec ../sample3.opus ../sample3_dec.wavopusdec ../sample3.opus ../sample3_dec.wavDecoding to 48000 Hz (2 channels)Encoded with Lavf57.83.100encoder=Lavc57.107.100 libopusDecoding complete.
opusenc ../sample3_dec.wav ../sample3_enc.opusopusenc ../sample3_dec.wav ../sample3_enc.opusEncoding using libopus 1.6.1 (audio)-----------------------------------------------------Input: WAV, 48 kHz, 2 channels, stereoOutput: Opus, 2 channels (2 coupled), stereo20ms packets, 96 kbit/s VBRPreskip: 312Encoding complete-----------------------------------------------------Encoded: 1 minute and 45.78 secondsRuntime: 3 seconds(35.26x realtime)Wrote: 1247600 bytes, 5289 packets, 108 pagesBitrate: 93.6385 kbit/s (without overhead)Instant rates: 67.2 to 193.6 kbit/s(168 to 484 bytes per packet)Overhead: 0.759% (container+metadata)
opusrtp推流
./opusrtp -d 127.0.0.1 -p 1234 -t 20 ../sample3.opus
./opusrtp -d 127.0.0.1 -p 1234 -t 20 ../sample3.opusSending ../sample3.opus...rtp 20 1804289384 846931846 20 ms 3 bytesrtp 20 1804289385 846932806 20 ms 126 bytesrtp 20 1804289386 846933766 20 ms 460 bytesrtp 20 1804289387 846934726 20 ms 236 bytesrtp 20 1804289388 846935686 20 ms 228 bytesrtp 20 1804289389 846936646 20 ms 237 bytesrtp 20 1804289390 846937606 20 ms 236 bytesrtp 20 1804289391 846938566 20 ms 233 bytesrtp 20 1804289392 846939526 20 ms 228 bytesrtp 20 1804289393 846940486 20 ms 458 bytes
opusrtp拉流
./opusrtp --sniff lo -p 1234 -t 20 -o ../sample3_rtp.wav# ./opusrtp --sniff lo -p 1234 -t 20 -o ../sample3_rtp.wavCapturing packets from loGot 85 byte packet (85 bytes captured)eth 0x0800 00:00:00:00:00:00 -> 00:00:00:00:00:00ipv4 protocol 6 127.0.0.1 -> 127.0.0.1
以上介紹了Opus使用Ogg封裝的格式, 了解ID頭部數據,注釋頭部數據,數據的解析。 重點是數據中如何得到Packet,了解以上格式可以方便調試。一般情況下使用官方開源的工具即可,可以直接構建使用或者集成到自己的項目。