一个将视频流中帧转为BMP图片的例子.
代码如下:
#include "stdafx.h"
#include <string>
using namespace std;
#include <Windows.h>
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#pragma comment(lib, "avcodec.lib")
#pragma comment(lib, "avutil.lib")
#pragma comment(lib, "avformat.lib")
#pragma comment(lib, "swscale.lib")
}
string GetFilePath(string strFileName)
{
char szPath[MAX_PATH];
GetModuleFileNameA(NULL,szPath,MAX_PATH);
char* pEnd = strrchr(szPath,'\\');
*(pEnd+1) = '\0';
string strPath = szPath;
return strPath + strFileName;
}
void SaveFrameBMP(AVFrame* pFrame,int width,int height,int iFrame)
{
// 位图文件头
BITMAPFILEHEADER bmpheader;
BITMAPINFO bmpinfo;
int nBit = 24;
int nSize = width*height*nBit/8;
bmpheader.bfType = ('M' <<8)|'B';
bmpheader.bfReserved1 = 0;
bmpheader.bfReserved2 = 0;
bmpheader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
bmpheader.bfSize = bmpheader.bfOffBits + nSize;
bmpinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmpinfo.bmiHeader.biWidth = width;
bmpinfo.bmiHeader.biHeight = 0-height;
bmpinfo.bmiHeader.biPlanes = 1;
bmpinfo.bmiHeader.biBitCount = nBit;
bmpinfo.bmiHeader.biCompression = BI_RGB;
bmpinfo.bmiHeader.biSizeImage = 0;
bmpinfo.bmiHeader.biXPelsPerMeter = 100;
bmpinfo.bmiHeader.biYPelsPerMeter = 100;
bmpinfo.bmiHeader.biClrUsed = 0;
bmpinfo.bmiHeader.biClrImportant = 0;
//
char szFilename[32];
sprintf_s(szFilename,sizeof(szFilename),"frame%d.BMP",iFrame);
string strFrameName = GetFilePath(szFilename);
FILE* pFile = NULL;
fopen_s(&pFile,strFrameName.c_str(),"wb");
if(pFile == NULL)
return;
fwrite(&bmpheader,sizeof(BITMAPFILEHEADER),1,pFile);
fwrite(&bmpinfo.bmiHeader,sizeof(BITMAPINFOHEADER),1,pFile);
fwrite(pFrame->data[0],nSize,1,pFile);
fclose(pFile);
}
int _tmain(int argc, _TCHAR* argv[])
{
string strFileName = GetFilePath("test.mp4");
//初始化,注册所有基于FFMpeg的编码器
av_register_all();
//打开视频流文件,获取头
AVFormatContext* pFormatCtx = NULL;
if(avformat_open_input(&pFormatCtx,strFileName.c_str(),NULL,NULL) != 0)
{
printf("av_open_input_file error.");
return -1;
}
//获取视频流信息
if(av_find_stream_info(pFormatCtx) < 0)
{
printf("av_find_stream_info error.");
return -1;
}
//获取其他信息
av_dump_format(pFormatCtx,0,strFileName.c_str(),0);
//查找视频流
int nVideoStream =-1;
for(unsigned int i = 0;i < pFormatCtx->nb_streams;i++)
{
if(pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
{
nVideoStream = i;
break;
}
}
if(nVideoStream == -1)
{
printf("nVideoStream == -1 error.");
return -1; // Didn't find a video stream
}
//获取视频流信息指针
AVCodecContext* pCodecCtx = pFormatCtx->streams[nVideoStream]->codec;
//查找视频流解码器
AVCodec* pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
if(pCodec == NULL)
{
printf("avcodec_find_decoder error.");
return -1; // Codec not found
}
//打开解码器
if(avcodec_open(pCodecCtx,pCodec) < 0)
{
printf("avcodec_open error.");
return -1;
}
//
int nCount = 0;
//用于保存解码前的视频数据帧
AVFrame* pFrame = avcodec_alloc_frame();
//用于保存解码后的视频数据帧
AVFrame* pFrameRGB = avcodec_alloc_frame();
int numBytes = avpicture_get_size(AV_PIX_FMT_RGB24,pCodecCtx->width,pCodecCtx->height);
uint8_t* buffer = (uint8_t*)av_malloc(numBytes*sizeof(uint8_t));
avpicture_fill((AVPicture *)pFrameRGB,buffer,AV_PIX_FMT_RGB24,pCodecCtx->width, pCodecCtx->height);
//用于解码后数据格式转换
SwsContext* pSwsCtx = sws_getContext(pCodecCtx->width,pCodecCtx->height,pCodecCtx->pix_fmt,pCodecCtx->width,pCodecCtx->height,AV_PIX_FMT_RGB24,SWS_BICUBIC,NULL, NULL, NULL);
AVPacket packet;
while(av_read_frame(pFormatCtx,&packet)>=0)
{
//当前读取的数据流是否为视频流数据
if(packet.stream_index == nVideoStream)
{
//是否一帧已经完成
int frameFinished;
//解码视频流数据
avcodec_decode_video2(pCodecCtx,pFrame,&frameFinished,&packet); //
if(frameFinished)
{ //视频流转换
sws_scale(pSwsCtx,pFrame->data,pFrame->linesize,0,pCodecCtx->height,pFrameRGB->data, pFrameRGB->linesize);
if(nCount <= 5)
{
SaveFrameBMP(pFrameRGB,pCodecCtx->width,pCodecCtx->height,nCount);
}
++nCount;
}
}
av_free_packet(&packet);
}
//释放
sws_freeContext(pSwsCtx);
av_free(buffer);
av_free(pFrameRGB);
av_free(pFrame);
avcodec_close(pCodecCtx);
av_close_input_file(pFormatCtx);
return 0;
}