现在的位置: 首页 > 综合 > 正文

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

2018年04月13日 ⁄ 综合 ⁄ 共 6044字 ⁄ 字号 评论关闭

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

Jun_L

http://blog.csdn.net/jingjun1822

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

     一、ffmpeg+vs2008环境配置
     网上有诸多编译生成库文件.lib和动态库.dll的的博客,那些主要是针对前期版本的ffmpeg。感谢ying1feng23同学的博客对我的启发,我从他的博客上了解到了快捷的办法:
     首先从ffmpeg官网中下载ffmpeg数据包:
     下载的时候要注意自己的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);


抱歉!评论已关闭.