利用VS2015与FFmpeg实现视频解编码的基本过程

利用VS2015与FFmpeg实现视频解编码的基本过程

       最近接到一个任务,是有关于视频的网络传输的技术应用,因为在传输的过程中用到了视频的编解码技术,所以近期对其进行了一系列学习。现将最近的学习成果即:利用VS2015与FFmpeg实现视频解编码的使用方法、使用过程分享如下。如果大家在观看当中遇到什么问题,也欢迎大家相互交流。

1. 前言

       FFmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。采用LGPL或GPL许可证。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/视频编解码库libavcodec,为了保证高可移植性和编解码质量,libavcodec里很多code都是从头开发的。(PS:来自百度百科。)
       因为我所用是VS2015,所以下面介绍的是FFmpeg编解码在Windows下的API使用操作。

2. VS2015配置FFmpeg库

2.1. 下载FFmpeg

       1)下载FFmpeg的软件包,进入FFmpeg官网 http://ffmpeg.org/download.html 进入下载界面并下载Windows环境下的win64下的Dev与Shared。操作如下:
利用VS2015与FFmpeg实现视频解编码的基本过程_第1张图片
       2)进入下载界面,点击Windows builds,下载win64的win64下的Dev与Shared
利用VS2015与FFmpeg实现视频解编码的基本过程_第2张图片
       下载完成后,对压缩包进行解压。得到如下图文件:
在这里插入图片描述
       3)取出ffmpeg-20190625-dd662bb-win64-dev文件夹中的include与lib文件夹;取出ffmpeg-20190625-dd662bb-win64-shared文件夹中的bin文件夹;将其放置到一个新文件夹中(此处我们暂定为advise_use)。如下图:
利用VS2015与FFmpeg实现视频解编码的基本过程_第3张图片
       接下来,我们对VS使用FFmpeg库进行相关配置。

2.2. 配置FFmpeg

       控制台程序,MFC都可以依照如下过程配置。亲测有效。
       1)打开控制台程序,新建工程并建立一cpp,添加代码如下:

#include

//FFmpeg的头文件
extern "C"
{
#include 
#include 
#include
#include 
#include 
#include 
#include 
#include 
}
using namespace std;
int main()
{

	return 0;
}

       FFmpeg头文件见上述代码。
       2)找到视图中的属性管理器,并将其打开。在Release|x64中添加新建项目属性表(名字自取),并将其打开。
利用VS2015与FFmpeg实现视频解编码的基本过程_第4张图片

利用VS2015与FFmpeg实现视频解编码的基本过程_第5张图片
       3)将属性页打开后,找到“VC++目录”下的“包含目录”,选择“包含目录”中的“编辑”操作,将我们刚才暂定名为advise_use的新文件夹中的include文件路径添加进去。如下图所示:
利用VS2015与FFmpeg实现视频解编码的基本过程_第6张图片

       4)同理,找到“VC++目录”下的“库目录”,选择“库目录”中的“编辑”操作,将我们刚才暂定名为advise_use的新文件夹中的lib文件路径添加进去。如下图所示:

利用VS2015与FFmpeg实现视频解编码的基本过程_第7张图片

       5)找到“链接器”的“输入”选型,点击“附加依赖项”的“编辑”,将advise_use文件夹的lib文件夹下的".lib"文件名,全部复制进去。如下图所示:

利用VS2015与FFmpeg实现视频解编码的基本过程_第8张图片
       最终点击确定。
       6)在Release|x64下生成程序,此时显示生成成功。如下图所示:

利用VS2015与FFmpeg实现视频解编码的基本过程_第9张图片
       7)但此时配置还需要最后一步,就是将advise_use文件夹的bin文件夹下所有的".dll"文件复制到所建的工程文件中X64文件下的Release文件夹下。如下图所示:
利用VS2015与FFmpeg实现视频解编码的基本过程_第10张图片

       该步完成后,安装和配置FFmpeg库就大功告成了。

3. 视频编码流程

       在进行视频传输时,由于摄像头一般采集回来的为像素数据,而像素数据量非常大,因此我们需要对像素数据进行压缩,也就是进行编码。下面介绍FFmpeg的编码实现步骤。

3.1.视频编码流程图

       FFmpeg的解码过程即编码流程如下图所示:
利用VS2015与FFmpeg实现视频解编码的基本过程_第11张图片

3.2.各步骤程序说明

       下面对上述流程中的各个步骤进行一个简要的程序说明。
1)API注册

/*API注册*/
av_register_all();              //API注册
avcodec_register_all();         //注册编解码器

