FFmpeg4Android is a way your application can run FFmpeg commands, only Java, no need for C code, or NDK.
=== new ========
New version 25.24
Android Studio full demo with ffmpeg4android_lib
Download version 25.24 https://drive.google.com/file/d/0B9qAjo6wKhk9MEpNNjMxaG5oWXM/view?usp=sharing
Running FFmpeg4Android demo project (Android Studio)
Just Download the Android Studio project above, open the zip, build, and run to your device.
Add FFmpeg support to your app (Android Studio)
Simple Guide with screenshots https://docs.google.com/document/d/1FWMAT3FbCXlW_91d6Ek_UCD_CtYJWxYaDVY2hE6Onig/edit?usp=sharing
Commands Examples
This commands verified to work with FFmpeg4Android:
This are only some basic examples, a lot more is possible…
Video Compress:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66// simple regular commad
ffmpeg -y -i /sdcard/videokit/in.mp4 -strict experimental -s 160x120 -r 25 -vcodec mpeg4 -b 150k -ab 48000 -ac 2 -ar 22050 /sdcard/videokit/out.mp4
// compress with h264 (to support chrome)
ffmpeg -y -i /sdcard/videokit/in.mp4 -strict experimental -vcodec libx264 -preset ultrafast -crf 24 -acodec aac -ar 44100 -ac 2 -b:a 96k -s 320x240 -aspect 4:3 /sdcard/videokit/out3.mp4
// As complex command, don't forget to use setCommandComplex(complexCommand)
// Use this format to support files that contains spaces and special characters
String[] complexCommand = {"ffmpeg","-y" ,"-i", "/sdcard/video kit/in.mp4","-strict","experimental","-s", "160x120","-r","25", "-vcodec", "mpeg4", "-b", "150k", "-ab","48000", "-ac", "2", "-ar", "22050", "/sdcard/video kit/out.mp4"};
The parameters that control the quality are the -s (resolution, currently set on 160x120) and the -b (the bitrate, currently set on 150k).
Increase them, e.g -s 480x320
And -b 900k
To improve quality (and get less compression).?
Note that you will need to use the Extras (see below) distribution to use this command.
Audio Compression
String commandStr = "ffmpeg -y -i /sdcard/vk2/in.wav -ar 44100 -ac 2 -ab 64k -f mp3 /sdcard/videokit/out.mp3";
Audio Trim (Crop)
String commandStr ={"ffmpeg","-y","-i","/storage/emulated/0/vk2/in.mp3","-strict","experimental","-acodec","copy","-ss","00:00:00","-t","00:00:03.000","/storage/emulated/0/videokit/out.mp3"};
Video Rotate (90 degrees cw):
ffmpeg -y -i /sdcard/videokit/in.mp4 -strict experimental -vf transpose=1 -s 160x120 -r 30 -aspect 4:3 -ab 48000 -ac 2 -ar 22050 -b 2097k /sdcard/video_output/out.mp4
Video Crop:
ffmpeg -y -i /sdcard/videokit/short.mp4 -strict experimental -vf crop=100:100:0:0 -s 320x240 -r 15 -aspect 3:4 -ab 12288 -vcodec mpeg4 -b 2097152 -sample_fmt s16 /sdcard/videokit/out.mp4
Extract Picture from Video:
ffmpeg -y -i /sdcard/videokit/in.mp4 -strict experimental -an -r 1/2 -ss 00:00:00.000 -t 00:00:03 /sdcard/videokit/filename%03d.jpg
Extract Sound from Video:
ffmpeg -y -i /sdcard/videokit/in.avi -strict experimental -acodec copy /sdcard/videokit/out.mp3
ffmpeg -y -i /sdcard/videokit/in.mp4 -strict experimental -vn -ar 44100 -ac 2 -ab 256k -f mp3 /sdcard/videokit/out.mp3
Re-encode Audio in Video:
ffmpeg -y -i /sdcard/in.mp4 -strict experimental -vcodec copy -acodec libmp3lame -ab 64k -ac 2 -b 1200000 -ar 22050 /sdcard/out.mp4
Change Video Resolution:
ffmpeg -y -i /sdcard/in.mp4 -strict experimental -vf transpose=3 -s 320x240 -r 15 -aspect 3:4 -ab 12288 -vcodec mpeg4 -b 2097152 -sample_fmt s16 /sdcard/out.mp4
Cut time segment from Video:
ffmpeg -ss 00:00:01.000 -y -i /sdcard/videokit/in.mp4 -strict experimental -t 00:00:02.000 -s 320x240 -r 15 -vcodec mpeg4 -b 2097152 -ab 48000 -ac 2 -b 2097152 -ar 22050 /sdcard/videokit/out.mp4
Transcode Audio:
ffmpeg -y -i /sdcard/videokit/big.wav /sdcard/videokit/small.mp3
WaterMark:
// test with watermark.png 128x128, add it to /sdcard/videokit/
String[] complexCommand = {"ffmpeg","-y" ,"-i", "/sdcard/videokit/in.mp4","-strict","experimental", "-vf", "movie=/sdcard/videokit/watermark.png [watermark]; [in][watermark] overlay=main_w-overlay_w-10:10 [out]","-s", "320x240","-r", "30", "-b", "15496k", "-vcodec", "mpeg4","-ab", "48000", "-ac", "2", "-ar", "22050", "/sdcard/videokit/out.mp4"};
Streaming simple:
* note that all streaming examples will need to add internet permission to the Android project Manifest file ( <uses-permission android:name="android.permission.INTERNET" /> )
// use this command on ffmpeg4android
ffmpeg -i /sdcard/videokit/2.mpg -strict experimental -f mpegts udp://192.168.0.114:8090
// You can use any player that supports streaming, on the target machine, to play the stream, in this case we used ffplay
ffplay -f mpegts -ast 1 -vst 0 -ar 48000 udp://192.168.0.114:8090
STREAM RAW VIDEO FROM CAMERA PREVIEW:
GETTING THE RAW STREAM FROM THE CAMERA PREVIEW:1
2
3
4
5
6
7
8
9
10
11
12
13Parameters parameters = camera.getParameters();
imageFormat = parameters.getPreviewFormat();
if (imageFormat == ImageFormat.NV21) {
Camera.Size previewSize = parameters.getPreviewSize();
frameWidth = previewSize.width;
frameHeight = previewSize.height;
Rect rect = new Rect(0, 0, frameWidth, frameHeight);
YuvImage img = new YuvImage(data, ImageFormat.NV21, frameWidth, frameHeight, null);
try {
outStream.write(data);
outStream.flush();
}
}
ENCODE AND STREAM USING FFMPEG4ANDROID:1
"ffmpeg -f rawvideo -pix_fmt nv21 -s 640x480 -r 15 -i " + Environment.getExternalStorageDirectory().getAbsolutePath().toString() + "/yuv.data rtmp://host/stream.flv"
STREAM ON ONE DEVICE, RECEIVE A STEAM ON SECOND DEVICE AND SAVE IT:
on the first device:1
ffmpeg -i /sdcard/one3.mp4 -f mpegts udp://192.168.0.107:8090
on the second device:1
String[] complexCommand = {"ffmpeg","-y" ,"-i", "udp://192.168.0.108:8090","-strict","experimental","-crf", "30","-preset", "ultrafast", "-acodec", "aac", "-ar", "44100", "-ac", "2", "-b:a", "96k", "-vcodec", "libx264", "-r", "25", "-b:v", "500k", "-f", "flv", "/sdcard/videokit/t.flv"};
- this needs internet permission in the manifest.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123H264 encoding:
ffmpeg -y -i /sdcard/Video/1.MTS -strict experimental -vcodec libx264 -preset ultrafast -crf 24 /sdcard/videokit/out.mp4
ffmpeg -y -i /sdcard/videokit/m.mkv -strict experimental -vcodec libx264 -preset ultrafast -crf 24 -sn /sdcard/videokit/m2.mkv
Subtitles:
ffmpeg -y -i /sdcard/videokit/m2.mkv -i /sdcard/videokit/in.srt -strict experimental -vcodec libx264 -preset ultrafast -crf 24 -scodec copy /sdcard/videokit/mo.mkv
ffmpeg -y -i /sdcard/videokit/m2.mkv -i /sdcard/videokit/in.srt -strict experimental -scodec copy /sdcard/videokit/outm3.mkv
Convert Audio file to m4a
ffmpeg -i /sdcard/videokit/in.mp3 /sdcard/videokit/out.m4a
Encode h264 video and aac audio in one command
ffmpeg -y -i /sdcard/videokit/in.mp4 -strict experimental -vcodec libx264 -crf 24 -acodec aac /sdcard/videokit/out.mkv
Vintage filter
commandStr = "ffmpeg -y -i /sdcard/videokit/in.mp4 -strict experimental -vf curves=vintage -s 640x480 -r 30 -aspect 4:3 -ab 48000 -ac 2 -ar 22050 -b 2097k -vcodec mpeg4 /sdcard/videokit/curve.mp4";
Black & White filter (Gray Scale):
commandStr = "ffmpeg -y -i /sdcard/videokit/in.mp4 -strict experimental -vf hue=s=0 -vcodec mpeg4 -b 2097152 -s 320x240 -r 30 /sdcard/videokit/out.mp4";
Sepia using colorchannelmixer
String[] complexCommand = {"ffmpeg","-y" ,"-i", "/sdcard/videokit/sample.mp4","-strict", "experimental", "-filter_complex",
"[0:v]colorchannelmixer=.393:.769:.189:0:.349:.686:.168:0:.272:.534:.131[colorchannelmixed];[colorchannelmixed]eq=1.0:0:1.3:2.4:1.0:1.0:1.0:1.0[color_effect]",
"-map", "[color_effect]","-map", "0:a", "-vcodec", "mpeg4","-b", "15496k", "-ab", "48000", "-ac", "2", "-ar", "22050","/sdcard/videokit/out.mp4"};
Create filter using Photoshop curves
You can create some effects using Photoshop curves operation (Image, Adjustments, Curves (CNTL + M)), export the curves acv file, and apply it using this command:
String[]
complexCommand={"ffmpeg","-y","-i","/storage/emulated/0/vk2/in.mp4","-strict","experimental","-vf","curves=psfile=/storage/emulated/0/videokit/sepia.acv","-b","2097k","-vcodec","mpeg4","-ab","48000","-ac","2","-ar","22050","/storage/emulated/0/videokit/out.mp4"}?
Fade in and out transition
String[] complexCommand = {"ffmpeg","-y" ,"-i", "/sdcard/videokit/in.m4v","-acodec", "copy", "-vf", "fade=t=in:st=0:d=5, fade=t=out:st=20:d=5", "/sdcard/videokit/out.mp4"};
Join 2 files using of the same size using filter_complex
String[] complexCommand = {"ffmpeg","-y","-i", "/sdcard/videokit/in1.mp4", "-i", "/sdcard/videokit/in2.mp4", "-strict","experimental", "-filter_complex", "[0:0] [0:1] [1:0] [1:1] concat=n=2:v=1:a=1", "/sdcard/videokit/out.mp4"};
// concat videos with different codecs, and different sizes, different rate and different aspect ratio:
String[] complexCommand = {"ffmpeg","-y","-i","/storage/emulated/0/videokit/sample.mp4",
"-i","/storage/emulated/0/videokit/in.mp4","-strict","experimental",
"-filter_complex",
"[0:v]scale=640x480,setsar=1:1[v0];[1:v]scale=640x480,setsar=1:1[v1];[v0][0:a][v1][1:a] concat=n=2:v=1:a=1",
"-ab","48000","-ac","2","-ar","22050","-s","640x480","-r","30","-vcodec","mpeg4","-b","2097k","/storage/emulated/0/vk2_out/out.mp4"}
Create a video from pictures
// note that this commans is sensative to the pictures size.
// all pictures should be with the same size, and correspond to s peticular video resolution.
// e.g hd video is: 1280x720, and this should be the pictures size
commandStr = "ffmpeg -y -r 1/5 -i /sdcard/videokit/pic00%d.jpg /sdcard/videokit/out.mp4";
Advanced filtering
String[] complexCommand = {"ffmpeg","-y" ,"-i", "/sdcard/videokit/in.mp4","-strict","experimental", "-vf", "crop=iw/2:ih:0:0,split[tmp],pad=2*iw[left]; [tmp]hflip[right]; [left][right] overlay=W/2", "-vb", "20M", "-r", "23.956", "/sdcard/videokit/out.mp4"};
Increase video and audio speed
String[] complexCommand = {"ffmpeg","-y" ,"-i", "/sdcard/videokit/in.mp4","-strict","experimental", "-filter_complex", "[0:v]setpts=0.5*PTS[v];[0:a]atempo=2.0[a]","-map","[v]","-map","[a]", "-b", "2097k","-r","60", "-vcodec", "mpeg4", "/sdcard/videokit/out.mp4"};
Overlay 2 videos side by side
String[] complexCommand = {"ffmpeg","-y" ,"-i", "/sdcard/Movies/sample.mp4","-i", "/sdcard/Movies/sample2.mp4", "-strict","experimental",
"-filter_complex",
"[0:v:0]pad=iw*2:ih[bg];" +
"[bg][1:v:0]overlay=w",
"-s", "320x240","-r", "30", "-b", "15496k", "-vcodec", "mpeg4","-ab", "48000", "-ac", "2", "-ar", "22050",
"/sdcard/videokit/out.mp4"};
vintage + watermark
String[] complexCommand = {"ffmpeg","-y" ,"-i", "/sdcard/Movies/sample.mp4","-strict","experimental",
"-vf",
"movie=/sdcard/videokit/watermark002.png [watermark];" +
"[in][watermark] overlay=main_w-overlay_w-10:10 [out_overlay];" +
"[out_overlay]curves=vintage[out]",
"-s", "320x240","-r", "30", "-b", "15496k", "-vcodec", "mpeg4","-ab", "48000", "-ac", "2", "-ar", "22050",
"/sdcard/videokit/out_water_vinta.mp4"};
Watermark + audio replace
String[] complexCommand = {"ffmpeg","-y" ,"-i", "/sdcard/Movies/sample.mp4","-i", "/sdcard/videokit/in.mp3", "-strict","experimental",
"-filter_complex",
"[1:a]atempo=1.0[a1];" +
"movie=/sdcard/videokit/watermark002.png [watermark];" +
"[0:v][watermark] overlay=main_w-overlay_w-10:10 [outv]",
"-map", "[outv]", "-map", "[a1]",
"-s", "320x240","-r", "30", "-b", "15496k", "-vcodec", "mpeg4","-ab", "48000", "-ac", "2", "-ar", "22050",
"-shortest","/sdcard/videokit/out_water.mp4"};
Looping on a picture and adding audio with audio encoding support
String commandStr = "ffmpeg -y -loop 1 -i /sdcard/videokit/pic001.jpg -i /sdcard/videokit/in.mp3 -strict experimental -s 1270x720 -r 25 -aspect 16:9 -vcodec mpeg4 -vcodec mpeg4 -ab 48000 -ac 2 -b 2097152 -ar 22050 -shortest /sdcard/videokit/out2.mp4";
Replacing video audio track, using the shotest (video or audio)
String[] complexCommand = {"ffmpeg","-y" ,"-i", "/sdcard/videokit/sample.mp4","-i", "/sdcard/videokit/in.mp3", "-strict","experimental",
"-map", "0:v", "-map", "1:a",
"-s", "320x240","-r", "30", "-b", "15496k", "-vcodec", "mpeg4","-ab", "48000", "-ac", "2", "-ar", "22050","-shortest","/sdcard/videokit/out.mp4"};
Animated GIF
//Compress animated gif:
fmpeg -f gif -i /sdcard/videokit/pic1.gif -strict experimental -r 10 /sdcard/videokit/pic1_1.gif
//Convert mp4 to animated gif:
ffmpeg -y -i /sdcard/videokit/in.mp4 -strict experimental -r 20 /sdcard/videokit/out.gif
//Convert animated gif to mp4:
ffmpeg -y -f gif -i /sdcard/videokit/infile.gif /sdcard/videokit/outfile.mp4
ARM64-v8a support
Arm64-v8a is part of the extras (see related section).
Note that although many devices claim they have 64bit support, they don’t.
To verify a device does use arm64 architecture, open telnet on the device (or use adb), then run:
getprop ro.product.cpu.abi
if you get: armv7a, then you have 32bit architecture.
if you get: arm64-v8a, then you have 64bit architecture.
Using complex commands
A complex command is a command that contains embedded elements (in most cases it uses quotations for that.
Here is an example:1
ffmpeg -i /sdcard/videokit/in.mp4 -aspect 1:1 -vf split "[main][tmp];[tmp] crop=iw/2:ih:0:0, hflip[tp],[tp] pad=2*iw[left]; [main] crop=iw/2:ih:iw/2:0[right]; [left][right] overlay=W/2" -vb 20M -r 23.956 /sdcard/videokit/outs.mp4
To set a complex command you should create a String array and use the method:1
setCommandComplex()
Like this:1
2
3String[] complexCommand = {"ffmpeg","-y" ,"-i", "/sdcard/videokit/mo.mkv","-strict","experimental", "-vf", "crop=iw/2:ih:0:0,split[tmp],pad=2*iw[left]; [tmp]hflip[right]; [left][right] overlay=W/2", "-vb", "20M", "-r", "23.956", "/sdcard/videokit/out.mp4"};
setCommandComplex(complexCommand);
Size Tuning - Keeping your app apk small
FFmpeg4Android lib contains a build specifically to tackle this issue - Small App Size.
This build contains a subset of FFmpeg4Android that contains features (filters, coders, encoders, muxers etc), That are the most commonly used - the “essentials build”.
This build is great for apps that need to keep small size.
All the performance benefits of the full ffmpeg4android are kept.
To use this build, goto ffmpeg_lib libs/armeabi-v7a folder,
simply rename libvideokit.so to libvideokit.so.full,
and rename libvideokit.so.ess to libvideokit.so
Refresh the ffmpeg_lib project, and build your project (that use the ffmpeg_lib).
Your project will now use the essentials build, and you will see that the apk created is much smaller in size.
You can also remove the libs/x86 folder, x86 devices will still work using the Houdini ARM real time translation, of course it will be slower then using the native.
You can also remove the libs/arm64-v8a folder since currently most devices does not support this platform, and all that do have backward compatibility with armeabi-v7a.
Make sure you test and see that all the functionality you need is still there.
Performance Tuning
Prefer mpeg4 codec (-codec mpeg4), since with this codec, ffmpeg4android can utilize all device cores.
Here is an example of command that use mpeg4 codec (in this case this command is doing a video compression):1
String[] complexCommand = {"ffmpeg","-y" ,"-i", "/sdcard/video kit/in.mp4","-strict","experimental","-s", "160x120","-r","25", "-vcodec", "mpeg4", "-b", "150k", "-ab","48000", "-ac", "2", "-ar", "22050", "/sdcard/video kit/out.mp4"};
If its possible, reduce output video resolution (-s) and bitrate (-b), the lower the video quality is, the faster it will take to produce.
Encoding using h264 can’t utilize all device cores, and thus is much slower. If you must use it, use the -preset ultrafast flag.
Using the -threads flag can only degrade the performance, as ffmpeg4android automatically detects the number of cores your device has, and set the optimal number of threads for the command used.
Extras
Contains extra functionality that is not part of the standard distribution.
Download
x264 encoding support.
To use it, download the extras distribution, extract it, it will look like this:
– libs
– armeabi-v7a
– libvideokit.so.full_with_x264
– x86
– libvideokit.so.full_with_x264
Copy the libvideokit.so.full_with_x264 to the ffmpeg4android_lib/libs to the coresponding architecture folder. Rename it to libvideokit.so, (replacing your current libvideokit.so).
Note that you must change your project manifest (not the lib manifest) to target api 22 or below, or the library will crash on Android 6 and above devices.
x86 Support
The standard Distribution is using Houdini Runtime ARM Intel translation.
To use native x86 follow the above (x264) explanations to copy the x86 so files.
This will also make it possible to run on x86 fast emulator (which does not have Houdini support).
You need to add the abiFilter “x86” in your project module defaultConfig:1
2
3
4
5
6
7
8
9defaultConfig {
applicationId "com.examples.ffmpeg4android_demo"
minSdkVersion 16
targetSdkVersion 23
ndk {
abiFilter "armeabi-v7a"
abiFilter "x86"
}
}
Note that you must change your project manifest (not the lib manifest) to target api 22 or below, or the library will crash on Android 6 and above devices.
arm64-v8a
The same as the x86 support, just add the “abiFilter arm64-v8a”
Note that we don’t recommend using this as our tests showed no significant performance improvement
from armeabi-v7a
FFmpeg Command Correctness
FFmpeg command can be quite complex, and in some cases, getting it right can be hard.
To help you with that, FFmpeg4Android will create a file called vk.log that contains the ffmpeg log in the device demo folder (by default /sdcard/videokit) This log can be very useful when understanding what is wrong with the FFmpeg command, and in most cases it will pinpoint you to the section in the command that is wrong.
If you still don’t understand what is wrong with your command, make sure the command works on a windows machine, and you can email us the command for inspection.
Known issues & TroubleShooting
- x264 encoding was moved to the extras, so using this encoder will throw CommandValidationException.
Its recommended to use the mpeg4 encoder instead, its much faster.
If you still need to use the x264 encoding, read the extras section to understand how to add it to your project.
- If you use only armeabi-v7a you must make sure all your other JNI libs are restricted to this architecture, If not the loader will fail to find the library so file (it will try to find it in x64 folders).
To do it, add this configuration in your module build.gradle (under defaultConfig):1
2
3ndk {
abiFilter "armeabi-v7a"
}
Latest version release notes
25.24
Patch 6
- Simple example android 6 fix.
- MultipleCommandsExample crash fix.
- Version code fix.
Patch 4
- Removed x86 (moved to extras).
abiFilter “x86” commented out
arm64-v8a moved to extras
- Fixed more issues related Android 6.
Loader improvements.
Support for targeting api 23 and above.
- Performance and size improvements.
- ess size reduced and added support for fade filter and image2 demuxer
to support creation of video from images. - Support for x264 detection in the validation mechanism.
- x264 encoding support is moved to extras,