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

基于Node.js和Express如何将动态生成字幕永久嵌入视频供用户下载

Node.js + Express 环境下视频内嵌字幕实现方案

核心实现思路

你可以通过FFmpeg实现字幕和视频的合并,支持硬烧录字幕(内嵌到画面无法移除)和软封装字幕(作为独立轨道可开关)两种模式,Node.js侧通过fluent-ffmpeg库调用FFmpeg能力完成处理,最终返回给用户下载。


前置准备

  • 首先在你的服务端部署环境中安装FFmpeg,Windows、Linux、macOS均有对应发行版,安装完成后确保FFmpeg已加入系统环境变量
  • 安装Node.js依赖包:
npm install fluent-ffmpeg

具体实现步骤

1. 生成标准字幕文件

你动态生成的字幕需要先转换为SRT/ASS格式,这是FFmpeg兼容性最好的字幕格式:

  • 可以将内存中的字幕数据临时写入本地.srt文件,建议用uuid作为文件名避免不同请求的文件冲突
  • 高并发场景下也可以直接使用流传输字幕,无需写入本地磁盘

2. 视频合并字幕逻辑

硬烧录字幕方案(所有播放器默认显示字幕)

直接将字幕渲染到视频画面中,兼容性最强:

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

// 入参:原视频路径、字幕文件路径、输出文件路径
function burnSubtitlesToVideo(videoPath, subtitlePath, outputPath) {
  return new Promise((resolve, reject) => {
    ffmpeg(videoPath)
      // 配置字幕滤镜,可额外调整字幕字体、大小、位置等参数
      .videoFilter(`subtitles=${subtitlePath}:force_style='FontSize=24,FontName=微软雅黑'`)
      .output(outputPath)
      .on('end', () => {
        // 处理完成后删除临时字幕文件
        fs.unlinkSync(subtitlePath);
        resolve(outputPath);
      })
      .on('error', (err) => {
        reject(err);
      })
      .run();
  });
}

软封装字幕方案(字幕作为独立轨道可开关)

字幕不会修改原视频画面,用户可以在播放器中手动开启/关闭字幕,处理速度更快,服务器资源消耗更低:

function encapsulateSubtitleTrack(videoPath, subtitlePath, outputPath) {
  return new Promise((resolve, reject) => {
    ffmpeg(videoPath)
      .addInput(subtitlePath)
      // 字幕编码适配MP4容器,若输出MKV格式可改为`srt`/`ass`
      .outputOptions('-c:s mov_text')
      .output(outputPath)
      .on('end', () => {
        fs.unlinkSync(subtitlePath);
        resolve(outputPath);
      })
      .on('error', (err) => {
        reject(err);
      })
      .run();
  });
}

3. Express接口对接

在Express路由中处理用户的下载请求:

const { v4: uuidv4 } = require('uuid');

app.get('/download/video-with-subtitle', async (req, res) => {
  const videoId = req.query.videoId;
  // 1. 获取对应原视频路径
  const originVideoPath = path.join(__dirname, `./videos/${videoId}.mp4`);
  // 2. 生成临时字幕文件
  const subtitleContent = await generateYourDynamicSubtitle(videoId); // 替换为你自己的动态字幕生成逻辑
  const subtitleTempPath = path.join(__dirname, `./temp/${uuidv4()}.srt`);
  fs.writeFileSync(subtitleTempPath, subtitleContent);
  // 3. 生成输出临时文件路径
  const outputTempPath = path.join(__dirname, `./temp/${uuidv4()}.mp4`);
  
  try {
    // 调用上面的合并方法,可按需选硬烧录/软封装
    await burnSubtitlesToVideo(originVideoPath, subtitleTempPath, outputTempPath);
    // 返回文件给用户下载
    res.download(outputTempPath, `video_with_subtitle.mp4`, (err) => {
      // 下载完成后删除临时输出文件
      fs.unlinkSync(outputTempPath);
    });
  } catch (err) {
    res.status(500).send('视频处理失败');
    // 出错时清理临时文件
    if (fs.existsSync(subtitleTempPath)) fs.unlinkSync(subtitleTempPath);
    if (fs.existsSync(outputTempPath)) fs.unlinkSync(outputTempPath);
  }
})

注意事项

  • FFmpeg视频处理属于CPU密集型操作,并发请求较多时建议引入任务队列做流量削峰,避免服务器资源耗尽
  • 大视频处理时可以监听FFmpeg的progress事件,将处理进度同步给前端,提升用户体验
  • 临时文件目录需要提前创建,同时做好权限配置,避免读写失败

内容的提问来源于stack exchange,提问作者Andres Eduardo Bercowsky

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 04:57:03