ffmpeg编码器+VS2008的环境配置以及视频编码过程详解

来源:互联网 发布:javascript classname 编辑:程序博客网 时间:2024/05/23 14:51

    ffmpeg编码器+VS2008的环境配置以及视频编码过程详解

Jun_L

http://blog.csdn.net/jingjun1822

 由于项目上的需要,连续六天被ffmpeg给坑了。跟X264不一样,ffmpeg编码器的资料五花八门,就一个编码的编译和环境配置过程,就让我头疼了好一段时间,为了让后面的享用ffmpeg的人不会像我这样晕,在完成任务的第六个晚上,我总结了一下我的ffmpeg编码器使用过程,我的了解也不是很深入,希望我的一点经验能给大家带来帮助,讲错的地方也希望大家指正。

     一、ffmpeg+vs2008环境配置
     网上有诸多编译生成库文件.lib和动态库.dll的的博客,那些主要是针对前期版本的ffmpeg。感谢ying1feng23同学的博客对我的启发,我从他的博客上了解到了快捷的办法:
http://blog.csdn.net/ying1feng23/article/details/9707437
     首先从ffmpeg官网中下载ffmpeg数据包:
http://www.ffmpeg.org/download.html,
     下载的时候要注意自己的VS的编译器是WIN32的还是WIN64的,注意只看编译器,跟系统位数没有关系。

     官网提供四个版本下载:
           1.静态编译版本:提供编译好的指令文件如ffmpeg、ffmplay等可执行文件
           2.共享库编译版本(shared):提供编译好的指令文件如ffmpeg、ffmplay等可执行文件和对应的dll
           3.开发版本(dev):提供编译好的库文件、dll和头文件
           4. 源码:直接下载ffmpeg的所有源码。
     我们这里要用到的是shared版本和dev版本,在shared版本下有我们需要用到的.dll动态链接库文件,dev版本中有.lib库文件(lib文件夹)和.h头文件(include文件夹)。下载以后,进行一下配置步骤:
     ①将include文件夹下的lib开头的文件夹libavcodec.......等拷贝到你的工程文件夹下,然后在文件前面include它们就OK了,因为ffmpeg是用C语言写的,所以要加上extern“C”这一句:
