ffmpeg纯前端实现对视频的操作 - fluent-ffmpeg踩坑、ffmpeg-statci踩坑、视频合并、图片转视频

ffmpeg纯前端实现对视频的操作 - fluent-ffmpeg踩坑、视频合并、图片转视频

ffmpeg有很多插件、js库,这里介绍ffmpeg-staticfluent-ffmpegffmpeg.wasm

ffmpeg.wasm

在 纯前端实现 vue 利用ffmpeg.wasm将图片合成视频 这篇文章的gitHub代码中,vite.config.js进行了如下配置:

 server: {
    // host: '0.0.0.0', //只能使用localhost
    //处理ffmpeg的SharedArrayBuffer is not defined
    headers: {
      "Cross-Origin-Embedder-Policy": "require-corp",
      "Cross-Origin-Opener-Policy": "same-origin",
    },
  },

如果你的项目中使用了阿里云存储图片,两者会有冲突,同源隔离的错误,我没解决了。。。

其次这个案例中使用的ffmpeg.FS同时只能执行一次,我没有过多研究。。。不建议使用

fluent-ffmpeg

https://github.com/fluent-ffmpeg/node-fluent-ffmpeg

这是一个js库,ffmpeg阉割版,和直接使用命令行相比差很多,很多命令都不能实现。比如实现“图片转带有静音音频的视频”,“多视频合并”都不能正常实现。即使是最简单的“图片转视频”也可能会报错!不建议使用

ffmpeg-static

ffmpeg-static可以让哦们使用ffmpeg命令

安装

npm i ffmpeg-static

node环境下使用ffmpeg命令

electron/main/index.ts

import { exec } from "child_process";

const ffmpegInstaller = require("ffmpeg-static");
const command = `${ffmpegInstaller} -version`

exec(command, (error, stdout, stderr) => {
	if (error) {
      console.log(error);
    } else {
      console.log(stdout);
      console.log("------------------");
      console.log(stderr);
    }
})

electron + ffmpeg-static打包问题

当你的项目使用electron-builder打包后,你会发现ffmpeg-static不好使了,因为路径不对了。

如果你在打包后的项目控制台打印ffmpegInstaller(见上述代码)是这样:

// 为了观看不同处, 手动换行
C:\Usersl31957\AppData\Local\programs\k8-video-analysis-web\resources\
app.asar
\node _modules\ffmpeg-static\ffmpeg.exe

ffmpeg纯前端实现对视频的操作 - fluent-ffmpeg踩坑、ffmpeg-statci踩坑、视频合并、图片转视频_第1张图片

但实际路径是:

// 为了观看不同处, 手动换行
C:\Users\31957\AppData\Local\Programs\k8-video-analysis-web\resources\
app.asar.unpacked
\node_modules\ffprobe-static\ffmpeg.exe

我在网上看了很多文章,也有这个问题。在npm ffmpeg-static官网中给出的解决方案没看懂,其他文章给出的解决方案也不好使。直接用我的!

const ffmpegInstaller = require("ffmpeg-static");
// 直接替换
let ffmpegInstallerPath = ffmpegInstaller.replace("app.asar", "app.asar.unpacked");

就是手动更换一下
在本地项目中,是不会有resources这个文件的,所以只会在生产环境替换,如果你不放心也可以自己加一个判断

ffmpeg.exe

ffmpeg安装教程(windows版)

为了方便测试你的操作是否正确,可以将下载ffmpeg,在cmd中直接使用命令

ffmpeg纯前端实现对视频的操作 - fluent-ffmpeg踩坑、ffmpeg-statci踩坑、视频合并、图片转视频_第2张图片

将图片转为带有静音音频的视频

    const thisCommand = `${ffmpegInstaller} -loop 1 -i  "${inputPath}" -t ${videoTime} -f lavfi -i anullsrc=channel_layout=stereo:sample_rate=44100 -vf scale=1280:720 -c:v libx264 -pix_fmt yuv420p -y -c:a aac -shortest "${outputVideoName}.mp4"`;

ChatGPT生成的命令。说几个要注意的地方:

  • -vf scale=1280:720:设置视频分辨率。如果图片的宽高有奇数,那么转换会报错

    • -vf scale=640:-2  
      改变视频分辨率,缩放到640px宽,高度的-2是考虑到libx264要求高度是偶数,所以设置成-2,让软件自动计算得出一个接近等比例的偶数高
      
  • "inputPath":是一个本地绝对路径。可能你的路径是“C:\Users\xy\Pictures\Saved Pictures",其中“Saved Pictures”纯在空格,导致无法正确的找到路径,所以要加上引号

  • 使用URl:inputPath也可以是url,图片案例https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png。最开始我用的逻辑是将url保存到本地然后再转格式。保存这里除了使用ffmpeg也是使用其他方法比如curl。但是我没有在转换格式命令中尝试。

    • let saveLocalPath = `${ffmpegInstaller} -i ${inputPath} ${outputImgName}`;// 将图片保存到本地
      

