【音视频】使用movie、drawtext过滤器实现图片、文字水印(6-3)

本来准备从六月份开始研究使用ffmpeg的movie filter实现图片水印、drawtext filter实现文字水印的能力,但一直没时间,临近中秋终于有空,于是研究了下ffmpeg命令行(这里不做展示,关注代码实现),从中转化为C++代码实现。

直接上代码,这里新增了视频水印类,以下是主要实现:

cpp 复制代码
int VideoWaterMarker::init(const VIDEO_FILTER_CTX& outCtx, const VIDEO_FILTER_CTX* inCtx0, const VIDEO_FILTER_CTX* inCtx1)
{
	int err = ERROR_CODE_OK;

	if (m_inited) {
		return err;
	}

	if (inCtx0 == nullptr) {
		err = ERROR_CODE_PARAMS_ERROR;
		return err;
	}

	do {
		m_filterGraph = avfilter_graph_alloc();
		if (m_filterGraph == nullptr) {
			err = ERROR_CODE_FILTER_ALLOC_GRAPH_FAILED;
			break;
		}

		m_filterInCtxs = new VIDEO_FILTER_CTX;
		memcpy_s(&m_filterInCtxs[0], sizeof(VIDEO_FILTER_CTX), inCtx0, sizeof(VIDEO_FILTER_CTX));
		m_filterOutCtx = outCtx;

		m_filterInCtxs[0].filterInout = avfilter_inout_alloc();
		m_filterOutCtx.filterInout = avfilter_inout_alloc();

		char filterInArg[512] = { 0 };
		sprintf_s(filterInArg, sizeof(filterInArg), "video_size=%dx%d:pix_fmt=%d:frame_rate=%d:time_base=%d/%d:pixel_aspect=%d/%d",
			m_filterInCtxs[0].width, m_filterInCtxs[0].height, m_filterInCtxs[0].pixelFmt, m_filterInCtxs[0].framerate,
			m_filterInCtxs[0].timebase.num, m_filterInCtxs[0].timebase.den, m_filterInCtxs[0].pixelAspect.num, m_filterInCtxs[0].pixelAspect.den);

		int ret = avfilter_graph_create_filter(&m_filterInCtxs[0].filterCtx, avfilter_get_by_name("buffer"), "in", filterInArg, nullptr, m_filterGraph);
		if (ret < 0) {
			err = ERROR_CODE_FILTER_CREATE_FILTER_FAILED;
			break;
		}

		ret = avfilter_graph_create_filter(&m_filterOutCtx.filterCtx, avfilter_get_by_name("buffersink"), "out", nullptr, nullptr, m_filterGraph);
		if (ret < 0) {
			err = ERROR_CODE_FILTER_CREATE_FILTER_FAILED;
			break;
		}

		av_opt_set_bin(m_filterOutCtx.filterCtx, "pix_fmts", (uint8_t*)&m_filterOutCtx.pixelFmt, sizeof(m_filterOutCtx.pixelFmt), AV_OPT_SEARCH_CHILDREN);

		m_filterInCtxs[0].filterInout->name = av_strdup("in");
		m_filterInCtxs[0].filterInout->filter_ctx = m_filterInCtxs[0].filterCtx;
		m_filterInCtxs[0].filterInout->pad_idx = 0;
		m_filterInCtxs[0].filterInout->next = nullptr;

		m_filterOutCtx.filterInout->name = av_strdup("out");
		m_filterOutCtx.filterInout->filter_ctx = m_filterOutCtx.filterCtx;
		m_filterOutCtx.filterInout->pad_idx = 0;
		m_filterOutCtx.filterInout->next = nullptr;

		std::string filterDesc;
		/* 1、图片水印 */
		filterDesc = "movie=11.png[wm];[in][wm]overlay=W-w:H-h[out]";
		/* 2、文字水印 */
		std::wstring tempText = L"Hello, 世界!";
		std::string strText = HELPER::StringConverter::convertUnicodeToUtf8(tempText);
		filterDesc = "drawtext=fontfile=\'C\\:/Windows/Fonts/simsun.ttc\':fontsize=72:fontcolor=white:box=1:boxcolor=blue@0.5:x=w-text_w:y=h-text_h:text=\'" + strText + "\'";
		/* 3、时间水印 */
		filterDesc = "drawtext=fontfile=\'C\\:/Windows/Fonts/Arial.ttf\':text=\'%{localtime\\:%Y-%m-%d %H.%M.%S}\':fontsize=36:fontcolor=white:box=1:boxcolor=blue:x=100:y=50";
		ret = avfilter_graph_parse_ptr(m_filterGraph, filterDesc.c_str(), &m_filterOutCtx.filterInout, &m_filterInCtxs[0].filterInout, nullptr);
		if (ret < 0) {
			err = ERROR_CODE_FILTER_PARSE_PTR_FAILED;
			break;
		}

		ret = avfilter_graph_config(m_filterGraph, nullptr);
		if (ret < 0) {
			err = ERROR_CODE_FILTER_CONFIG_FAILED;
			break;
		}

		m_inited = true;
	} while (0);

	if (err != ERROR_CODE_OK) {
		LOGGER::Logger::log(LOGGER::LOG_TYPE_ERROR, "[%s] init mixer, error: %s", __FUNCTION__, HCMDR_GET_ERROR_DESC(err));
		cleanup();
	}

	return err;
}

