[Node] MP4 文件 按文件大小 切成 多个小文件

1,543 阅读2分钟

有时候上传接口可能会有限制,需要先划分成小文件再进行上传.

避免增大内存压力,应该采用流进行操作

const fs = require('fs');
const { Writable } = require('stream');

const maxPieceSize = 1024 * 1024 * 100;
const pieceExt = '.temp.mp4';

async function split(readStream){
  let currPieceNumber = 0;
  let currPieceSize = 0;
  
  let currPiceWriteStream = fs.createWriteStream(`./split/${currPieceNumber}${pieceExt}`);
  const countSizeWritable = new Writable({
    write: (chunk, encoding, callback) => {
      if(chunk.length+ currPieceSize >= maxPieceSize){
        currPiceWriteStream.end()
        currPieceNumber++
        currPieceSize = 0
        currPiceWriteStream = fs.createWriteStream(`./split/${currPieceNumber}${pieceExt}`);
      }
      
      currPieceSize += chunk.length
      currPiceWriteStream.write(chunk)
      callback()
    },
  });
  
  readStream.pipe(countSizeWritable).on('unpipe', () => {
    //  结束了
    console.log(`文件停止推流,切片结束`);
  });
}
// 需要切片的文件流
const inputFile = './data/test.mp4';
const originReadStream = fs.createReadStream(inputFile);
split(originReadStream);
  

当然你会发现虽然切均匀了,但是视频却无法播放

所以应该借用 ffmpeg 进行转换.

# 可以通过时间规范大小
ffmpeg -i 鱼钩的前端.mp4 -f segment -segment_time 1200 output%2d.mp4

转换结果如下,%2d依次转为 00 01 02...:

PS: 网上说可以 结合 ffprobe 与 -fs 进行以限定文件固定大小进行切分, 但是我没搞懂,望有识之士相助评论.

node 下可以用 fluent-ffmpeg

const ffmpeg = require('fluent-ffmpeg');
const fs = require('fs')
const fileStream = fs.createReadStream('./data/鱼钩的前端.mp4')
const filePath = './data/split/split'
ffmpeg()
  .input(fileStream)
  .outputOptions(['-c copy', '-f segment', '-segment_time 1200'])
  .output(`${filePath}_piece_%02d.mp4`)
  .on('start', () => {
    console.log(`<<<<11-29 14:39:54>>>>⬇️\n✨`,`start`)
  })
  .on('error', (err) => {
    console.log(`<<<<11-29 14:40:17>>>>⬇️\n✨`, `error`, err);
  })
  .on('end', () => {
    onsole.log(`<<<<11-29 14:40:17>>>>⬇️\n✨`, `end`);
  })
  .run();

或者 MP4box 也可以轻松解决

# 默认kB
mp4box -splits 200000 test.mp4
# 等同 可以加单位
mp4box -splits 200m test.mp4

转换成响应的结果如下:

如果你想使用 node 进行操作,那么你可以安装 mp4box 后这样进行操作

const spawn= require('cross-spawn')
const result = spawn.sync('mp4box', ['-splits', '200000', './data/test.mp4'], {
  stdio: 'inherit'
});
console.log('radiorz...结束划分 MP4 文件',result) 

PS: 也可以用 google/zx, 或者 shelljs 执行 shell 命令.

PS: MP4box.js 没整明白,欲哭无泪.

参考

  1. ^using-ffmpeg-to-split-video-files-by-size stackoverflow.com/questions/3…
  2. ^mp4box 官方文档 github.com/gpac/gpac/w…
  3. ^google/zx github.com/google/zx
  4. ^github: shelljs github.com/shelljs/she…
  5. ^stackoverflow.com/questions/7…