FFmpeg的名声很大,是很多应用都使用的一个开源库。我们可以下载编译好的FFmpeg的so文件来使用,但是我们还是从0开始学会自己编译FFmpeg来提供给Android使用
编译FFmpeg大体的步骤就是到官网下载FFmpeg源码,然后编译为so库
修改ffmpeg-3.3.6/configure文件如下几行配置,因为编译出来的动态库文件名的版本号在.so之后(例如“libavcodec.so.5.100.1”),而android平台不能识别这样文件名,所以需要修改这种文件名
SLIBNAME_WITH_MAJOR='$(SLIBNAME).$(LIBMAJOR)'
LIB_INSTALL_EXTRA_CMD='$$(RANLIB)"$(LIBDIR)/$(LIBNAME)"'
SLIB_INSTALL_NAME='$(SLIBNAME_WITH_VERSION)'
SLIB_INSTALL_LINKS='$(SLIBNAME_WITH_MAJOR)$(SLIBNAME)'
替换为:
SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(FULLNAME)-$(LIBMAJOR)$(SLIBSUF)'
LIB_INSTALL_EXTRA_CMD='$$(RANLIB)"$(LIBDIR)/$(LIBNAME)"'
SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)'
SLIB_INSTALL_LINKS='$(SLIBNAME)'
编译脚步如下:
#!/bin/bash
# NDK的路径,根据自己的安装位置进行设置
#NDK=/Users/yehu/Library/Android/sdk/ndk-bundle
NDK=/Users/yehu/Downloads/android-ndk-r15c
SYSROOT=$NDK/platforms/android-21/arch-arm
TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64
#这句很重要,不然会报错 unable to create temporary file in ...
export TMPDIR="/Users/yehu/Study/Android/MultiMedia/FFmpeg/ffmpeg-3.3.6/ffmpegtemp"
function build_one
{
bash ./configure \
--prefix=$PREFIX \
--enable-shared \
--enable-avdevice \
--enable-gpl \
--enable-nonfree \
--enable-postproc \
--disable-static \
--disable-doc \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--disable-ffserver \
--disable-doc \
--disable-symver \
--cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
--target-os=linux \
--arch=arm \
--enable-cross-compile \
--sysroot=$SYSROOT \
--extra-cflags="-Os -fpic $ADDI_CFLAGS" \
--extra-ldflags="$ADDI_LDFLAGS" \
$ADDITIONAL_CONFIGURE_FLAG
make clean
make
make install
}
CPU=arm
PREFIX=$(pwd)/android/$CPU
ADDI_CFLAGS="-marm"
build_one
chimed +x build_android.sh
./build_android.sh
如果成功就可以在ffmepg-3.3.6/android下面看到编译好的so文件了
我刚开始编译的时候使用的ndk是最新的16,FFmpeg是最新的3.4.2,但是不是出现Ndk的问题里面的文件头部缺失就是出现FFmpeg的问题,里面的c代码编译出问题,完全整不明白,于是把NDK和FFmpeg都降低了一个版本来编译,然后就编译通过了。
最后追踪到自己编译的问题所在个个解决即可
当然我实在是拿最新的编译不出来就整低一个版本的了。
上述编译过程参考链接查看
我们这里仿照之前的Ndk的文章以两种方式ndk-build和Cmake来集成使用so库。同时把之前学习的NDK的配置复习一下。
由于第一次使用最新的ndk和FFmpeg我在Mac平台下编译FFmepg失败了,所以我下面的ndk-build方式引入so用的都是来自雷神的博客里面的。
so库下载地址查看
引入到我的项目工程结构如下图:
建立自己的native-lib.c文件用来调用Ffmepg的so代码
修改Android.mk文件替换引入我自己的native-lib.c文件
在jni目录执行ndk-build命令:
在gradle文件里面进行简单的配置,把jniLibs指向libs:
android {
defaultConfig {
//...
sourceSets.main.jniLibs.srcDirs = ['src/main/libs','libs']
sourceSets.main.jni.srcDirs = []
}
//...
}
现在就只要倒入我们的native-lib库,然后调用它实现的方法就好了:
public class BasicInfoActivity extends AppCompatActivity {
private static final String TAG = "BasicInfoActivity";
private TextView mContentTV;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_basic_info);
mContentTV = findViewById(R.id.tv_content);
}
public void onClickProtocol(View view) {
mContentTV.setText(urlprotocolinfo());
}
public void onClickFormat(View view) {
mContentTV.setText(avformatinfo());
}
public void onClickCodec(View view) {
mContentTV.setText(avcodecinfo());
}
public void onClickFilter(View view) {
mContentTV.setText(avfilterinfo());
}
public void onClickConfigure(View view) {
mContentTV.setText(configurationinfo());
}
public native String urlprotocolinfo();
public native String avformatinfo();
public native String avcodecinfo();
public native String avfilterinfo();
public native String configurationinfo();
static {
// System.loadLibrary("avutil-54");
// System.loadLibrary("swresample-1");
// System.loadLibrary("avcodec-56");
// System.loadLibrary("avformat-56");
// System.loadLibrary("swscale-3");
// System.loadLibrary("postproc-53");
// System.loadLibrary("avfilter-5");
// System.loadLibrary("avdevice-56");
// System.loadLibrary("sffhelloworld");
System.loadLibrary("native-lib");
}
}
运行程序可以看到FFmepg的基本的信息啦。
参考代码地址:查看
这里我们编译成功的FFmpeg的版本是3.3.6
cmake_minimum_required(VERSION 3.4.1)
set(jnilibs "${CMAKE_SOURCE_DIR}/src/main/jniLibs")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${jnilibs}/${ANDROID_ABI})
add_library( # Sets the name of the library.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/native-lib.c)
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 )
#第三方so存放的根目录
SET(
third_path
${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}
)
#引入源文件.h目录
include_directories( ${CMAKE_SOURCE_DIR}/src/main/cpp/include)
#设置libavcodec.so
add_library(libavcodec-57
SHARED
IMPORTED
)
set_target_properties(libavcodec-57
PROPERTIES IMPORTED_LOCATION
${third_path}/libavcodec-57.so
)
#设置libavdevice.so
add_library(libavdevice-57
SHARED
IMPORTED
)
set_target_properties(libavdevice-57
PROPERTIES IMPORTED_LOCATION
${third_path}/libavdevice-57.so
)
#设置libavfilter.so
add_library(libavfilter-6
SHARED
IMPORTED
)
set_target_properties(libavfilter-6
PROPERTIES IMPORTED_LOCATION
${third_path}/libavfilter-6.so
)
#设置libavformat.so
add_library(libavformat-57
SHARED
IMPORTED
)
set_target_properties(libavformat-57
PROPERTIES IMPORTED_LOCATION
${third_path}/libavformat-57.so
)
#设置libavutil.so
add_library(libavutil-55
SHARED
IMPORTED
)
set_target_properties(libavutil-55
PROPERTIES IMPORTED_LOCATION
${third_path}/libavutil-55.so
)
#设置libpostproc.so
add_library(libpostproc-54
SHARED
IMPORTED
)
set_target_properties(libpostproc-54
PROPERTIES IMPORTED_LOCATION
${third_path}/libpostproc-54.so
)
#设置libswresample.so
add_library(libswresample-2
SHARED
IMPORTED
)
set_target_properties(libswresample-2
PROPERTIES IMPORTED_LOCATION
${third_path}/libswresample-2.so
)
#设置libswscale.so
add_library(libswscale-4
SHARED
IMPORTED
)
set_target_properties(libswscale-4
PROPERTIES IMPORTED_LOCATION
${third_path}/libswscale-4.so
)
target_link_libraries(
native-lib
libavcodec-57
libavdevice-57
libavfilter-6
libavformat-57
libavutil-55
libpostproc-54
libswresample-2
libswscale-4
${log-lib} )
在C层和Java层的调用和上面一样,我们只是换了一下底层的库和引入的方式而已。
要注意的是我在项目最后运行的时候遇到一个这样的错误:
android {
//...
packagingOptions {
pickFirst 'lib/armeabi-v7a/libnative-lib.so'
}
}
配置这个信息即可:
本示例Module地址:查看
参考链接:
雷神blog
Mac平台编译FFmpeg参考