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

如何使用Node.js与FFmpeg生成视频预览缩略图及对应WEBVTT索引文件

实现方案

前置依赖

你需要先安装两个核心依赖:

  • 系统环境安装FFmpeg(确保命令行可调用)
  • Node.js项目安装fluent-ffmpeg库,用于调用FFmpeg获取视频元数据、执行命令
npm install fluent-ffmpeg

核心实现逻辑

你当前用的FFmpeg命令是每60帧抽1张缩略图,按4列8行拼接成雪碧图,生成WEBVTT的逻辑可以和抽帧参数完全对齐,步骤如下:

步骤1:获取视频基础元数据

首先通过ffprobe读取视频的帧率、总时长,用于计算抽帧间隔和总抽帧数量。

步骤2:固定参数对齐你的FFmpeg命令

你现在的命令对应固定参数如下,可根据自己的抽帧规则调整:

  • 抽帧间隔帧:60(对应not(mod(n,60)))
  • 雪碧图行列:4列、8行(对应tile=layout=4x8)
  • 单张缩略图宽度:240(对应scale=240:-1)

步骤3:完整代码示例

const ffmpeg = require('fluent-ffmpeg');
const fs = require('fs');

// 配置参数,和你的FFmpeg命令保持一致
const CONFIG = {
  videoPath: './input.mp4', // 输入视频路径
  spritePath: 'outputfile.jpg', // 生成的雪碧图路径
  vttOutputPath: './thumbnails.vtt', // 输出VTT路径
  frameInterval: 60, // 每N帧抽一张
  tileCols: 4, // 雪碧图列数
  tileRows: 8, // 雪碧图行数
  thumbWidth: 240 // 单张缩略图宽度
};

// 秒转成WEBVTT需要的HH:MM:SS.mmm格式
function formatTime(seconds) {
  const h = String(Math.floor(seconds / 3600)).padStart(2, '0');
  const m = String(Math.floor((seconds % 3600) / 60)).padStart(2, '0');
  const s = String(Math.floor(seconds % 60)).padStart(2, '0');
  const ms = String(Math.floor((seconds % 1) * 1000)).padStart(3, '0');
  return `${h}:${m}:${s}.${ms}`;
}

async function generateVtt() {
  // 1. 获取视频元数据
  const meta = await new Promise((resolve, reject) => {
    ffmpeg.ffprobe(CONFIG.videoPath, (err, data) => {
      if (err) reject(err);
      else resolve(data);
    });
  });

  const videoStream = meta.streams.find(s => s.codec_type === 'video');
  const fps = eval(videoStream.r_frame_rate); // 帧率转数字,比如24000/1001转换为对应数值
  const duration = meta.format.duration; // 视频总时长(秒)
  const interval = CONFIG.frameInterval / fps; // 每张缩略图对应的时间间隔(秒)
  const thumbHeight = Math.round(CONFIG.thumbWidth * videoStream.height / videoStream.width); // 按比例计算缩略图高度
  const totalThumbs = Math.min(Math.ceil(duration / interval), CONFIG.tileCols * CONFIG.tileRows); // 总缩略图数量,不超过单页雪碧图最大容量

  // 2. 生成VTT内容
  let vttContent = 'WEBVTT\n\n';
  for (let i = 0; i < totalThumbs; i++) {
    const startTime = i * interval;
    const endTime = Math.min((i + 1) * interval, duration);
    const col = i % CONFIG.tileCols;
    const row = Math.floor(i / CONFIG.tileCols);
    const x = col * CONFIG.thumbWidth;
    const y = row * thumbHeight;

    vttContent += `${formatTime(startTime)} --> ${formatTime(endTime)}\n`;
    vttContent += `${CONFIG.spritePath}#xywh=${x},${y},${CONFIG.thumbWidth},${thumbHeight}\n\n`;
  }

  // 3. 写入VTT文件
  fs.writeFileSync(CONFIG.vttOutputPath, vttContent.trim());
  console.log('WEBVTT文件生成完成');
}

generateVtt().catch(console.error);

注意事项

  • 如果视频时长超过单页雪碧图可容纳的总时长(即CONFIG.tileCols * CONFIG.tileRows * interval),你需要修改FFmpeg命令生成多页雪碧图,同时在生成VTT时按页切换对应雪碧图文件名即可。
  • 如果你修改了抽帧规则、雪碧图行列数、缩略图尺寸,只要同步修改CONFIG里的对应参数即可,不需要调整核心逻辑。

内容的提问来源于stack exchange,提问作者The Dead Man

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 00:45:04