現象
最近嘗試基於ffmpeg封裝一個dll, 用於視頻解碼, 然後將yuv轉成RGB, 用於在網頁顯示視頻的畫面.
可是我將YUV轉成RGB之後, 發現花屏了.
研究了很久, 計算方式沒有發現問題, 代碼如下.
yuv420pToRGB24(pFrame->data[0], pFrame->data[1], pFrame->data[2],
pFrame->width, pFrame->height, outputBuf);
void yuv420pToRGB24(const BYTE *yBuf, const BYTE *uBuf, const BYTE *vBuf,
const int width, const int height,
BYTE *rgbBuf) {
int index = (width*height - 1) * 3;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
BYTE y = yBuf[i*width + j];
BYTE u = uBuf[(i / 2)*(width / 2) + j / 2];
BYTE v = vBuf[(i / 2)*(width / 2) + j / 2];
int data = (int)(y + 1.772 * (u - 128));
rgbBuf[index] = ((data < 0) ? 0 : (data > 255 ? 255 : data));
data = (int)(y - 0.34414 * (u - 128) - 0.71414 * (v - 128));
rgbBuf[index + 1] = ((data < 0) ? 0 : (data > 255 ? 255 : data));
data = (int)(y + 1.402 * (v - 128));
rgbBuf[index + 2] = ((data < 0) ? 0 : (data > 255 ? 255 : data));
index -= 3;
}
}
}
調試分析
仔細看花屏的紋路, 是每行都偏一定的大小, 又由於觀察到linesize[0]稍大於width (ffmpeg輸出的linesize[0]會基於2的指數倍對齊),
因此我懷疑是linesize[0], linesize[1], linesize[2] 對應的buf多出的部分參與了計算, 導致的問題.
有可能每行都多出一小段, 造成出現上圖中每行偏一點的情況.
爲了驗證這個猜測, 我嘗試將做如下修改.
修改
將linesize[0]傳入, 在每輪循環中, 遇到取yBuf, uBuf, vBuf時, 基於lineSize累加來計算地址偏移,
這樣跳過無效的地址, 以下代碼在計算中還可優化, 爲了方便理解, 就保留成下面的樣子.
花屏問題消失, 輸出RGB保存爲bmp顯示出來了.
yuv420pToRGB24(pFrame->data[0], pFrame->data[1], pFrame->data[2],
pFrame->width, pFrame->height, pFrame->linesize[0], outputBuf);
void yuv420pToRGB24(const BYTE *yBuf, const BYTE *uBuf, const BYTE *vBuf,
const int width, const int height, int lineSize,
BYTE *rgbBuf) {
int index = (width * height - 1) * 3;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
BYTE y = yBuf[i * lineSize + j];
BYTE u = uBuf[(i / 2) * (lineSize / 2) + j / 2];
BYTE v = vBuf[(i / 2) * (lineSize / 2) + j / 2];
int data = (int)(y + 1.772 * (u - 128));
rgbBuf[index] = ((data < 0) ? 0 : (data > 255 ? 255 : data));
data = (int)(y - 0.34414 * (u - 128) - 0.71414 * (v - 128));
rgbBuf[index + 1] = ((data < 0) ? 0 : (data > 255 ? 255 : data));
data = (int)(y + 1.402 * (v - 128));
rgbBuf[index + 2] = ((data < 0) ? 0 : (data > 255 ? 255 : data));
index -= 3;
}
}
}
圖像反了
雖然不花屏了, 但是轉出來的RGB上下是顛倒的.
所以再次修改代碼, 將 ((height-i-1)*width + j)*3作爲新的索引.
圖像翻轉回來, 這下徹底正常了.
void yuv420pToRGB24(const BYTE *yBuf, const BYTE *uBuf, const BYTE *vBuf,
const int width, const int height, int lineSize, BYTE *rgbBuf) {
int dstIndex = 0;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
BYTE y = yBuf[i * lineSize + j];
BYTE u = uBuf[(i / 2) * (lineSize / 2) + j / 2];
BYTE v = vBuf[(i / 2) * (lineSize / 2) + j / 2];
dstIndex = ((height - i - 1) * width + j) * 3;
int data = (int)(y + 1.772 * (u - 128));
rgbBuf[dstIndex] = ((data < 0) ? 0 : (data > 255 ? 255 : data));
data = (int)(y - 0.34414 * (u - 128) - 0.71414 * (v - 128));
rgbBuf[dstIndex + 1] = ((data < 0) ? 0 : (data > 255 ? 255 : data));
data = (int)(y + 1.402 * (v - 128));
rgbBuf[dstIndex + 2] = ((data < 0) ? 0 : (data > 255 ? 255 : data));
}
}
}
總結
如果不是觀察到linesize[0]寬度稍大於width, 怎麼也想不到這個跳過的方法.
所以, 在調試過程中遇到不合理的情況, 記下來, 方便在遇到問題時, 可以作爲分析問題的依據.
代碼
以下是我基於最新ffmpeg編譯的代碼, 包含轉RGB, 存bmp文件.
https://github.com/gzx-miller/ffmpeg_simple_api