ffmpeg-android-java
ffmpeg-android-java copied to clipboard
Cannot run program "/data/user/0/com.package.name/files/ffmpeg": error=13, Permission denied
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.
Hey,
- 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);
}
- 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();
} }
is this worked for u?
Any updates on this?
I suppose its the limitation of Android Q and target api 29, as the library executes binary file. Indeed it looks really tricky
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, I would recommend you to try MediaCodec API, it really works as ffmpeg. The only limitation is api 18+.
Does anybody got the solution for this ? I am also stuck here
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+
i recommend google/android to remove this scoped storage shit. Same issue...
Hi............ Does anybody got the solution for this ? I am also stuck here.. Please send me reply
@gowthami77 the temporary solution is set target api < 29 or you can use MediaCodec API.
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!
Also stuck on this issue. If anybody found a solution except for adding targetSdk 28. Please any help will be appreaciated.
@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.
I found this library and it worked well on Android 10 https://github.com/tanersener/mobile-ffmpeg
@gowthami77 is it working without any errors?? can you post a sample code over here.
@gowthami77 i tried to use library but it requires minsdk 24 so its useless.
@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'
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
@gowthami77 i tried to use library but it requires minsdk 24 so its useless.
but in his documentation saying it requires API 16+
I solved the same problem as below. I solved the problem by using a different library.
https://github.com/tanersener/mobile-ffmpeg
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.
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)