ffmpeg-android-java icon indicating copy to clipboard operation
ffmpeg-android-java copied to clipboard

Cannot run program "/data/user/0/com.package.name/files/ffmpeg": error=13, Permission denied

Open budiardianata opened this issue 4 years ago • 23 comments

i have error "error=13, Permission denied" while trying to run Ffmpeg in android 10 (Q).

java.io.IOException: Cannot run program "/data/user/0/com.package.name/files/ffmpeg": error=13, Permission denied at java.lang.ProcessBuilder.start(ProcessBuilder.java:1050) at java.lang.Runtime.exec(Runtime.java:698) at java.lang.Runtime.exec(Runtime.java:563) at com.github.hiteshsondhi88.libffmpeg.ShellCommand.run(ShellCommand.java:11) at com.github.hiteshsondhi88.libffmpeg.FFmpegExecuteAsyncTask.doInBackground(FFmpegExecuteAsyncTask.java:40) at com.github.hiteshsondhi88.libffmpeg.FFmpegExecuteAsyncTask.doInBackground(FFmpegExecuteAsyncTask.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.(UNIXProcess.java:133) at java.lang.ProcessImpl.start(ProcessImpl.java:141) at java.lang.ProcessBuilder.start(ProcessBuilder.java:1029) at java.lang.Runtime.exec(Runtime.java:698)  at java.lang.Runtime.exec(Runtime.java:563)  at com.github.hiteshsondhi88.libffmpeg.ShellCommand.run(ShellCommand.java:11)  at com.github.hiteshsondhi88.libffmpeg.FFmpegExecuteAsyncTask.doInBackground(FFmpegExecuteAsyncTask.java:40)  at com.github.hiteshsondhi88.libffmpeg.FFmpegExecuteAsyncTask.doInBackground(FFmpegExecuteAsyncTask.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) 

budiardianata avatar Sep 17 '19 13:09 budiardianata

Hey,

  1. First you need to get permissions for external storage.

if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_RQ);

                                           }
  1. Pay attention to the oder - first wait for ffmpeg lib to finish loading - only then start any commands

package appsforactors.com.myselftapeapp.Mp4Tasks;

import android.content.Context; import android.util.Log;

import com.crashlytics.android.Crashlytics; import com.github.hiteshsondhi88.libffmpeg.ExecuteBinaryResponseHandler; import com.github.hiteshsondhi88.libffmpeg.FFmpeg; import com.github.hiteshsondhi88.libffmpeg.LoadBinaryResponseHandler; import com.github.hiteshsondhi88.libffmpeg.exceptions.FFmpegCommandAlreadyRunningException; import com.github.hiteshsondhi88.libffmpeg.exceptions.FFmpegNotSupportedException;

import org.bytedeco.javacpp.presets.opencv_core;

import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List;

import appsforactors.com.myselftapeapp.Utility.FileUtility; import appsforactors.com.myselftapeapp.Utility.ProcessUtils; import appsforactors.com.myselftapeapp.Utility.TimeUtilility; import io.fabric.sdk.android.services.common.Crash;

