ffmpeg滤镜AVFilter之图片水印

给视频添加图片水印可用以下命令:

ffmpeg -i 123.mp4 -vf "movie=logo.png[wm1];[in][wm1] overlay=main_w-overlay_w-10:main_h-overlay_h-10[out] " test.mp4

下面来说明一下这条命令中基本参数的含义:

-i :表示输入
123.mp4: 表示要处理的视频源
-vf:滤镜相关,视频裁剪,水印等等操作都需要它完成
logo.png:要添加的水印图片地址
overlay:水印参数
main_w-overlay_w-10:水印在x轴的位置
main_h-overlay_h-10:水印在y轴的位置
main_w:视频宽度
main_h:视频高度
overlay_w:水印图片宽度
overlay_h:水印图片高度

代码实现,这里封装了滤镜水印命令,方便调用

//
// 水印坐标方向
//
typedef enum _DIRECTION
{
    left_top      = 1,  //左上角
	left_bottom   = 2,  //左下角
	right_top     = 3,  //右上角
	right_bottom  = 4   //右下角
}DIRECTION;

typedef struct _DIRECTION_STR
{
	char* left_top_str     = "%d:%d";  //左上角
	char* left_bottom_str  = "%d:main_h-overlay_h-%d";  //左下角
	char* right_top_str    = "main_w-overlay_w-%d:%d";  //右上角
	char* right_bottom_str = "main_w-overlay_w-%d:main_h-overlay_h-%d";   //右下角
	//char* right_bottom_str = "w-tw-%d:h-th-%d";   //右下角
	char* in_str           = "[in]";
	char* out_str          = "[out]";
	char* a_str            = "[a]";
}DIRECTION_STR;

//
// 水印内容结构体,图片路径,坐标等信息
//
typedef struct _WATERMARK_PICTURE_INFO
{
	char picname[128];
	DIRECTION direction;
	int x;
	int y;
}WATERMARK_PICTURE_INFO;

//
// 图片水印命令结构体
//
typedef struct _WATERMARK_PICTURE_STR
{
	char* movie_command_str      = "movie=%s";              //图片水印:%s--图片名称[可包含路径] 
	char* wm_command_str         = "[wm%d]";                //%d--水印序号[可添加多个水印]
	char* overlay_command_str    = "%soverlay=%s";          //%s--水印坐标

}WATERMARK_PICTURE;

.h文件

class CWatermarkPicture
{
public:
	CWatermarkPicture();
	~CWatermarkPicture();

public:
	int InitWatermark_Filters(AVCodecContext *pCodecCtx, WATERMARK_PICTURE_INFO** wInfos);
	AVFilterContext* GetBuffersrc_ctx(){ return buffersrc_ctx; };
	AVFilterContext* GetBuffersink_ctx(){ return buffersink_ctx; };
	void Dispose();

private:
	int InitDescr(char* filter_descr, WATERMARK_PICTURE_INFO** wInfos);

private:
	AVFilterGraph *filter_graph;
	AVFilterContext *buffersrc_ctx;
	AVFilterContext *buffersink_ctx;

};

.cpp文件

CWatermarkPicture::CWatermarkPicture()
{
}

CWatermarkPicture::~CWatermarkPicture()
{
}

void CWatermarkPicture::Dispose()
{
	if (buffersrc_ctx)
		avfilter_free(buffersrc_ctx);

	if (buffersink_ctx)
		avfilter_free(buffersink_ctx);

	if (filter_graph)
		avfilter_graph_free(&filter_graph);
}

