a13baed16fde7c1ec53de30a8d691358.png
最近的案子使我又回老本行-處理聲音壓縮。以前都是用 G 系列的,而最近要接觸的是一個 open、free royalty 的 audio codec,Vorbis。
跟 G 系列的不同是,它不是 speech codec 而是 audio codec。所以可以應用的範圍就更為廣泛,除了語音壓縮之外,還可以用在音樂等其他的壓縮。
為了怕自己把看過的文件忘光光(年過三十之後就開始有記憶力退化的狀況),必須做點筆記,把這當作 "外部儲存設備" ,避免腦袋老化的情形!
主要說明文件在此:Vorbis Specification,有興趣的人可以 認真看!看不懂請不要問我,因為我也看不懂!
以下只做簡單、重要部分的筆記。
Vorbis 提供兩種聲音壓縮 Mode,以下這段話相當重要,也因為這樣的彈性,使的我必須去深究 Vorbis 的內部,然後調整到可以在我的專案中使用。
 
Window shape decode (long windows only) Vorbis frames may be one of two PCM sample sizes specified during codec setup. In Vorbis I, legal frame sizes are powers of two from 64 to 8192 samples. Aside from coupling, Vorbis handles channels as independent vectors and these frame sizes are in samples per channel.
 
1. 第一種模式,是相同 size 的 window 處理方式。
011e1990229b4c9d7a7104d5aa0a3c1d.png
 
2. 第二種模式,是不同 size 的 window 處理方式。
我個人認為,這樣不同的 size 是為了 internet streaming 的需要,因為可能因為網路 jitter 的關係,聲音來源信號不穩定,所以有這樣的模式可以處理 "有多少資料就壓多少資料" 的特性。
只是實務上會把程式架構搞複雜,通常對這種不穩定的狀況,我們都會 maintain 一個 jitter buffer 做處理,不過有 jitter buffer 又會影響到 real time 的要求。不過,網路不穩定,又要兼顧 real-time 以及 jitter buffer,那就變得很複雜了!
所以需要一個 adaptive jitter buffer 來處理。那正是小弟研究所論文的題目!請自行到國家圖書館查閱。呵呵,扯遠了。
 
9a0f67e5902a51d2a006933640523c46.png
 
簡單提一下原理後,就要進入程式的深處了。先講一下 Vorbis 的壓縮現象以及我的需求。
 
在 Vorbis 中,如果每次丟進同樣長度的 sample (比如說 32k 個 samples) ,在前兩次,Initial 階段,都不會有資料輸出。要到了第三組 sample 後才會開始輸出資料。可是問題就出在這!每次輸出資料的 "長度" 是不同的。這件事情應用在 streaming 上問題還不大,可是要應用在結合 video 後 editing 的需求問題就很大了!
 
試想,如果今天在一段影片 中,video frame 與 voice frame 一起存放,但是因為輸出的長度不同,編輯時就很有可能剛好 "切錯" 地方,這樣你的 editing 就會爛掉了!所以,必須調整他的機制變成每次輸入固定的 samples 就要輸出相同的 encoded frame。這樣在編輯時才不需要為 video 與 audio maintain 兩組 timecode ,造成 sync 上的複雜度。
 
( code tracing 中....)
 
經過一段時間的 tracing 後,以下跳過中間研究的過程,直接跳到結論。
 
由於有動態 block size encoding 的特性,所以造成我的困擾,所以,先抓幾種不同的聲音來測試看看反應。
 
測試後發現,輸出的行為跟 "聲音本身" 有關,也就是說,Vorbis 跟大多數的 voice/speech codec  一樣,會去看聲音的特性來決定壓縮的方式以及 codebook。特別用 single-tone / dual-tone 測試時特別明顯,會在聲音波形的 Boundry 的地方產生異動。所以,暫時先放棄去追如何判斷聲音的部分,因為較複雜,改從 window block size 的方向下手。
 
研究 source code 之後發現,只要修改 window size ,限制最大和最小容許值就可以了。以下為修改的部分:
在 Vorbis\libvorbis\lib\modes\ 下有 setup_8.h, setup_11.h, setup_16.h, setup_22.h, setup_32.h, setup_44.h, setup_44p51.h, setup_44u.h, setup_X.h。
 
分別是不同的 sample rate 會使用不同的 setup 。在我的專案中,我需要修改的是 setup_44.h。
 
在這個檔案中,有定義了 blocksize_short_44 以及 blocksize_long_44。事實上,這兩個常數陣列就是前面所提到的 window mode。
 
blocksize_short_44 表示壓縮所需 "最小" 的 block size,blocksize_long_44 則表示 "最大" 的。
原本的內容是:
static const int blocksize_short_44[11]={
  512,256,256,256,256,256,256,256,256,256,256
};
 
static const int blocksize_long_44[11]={
  4096,2048,2048,2048,2048,2048,2048,2048,2048,2048,2048
};
只要修改以下即可:
static const int blocksize_short_44[11]={
    512,2048,256,256,256,256,256,256,256,256,256
};
 
static const int blocksize_long_44[11]={
    4096,2048,2048,2048,2048,2048,2048,2048,2048,2048,2048
};
眼尖的你應該會發現。我只有調整 blocksize_short_44 的第二個元素為 2048。
 
因為在我的專案中,我所選用的 quality = 0 。( range : -0.1 ~ 1.0 )
 
所以我只調整第二組,如果你用的 quality 不同,則要調整相對應的元素。對應表可參考以下陣列:
static const double quality_mapping_44[12]={
  -.1,.0,.1,.2,.3,.4,.5,.6,.7,.8,.9,1.0
};

這樣修改實測後發現還是有些奇怪的現象。一般情況下輸出兩組 samples 會吐出兩組 encoded packets。但是某些情況會輸入兩組,吐出三組,但下一次就一定是輸入兩組,吐出一組,平均來說還是輸入兩組吐出兩組。符合正規的情形,所以這樣把壓縮後的資料存到 video frame 裡面是不會有問題的!接下來就是要把這些情形封裝到一個物件中,對上層 AP 來說,應該就是丟一組吐一組,一個很規律的情形。這部分只要做一個 buffer 處理就可以了,並不是太困難,等寫好再來分享經驗!
 
順帶一提,網路上關於 Vobis 的討論真的很少,大家似乎都只拿來用,很少去改動裡面的東西。所以只能像瞎子摸象,憑感覺+經驗去查找,測試。所以我也不敢講這是 100% 正確的,不過就我對 voice codec 的了解,這樣邏輯上應該是對的,實際丟不同的聲音類型 ( speech / slience / noise / singal-tone / dual-tone ) 進去都是對的。接下來就是要實戰了!
若有朋友也在苦戰 vorbis 的話,歡迎留言討論喔!
Facebook 討論區載入中...