本图片转载自相关开发文档
文件格式头是干什么的?如果把后缀名比作一本书的“标题”“封面”,那么文件格式头实际上就是一个文件信息,好比文章中的“导论”“前言”。在操作系统中,虽然我们有注册表中的后缀名这种机制去分别不同的文件,但是单靠后缀名是不够的,因为后缀名在某些时候会被不经意的修改。比如,恶意软件、病毒等,这就好比“挂羊头,卖狗肉”地给了你一本《九阴真经》,实际上却是个漫画书一样。因此,给不同格式的文件创建相应的文件头,就可以保证在后缀名被修改的情况下,依然能被识别。在浏览视频图片这些非线性数据结构时,我们要用创建相应的文件信息头去描述这个文件“是什么”“有什么作用”等信息,这样就可以保证,当我们的软件打开这些文件的时候,就能选择正确的方法。因为,即便是相同的图片,也有不同的格式和编解码算法,因此“前言”虽短,却很重要。他告知软件采取什么样的方式打开识别这个文件,他宣布了文件是以什么样的方式组织、编码,今天我想说的就是BMP文件头,如果我们用一些开源的编解码库获取了描述颜色的原始数据,如何人工给他创建文件头?
1.你必须查阅相关资料,获取某种格式的文件头描述。在音视频中,“文件头描述+后缀名”这2个关联的机制,被称之为“描述容器的组成结构”。
2.你应该根据需要,从原始数据中获取文件头需要的信息,比如,文件大小?设备无关性?是否压缩?编码方式?组成结构?
3.先将文件头数据一一填充,以二进制的方式写入,即使用 fwrite,绝非 fprintf。再按照组成顺序,写入原始数据。比如,bmp 位图中,文件开始部分是 height - 1 高度的一行数据,最后面才是 0 高度的数据,如果你的循环是从 height - 1 向 0 这个方向进行并写入数据的,那么你得到的图片双击打开后就是正着的,否则会上下颠倒。
对于已知 RGB 位图数据,绘制代码可以如下编写(BitBlt不支持缩放,StretchBlt 缩放位图,TransparentBlt 缩放并支持透明):
HDC hCompatibleDC = CreateCompatibleDC(hDc);
HBITMAP hCompatibleBitmap = CreateCompatibleBitmap(hDc, bitmapinfoheader.biWidth,
bitmapinfoheader.biHeight);
HBITMAP hOldBitmap = (HBITMAP)SelectObject(hCompatibleDC, hCompatibleBitmap);
SetDIBits(hDc, hCompatibleBitmap, 0, bitmapinfoheader.biHeight,
buffer, (BITMAPINFO*)&bitmapinfoheader, DIB_RGB_COLORS);
BitBlt(hDc, nStartX, nStartY, bitmapinfoheader.biWidth, bitmapinfoheader.biHeight,
hCompatibleDC, 0, 0, SRCCOPY);
SelectObject(hCompatibleDC, hOldBitmap);
DeleteObject(hCompatibleDC);
DeleteObject(hCompatibleDC);
// 这么干也行
StretchDIBits(hDc, nStartX, nStartY, bitmapinfoheader.biWidth,bitmapinfoheader.biHeight, 0, 0,
bitmapinfoheader.biWidth,bitmapinfoheader.biHeight, buffer, (BITMAPINFO*)&bitmapinfoheader,
DIB_RGB_COLORS, SRCCOPY);
具体实例代码:
void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame)
{
FILE *pFile;
char szFilename[32];
int y;
// Open file, 就是编写封面、书名
sprintf(szFilename, "frame%d.bmp", iFrame);
pFile=fopen(szFilename, "wb");
if(pFile==NULL)
return;
// Write header:
// SweetLover added it on Nov the 7th, 2013
// The original data format is ppm file format
// 就是写“前言”
BITMAPFILEHEADER bfh;
bfh.bfType = 0x4D42;
bfh.bfSize = width * height * 3 + 54;
bfh.bfReserved1 = 0;
bfh.bfReserved2 = 0;
bfh.bfOffBits = 0x36;
BITMAPINFOHEADER bih;
bih.biSize = 0x28;
bih.biWidth = width;
bih.biHeight = height;
bih.biPlanes = 1;
bih.biBitCount = 24;
bih.biCompression = 0;
bih.biSizeImage = width * height * 3;
bih.biXPelsPerMeter = 0;
bih.biYPelsPerMeter = 0;
bih.biClrUsed = 0;
bih.biClrImportant = 0;
fwrite(&bfh, 1, sizeof(bfh), pFile);
fwrite(&bih, 1, sizeof(bih), pFile);
// Write pixel data:
// SweetLover corrected it on Nov the 7th, 2013
// It's different from ppm file, the height in bmp file is from high to low
// 将一个个点按照从上到下的顺序画进去,所以 height 从大到 0
for(y=height-1; y>=0; y--)
fwrite(pFrame->data[0]+y*pFrame->linesize[0], 1, width*3, pFile);
// Close file
// 封底
fclose(pFile);
}