int CWatermarkPicture::InitDescr(char* filter_descr, WATERMARK_PICTURE_INFO** wInfos)
{
	int wInfos_leng = sizeof(wInfos) / sizeof(WATERMARK_PICTURE_INFO*);
	if (wInfos_leng <= 0)
		return -1;

	char picInfo[1024] = "";
	char directionInfo[1024] = "";
	WATERMARK_PICTURE watermark;
	DIRECTION_STR direction_str;
	//遍历水印集合
	for (int i = 0; i < wInfos_leng; i++)
	{
		char oneInfo1[512];
		snprintf(oneInfo1, sizeof(oneInfo1), watermark.movie_command_str, wInfos[i]->picname);
		char oneInfo2[16];
		snprintf(oneInfo2, sizeof(oneInfo2), watermark.wm_command_str, i + 1);
		char oneInfo3[64];
		switch (wInfos[i]->direction)
		{
		case DIRECTION::left_top:
			snprintf(oneInfo3, sizeof(oneInfo3), direction_str.left_top_str, wInfos[i]->x, wInfos[i]->y);
			break;
		case DIRECTION::left_bottom:
			snprintf(oneInfo3, sizeof(oneInfo3), direction_str.left_bottom_str, wInfos[i]->x, wInfos[i]->y);
			break;
		case DIRECTION::right_top:
			snprintf(oneInfo3, sizeof(oneInfo3), direction_str.right_top_str, wInfos[i]->x, wInfos[i]->y);
			break;
		case DIRECTION::right_bottom:
			snprintf(oneInfo3, sizeof(oneInfo3), direction_str.right_bottom_str, wInfos[i]->x, wInfos[i]->y);
			break;
		}
		char oneInfo4[512];
		snprintf(oneInfo4, sizeof(oneInfo4), watermark.overlay_command_str, oneInfo2, oneInfo3);

		strcat(picInfo, oneInfo1);
		strcat(picInfo, oneInfo2);
		strcat(picInfo, ";");

		if (i == 0)
			strcat(directionInfo, direction_str.in_str);

		strcat(directionInfo, oneInfo4);

		if (wInfos_leng > 1 && i < wInfos_leng - 1)
			strcat(directionInfo, direction_str.a_str);
		if (i < wInfos_leng - 1)
			strcat(directionInfo, ";");
	}
	strcat(directionInfo, direction_str.out_str);

	strcat(filter_descr, picInfo);
	strcat(filter_descr, directionInfo);

	return 0;
}

int CWatermarkPicture::InitWatermark_Filters(AVCodecContext *pCodecCtx, WATERMARK_PICTURE_INFO** wInfos)
{
	int result = 0;
	char filters_descr[1024] = "";
	if (result = InitDescr(filters_descr, wInfos) < 0)
		goto End;
	if (!pCodecCtx)
		goto End;

	AVFilter* buffersrc = avfilter_get_by_name("buffer");
	AVFilter* buffersink = avfilter_get_by_name("ffbuffersink");
	AVBufferSinkParams *buffersink_params;
	enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE };
	AVFilterInOut *outputs = avfilter_inout_alloc();
	AVFilterInOut *inputs = avfilter_inout_alloc();

	filter_graph = avfilter_graph_alloc();

	char arg[512];
	snprintf(arg, sizeof(arg),
		"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
		pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
		pCodecCtx->time_base.num, pCodecCtx->time_base.den,
		pCodecCtx->sample_aspect_ratio.num, pCodecCtx->sample_aspect_ratio.den);

	result = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in", arg, NULL, filter_graph);
	if (result)
	{
		goto End;
	}
	buffersink_params = av_buffersink_params_alloc();
	buffersink_params->pixel_fmts = pix_fmts;
	result = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out", NULL, buffersink_params, filter_graph);
	av_free(buffersink_params);
	if (result)
	{
		goto End;
	}
	outputs->name = av_strdup("in");
	outputs->filter_ctx = buffersrc_ctx;
	outputs->pad_idx = 0;
	outputs->next = NULL;

	inputs->name = av_strdup("out");
	inputs->filter_ctx = buffersink_ctx;
	inputs->pad_idx = 0;
	inputs->next = NULL;

	if ((result = avfilter_graph_parse_ptr(filter_graph, filters_descr, &inputs, &outputs, NULL)) < 0)
		goto End;

	if ((result = avfilter_graph_config(filter_graph, NULL)) < 0)
		goto End;

End:
	return result;
}

封装好了,这里创建水印类,这里可以添加多个图片水印,例子只添加了一个图片水印

WATERMARK_PICTURE_INFO** wInfos = new WATERMARK_PICTURE_INFO*[1];
	wInfos[0] = new WATERMARK_PICTURE_INFO();
	snprintf(wInfos[0]->picname, sizeof(wInfos[0]->picname),"%s", "logo.png");
	wInfos[0]->direction = DIRECTION::right_bottom;
	wInfos[0]->x = 10;
	wInfos[0]->y = 5;

	CWatermarkPicture pic = {};
	if (pic.InitWatermark_Filters(pCodecCtx, wInfos) < 0)
		return 0;

输入解码后的图像帧和获取滤镜后的帧

// 输入图像帧
if (av_buffersrc_add_frame(pic.GetBuffersrc_ctx(), pFrame) < 0) {
		break;
}
// 获取滤镜后的图像帧
ret = av_buffersink_get_frame(pic.GetBuffersink_ctx(), pFrame_out);
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

音视频研究中心

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值