Ogg比特流格式,是一種通用的、免費的媒體流封裝格式。它能夠?qū)⑷魏畏N類和數(shù)量的音視頻編碼格式以及其他數(shù)據(jù)流封裝在單個比特流中,詳見規(guī)格書RFC3533。
https://datatracker.ietf.org/doc/html/rfc3533例如Opus就是使用Ogg封裝格式, Opus格式后面再分享。
Ogg的設(shè)計思想是提供一種通用的線性媒體傳輸格式,獨立于媒體數(shù)據(jù)編碼格式。支持一個或多個交織媒體流,支持基于文件存儲和基于流傳輸。簡單理解Ogg是一個媒體數(shù)據(jù)流的容器,并不管數(shù)據(jù)流是什么格式,是什么內(nèi)容。
Ogg封裝格式是一種容器格式,僅封裝內(nèi)容(如Vorbis編碼的音頻)。它不提供對自身或其包含的比特流內(nèi)容的任何通用加密或簽名。然而,只要有對應(yīng)編解碼器,它就可以封裝任何類型的比特流內(nèi)容,因此也能夠包含加密和簽名的數(shù)據(jù)內(nèi)容。還可以添加外部安全機制,對Ogg物理比特流進行加密或簽名,從而提供內(nèi)容的機密性和真實性。
- Page組成邏輯比特流
- 不同邏輯比特流可交織
- 支持損壞檢測
- 支持解析錯誤后進行流重新同步
- 支持用于直接隨機訪問比特流中任意位置的位置地標(biāo)
- 流傳輸能力(即不需要尋找seeking來構(gòu)建100%完整的比特流)
- 開銷小,用于其他信息記錄的比特流帶寬不超過約1-2%
- 簡化以實現(xiàn)快速解析
- 多個物理比特流的簡單級聯(lián)機制
physical Ogg bitstream物理(Ogg)比特流:Ogg封裝的結(jié)果,可以多個物理比特流級聯(lián)。由邏輯比特流中的一系列頁面Page按照正確的時間順序排列組成。
logical bitstreams邏輯比特流: 編碼器創(chuàng)建的比特流, 即編碼器對媒體數(shù)據(jù)流編碼后的輸出,多個邏輯比特流交織成物理比特流。
(Ogg) packet數(shù)據(jù)包: 邏輯比特流的一個子部分,代表編碼器的一個有意義的實體,可以理解為按照Packet為單位對其進行封裝。
(Ogg) page頁面: 物理比特流由一系列Ogg頁面組成,這些頁面僅包含一個邏輯比特流的數(shù)據(jù)。它通常只包含一個數(shù)據(jù)包的一組連續(xù)段segment,但有時數(shù)據(jù)包太大,需要拆分到幾個頁面上。
bos-page:邏輯比特流的第一個Page
eos-page:邏輯比特流的最后Page
chaining (or sequential multiplexing):鏈式(或順序多路復(fù)用):兩個或多個完整的物理Ogg比特流的級聯(lián)。
(Ogg) segment (Ogg)段:Ogg封裝過程將每個數(shù)據(jù)包packet拆分為255字節(jié)的數(shù)據(jù)塊加上最后可能小于255字節(jié)的數(shù)據(jù)塊。這些塊稱為段。
lacing value: Page的HDR中的 segment table 字段中,一個字節(jié)對應(yīng)一個lacing value, 表示對應(yīng)的segment的字節(jié)大小。所有的lacing value的和即該Page中所有segment的大小的和,即整個Page的數(shù)據(jù)的大小。
granule position: 存儲在Page頭中的特定邏輯比特流的遞增位置號。它的含義取決于該邏輯比特流的編解碼器。
grouping (or concurrent multiplexing):分組(或并發(fā)復(fù)用):將多個邏輯比特流Page交織成一個完整的物理Ogg比特流。限制是所有分組邏輯比特流的所有bos page必須出現(xiàn)在任何數(shù)據(jù)page之前。即一開始就放所有邏輯比特流的bos page。
media mapping媒體映射: 應(yīng)用Ogg封裝格式對特定(一組)編解碼器的輸出數(shù)據(jù)進行封裝。
簡單的理解是對編碼器的輸出原始數(shù)據(jù),按照Packet為基礎(chǔ)單位,將其封裝到Page中。由于Packet很大,所以劃分更多的小Segment,一個Page中可以放一個或者多個Segment。
按照自頂向下的結(jié)構(gòu)理解就是:物理比特流 - 邏輯比特流 - Page的層次。
物理Ogg比特流由多個邏輯比特流交織組成。物理比特流還可以級聯(lián)。
邏輯比特流就是一串Page組成,每個page頭部都有遞增的唯一序列號來標(biāo)識,并且標(biāo)記開頭bos,結(jié)尾eos。這樣從bos的page開始串起來各個page到eos page結(jié)束就組成以惡個邏輯比特流。 上述多個邏輯比特流可能交織, 即不同邏輯比特流的Page可以交錯排列。每條邏輯比特流都自己的序列號用于區(qū)分不同的邏輯比特流,同一條邏輯比特流內(nèi)的該序號一致。 每個Page還有自己的遞增序列號,可以標(biāo)記Page是否有丟失是否連續(xù)。
每個 Ogg Page僅包含一種類型的數(shù)據(jù),因為它只屬于一個邏輯比特流。Page大小可變,并且具有包含封裝和錯誤恢復(fù)信息的Page頭。物理 Ogg 比特流中的每個邏輯比特流都以一個特殊的起始頁面(bos=流開始)開始,并以一個特殊頁面(eos=流結(jié)束)結(jié)束。
bos 頁包含可用于唯一識別編解碼器類型的信息,并且可以包含用于設(shè)置解碼過程的信息。bos 頁還應(yīng)包含有關(guān)編碼媒體的信息——例如,對于音頻,它應(yīng)包含采樣率和聲道數(shù)。按照慣例,bos 頁的前幾個字節(jié)包含唯一標(biāo)識所需編解碼器的魔術(shù)數(shù)據(jù)。處理新編解碼器的任何人都有責(zé)任確保能夠可靠地區(qū)分其編解碼器與所有其他正在使用的編解碼器。
沒有固定的方法可以檢測編解碼器標(biāo)識標(biāo)記的結(jié)束。bos頁的格式取決于編解碼器,因此必須在該邏輯比特流類型的封裝規(guī)范中給出。Ogg也允許但不要求在邏輯比特流的bos頁之后設(shè)置二級頭部包,并且這些包也必須位于任何邏輯比特流中的數(shù)據(jù)包之前。這些后續(xù)的頭部包被封裝成整數(shù)頁,不包含任何數(shù)據(jù)包。因此,一個物理比特流以所有邏輯比特流的bos頁開始,每頁包含一個初始頭部包,然后是所有流的附屬頭部包,最后是包含數(shù)據(jù)包的頁。
一個或多個邏輯比特流的封裝規(guī)范稱為“媒體映射”。媒體映射的一個例子是“Ogg Vorbis”,它使用 Ogg 框架來封裝 Vorbis 編碼的音頻數(shù)據(jù),以便進行基于流的存儲(例如文件)和傳輸(例如 TCP 流或管道)。Ogg Vorbis 在 Ogg Vorbis bos 頁面上提供了 Vorbis 編解碼器的名稱和版本、音頻采樣率以及音頻質(zhì)量。它還為每個邏輯比特流使用兩個額外的頭頁。Ogg Vorbis bos 頁面以字節(jié) 0x01 開始,隨后是“vorbis”(共 7 個字節(jié)的標(biāo)識符)。
Ogg 知道兩種類型的復(fù)用:并發(fā)復(fù)用(所謂的“分組”)和順序復(fù)用(所謂的“鏈式”)。分組定義了如何在同一個物理比特流中按頁面交錯多個邏輯比特流。例如,分組用于將視頻流與幾個使用不同編解碼器的同步音頻軌道在不同邏輯比特流中交錯。另一方面,鏈式定義了一種簡單的機制,用于連接物理 Ogg 比特流,這在流媒體應(yīng)用中經(jīng)常需要。
在分組中,所有邏輯比特流的 bos 頁面必須在 Ogg 比特流的開頭一起出現(xiàn)。媒體映射規(guī)定了初始頁面的順序。例如,特定 Ogg 視頻和 Ogg 音頻比特流的分組可能規(guī)定物理比特流必須以邏輯視頻比特流的 bos 頁面開頭,隨后是音頻比特流的 bos 頁面。與 bos 頁面不同,邏輯比特流的 eos 頁面不需要全部連續(xù)出現(xiàn)。Eos 頁面可能是“空”頁面,即不包含內(nèi)容的頁面,僅包含頁面頭信息和設(shè)置在頁面頭的 eos 標(biāo)志。每個分組的邏輯比特流在物理比特流的范圍內(nèi)必須有唯一的序列號。
在鏈式處理中,完整的邏輯比特流會被串聯(lián)起來。這些比特流不會重疊,即給定邏輯比特流的結(jié)束頁緊接著下一個邏輯比特流的開始頁。每個鏈式邏輯比特流在物理比特流的范圍內(nèi)必須具有唯一的序列號。
可以連續(xù)地將一組組并發(fā)復(fù)用的比特流鏈接起來。當(dāng)這些組被解鏈時,它們必須能夠獨立作為一個有效的并發(fā)復(fù)用比特流存在。下圖展示了一個遵循分組和鏈式復(fù)用比特流所有規(guī)則的物理比特流示意例子。
物理比特流,其中不同邏輯比特流的頁面被分組和鏈接

