FFmpeg添加水印

项目简介

本项目是通过ffmpeg代码实现给264/265裸码流添加水印.添加的水印有图片水印文本水印两种;水印可以在视频画面中按一定路径移动,也可以在指定位置显示.

水印效果

图片水印

文本水印

ffmpeg添加水印流程

a.初始化输入文件 ,得到输入上下文 (输入格式上下文 ,解码上下文).

b.初始化输出文件,得到输出上下文(输出格式上下文,编码上下文).

c.初始化滤镜,得到滤镜图对象和滤镜上下文(输入滤镜上下文 ,输出滤镜上下文).

d.处理帧信息,将输入文件解码.

e.将解码后的帧传给滤镜处理接口,生成添加滤镜后的帧信息.

f.将添加滤镜后的帧信息编码,生成编码后的帧数据 .

g.将编码后的帧数据写入文件 .

ffmpeg添加水印实现代码

图片水印实现代码

cpp 复制代码
int WaterMark::init_pic_filters(AVFilterGraph **graph, AVFilterContext **src, AVFilterContext **sink, AVCodecContext *dec_ctx, const char *watermark_path, const char *position)
{
    char args[512];
    const AVFilter *buffersrc ;
    buffersrc = avfilter_get_by_name("buffer");
    const AVFilter *buffersink ;
    buffersink = avfilter_get_by_name("buffersink");
    AVFilterInOut *outputs = avfilter_inout_alloc();
    AVFilterInOut *inputs = avfilter_inout_alloc();

    *graph = avfilter_graph_alloc();
    if (!outputs || !inputs || !*graph) {
        return AVERROR(ENOMEM);
    }

    // 配置输入源
#if 0
    snprintf(args, sizeof(args),
             "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
             dec_ctx->width, dec_ctx->height, dec_ctx->pix_fmt,
             dec_ctx->time_base.num, dec_ctx->time_base.den,
             dec_ctx->sample_aspect_ratio.num, dec_ctx->sample_aspect_ratio.den);
#else
    snprintf(args, sizeof(args),
             "video_size=%dx%d:pix_fmt=%s:time_base=%d/%d:pixel_aspect=%d/%d",
             dec_ctx->width, dec_ctx->height, "yuv420p",
             1, 25,
             1, 1);
#endif
    fprintf(stderr,"args:%s\n",args);


#if 0

    if (avfilter_graph_create_filter(src, buffersrc, "in", args, NULL, *graph) < 0) {
        fprintf(stderr, "Cannot create buffer source\n");
        return -1;
    }

    // 配置输出接收器
    if (avfilter_graph_create_filter(sink, buffersink, "out", NULL, NULL, *graph) < 0) {
        fprintf(stderr, "Cannot create buffer sink\n");
        return -1;
    }

    // 构建水印滤镜链
    AVFilterContext *watermark_src;
    AVFilterContext *overlay;

    // 添加水印源滤镜
    snprintf(args, sizeof(args), "filename=%s", watermark_path);
    if (avfilter_graph_create_filter(&watermark_src, avfilter_get_by_name("movie"), "wm", args, NULL, *graph) < 0) {
        fprintf(stderr, "Cannot create watermark source\n");
        return -1;
    }
    char overlay_args[256];

    //螺旋渐进式运动‌
    snprintf(overlay_args, sizeof(overlay_args),
             "x='main_w/2 + (main_w/2-overlay_w/2)*sin(t/2)*exp(-t/20)':"
             "y='main_h/2 + (main_h/2-overlay_h/2)*cos(t/2)*exp(-t/20)':"
             "enable='1'");
    //对角线往返扫描‌
    snprintf(overlay_args, sizeof(overlay_args),
             "x='mod(t*30, main_w-overlay_w)':"
             "y='mod(t*20, main_h-overlay_h)':"
             "enable='between(t,0,30)'");
    //八字形无限循环路径
    snprintf(overlay_args, sizeof(overlay_args),
             "x='main_w/2 + (main_w/3)*sin(t)':"
             "y='main_h/2 + (main_h/4)*sin(2*t)':"  // Lissajous曲线
             "enable='1'");

    //‌圆周运动+大小变化
    // 动态水印速度控制参数
    float speed_factor = 0.5;  // 速度系数(1.0为原速)

    snprintf(overlay_args, sizeof(overlay_args),
        "x='main_w/2 + (main_w/3)*sin(%.1f*t) - overlay_w/2':"  // 添加速度系数
        "y='main_h/2 + (main_h/3)*cos(%.1f*t) - overlay_h/2':"  // 同步修改Y轴
        "enable='1'",
        speed_factor, speed_factor);  // 参数注入

//    snprintf(overlay_args, sizeof(overlay_args),
//             "x='main_w/2 + (main_w/3)*sin(t) - overlay_w/2':"
//             "y='main_h/2 + (main_h/3)*cos(t) - overlay_h/2':"
//             "enable='1'");
#if 0
    //随机弹跳效果
    snprintf(overlay_args, sizeof(overlay_args),
             "x='if(lte(mod(t,3),1), random(1)*main_w, if(lte(mod(t,3),2), main_w-overlay_w, NAN))':"
             "y='if(lte(mod(t,4),1), random(1)*main_h, if(lte(mod(t,4),3), main_h-overlay_h, NAN))':"
             "enable='1'");
#endif
    // 添加overlay滤镜
    if (avfilter_graph_create_filter(&overlay, avfilter_get_by_name("overlay"), "overlay", overlay_args, NULL, *graph) < 0) {
        fprintf(stderr, "Cannot create overlay filter\n");
        return -1;
    }

    // 连接滤镜
    if (avfilter_link(*src, 0, overlay, 0) < 0 ||
            avfilter_link(watermark_src, 0, overlay, 1) < 0 ||
            avfilter_link(overlay, 0, *sink, 0) < 0) {
        fprintf(stderr, "Failed to link filters\n");
        return -1;
    }

#else
    /*
        //命令方式
        ffmpeg -i input.265 -i title.png -filter_complex
        "[1:v]format=rgba,rotate=PI/4:ow=rotw(PI/4):oh=roth(PI/4):c=0x00000000[wm];
        [0:v][wm]overlay=10:10,format=yuv420p" -c:v libx265 output.mp4
    */

    char watermark_args[512],overlay_args[512];
    snprintf(watermark_args, sizeof(watermark_args), "filename=%s", watermark_path);
    //八字形无限循环路径
    snprintf(overlay_args, sizeof(overlay_args),
             "x='main_w/2 + (main_w/3)*sin(t)':"
             "y='main_h/2 + (main_h/4)*sin(2*t)':"  // Lissajous曲线
             "enable='1'");
    fprintf(stderr,"watermark_args:%s\n",watermark_args);



    //创建输入滤镜
    if(avfilter_graph_create_filter(src, buffersrc, "in", args, NULL, *graph)< 0){
        fprintf(stderr, "Cannot create in filter\n");
        return -1;
    }
    if(avfilter_graph_create_filter(&m_watermark_src_ctx, avfilter_get_by_name("movie"), "watermark_in", watermark_args, NULL, *graph)<0){
        fprintf(stderr, "Cannot create watermark_in filter\n");
        return -1;
    }

    // 创建输出滤镜
    if(avfilter_graph_create_filter(sink, buffersink, "out", NULL, NULL, *graph)<0){
        fprintf(stderr, "Cannot create out filter\n");
        return -1;
    }


    // 主视频源作为第一个输入
    outputs->name = av_strdup("in");
    outputs->filter_ctx = *src;


    // 水印源作为第二个输入
    AVFilterInOut *wm_inputs = avfilter_inout_alloc();
    wm_inputs->name = av_strdup("watermark_in");
    wm_inputs->filter_ctx = m_watermark_src_ctx;

    //输入形成链表
    outputs->next = wm_inputs;


    inputs->name = av_strdup("out");
    inputs->filter_ctx = *sink;
    inputs->next = nullptr;

    // 构建滤镜链(水印旋转+叠加)
    char filter_desc[2048];
#if 1
    //水印设置旋转
    snprintf(filter_desc, sizeof(filter_desc),
             "[watermark_in]format=rgba,rotate='%s':ow=rotw('%s'):oh=roth('%s'):c=0x00000000[wm];"
             "[in][wm]overlay=x='main_w/2 + (main_w/3)*sin(0.5*t)':y='main_h/2 + (main_h/4)*cos(0.5*t)':enable='1'[out]",
             ROTATION_ANGLE, ROTATION_ANGLE, ROTATION_ANGLE);
#endif
    fprintf(stderr,"filter_desc:%s\n",filter_desc);

    if ((avfilter_graph_parse_ptr(*graph, filter_desc,
                                  &inputs, &outputs, NULL)) < 0) {
        fprintf(stderr, "滤镜图解析失败\n");
        return -1;
    }

#endif

    // 配置滤镜图
    if (avfilter_graph_config(*graph, NULL) < 0) {
        f
相关推荐
枫叶梨花19 分钟前
从 M4S 到 MP4:用 FFmpeg 轻松合并音视频文件
ffmpeg·音视频
blanks202014 小时前
qt mac 解决 info.plist 自定义问题
ffmpeg
学而知不足~2 天前
FFmpeg03:多媒体文件处理基础
ffmpeg
dualven_in_csdn2 天前
ffmpeg编译
ffmpeg
Kevin Wang7272 天前
FFmpeg如何使用GPU加速
ffmpeg
feiyangqingyun3 天前
纯Qt结合ffmpeg实现本地摄像头采集/桌面采集/应用程序窗口采集/指定采集帧率和分辨率等
qt·ffmpeg·qt桌面采集·qt摄像头采集·qt程序窗口采集
shelutai3 天前
ubuntu 编译ffmpeg6.1 增加drawtext,libx264,libx265等
linux·ubuntu·ffmpeg
陆远方4 天前
AttributeError: module ‘ffmpeg‘ has no attribute ‘probe‘
ffmpeg
进击ing小白4 天前
FFmpeg的基本概述(二)
ffmpeg