用ffmpeg来处理多媒体的就上面的步骤M那么简单,即使你的第四步可能很复杂。所以在本教程,我们先打开一个视频,读取视频流,获得帧,然后第四步是把帧数据存储为PPM文件。
打开流媒体文件:
打开一个视频,首先头文件包含进来。
#include
#include
#include
av_register_all()函数,只需调用一次,他会注册所有可用的文件格式和编码库,当文件被打开时他们会自动匹配相应解码库。
现在真正要打开一个文件:
AVFormatContext *pFormatCtx;
If(avformat_open_input(&pFormatCtx,filename,NULL,0,NULL)!= 0)
Return -1;
参数:获得文件路径,这个函数会读取文件头信息,并把信息保存在PformatCtx结构体当中,这个函数后面三个参数分别:指定文件格式、缓存大小和格式化选项,当我们设置为NULL或者0时,libavformat会自动完成这些工作。
这个函数仅仅是用于获取头信息,接下来我们得到流信息:
if(avformat_find_stream_info(pFormatCtx) < 0)
return -1;
这个函数填充了pFormatCtx->streams流信息,可通过函数av_dump_format把信息打印出来:
av_dump_format(pFormatCtx,0,filename,0);
pFormatCtx->streams只是大小为pFormatCtx->nb_streams的一系列的点,我们从中可以得到视频流。
int i;
AVCodecContext *pCodecCtx;
videoStream = -1;
for(I = 0; I
{
if(pFormatCtx->stream[i]->codec_type== AVMEDIA_TYPE_VIDEO)
{
videoStream = I;
break;
}
}
if(videoStream == -1)
return-1;
数据存储:
我们现在需要存储一帧数据:
AVFrame *pFrame;
pFrame = av_frame_alloc(void);
我们计划存储的PPM文件,其存储的数据是24位RGB,我们需要把得到的一帧数据从本地格式转换为RGB,ffmpeg可以帮我们完成这个工作。在很多工程里,我们都希望把原始数据帧转换到特定格式。
AVFrame *pFrameRGB;
pFrameRGB =av_frame_alloc(void);
if(pFrameRGB == NULL)
return -1;
即使分配了帧空间,我们需要空间来存放转换时raw数据,我们用avpicture_get_size来得到需要的空间,然后手动分配。
Uint8_t *buffer;
Int numBytes;
numByters =avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,pCodecCtx->height);
buffer = (uint8_t *)av_malloc(numByters*sizeof(uint8_t));
av_malloc是ffmpeg简单封装的一个分配函数,重要用于保证内存地址的对齐等,他并不保证·内存泄漏、二次释放或者其它malloc问题。
现在,我们使用avpicture_fill来关联新分配的缓冲区的帧。AVPicture结构体是AVFrame结构体的一个子集,开始的AVFrame是和AVPicture相同。
avpicture_fill((AVPicture*)pFrameRGB, buffer, PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height);
下一步我们准备读取流了!
读取流媒体数据:
在读取整个视频数据流是通过包来实现的,然后在解码到帧中,进行一帧数据读取,并保存它。
int framFinished;
AVPacket packet;
i=0;
while(av_read_frame(pFormatCtx,&packet)>=0) {
if(packet.stream_index==videoStream) {
int result;
avcodec_decode_video2(pCodecCtx,pFrame,&frameFinished,&packet);
if(frameFinished) {
img_convert_ctx = sws_getContext(pCodecCtx->width,pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,PIX_FMT_RGB24,SWS_BICUBIC,NULL, NULL,NULL);
result = sws_scale(img_convert_ctx, (const uint8_t*const*)pFrame->data,
pFrame->linesize,0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);
printf("get result is %d\n",result);
printf("i is %d \n",i);
if(++i<=5) {
SaveFrame(pFrameRGB, pCodecCtx->width,pCodecCtx->height, i);
}
}
}
av_free_packet(&packet);
}
现在需要做的事情就是写入SaveFrame函数,该函数用于保存数据到PPM文件。
void SaveFrame(AVFrame*pFrame, int width, int height, int iFrame) {
FILE *pFile;
char szFilename[32];
nt y;
printf("start sws_scale\n");
sprintf(szFilename, "frame%d.ppm", iFrame);
pFile=fopen(szFilename, "wb");
if(pFile==NULL){
printf("pFile is null");
return;
}
fprintf(pFile, "P6\n%d %d\n255\n", width, height);
for(y=0; y fwrite(pFrame->data[0]+y*pFrame->linesize[0], 1, width*3,pFile); } fclose(pFile); } 上述函数是写入RGB数据,一次写入一行文件,PPM文件就是简单的把RGB信息保存为一长串,头部记录着宽和高,和RGB的最大尺寸。 av_free(buffer); av_free(pFrameRGB); av_free(pFrame); avcodec_close(pCodecCtx); av_close_input_file(pFormatCtx); return 0; 这些就是全部代码来,现在你需要编译和运行 gcc -o 1 1.c -lavformat -lavcodec -lswscale -lz 得到video ,执行以下语句可得到同级目录下的5个PPM文件 ./1 2.mp4