如上圖所示,有兩個鏈式的物理比特流:
其中第一個是由三個邏輯比特流A、B和C組成的分組比特流,都是從bos開始eos結(jié)束,ABC三個邏輯流組成物理流。

第二個物理比特流在上述分組比特流結(jié)束后鏈式連接,如下的邏輯流D。上述分組比特流在其所有邏輯比特流的最后的 eos 之后結(jié)束。上述分組比特流會一起開始——所有的 bos 必須出現(xiàn)在任何數(shù)據(jù)page之前。復(fù)用的比特流的page不需要遵循固定順序即ABC之間的相對順序無要求。一個分組比特流可以在該組中其他比特流結(jié)束之前很久就結(jié)束,比如上述的#A eos造就結(jié)束了,后面還有很多BC。

Ogg 對編解碼器數(shù)據(jù)的具體信息一無所知,除了每個邏輯比特流屬于不同的編解碼器外,來自編解碼器的數(shù)據(jù)是按順序排列的,并且?guī)в形恢脴?biāo)記(所謂的“granule_position”)。Ogg 并沒有“時間”的概念:它只知道順序遞增的、無單位的位置標(biāo)記。應(yīng)用程序只能通過更高層訪問編解碼器 API 來獲取時間信息,以分配和轉(zhuǎn)換granule_position或時間。
使用 Ogg 的媒體映射的具體定義可能會對其使用 Ogg 比特流格式的特定方式施加進一步的限制。例如,某個特定的媒體映射可能要求所有分組比特流的所有 eos 頁面必須按直接順序出現(xiàn)。媒體映射的一個例子是“Ogg Vorbis”的規(guī)范。另一個例子是即將發(fā)布的“Ogg Theora”規(guī)范,它封裝了 Theora 編碼的視頻數(shù)據(jù),并且通常與 Vorbis 流復(fù)用,用于包含同步音頻和視頻的 Ogg。由于 Ogg 并未指定封裝的并發(fā)復(fù)用比特流之間的時間關(guān)系,因此音頻和視頻流之間的時間同步將在此媒體映射中指定。為了實現(xiàn)流式傳輸,各邏輯比特流的頁面通常會按時間順序交錯。
如上所述,多路復(fù)用不同邏輯比特流的過程發(fā)生在頁面級別。然而,編碼器提供的比特流作為所謂的“數(shù)據(jù)包”移交給Ogg,數(shù)據(jù)包邊界取決于編碼格式?,F(xiàn)在將描述將數(shù)據(jù)包封裝到頁面中的過程。
從Ogg的角度來看,數(shù)據(jù)包可以是任意大小的。特定的媒體映射將定義如何對來自特定媒體編碼器的數(shù)據(jù)包進行分組或拆分。由于Ogg頁面的最大大小約為64kBytes,有時一個數(shù)據(jù)包必須分布在多個頁面上。為了簡化這個過程,Ogg將每個數(shù)據(jù)包劃分為255字節(jié)長的塊和最后一個較短的塊。這些塊被稱為“Ogg Segments”。它們只是一個邏輯結(jié)構(gòu),本身沒有標(biāo)題。
一組連續(xù)的段被包裝成一個可變長度的頁面,前面有一個標(biāo)題。頁面標(biāo)題中的分段表告訴頁面中包含的分段的“Lacing值”(大?。?。頁頭中的標(biāo)志告訴一個頁是否包含從前一頁繼續(xù)的數(shù)據(jù)包。請注意,255的lacing值意味著數(shù)據(jù)包中會有第二個lacing值,而小于255的值則表示數(shù)據(jù)包在多個額外字節(jié)之后結(jié)束。255字節(jié)(或255字節(jié)的倍數(shù))的數(shù)據(jù)包以0作為結(jié)尾。還要注意,“nil”(零長度)數(shù)據(jù)包不是錯誤;它只不過是標(biāo)頭中的一個零值。
編碼針對速度和大多數(shù)數(shù)據(jù)包大小在50到200字節(jié)之間的預(yù)期情況進行了優(yōu)化。這是一種設(shè)計理由,而不是建議。這種編碼既避免了施加最大的數(shù)據(jù)包大小,也避免了對小數(shù)據(jù)包施加最小的開銷。相比之下,例如,簡單地在每個數(shù)據(jù)包的開頭使用兩個字節(jié),并且最大數(shù)據(jù)包大小為32k字節(jié),總是會以兩倍的分段開銷懲罰小數(shù)據(jù)包(典型情況下小于255字節(jié))。使用建議的lacing值,小數(shù)據(jù)包在編碼空間上的開銷最?。?/span>1字節(jié)),大數(shù)據(jù)包(>512字節(jié))的開銷相當(dāng)恒定,約為0.5%。
下圖演示了使用Ogg和分組邏輯比特流對媒體數(shù)據(jù)進行封裝:

