首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

  • 24-11-26 09:05
  • 3964
  • 15355
juejin.cn

最近在写一个自动匹配弹幕的动漫播放器,里面需要使用 FFmpeg 对视频进行解析,但发现如何根据不同的平台打包不同 FFmpeg 到 Electron 里,是个挺麻烦的问题,这篇文章就来讲述下我的解决思路。

安装

用户的电脑很有可能没有安装 FFmpeg,所以我们需要把 FFmpeg 打包进我们的应用里面。

想要在 Electron 开发环境里面导入 FFmpeg 还是比较简单的,只需要安装下面的包,然后就能够在 Electron 中使用了。

bash
代码解读
复制代码
pnpm add -D @ffmpeg-installer/ffmpeg @ffprobe-installer/ffprobe fluent-ffmpeg

创建 ffmpeg.ts,具体使用方式可以阅读 fluent-ffmpeg。

typescript
代码解读
复制代码
import ffmpegPath from '@ffmpeg-installer/ffmpeg' // 安装 ffmpeg 的二进制文件 import ffprobePath from '@ffprobe-installer/ffprobe' // 安装 ffprobe 的二进制文件 import ffmpeg from 'fluent-ffmpeg' // 一个封装了 ffmpeg API 的库,当然可以选择不安装,直接使用字符串拼接的方式调用 ffmpeg.setFfmpegPath(ffmpegPath.path) ffmpeg.setFfprobePath(ffprobePath.path) export default class FFmpeg { ffmpeg: ffmpeg.FfmpegCommand constructor(inputPath: string) { this.ffmpeg = ffmpeg(inputPath) } }

之后我们在开发环境里面就能正常使用 FFmpeg 了。

打包

路径问题

我的项目是使用 electron builder 进行打包的(具体的 electron-builder.yml配置可以在 Electron 代码签名和公证 中查看),打包之后你会发现项目是无法正确使用 FFmpeg,但在 dev 环境下到是正常的。

这是因为 ffmpeg 是二进制文件,会被打包进 app.asar.unpacked 而非 app.asar 从而导致 setFfmpegPath 路径出现问题,所以修改对应的 path 即可,这个问题在 @ffmpeg-installer/ffmpeg 中也有提到。

typescript
代码解读
复制代码
import ffmpegPath from '@ffmpeg-installer/ffmpeg' import ffprobePath from '@ffprobe-installer/ffprobe' import ffmpeg from 'fluent-ffmpeg' ffmpeg.setFfmpegPath(ffmpegPath.path.replace('app.asar', 'app.asar.unpacked')) // 修改 ffmpeg.setFfprobePath(ffprobePath.path.replace('app.asar', 'app.asar.unpacked')) // 修改 export default class FFmpeg { ffmpeg: ffmpeg.FfmpegCommand constructor(inputPath: string) { this.ffmpeg = ffmpeg(inputPath) } }

我使用的电脑是 MacBook Pro M1 Pro 即 macOS ARM64

打包完成之后,此时运行 ARM64 版本的 .app 是没有问题的,FFmpeg 也能正确运行。

FFmpeg 的架构版本问题

启动报错

但可不要高兴的太早,我们换一台运行 macOS x64 的电脑,运行刚才用 macOS ARM64 电脑打包出来的 x64 版本的 .app 就会直接报错,然后显示一个完全摸不到头脑的错误。

启动报错

原因分析

我一开始看到这个错误也是完全懵逼的,使用 debugtron 对主线程进行调试也完全没有输出。之后尝试对包进行分析,发现 x64 版本的 .app 打包的 FFmpeg 竟然是 ARM64 版本的,这不报错才怪呢。

x64版本安装包

这里我便对 @ffmpeg-installer/ffmpeg的实现感到了好奇,他是如何匹配不同的平台,从而安装对应其平台的 FFmpeg 二进制文件。通过阅读其源码:

json
代码解读
复制代码
{ "name": "@ffmpeg-installer/ffmpeg", "optionalDependencies": { "@ffmpeg-installer/darwin-arm64": "4.1.5", "@ffmpeg-installer/darwin-x64": "4.1.0", "@ffmpeg-installer/linux-arm": "4.1.3", "@ffmpeg-installer/linux-arm64": "4.1.4", "@ffmpeg-installer/linux-ia32": "4.1.0", "@ffmpeg-installer/linux-x64": "4.1.0", "@ffmpeg-installer/win32-ia32": "4.1.0", "@ffmpeg-installer/win32-x64": "4.1.0" } }
json
代码解读
复制代码
{ "name": "@ffmpeg-installer/darwin-x64", "os": [ "darwin" ], "cpu": [ "x64" ], }

发现@ffmpeg-installer/ffmpeg 封装了多个平台 FFmpeg 依赖,然后放入 optionalDependencies 中。每个平台的 FFmpeg 包再通过设置 cpu + os 字段,从而实现用户安装 @ffmpeg-installer/ffmpeg 即可匹配用户系统,来安装对应的 ffmpeg,这也让我涨知识了。