视频合并

ffmpeg合并多个MP4视频

除了这篇文章写的,还有其他方法,可以参考 FFmpeg教程(超级详细版)或者直接阅读官网文档

ffmpeg中文网

我使用的是第一篇文章中的第三种:

直接将多个MP4直接拼接成一个MP4

1、将合并的文件夹都放在同一个文件夹下,例如:test.mp4,test2.mp4

2、新建一个video.txt文件,文件的内容是需要合并的视频名称,内容格式为

file C:\/Users\/xy\/Videos\/temp_81el8n.mp4
file 'C:\/Users\/xy\/Videos\/temp_81el8n.mp4'

3、执行ffmpeg的命令

ffmpeg -f concat -i video.txt -c copy concat.mp4

注意video.txt中文件的路径写法,对于win和mac文件路径的拼接是不同的

如果你在项目获取得的路径是C:\\Users\\xy\\Videos\\temp_81el8n.mp4或者C:\Users\sc\Videos\temp_81el8n.mp4,需要手动转换格式

  • win:'C:\/Users\/xy\/Videos\/temp_81el8n.mp4'
  • mac:'/Users/xy/Videos/temp_81el8n.mp4'

electron/main/index.ts

const cachePath = app.getPath("videos");

function selectDirectory(arg) {
  dialog
    .showSaveDialog({
      title: "保存视频",
      defaultPath: path.join(desktopDir, "视频.mp4"),
      buttonLabel: "保存",
      filters: [{ name: "Movies", extensions: ["mp4"] }],
    })
    .then((file) => {
      if (!file.canceled) {
        console.log(file.filePath, "pathpath"); // 选择的文件夹路径
        concatVideos(arg, file.filePath);
        // mergeVideos(arg, file.filePath);
      }
    })
    .catch((err) => {
      console.error(err);
    });
}
// 合并视频
function concatVideos(mergeList, concatPath) {
  let localUrl = mergeList.map((url) => {
    let outputName;
    // 注意路径拼接
    if (process.platform === "win32") {
       outputName = `${cachePath}\\temp_${Math.random()
        .toString(36)
        .substring(7)}.mp4`;
    } else {
       outputName = `${cachePath}/temp_${Math.random()
        .toString(36)
        .substring(7)}.mp4`;
    }
    let saveLocalPath = `${ffmpegInstallerPath} -i ${url} ${outputName}`;
    execSync(saveLocalPath, (error, stdout, stderr) => {
      if (error) {
        console.log(error);
      }
    });
    return outputName;
  });
  let videoCancatFileUrl;
  if (process.platform === "win32") {
    videoCancatFileUrl = cachePath + "\\videoCancatFile.txt";
  } else {
    videoCancatFileUrl = cachePath + "/videoCancatFile.txt";
  }

  let videoCancatFileWords = [];
  // 转换
  localUrl.forEach((fileUrl) => {
    /* win 和 mac 系统路径不同 
       win 获取到的路径为 C:\\Users\\sc\\Videos\\temp_81el8n.mp4
       ffmpeg 需要的为 C:\/Users\/sc\/Videos\/temp_81el8n.mp4

       mac 获取到的路径为 /Users/mmmddd/Movies\temp sc30ye.mp4
       ffmpeg 需要的为 /Users/mmmddd/Movies/temp sc30ye.mp4
    */
    let str;
    if (process.platform === "win32") {
      str = fileUrl.replace(/\\/g, "\\/");
    } else {
      str = fileUrl.replace("\\", "/");
    }
    videoCancatFileWords.push(`file '${str}'`);
  });
  console.log(videoCancatFileUrl, "333333333333");

  fs.writeFileSync(videoCancatFileUrl, videoCancatFileWords.join("\n"));

  const concatCommand = `${ffmpegInstallerPath} -f concat -safe 0 -i ${videoCancatFileUrl} -y -c copy ${concatPath}`;
  execSync(concatCommand, (error, stdout, stderr) => {
    if (error) {
      console.log(error);
    }
  });
  localUrl.forEach((file) => {
    fs.unlinkSync(file);
  });
  fs.unlinkSync(videoCancatFileUrl);
}

转换后的视频使用mac默认的播放器 QuickTime Player 会出现,建议更换播放器或者自己调试ffmpeg参数

electron 集成 ffmpeg

3-electron-builder-如何将ffmpe集成到Electron中

你可能感兴趣的:(前端实现某某功能,ffmpeg,前端,音视频,electron)