extern "C" {   #include <libavutil/opt.h>   #include <libavcodec/avcodec.h>   #include <libavutil/channel_layout.h>   #include <libavutil/common.h>   #include <libavutil/imgutils.h>   #include <libavutil/mathematics.h>   #include <libavutil/samplefmt.h>   #include <libswscale/swscale.h>};
     ②将lib文件夹下的.lib文件拷贝到工程文件夹下,在文件前面加上:
#pragma comment(lib, "avcodec.lib")#pragma comment(lib, "avformat.lib")#pragma comment(lib, "avutil.lib")#pragma comment(lib, "swscale.lib")</span>
     ③将.dll文件拷贝到工程中debug和release文件夹中。OK,配置完成。接下来就可以使用ffmpeg了。头文件还需要用到这些:
#include "stdint.h"#include <stdio.h>#include <stdlib.h>#include <vector>#include<iostream></span>
     二、我这边实现的是将视频处理算法产生的结果帧压缩成H.264的视频,网上很多代码要不就是编解码都有的程序,要不就是对几张图片进行编码压缩的程序,资料并不多,调试的过程中我遭遇了很多的问题。感谢EightDegreeymsdu2004两位的程序,给了我很大的启发,ffmpeg不断在更新,我们在前辈的代码上做一些修改就可以得到我们想要的结果了。EightDegree的代码是将5幅1280*720大小的图片进行编码,并且写到文件中,ymsdu2004更牛逼,深入分析了ffmpeg源代码中关于编码延时的过程。大家编代码的时候可以参考他们的博客:http://blog.csdn.net/eightdegree/article/details/7425635http://blog.csdn.net/ymsdu2004/article/details/8565822
其实在官方下载的ffmpeg里面有示例代码,个人觉得这些代码有点坑,有点误导人:
                      
     下面用注释的代码说一下ffmpeg编码器使用的流程:

     ①定义编码器,设置编码器参数,查找编码器,申请内存

      关于几个结构体的形式参考:http://blog.csdn.net/leixiaohua1020/article/details/14214577

 AVCodec *codec;//编码器 AVCodecContext *c; //描述编解码器上下文的数据结构 AVFrame *picture; //图像数据存储结构体 AVPacket pkt;//编码暂存容器 avcodec_register_all();//注册各种编解码器 //av_register_all();//新版本不需要这一句了 //查找编码器 codec = avcodec_find_encoder(AV_CODEC_ID_H264);//h.264编码器查找 if (!codec){  fprintf(stderr,"codec not found\n");  exit(1); } //设置编码器参数 c = avcodec_alloc_context3(codec); c->bit_rate = 400000;//比特率,最低默认20000,设置太低编码视频效果差,必须为2的倍数 c->width = video_width; c->height = video_height; c->time_base.den = video_fps;//帧率=den/num c->time_base.num = 1; c->gop_size = 10;//每10帧插入一个I帧 c->max_b_frames = 1;//非B帧之间的最大B帧数 c->pix_fmt = AV_PIX_FMT_YUV420P;//设置像素格式  if (avcodec_open2(c, codec, NULL)<0){  fprintf(stderr,"could not open codec\n");  exit(1); }//av_opt_set(c->priv_data, "preset", "slow", 0); av_opt_set(c->priv_data, "preset", "super fast", 0); av_opt_set(c->priv_data, "tune", "zero latency", 0);//这两句实现实时编码模式,也可以用slow模式,slow///模式延迟帧更多,还会导致你的pts不对,注意这个设置不要放在上一个if以前,否则会打不开编码器<span style="font-family: KaiTi_GB2312;">could not o//pen codec!</span> //初始化picture picture = avcodec_alloc_frame(); if (!picture){  fprintf(stderr,"could not allocate video frame\n");  exit(1); } picture->format = c->pix_fmt; picture->width = c->width; picture->height = c->height; //开辟内存 ret = av_image_alloc(picture->data,picture->linesize,c->width,c->height,c->pix_fmt,32); if (ret<0){  fprintf(stderr,"could not alloc raw picture buffer\n");  exit(1); }

    ②打开要编码的文件,转换图像格式,编码

fp_264 = fopen(fileOutName, "ab+");//执行fopen以后,会自动生成名字为fileOutName的文件,这个名字先自己///定义好uint8_t * pYuvBuff = NULL;// 开始循环获取每一帧,并编码int yuvimg_size = video_width * video_height;pYuvBuff = (uint8_t *) malloc((yuvimg_size * 3) / 2);uint8_t * outbuf = (uint8_t *) malloc((yuvimg_size * 3) / 2);int frame_number = 0;int j = 0; picture->pts = 0;//和下面picture->pts=frame_number这一句配合,使得生成的视频帧率过快//看了我这里的写法,希望对你的代码有帮助,pRGBDatas是我的opencv处理以后得到的图像数据while (frame_number < frame_count)//每次压缩frame_count帧{       for ( i = 0; i < video_fps; i++)       {      av_init_packet(&pkt);//初始化暂存容器      pkt.data = outbuf;      pkt.size = yuvimg_size;      frame_number = j * video_fps + i;      if (frame_number >= frame_count) break;      read_yuv_image(picture, video_width, video_height, pRGBDatas[frame_number],pYuvBuff);//图像RGB数据向YUV数据的转换,函数代码在下面              fflush(stdout);                    /* encode the image */      ret = avcodec_encode_video2(c, &pkt, picture, &go_output);      picture->pts=frame_number;      if (ret < 0) {         fprintf(stderr, "error encoding frame\n"); exit(1);    }      if (go_output) {         fwrite(pkt.data, 1, pkt.size, fp_264); av_free_packet(&pkt);    }}j++;             //官方参考代码里面的延迟帧处理,后来发现实时编码模式中不需要这一段/*for (i= 1; go_output; i++) {fflush(stdout);                                ret = avcodec_encode_video2(c, &pkt, NULL, &go_output);picture->pts++;if (ret < 0) {fprintf(stderr, "error encoding frame\n");exit(1);}if (go_output){        fwrite(pkt.data, 1, pkt.size, fp_264);av_free_packet(&pkt);}}*/}read_yuv_image函数完成图像RGB数据向YUV数据的转换void read_yuv_image(AVFrame *_picture, int video_width, int video_height, unsigned char * pRGBData,uint8_t * yuv_buff){AVFrame *m_pRGBFrame = new AVFrame();//仔细看清楚转换的流程,然后大家去查一下sws_scale的用法就会明白这个转换的过程///了,这里我就不多说了avpicture_fill((AVPicture*)m_pRGBFrame, (uint8_t*)pRGBData, AV_PIX_FMT_RGB24, video_width, video_height);      //初始化SwsContext  SwsContext * scxt = sws_getContext(video_width,video_height,AV_PIX_FMT_BGR24, \video_width,video_height,AV_PIX_FMT_YUV420P, \SWS_POINT,NULL,NULL,NULL); int yuv_size = video_width * video_height * 3 / 2;//yuv_buff = new uint8_t[yuv_size];avpicture_fill((AVPicture *) _picture, (uint8_t *) yuv_buff, AV_PIX_FMT_YUV420P, video_width, video_height);         //参考代码中的图像翻转,这里不需要//m_pRGBFrame->data[0]  += m_pRGBFrame->linesize[0] * (video_height - 1);//m_pRGBFrame->linesize[0] *= -1;                   //m_pRGBFrame->data[1]  += m_pRGBFrame->linesize[1] * (video_height / 2 - 1);//m_pRGBFrame->linesize[1] *= -1;//m_pRGBFrame->data[2]  += m_pRGBFrame->linesize[2] * (video_height / 2 - 1);//m_pRGBFrame->linesize[2] *= -1;sws_scale(scxt, m_pRGBFrame->data, m_pRGBFrame->linesize, 0, video_height, _picture->data, _picture->linesize);        //什么时候用delete什么时候用free要注意,小心一直在用的空间被free了,很容易出错sws_freeContext(scxt);//av_free(m_pRGBFrame);if(m_pRGBFrame){        delete m_pRGBFrame;}return;}

      ③关闭文件,释放内存

      使用ffmpeg的时候很容易不小心使得内存泄露,所以要仔细检查哪里该用delete,哪里该free,只有在该内存空间用完时才能free

if(pYuvBuff){free(pYuvBuff);}        fclose(fp_264);av_free(picture);avcodec_close(c);av_free(c);free(outbuf);





0 0
原创粉丝点击