ffmpeg 视频教程 添加水印附源码

本文主要讲述如何利用Ffmpeg向视频文件 添加水印这一功能,文中最后会给出源代码下载地址以及视频

下载地址,视频除了讲述添加水印的基本原理以及代码实现,还提到了要注意的一些地方,因为直接运行

demo源码可能会有问题。利用Ffmpeg向视频文件添加水印的基本原理是将视频文件的视频包解码成一帧帧

“Frame”,通过ffmpeg  Filter(overlay)实现待添加水印与“Frame”的叠加,最后将叠加后的视频帧进行编码

并将编码后的数据写到输出文件里。基本的流程如下图所示:

图1 ffmpeg 添加水印基本流程

了解了添加水印的基本原理及流程以后,下面给出代码部分:

一  打开输入源:

与以往的打开输入源的方法不同,这里的多了一个入参,根据输入的参数不同既可打开视频文件,可以打开水印图片。

int OpenInput(char *fileName,int inputIndex)
{
	context[inputIndex] = avformat_alloc_context();
	context[inputIndex]->interrupt_callback.callback = interrupt_cb;
	AVDictionary *format_opts =  nullptr;

	int ret = avformat_open_input(&context[inputIndex], fileName, nullptr, &format_opts);
	if(ret < 0)
	{
		return  ret;
	}
	ret = avformat_find_stream_info(context[inputIndex],nullptr);
	av_dump_format(context[inputIndex], 0, fileName, 0);
	if(ret >= 0)
	{
		std::cout <<"open input stream successfully" << endl;
	}
	return ret;
}

 二. 读取视频包(图片帧)

shared_ptr<AVPacket> ReadPacketFromSource(int inputIndex)
{
	 std::shared_ptr<AVPacket> packet(static_cast<AVPacket*>(av_malloc(sizeof(AVPacket))), [&](AVPacket *p) { av_packet_free(&p); av_freep(&p); });
	av_init_packet(packet.get());
	int ret = av_read_frame(context[inputIndex], packet.get());
	if(ret >= 0)
	{
		return packet;
	}
	else
	{
		return nullptr;
	}
}

  三. 创建输出上下文

int OpenOutput(char *fileName,int inputIndex)
{
	int ret = 0;
	ret  = avformat_alloc_output_context2(&outputContext, nullptr, "mpegts", fileName);
	if(ret < 0)
	{
		goto Error;
	}
	ret = avio_open2(&outputContext->pb, fileName, AVIO_FLAG_READ_WRITE,nullptr, nullptr);
	if(ret < 0)
	{
		goto Error;
	}

	for(int i = 0; i < context[inputIndex]->nb_streams; i++)
	{
		AVStream * stream = avformat_new_stream(outputContext, outPutEncContext->codec);
		stream->codec = outPutEncContext;
		if(ret < 0)
		{
			goto Error;
		}
	}
	av_dump_format(outputContext, 0, fileName, 1);
	ret = avformat_write_header(outputContext, nullptr);
	if(ret < 0)
	{
		goto Error;
	}
	if(ret >= 0)
		cout <<"open output stream successfully" << endl;
	return ret ;
Error:
	if(outputContext)
	{
		avformat_close_input(&outputContext);
	}
	return ret ;
}

  四. 初始化编解码code

int InitEncoderCodec( int iWidth, int iHeight,int inputIndex)
{
	AVCodec *  pH264Codec = avcodec_find_encoder(AV_CODEC_ID_H264);
	if(NULL == pH264Codec)
	{
		printf("%s", "avcodec_find_encoder failed");
		return  -1;
	}
	outPutEncContext = avcodec_alloc_context3(pH264Codec);
	outPutEncContext->gop_size = 30;
	outPutEncContext->has_b_frames = 0;
	outPutEncContext->max_b_frames = 0;
	outPutEncContext->codec_id = pH264Codec->id;
	outPutEncContext->time_base.num =context[inputIndex]->streams[0]->codec->time_base.num;
	outPutEncContext->time_base.den = context[inputIndex]->streams[0]->codec->time_base.den;
	outPutEncContext->pix_fmt            = *pH264Codec->pix_fmts;
	outPutEncContext->width              =  iWidth;
	outPutEncContext->height             = iHeight;

	outPutEncContext->me_subpel_quality = 0;
	outPutEncContext->refs = 1;
	outPutEncContext->scenechange_threshold = 0;
	outPutEncContext->trellis = 0;
	AVDictionary *options = nullptr;
	outPutEncContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;

	int ret = avcodec_open2(outPutEncContext, pH264Codec, &options);
	if (ret < 0)
	{
		printf("%s", "open codec failed");
		return  ret;
	}
	return 1;
}

