本文主要讲述如何利用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 视频教程 添加水印附源码的更多相关文章

  1. 为SRS流媒体服务器添加HLS加密功能(附源码)

    为SRS流媒体服务器添加HLS加密功能(附源码) 之前测试使用过nginx的HLS加密功能,会使用到一个叫做nginx-rtmp-module的插件,但此插件很久不更新了,网上搜索到一个中国制造的叫做 ...

  2. 一步步实现windows版ijkplayer系列文章之七——终结篇(附源码)

    一步步实现windows版ijkplayer系列文章之一--Windows10平台编译ffmpeg 4.0.2,生成ffplay 一步步实现windows版ijkplayer系列文章之二--Ijkpl ...

  3. 在网站开发中很有用的8个 jQuery 效果【附源码】

    jQuery 作为最优秀 JavaScript 库之一,改变了很多人编写 JavaScript 的方式.它简化了 HTML 文档遍历,事件处理,动画和 Ajax 交互,而且有成千上万的成熟 jQuer ...

  4. Web 开发中很实用的10个效果【附源码下载】

    在工作中,我们可能会用到各种交互效果.而这些效果在平常翻看文章的时候碰到很多,但是一时半会又想不起来在哪,所以养成知识整理的习惯是很有必要的.这篇文章给大家推荐10个在 Web 开发中很有用的效果,记 ...

  5. MVC系列——MVC源码学习:打造自己的MVC框架(二:附源码)

    前言:上篇介绍了下 MVC5 的核心原理,整篇文章比较偏理论,所以相对比较枯燥.今天就来根据上篇的理论一步一步进行实践,通过自己写的一个简易MVC框架逐步理解,相信通过这一篇的实践,你会对MVC有一个 ...

  6. C#进阶系列——一步一步封装自己的HtmlHelper组件:BootstrapHelper(三:附源码)

    前言:之前的两篇封装了一些基础的表单组件,这篇继续来封装几个基于bootstrap的其他组件.和上篇不同的是,这篇的有几个组件需要某些js文件的支持. 本文原创地址:http://www.cnblog ...

  7. 轻量级通信引擎StriveEngine —— C/S通信demo(2) —— 使用二进制协议 (附源码)

    在网络上,交互的双方基于TCP或UDP进行通信,通信协议的格式通常分为两类:文本消息.二进制消息. 文本协议相对简单,通常使用一个特殊的标记符作为一个消息的结束. 二进制协议,通常是由消息头(Head ...

  8. jquery自定义插件结合baiduTemplate.js实现异步刷新(附源码)

    上一篇记录了BaiduTemplate模板引擎使用示例附源码,在此基础上对使用方法进行了封装 自定义插件jajaxrefresh.js 代码如下: //闭包限定命名空间 (function ($) { ...

  9. 精选9个值得学习的 HTML5 效果【附源码】

    这里精选了一组很酷的 HTML5 效果.HTML5 是现 Web 开发领域的热点, 拥有很多让人期待已久的新特性,特别是在移动端,Web 开发人员可以借助 HTML5 强大功能轻松制作各种交互性强.效 ...

随机推荐

  1. TOJ1698: Balanced Lineup

    Description For the daily milking, Farmer John's N cows (1 ≤ N ≤ 50,000) always line up in the same ...

  2. API生命周期第三阶段:API实施模式,以及结合swagger和项目现状的最佳模式

    这篇博客,主要是宏观介绍一下开发模式,尤其是针对于目前公司前后分离的项目! 一.API实施模式概述 API实施模式,主要是三个,其中API-First又是作为一种指导思想的一种,所以,简单来说事实实施 ...

  3. 《分布式对象存储》作者手把手教你写 GO 语言单元测试!

    第一部分:如何写Go语言单元测试 Go语言内建了单元测试(Unit Test)框架.这是为了从语言层面规范写UT的方式. Go语言的命名规则会将以_test.go结尾的go文件视作单元测试代码. 当我 ...

  4. 如何在c#代码中执行带GO语句的SQL文件

      需要在C#代码中执行一个SQL文件的内容.遇到了两个问题: 1. 因为SQL文件中有"GO"语句,执行时报错"Incorrect syntax near 'GO'.& ...

  5. 飞行员配对方案问题(匈牙利算法+sort)

    洛谷传送门 匈牙利算法+sort 没什么好说的. ——代码 #include <cstdio> #include <cstring> #include <algorith ...

  6. bzoj1023【SHOI2008】cactus仙人掌图

    题意:http://www.lydsy.com/JudgeOnline/problem.php?id=1023 求一棵仙人掌的直径 sol :orz YDC神犇 http://ydcydcy1.blo ...

  7. iOS-Core Data基础

    Core Data基础 Core Data是一个API集合,被设计用来简化数据对象的持久存储. 在此先不普及概念,先通过一个简单的案例使用来感受一下Core Data的精妙之处. 在创建工程的时候勾选 ...

  8. 使用JWT实现Token认证

    为什么使用JWT? 随着技术的发展,分布式web应用的普及,通过session管理用户登录状态成本越来越高,因此慢慢发展成为token的方式做登录身份校验,然后通过token去取redis中的缓存的用 ...

  9. 【BZOJ入门3189】 猜数字(数学,搜索)

    Description 味味最近在玩猜数字的游戏,现在她也希望你来玩一下这个游戏.猜数字游戏的规则是这样的,告诉你一个正整数 n(2<=n<=11),然后味味心中会想一个 n 个数字组成的 ...

  10. locust性能测试安装

    Locust简介 Locust是一款易于使用的分布式用户负载测试工具.它用于对网站(或其他系统)进行负载测试,并确定系统可以处理多少并发用户.这个想法是,在测试期间,一群蝗虫(Locust)会攻击你的 ...