1、对于图片的水印,使用movie filter将图片作为一个输入源wm跟另外一个视频输入源in(摄像头或桌面采集)输出新的out视频源。"movie=11.pngwm;inwmoverlay=W-w:H-hout",其中W、H是in的大小,w、h是wm的大小,将wmoverlay在in上面,且显示在右下角。

2、对于文字水印,使用drawtext filter,首先要指定绝对路径的文字文件,例如fontfile='C\:/Windows/Fonts/simsun.ttc',在windows下一定要这样写,要不然找不到指定的文字文件而使用默认的文字文件,其次文字输入如果包含中文则需要将其转化为UTF8格式作为text的值传入。一般文字大小从8磅到72磅,这里指定文字大小为72磅,颜色为白色,box=1表示开启文字背景色,指定背景色为蓝色且透明度为50%(文字也可以像这样单独指定透明度;如果要同时指定文字和背景色透明度,可以使用命令alpha=0.5),这里x、y将文字位置至于右下角。

3、对于时间水印,同样需要指定绝对路径fontfile,其次text需要指定例如text='%{localtime\:%Y-%m-%d %H.%M.%S}',Y表示年,m表示月份,d表示天,H表示小时,M表示分钟,S表示秒,Arial字体的时间样式:

PS:因为视频水印也是线程驱动的,剩下的一些实现如开始线程start()、停止线程stop()、喂帧addFrame()、清空cleanup()和线程函数mixProcess()跟《【音视频】视频混流-avfilter(2-2)》类似,可以跳转参考。

相关推荐
倒头就睡的小比特3 天前
算法竞赛C++常用的STL
c++·算法
weilx12343 天前
C++笔记-文件IO-<fcntl.h>
c++
小羊没烦恼!3 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
rockmelodies3 天前
# Windows Server2008 R2 Standard(SP1镜像U盘)重置本地管理员密码【完整详细步骤】
windows·密码破解
美狐美颜SDK开放平台3 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
Smileyqp沛沛3 天前
前端?C++ ?较大差异基础罗列
c++·基础·前端转c++
C语言小火车3 天前
C/C++ 为什么需要编译器?
开发语言·c++
旖旎夜光3 天前
力控面试题 01.01: 判定字符是否唯一(位运算) —— 题解
c++·学习·算法·leetcode·力控
吞下星星的少年·-·3 天前
C++ 萌新语法入门篇
c++·算法比赛
霍霍的袁3 天前
【C++】map 和 set 的使用 | 从用法到底层
开发语言·c++·学习·visual studio