【音视频】使用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.png[wm];[in][wm]overlay=W-w:H-h[out]",其中W、H是[in]的大小,w、h是[wm]的大小,将[wm]overlay在[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)》类似,可以跳转参考。

相关推荐
程序员编程指南43 分钟前
Qt 网络编程进阶:RESTful API 调用
c语言·网络·c++·qt·restful
肥or胖1 小时前
【音视频协议篇】WebRTC 快速入门
ffmpeg·音视频·webrtc
程序员编程指南1 小时前
Qt XML 与 JSON 数据处理方法
xml·c语言·c++·qt·json
qyhua2 小时前
Windows 平台源码部署 Dify教程(不依赖 Docker)
人工智能·windows·python
aqi002 小时前
FFmpeg开发笔记(七十八)采用Kotlin+Compose的NextPlayer播放器
android·ffmpeg·音视频·直播·流媒体
Algebraaaaa2 小时前
【C++基础】指针常量 | 常量指针 | int* p | const int* p | int* const p| const int* const p
c++
祁同伟.2 小时前
【C++】类和对象(中)构造函数、析构函数
开发语言·c++
郝学胜-神的一滴3 小时前
C++ 类型萃取:深入理解与实践
开发语言·c++·程序人生
程序员编程指南3 小时前
Qt 网络编程进阶:网络安全与加密
c语言·网络·c++·qt·web安全
女程序猿!!!3 小时前
网址收集总结
windows