现象
最近尝试基于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