FFmpeg is an open source computer program that can record, convert, and stream digital audio and video. It provides a complete solution for recording, converting and streaming audio and video.
1. Write at the front
In the last article “Android NDK development (3) in the Linux environment to compile FFmpeg”, we learned how to compile FFmpeg source into so files, but these so files can not be directly referenced to the Android project, but also need to compile again to use. Today let’s learn how to port FFmpeg to Android and how to use FFmpeg from the command line in Android projects.
2. Environment construction
OS: Windows 10 64bit
NDK version: Android-NdK-R14B-Windows-x86_64
FFmpeg version: 3.4.2
Compiler: Android Studio 3.0
NDK build tool: CMake
Create an FFmpeg project
When creating a project, check the Include C++ support option and go all the way to the Customize C++ support Settings page:
You can see three options:
-
C++ Standard: C++ Standard. Selecting Toolchain Default will use the Default CMake configuration.
-
Exceptions Support: C++ exception handling is supported. The flag is -fexceptions.
-
Runtime Type Information Support: Runtime Type recognition, marked -FRTTI, enables programs to use Pointers or references to base classes to check the actual derived Type of the object to which these Pointers or references refer.
Here we use the default C++ standard, uncheck the following two options, and click Finish to proceed to the next step to see the structure of the project:
3. The FFmpeg transplantation
The preparatory work
-
Copy the compiled include folder to the SRC \main\ CPP directory.
-
Copy the cmDutils. c, cmDutils. h, FFmpeg. c, ffmpeg.h, ffmpeg_filter.c, and ffmpeg_opt.c classes in the FFTools directory of the FFmpeg source code to the SRC \main\ CPP directory.
-
Copy the compiled so file to SRC \main\jniLibs\armeabi, or create a new one if you don’t have one.
-
Rename native lib. CPP in SRC \main\jniLibs\armeabi to ffmpeg_cmd.c.
-
This article only compiles armeabi so files, so you need to set this up in build.gradle:
android {
...
defaultConfig {
...
ndk {
abiFilters "armeabi"}}}Copy the code
Take a look at the project structure at this point:
Compile the script
Cmakelists.txt: cmakelists.txt: cmakelists.txt: cmakelists.txt
Set the Cmake versionCmake_minimum_required (VERSION 3.4.1 track)Set the CPP directory path
set(CPP_DIR ${CMAKE_SOURCE_DIR}/src/main/cpp)
# set jniLibs directory path
set(LIBS_DIR ${CMAKE_SOURCE_DIR}/src/main/jniLibs)
# add library
add_library( # library name
ffmpeg
# Dynamic library, generate so file
SHARED
# source
${CPP_DIR}/cmdutils.c
${CPP_DIR}/ffmpeg.c
${CPP_DIR}/ffmpeg_filter.c
${CPP_DIR}/ffmpeg_opt.c
${CPP_DIR}/ffmpeg_cmd.c )
Used for various types of sound, image coding and decoding
add_library( # library name
avcodec
# Dynamic library, generate so file
SHARED
# indicates that the library is referenced, not generated
IMPORTED )
# reference library file
set_target_properties( # library name
avcodec
# library path
PROPERTIES IMPORTED_LOCATION
${LIBS_DIR}/armeabi/libavcodec.so )
For all kinds of audio and video packaging format generation and analysis, read audio and video frames and other functions
add_library( avformat
SHARED
IMPORTED )
set_target_properties( avformat
PROPERTIES IMPORTED_LOCATION
${LIBS_DIR}/armeabi/libavformat.so )
Contains some common utility functions
add_library( avutil
SHARED
IMPORTED )
set_target_properties( avutil
PROPERTIES IMPORTED_LOCATION
${LIBS_DIR}/armeabi/libavutil.so )
A variety of audio and video filters are provided
add_library( avfilter
SHARED
IMPORTED )
set_target_properties( avfilter
PROPERTIES IMPORTED_LOCATION
${LIBS_DIR}/armeabi/libavfilter.so )
Used for audio resampling, sampling format conversion and mixing
add_library( swresample
SHARED
IMPORTED )
set_target_properties( swresample
PROPERTIES IMPORTED_LOCATION
${LIBS_DIR}/armeabi/libswresample.so )
# Used for video scene scaling, color mapping conversion
add_library( swscale
SHARED
IMPORTED )
set_target_properties( swscale
PROPERTIES IMPORTED_LOCATION
${LIBS_DIR}/armeabi/libswscale.so )
# quote source.. / indicates the upper-level directoryinclude_directories( .. /.. / ffmpeg 3.4.2 /${CPP_DIR}/include/ )
# associated library
target_link_libraries( ffmpeg
avcodec
avformat
avutil
avfilter
swresample
swscale )
Copy the code
Include_directories = FFmpeg = FFmpeg = FFmpeg = FFmpeg = FFmpeg = FFmpeg = FFmpeg = FFmpeg = FFmpeg But I tried to find a few files, so I referenced both.
Modify FFmpeg source code
- src\main\cpp\ffmpeg.c
Modify main method:
// Modify: int main(int argc, char **argv)Copy the code
Reinitialize after executing the command:
// returnPreviously added: nb_filterGraphs = 0; progress_avio = NULL; input_streams = NULL; nb_input_streams = 0; input_files = NULL; nb_input_files = 0; output_streams = NULL; nb_output_streams = 0; output_files = NULL; nb_output_files = 0;Copy the code
Comment out the following code, otherwise it will crash after executing the command:
exit_program(received_nb_signals ? 255 : main_return_code);
Copy the code
- src\main\cpp\ffmpeg.h
Int run(int argc, char **argv);Copy the code
- src\main\cpp\cmdutils.c
Modify the exit method, the original code will directly exit APP, so need to modify:
Void exit_program(int ret) {if (program_exit)
program_exit(ret);
exit(ret); } int exit_program(int ret) {return ret;
}
Copy the code
- src\main\cpp\cmdutils.h
Void exit_program(int ret) av_noreturn; Int exit_program(int ret);Copy the code
Modify ffmpeg_cmd. C
#include <jni.h>
#include "ffmpeg.h"
JNIEXPORT jint
JNICALL
Java_com_yl_ffmpeg4android_MainActivity_run(
JNIEnv *env, jclass obj, jobjectArray commands) {
int argc = (*env)->GetArrayLength(env, commands);
char *argv[argc];
int i;
for (i = 0; i < argc; i++) {
jstring js = (jstring) (*env)->GetObjectArrayElement(env, commands, i);
argv[i] = (char *) (*env)->GetStringUTFChars(env, js, 0);
}
return run(argc, argv);
}
Copy the code
The method is simple: pass in instructions (type String[]), then call the run method in ffmpeg.h to execute these instructions, and click the compile button. If nothing else, an error will be reported.
Setting the compile mode
First look at the error:
After checking Baidu and Google, I did not find a solution. Later, I looked at CMake document again and again, and tried one parameter after another. Finally, I was in a good mood and looked at how to solve it:
android {
...
defaultConfig {
...
externalNativeBuild {
cmake {
arguments '-DANDROID_ARM_MODE=arm'}}}Copy the code
Set ANDROID_ARM_MOD to arm, default is thumb, OK, continue compiling, what? Error:
There are some undefined methods. Fortunately, we add a few empty methods to ffmpeg.c:
HWDevice *hw_device_get_by_name(const char *name) {
}
int hw_device_init_from_string(const char *arg, HWDevice **dev) {
}
void hw_device_free_all(void) {
}
int hw_device_setup_for_decode(InputStream *ist) {
}
int hw_device_setup_for_encode(OutputStream *ost) {
}
Copy the code
Recompile:
A long time ago the message appears, look at the compiled so file where:
4. Test
Write a simple Demo to test the successful compilation of FFmpeg, a video of the first 100 frames into a GIF, the code above:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
static {
System.loadLibrary("ffmpeg");
}
private ImageView ivGif; private Button btnConvert; private ProgressDialog progressDialog; / / equipment private root directory path String path = Environment. External.getexternalstoragedirectory () getAbsolutePath (); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);
ivGif= findViewById(R.id.iv_gif); btnConvert = findViewById(R.id.btn_convert); btnConvert.setOnClickListener(this); } @override public void onClick(View v) {// Capture the first 100 frames of the video final String CMD ="ffmpeg -i " + path + "/ video.mp4-vframes 100 -y -f gif-s 480x497" + path + "/video_100.gif"; Loading progressDialog = new progressDialog (this); progressDialog.setTitle("Intercepting...");
progressDialog.show();
new Thread() {
@Override
public void run() { super.run(); CmdRun (CMD); // Hide loading runOnUiThread(new)Runnable() {
@Override
public void run() { progressDialog.dismiss(); progressDialog = null; GIF Glide. With (mainactivity.this).load(new File(path +)"/video_500.gif"))
.into(ivGif); }}); } }.start(); } /** * split directives with Spaces to generate an array of type String ** @param CMD directives * @returnRun code */ private int cmdRun(String CMD) {String regulation ="[ \\t]+";
final String[] split = cmd.split(regulation);
returnrun(split); } /** * the run method defined in ffmpeg_cmd ** @param CMD * @returnExecute code */ public native int run(String[] CMD); }Copy the code
Take a look at the GIF:
5. Write at the end
If there are mistakes in the article, you can give me a message to correct, thank you!
The source code of FFmpeg used in the article, the compilation script and the generated so file have been uploaded to GitHub, and will be updated in the future. Welcome Start, Fork!
Making portal
Click me to download the Apk for this Demo