所以我需要在android中使用ffmpeg。 (用于复用音频和视频)。 我找到了android studio的这个很棒的项目(链接在帖子的末尾,在rar中为ffmpeg4android_demo_studio2)。 我工作得很完美,复杂和整体令人敬畏的项目。所以我决定从该项目为anroid制作插件,所以我重新制作了这样的项目:(链接在帖子的末尾,在rar中为ffmpeg4android_demo_studio((pS是没有GeneralUtils代码,因为它的大,请下载rar并打开它。))
package com.netcompss.ffmpeg4android;
public class CommandValidationException extends Exception {
private static final long serialVersionUID = 1L;
}
=============================================
package com.netcompss.ffmpeg4android;
import android.content.Context;
import android.util.Log;
import android.widget.Toast;
public class FFMpeg {
private Context context;
private static FFMpeg instance;
public FFMpeg (){
this.instance = this;
}
public static FFMpeg instance(){
if(instance == null){
instance = new FFMpeg();
}
return instance;
}
public void setContext(Context context){
this.context = context;
}
public void mux(String video,String audio,String out){
//GeneralUtils.checkForPermissionsMAndAbove(currentActivity, true);
LoadJNI vk = new LoadJNI();
try {
String workFolder = context.getFilesDir().getAbsolutePath()+ "/";
String cmd = "ffmpeg -i "+video+" -i "+audio+" -c copy -map 0:v:0 -map 1:a:0 -shortest "+out;
vk.run(GeneralUtils.utilConvertToComplex(cmd) , workFolder , context);
Log.i("test", "ffmpeg4android finished successfully");
} catch (Throwable e) {
Log.e("test", "vk run exception.", e);
}
}
public void showMessage(String message){
Toast.makeText(this.context,message,Toast.LENGTH_SHORT).show();
}
}
==================
包com.netcompss.ffmpeg4android;
import android.app.Activity;
import android.content.Context;
import android.widget.TextView;
import android.os.Bundle;
public class LicenseCheckJNI
{
public int licenseCheck(String path, Context ctx) {
String rcStr = "-100";
rcStr = licenseCheckComplexJNI(path);
int rc =Integer.decode(rcStr);
return rc;
}
public native String licenseCheckComplexJNI(String path);
public native String licenseCheckSimpleJNI(String path);
static {
System.loadLibrary("license-jni");
}
}
package com.netcompss.ffmpeg4android;
import java.io.File;
import android.content.Context;
import android.nfc.Tag;
import android.util.Log;
public final class LoadJNI {
static {
System.loadLibrary("loader-jni");
System.loadLibrary("license-jni");
System.loadLibrary("videokit");
}
/**
*
* @param args ffmpeg command
* @param workFolder working directory
* @param ctx Android context
* @param isValidate apply validation to the command
* @throws CommandValidationException
*/
public void run(String[] args, String workFolder, Context ctx, boolean isValidate) throws CommandValidationException {
Log.i(Prefs.TAG, "running ffmpeg4android_lib: " + Prefs.version);
// delete previous log: this is essential for correct progress calculation
String vkLogPath = workFolder + "vk.log";
GeneralUtils.deleteFileUtil(vkLogPath);
GeneralUtils.printCommand(args);
//printInternalDirStructure(ctx);
if (isValidate) {
if (GeneralUtils.isValidCommand(args)) {
Log.d(Prefs.TAG, "=LOAD================");
load(args, workFolder, getVideokitLibPath(ctx), true);
}
else
throw new CommandValidationException();
}
else {
Log.d(Prefs.TAG, "=LOAD================");
load(args, workFolder, getVideokitLibPath(ctx), true);
}
}
/**
*
* @param args ffmpeg command
* @param workFolder working directory
* @param ctx Android context
* @throws CommandValidationException
*/
public void run(String[] args, String workFolder, Context ctx) throws CommandValidationException {
run(args, workFolder, ctx, true);
}
private static void printInternalDirStructure(Context ctx) {
Log.d(Prefs.TAG, "=printInternalDirStructure=");
Log.d(Prefs.TAG, "==============================");
File file = new File(ctx.getFilesDir().getParent());
analyzeDir(file);
Log.d(Prefs.TAG, "==============================");
}
private static void analyzeDir(File path) {
if (path.isDirectory()) {
Log.d(Prefs.TAG,"Scanning dir: " + path.getAbsolutePath());
File[] files1 = path.listFiles();
for (int i = 0; i < files1.length; i++) {
analyzeDir(files1[i]);
}
Log.d(Prefs.TAG, "==========");
}
else {
Log.d(Prefs.TAG, path.getAbsolutePath());
}
}
private static String getVideokitLibPath(Context ctx) {
//File file = new File(ctx.getFilesDir().getParent() + "/lib/");
//analyzeDir(file);
String videokitLibPath = ctx.getFilesDir().getParent() + "/lib/libvideokit.so";
File file = new File(videokitLibPath);
if(file.exists()) {
Log.i(Prefs.TAG, "videokitLibPath exits");
}
else {
Log.w(Prefs.TAG, "videokitLibPath not exits: " + videokitLibPath);
videokitLibPath = ctx.getFilesDir().getParent() + "/lib/arm64/libvideokit.so";
Log.i(Prefs.TAG, "trying videokitLibPath: " + videokitLibPath);
file = new File(videokitLibPath);
if(file.exists()) {
Log.i(Prefs.TAG, "videokitLibPath exits: " + videokitLibPath);
}
else {
Log.w(Prefs.TAG, "videokitLibPath not exits: " + videokitLibPath);
videokitLibPath = "/data/app/com.examples.ffmpeg4android_demo-1/lib/arm64/libvideokit.so";
Log.i(Prefs.TAG, "trying videokitLibPath: " + videokitLibPath);
file = new File(videokitLibPath);
if(file.exists()) {
Log.i(Prefs.TAG, "videokitLibPath exits: " + videokitLibPath);
}
else {
Log.w(Prefs.TAG, "videokitLibPath not exits: " + videokitLibPath);
videokitLibPath = "/data/app/com.examples.ffmpeg4android_demo-2/lib/arm64/libvideokit.so";
Log.i(Prefs.TAG, "trying videokitLibPath: " + videokitLibPath);
if(file.exists()) {
Log.i(Prefs.TAG, "videokitLibPath exits: " + videokitLibPath);
}
else {
Log.e(Prefs.TAG, "can't find path of lib");
}
}
}
}
//String videokitLibPath = ctx.getFilesDir().getParent() + "/lib/arm64/libvideokit.so";
// only this works on Android M, and the number changes (demo-2, demo-1)
//String videokitLibPath = "/data/app/com.examples.ffmpeg4android_demo-1/lib/arm64/libvideokit.so";
//Log.i(Prefs.TAG, "videokitLibPath: " + videokitLibPath);
return videokitLibPath;
}
public void fExit( Context ctx) {
fexit(getVideokitLibPath(ctx));
}
public native String fexit(String videokitLibPath);
public native String unload();
public native String load(String[] args, String videokitSdcardPath, String videokitLibPath, boolean isComplex);
}
============================
package com.netcompss.ffmpeg4android;
public class Prefs {
public static final String TAG = "ffmpeg4android";
public static final String version = "322.00.00_LM322";
}
[/code]
[code=JavaScript]
package com.netcompss.ffmpeg4android;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import android.util.Log;
public class ProgressCalculator {
private int _durationOfCurrentWaitIndex = 0;
private final int DURATION_OF_CURRENT_WAIT_INDEX_LIMIT = 12;
private String _durationOfCurrent;
private long _lastVklogSize = -1;
private int _vkLogNoChangeCounter = 0;
private SimpleDateFormat _simpleDateFormat;
long _timeRef = -1;
int _prevProgress = 0;
private String vkLogPath = null;
public ProgressCalculator(String vkLogPathIn) {
vkLogPath = vkLogPathIn;
_simpleDateFormat = new SimpleDateFormat("HH:mm:ss.SS");
try {
Date ref = _simpleDateFormat.parse("00:00:00.00");
ref.setYear(112);
_timeRef = ref.getTime();
} catch (ParseException e) {
Log.w(Prefs.TAG, "failed to set _timeRef");
}
}
public void initCalcParamsForNextInter() {
Log.i(Prefs.TAG, "initCalcParamsForNextInter");
_lastVklogSize = -1;
_vkLogNoChangeCounter = 0;
_durationOfCurrent = null;
}
public int calcProgress() {
return calcProgress(1);
}
public int calcProgress(int durationMultiplyer) {
//Log.i(Prefs.TAG, "========calc progress======= " + durationMultiplyer);
int progress = 0;
if (_durationOfCurrent == null) {
String dur = GeneralUtils.getDutationFromVCLogRandomAccess(vkLogPath);
Log.d(Prefs.TAG, "dur: " + dur);
if (dur == null || dur.equals("") || dur.equals("null") ) {
Log.i(Prefs.TAG, "dur is not good, not setting ");
if (_durationOfCurrentWaitIndex < DURATION_OF_CURRENT_WAIT_INDEX_LIMIT) {
Log.i(Prefs.TAG, "waiting for real duration, going out of calcProgress with 0");
_durationOfCurrentWaitIndex ++;
return 0;
}
else {
Log.i(Prefs.TAG, "_durationOfCurrentWaitIndex is equal to: " + DURATION_OF_CURRENT_WAIT_INDEX_LIMIT + " reseting.");
_durationOfCurrentWaitIndex = 0;
Log.i(Prefs.TAG, "setting fake Prefs.durationOfCurrent");
_durationOfCurrent = "00:03:00.00";
Log.w(Prefs.TAG, "setting fake Prefs.durationOfCurrent (Cant get from file): " + _durationOfCurrent);
}
}
else {
_durationOfCurrent = GeneralUtils.getDutationFromVCLogRandomAccess(vkLogPath);
Log.i(Prefs.TAG, "duration: " + _durationOfCurrent + " \nTranscoding...");
}
}
if (_durationOfCurrent != null) {
long currentVkLogSize = -1;
currentVkLogSize = GeneralUtils.getVKLogSizeRandomAccess(vkLogPath);
//Log.d(Prefs.TAG, "currentVkLogSize: " + currentVkLogSize + " _lastVklogSize: " + _lastVklogSize);
if (currentVkLogSize > _lastVklogSize) {
_lastVklogSize = currentVkLogSize;
_vkLogNoChangeCounter = 0;
}
else {
//Log.w(Prefs.TAG, "Looks like Vk log is not increasing in size");
_vkLogNoChangeCounter++;
}
String currentTimeStr = GeneralUtils.readLastTimeFromVKLogUsingRandomAccess(vkLogPath);
//Log.d(Prefs.TAG, "currentTimeStr: " + currentTimeStr);
if (currentTimeStr.equals("exit")) {
Log.d(Prefs.TAG, "============Found one of the exit tokens in the log============");
return 100;
}
else if (currentTimeStr.equals("error") && _prevProgress == 0) {
Log.d(Prefs.TAG, "============Found error in the log============");
return 100;
}
else if (_vkLogNoChangeCounter > 16) {
Log.e(Prefs.TAG, "VK log is not changing in size, and no exit token found");
return 100;
}
try {
Date durationDate = _simpleDateFormat.parse(_durationOfCurrent);
Date currentTimeDate = _simpleDateFormat.parse(currentTimeStr);
currentTimeDate.setYear(112);
durationDate.setYear(112);
//Log.d(Prefs.TAG, " durationDate: " + durationDate + " currentTimeDate: " + currentTimeDate);
long durationLong = durationDate.getTime() - _timeRef;
if (durationMultiplyer != 1) {
//Log.i(Prefs.TAG, "====durationMultiplyer is not 1, handling===");
//Log.i(Prefs.TAG, "durationLong before: " + durationLong);
durationLong = durationLong * durationMultiplyer;
//Log.i(Prefs.TAG, "durationLong after: " + durationLong);
}
long currentTimeLong = currentTimeDate.getTime() - _timeRef;
//Log.d(Prefs.TAG, " durationLong: " + durationLong + " currentTimeLong: " + currentTimeLong + " diff: " + (durationLong - currentTimeLong));
progress = Math.round(((float)currentTimeLong / durationLong) * 100);
if (progress >= 100) {
Log.w(Prefs.TAG, "progress is 100, but can't find exit in the log, probably fake progress, still running...");
progress = 99;
}
_prevProgress = progress;
} catch (ParseException e) {
Log.w(Prefs.TAG, e.getMessage());
}
}
return progress;
}
}
==================================
然后点击构建并在assets / Plugins / Android / libs /下的unity项目中复制ffmpeg4android_lib.aar 然后做了这个总结
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.IO;
public class Test : MonoBehaviour {
private AndroidJavaObject FFMpeg = null;
private AndroidJavaObject activityContext = null;
public string Path1;
public string Path2;
public string Out3;
public string path;
public Text File1;
public Text File2;
public Text Context;
public Text End;
public void Convert(){
File1.text = File.Exists (path+Path1).ToString();
File2.text = File.Exists (path+Path2).ToString();
if (FFMpeg == null) {
using(AndroidJavaClass activityclass = new AndroidJavaClass("com.unity3d.player.UnityPlayer")){
activityContext = activityclass.GetStatic<AndroidJavaObject> ("currentActivity");
}
Context.text = "Context =" + activityContext;
}
using (AndroidJavaClass pluginClass = new AndroidJavaClass ("com.netcompss.ffmpeg4android.FFMpeg")) {
if (pluginClass != null) {
FFMpeg = pluginClass.CallStatic<AndroidJavaObject> ("instance");
FFMpeg.Call ("setContext", activityContext);
//activityContext.Call ("runOnUiThread", new AndroidJavaRunnable (() => {
FFMpeg.Call ("mux", path+Path1,path+Path2,path+Out3);
//}));
}
}
End.text = "Done";
}
}
=============================================
问题是: 当我在AndroidStudio中启动原始项目制作的apk时,一切正常。 当我推出统一制造的apk时,它的工作正常,直到这部分: load(args,workFolder,getVideokitLibPath(ctx),true);
IT确实加载了所有lib ok。 在尝试执行该代码时,它会在logcat中抛出此错误
03-23 10:43:17.293 28263-28277/? W/dalvikvm: No implementation found for native Lcom/netcompss/ffmpeg4android/LoadJNI;.load:([Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)Ljava/lang/String;
03-23 10:43:17.294 28263-28277/? E/test: vk run exception.
java.lang.UnsatisfiedLinkError: Native method not found: com.netcompss.ffmpeg4android.LoadJNI.load:([Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)Ljava/lang/String;
at com.netcompss.ffmpeg4android.LoadJNI.load(Native Method)
at com.netcompss.ffmpeg4android.LoadJNI.run(LoadJNI.java:37)
at com.netcompss.ffmpeg4android.LoadJNI.run(LoadJNI.java:57)
at com.netcompss.ffmpeg4android.FFMpeg.mux(FFMpeg.java:36)
at com.unity3d.player.UnityPlayer.nativeRender(Native Method)
at com.unity3d.player.UnityPlayer.a(Unknown Source)
at com.unity3d.player.UnityPlayer$b$1.handleMessage(Unknown Source)
at android.os.Handler.dispatchMessage(Handler.java:103)
at android.os.Looper.loop(Looper.java:194)
at com.unity3d.player.UnityPlayer$b.run(Unknown Source)
项目Dropbox链接: https://www.dropbox.com/s/6vglcw7xk2n8lwu/AndroidStudioProjects.rar?dl=0