上篇中我们已经编译好了ffmpeg的动态库和静态库,接下来我们要在android上使用,网上也有很多例子,把上篇的多个动态库编译成一个so库,我也尝试过,但是编译出来的libffmpeg.so库有十多M,而多个so库总共才4M多,相对来说太大了,而使用多个so,需要加载太多了so库文件,所以我使用静态库来生成一个动态库,然后再使用这个动态库。
1、创建一个android项目,支持Cmake,然后将之前编译好的静态库分别拷到jniLibs下如下图
2、FFmpeg是C开发,api比较多,如果使用api来开发我们要的功能,成本高,费时久,所以我采用的是命令形式,操作很简单,在FFmpeg将cmdutils.c 、cmdutils.h 、cmdutils_common_opts.h、 config.h、 ffmpeg.c、 ffmpeg.h、 ffmpeg_filter.c、 ffmpeg_opt.c拷到cpp目录下,并且将各个模块的.h文件过滤出来(写个脚本去把所有的.h文件提取出来,为了android项目编译的通过,否则会找不到头文件),这个我是参照www.jianshu.com/p/ceaa286d8aff,如下图
3、修改文件
找到ffmpeg.c,把int main(int argc, char argv) 改名为 int jxRun(int argc, char argv)
找到ffmpeg.h, 在文件末尾添加函数申明: int jxRun(int argc, char **argv);然后在return前加上如下代码即可
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;
找到cmdutils.c中的exit_program函数,修改为
在cmdutils.h文件中将exit_program 返回 改成int 并移除掉av_noreturn
创建jni 与java交互的.c文件ffmpeg-pro-lib.c
4、CMakeLists.txt的配置
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)
add_library( # Sets the name of the library.
ffmpeg-pro-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/cmdutils.c
src/main/cpp/ffmpeg.c
src/main/cpp/ffmpeg_filter.c
src/main/cpp/ffmpeg_opt.c
src/main/cpp/ffmpeg-pro-lib.c )
add_library(
avcodec
STATIC
IMPORTED
)
add_library(
avfilter
STATIC
IMPORTED
)
add_library(
avformat
STATIC
IMPORTED
)
add_library(
avutil
STATIC
IMPORTED
)
add_library(
swresample
STATIC
IMPORTED
)
add_library(
swscale
STATIC
IMPORTED
)
add_library(
avdevice
STATIC
IMPORTED
)
add_library(
avresample
STATIC
IMPORTED
)
add_library(
postproc
STATIC
IMPORTED
)
add_library(
x264
STATIC
IMPORTED
)
if(${ANDROID_ABI} STREQUAL "armeabi-v7a")
set_target_properties(
avcodec
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a/libavcodec.a
)
set_target_properties(
avfilter
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a/libavfilter.a
)
set_target_properties(
avformat
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a/libavformat.a
)
set_target_properties(
avutil
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a/libavutil.a
)
set_target_properties(
swresample
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a/libswresample.a
)
set_target_properties(
swscale
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a/libswscale.a
)
set_target_properties(
avdevice
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a/libavdevice.a
)
set_target_properties(
avresample
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a/libavresample.a
)
set_target_properties(
postproc
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a/libpostproc.a
)
set_target_properties(
x264
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a/libx264.a
)
endif(${ANDROID_ABI} STREQUAL "armeabi-v7a")
if(${ANDROID_ABI} STREQUAL "x86")
set_target_properties(
avcodec
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/jniLibs/x86/libavcodec.a
)
set_target_properties(
avfilter
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/jniLibs/x86/libavfilter.a
)
set_target_properties(
avformat
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/jniLibs/x86/libavformat.a
)
set_target_properties(
avutil
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/jniLibs/x86/libavutil.a
)
set_target_properties(
swresample
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/jniLibs/x86/libswresample.a
)
set_target_properties(
swscale
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/jniLibs/x86/libswscale.a
)
set_target_properties(
avdevice
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/jniLibs/x86/libavdevice.a
)
set_target_properties(
avresample
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/jniLibs/x86/libavresample.a
)
set_target_properties(
postproc
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/jniLibs/x86/libpostproc.a
)
set_target_properties(
x264
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/jniLibs/x86/libx264.a
)
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-warn-shared-textrel")
endif(${ANDROID_ABI} STREQUAL "x86")
include_directories(
src/main/cpp
)
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
target_link_libraries( # Specifies the target library.
ffmpeg-pro-lib
swscale
swresample
postproc
avresample
avdevice
avfilter
avformat
avcodec
avutil
x264
# Links the target library to the log library
# included in the NDK.
${log-lib})
由于使用的是静态库,所有链接顺序不要乱,否则build不过,可以按照我上面的顺序。之后就是编写java代码了,创建一个java类FFmpegNativeHelper
static{
System.loadLibrary("ffmpeg-pro-lib");
}
public static booleancompressVideo(String videoPath,String resultPath){
int ret = ffmpegCmdRun(new String[]{"ffmpeg",
"-i",videoPath,
"-y",
"-c:v","libx264",
"-c:a","aac",
"-crf","25",
"-vf","scale=-1:640",
"-preset","ultrafast",
"-b:v","900k",
"-bufsize","900k",
"-b:a","96k",
resultPath});
if(ret == 0){
return true;
}
}
return false;
}
private static native intffmpegCmdRun(String[] cmd);
上面只是简单的测试命令,你也可以尝试其它的命令。
生成apk后,只有一个so库文件,另外四个是我用了第三方播放器加载进来的忽略掉。
视频压缩后的结果8M多压成了1M多,感觉还行。
如下:
由于本人一直是从事android开发的,第一次写关于ffmpeg文章,可能有很多不足之处,希望大家谅解下,如有疑问,欢迎提出,代码的话,改天有空上传到github。
提示:在x86下,请记得替换掉config.h文件(目前这个config.h文件需要作ABI的区分),因为不同平台下编译生成的config.h文件不一样。