關於ffmpeg解碼輸出的YUV轉RGB花屏問題

現象

最近嘗試基於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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章