ffmpeg视频解码讲解

  用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 nb_streams ; 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 ,执行以下语句可得到同级目录下的5PPM文件

./1  2.mp4





 


    

你可能感兴趣的:(ffmpeg视频解码讲解)