2021-03-03 10:11:19 +02:00
|
|
|
# FFmpegKit for Android
|
|
|
|
|
|
|
|
### 1. Features
|
2021-03-04 02:31:47 +02:00
|
|
|
- Supports `API Level 24+` on Main releases and `API Level 16+` on LTS releases
|
|
|
|
- Includes `arm-v7a`, `arm-v7a-neon`, `arm64-v8a`, `x86` and `x86_64` architectures
|
|
|
|
- Can handle Storage Access Framework (SAF) Uris
|
2021-03-03 10:11:19 +02:00
|
|
|
- Camera access on [supported devices](https://developer.android.com/ndk/guides/stable_apis#camera)
|
|
|
|
- Builds shared native libraries (.so)
|
|
|
|
- Creates Android archive with .aar extension
|
|
|
|
|
|
|
|
### 2. Building
|
|
|
|
|
|
|
|
Run `android.sh` at project root directory to build `ffmpeg-kit` and `ffmpeg` shared libraries.
|
|
|
|
|
2021-03-04 02:31:47 +02:00
|
|
|
Please note that `FFmpegKit` project repository includes the source code of `FFmpegKit` only. `android.sh` needs
|
|
|
|
network connectivity and internet access to `github.com` in order to download the source code of `FFmpeg` and
|
|
|
|
external libraries enabled.
|
|
|
|
|
2021-03-03 10:11:19 +02:00
|
|
|
#### 2.1 Prerequisites
|
|
|
|
|
2021-03-04 02:31:47 +02:00
|
|
|
`android.sh` requires the following tools and packages.
|
2021-03-03 10:11:19 +02:00
|
|
|
|
2021-03-04 02:31:47 +02:00
|
|
|
##### 2.1.1 Android Tools
|
|
|
|
- Android SDK Build Tools
|
2022-01-03 11:45:25 +02:00
|
|
|
- Android NDK r22b or later with LLDB and CMake (See [#292](https://github.com/tanersener/ffmpeg-kit/issues/292) if you want to use NDK r23b)
|
2021-03-03 10:11:19 +02:00
|
|
|
|
2021-03-04 02:31:47 +02:00
|
|
|
##### 2.1.2 Packages
|
2021-03-03 10:11:19 +02:00
|
|
|
|
2021-03-04 02:31:47 +02:00
|
|
|
Use your package manager (apt, yum, dnf, brew, etc.) to install the following packages.
|
2021-03-03 10:11:19 +02:00
|
|
|
|
2021-03-04 02:31:47 +02:00
|
|
|
```
|
|
|
|
autoconf automake libtool pkg-config curl cmake gcc gperf texinfo yasm nasm bison autogen git wget autopoint meson ninja
|
|
|
|
```
|
|
|
|
|
|
|
|
##### 2.1.3 Environment Variables
|
2021-03-03 10:11:19 +02:00
|
|
|
|
2021-03-04 02:31:47 +02:00
|
|
|
Set `ANDROID_SDK_ROOT` and `ANDROID_NDK_ROOT` environment variables before running `android.sh`.
|
|
|
|
|
|
|
|
```
|
|
|
|
export ANDROID_SDK_ROOT=<Android SDK Path>
|
|
|
|
export ANDROID_NDK_ROOT=<Android NDK Path>
|
|
|
|
```
|
2021-03-03 10:11:19 +02:00
|
|
|
|
|
|
|
#### 2.2 Options
|
|
|
|
|
2021-03-04 02:31:47 +02:00
|
|
|
Use `--enable-<library name>` flag to support additional external or system libraries and
|
2021-03-03 10:11:19 +02:00
|
|
|
`--disable-<architecture name>` to disable architectures you don't want to build.
|
|
|
|
|
|
|
|
```
|
|
|
|
./android.sh --enable-fontconfig --disable-arm-v7a-neon
|
|
|
|
```
|
|
|
|
|
|
|
|
Run `--help` to see all available build options.
|
|
|
|
|
|
|
|
#### 2.3 LTS Binaries
|
|
|
|
|
|
|
|
Use `--lts` option to build lts binaries for each architecture.
|
|
|
|
|
|
|
|
#### 2.4 Build Output
|
|
|
|
|
|
|
|
All libraries created by `android.sh` can be found under the `prebuilt` directory.
|
|
|
|
|
|
|
|
- `Android` archive (.aar file) for `Main` builds is located under the `bundle-android-aar` folder.
|
|
|
|
- `Android` archive (.aar file) for `LTS` builds is located under the `bundle-android-aar-lts` folder.
|
|
|
|
|
|
|
|
### 3. Using
|
|
|
|
|
|
|
|
#### 3.1 Android API
|
|
|
|
|
|
|
|
1. Declare `mavenCentral` repository and add `FFmpegKit` dependency to your `build.gradle` in
|
|
|
|
`ffmpeg-kit-<package name>` pattern. Use one of the `FFmpegKit` package names given in the
|
|
|
|
project [README](https://github.com/tanersener/ffmpeg-kit).
|
|
|
|
|
2021-10-06 22:33:23 +03:00
|
|
|
```yaml
|
2021-03-03 10:11:19 +02:00
|
|
|
repositories {
|
|
|
|
mavenCentral()
|
|
|
|
}
|
|
|
|
|
|
|
|
dependencies {
|
2021-12-29 13:53:56 +02:00
|
|
|
implementation 'com.arthenica:ffmpeg-kit-full:4.5.1'
|
2021-03-03 10:11:19 +02:00
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2021-03-04 02:31:47 +02:00
|
|
|
2. Execute synchronous `FFmpeg` commands.
|
2021-03-03 10:11:19 +02:00
|
|
|
|
2021-10-06 22:33:23 +03:00
|
|
|
```java
|
2021-03-03 10:11:19 +02:00
|
|
|
import com.arthenica.ffmpegkit.FFmpegKit;
|
|
|
|
|
|
|
|
FFmpegSession session = FFmpegKit.execute("-i file1.mp4 -c:v mpeg4 file2.mp4");
|
|
|
|
if (ReturnCode.isSuccess(session.getReturnCode())) {
|
2021-03-04 02:31:47 +02:00
|
|
|
|
2021-03-03 10:11:19 +02:00
|
|
|
// SUCCESS
|
2021-03-04 02:31:47 +02:00
|
|
|
|
2021-03-03 10:11:19 +02:00
|
|
|
} else if (ReturnCode.isCancel(session.getReturnCode())) {
|
2021-03-04 02:31:47 +02:00
|
|
|
|
2021-03-03 10:11:19 +02:00
|
|
|
// CANCEL
|
2021-03-04 02:31:47 +02:00
|
|
|
|
2021-03-03 10:11:19 +02:00
|
|
|
} else {
|
2021-03-04 02:31:47 +02:00
|
|
|
|
2021-03-03 10:11:19 +02:00
|
|
|
// FAILURE
|
|
|
|
Log.d(TAG, String.format("Command failed with state %s and rc %s.%s", session.getState(), session.getReturnCode(), session.getFailStackTrace()));
|
2021-03-04 02:31:47 +02:00
|
|
|
|
2021-03-03 10:11:19 +02:00
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2021-03-04 02:31:47 +02:00
|
|
|
3. Each `execute` call (sync or async) creates a new session. Access every detail about your execution from the
|
|
|
|
session created.
|
|
|
|
|
2021-10-06 22:33:23 +03:00
|
|
|
```java
|
2021-03-04 02:31:47 +02:00
|
|
|
FFmpegSession session = FFmpegKit.execute("-i file1.mp4 -c:v mpeg4 file2.mp4");
|
|
|
|
|
|
|
|
// Unique session id created for this execution
|
|
|
|
long sessionId = session.getSessionId();
|
|
|
|
|
|
|
|
// Command arguments as a single string
|
|
|
|
String command = session.getCommand();
|
|
|
|
|
|
|
|
// Command arguments
|
|
|
|
String[] arguments = session.getArguments();
|
|
|
|
|
|
|
|
// State of the execution. Shows whether it is still running or completed
|
|
|
|
SessionState state = session.getState();
|
|
|
|
|
|
|
|
// Return code for completed sessions. Will be null if session is still running or ends with a failure
|
|
|
|
ReturnCode returnCode = session.getReturnCode();
|
|
|
|
|
|
|
|
Date startTime = session.getStartTime();
|
|
|
|
Date endTime = session.getEndTime();
|
|
|
|
long duration = session.getDuration();
|
|
|
|
|
|
|
|
// Console output generated for this execution
|
|
|
|
String output = session.getOutput();
|
|
|
|
|
|
|
|
// The stack trace if FFmpegKit fails to run a command
|
|
|
|
String failStackTrace = session.getFailStackTrace();
|
|
|
|
|
|
|
|
// The list of logs generated for this execution
|
|
|
|
List<com.arthenica.ffmpegkit.Log> logs = session.getLogs();
|
|
|
|
|
|
|
|
// The list of statistics generated for this execution
|
|
|
|
List<Statistics> statistics = session.getStatistics();
|
|
|
|
```
|
|
|
|
|
|
|
|
4. Execute asynchronous `FFmpeg` commands by providing session specific `execute`/`log`/`session` callbacks.
|
2021-03-03 10:11:19 +02:00
|
|
|
|
2021-10-06 22:33:23 +03:00
|
|
|
```java
|
2021-12-29 13:53:56 +02:00
|
|
|
FFmpegKit.executeAsync("-i file1.mp4 -c:v mpeg4 file2.mp4", new FFmpegSessionCompleteCallback() {
|
2021-03-03 10:11:19 +02:00
|
|
|
|
|
|
|
@Override
|
2021-12-29 13:53:56 +02:00
|
|
|
public void apply(FFmpegSession session) {
|
2021-03-03 10:11:19 +02:00
|
|
|
SessionState state = session.getState();
|
|
|
|
ReturnCode returnCode = session.getReturnCode();
|
|
|
|
|
|
|
|
// CALLED WHEN SESSION IS EXECUTED
|
|
|
|
|
|
|
|
Log.d(TAG, String.format("FFmpeg process exited with state %s and rc %s.%s", state, returnCode, session.getFailStackTrace()));
|
|
|
|
}
|
|
|
|
}, new LogCallback() {
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void apply(com.arthenica.ffmpegkit.Log log) {
|
|
|
|
|
|
|
|
// CALLED WHEN SESSION PRINTS LOGS
|
|
|
|
|
|
|
|
}
|
|
|
|
}, new StatisticsCallback() {
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void apply(Statistics statistics) {
|
|
|
|
|
|
|
|
// CALLED WHEN SESSION GENERATES STATISTICS
|
|
|
|
|
|
|
|
}
|
|
|
|
});
|
|
|
|
```
|
|
|
|
|
2021-03-04 02:31:47 +02:00
|
|
|
5. Execute `FFprobe` commands.
|
|
|
|
|
|
|
|
- Synchronous
|
2021-03-03 10:11:19 +02:00
|
|
|
|
2021-10-06 22:33:23 +03:00
|
|
|
```java
|
2021-03-03 10:11:19 +02:00
|
|
|
FFprobeSession session = FFprobeKit.execute(ffprobeCommand);
|
|
|
|
|
|
|
|
if (!ReturnCode.isSuccess(session.getReturnCode())) {
|
|
|
|
Log.d(TAG, "Command failed. Please check output for the details.");
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2021-03-04 02:31:47 +02:00
|
|
|
- Asynchronous
|
|
|
|
|
2021-10-06 22:33:23 +03:00
|
|
|
```java
|
2021-12-29 13:53:56 +02:00
|
|
|
FFprobeKit.executeAsync(ffprobeCommand, new FFprobeSessionCompleteCallback() {
|
2021-03-04 02:31:47 +02:00
|
|
|
|
|
|
|
@Override
|
2021-12-29 13:53:56 +02:00
|
|
|
public void apply(FFprobeSession session) {
|
2021-03-04 02:31:47 +02:00
|
|
|
|
|
|
|
CALLED WHEN SESSION IS EXECUTED
|
|
|
|
|
|
|
|
}
|
|
|
|
});
|
|
|
|
```
|
|
|
|
|
|
|
|
6. Get media information for a file.
|
2021-03-03 10:11:19 +02:00
|
|
|
|
2021-10-06 22:33:23 +03:00
|
|
|
```java
|
2021-03-04 02:31:47 +02:00
|
|
|
MediaInformationSession mediaInformation = FFprobeKit.getMediaInformation("<file path or uri>");
|
|
|
|
mediaInformation.getMediaInformation();
|
2021-03-03 10:11:19 +02:00
|
|
|
```
|
|
|
|
|
2021-03-04 02:31:47 +02:00
|
|
|
7. Stop ongoing `FFmpeg` operations.
|
2021-03-03 10:11:19 +02:00
|
|
|
|
|
|
|
- Stop all executions
|
2021-10-06 22:33:23 +03:00
|
|
|
```java
|
2021-03-03 10:11:19 +02:00
|
|
|
FFmpegKit.cancel();
|
|
|
|
```
|
|
|
|
- Stop a specific session
|
2021-10-06 22:33:23 +03:00
|
|
|
```java
|
2021-03-03 10:11:19 +02:00
|
|
|
FFmpegKit.cancel(sessionId);
|
|
|
|
```
|
|
|
|
|
2021-03-04 02:31:47 +02:00
|
|
|
8. Convert Storage Access Framework (SAF) Uris into paths that can be read or written by `FFmpegKit`.
|
2021-05-06 22:50:38 +03:00
|
|
|
- Reading a file:
|
|
|
|
|
2021-10-06 22:33:23 +03:00
|
|
|
```java
|
2021-05-06 22:50:38 +03:00
|
|
|
Uri safUri = intent.getData();
|
|
|
|
String inputVideoPath = FFmpegKitConfig.getSafParameterForRead(requireContext(), safUri);
|
|
|
|
FFmpegKit.execute("-i " + inputVideoPath + " -c:v mpeg4 file2.mp4");
|
|
|
|
```
|
|
|
|
|
|
|
|
- Writing to a file:
|
|
|
|
|
2021-10-06 22:33:23 +03:00
|
|
|
```java
|
2021-05-06 22:50:38 +03:00
|
|
|
Uri safUri = intent.getData();
|
|
|
|
String outputVideoPath = FFmpegKitConfig.getSafParameterForWrite(requireContext(), safUri);
|
|
|
|
FFmpegKit.execute("-i file1.mp4 -c:v mpeg4 " + outputVideoPath);
|
|
|
|
```
|
2021-03-03 10:11:19 +02:00
|
|
|
|
2021-12-29 13:53:56 +02:00
|
|
|
- Writing to a file in a custom mode.
|
|
|
|
|
|
|
|
```java
|
|
|
|
Uri safUri = intent.getData();
|
|
|
|
String path = FFmpegKitConfig.getSafParameter(requireContext(), safUri, "rw");
|
|
|
|
FFmpegKit.execute("-i file1.mp4 -c:v mpeg4 " + path);
|
|
|
|
```
|
|
|
|
|
2021-03-04 02:31:47 +02:00
|
|
|
9. Get previous `FFmpeg` and `FFprobe` sessions from session history.
|
2021-03-03 10:11:19 +02:00
|
|
|
|
2021-10-06 22:33:23 +03:00
|
|
|
```java
|
2021-03-04 02:31:47 +02:00
|
|
|
List<Session> sessions = FFmpegKitConfig.getSessions();
|
|
|
|
for (int i = 0; i < sessions.size(); i++) {
|
|
|
|
Session session = sessions.get(i);
|
|
|
|
Log.d(TAG, String.format("Session %d = id:%d, startTime:%s, duration:%s, state:%s, returnCode:%s.",
|
2021-03-03 10:11:19 +02:00
|
|
|
i,
|
|
|
|
session.getSessionId(),
|
|
|
|
session.getStartTime(),
|
|
|
|
session.getDuration(),
|
|
|
|
session.getState(),
|
|
|
|
session.getReturnCode()));
|
2021-03-04 02:31:47 +02:00
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
10. Enable global callbacks.
|
|
|
|
|
2021-12-29 13:53:56 +02:00
|
|
|
- Session type specific Complete Callbacks, called when an async session has been completed
|
2021-03-04 02:31:47 +02:00
|
|
|
|
2021-10-06 22:33:23 +03:00
|
|
|
```java
|
2021-12-29 13:53:56 +02:00
|
|
|
FFmpegKitConfig.enableFFmpegSessionCompleteCallback(new FFmpegSessionCompleteCallback() {
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void apply(FFmpegSession session) {
|
|
|
|
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
FFmpegKitConfig.enableFFprobeSessionCompleteCallback(new FFprobeSessionCompleteCallback() {
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void apply(FFprobeSession session) {
|
|
|
|
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
FFmpegKitConfig.enableMediaInformationSessionCompleteCallback(new MediaInformationSessionCompleteCallback() {
|
2021-03-04 02:31:47 +02:00
|
|
|
|
|
|
|
@Override
|
2021-12-29 13:53:56 +02:00
|
|
|
public void apply(MediaInformationSession session) {
|
2021-03-04 02:31:47 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
});
|
|
|
|
```
|
|
|
|
|
|
|
|
- Log Callback, called when a session generates logs
|
|
|
|
|
2021-10-06 22:33:23 +03:00
|
|
|
```java
|
2021-03-04 02:31:47 +02:00
|
|
|
FFmpegKitConfig.enableLogCallback(new LogCallback() {
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void apply(final com.arthenica.ffmpegkit.Log log) {
|
|
|
|
...
|
|
|
|
}
|
|
|
|
});
|
|
|
|
```
|
|
|
|
|
|
|
|
- Statistics Callback, called when a session generates statistics
|
|
|
|
|
2021-10-06 22:33:23 +03:00
|
|
|
```java
|
2021-03-04 02:31:47 +02:00
|
|
|
FFmpegKitConfig.enableStatisticsCallback(new StatisticsCallback() {
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void apply(final Statistics newStatistics) {
|
|
|
|
...
|
|
|
|
}
|
|
|
|
});
|
|
|
|
```
|
|
|
|
|
|
|
|
11. Ignore the handling of a signal. Required by `Mono` and frameworks that use `Mono`, e.g. `Unity` and `Xamarin`.
|
|
|
|
|
2021-10-06 22:33:23 +03:00
|
|
|
```java
|
2021-03-04 02:31:47 +02:00
|
|
|
FFmpegKitConfig.ignoreSignal(Signal.SIGXCPU);
|
|
|
|
```
|
|
|
|
|
|
|
|
12. Register system fonts and custom font directories.
|
|
|
|
|
2021-10-06 22:33:23 +03:00
|
|
|
```java
|
2021-03-04 02:31:47 +02:00
|
|
|
FFmpegKitConfig.setFontDirectoryList(context, Arrays.asList("/system/fonts", "<folder with fonts>"), Collections.EMPTY_MAP);
|
|
|
|
```
|
2021-03-03 10:11:19 +02:00
|
|
|
|
|
|
|
### 4. Test Application
|
|
|
|
|
2021-03-04 02:31:47 +02:00
|
|
|
You can see how `FFmpegKit` is used inside an application by running `Android` test applications developed under the
|
2021-03-03 10:11:19 +02:00
|
|
|
[FFmpegKit Test](https://github.com/tanersener/ffmpeg-kit-test) project.
|