java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.forsale.forsale-2/base.apk"],nativeLibraryDirectories=[/data/app/com.forsale.forsale-2/lib/arm64, /data/app/com.forsale.forsale-2/base.apk!/lib/arm64-v8a, /system/lib64, /vendor/lib64]]] couldn't find "libloader-jni.so"
at java.lang.Runtime.loadLibrary0(Runtime.java:972)
at java.lang.System.loadLibrary(System.java:1567)
at com.netcompss.loader.LoadJNI.<clinit>(LoadJNI.java:15)
at com.forsale.app.utils.facades.VideoCompressor$getCompressedVideo$retValue$1.invokeSuspend(VideoCompressor.kt:21)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:32)
at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:236)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:594)
at kotlinx.coroutines.scheduling.CoroutineScheduler.access$runSafely(CoroutineScheduler.kt:60)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:742)
I am getting the crash above when try to run my code
fun getCompressVideo (video: String) {
launch (Dispatchers.Main){
viewModel.mediaViewModel.video = videoCompressor.getCompressedVideo(context, Uri.parse(viewModel.mediaViewModel.video))
}
}
override suspend fun getCompressedVideo(context: Context?, video: Uri): String {
val retValue = withContext(Dispatchers.IO) {
val inputFile = video.path
Log.i("AMIRA3000" , inputFile)
val loadJNI = LoadJNI()
val workFolder: String = context!!.filesDir.absolutePath
val outputFile: String = getFileFullName(
FilesConstants.VIDEO_FOLDER,
String.format(FilesConstants.VIDEO_NAME_FILE_FORMAT, System.currentTimeMillis())
)
val complexCommand = arrayOf(
"ffmpeg", "-y", "-i", inputFile, "-strict", "experimental", "-s", "320x240", "-r", "25", "-aspect", "4:3", "-ab", "48000", "-ac", "2", "-vcodec", "mpeg4", "-movflags", "+faststart", "-ar", "22050", "-b", "2097k", outputFile
)
loadJNI.run(complexCommand, workFolder, context)
}
return retValue.toString()
}
EDIT
public final class LoadJNI {
static {
System.loadLibrary("loader-jni");
}
/**
*
* #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))
load(args, workFolder, getVideokitLibPath(ctx), true);
else
throw new CommandValidationException();
}
else {
load(args, workFolder, getVideokitLibPath(ctx), true);
}
}
/**
*
* #param args ffmpeg command
* #param videokitSdcardPath 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: " + videokitLibPath);
}
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);
}
the file is found in the library
I know it's late to answer this question but may help someone who is looking for it.
I was also getting the same error and was working on it for couple of days.
i had tried all the possible answers but still was not able to solve them.I was using an old project in which jniLibs was already present but still i was getting the same error. After a lot of research i got my answer and it worked. this is the link which i followed:
http://androidwarzone.blogspot.com/2011/12/ffmpeg4android.html
only three things i had to do:
Add this line to your app gradle.build:
implementation 'com.netcompss:ffmpeg4android_lib:41.08'
Add this to your gradle.properties:
android.useDeprecatedNdk=true
Add this in the application tag in manifest file:
android:extractNativeLibs="true"
I hope it may help someone: Good Luck.
Related
In my Android Project, I try to use Transform API and Javasist to make an AOP framework.
But some class is transformed to an empty class and then I got the build error :
/build/intermediates/transforms/jarMerging/debug/jars/1/1f/combined.jar] (Can't process class xxx/xxx/xxx/ABC.class
because the ABC.class get empty after javasist handle(I just load it with javasiit and then write back).
The code I write to modify class is :
public void weave(String path) {
println("Begin to weave androidClassPath = " + androidClassPath)
println("Begin to weave path = " + path)
pool.appendClassPath(path)
pool.appendClassPath(androidClassPath)
pool.importPackage("android.util.Log");
File dir = new File(path)
int indexOfPackage = path.length() + 1;
if (dir.isDirectory()) {
dir.eachFileRecurse { File file ->
String filePath = file.absolutePath
if (isWeavableClass(filePath)) {
println("Begin to inject filePath " + filePath)
int end = filePath.length() - 6 // .class = 6
String className = filePath.substring(indexOfPackage, end).replace(File.separator, '.')
CtClass clazz = pool.getCtClass(className)
if (clazz.isFrozen()) {
clazz.defrost()
}
boolean timeDebugClass = false;
boolean parameterDebugClass = false;
if(clazz.hasAnnotation(TimingDebug.class)) {
timeDebugClass = true;
}
if(clazz.hasAnnotation(ParameterDebug.class)) {
parameterDebugClass = true;
}
println "timeDebugClass = "+ timeDebugClass + " parameterDebugClass = " + parameterDebugClass
CtMethod[] methods = clazz.getDeclaredMethods();
for (CtMethod method : methods) {
boolean emptyMethod = method.isEmpty()
boolean isNativeMethod = Modifier.isNative(method.getModifiers());
println("method name = " + method + " emptyMethod " + emptyMethod + " isNativeMethod = " + isNativeMethod)
if (!emptyMethod && !isNativeMethod) {
if (method.hasAnnotation(ParameterDebug.class) || parameterDebugClass) {
weaveParameterDebugMethod(clazz, method)
}
if (method.hasAnnotation(TimingDebug.class) || timeDebugClass) {
weaveTimingDebugMethod(clazz, method)
}
}
}
CtConstructor[] constructors = clazz.getDeclaredConstructors();
for(CtConstructor constructor: constructors){
boolean emptyMethod = constructor.isEmpty()
println("constructor name = " + constructor + " emptyMethod " + emptyMethod)
if (!emptyMethod) {
if (constructor.hasAnnotation(ParameterDebug.class) || parameterDebugClass) {
weaveParameterDebugMethod(clazz, constructor)
}
if (constructor.hasAnnotation(TimingDebug.class) || timeDebugClass) {
weaveTimingDebugMethod(clazz, constructor)
}
}
}
clazz.writeFile(path)
clazz.detach()
}
}
}
}
I had some code to get a list of all the classes in a package that looked something like this:
try {
DexFile df = new DexFile(context.getPackageCodePath());
for (Enumeration<String> iter = df.entries(); iter.hasMoreElements();) {
String s = iter.nextElement();
}
} catch (IOException e) {
e.printStackTrace();
}
However this code has stopped working since I upgraded my Android Studio to version 2.0. I've found that the culprit is Instant Run. If I debug the app I can see that without instance run, the DexFile variable, df, contains a list of class names (over 4,000 of them). When Instant Run is turned on, I only get like 30 or so class names, and the classes that I'm looking for aren't there. I have a feeling it has something to do with multi dex but I'm not sure how Instant Run is working under the covers (my app does not use multidex).
Does anyone know how I can get a list of classes like this with Instant Run turned on? Or does anyone know exactly why Im seeing this behaviour (would be great to understand it)?
We can handle the DEX files built by instant run in application data path.
public class MultiDexHelper {
private static final String EXTRACTED_NAME_EXT = ".classes";
private static final String EXTRACTED_SUFFIX = ".zip";
private static final String SECONDARY_FOLDER_NAME = "code_cache" + File.separator +
"secondary-dexes";
private static final String PREFS_FILE = "multidex.version";
private static final String KEY_DEX_NUMBER = "dex.number";
private static SharedPreferences getMultiDexPreferences(Context context) {
return context.getSharedPreferences(PREFS_FILE, Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB ?
Context.MODE_PRIVATE :
Context.MODE_PRIVATE | Context.MODE_MULTI_PROCESS);
}
/**
* get all the dex path
*
* #param context the application context
* #return all the dex path
* #throws PackageManager.NameNotFoundException
* #throws IOException
*/
public static List<String> getSourcePaths(Context context) throws PackageManager.NameNotFoundException, IOException {
ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), 0);
File sourceApk = new File(applicationInfo.sourceDir);
File dexDir = new File(applicationInfo.dataDir, SECONDARY_FOLDER_NAME);
if (LogUtil.isDebugModeEnable()) {
LogUtil.d("MultiDexHelper",
"getSourcePaths sourceDir=" + applicationInfo.sourceDir + ", dataDir=" + applicationInfo.dataDir);
}
List<String> sourcePaths = new ArrayList<String>();
sourcePaths.add(applicationInfo.sourceDir); //add the default apk path
//the prefix of extracted file, ie: test.classes
String extractedFilePrefix = sourceApk.getName() + EXTRACTED_NAME_EXT;
//the total dex numbers
int totalDexNumber = getMultiDexPreferences(context).getInt(KEY_DEX_NUMBER, 1);
if (LogUtil.isDebugModeEnable()) {
LogUtil.d("MultiDexHelper", "getSourcePaths totalDexNumber=" + totalDexNumber);
}
for (int secondaryNumber = 2; secondaryNumber <= totalDexNumber; secondaryNumber++) {
//for each dex file, ie: test.classes2.zip, test.classes3.zip...
String fileName = extractedFilePrefix + secondaryNumber + EXTRACTED_SUFFIX;
File extractedFile = new File(dexDir, fileName);
if (extractedFile.isFile()) {
sourcePaths.add(extractedFile.getAbsolutePath());
//we ignore the verify zip part
} else {
throw new IOException("Missing extracted secondary dex file '" +
extractedFile.getPath() + "'");
}
}
try {
// handle dex files built by instant run
File instantRunFilePath = new File(applicationInfo.dataDir,
"files" + File.separator + "instant-run" + File.separator + "dex");
if (LogUtil.isDebugModeEnable()) {
LogUtil.d("MultiDexHelper", "getSourcePaths instantRunFile exists=" + instantRunFilePath.exists() + ", isDirectory="
+ instantRunFilePath.isDirectory() + ", getAbsolutePath=" + instantRunFilePath.getAbsolutePath());
}
if (instantRunFilePath.exists() && instantRunFilePath.isDirectory()) {
File[] sliceFiles = instantRunFilePath.listFiles();
for (File sliceFile : sliceFiles) {
if (null != sliceFile && sliceFile.exists() && sliceFile.isFile() && sliceFile.getName().endsWith(".dex")) {
sourcePaths.add(sliceFile.getAbsolutePath());
}
}
}
} catch (Throwable e) {
LogUtil.e("MultiDexHelper", "getSourcePaths parse instantRunFilePath exception", e);
}
return sourcePaths;
}
// /**
// * get all the classes name in "classes.dex", "classes2.dex", ....
// *
// * #param context the application context
// * #return all the classes name
// * #throws PackageManager.NameNotFoundException
// * #throws IOException
// */
// public static List<String> getAllClasses(Context context) throws PackageManager.NameNotFoundException, IOException {
// List<String> classNames = new ArrayList<String>();
// for (String path : getSourcePaths(context)) {
// try {
// DexFile dexfile = null;
// if (path.endsWith(EXTRACTED_SUFFIX)) {
// //NOT use new DexFile(path), because it will throw "permission error in /data/dalvik-cache"
// dexfile = DexFile.loadDex(path, path + ".tmp", 0);
// } else {
// dexfile = new DexFile(path);
// }
// Enumeration<String> dexEntries = dexfile.entries();
// while (dexEntries.hasMoreElements()) {
// classNames.add(dexEntries.nextElement());
// }
// } catch (IOException e) {
// throw new IOException("Error at loading dex file '" +
// path + "'");
// }
// }
// return classNames;
// }
/**
* scan parent class's sub classes
*
* #param context
* #param packageName
* #param parentClass
* #param <T>
* #return
*/
public static <T> Set<Class<? extends T>> scanClasses(Context context, String packageName, Class<T> parentClass) {
Set<Class<? extends T>> classes = new HashSet<Class<? extends T>>();
try {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
for (String path : getSourcePaths(context)) {
if (LogUtil.isDebugModeEnable()) {
LogUtil.d("MultiDexHelper", "scanClasses path=" + path);
}
try {
DexFile dexfile = null;
if (path.endsWith(EXTRACTED_SUFFIX)) {
//NOT use new DexFile(path), because it will throw "permission error in /data/dalvik-cache"
dexfile = DexFile.loadDex(path, path + ".tmp", 0);
} else {
dexfile = new DexFile(path);
}
Enumeration<String> dexEntries = dexfile.entries();
while (dexEntries.hasMoreElements()) {
String className = dexEntries.nextElement();
if (LogUtil.isDebugModeEnable()) {
LogUtil.d("MultiDexHelper", "scanClasses className=" + className);
}
if (className.toLowerCase().startsWith(packageName.toLowerCase())) {
Class clazz = classLoader.loadClass(className);
if (LogUtil.isDebugModeEnable()) {
LogUtil.d("MultiDexHelper",
"scanClasses clazz=" + clazz + ", parentClass=" + parentClass + ", equals=" + clazz
.getSuperclass().equals(parentClass));
}
if (clazz.getSuperclass().equals(parentClass)) {
classes.add(clazz);
}
}
}
} catch (Throwable e) {
LogUtil.e("MultiDexHelper", "scanClasses Error at loading dex file '" +
path + "'", e);
}
}
} catch (Throwable e) {
LogUtil.e("MultiDexHelper", "scanClasses exception", e);
}
return classes;
}
}
i have an FTP connection which is working fine , i can download my files using a fragment which call > an asynctaskt which call > the FTP server
public static void downloadDirectory(FTPClient ftpClient, String parentDir,
String currentDir, String saveDir) throws IOException {
String dirToList = parentDir;
if (!currentDir.equals("")) {
dirToList += "/" + currentDir;
}
FTPFile[] subFiles = ftpClient.listFiles(dirToList);
if (subFiles != null && subFiles.length > 0) {
for (FTPFile aFile : subFiles) {
String currentFileName = aFile.getName();
if (currentFileName.equals(".") || currentFileName.equals("..")) {
// skip parent directory and the directory itself
continue;
}
String filePath = parentDir + "/" + currentDir + "/"
+ currentFileName;
if (currentDir.equals("")) {
filePath = parentDir + "/" + currentFileName;
}
String newDirPath = saveDir + parentDir + File.separator
+ currentDir + File.separator + currentFileName;
if (currentDir.equals("")) {
newDirPath = saveDir + parentDir + File.separator
+ currentFileName;
}
if (aFile.isDirectory()) {
// create the directory in saveDir
File newDir = new File(newDirPath);
boolean created = newDir.mkdirs();
if (created) {
System.out.println("CREATED the directory: "
+ newDirPath);
} else {
System.out.println("COULD NOT create the directory: "
+ newDirPath);
}
// download the sub directory
downloadDirectory(ftpClient, dirToList, currentFileName,
saveDir);
} else {
// download the file
boolean success = downloadSingleFile(ftpClient, filePath,
newDirPath);
if (success) {
System.out.println("DOWNLOADED the file: " + filePath);
} else {
System.out.println("COULD NOT download the file: "
+ filePath);
}
}
}
try {
Log.v("LogoutInformation", "Logout from FTP");
ftpClient.logout();
} catch (Exception e) {
Log.e("LogoutInformation", "Logout Fail");
}
try {
Log.v("DisconnectInformation", "Disconnect from FTP");
ftpClient.disconnect();
} catch (Exception e) {
Log.e("DisconnectInformation", "Disconnect Fail");
}
}
Here my function from FTPserver.
System.out.println("DOWNLOADED the file: " + filePath);
And this is what I want to show on my fragment , i want to make a toast which display the file which is actually in download.
But it's background processing , so i can't display information on my fragment , so i don't know how i can do it.
As from tags, you are using AsyncTask. Modify it's constructor to accept a reference to context.
public class MyAsyncTask extends AsyncTask<..> {
private Context mContext;
public MyAsyncTask(Context c){
this.mContext = c;
}
...
When your task is running in doInBackground, you should publishProgress(String):
doInBackground(String.. params){
for(something){
...
publishProgress(fileName);
}
}
publishProgress(String fName){
Toast.makeText(mContext, name, Toast.Length_long).show();
}
You only need to do this.
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(context,"Your text", Toast.LENGTH_LONG).show();
}
});
Are you calling the downloadDirectory in the doInBackground event of the AsyncTask class?
If so, you can make Toast message like so:
#Override
protected void onPostExecute(Void result) {
Toast.makeText(getActivity(),"DOWNLOADED the file: "+filePath,Toast.LENGTH_SHORT).show();
}
Where you make filePath global variable.
Something like that. Hope it helps..
Try to use:
Toast.makeText(getActivity(), "File Downloaded:"+currentFileName, Toast.LENGTH_LONG).show()
I am using this code. I need to merge two videos. It saved all videos in temp folder but not in merged condition. Append and DoAppend are my functions which I want for merging the videos.
public String append(ArrayList<String> trimVideos) {
for (int i = 0; i < trimVideos.size() - 1; i++) {
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
if (i == 0) {
String OutPutFileName = Constants.STORAGE_VIDEO_TEMP_PATH +
File.separator + "APPEND" + "_" + timeStamp + ".mp4";
doAppend(trimVideos.get(0), trimVideos.get(i + 1),OutPutFileName);
Log.e(Constants.TAG, "In First: " + i + " " + OutPutFileName);
} else {
String OutPutFileName = Constants.STORAGE_VIDEO_TEMP_PATH
+ File.separator + "APPEND" + i + "_" + timeStamp + ".mp4";
doAppend(lastAppendOut, trimVideos.get(i + 1), OutPutFileName);
Log.e(Constants.TAG, "In Second: " + i + " " + OutPutFileName);
}
}
Log.e(Constants.TAG, "In End: " + " " + lastAppendOut);
return lastAppendOut;
}
This Method Crashed my application on add track.
private String doAppend(String _firstVideo, String _secondVideo,String _newName) {
try {
Log.e("test", "Stage1");
FileInputStream fis1 = new FileInputStream(_firstVideo);
FileInputStream fis2 = new FileInputStream(_secondVideo);
Movie[] inMovies = new Movie[] {
MovieCreator.build(fis1.getChannel()),MovieCreator.build(fis2.getChannel()) };
List<Track> videoTracks = new LinkedList<Track>();
List<Track> audioTracks = new LinkedList<Track>();
//It returns one item of video and 2 item of video.
for (Movie m : inMovies) {
for (Track t : m.getTracks()) {
if (t.getHandler().equals("soun")) {
audioTracks.add(t);
}
if (t.getHandler().equals("vide")) {
videoTracks.add(t);
}
}
}
Log.e("test", "Stage2");
Movie result = new Movie();
if (audioTracks.size() > 0) {
result.addTrack(new AppendTrack(audioTracks.toArray(new Track[audioTracks.size()])));
}
if (videoTracks.size() > 0) {
result.addTrack(new AppendTrack(videoTracks.toArray(new Track[videoTracks.size()])));
}
IsoFile out = new DefaultMp4Builder().build(result);
Log.e("test", "Stage3");
String filename = _newName;
lastAppendOut = filename;
Log.e(Constants.TAG, "In Append: " + " " + lastAppendOut);
FileOutputStream fos = new FileOutputStream(filename);
FileChannel fco = fos.getChannel();
fco.position(0);
out.getBox(fco);
fco.close();
fos.close();
fis1.close();
fis2.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
Log.e("check", e.getMessage());
}
return _newName;
}
Code For Merging Multiple Video
Gradle Dependency
implementation 'com.googlecode.mp4parser:isoparser:1.1.9'
Code
private String appendTwoVideos(String firstVideoPath, String secondVideoPath)
{
try {
Movie[] inMovies = new Movie[2];
inMovies[0] = MovieCreator.build(firstVideoPath);
inMovies[1] = MovieCreator.build(secondVideoPath);
List<Track> videoTracks = new LinkedList<>();
List<Track> audioTracks = new LinkedList<>();
for (Movie m : inMovies) {
for (Track t : m.getTracks()) {
if (t.getHandler().equals("soun")) {
audioTracks.add(t);
}
if (t.getHandler().equals("vide")) {
videoTracks.add(t);
}
}
}
Movie result = new Movie();
if (audioTracks.size() > 0) {
result.addTrack(new AppendTrack(audioTracks
.toArray(new Track[audioTracks.size()])));
}
if (videoTracks.size() > 0) {
result.addTrack(new AppendTrack(videoTracks
.toArray(new Track[videoTracks.size()])));
}
BasicContainer out = (BasicContainer) new DefaultMp4Builder().build(result);
#SuppressWarnings("resource")
FileChannel fc = new RandomAccessFile(Environment.getExternalStorageDirectory() + "/wishbyvideo.mp4", "rw").getChannel();
out.writeContainer(fc);
fc.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
String mFileName = Environment.getExternalStorageDirectory().getAbsolutePath();
mFileName += "/wishbyvideo.mp4";
return mFileName;
}
You might wanna call this function from a background thread.
The above answer is perfectly right but It will only work if your media encoder is H264.....
mediaRecorder.setVideoEncoder(VideoEncoder.H264);
happy Coding :) :D
The above answer will only work when your codec, framerate, and bitrate are the same.
Gradle Dependency(This library will work in nearly every case)
implementation 'com.github.yangjie10930:EpMedia:v0.9.5'
CODE
private void mergeVideos() {
ArrayList<EpVideo> epVideos = new ArrayList<>();
epVideos.add(new EpVideo (file2)); // Video 1
epVideos.add(new EpVideo (file1)); // Video 2
EpEditor. OutputOption outputOption =new EpEditor.OutputOption(fileOutput);
outputOption.setWidth(720);
outputOption.setHeight(1280);
outputOption.frameRate = 25 ;
outputOption.bitRate = 10 ; //Default
EpEditor.merge(epVideos, outputOption, new OnEditorListener() {
#Override
public void onSuccess () {
Log.d("Status","Success");
}
#Override
public void onFailure () {
}
#Override
public void onProgress ( float progress ) {
// Get processing progress here
Log.d("Progress",""+progress);
}
});
}
Gradle Dependency
implementation "com.writingminds:FFmpegAndroid:0.3.2"
Code
Command to concate two videos side by side into one
val cmd : arrayOf("-y", "-i", videoFile!!.path, "-i", videoFileTwo!!.path, "-filter_complex", "hstack", outputFile.path)
Command to append two videos (one after another) into one
val cmd : arrayOf("-y", "-i", videoFile!!.path, "-i", videoFileTwo!!.path, "-strict", "experimental", "-filter_complex",
"[0:v]scale=iw*min(1920/iw\\,1080/ih):ih*min(1920/iw\\,1080/ih), pad=1920:1080:(1920-iw*min(1920/iw\\,1080/ih))/2:(1080-ih*min(1920/iw\\,1080/ih))/2,setsar=1:1[v0];[1:v] scale=iw*min(1920/iw\\,1080/ih):ih*min(1920/iw\\,1080/ih), pad=1920:1080:(1920-iw*min(1920/iw\\,1080/ih))/2:(1080-ih*min(1920/iw\\,1080/ih))/2,setsar=1:1[v1];[v0][0:a][v1][1:a] concat=n=2:v=1:a=1",
"-ab", "48000", "-ac", "2", "-ar", "22050", "-s", "1920x1080", "-vcodec", "libx264", "-crf", "27",
"-q", "4", "-preset", "ultrafast", outputFile.path)
Note :
"videoFile" is your first video path.
"videoFileTwo" is your second video path.
"outputFile" is your combined video path which is our final output path
To create output path of video
fun createVideoPath(context: Context): File {
val timeStamp: String = SimpleDateFormat(Constant.DATE_FORMAT, Locale.getDefault()).format(Date())
val imageFileName: String = "APP_NAME_"+ timeStamp + "_"
val storageDir: File? = context.getExternalFilesDir(Environment.DIRECTORY_MOVIES)
if (storageDir != null) {
if (!storageDir.exists()) storageDir.mkdirs()
}
return File.createTempFile(imageFileName, Constant.VIDEO_FORMAT, storageDir)
}
Code to execute command
try {
FFmpeg.getInstance(context).execute(cmd, object : ExecuteBinaryResponseHandler() {
override fun onStart() {
}
override fun onProgress(message: String?) {
callback!!.onProgress(message!!)
}
override fun onSuccess(message: String?) {
callback!!.onSuccess(outputFile)
}
override fun onFailure(message: String?) {
if (outputFile.exists()) {
outputFile.delete()
}
callback!!.onFailure(IOException(message))
}
override fun onFinish() {
callback!!.onFinish()
}
})
} catch (e: Exception) {
} catch (e2: FFmpegCommandAlreadyRunningException) {
}
I am trying to start an activity, when a file is inserted into sdcard. For that, I want to start a helloworld.java activity(dummy one). I am getting 'undefined' error at startActivity() method. The Eclipse highlights the error code code with red underline. I have registered both the classes in manifest file. So no problem in manifest file.
public class MyFileObserver extends FileObserver {
public static final String PREFS_NAME = "MyPreferencesFile";
public static String absolutePath;
//final adapter info = new adapter(this);
HashSet<ObserverActivity> registeredObservers;
FileEvent fileevent = new FileEvent();
final filehelper f_help = new filehelper(fileevent);
private Context context;
public MyFileObserver(Context context) {
super(absolutePath);
this.context = context;
}
public MyFileObserver(String path) {
super(path, FileObserver.ALL_EVENTS);
//this.fileevent = fileevent;
absolutePath = path;
registeredObservers = new HashSet<ObserverActivity>();
}
public void registerObserver(ObserverActivity toRegister){
registeredObservers.add(toRegister);
}
public void unregisterObserver(ObserverActivity toUnregister){
registeredObservers.remove(toUnregister);
}
#Override
public void onEvent(int event, String path) {
// try{
if (path == null)
{
return;
}
/*for(ObserverActivity o: registeredObservers){
o.onFileObserved(event, path);
}*/
//a new file or subdirectory was created under the monitored directory
if ((FileObserver.CREATE & event)!=0) {
FileAccessLogStatic.accessLogMsg += absolutePath + "/" + path + " is created\n";
Log.v(path+ " in FileObserver of sample_fileobserver ====>>>> ",path);
// setpath(path);
//fileevent.insert(path);
/*for(ObserverActivity o: registeredObservers){
o.onFileObserved(event, path);
}*///try
// {
FileEvent.path2 = path;
Intent i = new Intent("com.example.sample_fileobserver.hello");
startActivity(i);
// startAct();
// fileevent.insert(path);
// }
//catch(Exception e)
//{
// Log.v("Activity cannot be started ====>>>> ",e.toString());
// }
//Intent i=new Intent("com.example.seperate_fileobserver.FileEvent");
// i.putExtra("path", path);
// startActivity(i);
}
//a file or directory was opened
if ((FileObserver.OPEN & event)!=0) {
FileAccessLogStatic.accessLogMsg += path + " is opened\n";
}
//data was read from a file
if ((FileObserver.ACCESS & event)!=0) {
FileAccessLogStatic.accessLogMsg += absolutePath + "/" + path + " is accessed/read\n";
}
//data was written to a file
if ((FileObserver.MODIFY & event)!=0) {
FileAccessLogStatic.accessLogMsg += absolutePath + "/" + path + " is modified\n";
}
//someone has a file or directory open read-only, and closed it
if ((FileObserver.CLOSE_NOWRITE & event)!=0) {
FileAccessLogStatic.accessLogMsg += path + " is closed\n";
}
//someone has a file or directory open for writing, and closed it
if ((FileObserver.CLOSE_WRITE & event)!=0) {
String filename = "";
int numbers = 0;
f_help.insertpic(filename,numbers);
FileAccessLogStatic.accessLogMsg += absolutePath + "/" + path + " is written and closed\n";
}
//[todo: consider combine this one with one below]
//a file was deleted from the monitored directory
if ((FileObserver.DELETE & event)!=0) {
//for testing copy file
// FileUtils.copyFile(absolutePath + "/" + path);
FileAccessLogStatic.accessLogMsg += absolutePath + "/" + path + " is deleted\n";
Log.v("deleting path",path);
// fileevent.delete(path);
//for(ObserverActivity o: registeredObservers){
// o.onFileObserved(event, path);
// }
try{
fileevent.delete(path);
}
catch(Exception e)
{
Log.v("File cannot be deleted ====>>>> ",e.toString());
}
}
//the monitored file or directory was deleted, monitoring effectively stops
if ((FileObserver.DELETE_SELF & event)!=0) {
FileAccessLogStatic.accessLogMsg += absolutePath + "/" + " is deleted\n";
}
//a file or subdirectory was moved from the monitored directory
if ((FileObserver.MOVED_FROM & event)!=0) {
FileAccessLogStatic.accessLogMsg += absolutePath + "/" + path + " is moved to somewhere " + "\n";
}
//a file or subdirectory was moved to the monitored directory
if ((FileObserver.MOVED_TO & event)!=0) {
FileAccessLogStatic.accessLogMsg += "File is moved to " + absolutePath + "/" + path + "\n";
}
//the monitored file or directory was moved; monitoring continues
if ((FileObserver.MOVE_SELF & event)!=0) {
FileAccessLogStatic.accessLogMsg += path + " is moved\n";
}
//Metadata (permissions, owner, timestamp) was changed explicitly
if ((FileObserver.ATTRIB & event)!=0) {
FileAccessLogStatic.accessLogMsg += absolutePath + "/" + path + " is changed (permissions, owner, timestamp)\n";
}
I am unable to get why it is undefined, and why FileObserver.onEvent() is not supporting startActivity(Intent) method.
Thanks in advance.
Try context.startActivity(i) instead.
startActivity is a method of Context and subclasses of Context, such as Activity and Service. You may be able to use context.startActivity(intent) to get rid of the syntax error, but you may want to consider placing this functionality inside a Service.