/**

  • Created by nikhil on 2016-01-29. */ public class FFmpegExtension {

    private FFmpeg mFFmpeg; private static final String TAG = "FFMPEG";

    private final String IMAGE_TO_MOVIE_COMMAND = "-loop^1^-i^%s^-i^%s^-c:v^libx264^-crf^23^-t^%d^-pix_fmt^yuv420p^-c:a^aac^-strict^-2^-b:a^192k^-b:v^2400000^-s^1280x720^-shortest^-preset^ultrafast^%s";

    private final String CONVERT_TO_INTERMEDIATE_FILE_COMMAND = "-i^%s^-c^copy^-bsf:v^h264_mp4toannexb^-f^mpegts^-preset^ultrafast^%s";

    // original //private final String MERGE_VIDEO_COMMAND = "-i^concat:%s^-c:a^aac^-crf^23^-b:v^1000000^-strict^-2^-bsf:a^aac_adtstoasc^-preset^ultrafast^%s"; private final String MERGE_VIDEO_COMMAND = "-i^concat:%s^-c:a^aac^-crf^23^-b:v^1000000^-strict^-2^-bsf:a^aac_adtstoasc^-preset^ultrafast^-tune^film^-fflags^+genpts^%s";

    // remove transcoding //private final String MERGE_VIDEO_COMMAND = "-i^concat:%s^-bsf:a^aac_adtstoasc^-c^copy^%s";

    // http://unix.stackexchange.com/questions/28803/how-can-i-reduce-a-videos-size-with-ffmpeg private final String REDUCE_MEDIA_FILE_SIZE_COMMAND = "-i %s^-b:v 1000000^-strict^-2^-preset ultrafast^%s";

    private String mLastMessage;

    private String[] mCommand;

    public static String[] commandFromCaretString(String caretSeperatedString) { return caretSeperatedString.split("\^"); }

    private Context mContext; private boolean mSuccess = false;

    private OnFFmpegListemer mListener;

    private class Command { public String command; public double duration; }

    private List<Command> commands = new ArrayList<>();

    private boolean loaded = false;

    public FFmpegExtension(Context context) { this.mContext = context; loadFFMpegBinary(); }

    /** *

    • @param imageFileName
    • @param movieFileName
    • @param
    •  duration -> Duration of the video to create
      

    */ public void setImageToMovieCommand(String imageFileName, String movieFileName, int duration) { String silenceAudioFilePath = FileUtility.getAssetsFile(mContext, "silence.wav").toString(); String caretCommand = String.format(IMAGE_TO_MOVIE_COMMAND, imageFileName, silenceAudioFilePath, duration, movieFileName); if (loaded) { execFFmpegBinary(caretCommand, duration); } else { Command command = new Command(); command.command = caretCommand; command.duration = duration; commands.add(command); } }

    public void setCreateIntermediateFileCommand(String videoFilePath, String videoFileName) { String intermediateMovieFileNamePath = FileUtility.getNewFilePath(mContext, videoFileName, "intermediate.ts"); String caretCommand = String.format(CONVERT_TO_INTERMEDIATE_FILE_COMMAND, videoFilePath, intermediateMovieFileNamePath); if (loaded) { execFFmpegBinary(caretCommand, 0); } else { Command command = new Command(); command.command = caretCommand; command.duration = 0; commands.add(command); } }

    /** *

    • @param videoFilePaths
    • @param videoFileNames
    • @param finalVideoPath
    • @param
    •  videoLength -> Resulting video duration in seconds (Cheap (and faster) way to do it instead of asking individual files for their times from ffmpeg).
      

    */ public void setMergeVideoCommand(ArrayList<String> videoFilePaths, ArrayList<String> videoFileNames, String finalVideoPath, double videoLength) { String concatFilePath = ""; for (int i = 0; i < videoFilePaths.size(); i++) { String intermediateMovieFileNamePath = FileUtility.getNewFilePath(mContext, videoFileNames.get(i), "intermediate.ts"); concatFilePath += intermediateMovieFileNamePath; if (i == videoFilePaths.size() - 1) { break; } concatFilePath += "|"; } String caretCommand = String.format(MERGE_VIDEO_COMMAND, concatFilePath, finalVideoPath); if (loaded) { execFFmpegBinary(caretCommand, videoLength); } else { Command command = new Command(); command.command = caretCommand; command.duration = videoLength; commands.add(command); } }

    private void loadFFMpegBinary() { try { if (mFFmpeg == null) { mFFmpeg = FFmpeg.getInstance(mContext); } mFFmpeg.loadBinary(new LoadBinaryResponseHandler() { @Override public void onFailure() { Log.d(TAG, "Failure"); }

             @Override
             public void onSuccess() {
                 Log.d(TAG, "<ffmpeg>mFFmpeg : correct Loaded");
                 loaded = true;
    
                 if (commands.size() > 0) {
                     for (Command command: commands) {
                         execFFmpegBinary(command.command, command.duration);
                     }
                 }
             }
         });
     } catch (FFmpegNotSupportedException e) {
         Crashlytics.logException(e);
         Log.d(TAG, "<ffmpeg>FFmpegNotSupportedException: " + e.getMessage());
     } catch (Exception e) {
         Crashlytics.logException(e);
         Log.d(TAG, "<ffmpeg>Exception no control: " + e.getMessage());
     }
    

    }

    private void execFFmpegBinary(String caretSeperatedCommand, final double duration) {

     final String[] command = commandFromCaretString(caretSeperatedCommand);
     Log.d(TAG,"<ffmpeg>Executing ffmpeg command: " + java.util.Arrays.toString(command));
     try {
         mFFmpeg.execute(command, new ExecuteBinaryResponseHandler() {
             @Override
             public void onFailure(String s) {
                 Log.d(TAG, "<ffmpeg>FAILED with output : " + s);
                 mSuccess = false;
                 mLastMessage = s;
             }
    
             @Override
             public void onSuccess(String s) {
                 Log.d(TAG, "<ffmpeg>SUCCESS with output : " + s);
                 mSuccess = true;
                 mLastMessage = s;
             }
    
             @Override
             public void onProgress(String s) {
                 if (s.contains("time=")) {
                     String time = s.substring(s.indexOf("time=") + "time=".length(), s.indexOf(" bitrate"));
                     Log.d(TAG, "<ffmpeg>Current Time: " + time);
    
                     try {
                         double timeProcessed = TimeUtilility.timeStringToSeconds(time);
                         mListener.ffmpegPercentComplete(timeProcessed / duration);
                     }
                     catch(Exception ex) {
                         Crashlytics.logException(ex);
                         // Do nothing. If we fail to report a progress, it's not a big deal.
                         // Just don't want the app to crash.
                     }
                 }
             }
    
             @Override
             public void onStart() {
                 Log.d(TAG, "<ffmpeg>Started command : mFFmpeg " + java.util.Arrays.toString(command));
             }
    
             @Override
             public void onFinish() {
                 Log.d(TAG, "<ffmpeg>Finished command : mFFmpeg " + java.util.Arrays.toString(command));
                 if (mSuccess) {
                     mListener.ffmpegSuccess(mLastMessage);
                 } else {
                     mListener.ffmpegFailed(mLastMessage);
                 }
             }
         });
     } catch (FFmpegCommandAlreadyRunningException e) {
         Crashlytics.logException(e);
         e.printStackTrace();
         mListener.ffmpegFailed("FFmpegCommandAlreadyRunningException");
     }
    

    }

    public boolean isRunning() { return mFFmpeg != null && mFFmpeg.isFFmpegCommandRunning(); }

    public void killRunningProcess() { if (mFFmpeg == null) { return; }

     mFFmpeg.killRunningProcesses();
    
     try {
         Field asyncTaskField = mFFmpeg.getClass().getDeclaredField("ffmpegExecuteAsyncTask");
         asyncTaskField.setAccessible(true);
         Object ffmpegExecuteAsyncTask = asyncTaskField.get(mFFmpeg);
    
         if (ffmpegExecuteAsyncTask != null) {
             Field processField = ffmpegExecuteAsyncTask.getClass().getDeclaredField("process");
             processField.setAccessible(true);
             Process process = (Process) processField.get(ffmpegExecuteAsyncTask);
    
             if (process != null) {
                 ProcessUtils.killProcess(process);
                 if (mListener != null) {
                     mListener.ffmpegDidKillProcess();
                 }
             }
         }
     } catch (NoSuchFieldException | IllegalAccessException | ClassCastException ex) {
         throw new RuntimeException(ex);
     }
    

    }

    public void setmListener(OnFFmpegListemer mListener) { this.mListener = mListener; }

    public interface OnFFmpegListemer { void ffmpegSuccess(String message);

     void ffmpegPercentComplete(double percentComplete);
    
     void ffmpegFailed(String message);
    
     void ffmpegDidKillProcess();
    

    } }