av_register_all():该函数在FFmpeg中所有的应用程序中最先被调用;
avcodec_register_all():该函数注册了与编解码器有关的组件。
2)查找编码器
        注册操作完成后,首先需要查找自己需要使用的编码器,可以通过 avcodec_find_encoder函数进行查找,在查找时可以通过AVCodecID进行查找。如果希望设置的编码器是H.264,将AVCodecID设置为AV_CODEC_ID_H264即可使用。

  /*查找编码器*/
AVCodec *pCodecH264; 
AVCodecID  mycodeid;                                                  //AVCodecID为:AV_CODEC_ID_H264
pCodecH264 = avcodec_find_encoder(mycodeid);    
if (!pCodecH264)
{
//此处说明未查找到H264编码器的代码

  exit(1);                                   
		     }

3)申请AVcodecContext
       申请到编码器后,需要借助AVCodec创建一个AVcodecContext,然后利用AVcodecContext对编码器进行设置。

	/*申请AVcodecContext*/
	c = NULL;                                
	c = avcodec_alloc_context3(pCodecH264);     
    if(!c)
    {
      //此处说明未申请到AVcodecContext的内容

     	exit(1);                                   
       }
	int    width ;                             //视频宽度  
	int    height;                             //视频高度

	//配置编码器参数
	c->bit_rate = 400000;                       //视频码率400kbit/s
	c->width = width;                           //视频宽度
	c->height = height;                         //视频高度
	c->time_base.den = 25;                      //帧率为25fps
	c->time_base.num = 1;
	c->gop_size = 10;                           //设置GOP大小,该值表示每10帧会插入一个I帧  
	c->max_b_frames = 1;                        //设置B帧最大数,该值表示在两个非B帧之间,所允许插入的B帧的最大帧数  
	c->pix_fmt = AV_PIX_FMT_YUV420P;            //设置像素格式YUV420P  

	av_opt_set(c->priv_data, "preset", "fast", 0);          
	av_opt_set(c->priv_data, "tune", "zerolatency", 0);     

注:
1.av_opt_set(c->priv_data, “preset”, “fast”, 0); 通过该函数可将编码器的编码速度提高;
2.av_opt_set(c->priv_data, “tune”, “zerolatency”, 0); 通过该函数可改变编码器的延时。
4)打开编码器
       设置参数后,通过avcodec_open2打开编码器。

	if (avcodec_open2(c, pCodecH264,NULL)<0)
	{
		//此处说明视频编码器未打开的错误
	
		exit(1);                               
	}

5)申请帧结构
       编码器打开后,需要申请视频帧的存储空间,用于存储每一帧的视频数据。因为我采集的是一个RGB数据,所以需要将RGB数据转换为YUV数据。

/*申请帧结构*/
int nDataLen;           //rgb图像数据区长度 
uint8_t *yuv_buff;      
uint8_t *rgb_buff;    
AVPacket pkt;         
AVFrame *m_pRGBFrame;   //RGB帧对象  
AVFrame *m_pYUVFrame;   //YUV帧对象
SwsContext *scxt;       //图像格式转换对象
	
nDataLen = height*width * 3;//计算图像rgb数据区长度  
yuv_buff = new uint8_t[nDataLen / 2];     //初始化数据区,为YUV图像帧准备填充缓存  
rgb_buff = new uint8_t[nDataLen];          //初始化数据区,为RGB图像帧准备填充缓存  
scxt = sws_getContext(c->width, c->height, AV_PIX_FMT_BGR24, c->width, c->height, AV_PIX_FMT_YUV420P, SWS_POINT, NULL, NULL, NULL);
av_init_packet(&pkt);                                                                                   //初始化压缩数据存储包             
memcpy(rgb_buff, data, nDataLen);                                                            //拷贝采集图像数据到rgb图像帧缓存中准备处理 
	                                                                                                                    // data是摄像头采集到的数据
avpicture_fill((AVPicture*)m_pRGBFrame, (uint8_t*)rgb_buff, AV_PIX_FMT_RGB24, width, height);//将rgb_buff填充到m_pRGBFrame  原始数据							
avpicture_fill((AVPicture*)m_pYUVFrame, (uint8_t*)yuv_buff, AV_PIX_FMT_YUV420P, width, height);//将yuv_buff填充到m_pYUVFrame  
sws_scale(scxt, m_pRGBFrame->data, m_pRGBFrame->linesize, 0, c->height, m_pYUVFrame->data, m_pYUVFrame->linesize);  // 将RGB转化为YUV 

