最近在学习使用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);
}