文章

【nodejs】视频压缩示例代码

const { spawn } = require("child_process");
const ffmpegPath = require("@ffmpeg-installer/ffmpeg").path;
const os = require('os')

function parseDuration(str) {
  const match = str.match(/Duration: (\d+):(\d+):(\d+\.\d+)/);
  if (!match) return 0;

  const [, h, m, s] = match;
  return Number(h) * 3600 + Number(m) * 60 + Number(s);
}

function parseTime(str) {
  const match = str.match(/time=(\d+):(\d+):(\d+\.\d+)/);
  if (!match) return 0;

  const [, h, m, s] = match;
  return Number(h) * 3600 + Number(m) * 60 + Number(s);
}

function compressVideo(input, output, onProgress) {
  return new Promise((resolve, reject) => {
    let totalDuration = 0;

    const threads = os.cpus().length;

    console.log('线程数:', threads)

    const useThreads = threads / 2

    const args = [
      "-i", input,
      "-vf", "scale='min(1280,iw)':-2",
      "-c:v", "libx265",
      "-crf", "23",
      "-preset", "fast",
      "-c:a", "aac",
      "-b:a", "128k",
      "-y",
      "-threads", useThreads, // 使用所有线程压缩视频
      output,
    ];

    const child = spawn(ffmpegPath, args);

    child.stderr.on("data", (data) => {
      const text = data.toString();

      // 获取总时长
      if (!totalDuration && text.includes("Duration")) {
        totalDuration = parseDuration(text);
      }

      // 获取当前进度
      if (text.includes("time=")) {
        const current = parseTime(text);
        if (totalDuration > 0) {
          const percent = ((current / totalDuration) * 100).toFixed(2);
          onProgress && onProgress(percent);
        }
      }

      // 输出日志(可注释)
      // console.log(text);
    });

    child.on("close", (code) => {
      if (code === 0) resolve("视频压缩完成:" + output);
      else reject(new Error("压缩失败,退出码:" + code));
    });

    child.on("error", reject);
  });
}


// CLI 模式
if (require.main === module) {
  const [, , input, output] = process.argv;

  if (!input || !output) {
    console.log("用法:node main.js input.mp4 output.mp4");
    process.exit(1);
  }

  // const os = require('os');

  // console.log("CPU 信息:", os.cpus());
  // console.log("物理内核数:", os.cpus().length / 2); // 如果 CPU 支持超线程,大约是逻辑核数的一半
  // console.log("逻辑核数:", os.cpus().length);
  // console.log("CPU 架构:", os.arch());
  // console.log("操作系统:", os.platform(), os.release());

  const time = Date.now()

  compressVideo(
    input,
    output,
    (percent) => console.log("进度:", percent + "%")
  )
    .then(console.log)
    .catch(console.error)
    .finally(() => {
      const now = Date.now()
      console.log(`总耗时:${(now - time) / 1000} 秒`)
    })
}

module.exports = compressVideo;

使用方式:

node main.js input.mp4 output.mp4

许可协议:  CC BY 4.0