关于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

 

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