因此也难怪 x64 版本的 .app 打包的 FFmpeg 是 ARM64 版本的,因为我们在最开始 pnpm install 的时候,就只安装了对应操作系统的 FFmpeg,build 的时候也只能打包当前安装的 FFmpeg。

举个例子,我是 ARM64 macOS, pnpm install 的时候只会安装 ARM 版本 FFmpeg,打包 x64 的时候,当然也只能打包 ARM 版本 FFmpeg 了,从而导致的错误。

node_modules目录

整理思路

那么我们的思路就很明确了:

  • ARM64 macOS -> 打包 ARM64 应用 -> 使用 ARM64 FFmpeg
  • ARM64 macOS -> 打包 x64 应用 -> 使用 x64 FFmpeg

同理:

  • x64 macOS -> 打包 ARM64 应用 -> 使用 ARM64 FFmpeg
  • x64 macOS -> 打包 x64 应用 -> 使用 x64 FFmpeg

那么如何实现呢?

这里思路完全是自己想的,或许有更好的方法,也请多多指教。

说一下我的思路,首先我们在 pnpm install 的时候只安装当前操作系统的 FFmpeg 是不变的,这样可以节约我们电脑的空间和安装依赖的速度。

之后只需要在执行 pnpm build:mac的时候,执行一个安装 mac 平台全部架构的 FFmpeg 依赖脚本就可以了。

解决问题

编写 scripts/install-darwin-deps.js

javascript
代码解读
复制代码
/* eslint-disable no-console */ import { exec } from 'node:child_process' import os from 'node:os' const platform = os.platform() if (platform === 'darwin') { console.log('Detected macOS, installing darwin dependencies...') // 为了在 macos arm64 架构下进行打包 x64 架构的 APP, 所以需要同时安装 x64 arm64 架构的 ffmpeg 和 ffprobe exec( 'pnpm i @ffmpeg-installer/darwin-x64@^4.1.0 @ffprobe-installer/darwin-x64@^5.1.0 @ffmpeg-installer/darwin-arm64@^4.1.5 @ffprobe-installer/darwin-arm64@^5.0.1 -D', (err, stdout, stderr) => { if (err) { console.error(`Error installing optional dependencies: ${stderr}`) throw new Error('Error installing optional dependencies') } else { console.log(`Optional dependencies installed: ${stdout}`) } }, ) } else { console.log('Non-macOS platform detected, skipping optional darwin installation.') }

之后在 package.json 里面加上 "build:mac": "node scripts/install-darwin-deps.js && electron-vite build && electron-builder --mac --publish never" 即可。

执行打包命令之后,macOS x64 也是正确运行 macOS ARM64 打包出来的 x64 版本的 xxx.app ,不再会出现之前那个摸不着头脑报错了。

不同平台只打包对应的 FFmpeg

这里新的问题有又出现了,我们发现当前 xxx.dmg 包体积大了很多,那是因为所有平台的 FFmpeg 都被打包进去了。例如,ARM64 版本 xxx.app 把 x64 和 ARM64 FFmpeg 都打包进去了。

ARM64 版本 xxx.app 把 x64 和 ARM64 FFmpeg 都打包进去了

这里我们得写一个脚本,在 electron builder 打包之后,把与目标平台不相符的 FFmpeg 给删除掉,编写 scripts/cleaned-unused-arch-deps.js

javascript
代码解读
复制代码
/* eslint-disable no-console */ import fs from 'node:fs' import path from 'node:path' export default async function cleanDeps(context) { const { packager, arch, appOutDir } = context const platform = packager.platform.nodeName if (platform !== 'darwin') { return } const archMap = { 1: 'x64', 3: 'arm64', } const currentArch = archMap[arch] if (!currentArch) { return } const unpackedPath = path.resolve( appOutDir, 'Marchen.app', 'Contents', 'Resources', 'app.asar.unpacked', 'node_modules', ) if (!fs.existsSync(unpackedPath)) { return } const ffmpegPath = path.resolve(unpackedPath, '@ffmpeg-installer') const ffprobePath = path.resolve(unpackedPath, '@ffprobe-installer') if (!fs.existsSync(ffmpegPath) || !fs.existsSync(ffprobePath)) { return } const removeUnusedArch = (basePath, unusedArch) => { const unusedPath = path.resolve(basePath, `darwin-${unusedArch}`) if (fs.existsSync(unusedPath)) { fs.rmSync(unusedPath, { recursive: true }) } } if (currentArch === 'x64') { removeUnusedArch(ffmpegPath, 'arm64') removeUnusedArch(ffprobePath, 'arm64') } else if (currentArch === 'arm64') { removeUnusedArch(ffmpegPath, 'x64') removeUnusedArch(ffprobePath, 'x64') } console.log('Cleaned unused arch dependencies.') }

之后在 electron-builder.yml 里面使用 afterPack: scripts/cleaned-unused-arch-deps.js 导入脚本。

然后执行 pnpm build:mac就实现了不同平台只打包对应的 FFmpeg,并且运行都正常了。查看包内容,发现确实只包含了目标平台的 FFmpeg。

不同平台只打包对应的 FFmpeg

注:本文转载自juejin.cn的suemor的文章"https://juejin.cn/post/7440468601641664564"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

104
前端
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top