- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我在互联网上看到很多关于如何减少 Android 中的 FFmpeg/FFprobe
库大小的帖子。我也对这个话题进行了大约几个月的调查。所以我决定分享我的结论。我不是在谈论编译时设置和禁用/启用功能。当然你可以这样做以获得更好的结果,但这篇文章不是关于那个的。它是关于归档 FFmpeg/FFprobe 编译文件,这将将您的库大小减少 85%。
最佳答案
基于 FFMpeg/FFprobe compiled for Android
1. 将 Assets 文件(ffmpeg && ffprobe)
压缩为任何存档格式,并替换为当前文件。
2. 使用一些额外的代码,使用asynctask
复制应用目录中的存档文件,
3. 使用 this 提取存档库(还支持 rar
存档)。
结果:79.8MB ==> 13.2MB
提取时间:1 秒(Honor 8 Lite API 25/26。)。
我正在使用此设置进行压缩:
Windows 10 64 位,
7-Zip
应用程序,
.7z
存档格式,
Ultra
压缩级别,
LZMA2
压缩方式。
您可以尝试其他压缩方式和归档格式来测试不同的结果。
将存档复制/提取到应用程序目录所需的代码:
将此添加到您想要初始化复制过程的任何位置(我更喜欢在 Application
类的 onCreate
中)。
FFmpegArchiveUtil.initFFmpegBinary(this, new FFmpegArchiveUtil.FFmpegSupportCallback() {
@Override
public void isFFmpegSupported(boolean isSupported) {
Log.d("isSupported: " , String.valueOf(isSupported));
}
});
上面的方法会处理这个函数:
将存档从 Assets 复制到应用程序目录。
检查 CPU 架构模型。
根据 CPU 型号提取存档。
提取后删除存档。
使文件可执行。
如果设备是否支持 ffmpeg,则返回 true/false。
完整代码:
添加到build.gradle:
android {
defaultConfig {
ndk {
abiFilters 'x86' , 'arm64-v8a' , 'armeabi-v7a'
}
}
}
dependencies {
implementation 'com.hzy:libp7zip:1.6.0'
implementation 'commons-io:commons-io:2.6'
}
FFmpegArchiveUtil.java
import android.content.Context;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Build;
import android.util.Log;
import com.hzy.libp7zip.P7ZipApi;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@SuppressWarnings({"unused", "WeakerAccess"})
public class FFmpegArchiveUtil {
public static final int VERSION = 17; // up this version when you add a new ffmpeg build
public static final String KEY_PREF_VERSION = "ffmpeg_version";
public static final String FFMPEG_ARCHIVE = "ffmpeg_arch.7z";
public static final String FFMPEG_FILE_NAME = "ffmpeg";
public static final String FFPROBE_FILE_NAME = "ffprobe";
public static File getFFmpegArchive(Context context) {
return new File(context.getFilesDir(), FFMPEG_ARCHIVE);
}
public static File getFFmpegFile(Context context) {
return new File(context.getFilesDir(), FFMPEG_FILE_NAME);
}
public static File getFFprobe(Context context) {
return new File(context.getFilesDir(), FFPROBE_FILE_NAME);
}
public interface FFmpegSupportCallback {
void isFFmpegSupported(boolean isSupported);
}
private static class FFmpegArchiveCopyTask extends AsyncTask<Void, Void, Boolean> {
InputStream stream;
File ffmpegArchive;
FFmpegSupportCallback callback;
FFmpegArchiveCopyTask(InputStream stream, File file, FFmpegSupportCallback callback) {
this.ffmpegArchive = file;
this.stream = stream;
this.callback = callback;
}
@Override
protected Boolean doInBackground(Void... params) {
File ffmpegFile = new File(FilenameUtils.getFullPath(ffmpegArchive.getAbsolutePath()) + FFMPEG_FILE_NAME);
if (ffmpegFile.exists()) {
return true;
} else {
if (ffmpegArchive.exists()) {
return true;
} else {
try {
FileUtils.copyToFile(stream, ffmpegArchive);
return true;
} catch (Exception e) {
return false;
}
}
}
}
@Override
protected void onPostExecute(Boolean result) {
try {
FFmpegExtractorAsyncTask fFmpegExtractorAsyncTask = new FFmpegExtractorAsyncTask(stream, ffmpegArchive, callback);
fFmpegExtractorAsyncTask.execute();
} catch (Exception e) {
e.printStackTrace();
}
}
}
@SuppressWarnings("DanglingJavadoc")
public static class FFmpegExtractorAsyncTask extends AsyncTask<Void, Void, Boolean> {
InputStream stream;
/**
String archiveFormat = {@link FFMPEG_ARCHIVE}
*/
/**
* The outPut path for copying archiveFormat
* /data/user/0/com.symphonyrecords.mediacomp/files/
*/
String bb;
/**
* FFmpeg archive file
* /data/user/0/com.symphonyrecords.mediacomp/files/archiveFormat
*/
File ffmpegArchive;
/**
* The main ffmpeg file
* /data/user/0/com.symphonyrecords.mediacomp/files/ffmpeg
*/
File ffmpegFile;
FFmpegSupportCallback callback;
FFmpegExtractorAsyncTask(InputStream stream, File file, FFmpegSupportCallback callback) {
this.ffmpegArchive = file;
this.stream = stream;
this.callback = callback;
bb = FilenameUtils.getFullPath(ffmpegArchive.getAbsolutePath());
ffmpegFile = new File(bb + FFMPEG_FILE_NAME);
}
@Override
protected Boolean doInBackground(Void... params) {
if (ffmpegFile.exists()) {
return true;
} else {
if (ffmpegArchive.exists()) {
try {
String cmd = extractCmd(ffmpegArchive.getAbsolutePath(), bb);
P7ZipApi.executeCommand(cmd);
return true;
} catch (Throwable e) {
e.printStackTrace();
return false;
}
} else {
try {
FileUtils.copyToFile(stream, ffmpegArchive);
} catch (Exception ignored) {
}
}
}
return false;
}
@Override
protected void onPostExecute(Boolean isSuccess) {
super.onPostExecute(isSuccess);
if (isSuccess) {
if (ffmpegFile.exists()) {
if (ffmpegArchive.exists()) {
deleteFile(ffmpegArchive.getAbsolutePath());
}
Log.d("onPostExecute", "ffmpegFile.exists()");
if (makeFileExecutable(ffmpegFile)) {
callback.isFFmpegSupported(true);
Log.d("onPostExecute", "makeFileExecutable Successful");
} else {
callback.isFFmpegSupported(false);
Log.d("onPostExecute", "makeFileExecutable Failed Again");
}
} else {
callback.isFFmpegSupported(false);
Log.d("onPostExecute", "!ffmpegFile.exists()");
}
Log.d("onPostExecuteResult", "Successful");
} else {
Log.d("onPostExecute", "NotSuccessful");
callback.isFFmpegSupported(false);
}
}
}
/**
* Copying FFMPEG binary to application directory////////////
*/
public static void initFFmpegBinary(Context context, FFmpegSupportCallback callback) {
try {
File f = getFFmpegFile(context);
if (f.exists() && f.canExecute()) {
callback.isFFmpegSupported(true);
} else {
extractFFMPEG(context, callback);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static void extractFFMPEG(Context context, FFmpegSupportCallback callback) {
if (CpuArchHelper.cpuNotSupported()) {
callback.isFFmpegSupported(false);
return;
}
// Copy Archive To App Dir
SharedPreferences settings = context.getSharedPreferences("ffmpeg_prefs", Context.MODE_PRIVATE);
int version = settings.getInt(KEY_PREF_VERSION, 0);
// check if ffmpeg file exists
if (getFFmpegFile(context).exists() && version >= VERSION) {
callback.isFFmpegSupported(true);
} else {
try {
Log.d("extractFFMPEG", "FFmpeg Binary does not exist, initializing copy process...");
InputStream stream = context.getAssets().open(FFMPEG_ARCHIVE);
FFmpegArchiveCopyTask fFmpegArchiveCopyTask = new FFmpegArchiveCopyTask(stream, getFFmpegArchive(context), callback);
fFmpegArchiveCopyTask.execute();
settings.edit().putInt(KEY_PREF_VERSION, VERSION).apply();
} catch (Exception e) {
Log.e("extractFFMPEG", "error while opening assets", e);
callback.isFFmpegSupported(false);
}
}
}
public static boolean makeFileExecutable(File file) {
if (!file.canExecute()) {
// try to make executable
try {
try {
Runtime.getRuntime().exec("chmod -R 777 " + file.getAbsolutePath()).waitFor();
} catch (InterruptedException e) {
Log.e("makeFileExecutable", "interrupted exception", e);
return false;
} catch (IOException e) {
Log.e("makeFileExecutable", "io exception", e);
return false;
}
if (!file.canExecute()) {
if (!file.setExecutable(true)) {
Log.e("makeFileExecutable", "unable to make executable");
return false;
}
}
} catch (SecurityException e) {
Log.e("makeFileExecutable", "security exception", e);
return false;
}
}
return file.canExecute();
}
private static void deleteFile(String fileName) {
try {
File file = new File(fileName);
if (FileUtils.deleteQuietly(file))
System.out.println(file.getName() + " is deleted!");
else {
if (file.delete()) {
System.out.println(file.getName() + " is deleted!");
} else {
try {
FileUtils.forceDelete(file);
} catch (Exception e) {
e.printStackTrace();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void createPath(File path) {
try {
FileUtils.forceMkdir(path);
} catch (Exception e) {
e.printStackTrace();
}
}
private static final String P7Z = "7z";
/**
* Command Description
* a Add
* b Benchmark
* d Delete
* e Extract
* h Hash
* i Show information about supported formats
* l List
* rn Rename
* t Test
* u Update
* x eXtract with full paths
*/
private static final String CMD_ADD = "a";
private static final String CMD_BENCHMARK = "b";
private static final String CMD_DELETE = "d";
private static final String CMD_EXTRACT = "e";
private static final String CMD_HASH = "h";
private static final String CMD_INFO = "i";
private static final String CMD_LIST = "l";
private static final String CMD_RENAME = "rn";
private static final String CMD_TEST = "t";
private static final String CMD_UPDATE = "u";
private static final String CMD_EXTRACT1 = "x";
/**
* Switch Description
* -i Include filenames
* -m Set Compression Method
* -o Set Output directory
* -p Set Password
* -t Type of archive
* -u Update options
* -x Exclude filenames
* -y Assume Yes on all queries
*/
private static final String SWH_INCLUDE = "-i";
private static final String SWH_METHOD = "-m";
private static final String SWH_OUTPUT = "-o";
private static final String SWH_PASSWORD = "-p";
private static final String SWH_TYPE = "-t";
private static final String SWH_UPDATE = "-u";
private static final String SWH_EXCLUDE = "-x";
private static final String SWH_YES = "-y";
public static String compressCmd(String filePath, String outPath, String type) {
return String.format("7z a -t%s '%s' '%s'", type, outPath, filePath);
}
public static String extractCmd(String archivePath, String outPath) {
return String.format("%s %s '%s' '%s%s' '%s' -aoa", P7Z, CMD_EXTRACT, archivePath, SWH_OUTPUT, outPath, CpuArchHelper.getCpuArchiveFolder());
}
// public static String extractCmd(String archivePath, String outPath) {
// return String.format("%s %s '%s' '%s%s' -aoa", P7Z, CMD_EXTRACT1,archivePath, SWH_OUTPUT, outPath);
// }
public static class CpuArchHelper {
//// --------- x86 Cpu ABI ----------- ////
private static final String X86_CPU = "x86";
private static final String X86_64_CPU = "x86_64";
//// --------- ARM Cpu ABI ----------- ////
private static final String ARM_ABI = "armeabi";
private static final String ARM_V7_CPU = "armeabi-v7a";
private static final String ARM_64_CPU = "arm64-v8a";
//// --------- MIPS Cpu ABI ----------- ////
private static final String MIPS_ABI = "mips";
private static final String MIPS64_ABI = "mips64";
public enum CpuArch {
ARMv7, x86, NONE
}
public static CpuArch getCpuArch() {
String cpu = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? Build.SUPPORTED_ABIS[0] : Build.CPU_ABI;
Log.d("Device_Cpu: ", cpu);
switch (cpu) {
case X86_CPU:
case X86_64_CPU:
return CpuArch.x86;
case ARM_ABI:
case ARM_V7_CPU:
case ARM_64_CPU:
return CpuArch.ARMv7;
case MIPS_ABI:
case MIPS64_ABI:
return CpuArch.NONE;
default:
return CpuArch.NONE;
}
}
public static boolean cpuNotSupported() {
return getCpuArch() == CpuArch.NONE;
}
public static String getCpuArchiveFolder() {
switch (getCpuArch()) {
case ARMv7:
return "arm";
case x86:
return "x86";
case NONE:
return "arm";
default:
return "arm";
}
}
}
}
关于android - 通过在 Android 中归档 ffmpeg/ffprobe 库来减小应用程序大小,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56862755/
Java 库和 android 库有什么区别,各自有什么优点/缺点? 最佳答案 您可以在 Android 应用程序中包含标准 Java .jar 文件库。它们在 .apk 构建时被翻译成 Dalvik
所以,我现在的代码就像从 Java 层加载库(比如 liba.so),并在内部 liba.so 加载 libb.so。因此,如果我必须将所有库打包到 APK 中并将其安装在没有 root 访问权限的设
我想在我的系统中设置 LEDA 库。 我已经从以下链接下载了 LEDA 库 http://www.algorithmic-solutions.info/free/d5.php Instruct
我想用 autoconf 创建一个共享库。但是,我希望共享库具有“.so”扩展名,而不是以“lib”开头。基本上,我想制作一个加载 dlopen 的插件。 .是否有捷径可寻? 当我尝试使用 autoc
我需要在 Apps 脚本应用程序上修改 PDF。为此,我想使用 JS 库:PDF-LIB 我的代码: eval(UrlFetchApp.fetch("https://unpkg.com/pdf-lib
我正在构建一个使用以下 Boost header 的程序(我使用的是 Microsoft Visual C++ 10), #include #include #include #include
当我通过 cygwin 在 hadoop 上运行此命令时: $bin/hadoop jar hadoop-examples-*.jar grep input output 'dfs[a-z.]+' 我
我已经通过 vcpgk 成功安装了一个 C++ 库,名为:lmdb:x64-windows 我还安装了lmdb通过 Cabal 安装的 Haskell 绑定(bind)包 在尝试测试 lmdb 包时:
我该如何解决这个问题? 我刚刚将 javacv jar 文件复制到我的项目 Lib 文件夹下,但出现了这个错误! 我可以找到这个thread来自谷歌,但不幸的是,由于我国的谷歌限制政策,该页面无法打开
我有一个 Android 库项目 FooLib。 FooLib 引用 Android Context 之类的东西,但不需要任何资源文件(res/ 中的东西)所以我目前将其打包为供我的应用使用的 JAR
我正在开发一个 Android 应用程序(使用 Android Studio),它能够通过手势识别算法了解您正在进行的 Activity 。对于我使用 nickgillian ithub 帐户上可用的
关于从 .NET Framework 项目中引用 .NET Standard 类库的问题有很多类似的问题,其中 netstandard 库中的 NuGet 包依赖项不会流向 netframework
我已经从互联网上下载了 jna-4.2.2.jar,现在想将这个 jar 导入到我的项目中。但是当我试图将这个 jar 导入我的项目时,出现以下错误。 [2016-06-20 09:35:01 - F
我正在尝试通过编译在 Mac 上安装 rsync 3.2.3。但是,我想安装所有功能。为此,它需要一些库,此处 ( https://download.samba.org/pub/rsync/INSTA
进入 Web 开发有点困难。过去 5 年我一直致力于 winforms 工作。所以我正在努力从一种切换到另一种。前段时间,我使用过 JavaScript,但现在还没有大量的 JavaScript 库
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 1
我正在寻找一个用Python编写的与logstash(ruby + java)类似的工具/库。 我的目标是: 从 syslog 中解析所有系统日志 解析应用程序特定日志(apache、django、m
就目前情况而言,这个问题不太适合我们的问答形式。我们希望答案得到事实、引用资料或专业知识的支持,但这个问题可能会引发辩论、争论、民意调查或扩展讨论。如果您觉得这个问题可以改进并可能重新开放,visit
我花了几天时间试图寻找用于 JavaPOS 实现的 .jar 库,但我找不到任何可以工作的东西。我找到了很多像这样的文档:http://jpos.1045706.n5.nabble.com/file/
这个问题在这里已经有了答案: Merge multiple .so shared libraries (2 个答案) 关闭 9 年前。 我有我在代码中使用的第三方库的源代码和对象。该库附带有关如何使
我是一名优秀的程序员,十分优秀!