整個過程分為3步
1)先按照媒體數(shù)據(jù)的包為單位進行處理。由多個包,每個包分為多個段segments。
2)按照Page進行封裝,一個Page可以組裝多個segments, 每個Page前面都由HDR頭部信息。 此時的Page就是邏輯bit流的單位。 上述所有的Page, 就是我們前面示意圖中的一條完整的邏輯比特流, #A(bos),B...,B(eos).
3)上述的一條邏輯比特流和和其他邏輯比特流多路復(fù)用交織, 組成最終的物理比特流。即物理比特流可以由多條邏輯比特流組成,且各條邏輯比特流可以交織。
就如上面介紹的圖所示

上面可以看出Ogg實際就是對Packet使用Page這個容器去封裝。封裝的單位就是Page, 即Page組成邏輯比特流,邏輯比特流交織成物理比特流。
就好比織麻繩,一股繩子就是一條邏輯比特流,而一股繩子是多段更短的繩子(Page)連接而成的。多股繩子交織,就組成了物理比特流。
Page大小可變,通常為4-8kB,最大為65307字節(jié)。Page開頭包含HDR信息,用于錯誤恢復(fù)和查找標(biāo)記等以便能解封裝。每個Page都是一個自包含的實體,即按照Page為單位,一次識別、驗證和處理單個Page,而不需要整個比特流。
Page詳細格式如下,多字節(jié)數(shù)據(jù)按照LSB,低字節(jié)在前編碼。

