雷神FFMpeg源码学习笔记

雷神FFMpeg源码学习笔记

文章目录

    • 雷神FFMpeg源码学习笔记
      • 读取编码并依据编码初始化内容结构
      • 每一帧的视频解码处理

读取编码并依据编码初始化内容结构

  1. 在开始编解码视频的时候首先第一步需要注册一个编解码器 :av_register_all();
  2. avformat_open_input来打开这个文件并给AVformartcontext赋值 ,在其中会去查找当前缓存文件的格式 avformat_open_input 来打开这个文件并给AVformartcontext 赋值 ,在其中会去查找当前缓存文件的格式
  3. avformat_find_stream_info使用该方法给每个视频/音频流的AVStream 结构体进行赋值并得到,这个参数在里面实现了一定的解码过程
  4. AVstream 有值了以后我们需要拿到当前当前的avcodecContext和其对应的AVcodeC(使用avcodec_find_decoder)
  5. avcodec_open2,初始化一个视音频编解码器的AVCodecContext,,调用AVCodeC的初始化到具体的解码器 AVCodeC init() 所以是在avcodec_open2在开始真正的初始化avcodecContext
  6. 在得到了初始化的AVcodecContext之后我们就可以开始为解码之后的AVframe分配空间(使用(unsigned char *)av_mallocz(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecContex->width, pCodecContex->height, 1))) av_image_fill_arrays():为AVframe 的像素点分配空间
    sws_getContext:使用源图像的高和宽得到目标图像的高和宽,flag 为设定图像拉伸的算法

每一帧的视频解码处理

  1. av_read_frame 在解码之前来获取一帧的视频帧压缩数据 或者是多帧的音频帧压缩数据 及我们得到的只是AVpacket
  2. avcodec_decode_video2使用该函数来解码得到的AVpacket,输出一个下一帧的AVframe 函数
  3. 使用sws_scale来对下一帧的AVframe 进行拉伸变化 ,输出想要得到的AVframe
  4. 释放上述的AVformartcontextAVstreamavcodecContext.

下面是代码的具体处理操作:

1.首先需要注册所有的编解码器
//1.注册所有的编解码器等等
   av_register_all();
//在需要网络的情况下初始化
   avformat_network_init();
//2.初始化AVformartcontext,AVformartcontext 是
   pContext=avformat_alloc_context();
//3.得到视频流的URL 地址
   
//4.尝试打开文件流,在AVformartcontext 中查找当前的AVInputContext的格式
   if (avformat_open_input(&pContext, input_str_full, NULL, NULL)!=0) {
       NSLog(@"打开文件流失败");
       return;
   }

avformat_open_input:内部实现就是去打开当前文件并将其赋值给AVformartcontext ,
然后查找到当前AVformartcontext的AVinputcontext的格式,如:flv 等

//5.在AVStream 解码一段视音流信息 ,
   if (avformat_find_stream_info(pContext, NULL)<0){
       NSLog(@"解码得到AVstream流信息失败");
       return;
   }
avformat_find_stream_info:该函数主要是给每个视频/音频流的AVStream 结构体进行赋值
其实在他内部本身实现了解码的整个流程:查找解码器->打开解码器->读取完整的一帧压缩编码数据->解码压缩编码数据得到信息
//6.拿到当前avformatcontext ->AVstream 的AVCodecContext 视频流
   int videoIndex=-1;
   for (int i=0; inb_streams; i++) {
       if (pContext->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO) {
           videoIndex=i;
       }  
   }
   if (videoIndex==-1) {
       NSLog(@"当前的数据是错误的");return;
   }
   //拿到视频流的上下文
   AVCodecContext *pCodecContex=pContext->streams[videoIndex]->codec;

//7.通过avcodec_find——decoder 去找寻当前id 下的AVcodeC 解码器
   AVCodec *pCodec=avcodec_find_decoder(pCodecContex->codec_id);
   if (pCodec==NULL) {
       NSLog(@"找不到AVCodeC");
       return;
   }
 //8.初始化一个视音频编解码器的AVCodecContext,,调用AVCodeC的初始化到具体的解码器 AVCodeC init() 所以是在
   avcodec_open2在开始真正的初始化avcodecContext
   if (avcodec_open2(pCodecContex, pCodec, NULL)!=0 ) {
       NSLog(@"打开codeC错误");
       return;
   }
 avcodec_open2:参数为初始化一个codecContext 目标解码器  AVdictionary
   //创建一个AVframe 就是用来预测下一帧的视频帧,使用avcodec_decode_video2 预测下一帧
    AVFrame *pFrame;
    pFrame=av_frame_alloc();
    AVFrame *pFrameYUV;
    pFrameYUV=av_frame_alloc();
    uint8_t *outBuffer;

   //9 pFrameYUV分配空间,该函数并没有为AVFrame的像素数据分配空间,需要使用av_image_fill_arrays分配
   outBuffer=(unsigned char *)av_mallocz(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecContex->width,   
   pCodecContex->height, 1));

   //给目标的AVframe 来分配空间
   av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize, outBuffer,  AV_PIX_FMT_YUV420P, pCodecContex->width,             
   pCodecContex->height, 1);
    //10.给AVFrame 分配完了地址空间并给 像素点添加进去之后 需要是用libswscale 包&来进行图像的格式转换和图像的拉伸
   AVPacket *packet=(AVPacket *)av_malloc(sizeof(AVPacket));
   struct SwsContext *img_conver_ctx;


   /**
* Allocate and return an SwsContext. You need it to perform
* scaling/conversion operations using sws_scale().
*
* @param srcW the width of the source image
* @param srcH the height of the source image
* @param srcFormat the source image format
* @param dstW the width of the destination image
* @param dstH the height of the destination image
* @param dstFormat the destination image format
* @param flags specify which algorithm and options to use for rescaling
* @return a pointer to an allocated context, or NULL in case of error
* @note this function is to be removed after a saner alternative is
*       written
*/
   img_conver_ctx= sws_getContext(pCodecContex->width, pCodecContex->height, pCodecContex->pix_fmt, pCodecContex->width, pCodecContex->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);

