我個人認為,這樣不同的 size 是為了 internet streaming 的需要,因為可能因為網路 jitter 的關係,聲音來源信號不穩定,所以有這樣的模式可以處理 "有多少資料就壓多少資料" 的特性。
只是實務上會把程式架構搞複雜,通常對這種不穩定的狀況,我們都會 maintain 一個 jitter buffer 做處理,不過有 jitter buffer 又會影響到 real time 的要求。不過,網路不穩定,又要兼顧 real-time 以及 jitter buffer,那就變得很複雜了!
簡單提一下原理後,就要進入程式的深處了。先講一下 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 的話,歡迎留言討論喔!