6)帧编码
       m_pYUVFrame信息存储空间申请完成,将数据写至YUV数据中,写入完成后,我们需要对其进行相应的数据编码,利用avcodec_encode_video2函数,生成AVPacket,即代码中的pkt。此时即可得到编码数据,即H.264格式的视频数据。此时只需要将编码的数据保存下来:fwrite(pkt.data, 1, pkt.size, file);或者将pkt进行传输。

/*帧编码*/
int myoutputlen = 0;
int returnvalue = avcodec_encode_video2(c, &pkt, m_pYUVFrame, &myoutputlen);
if(returnvalue<0)
{
//此处说明视频编码错误的代码

exit(1);
}

7)收尾
       将之前申请的各项资源进行释放。

/*收尾*/
av_free_packet(&pkt);     
delete[]rgb_buff;
delete[]yuv_buff;
delete[]outbuf;
sws_freeContext(scxt);
avcodec_close(c);//关闭编码器  
av_free(c);

注:
1.上述过程只是陈述了利用FFmpeg进行视频编码过程的一个基本流程,具体的代码细节,在使用过程中还要进行具体分析。
2.上述编码之后得到的数据是H.264数据,如果想要对其进一步封装,再进行查询相关的函数即可。

4. 视频解码流程

4.1.视频解码流程图

       FFmpeg的解码过程即编码流程如下图所示:
利用VS2015与FFmpeg实现视频解编码的基本过程_第12张图片

2.各步骤程序说明

       下面对上述流程中的各个步骤进行一个简要的程序说明。
1)API注册

/*API注册*/
av_register_all();              //API注册
avcodec_register_all();         //注册编解码器

av_register_all():该函数在FFmpeg中所有的应用程序中最先被调用;
avcodec_register_all():该函数注册了与编解码器有关的组件。

2)查找编码器
       与编码过程类似,此处不再赘述。

  /*查找编码器*/
AVCodec *pCodecH264; 
AVCodecID  mycodeid;                                                  //AVCodecID为:AV_CODEC_ID_H264
pCodecH264 = avcodec_find_encoder(mycodeid);    
if (!pCodecH264)
{
//此处说明未查找到H264编码器的代码

  exit(1);                                   
		     }

3)申请AVcodecContext
       与编码过程类似,此处不再赘述。

/*申请AVcodecContext*/
AVCodecContext *c;		   
c = NULL;                                
c = avcodec_alloc_context3(pCodecH264);     
if(!c)
  {
     //此处说明未申请到AVcodecContext的内容

     exit(1);                                   
       }

4)打开编码器
       设置参数后,通过avcodec_open2打开编码器。

	if (avcodec_open2(c, pCodecH264,NULL)<0)
	{
		//此处说明视频编码器未打开的错误
	
		exit(1);                               
	}

5)帧解码
注:我所获取的数据为H.264视频数据。
1.首先利用av_parser_parse2函数获得AVPacket数据;
2.通过 avcodec_decode_video2函数对获得的AVPacket数据进行解码,获得YUV数据。

AVCodecParserContext *avParserContext;
uint8_t *yuv_buff;          //yuv图像数据区
int nOutSize=0;			    //用以记录帧数据长度
uint8_t *filebuf;		    //读入文件缓存
int haveread=0;			    //用以记录已读buf长度
int nLength=av_parser_parse2(avParserContext, c, &yuv_buff,&nOutSize, filebuf + haveread, bufferSize_Sum, 0, 0, 0);
bufferSize_Sum -= nLength;                     
Video_decode.haveread += nLength;
AVPacket avpkt;			              //AVPacket数据
avpkt.size =nOutSize;              //将帧数据放进AVPacket包中
avpkt.data = Video_decode.yuv_buff;    //长度为 bufferSize_Sum
AVFrame *m_pYUVFrame;
int piclen;	
int decodelen = avcodec_decode_video2(c, m_pYUVFrame, &piclen, &avpkt);//解码

注:获取到一帧数据放置于filebuf,长度为 bufferSize_Sum;
6)帧存储/帧处理
       解码之后,数据将被存置m_pYUVFrame中,接下来可以对m_pYUVFrame中的数据进行操作。例如将数据存储到文件中,或者转换为硬件输出的buffer支持的格式,例如RGB等。
7)收尾
       将之前申请的各项资源进行释放。

	av_free(m_pYUVFrame);//释放帧资源
	avcodec_close(c);//关闭解码器
	av_free(c);

注:
1.上述过程只是陈述了利用FFmpeg进行视频解码H.264文件过程的一个基本流程,具体的代码细节,在使用过程中还要进行具体分析。

5. 参考文献

[1] 刘歧、赵文杰.FFmpeg从入门到精通[M].北京:机械工业出版社,2018.03

你可能感兴趣的:(视频编解码)