Ogg 檔案格式
Ogg 容器,常用來封裝 Vorbis/Opus 音訊或 Theora 視訊。
- MIME 類型
- application/ogg
- 副檔名
- .ogg, .oga, .ogv
- 分類
- 音訊
Magic number(檔案簽章)
| 0x00000000 | 4F676753 | OggS | 'OggS' page capture pattern |
關於 Ogg 格式
Ogg 是開放、無專利負擔的串流容器,最常裝的是 Vorbis 或 Opus 音訊,在遊戲與開源軟體中廣泛使用。每個 page 都以擷取樣式 'OggS' 開頭。那個「每個」就是整個設計的重點:Ogg 檔案不是「標頭後面接資料」,而是一串自我描述的 page,每一頁帶著自己的檢查碼、序號和串流序列號,所以解碼器可以中途插進一條即時串流,而受損的下載也能靠往前掃描下一組 4F 67 67 53 重新同步。容器本身對編碼隻字未提 — 那是由第一頁上的第一個封包決定的。
檔頭結構,一個位元組一個位元組看
| 位移 | 長度 | 欄位 | 意義 |
|---|---|---|---|
| 0 | 4 | Capture pattern 'OggS' | 4F 67 67 53。它標記的是一個 page 的開頭,不是檔案的開頭,所以一段一秒的音訊裡就已經有好幾十個;在 hex 編輯器裡搜尋它,找到的是整條串流的每一個 page 邊界。 |
| 4 | 1 | Stream structure version | 固定是 0。Ogg 的框架從來沒有定義過第二個版本,所以這裡是別的值,就代表這些位元組根本不是 Ogg page。 |
| 5 | 1 | Header type flag | 位元 0 表示這一頁的第一個封包是從上一頁接續下來的,位元 1 表示這是邏輯串流的開始,位元 2 表示結束。所以一個檔案的第一頁這裡讀到的是 02。 |
| 6 | 8 | Granule position | 一個小端序有號 64 位元位置,它的意義由編碼決定、不由 Ogg 決定 — Vorbis 是 PCM 取樣數,Opus 是 48 kHz 取樣數。全部位元皆為 1(-1)代表這一頁上沒有任何封包結束,所以沒有新的位置可報。 |
| 14 | 4 | Bitstream serial number | 一個小端序、接近隨機的 id,用來標明這一頁屬於哪一條邏輯串流。同時裝著影片和音訊的檔案會交錯不同序列號的 page,而串接起來的檔案則是每一段一個序列號。 |
| 18 | 4 | Page sequence number | 小端序,在每條邏輯串流裡從 0 開始數。這個號碼跳號,正是解碼器判斷「掉了一頁」而不是「資料損壞」的依據。 |
| 22 | 4 | Page checksum | 對整個 page 算出來的 CRC-32,計算時這個欄位暫時歸零,用的是多項式 0x04C11DB7,輸入輸出都不反轉,最後也不做 XOR — 這是刻意不跟 ZIP 和 gzip 用的那個 CRC-32 一樣,好讓兩者不會被搞混。 |
| 26 | 1 | Number of page segments | 分段表有幾筆,0 到 255。它讓一個 page 的上限是 27 + 255 + 255 x 255 = 65307 個位元組,這也是為什麼不管編碼怎麼做,Ogg 的延遲都有上界。 |
| 27 | variable | Segment table | 每個分段一個位元組,值是 0 到 255。一個封包的長度是一路累加下去,直到出現一個小於 255 的值才結束,所以剛好是 255 永遠代表「還沒完」。每個手寫的 Ogg 剖析器都是在這裡出錯的。 |
| variable | variable | First packet — the codec identification header | 緊接在分段表之後,所以在只有一個分段的第一頁上就是位移 28。Vorbis 寫 01 後面接 'vorbis',Opus 寫 'OpusHead',Ogg 裡的 FLAC 寫 7F 後面接 'FLAC',Theora 寫 80 後面接 'theora'。這裡、而不是副檔名,才是這段音訊真正的身分。 |
怎麼跟長得像的格式分開
| 容易搞混的 | 怎麼分 |
|---|---|
| oga/ogv/opus | 在容器層級根本沒有東西可以分辨:這四個副檔名用的都是同一套 'OggS' 框架,而慣例只是說 .oga 是音訊、.ogv 是影片、.opus 是 Opus 串流。答案永遠在分段表後面的識別封包 — 純 Vorbis 檔案位移 28 是 01 加 'vorbis',Opus 則是位移 28 的 'OpusHead'。改檔名什麼都改變不了,相信 MIME type 也一樣。 |
| FLAC | FLAC 有兩層皮,而且很容易搞混。原生 FLAC 位移 0 就是 66 4C 61 43,然後直接進一個 34 位元組的 STREAMINFO 區塊。裝在 Ogg 裡的 FLAC 跟其他東西一樣以 4F 67 67 53 開頭,而 7F 46 4C 41 43 這個標記出現在位移 28,後面接一個對應版本、一個標頭封包數,然後才是原生的 'fLaC' 標記和它的 STREAMINFO。 |
| WebM | 另一個無專利負擔的開放容器,也是大家在問「該用哪一個」時通常的另一個選項。WebM 和 Matroska 是 EBML 文件,開頭是 1A 45 DF A3,跟 'OggS' 一個位元組都不共用。影片在瀏覽器裡是 WebM 贏了;Ogg 守住了 Vorbis 和 Opus。 |
| MP3 | MP3 是沒有容器的裸音框串流,開頭不是一個 'ID3' 標籤就是一個十一位元全設的音框同步。它沒有 page 檢查碼、沒有序號,也沒有辦法通報掉了一個音框 — 而那正是 Ogg 當初為串流而生要補的缺口。 |
常見的壞法
- 即時串流裡掉了一頁。Ogg 把這件事當成正常而不是致命的:位移 18 的序號跳號,解碼器回報一段空缺,然後在下一個擷取樣式的位置繼續播。部分損壞的下載也是同一套救法 — 往前掃描 4F 67 67 53,從那裡接下去。
- 被截斷、而且沒有結束串流的那一頁。因為最後一頁永遠沒被設上標頭類型旗標的位元 2,沒有東西交代最終的 granule 位置,所以總長度是未知的。音訊會把收到的部分播完,而進度條和報出來的長度一直是錯的。
- page 內容位元腐蝕。位移 22 的 page 檢查碼對不上,解碼器丟掉的是整個 page — 零點幾秒的音訊 — 而不是整個檔案。損壞聽起來是一聲喀,後面每一頁的驗證照樣通過。
- 隨手串接。用 `cat` 把兩個 Ogg 檔案接起來,得到的是一條合法的鏈式串流、帶著兩個不同的序列號,但非常多播放器會停在第一個結束串流的 page,只報出第一個檔案的長度。解法是重新封裝,不是串接。
- 前面被塞了位元組。一段 HTTP 錯誤內容或被黏在前面的 ID3 標籤,會把第一個擷取樣式擠離位移 0。每一頁都完好無缺,會重新同步的播放器照樣找得到它們,但一個只讀位移 0 的偵測器會把這個檔案報成不明格式。
這個格式現在還在用嗎
Ogg 今天的任務比從前窄,但站得非常穩。Vorbis 是當年對抗 MP3 的自由答案,至今仍是極大量遊戲與開源應用程式封裝音訊的方式,因為在 MP3 還有專利包袱的年代它沒有。Opus 標準化在 RFC 6716,檔案用 Ogg 裝、通話用 RTP 送,如今是 WebRTC 的預設語音編碼,也是現存最好的通用低延遲音訊編碼 — 所以 Ogg 從編碼大戰裡走出來時,裡面裝的正是贏家。影片這一塊,這個容器輸給 WebM 和 MP4 輸得很徹底,.ogv 實際上已經是歷史名詞。讓 Ogg 仍是第一選擇的,是那種必須在丟包中活下來的串流:page 是自我描述、帶檢查碼、而且可以重新同步的,所以解碼器可以中途加入即時串流,也能在掉了一個封包之後自行復原,完全不需要任何帶外訊號。
在這個站上打開 Ogg 檔
這些全部在你的瀏覽器裡跑,檔案不會離開你的電腦。
相關格式
容易和 Ogg 搞混的格式,可以比對它們的開頭位元組。