brisksvoronko avatar Oct 04 '19 10:10 brisksvoronko

is this worked for u?

riddhee avatar Dec 17 '19 08:12 riddhee

Any updates on this?

kartik1225 avatar Dec 28 '19 13:12 kartik1225

I suppose its the limitation of Android Q and target api 29, as the library executes binary file. Indeed it looks really tricky

st235 avatar Jan 01 '20 11:01 st235

Hi there, i am facing the same issue with android Q on target api 29. Taking write permission and waiting for library to load is not helping. Do anyone has any solution?? Thanks.

rkkalkii avatar Jan 08 '20 04:01 rkkalkii

@rkkalkii, I would recommend you to try MediaCodec API, it really works as ffmpeg. The only limitation is api 18+.

st235 avatar Jan 08 '20 21:01 st235

Does anybody got the solution for this ? I am also stuck here

appteamwebcontentor avatar Jan 22 '20 08:01 appteamwebcontentor

Does anybody got the solution for this ? I am also stuck here

Yeah, as I wrote below, this solution will not work anymore until authors will not recompile binary file as a static library and wrap the code with JNI methods.

The optimal solution without using any library is to use MediaCodec API provided by Android since API 18+

st235 avatar Jan 22 '20 12:01 st235

i recommend google/android to remove this scoped storage shit. Same issue...

pezezzle avatar Feb 10 '20 20:02 pezezzle

Hi............ Does anybody got the solution for this ? I am also stuck here.. Please send me reply

gowthami77 avatar Feb 24 '20 11:02 gowthami77

@gowthami77 the temporary solution is set target api < 29 or you can use MediaCodec API.

alexict avatar Feb 25 '20 01:02 alexict

I have the same issue on Android 10+ with targetsdk 29. I can confirm it works fine with targetSdk 28. But I would like to force it work with the latest sdk. And I cannot use MediaCodec API due to limited functionallity. Let me know if anybody has solved the issue. Thanks!

