You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用FFmpeg(libav/libavfilter等)C/C++ API为视频帧添加图片或文字水印?

用FFmpeg libavfilter实现视频帧添加水印(图片/文字)

核心逻辑

基于FFmpeg的libavfilter构建滤镜图,将解码后的AVFrame送入滤镜链处理,完成水印添加后,输出处理后的帧用于编码。图片水印和文字水印的底层滤镜框架通用,仅需调整滤镜命令参数。

具体实现步骤

1. 初始化滤镜环境

首先注册所有FFmpeg滤镜,确保后续能调用所需滤镜:

avfilter_register_all();

2. 构建滤镜命令

根据水印类型选择对应滤镜:

  • 图片水印:使用overlay滤镜,示例命令(将logo置于右上角,保留10像素边距):
    [in] overlay=W-w-10:H-h-10 [out]
    
    其中W/H为视频帧宽高,w/h为logo图宽高。
  • 文字水印:使用drawtext滤镜,示例命令(左上角白色24号文字):
    [in] drawtext=text='Hello World':fontsize=24:fontcolor=white:x=10:y=10 [out]
    
    若需显示中文,需追加字体路径参数:fontfile=/path/to/your/font.ttf。

3. 初始化滤镜图与上下文

创建并配置滤镜图、输入输出上下文,解析滤镜命令并完成初始化:

// 初始化滤镜图
AVFilterGraph *filter_graph = avfilter_graph_alloc();
AVFilterInOut *inputs = avfilter_inout_alloc();
AVFilterInOut *outputs = avfilter_inout_alloc();

// 创建输入buffer滤镜(需匹配视频帧的像素格式、宽高、时间基)
const AVFilter *buffer_src = avfilter_get_by_name("buffer");
AVFilterContext *buffer_src_ctx;
AVDictionary *opts = NULL;
av_dict_set(&opts, "video_size", "1920x1080", 0); // 替换为实际视频尺寸
av_dict_set(&opts, "pix_fmt", av_get_pix_fmt_name(AV_PIX_FMT_YUV420P), 0); // 替换为实际像素格式
av_dict_set(&opts, "time_base", "1/25", 0); // 替换为实际视频的time_base
avfilter_graph_create_filter(&buffer_src_ctx, buffer_src, "in", NULL, &opts, filter_graph);

// 创建输出buffersink滤镜
const AVFilter *buffer_sink = avfilter_get_by_name("buffersink");
AVFilterContext *buffer_sink_ctx;
avfilter_graph_create_filter(&buffer_sink_ctx, buffer_sink, "out", NULL, NULL, filter_graph);

// 绑定输入输出的InOut节点
inputs->name = av_strdup("out");
inputs->filter_ctx = buffer_sink_ctx;
inputs->pad_idx = 0;
inputs->next = NULL;

outputs->name = av_strdup("in");
outputs->filter_ctx = buffer_src_ctx;
outputs->pad_idx = 0;
outputs->next = NULL;

// 解析滤镜命令(以图片水印为例)
char filter_desc[1024];
snprintf(filter_desc, sizeof(filter_desc), "[in] overlay=W-w-10:H-h-10 [out]");
int ret = avfilter_graph_parse_ptr(filter_graph, filter_desc, &inputs, &outputs, NULL);
if (ret < 0) {
    // 错误处理逻辑
}

// 完成滤镜图配置
ret = avfilter_graph_config(filter_graph, NULL);
if (ret < 0) {
    // 错误处理逻辑
}

4. 处理解码帧并获取带水印的帧

将解码得到的AVFrame送入滤镜处理,再取出处理后的帧:

// 调整帧的时间基,匹配滤镜输入要求
frame->pts = av_rescale_q(frame->pts, codec_ctx->time_base, buffer_src_ctx->inputs[0]->time_base);

// 将帧送入滤镜输入
int ret = av_buffersrc_add_frame_flags(buffer_src_ctx, frame, AV_BUFFERSRC_FLAG_KEEP_REF);
if (ret < 0) {
    // 错误处理逻辑
}

// 从滤镜输出获取处理后的帧
AVFrame *filtered_frame = av_frame_alloc();
ret = av_buffersink_get_frame(buffer_sink_ctx, filtered_frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
    // 无帧可获取,结束处理或继续等待
} else if (ret < 0) {
    // 错误处理逻辑
}
// filtered_frame即为带水印的帧,可用于后续编码

5. 资源释放

处理完成后释放所有滤镜相关资源:

av_frame_free(&filtered_frame);
avfilter_graph_free(&filter_graph);
avfilter_inout_free(&inputs);
avfilter_inout_free(&outputs);

注意事项

  • 图片水印需提前将logo解码为AVFrame,并通过overlay的第二个输入构建多输入滤镜链;静态logo可解码一次后重复使用。
  • drawtext滤镜要求FFmpeg编译时开启--enable-libfreetype选项,否则无法使用。
  • 输入帧的像素格式必须与滤镜buffer源配置的格式一致,否则会触发参数不匹配错误。

内容的提问来源于stack exchange,提问作者Alex

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 18:26:03