一、前言
ffmpeg在視音頻編解碼領域算是一個比較成熟的解決方案了。公司的一款視頻編輯軟件正是基於ffmpeg做了二次封裝,並在此基礎上進行音視頻的編解碼處理。然而,在觀察編碼后的視頻質量時,發現圖像幀出現了較為明顯噪聲,類似於水面波紋一般散發開來,在運動場景下尤為明顯。初步懷疑應該是碼率太低導致的畫面失真。於是增大碼率重新編碼一次,噪聲仍然很明顯。基本上可以排除是碼率太低的問題。
仔細觀察原片,也可發現有類似的圖像噪聲出現,但是微乎其微到幾乎不可察覺。於是再次懷疑是ffmpeg在編解碼的過程中,將這個噪聲放大了,導致最終產出的視頻出現了明顯的噪聲干擾。而代碼中我們正好用了ffmpeg實現的swscale()方法。在正式編碼之前,我們需要用該方法將YUV數據轉換為RGB數據來處理。因此,此處調用正是症結所在。
二、解決方案
前面說了,在正式編碼之前我們需要將YUV數據轉換為RGB來渲染。既然是swscale()方法的原因,那么是否可以在渲染的時候通過多重采樣來降低圖形噪聲呢?事實上還是too young too simple。開啟多重采樣還是沒有卵用。我們的渲染庫又必須采用RGB格式的數據,難道不用swscale()方法么?是否有替代品呢?
libyuv is an open source project that includes YUV scaling and conversion functionality.
於是馬上下載下來進行測試。
git clone https://chromium.googlesource.com/libyuv/libyuv
網上關於libyuv的資料貌似比較少,官方也沒看到什么文檔。好在項目也不大,把頭文件看一看,基本上可以了解具體的使用方法的。使用CMake生成VS編譯工程,可以產出靜態庫和動態庫兩種類型:

使用實例:
// yuv420p -> bgr24
if (mOutputFormat == AV_PIX_FMT_BGR24)
{
I420ToRGB24(src_frame->data[0],
src_frame->linesize[0],
src_frame->data[1],
src_frame->linesize[1],
src_frame->data[2],
src_frame->linesize[2],
scale_frame->data[0],
mOutputWidth*3,
mOutputWidth,
mOutputHeight);
}
// bgr24 -> yuv420p
else if (mOutputFormat == AV_PIX_FMT_YUV420P)
{
RGB24ToI420(src_frame->data[0],
src_frame->linesize[0],
scale_frame->data[0],
scale_frame->linesize[0],
scale_frame->data[1],
scale_frame->linesize[1],
scale_frame->data[2],
scale_frame->linesize[2],
mOutputWidth,
mOutputHeight);
}
else {
return false;
}
接口非常簡潔明了。編碼的視頻效果對比如下(上圖是libyuv實現,下圖是ffmpeg的swscale實現):


可以看出來,下圖有類似於水面波紋一樣的噪聲,而上圖則幾乎不可見。