dmitriy-chernysh avatar Apr 06 '20 14:04 dmitriy-chernysh

Also stuck on this issue. If anybody found a solution except for adding targetSdk 28. Please any help will be appreaciated.

aliraza96 avatar Apr 13 '20 11:04 aliraza96

@aliraza96, if MediaCodec is not applicable for you, its still possible to compile ffmpeg from sources as shared library. Here you may found some related info about compilation for Android. Also this one repo structured very well.

st235 avatar Apr 13 '20 11:04 st235

I found this library and it worked well on Android 10 https://github.com/tanersener/mobile-ffmpeg

gowthami77 avatar Apr 15 '20 11:04 gowthami77

@gowthami77 is it working without any errors?? can you post a sample code over here.

androidvk avatar May 03 '20 16:05 androidvk

@gowthami77 i tried to use library but it requires minsdk 24 so its useless.

ShankyPatel avatar May 22 '20 07:05 ShankyPatel

@gowthami77 i tried to use library but it requires minsdk 24 so its useless. Its main releases support API 24+ but you can use LTS for minsdk 16+ i.e. use implementation 'com.arthenica:mobile-ffmpeg-full:4.4.LTS' instead of implementation 'com.arthenica:mobile-ffmpeg-full:4.4'

DevMayur avatar Dec 30 '20 18:12 DevMayur

I found this library and it worked well on Android 10 https://github.com/tanersener/mobile-ffmpeg

can you please share how to implement this library because I can't find implementation about this library? Thanks

bilal96aslam avatar Mar 19 '21 13:03 bilal96aslam

@gowthami77 i tried to use library but it requires minsdk 24 so its useless.

but in his documentation saying it requires API 16+

bilal96aslam avatar Mar 19 '21 13:03 bilal96aslam

I solved the same problem as below. I solved the problem by using a different library.

https://github.com/tanersener/mobile-ffmpeg

yyms3275 avatar May 13 '21 04:05 yyms3275

I solved the same problem as below. I solved the problem by using a different library.

https://github.com/tanersener/mobile-ffmpeg

Yeah. I started using this lib too. The problem has gone. Everything works fine on Android 11.

dmitriy-chernysh avatar May 13 '21 07:05 dmitriy-chernysh

Use below library, https://github.com/tanersener/ffmpeg-kit/tree/main/android

and put below packagingOptions in build.gradle:

packagingOptions { pickFirst 'lib/x86/libswscale.so' pickFirst 'lib/x86/libavcodec.so' pickFirst 'lib/x86_64/libavutil.so' pickFirst 'lib/armeabi-v7a/libswscale_neon.so' pickFirst 'lib/x86_64/libavcodec.so' pickFirst 'lib/x86_64/libswscale.so' pickFirst 'lib/x86_64/libavformat.so' pickFirst 'lib/x86/libavfilter.so' pickFirst 'lib/x86/libswresample.so' pickFirst 'lib/arm64-v8a/libavcodec.so' pickFirst 'lib/armeabi-v7a/libavfilter_neon.so' pickFirst 'lib/arm64-v8a/libavformat.so' pickFirst 'lib/x86/libavformat.so' pickFirst 'lib/arm64-v8a/libavutil.so' pickFirst 'lib/x86_64/libavdevice.so' pickFirst 'lib/arm64-v8a/libavfilter.so' pickFirst 'lib/x86_64/libswresample.so' pickFirst 'lib/arm64-v8a/libswscale.so' pickFirst 'lib/x86/libavdevice.so' pickFirst 'lib/x86/libavutil.so' pickFirst 'lib/armeabi-v7a/libavcodec_neon.so' pickFirst 'lib/x86_64/libavfilter.so' pickFirst 'lib/arm64-v8a/libswresample.so' pickFirst 'lib/arm64-v8a/libavdevice.so' }

it will solve your problem

FFmpegSession session = FFmpegKit.execute(command); if (ReturnCode.isSuccess(session.getReturnCode())) { progressbar_popup.setVisibility(View.GONE); pop_txt_progress.setVisibility(View.GONE); refreshAndroidGallery(Uri.parse(destPath)); // renameDialog.dismiss(); if (!PreferenceManager.getRemove()) { viewfullpagead(); } else { renameDialog.dismiss(); //card_pre_video.setVisibility(View.GONE); refreshwaiting(); //startActivity(new Intent(Home.this,VideoFolderList.class)); } } if (ReturnCode.isCancel(session.getReturnCode())) { }

this work for android 11 also (tested)

natvar97 avatar Nov 27 '21 20:11 natvar97