zoukankan      html  css  js  c++  java
  • 利用FFmpge进行视频压缩(从图像到H264视频流)

        对于FFmpeg相信做视频或图像处理这一块的都不会陌生,在网上也能找到非常多相关的代码。但因为版本号不同等原因。往往找到的代码都是须要自行改动才干够用,为此本人希望能尽绵薄之力,将开发包和自行编写的代码都放出来,假设刚開始学习的人想要能够直接执行的代码做參考的话。能够下载我放出的FFmpeg开发包进行配置(配置的教程地址例如以下:点击打开链接)。然后參考我写的编解码代码来进行程序的开发。

        以下贴出的是我自己封装的FFmpeg视频压缩代码。如有更好的建议请告诉我,转载请注明出处。

        首先我们设计一个视频压缩相关的类,定义例如以下

    class Ffmpeg_Encoder
    {
    public:
    	AVFrame *m_pYUVFrame;	//帧对象
    	AVCodec *pCodecH264;	//编码器
    	AVCodecContext *c;		//编码器数据结构对象
    	uint8_t *yuv_buff;      //yuv图像数据区
    	uint8_t *rgb_buff;		//rgb图像数据区
    	SwsContext *scxt;		//图像格式转换对象
    	uint8_t *outbuf;		//编码出来视频数据缓存
    	int outbuf_size;        //编码输出数据去大小
    	int nDataLen;			//rgb图像数据区长度
    	int width;				//输出视频宽度
    	int height;				//输出视频高度
    	AVPacket pkt;			//数据包对象
    	int imgwidthlen;		//图像宽度占用空间
    public:
    	void Ffmpeg_Encoder_Init();//初始化
    	void Ffmpeg_Encoder_Setpara(AVCodecID mycodeid, int vwidth, int vheight);//设置參数,第一个參数为编码器,第二个參数为压缩出来的视频的宽度,第三个视频则为其高度
    	void Ffmpeg_Encoder_Encode(FILE *file, uint8_t *data);//编码并写入数据到文件
    	void Ffmpeg_Encoder_Close();//关闭
    };

        对类中声明的四个函数进行定义

    void Ffmpeg_Encoder::Ffmpeg_Encoder_Init()
    {
    	av_register_all();
    	avcodec_register_all();
    	m_pYUVFrame = new AVFrame[1];//YUV帧数据赋值  
    	c = NULL;//解码器指针对象赋初值
    }

    void Ffmpeg_Encoder::Ffmpeg_Encoder_Setpara(AVCodecID mycodeid, int vwidth, int vheight)
    {
    	pCodecH264 = avcodec_find_encoder(mycodeid);//查找h264编码器
    	if (!pCodecH264)
    	{
    		fprintf(stderr, "h264 codec not found
    ");
    		exit(1);
    	}
    	width = vwidth;
    	height = vheight;
    
    	c = avcodec_alloc_context3(pCodecH264);//函数用于分配一个AVCodecContext并设置默认值。假设失败返回NULL。并可用av_free()进行释放
    	c->bit_rate = 1024*1024; //码率,越高视频质量越好
    	c->width = vwidth;//设置编码视频宽度 
    	c->height = vheight; //设置编码视频高度
    	c->time_base.den = 25;//设置帧率,num为分子和den为分母,假设是1/25则表示25帧/s
    	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;//设置像素格式
    
    	imgwidthlen = c->width * 3;
    	av_opt_set(c->priv_data, "tune", "zerolatency", 0);//设置编码器的延时,解决前面的几十帧不出数据的情况
    
    	if (avcodec_open2(c, pCodecH264, NULL) < 0)//打开编码器
    		return;
    
    	nDataLen = vwidth*vheight * 3;//计算图像rgb数据区长度
    
    	yuv_buff = new uint8_t[nDataLen/2];//初始化数据区,为yuv图像帧准备填充缓存
    	rgb_buff = new uint8_t[nDataLen];//初始化数据区,为rgb图像帧准备填充缓存
    	outbuf_size = 100000;////初始化编码输出数据区
    	outbuf = new uint8_t[outbuf_size];
    
    	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_image_fill_arrays(m_pYUVFrame->data, m_pYUVFrame->linesize, yuv_buff, AV_PIX_FMT_YUV420P, width, height, 1);
    }
    
    
    void Ffmpeg_Encoder::Ffmpeg_Encoder_Encode(FILE *file, uint8_t *data)
    {
    	av_init_packet(&pkt);
    	memcpy(rgb_buff, data, nDataLen);//拷贝图像数据到rgb图像帧缓存中准备处理   
    	sws_scale(scxt, &rgb_buff, &imgwidthlen, 0, c->height, m_pYUVFrame->data, m_pYUVFrame->linesize);// 将RGB转化为YUV  
    	
    	int myoutputlen = 0;
    	int returnvalue = avcodec_encode_video2(c, &pkt, m_pYUVFrame, &myoutputlen);
    	if (returnvalue == 0)
    	{
    		fwrite(pkt.data, 1, pkt.size, file);
    	}
    }
    
    
    void Ffmpeg_Encoder::Ffmpeg_Encoder_Close()
    {
    	delete[]m_pYUVFrame;
    	delete[]rgb_buff;
    	delete[]yuv_buff;
    	delete[]outbuf;
    	sws_freeContext(scxt);
    	avcodec_close(c);//关闭编码器
    	av_free(c);
    }
    
        最后我们仅仅须要在主函数对这几个函数进行调用就能够了。因为本人为了方便直接用OpencCV来打开图像并取得图像的数据区。所以假设希望直接执行本人后面所发的project的话还须要自行去配置OpenCV。只是这个在网上实在是说烂了,随便找找就能配置出来。假设不希望用OpenCV来打开图像的话,本人在代码中也注明了应该改动的位置。可自行想办法得到图像数据区并放入视频压缩函数就可以。
    void main()
    {
    	Ffmpeg_Encoder ffmpegobj;
    	//图象编码
    	FILE *f = NULL;
    	char * filename = "myData.h264";
    	fopen_s(&f, filename, "wb");//打开文件存储编码完毕数据
    
    	IplImage* img = NULL;//OpenCV图像数据结构指针  
    	
    	int picturecount = 1;
    	bool firstencode = true;
    	/**此部分用的是OpenCV读入图像对象并取得图像的数据区,也能够用别的方法获得图像数据区**/
    	img = cvLoadImage("1.png", 1);//打开图像
    	while (picturecount != 60)
    	{
    		if (firstencode)
    		{
    			firstencode = false;
    			ffmpegobj.Ffmpeg_Encoder_Init();//初始化编码器
    			ffmpegobj.Ffmpeg_Encoder_Setpara(AV_CODEC_ID_H264, img->width, img->height);//设置编码器參数
    		}
    		ffmpegobj.Ffmpeg_Encoder_Encode(f, (uchar*)img->imageData);//编码
    		picturecount++;
    	}
    	fclose(f);
    	ffmpegobj.Ffmpeg_Encoder_Close();
    	cvReleaseImage(&img);//释放图像数据结构指针对像所指内容 
    }

    
    

        OK。到此用FFmpeg进行视频压缩的博客就告一段落了,以下是整个project的下载地址:点击打开链接。只是假设想要执行起来配置OpenCV还是须要自行动手的哈哈。

        另外在做项目的过程中在编码这块还遇到一个非常奇怪的问题。就是在编码调用到avcodec_encode_video2特别是project也调用到解码函数的时候,在VS里面点击执行是没有问题的,但一执行release目录以下编译出来的exe文件,程序偶尔会报内存错误,错误的位置在avcodec_encode_video2这个函数里面。

    这个让人百思不得其解。后来想想或者VS在执行的时候兼容性比較好,所以没有这个问题。是故例如以下图对exe文件的兼容性进行设置


        这个问题就攻克了,是故怀疑ffmpeg库的avcodec_encode_video2这个函数所在的dll
    对于windows或者是windows64位系统的兼容性不大好。
      








  • 相关阅读:
    Codeforces Round #324 (Div. 2) D. Dima and Lisa 哥德巴赫猜想
    Codeforces Round #324 (Div. 2) C. Marina and Vasya 贪心
    Codeforces Round #324 (Div. 2) B. Kolya and Tanya 快速幂
    Codeforces Round #324 (Div. 2) A. Olesya and Rodion 水题
    使用spring-loaded实现应用热部署
    maven中properties标签定义变量
    java中的匿名内部类总结
    泛型类型限定和通配符类型限定
    基于ActiveMQ的Topic的数据同步——消费者持久化
    基于ActiveMQ的Topic的数据同步——初步实现
  • 原文地址:https://www.cnblogs.com/gavanwanggw/p/6853826.html
Copyright © 2011-2022 走看看