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

如何用Node.js fluent-FFmpeg将S3中MP4转HLS并存储至目标S3?

解决FFmpeg转HLS后将.m3u8和.ts分片同步到S3的方案

嘿,我之前做视频流媒体应用时也踩过这个坑——本地转HLS顺风顺水,一碰到S3就卡壳,毕竟FFmpeg本身没法直接把多个输出文件(m3u8+ts)直接写到S3。下面给你两个亲测有效的方案,按需选就行:

方案1:本地临时目录中转(最稳妥,首推)

这个思路就是把S3上的MP4先拉到本地临时目录,转码生成HLS文件后,再把所有相关文件批量上传回S3。好处是完全兼容你本地已经跑通的FFmpeg命令,只需要加一层S3的下载/上传逻辑。

具体步骤:

  1. 创建本地临时目录:用tmp这类npm包生成临时文件夹,避免污染本地文件系统,转码完成后自动清理。
  2. 从S3下载源MP4:用AWS SDK(比如@aws-sdk/client-s3)把视频下载到临时目录。
  3. FFmpeg转HLS:指定输出的.m3u8路径为临时目录下的文件,同时用hls_segment_filename参数把.ts分片也生成在同一个临时目录里。示例代码如下:
const ffmpeg = require('fluent-ffmpeg');
const tmp = require('tmp');
const fs = require('fs');
const { S3Client, GetObjectCommand, PutObjectCommand } = require('@aws-sdk/client-s3');

const s3Client = new S3Client({ region: '你的AWS区域' });

// 创建自动清理的临时目录
const tmpDir = tmp.dirSync({ unsafeCleanup: true });
const inputPath = `${tmpDir.name}/source.mp4`;
const outputM3u8Path = `${tmpDir.name}/output.m3u8`;

// 1. 从S3下载源视频到本地临时目录
async function downloadFromS3() {
  const params = {
    Bucket: '你的S3桶名',
    Key: '源MP4在S3中的路径,比如videos/upload/sample.mp4'
  };
  const response = await s3Client.send(new GetObjectCommand(params));
  const fileStream = fs.createWriteStream(inputPath);
  await new Promise((resolve, reject) => {
    response.Body.pipe(fileStream);
    fileStream.on('finish', resolve);
    fileStream.on('error', reject);
  });
}

// 2. 用FFmpeg转码生成HLS文件
function transcodeToHLS() {
  return new Promise((resolve, reject) => {
    ffmpeg(inputPath)
      .outputOptions([
        '-hls_time 10', // 每个分片10秒
        '-hls_list_size 0', // 保留所有分片(不自动删除旧分片)
        `-hls_segment_filename ${tmpDir.name}/segment_%03d.ts` // 指定分片文件名格式
      ])
      .save(outputM3u8Path)
      .on('end', resolve)
      .on('error', reject);
  });
}

// 3. 批量上传所有HLS文件到S3指定路径
async function uploadToS3() {
  const targetS3Prefix = 'HLS文件存储的S3前缀,比如videos/hls/sample/';
  const files = fs.readdirSync(tmpDir.name);
  
  for (const file of files) {
    const filePath = `${tmpDir.name}/${file}`;
    const fileContent = fs.readFileSync(filePath);
    // 正确设置文件的Content-Type,否则播放器可能无法识别
    const contentType = file.endsWith('.m3u8') ? 'application/x-mpegURL' : 'video/MP2T';
    const params = {
      Bucket: '你的S3桶名',
      Key: `${targetS3Prefix}${file}`,
      Body: fileContent,
      ContentType: contentType
    };
    await s3Client.send(new PutObjectCommand(params));
  }
}

// 执行完整流程
async function processVideo() {
  try {
    await downloadFromS3();
    await transcodeToHLS();
    await uploadToS3();
    console.log('转码并上传S3完成!');
  } catch (err) {
    console.error('处理失败:', err);
  } finally {
    // 不管成功失败,都清理临时目录
    tmpDir.removeCallback();
  }
}

processVideo();
  1. 注意事项:
    • 如果是服务器less环境(比如Lambda),要注意临时目录的大小限制(Lambda默认512MB),大视频转码可能需要挂载EFS扩展空间。
    • 上传时一定要设置正确的ContentType,否则前端播放器可能加载失败。

方案2:用S3FS挂载S3为本地文件系统(适合持续转码场景)

如果你的服务器是长期运行的,可以用s3fs把S3桶挂载成本地目录,这样FFmpeg就像操作本地文件一样直接把.m3u8和.ts写到挂载目录,自动同步到S3。不过这个方案依赖系统环境,稳定性不如方案1,适合批量转码的后台服务。

简单配置示例(Linux环境):

  1. 安装s3fs:sudo apt-get install s3fs
  2. 配置AWS凭证:把你的AWS密钥写入~/.passwd-s3fs,并设置权限为600:echo "AKIAXXX:secretXXX" > ~/.passwd-s3fs && chmod 600 ~/.passwd-s3fs
  3. 挂载S3目录:s3fs your-bucket-name /mnt/s3-hls -o use_cache=/tmp,allow_other
  4. 之后FFmpeg的输出路径直接指向/mnt/s3-hls/your/hls/path/,生成的所有文件会自动同步到S3。

关键提醒

转码后一定要检查.m3u8文件里的分片路径:

  • 如果用相对路径,只要所有文件都在S3的同一个前缀下,播放器就能正确加载分片;
  • 如果需要绝对URL,可以在FFmpeg的输出参数里加-hls_base_url https://your-bucket.s3.region.amazonaws.com/videos/hls/sample/,这样.m3u8里的分片路径会自动加上这个前缀。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 07:39:02