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
相关推荐
aqi006 小时前
FFmpeg开发笔记(八十三)国产的视频裁剪框架AndroidVideoTrimmer
android·ffmpeg·音视频·流媒体
彷徨而立18 小时前
【win32】FFmpeg 硬件解码器
windows·ffmpeg
dddddppppp1231 天前
linux sdl图形编程之helloworld.
linux·运维·ffmpeg
给大佬递杯卡布奇诺1 天前
FFmpeg 基本API avio_read函数内部调用流程分析
c++·ffmpeg·音视频
华仔啊2 天前
别再用在线工具转GIF了!FFmpeg实现高清无损转换,这才是真优雅
ffmpeg
落淼喵_G2 天前
ffmpeg转化mp3至wav格式
ffmpeg
jndingxin3 天前
瑞芯微算法环境搭建(1)------编译ffmpeg
ffmpeg
彷徨而立4 天前
【FFmpeg】销毁解码器时,必须清理剩余帧吗?
ffmpeg
骄傲的心别枯萎4 天前
项目1:FFMPEG推流器讲解(五):FFMPEG时间戳、时间基、时间转换的讲解
ffmpeg·音视频·视频编解码·时间戳·rv1126