FFmpeg Flutter Kit 软件包已停用,预编译的二进制文件也已移除。这意味着开发者现在需要在本地编译 FFmpeg 二进制文件才能在 Flutter 应用中使用它们。在本指南中,我们将逐步讲解为 Android 和 iOS 平台编译 FFmpeg 所需的步骤,确保您能够继续在 Flutter 项目中使用 FFmpeg。
1. 克隆 FFmpeg Kit 仓库
首先,从 GitHub 克隆 FFmpeg Kit 的源代码。打开终端并运行:
git clone https://github.com/arthenica/ffmpeg-kit.git
2. 通过 Homebrew 安装依赖项
在编译二进制文件之前,我们需要使用 Homebrew 安装一些必要的依赖项。这些软件包将有助于构建 Android 和 iOS 的二进制文件。
安装pkg-config(用于管理库编译和链接标志)、libtool(用于管理共享库)、NASM(用于构建汇编代码)、Automake(用于自动生成 Makefile 文件)和Xcode 命令行工具(如果尚未安装,请接受 Xcode 许可证)。
brew install pkg-config
brew install libtool
brew install nasm
brew install automake
brew install autoconf
xcode-select --install
sudo xcodebuild -license accept
这些依赖项对于在 macOS 上成功编译 FFmpeg 至关重要。
安装依赖项后,导航到克隆 FFmpeg Kit 存储库的目录:
cd ffmpeg-kit
3. 编译 FFmpeg for iOS
要编译 FFmpeg for iOS,需要运行 ios.sh
脚本,它能自动执行编译过程。您可以根据自己的具体需求(如启用某些编解码器或库)自定义该命令,只需让 Chat GPT 或任何 AI 代理根据您的需求构建命令即可,但不要忘了在末尾加上 -xcframework,以确保输出是跨平台的框架格式
./ios.sh --enable-gpl --enable-x264 --target=12.1 --xcframework
运行脚本后,编译将在 prebuilt/bundle-apple-framework-ios
目录中生成必要的框架文件。编译完成后,确保 build.log 文件中没有错误。
4. 在 iOS 项目中添加框架
接下来,将 prebuilt/bundle-apple-framework-ios
中的框架文件复制到 ffmpeg-kit/flutter/flutter/ios/Frameworks 目录中的 iOS 项目中。
mkdir -p flutter/flutter/ios/Frameworks
cp -r prebuilt/bundle-apple-xcframework-ios/* flutter/flutter/ios/Frameworks/
然后,需要将生成的框架添加到您的 iOS 项目中。打开 ffmpeg-kit/flutter/flutter/ios/ffmpeg_kit_flutter.podspec 文件,在 subspec 部分添加以下一行:
//将 https 改为 precompiled-gpl(或任何您想要的名称)
s.default_subspec = 'precompiled-gpl'
//然后,添加以下部分
s.subspec 'precompiled-gpl' do |ss|
ss.vendored_frameworks = 'Frameworks/ffmpegkit.xcframework',
'Frameworks/libavcodec.xcframework',
'Frameworks/libavdevice.xcframework',
'Frameworks/libavfilter.xcframework',
'Frameworks/libavformat.xcframework',
'Frameworks/libavutil.xcframework',
'Frameworks/libswresample.xcframework',
'Frameworks/libswscale.xcframework'
end
此步骤确保iOS项目可以链接到已编译的FFmpeg库。
现在,我们已经完成了 IOS 部分。
5. 为Android编译FFmpeg
对于 Android,需要使用android.sh
脚本编译 FFmpeg。首先,请确保您的 Android NDK 和 SDK 已正确设置。如果尚未全局设置,您可以在终端中手动定义路径:
//注意:较高的NDK版本可能会失败
export ANDROID_NDK_ROOT= "/Users/ibrahimeltayfe/Library/Android/sdk/ndk/25.1.8937393"
export ANDROID_SDK_ROOT= "/Users/ibrahimeltayfe/Library/Android/sdk"
接下来,运行android.sh
脚本为 Android 编译 FFmpeg:
./android.sh --enable-gpl --enable-x264 --api-level=24
这将生成一个.aar
文件,它是包含已编译的 FFmpeg 二进制文件的 Android 库。
6. 将 FFmpeg .aar 集成到您的 Android 项目中
生成 .aar
文件后,将其移至 flutter/flutter/android/libs 目录。
mkdir -p flutter/flutter/android/libs
cp -r prebuilt/bundle-android-aar/ffmpeg-kit/ffmpeg-kit.aar flutter/flutter/android/libs/
您还需要加入其他必要的库:
1)下载以下 JAR 文件并将其放入 android/libs 目录:
smart-exception-java-0.2.1.jar
smart-exception-common-0.2.1.jar
您可以在这里找到它们:https://github.com/tanersener/smart-exception/releases/tag/v0.2.1
2)修改 build.gradle
文件,将 .aar 和 .jar 文件列为依赖项:
dependencies {
//注释此行
//implementation 'com.arthenica:ffmpeg-kit-https:6.0-2'
//添加此行
implementation fileTree(dir: 'libs', include: ['*.jar']) //'*.aar'
}
但这里有一个问题,我们不能在实现中直接包含 *.aar,因为它会引发错误:
What went wrong:
Execution failed for task ':ffmpeg_kit_flutter:bundleDebugAar'.
> Error while evaluating property 'hasLocalAarDeps' of task ':ffmpeg_kit_flutter:bundleDebugAar'.
> Direct local .aar file dependencies are not supported when building an AAR. The resulting AAR would be broken because the classes and Android resources from any local .aar file dependencies would not be packaged in the resulting AAR. Previous versions of the Android Gradle Plugin produce broken AARs in this case too (despite not throwing this error). The following direct local .aar file dependencies of the
因此,我们应该将 .aar 文件上传到远程 maven 存储库,或者创建一个本地 maven 存储库,我遇到了一个 fork,作者在其中创建了一个本地 Maven 存储库,因此我借用了他的代码:)
https://github.com/hellohejinyu/ffmpeg_kit_flutter_full_gpl/blob/main/android/build.gradle
这就是我现在的 build.gradle:
import java.security.MessageDigest
import java.security.NoSuchAlgorithmException
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.1.0'
}
}
String localMavenPath = project.mkdir("build").absolutePath
rootProject.allprojects {
repositories {
google()
mavenCentral()
maven { url "file://$localMavenPath" }
}
}
apply plugin: 'com.android.library'
android {
// Conditional for compatibility with AGP <4.2.
if (project.android.hasProperty("namespace")) {
namespace 'com.arthenica.ffmpegkit.flutter'
}
compileSdkVersion 33
defaultConfig {
minSdkVersion 24
targetSdkVersion 33
versionCode 603
versionName "6.0.3"
}
buildTypes {
release {
minifyEnabled false
}
}
lintOptions {
disable 'GradleCompatible'
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
task setupDependencies(type: Exec) {
workingDir '.'
commandLine 'sh', '-c', '../scripts/setup_android.sh'
onlyIf { !file('libs/com.arthenica.ffmpegkit-flutter-6.0.3.aar').exists() }
}
}
dependencies {
implementation 'androidx.annotation:annotation:1.5.0'
implementation fileTree(dir: 'libs', include: ['*.jar'])
}
String aarPath = localMavenPath
task useAar {
File file = project.file("libs")
if (file.exists() && file.isDirectory()) {
file.listFiles(new FileFilter() {
@Override
boolean accept(File pathname) {
return pathname.name.endsWith(".aar")
}
}).each { item ->
String aarName = item.name.substring(0, item.name.length() - 4)
String[] aarInfo = aarName.split("-")
String sha1 = getFileSha1(item)
String md5 = getFileMD5(item)
println("aar: " + aarInfo + " file sha1:" + sha1 + " md5:" + md5)
String fromStr = item.path
String intoStr = aarPath + "/" + aarInfo[0].replace(".", "/") + "/" + aarInfo[1] + "/" + aarInfo[2]
String newName = aarInfo[1] + "-" + aarInfo[2] + ".aar"
project.copy {
from fromStr
into intoStr
rename(item.name, newName)
}
project.file(intoStr + "/" + newName + ".md5").write(md5)
project.file(intoStr + "/" + newName + ".sha1").write(sha1)
String pomPath = intoStr + "/" + newName.substring(0, newName.length() - 4) + ".pom"
project.file(pomPath).write(createPomStr(aarInfo[0], aarInfo[1], aarInfo[2]))
project.file(pomPath + ".md5").write(getFileMD5(project.file(pomPath)))
project.file(pomPath + ".sha1").write(getFileSha1(project.file(pomPath)))
String metadataPath = project.file(intoStr).getParentFile().path + "/maven-metadata.xml"
project.file(metadataPath).write(createMetadataStr(aarInfo[0], aarInfo[1], aarInfo[2]))
project.file(metadataPath + ".md5").write(getFileMD5(project.file(metadataPath)))
project.file(metadataPath + ".sha1").write(getFileSha1(project.file(metadataPath)))
dependencies {
implementation "${aarInfo[0]}:${aarInfo[1]}:${aarInfo[2]}"
}
}
}
}
public static String createMetadataStr(String groupId, String artifactId, String version) {
return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<metadata>\n" +
" <groupId>$groupId</groupId>\n" +
" <artifactId>$artifactId</artifactId>\n" +
" <versioning>\n" +
" <release>$version</release>\n" +
" <versions>\n" +
" <version>$version</version>\n" +
" </versions>\n" +
" <lastUpdated>${new Date().format('yyyyMMdd')}000000</lastUpdated>\n" +
" </versioning>\n" +
"</metadata>\n"
}
public static String createPomStr(String groupId, String artifactId, String version) {
return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<project xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\" xmlns=\"http://maven.apache.org/POM/4.0.0\"\n" +
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n" +
" <modelVersion>4.0.0</modelVersion>\n" +
" <groupId>$groupId</groupId>\n" +
" <artifactId>$artifactId</artifactId>\n" +
" <version>$version</version>\n" +
" <packaging>aar</packaging>\n" +
"</project>\n"
}
public static String getFileSha1(File file) {
FileInputStream input = null;
try {
input = new FileInputStream(file);
MessageDigest digest = MessageDigest.getInstance("SHA-1");
byte[] buffer = new byte[1024 * 1024 * 10];
int len = 0;
while ((len = input.read(buffer)) > 0) {
digest.update(buffer, 0, len);
}
String sha1 = new BigInteger(1, digest.digest()).toString(16);
int length = 40 - sha1.length();
if (length > 0) {
for (int i = 0; i < length; i++) {
sha1 = "0" + sha1;
}
}
return sha1;
}
catch (IOException e) {
System.out.println(e);
}
catch (NoSuchAlgorithmException e) {
System.out.println(e);
}
finally {
try {
if (input != null) {
input.close();
}
}
catch (IOException e) {
System.out.println(e);
}
}
}
public static String getFileMD5(File file) {
FileInputStream input = null;
try {
input = new FileInputStream(file);
MessageDigest digest = MessageDigest.getInstance("MD5");
byte[] buffer = new byte[1024 * 1024 * 10];
int len = 0;
while ((len = input.read(buffer)) > 0) {
digest.update(buffer, 0, len);
}
String md5 = new BigInteger(1, digest.digest()).toString(16);
int length = 32 - md5.length();
if (length > 0) {
for (int i = 0; i < length; i++) {
md5 = "0" + md5;
}
}
return md5;
}
catch (IOException e) {
System.out.println(e);
}
catch (NoSuchAlgorithmException e) {
System.out.println(e);
}
finally {
try {
if (input != null) {
input.close();
}
}
catch (IOException e) {
System.out.println(e);
}
}
}
preBuild.dependsOn setupDependencies
useAar.dependsOn setupDependencies
preBuild.dependsOn useAar
不要忘记将 aar 文件的名称从libs/ffmpeg-kit.aar更改为libs/com.arthenica.ffmpegkit-flutter-6.0.3.aar
5. 解决 Flutter 和 FFmpeg Kit 与 Android 的兼容性问题
您会遇到的最后一个问题与 Flutter 在最新更新中取消了对 Embedded V1 的支持有关。
MSOB7YY 制作的 fork 修复了这一问题,只需将该 fork 中的更新代码替换 FFmpegKitFlutterPlugin.java 文件中的现有代码即可:
https://github.com/arthenica/ffmpeg-kit/blob/1d29b16bfca1baf03bf005cacebacdd0a225f731/flutter/flutter/android/src/main/java/com/arthenica/ffmpegkit/flutter/FFmpegKitFlutterPlugin.java
您可以在FFmpegKitFlutterPlugin.java以下位置找到您的路径:
flutter/flutter/android/src/main/java/com/arthenica/ffmpegkit/flutter/FFmpegKitFlutterPlugin.java
就是这样!要将 ffmpeg-kit 集成到您的项目中,只需在 pubspec.yaml 中添加以下一行即可:
dependencies:
ffmpeg_kit_flutter:
path: path-to-your-ffmpegkit-package/flutter/flutter
此外,确保 Android minSdkVersion
设置为 24,iOS deployment target
设置为 12.1 或更高版本。
最后,跟踪和关注正在进行的修复
如果要想避免未来出现令人头疼的问题,那么关注社区的持续更新和修复是个好主意。有很多贡献者会创建分支并提供常见问题的解决方案。
您可以在此处跟踪这些贡献和正在进行的修复:GitHub 上的 FFmpeg Kit 问题
作者:Ibrahimeltayfe
本文来自作者投稿,版权归原作者所有。如需转载,请注明出处:https://www.nxrte.com/jishu/57555.html