用ffmpeg解码MP4后,得到YUV数据,然后YUV转换成RGB.
没隔一段时间需要保存一张图片,这里就打算直接将RGB数据写成BMP文件。
因为BMP文件是直接写RGB数据,并不编码,减少CPU的损耗。
就这么一个功能,遇到了好多坑。
一,如何用RGB写BMP文件
BMP文件的图片数据是原始RGB数据,不需要编码。但是需要在文件最开始打上BMP文件头,后面直接写RGB的数据就可以了。
BMP文件头信息如下:
typedef struct {
WORD bfType;//0x4d42
DWORD bfSize;//sizeof(BMPFILEHEADER_T)+sizeof(BMPINFOHEADER_T)+width*height*3, 整个文件的大小
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;//sizeof(BMPFILEHEADER_T)+sizeof(BMPINFOHEADER_T),就是RGB数据的偏移量, 偏移这两个数据结构
} BMPFILEHEADER_T;
typedef struct{
DWORD biSize;//sizeof(BMPINFOHEADER_T)
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;//RGB24, 就是24位
DWORD biCompression;//0, 不压缩
DWORD biSizeImage;//RGB24数据大小
LONG biXPelsPerMeter;//
LONG biYPelsPerMeter;//
DWORD biClrUsed;//0
DWORD biClrImportant;//0
} BMPINFOHEADER_T;
也就是说,整个BMP文件就是:BMPFILEHEADER_T+BMPINFOHEADER_T+RGB原始数据。简单吧。
然后直接写文件就可以了:
bmpheader.bfType = 0x4d42;
bmpheader.bfReserved1 = 0;
bmpheader.bfReserved2 = 0;
bmpheader.bfOffBits = <span style="font-family: Arial, Helvetica, sans-serif;">sizeof(BMPFILEHEADER_T) </span>+ sizeof(BMPINFOHEADER_T);
bmpheader.bfSize = bmpheader.bfOffBits + width*height*bpp/8;
bmpinfo.biSize = sizeof(BMPINFOHEADER_T);
bmpinfo.biWidth = width;
bmpinfo.biHeight = -height;
bmpinfo.biPlanes = 1;
bmpinfo.biBitCount = bpp;
bmpinfo.biCompression = 0;
bmpinfo.biSizeImage = (width*bpp+31)/32*4*height;
bmpinfo.biXPelsPerMeter = 100;
bmpinfo.biYPelsPerMeter = 100;
bmpinfo.biClrUsed = 0;
bmpinfo.biClrImportant = 0;
fwrite(&bmpheader, sizeof(BMPFILEHEADER_T), 1, fp);
fwrite(&bmpinfo, sizeof(bmpinfo), 1, fp);
fwrite(pFrameRGB, width*height*bpp/8, 1, fp);
fclose(fp);
按照上面的代码写出来的文件,文件图片打开失败,说文件破损。
后来才发现BMPFILEHEADER_T这个数据接口的内部变量排序,并没有4字节对齐,导致sizeof(BMPFILEHEADER_T)比预想的大!
所以直接用宏设置数据结构的字节对齐方式为:1字节对齐。
#pragma pack(1)
typedef struct {
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BMPFILEHEADER_T;
typedef struct{
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BMPINFOHEADER_T;
#pragma pack()
这些好了,程序运行图片可以打开了,但是还有另外一个坑。
现在图片可以打开,但是人物的颜色都是绿色,都变成“绿巨人”
三、坑二:图片颜色不对--RGB24还是BRG24?
YUV转RGB用的是ffmpeg的swscale代码:
_swsContext = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
pCodecCtx->width, pCodecCtx->height, <span style="background-color: rgb(51, 51, 255);">AV_PIX_FMT_RGB24</span>, SWS_BICUBIC, NULL, NULL, NULL);
YUV转换的结果是RGB24,但是这样出来的RGB数据,写成的BMP文件,显然是颜色错误。
修改成BRG24问题得到解决,如下:
_swsContext = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_BGR24, SWS_BICUBIC, NULL, NULL, NULL);