int InitDecodeCodec(AVCodecID codecId,int inputIndex)
{
	auto codec = avcodec_find_decoder(codecId);
	if(!codec)
	{
		return -1;
	}
	decoderContext[inputIndex] = context[inputIndex]->streams[0]->codec;
	if (!decoderContext) {
		fprintf(stderr, "Could not allocate video codec context\n");
		exit(1);
	}

	int ret = avcodec_open2(decoderContext[inputIndex], codec, NULL);
	return ret;

}

五. 初始化Filter

int InitInputFilter(AVFilterInOut *input,const char *filterName,  int inputIndex)
{
	char args[512];
	memset(args,0, sizeof(args));
	AVFilterContext *padFilterContext = input->filter_ctx;

	 auto filter = avfilter_get_by_name("buffer");
	auto codecContext = context[inputIndex]->streams[0]->codec;

	sprintf_s(args, sizeof(args),
		"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
		codecContext->width, codecContext->height, codecContext->pix_fmt,
		codecContext->time_base.num, codecContext->time_base.den /codecContext->ticks_per_frame,
		codecContext->sample_aspect_ratio.num, codecContext->sample_aspect_ratio.den);

	int ret = avfilter_graph_create_filter(&inputFilterContext[inputIndex],filter,filterName, args,
		NULL, filter_graph);
	 if(ret < 0)  return ret;
	 ret = avfilter_link(inputFilterContext[inputIndex],0,padFilterContext,input->pad_idx);
	 return ret;
}

int InitOutputFilter(AVFilterInOut *output,const char *filterName)
{
	AVFilterContext *padFilterContext = output->filter_ctx;
	 auto filter = avfilter_get_by_name("buffersink");

	int ret = avfilter_graph_create_filter(&outputFilterContext,filter,filterName, NULL,
		NULL, filter_graph);
	 if(ret < 0)  return ret;
	 ret = avfilter_link(padFilterContext,output->pad_idx,outputFilterContext,0);

	 return ret;
}

 六. Demo

