I'm using FFmpeg in one of my projects for video compression. On Android 10 (Google Pixel 3a), it goes straight to onFailure(String message) with empty message for any command sent for execution.
so I have (api 'com.writingminds:FFmpegAndroid:0.3.2') specified in my app gradle file,
permission (android.permission.WRITE_EXTERNAL_STORAGE) in the manifest is specified
So I do:
InitializationCallback initializationCallback = new InitializationCallback();
try {
FFmpeg.getInstance(context).loadBinary(initializationCallback);
} catch (FFmpegNotSupportedException e) {
initializationCallback.onFailure();
initializationCallback.onFinish();
}
Initializes just fine, no problems here.
Later:
void getData(File inputFile) {
//inputFile points to: /storage/emulated/0/Android/data/{package_name}/files/temp_files/temp_1.mp4
String[] cmd = ("-i " + inputFile.getAbsolutePath()).split(" ");
try {
FFmpeg.getInstance(App.instance).execute(cmd, this);
} catch (FFmpegCommandAlreadyRunningException e) {
throw new Error(e);
}
}
@Override
public void onStart() {
//This method is called
}
@Override
public void onSuccess(String message) {
//This method is NOT called
extractAvailableData(message);
}
@Override
public void onProgress(String message) {
//This method is NOT called
extractAvailableData(message);
}
@Override
public void onFailure(String message) {
//This method is called and the message is empty
extractAvailableData(message);
}
@Override
public void onFinish() {
//This method is called
}
If I do something like:
String command = "-i ***/file1.mp4 -map 0:v -map 0:a -preset ultrafast -s:v 750:350 ***/file2.mp4";
//file2.mp4 is a non existent file at this point
// (***) --> is just a replacement for the full path of the file, just to keep things shorter here.
String[] cmd = command.split(" ");
try {
FFmpeg.getInstance(App.instance).execute(cmd, this);
} catch (FFmpegCommandAlreadyRunningException e) {
throw new Error(e);
}
gives the same result, no video conversion, just a call to onFailure("Nothing")
Even if I do:
String[] cmd = {"-version"};
try {
FFmpeg.getInstance(App.instance).execute(cmd, this);
} catch (FFmpegCommandAlreadyRunningException e) {
throw new Error(e);
}
I get nothing, no output at all.
I encountered this issue only on Android 10 so far, it works fine on other devices.
Android 10 had behavioral changes where execute permission for app home directory was removed. You will get a permission denied exception if you try and run the execution file from App home directory.
Here are the details on the changes for target SDK version 29 : https://developer.android.com/about/versions/10/behavior-changes-10#execute-permission
java.io.IOException: Cannot run program "/data/user/0/<package name>/files/ffmpeg": error=13, Permission denied
at java.lang.ProcessBuilder.start(ProcessBuilder.java:1050)
at nl.bravobit.ffmpeg.ShellCommand.run(ShellCommand.java:15)
at nl.bravobit.ffmpeg.FFcommandExecuteAsyncTask.doInBackground(FFcommandExecuteAsyncTask.java:43)
at nl.bravobit.ffmpeg.FFcommandExecuteAsyncTask.doInBackground(FFcommandExecuteAsyncTask.java:12)
at android.os.AsyncTask$3.call(AsyncTask.java:378)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:919)
Caused by: java.io.IOException: error=13, Permission denied
at java.lang.UNIXProcess.forkAndExec(Native Method)
at java.lang.UNIXProcess.<init>(UNIXProcess.java:133)
at java.lang.ProcessImpl.start(ProcessImpl.java:141)
I don't know if it'd be your case, but in my case the problem was that I didn't call ffmpeg.loadBinary before using it.
It seems a bit counter intuitive, because it actually work with my testing device without doing that. I have some ideas on why, but no time to test these. The important thing is that it's working now. Here an example of what I had to implement:
FFmpeg ffmpeg = FFmpeg.getInstance(context);
ffmpeg.loadBinary(new FFmpegLoadBinaryResponseHandler() {
@Override
public void onFailure() {
// Report failure to logcat,show the user a dialog, throw an exception or whatever you want to do in case of failure
}
@Override
public void onSuccess() {
// Execute the real ffmpeg stuff here
}
@Override
public void onStart() {
}
@Override
public void onFinish() {
}
});
In a real world scenario, if you plan to use it several times, you may use a flag to make sure it's not been loaded already and avoid trying to reload it in such case.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With