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群1038388075,766718184,或者QQ:350197870
视频下载地址:http://www.chungen90.com/?news_3/
Demo下载地址: http://www.chungen90.com/?news_2
ffmpeg 视频教程 添加水印附源码的更多相关文章
- 为SRS流媒体服务器添加HLS加密功能(附源码)
为SRS流媒体服务器添加HLS加密功能(附源码) 之前测试使用过nginx的HLS加密功能,会使用到一个叫做nginx-rtmp-module的插件,但此插件很久不更新了,网上搜索到一个中国制造的叫做 ...
- 一步步实现windows版ijkplayer系列文章之七——终结篇(附源码)
一步步实现windows版ijkplayer系列文章之一--Windows10平台编译ffmpeg 4.0.2,生成ffplay 一步步实现windows版ijkplayer系列文章之二--Ijkpl ...
- 在网站开发中很有用的8个 jQuery 效果【附源码】
jQuery 作为最优秀 JavaScript 库之一,改变了很多人编写 JavaScript 的方式.它简化了 HTML 文档遍历,事件处理,动画和 Ajax 交互,而且有成千上万的成熟 jQuer ...
- Web 开发中很实用的10个效果【附源码下载】
在工作中,我们可能会用到各种交互效果.而这些效果在平常翻看文章的时候碰到很多,但是一时半会又想不起来在哪,所以养成知识整理的习惯是很有必要的.这篇文章给大家推荐10个在 Web 开发中很有用的效果,记 ...
- MVC系列——MVC源码学习:打造自己的MVC框架(二:附源码)
前言:上篇介绍了下 MVC5 的核心原理,整篇文章比较偏理论,所以相对比较枯燥.今天就来根据上篇的理论一步一步进行实践,通过自己写的一个简易MVC框架逐步理解,相信通过这一篇的实践,你会对MVC有一个 ...
- C#进阶系列——一步一步封装自己的HtmlHelper组件:BootstrapHelper(三:附源码)
前言:之前的两篇封装了一些基础的表单组件,这篇继续来封装几个基于bootstrap的其他组件.和上篇不同的是,这篇的有几个组件需要某些js文件的支持. 本文原创地址:http://www.cnblog ...
- 轻量级通信引擎StriveEngine —— C/S通信demo(2) —— 使用二进制协议 (附源码)
在网络上,交互的双方基于TCP或UDP进行通信,通信协议的格式通常分为两类:文本消息.二进制消息. 文本协议相对简单,通常使用一个特殊的标记符作为一个消息的结束. 二进制协议,通常是由消息头(Head ...
- jquery自定义插件结合baiduTemplate.js实现异步刷新(附源码)
上一篇记录了BaiduTemplate模板引擎使用示例附源码,在此基础上对使用方法进行了封装 自定义插件jajaxrefresh.js 代码如下: //闭包限定命名空间 (function ($) { ...
- 精选9个值得学习的 HTML5 效果【附源码】
这里精选了一组很酷的 HTML5 效果.HTML5 是现 Web 开发领域的热点, 拥有很多让人期待已久的新特性,特别是在移动端,Web 开发人员可以借助 HTML5 强大功能轻松制作各种交互性强.效 ...
随机推荐
- c3p0-config.xml模板详解
c3p0-config.xml模板详解 <c3p0-config> <default-config> <!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数.De ...
- bootstrap 中dropmenu不起作用
今天在使用bootstrap发现dropmenu一直不起作用,代码是从官网拷贝过来. 网上查找可以用的页面进行一点点的去除分析,发现竟然是顺序反了导致的. 在使用dropmenu时需要引入jquery ...
- 【Luogu】P3565HOT-Hotels(树形DP)
题目链接 水了半个月之后Fd终于开始做题啦! 然后成功的发现自己什么都不会了 树形DP,既然是三个点两两距离相等那一定得有个中心点吧,枚举那个中心点,然后暴力DFS,根据乘法原理算. 乘法原理就是我一 ...
- 本博客由CSDN迁移而来,以前的博文可能显示不正常
如题,原博客地址 http://blog.csdn.net/vicjiao 或点击右侧友链
- docker (centOS 7) 使用笔记2 - 使用nfs作为volume
本次测试的服务器2台,服务器#1(centos7)最为docker容器所在的host,服务器#2(centos6)提供NFS服务 1. #2上配置NFS服务 (1) 安装nfs软件包 yum -y i ...
- docker 镜像阿里加速器
1.登录 https://cr.console.aliyun.com/#/imageList 2.点击加速器tab,获取自己的加速器地址,然后执行黑框内的命令. .sudo mkdir -p /etc ...
- hdu 2262 高斯消元求期望
Where is the canteen Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Ot ...
- django相关字段解释(slug)
1.slug:用于生成一个有意义(valid, meaninful)URL 参考(http://stackoverflow.com/questions/427102/what-is-a-slug-in ...
- vue 之循环添加不同class
在vue中按条件为class动态添加直接使用:class="[{ active: isActive }, errorClass]"之类的表达式就可以 但是如果我们要为一个循环列表按 ...
- k8s入门简介
1.docker的三种编排工具 Docker的第一类编排工具: a.docker compose(docker原生):只能对一个主机上的容器进行编排,无法编排多个主机上的容器; b.docker sw ...