各字段含義如下
1.capture_pattern: 標(biāo)志Page的開始,固定為0x4f ’O’ 0x67 ’g’ 0x67 ’g’ 0x53 ’S’
例如隨便找一個Opus文件,搜索oggs就可以定位到Page邊界

1.version:1個字節(jié),表示此流中使用的Ogg文件格式的版本號(目前版本為0)。
2.header_type:1字節(jié),不同位標(biāo)識此頁面的特定類型。
bit0 為1 頁面包含從上一頁繼續(xù)的數(shù)據(jù)包的數(shù)據(jù)
為0 頁面包含一個新的數(shù)據(jù)包
bit1 為1 邏輯比特流的第一個Page即bos。
為0 非bos
bit2 為1 邏輯比特流的最后一個Page即eos。
為0 非eos
3.granule_position:一個包含位置信息的8字節(jié)字段。例如,對于音頻流,它表示包括此Page所有編碼的PCM采樣點數(shù)。對于視頻流,它表在包括此Page編碼的視頻幀總數(shù)。其含義依賴于該邏輯比特流的編解碼器,特殊值-1表示此頁上沒有數(shù)據(jù)包完成。
4.bitstream_serial_number:一個4字節(jié)的字段,包含標(biāo)識邏輯比特流的唯一序列號。因為物理比特流有多個邏輯比特流,所以需要序列號區(qū)分。
5.page_sequence_number:一個4字節(jié)的字段,包含頁面的序列號,以便解碼器識別頁面丟失。該序列號在每個邏輯比特流上分別增加。
6.CRC_checksum:一個4字節(jié)的字段,包含頁面的32位CRC校驗和(先CRC_checksum設(shè)置為0然后對HDR和后面的數(shù)據(jù)整個Page計算校驗)。生成多項式為0x04c11db7。
7.page_segments:1個字節(jié),表示segment_table中編碼的段segment條目數(shù)。
8.segment_table: 總共page_segments個字節(jié),每個字節(jié)對應(yīng)一個lacing value該值即表示對應(yīng)的segment的字節(jié)大小,一個Page包含多個Segment,一共page_segments個segment。page_segments個字節(jié)之后才是每個segment的數(shù)據(jù)內(nèi)容。
HDR的總字節(jié)大小為:
header_size = page_segments + 27
總的Page字節(jié)大小為: 即HDR加上所有segment的大小
page_size = header_size + sum(lacing_values: 1..page_segments)
從以下地址下載Opus測試文件sample3.opus
https://filesamples.com/formats/opus#google_vignette使用二進制編輯工具打開該文件
連續(xù)看兩個Pages