int _tmain(int argc, _TCHAR* argv[])
{
	//string fileInput = "D:\\test11.ts";
	string fileInput[2];
	fileInput[0]= "F:\\test.ts";
	fileInput[1]= "F:\\test1.jpg";
	string fileOutput = "F:\\codeoutput.ts";
	std::thread decodeTask;
	Init();
	int ret = 0;
	for(int i = 0; i < 2;i++)
	{
		if(OpenInput((char *)fileInput[i].c_str(),i) < 0)
		{
			cout << "Open file Input 0 failed!" << endl;
			this_thread::sleep_for(chrono::seconds(10));
			return 0;
		}

		ret = InitDecodeCodec(context[i]->streams[0]->codec->codec_id,i);
		if(ret <0)
		{
			cout << "InitDecodeCodec failed!" << endl;
			this_thread::sleep_for(chrono::seconds(10));
			return 0;
		}
	}

	ret = InitEncoderCodec(decoderContext[0]->width,decoderContext[0]->height,0);
	if(ret < 0)
	{
		cout << "open eccoder failed ret is " << ret<<endl;
		cout << "InitEncoderCodec failed!" << endl;
		this_thread::sleep_for(chrono::seconds(10));
		return 0;
	}

	//ret = InitFilter(outPutEncContext);
	if(OpenOutput((char *)fileOutput.c_str(),0) < 0)
	{
		cout << "Open file Output failed!" << endl;
		this_thread::sleep_for(chrono::seconds(10));
		return 0;
	}

	AVFrame *pSrcFrame[2];
	pSrcFrame[0] = av_frame_alloc();
	pSrcFrame[1] = av_frame_alloc();

	AVFrame *inputFrame[2];
	inputFrame[0] = av_frame_alloc();
	inputFrame[1] = av_frame_alloc();
	auto  filterFrame = av_frame_alloc();
	int got_output = 0;
	int64_t  timeRecord = 0;
	int64_t  firstPacketTime = 0;
	int64_t outLastTime = av_gettime();
	int64_t inLastTime = av_gettime();
	int64_t videoCount = 0;

	filter_graph = avfilter_graph_alloc();
	if(!filter_graph)
	{
		cout <<"graph alloc failed"<<endl;
		goto End;
	}
	avfilter_graph_parse2(filter_graph, filter_descr, &inputs, &outputs);
	InitInputFilter(inputs,"MainFrame",0);
	InitInputFilter(inputs->next,"OverlayFrame",1);
	InitOutputFilter(outputs,"output");

	FreeInout();

    ret = avfilter_graph_config(filter_graph, NULL);
	if(ret < 0)
	{
          goto End;
	}
	decodeTask.swap(thread([&]{
		bool ret = true;
		while(ret)
		{
			auto packet = ReadPacketFromSource(1);
			ret = DecodeVideo(packet.get(),pSrcFrame[1],1);
			if(ret) break;
		}
	}));
	decodeTask.join();

	while(true)
	{
		outLastTime = av_gettime();
		auto packet = ReadPacketFromSource(0);
		if(packet)
		{
			if(DecodeVideo(packet.get(),pSrcFrame[0],0))
			{
				av_frame_ref( inputFrame[0],pSrcFrame[0]);
				if (av_buffersrc_add_frame_flags(inputFilterContext[0], inputFrame[0], AV_BUFFERSRC_FLAG_PUSH) >= 0)
				{
					pSrcFrame[1]->pts = pSrcFrame[0]->pts;
					//av_frame_ref( inputFrame[1],pSrcFrame[1]);
					if(av_buffersrc_add_frame_flags(inputFilterContext[1],pSrcFrame[1], AV_BUFFERSRC_FLAG_PUSH) >= 0)
					{
						ret = av_buffersink_get_frame_flags(outputFilterContext, filterFrame,AV_BUFFERSINK_FLAG_NO_REQUEST);

						//this_thread::sleep_for(chrono::milliseconds(10));
						if ( ret >= 0)
						{
							std::shared_ptr<AVPacket> pTmpPkt(static_cast<AVPacket*>(av_malloc(sizeof(AVPacket))), [&](AVPacket *p) { av_packet_free(&p); av_freep(&p); });
							av_init_packet(pTmpPkt.get());
							pTmpPkt->data = NULL;
							pTmpPkt->size = 0;
							ret = avcodec_encode_video2(outPutEncContext, pTmpPkt.get(), filterFrame, &got_output);
							if (ret >= 0 && got_output)
							{
								int ret = av_write_frame(outputContext, pTmpPkt.get());
							}
							//this_thread::sleep_for(chrono::milliseconds(10));
						}
						av_frame_unref(filterFrame);
					}
				}
			}
		}
		else break;
	}
	CloseInput(0);
	CloseOutput();
	std::cout <<"Transcode file end!" << endl;
	End:
	this_thread::sleep_for(chrono::hours(10));
	return 0;
}

  如有什么问题,欢迎加入QQ群交流,群号:127903734。 

  源码地址:http://pan.baidu.com/s/1o8Lkozw

  视频地址:http://pan.baidu.com/s/1jH4dYN8

时间: 2024-11-04 21:34:31

ffmpeg 视频教程 添加水印附源码的相关文章

史上最简单的Hibernate4视频教程(附源码和笔记)

Hibernate4是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库.    Hibernate可以应用在任何使用JDBC的场合,既可以在Java的客户端程序使用, 也可以在Servlet/JSP的Web应用中使用,最具革命意义的是,Hibernate4可以在应用EJB的J2EE架构中取代CMP,完成数据持久化的重任.  由于hibernate4的这些优点,使得hibernate4在业界获得广泛的应用,成为持

使用aforg.net 录制摄像头 附源码

这一篇在上一篇 使用aforg.net 捕获摄像头 的基础上稍加修改 增加录制功能 录制功能使用AForge.Video.FFMPEG 需要添加对 AForge.Video.FFMPEG.dll的引用 并且拷贝AForge.NET\Framework\Externals\ffmpeg\bin路径下的全部dll到Debug目录下 直接上代码了 对上一篇的代码稍有修改 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 2

一组网页边栏过渡动画,创意无限!【附源码下载】

今天我们想与大家分享另一套过渡效果.这一次,我们将探讨如何实现侧边栏的过渡动画,就像我们已经在多级推出菜单中使用的.我们的想法是,以细微的 过渡动画显示一些隐藏的侧边栏,其余的内容也是.通常侧边栏滑入,把其他内容推到一边.这个可过程中可以加入很多微妙而奇特的效果,而今天这篇文章能够给 你一些启示. 温馨提示:为保证最佳的效果,请在 IE10+.Chrome.Firefox 和 Safari 等现代浏览器中浏览. 立即下载      在线演示 因为我们希望能够在一个页面上展现所有的效果,因此我们示

