Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

FFmpeg doesn't work on android 10, goes strait to onFailure(String message) with empty message

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.

like image 483
nolanic Avatar asked Oct 25 '19 20:10

nolanic


2 Answers

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)
like image 61
Shashank Kadne Avatar answered Oct 24 '22 05:10

Shashank Kadne


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.

like image 42
Fran Marzoa Avatar answered Oct 24 '22 04:10

Fran Marzoa