代码改变世界

ffmpeg编程学习 ---chapter 01

2015-09-06 10:38  fengguowuhen201314  阅读(345)  评论(0编辑  收藏  举报

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

}