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

如何在Node.js+Express.js中用FFmpeg添加随机移动视频水印

实现FFmpeg动态随机位置水印(每5秒变换)

需求:基于Node.js + Express + FFmpeg实现视频水印,要求邮箱水印每5秒随机变换一次位置,防止视频盗用。当前已能添加静态水印,但动态位置逻辑未实现。

现有代码

const email = 'name@gmail.com';
app.post('/addwatermark', upload.single('video'), (req, res) => {
const fileName = path.basename(req.file.path) + '.mp4';
const outputPath = `public/reduceSize/${fileName}`;
const outputPathWithWatermark = `public/reduceSize/${fileName}-wm.mp4`;
let watermarkPosition = { x: 10, y: 650 };

// 创建输出目录(如果不存在)
const outputDir = path.dirname(outputPathWithWatermark);
if (!fs.existsSync(outputDir)) {
    fs.mkdirSync(outputDir, { recursive: true });
}

// 发送心跳保持连接
res.write(': ping\n\n');

// 生成随机水印位置的函数
const changeWatermarkPosition = () => {
    watermarkPosition = { x: Math.floor(Math.random() * 100), y: Math.floor(Math.random() * 100) };
};

// 每5秒变更一次水印位置
setInterval(changeWatermarkPosition, 5000);
const watermarkStyle = `-vf drawtext=text='${email}':x=(w-text_w-10):y=(h-text_h-10):fontcolor=white:fontsize=24:shadowcolor=black:shadowx=1:shadowy=1:box=1:y=${watermarkPosition.y}:fontcolor=red:fontsize=24:shadowcolor=black:shadowx=1:shadowy=1:box=1:boxcolor=black@0.5`

ffmpeg(req.file.path)
    .addOption(watermarkStyle)
    .output(outputPathWithWatermark)
    .videoCodec('libx264')
    .audioCodec('aac')
    .size('50%')
    .on('error', (err) => {
        console.error(`FFmpeg error: ${err.message}`);
        res.status(500).json({ message: err.message });
    })
    .on('progress', (progress) => {
        // 每5秒设置水印位置
        changeWatermarkPosition();

        const mb = progress.targetSize / 1024;
        const data = {
            message: `Processing: ${mb.toFixed(2)} MB converted`,
            watermark: `Adding watermark to video: ${progress.framesProcessed} frames processed`
        };
        console.log(data.message);
        res.write(`data: ${JSON.stringify(data)}\n\n`);

    })
    .on('end', () => {
        console.log(`Video successfully converted to ${outputPathWithWatermark}`);
        // 可在此添加临时文件删除逻辑
    })
    .run();
 });

解决方案

你当前的思路存在问题:Node.js的setInterval和变量修改无法影响已经启动的FFmpeg进程,因为FFmpeg启动后滤镜参数是固定的。必须利用FFmpeg自身的滤镜表达式,实现时间触发的位置动态变化。

核心实现逻辑

通过FFmpeg的drawtext滤镜结合以下函数实现需求:

  • t:当前视频处理的时间(秒)
  • mod(t,5):计算当前时间与5的余数,余数为0时刚好是5秒的整数倍
  • random(min, max):生成指定范围内的随机数,结合视频宽高w/h和水印文本尺寸text_w/text_h,确保水印不超出画面
  • if(condition, true_val, false_val):仅在每5秒的节点更新位置,其余时间保持当前位置,避免水印闪烁

修改后的完整代码

const email = 'name@gmail.com';
app.post('/addwatermark', upload.single('video'), (req, res) => {
    const fileName = path.basename(req.file.path) + '.mp4';
    const outputPathWithWatermark = `public/reduceSize/${fileName}-wm.mp4`;

    // 创建输出目录(如果不存在)
    const outputDir = path.dirname(outputPathWithWatermark);
    if (!fs.existsSync(outputDir)) {
        fs.mkdirSync(outputDir, { recursive: true });
    }

    // 发送心跳保持连接
    res.write(': ping\n\n');

    // 动态水印滤镜:每5秒随机变换位置
    const watermarkStyle = `-vf drawtext=text='${email}':` +
        `x='if(mod(t,5)=0, random(0, w-text_w), x)':` +
        `y='if(mod(t,5)=0, random(0, h-text_h), y)':` +
        `fontcolor=white:fontsize=24:shadowcolor=black:shadowx=1:shadowy=1:` +
        `box=1:boxcolor=black@0.5`;

    ffmpeg(req.file.path)
        .addOption(watermarkStyle)
        .output(outputPathWithWatermark)
        .videoCodec('libx264')
        .audioCodec('aac')
        .size('50%')
        .on('error', (err) => {
            console.error(`FFmpeg error: ${err.message}`);
            res.status(500).json({ message: err.message });
        })
        .on('progress', (progress) => {
            const mb = progress.targetSize / 1024;
            const data = {
                message: `Processing: ${mb.toFixed(2)} MB converted`,
                watermark: `Adding watermark to video: ${progress.framesProcessed} frames processed`
            };
            console.log(data.message);
            res.write(`data: ${JSON.stringify(data)}\n\n`);
        })
        .on('end', () => {
            console.log(`Video successfully converted to ${outputPathWithWatermark}`);
            // 可在此添加临时文件删除逻辑
        })
        .run();
});

关键参数说明

  • x='if(mod(t,5)=0, random(0, w-text_w), x)':每5秒触发一次x坐标随机更新,范围是0到视频宽度减去水印文本宽度,确保水印不超出右侧边界
  • y='if(mod(t,5)=0, random(0, h-text_h), y)':同理y坐标,范围是0到视频高度减去水印文本高度,确保水印不超出底部边界
  • 移除了原本的setInterval和changeWatermarkPosition函数,FFmpeg内部已处理位置更新逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 23:59:58