sws_getContext:从sws_getContext()的定义中可以看出,它首先调用了一个函数sws_alloc_context()用于给SwsContext分配内存。然后将传入的源图像,目标图像的宽高,像素格式,以及标志位分别赋值给该SwsContext相应的字段。最后调用一个函数sws_init_context()完成初始化工作

   int frame_cnt;
   //用来计算处理事件用了多久的时间
   clock_t time_start ,timeFinsh;
   time_start=clock();
   int y_size;
   int got_picture, ret;
   
   FILE *fp_yuv;
   fp_yuv=fopen(output_str_full, "wb+");
   if (fp_yuv==NULL) {
       NSLog(@"不能打开该地址下的文件");
       return;
   }
   //11.解码之前都要先用 av_read_frame()获得一帧视频的压缩数据,或者是若干帧的音频数据,
   while (av_read_frame(pContext, packet)>=0) {
       if (packet->stream_index==videoIndex) {
           //avcodec_decode_video2()的作用是解码一帧视频数据。输入一个压缩编码的结构体AVPacket,输出一个解码后的结构体AVFrame
           ret=avcodec_decode_video2(pCodecContex, pFrame, &got_picture, packet);
           if (ret<0) {
               NSLog(@"解码下一帧失败");
               return;
           }
       if (got_picture) {
           //对图像进行拉伸等处理,这里是不进行拉伸变换
           sws_scale(img_conver_ctx, (const uint8_t* const* )pFrame->data, pFrame->linesize, 0, pCodecContex->height,                        pFrameYUV->data, pFrameYUV->linesize);
           y_size=pCodecContex->width*pCodecContex->height;
           fwrite(pFrameYUV->data[0], 1, y_size, fp_yuv);
           fwrite(pFrameYUV->data[1], 1, y_size/4, fp_yuv);
           fwrite(pFrameYUV->data[2], 1, y_size/4, fp_yuv);
           char picture_type_str[10]={0};
           switch (pFrame->pict_type) {
               case AV_PICTURE_TYPE_I:sprintf(picture_type_str,"I");break;
               case AV_PICTURE_TYPE_P:sprintf(picture_type_str,"P");break;
               case AV_PICTURE_TYPE_B:sprintf(picture_type_str,"B");break;
               default:sprintf(picture_type_str,"Other");break;
           }
           NSLog(@"frame index:%.5d type:%s",frame_cnt,picture_type_str);
           frame_cnt++;
       }
       }
       av_free_packet(packet);
   }
while (1) {
       ret=avcodec_decode_video2(pCodecContex, pFrame, &got_picture, packet);
       if (ret<0) {
           return;
       }
       if (!got_picture) {
           break;
       }
       sws_scale(img_conver_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecContex->height,          pFrameYUV->data, pFrameYUV->linesize);
       int y_size=pCodecContex->width*pCodecContex->height;
       fwrite(pFrameYUV->data[0],1,y_size,fp_yuv);    //Y
       fwrite(pFrameYUV->data[1],1,y_size/4,fp_yuv);  //U
       fwrite(pFrameYUV->data[2],1,y_size/4,fp_yuv);  //V
       //Output info
       char pictype_str[10]={0};
       switch(pFrame->pict_type){
           case AV_PICTURE_TYPE_I:sprintf(pictype_str,"I");break;
           case AV_PICTURE_TYPE_P:sprintf(pictype_str,"P");break;
           case AV_PICTURE_TYPE_B:sprintf(pictype_str,"B");break;
           default:sprintf(pictype_str,"Other");break;
       }
       printf("Frame Index: %5d. Type:%s\n",frame_cnt,pictype_str);
       frame_cnt++;
   
   }
   timeFinsh=clock();
   double time_duration=timeFinsh-time_start;
   NSLog(@"这个过程持续的时间是%f",time_duration);
   sws_freeContext(img_conver_ctx);
   fclose(fp_yuv);
   av_frame_free(&pFrameYUV);
   av_frame_free(&pFrame);
   avcodec_close(pCodecContex);
   avformat_close_input(&pContext);

你可能感兴趣的:(FFMpeg)