创意无限!一组网页边栏过渡动画【附源码下载】

今天我们想与大家分享另一套过渡效果.这一次,我们将探讨如何实现侧边栏的过渡动画,就像我们已经在多级推出菜单中使用的.我们的想法是,以细微的过渡动画显示一些隐藏的侧边栏,其余的内容也是.通常侧边栏滑入,把其他内容推到一边.这个可过程中可以加入很多微妙而奇特的效果,而今天这篇文章能够给你一些启示. 温馨提示:为保证最佳的效果,请在 IE10+.Chrome.Firefox 和 Safari 等现代浏览器中浏览. 立即下载      在线演示 因为我们希望能够在一个页面上展现所有的效果,因此我们示例的

C#版无人驾驶汽车(附源码)

一,简单问题复杂化: 100公里/1小时的速度,在日常生活中是比较常见的速度,把它转换为其它单位: 100公里/1小时 ≈ 28米/1秒 100公里/1小时 ≈ 2800厘米/秒 如果想要无人驾驶汽车达到厘米级的位移监测.探测器扫描路况时,每秒上传2800次数据给PC机.若一辆汽车有10个探测器,就意味着每秒的并发量为2.8W次/秒. 2.8W次/秒的并发量,在网站上肯定会采用分布式,缓存,读写分离,集群技术,关键还有这个数据的存储,到底用二维数据库,还是用NOSQL.这些问题是不是让你很头痛?

wpf 模拟3D效果(和手机浏览图片效果相似)(附源码)

原文 wpf 模拟3D效果(和手机浏览图片效果相似)(附源码) pf的3D是一个很有意思的东西,类似于ps的效果,类似于电影动画的效果,因为动画的效果,(对于3D基础的摄像机,光源,之类不介绍,对于依赖属性也不介绍.),个人认为,依赖属性这个东西,有百分之五十是为了3D而存在.(自己写的类似于demo的东西)先上图,无图无真相这是demo的整个效果图,可以用鼠标移动,触摸屏也可以手指滑动,图片会移动,然后移动结束,会有一个回弹的判断. <Window x:Class="_3Dshow.Wi

10个Web前端值得收藏的背景全屏效果展示(附源码)(上)

作为一个前沿的 Web 开发者,对于 HTML5 和 现在流行的3D技术或多或少都有掌握.特别是在移动端大显身手.这篇文章挑选了10个绚丽的背景全景展示效果,希望对你有所帮助. 1.  JS图片背景全屏代码实现物理效果 玩法介绍:可以随意拖动鼠标.按住鼠标左键选中旋转物体.或者按住鼠标滑轮放大或者缩小,有不同的效果,赶紧来体验一下. 源码下载  /  在线演示 2.  CSS3学习 - 网站背景拉伸平铺jQuery插件 这个插件集成了一些非常好的 JavaScript 库,提供一个方便使用的文本

【网站国际化必备】Asp.Net MVC 集成Paypal(贝宝)快速结账 支付接口 ,附源码demo

开篇先给大家讲段历史故事,博主是湖北襄阳人.襄阳物华天宝,人杰地灵,曾用名襄樊.在2800多年的历史文化中出现了一代名相诸葛亮(卧龙),三国名士庞统(凤雏),魏晋隐士司马徽(水镜先生),唐代大诗人孟浩然(孟襄阳),张继.杜审言,文学家皮日休,北宋著名书画家米芾(米襄阳),“允冠百王”的光武帝刘秀,东方圣人释道安等一大批历史文化名人.小说<三国演义>120回故事中有30多回提到襄阳. 相传诸葛亮的老婆黄月英黄头发黑皮肤,但知识广博.诸葛亮发明木牛流马,就是从黄月英的传授的技巧上发展出来.不仅如此

利用Java针对MySql封装的jdbc框架类 JdbcUtils 完整实现(包含增删改查、JavaBean反射原理,附源码)

最近看老罗的视频,跟着完成了利用Java操作MySql数据库的一个框架类JdbcUtils.java,完成对数据库的增删改查.其中查询这块,包括普通的查询和利用反射完成的查询,主要包括以下几个函数接口: 1.public Connection getConnection()   获得数据库的连接 2.public boolean updateByPreparedStatement(String sql, List<Object>params)throws SQLException  更新数据库