/*
* Copyright (c) 2021 Taner Sener
*
* This file is part of FFmpegKit.
*
* FFmpegKit is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FFmpegKit is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with FFmpegKit. If not, see .
*/
package com.arthenica.ffmpegkit;
import android.os.Build;
import com.arthenica.smartexception.java.Exceptions;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Locale;
/**
*
Responsible of loading native libraries.
*/
public class NativeLoader {
static final String[] FFMPEG_LIBRARIES = {"avutil", "swscale", "swresample", "avcodec", "avformat", "avfilter", "avdevice"};
static final String[] LIBRARIES_LINKED_WITH_CXX = {"chromaprint", "openh264", "rubberband", "snappy", "srt", "tesseract", "x265", "zimg"};
static boolean isTestModeDisabled() {
return (System.getProperty("enable.ffmpeg.kit.test.mode") == null);
}
private static void loadLibrary(final String libraryName) {
if (isTestModeDisabled()) {
try {
System.loadLibrary(libraryName);
} catch (final UnsatisfiedLinkError e) {
throw new Error(String.format("FFmpegKit failed to start on %s.", getDeviceDebugInformation()), e);
}
}
}
private static List loadExternalLibraries() {
if (isTestModeDisabled()) {
return Packages.getExternalLibraries();
} else {
return Collections.emptyList();
}
}
private static String loadNativeAbi() {
if (isTestModeDisabled()) {
return AbiDetect.getNativeAbi();
} else {
return Abi.ABI_X86_64.getName();
}
}
static String loadAbi() {
if (isTestModeDisabled()) {
return AbiDetect.getAbi();
} else {
return Abi.ABI_X86_64.getName();
}
}
static String loadPackageName() {
if (isTestModeDisabled()) {
return Packages.getPackageName();
} else {
return "test";
}
}
static String loadVersion() {
final String version = "6.0";
if (isTestModeDisabled()) {
return FFmpegKitConfig.getVersion();
} else if (loadIsLTSBuild()) {
return String.format("%s-lts", version);
} else {
return version;
}
}
static boolean loadIsLTSBuild() {
if (isTestModeDisabled()) {
return AbiDetect.isNativeLTSBuild();
} else {
return true;
}
}
static int loadLogLevel() {
if (isTestModeDisabled()) {
return FFmpegKitConfig.getNativeLogLevel();
} else {
return Level.AV_LOG_DEBUG.getValue();
}
}
static String loadBuildDate() {
if (isTestModeDisabled()) {
return FFmpegKitConfig.getBuildDate();
} else {
return new SimpleDateFormat("yyyyMMdd", Locale.getDefault()).format(new Date());
}
}
static void enableRedirection() {
if (isTestModeDisabled()) {
FFmpegKitConfig.enableRedirection();
}
}
static void loadFFmpegKitAbiDetect() {
loadLibrary("ffmpegkit_abidetect");
}
static boolean loadFFmpeg() {
boolean nativeFFmpegLoaded = false;
boolean nativeFFmpegTriedAndFailed = false;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
/* LOADING LINKED LIBRARIES MANUALLY ON API < 21 */
final List externalLibrariesEnabled = loadExternalLibraries();
for (String dependantLibrary : LIBRARIES_LINKED_WITH_CXX) {
if (externalLibrariesEnabled.contains(dependantLibrary)) {
loadLibrary("c++_shared");
break;
}
}
if (AbiDetect.ARM_V7A.equals(loadNativeAbi())) {
try {
for (String ffmpegLibrary : FFMPEG_LIBRARIES) {
loadLibrary(ffmpegLibrary + "_neon");
}
nativeFFmpegLoaded = true;
} catch (final Error e) {
android.util.Log.i(FFmpegKitConfig.TAG, String.format("NEON supported armeabi-v7a ffmpeg library not found. Loading default armeabi-v7a library.%s", Exceptions.getStackTraceString(e)));
nativeFFmpegTriedAndFailed = true;
}
}
if (!nativeFFmpegLoaded) {
for (String ffmpegLibrary : FFMPEG_LIBRARIES) {
loadLibrary(ffmpegLibrary);
}
}
}
return nativeFFmpegTriedAndFailed;
}
static void loadFFmpegKit(final boolean nativeFFmpegTriedAndFailed) {
boolean nativeFFmpegKitLoaded = false;
if (!nativeFFmpegTriedAndFailed && AbiDetect.ARM_V7A.equals(loadNativeAbi())) {
try {
/*
* THE TRY TO LOAD ARM-V7A-NEON FIRST. IF NOT LOAD DEFAULT ARM-V7A
*/
loadLibrary("ffmpegkit_armv7a_neon");
nativeFFmpegKitLoaded = true;
AbiDetect.setArmV7aNeonLoaded();
} catch (final Error e) {
android.util.Log.i(FFmpegKitConfig.TAG, String.format("NEON supported armeabi-v7a ffmpegkit library not found. Loading default armeabi-v7a library.%s", Exceptions.getStackTraceString(e)));
}
}
if (!nativeFFmpegKitLoaded) {
loadLibrary("ffmpegkit");
}
}
@SuppressWarnings("deprecation")
static String getDeviceDebugInformation() {
final StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("brand: ");
stringBuilder.append(Build.BRAND);
stringBuilder.append(", model: ");
stringBuilder.append(Build.MODEL);
stringBuilder.append(", device: ");
stringBuilder.append(Build.DEVICE);
stringBuilder.append(", api level: ");
stringBuilder.append(Build.VERSION.SDK_INT);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
stringBuilder.append(", abis: ");
stringBuilder.append(FFmpegKitConfig.argumentsToString(Build.SUPPORTED_ABIS));
stringBuilder.append(", 32bit abis: ");
stringBuilder.append(FFmpegKitConfig.argumentsToString(Build.SUPPORTED_32_BIT_ABIS));
stringBuilder.append(", 64bit abis: ");
stringBuilder.append(FFmpegKitConfig.argumentsToString(Build.SUPPORTED_64_BIT_ABIS));
} else {
stringBuilder.append(", cpu abis: ");
stringBuilder.append(Build.CPU_ABI);
stringBuilder.append(", cpu abi2s: ");
stringBuilder.append(Build.CPU_ABI2);
}
return stringBuilder.toString();
}
}