最近在学习使用ffmpeg进行视频图像的处理,现将学习心得记录如下:
使用ffmpeg编程主要是使用库里面的一些函数进行视频处理,之前也在网络上找了一些教程来学习,但是由于网络上的教程都是比较早的,但是现在很多接口在库中的定义都已经改变跟替换了,所以导致原先编译存在许多错误,经过多次的查阅资料和查看源代码,终于将第一部分关于ffmpeg功能实现了,实现的功能是将视频中一部分视频帧保存成PPM格式文件。
大体步骤如下:
1> 首先使用av_register_all()函数来注册ffmpeg使用到的所有的库
2>使用avformat_open_input()函数来打开一个文件,并将文件的头信息保存到AVFormatContext类指针所指向的那个类当中。函数调用如下avformat_open_input(AVFormatContext*pFormatCtx,const char*path,NULL,NULL);后面两个参数没用到,所以用NULL,如有需要查看源代码。(注:这个函数在旧的版本中是av_open_input_file,现已被替换)
3>使用av_format_find_stream_info()函数来查看流信息;
4>从AVFormatContext类中找到编解码器上下文指针。pFormatCtx->streams[videoStream]->codec(注:此处在判断某个流是视频流的时候,使用AVMEDIA_TYPE_VIDEO代替旧版本的CODEC_TYPE_VIDEO)
5>根据编解码器上下文中的codec_id找到编码器 使用avcodec_find_decoder(pCodecCtx->codec_id);
6>打开编解码器:avcodec_open2(pCodecCtx,pCodec,NULL) (注:旧版本是avcodec_open已被替换);
7>使用avcodec_alloc_frame()开辟两个帧空间,一个原始帧空间,一个准备转换后的帧空间
8>使用avpicture_get_size来判断我们需要开辟多大的空间
9>使用avpicture_fill将我们开辟的空间与AVPicuture指针关联起来,(注:由于AVPicture 是 AVFrame的子集,所以此处将AVFrame强制转换成AVPicture)
10>从视频流读取包,使用av_read_frame(pFormatCtx,&pachet)
11>把包含我们所需要的原始数据的帧从包里解析出来,使用avcodec_decode_video2(pCodecCtx,pFram,&frameFinished,&packet);函数。(注:旧版本是avcodec_decode_video)函数中frameFinished作为帧结束标志。
12>进行帧格式的转化,旧版本中使用img_convert()函数,这个函数已经不存在了,现在使用sws_scale()函数进行替换
使用步骤如下:
(1)获取SwsContext的信息
(2)使用sws_scale进行转换
13>将帧保存为PPM格式的文件,这里使用的不是库函数,而是自定义函数
14>释放所有资源
源代码如下:
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavcodec/avcodec.h>
void SaveFrame(AVFram* pFrame,int width,int height,int iFrame);
int main(int argc,char* argv[])
{
AVFormatContext *pFormatCtx = NULL;
if(argc < 2)
{
return -1;
}
char *filepath = argv[1];
if(avformat_open_input(&pFormatCtx,filepath,NULL,NULL) != 0)
{
printf("can not open the media file you specified!\n");
return -1;
}
if(avformat_find_stream_info(pFormatCtx,NULL) < 0)
{
printf("can not get the file information you specified!\n");
return -1;
}
int i = 0;
int videoStream = -1;
AVCodecContext *pCodecCtx = NULL;
for(i = 0; i < pFormatCtx->nb_streams; i++)
{
if(pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
{
videoStream = i;
break;
}
}
if(videoStream == -1)
return -1;//Didn't find a video stream
//Get a pointer to the codec context for the video stream
pCodecCtx = pFormatCtx->streams[videoStream]->codec;
AVCodec *pCodec = NULL;
//Find the decoder for the video stream
pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
if(pCodec == NULL)
{
fprintf(stderr,"Unsupported codec!\n");
return -1;
}
//Open Codec
if(avcodec_open2(pCodecCtx,pCodec,NULL) < 0)
{
fprintf(stderr,"Could not open codec");
return -1;
}
AVFrame *pFrame = NULL,*pFrameRGB = NULL;
pFrame = avcodec_alloc_frame();
pFrameRGB = avcodec_alloc_frame();
if(pFrameRGB == NULL)
return -1;
uint8_t *buffer;
int numBytes;
numBytes = avpicture_get_size(PIX_FMT_RGB24,pCodecCtx->width,
pCodecCtx->height);
buffer = (uint8_t*)av_malloc(numBytes*sizeof(uint8_t));
avpicture_fill((AVPicutre *)pFrameRBG,buffer,PIX_FMT_RBG24,
pCodecCtx->width,pCodecCtx->height);
int frameFinished;
AVPacket packet;
while(av_read_frame(pFormatCtx,&packet) >= 0)
{
if(packet.stream_index == videoStream)
{
avcodec_decode_video2(pCodecCtx,pFrame,&frameFinished,&packet);
if(frameFinished)
{
struct SwsContext *pSwsCtx;
pSwsCtx = sws_getContext(pCodecCtx->width,pCodecCtx->height,
pCodecCtx->pix_fmt,pCodecCtx->width,
pCodecCtx->height,PIX_FMT_RGB24,SWS_BICUBIC,
NULL,NULL,NULL);
pFrame->data[0] += pFrame->linesize[0]*(pCodecCtx->height - 1 );
pFrame->linesize[0] *= -1;
pFrame->data[1] += pFrame->linesize[1]*(pCodecCtx->height/2 - 1);
pFrame->linesize[1] *= -1;
pFrame->data[2] += pFrame->linesize[2]*(pCodecCtx->height/2 - 1);
pFrame->linesize[2] *= -1;
sws_scale(pSwsCtx,pFrame->data,pFrame->linesize,0,
pCodecCtx->height,pFrameRGb->data,
pFrameRGB->linesize);
if( ++i <= 15)
saveFrame(pFrameRGB,pCodecCtx->width,pCodecCtx->height,i);
}
}
av_free_packet(&packet);
}
av_free(buffer);
av_free(pFrameRGB);
av_free(pFrame);
avcodec_close(pCodecCtx);
avformat_close_input(&pFormatCtx);
return 0;
}
void SaveFrame(AVFrame *pFrame,int width,int height,int iFrame)
{
FILE *pFile;
char szFilename[32];
int y;
//Open file
sprintf(szFilename,"frame%d.ppm",iFrame);
pFile = fopen(szFilename,"wb");
if(pFile == NULL)
return;
//write header
fprintf(pFile,"P6\n%d %d\n255\n",width,height);
//write pixel data
for(y = 0;y < height ;y++)
fwrite(pFrame->data[0] + y*pFrame->linesize[0],1,width*3,pFile);
fclose(pFile);
}