capture_pattern: 4F 67 67 53即Oggs開頭
version: 都是0
header_type:第一個是0x02 bos 新數(shù)據(jù)包,第二個是0x00,新數(shù)據(jù)包。
granule_position:都是0
bitstream_serial_number:都是F08BE963 同一個物理比特流要一樣
page_sequence_number:前者是00000000 后者是01000000,小端即分別是0x00,0x01,Page序列號遞增。
CRC_checksum前者是0x1082E2FB后者是0xF040D738
4F 67 67 53 00 02 00 00 00 00 00 00 00 00 F0 8BE9 63 00 00 00 00 FB E2 82 10 01 13 4F 70 75 7348 65 61 64 01 02 38 01 80 BB 00 00 00 00 00
將FB E2 82 10替換為00 00 00 00
4F 67 67 53 00 02 00 00 00 00 00 00 00 00 F0 8BE9 63 00 00 00 00 00 00 00 00 01 13 4F 70 75 7348 65 61 64 01 02 38 01 80 BB 00 00 00 00 00
https://www.lddgo.net/encrypt/crc在線計算CRC為0x1082E2FB是對應(yīng)的

page_segments兩者都是1,即都只有一個segment
第一個的segment大小是0x13,后面剛好19字節(jié)數(shù)據(jù)4F 70 75 73 48 65 61 64 01 02 38 01 80 BB 00 00 00 00 00
第二個的segment大小是0x3F,后面剛好是0x3F字節(jié)的數(shù)據(jù)
4F 70 75 73 54 61 67 73 0D 00 00 00 4C 61 76 6635 37 2E 38 33 2E 31 30 30 01 00 00 00 1E 00 0000 65 6E 63 6F 64 65 72 3D 4C 61 76 63 35 37 2E31 30 37 2E 31 30 30 20 6C 69 62 6F 70 75 73
以上介紹了Ogg封裝格式,簡單理解就是使用Page這個容器對編碼器輸出的Packet數(shù)據(jù)進行封裝,所以核心是理解Page的格式,然后理解Page如何構(gòu)成邏輯比特流,如何交織或者鏈接成物理比特流。