1.环境配置
ffmpeg库已经编译好了,接下来准备将so引入Android studio进行开发
我们创建一个新的项目,注意在创建过程中这几个选项的勾选
勾选添加C++支持,Android studio会自动帮我们做一些配置,后边进行简单的解释
C++ Standard:使用哪种 C++ 标准。选择 Toolchain Default 会使用默认的 CMake 设置。有C11和C14两种,我们选择C11
Exceptions Support:如果希望启用对 C++ 异常处理的支持,请选中此复选框。如果启用此复选框,Android Studio 会将 -fexceptions 标志添加到模块级 build.gradle 文件的 cppFlags 中,Gradle 会将其传递到 CMake。
Runtime Type Information Support:(Run-Time Type Identification),通过运行时类型信息程序能够使用基类的指针或引用来检查这些指针或引用所指的对象的实际派生类型。如果希望支持 RTTI,请选中此复选框。如果启用此复选框,Android Studio 会将 -frtti 标志添加到模块级 build.gradle 文件的 cppFlags 中,Gradle 会将其传递到 CMake。
项目创建出来之后,可以看到,项目默认创建了一个调用C++代码的小demo输出一行字符串。在app根目录可以看到一个CMakeLists.txt的文件,这是添加c++支持后默认创建的cmake脚本,我们将使用这个脚本对ffmpeg进行编译
打开app目录下的build.gradle,可以看到下边两项配置
apply plugin: 'com.android.application'
android {
...
defaultConfig {
...
externalNativeBuild {
cmake {
//C++标准选择C11之后做的配置
cppFlags "-std=c++11"
}
}
}
externalNativeBuild {
cmake {
//指定的CMakeLists脚本文件的路径
path "CMakeLists.txt"
}
}
}
这是工具自动做好的配置,接下来还需要我们手动做一些处理,来完善ffmpeg编译的环境。
第一. ffmpeg播放视频会涉及到操作内存卡,所以需要配置存储权限,6.0及以上Android版本还要记得动态权限获取的配置,这里不多说
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
第二. 在app main目录下创建jniLibs目录,将我们创建好的so放在这个目录下,或者直接把so放在libs目录下,但是这种方式需要我们在build.gradle中配置路径,这里选用第二种方式
然后打开app下build.gradle文件,在android/defaultConfig节点下添加如下配置,指定so文件的存放目录,默认是jniLibs
sourceSets{
//将so放在libs文件夹下,需要指定这个路径,因为默认路径是jniLibs
main{
jniLibs.srcDirs=['libs']
}
}
还有一点,因为我们只编译了armeabi-v7a版本的ffmpeg,所以需要指定过滤版本,在android/defaultConfig/externalNativeBuild节点中添加
ndk{
abiFilters "armeabi-v7a"
}
此时,整个build.gradle文件应该是这样的(只留下了ndk相关的配置)
apply plugin: 'com.android.application'
android {
...
defaultConfig {
...
externalNativeBuild {
cmake {
cppFlags "-std=c++11"
}
//默认情况下,Gradle 会针对 [NDK 支持的 ABI](https://developer.android.google.cn/ndk/guides/abis.html?hl=zh-cn#sa)
//将原生库构建到单独的 .so文件中,并将其全部打包到 APK 中。如果希望 Gradle 仅构建和打包原生
//库的特定 ABI 配置,可以在模块级build.gradle文件中使用 ndk.abiFilters标志指定这些配置
ndk{
//abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a', 'arm64-v8a'
abiFilters "armeabi-v7a"
}
}
sourceSets{
//将so放在libs文件夹下,需要指定这个路径,因为默认路径是jniLibs
main{
jniLibs.srcDirs=['libs']
}
}
}
...
//将 Gradle 关联到原生库,需要提供一个指向 CMake 或 ndk-build 脚本文件的路径。在构建应用时,Gradle
//会以依赖项的形式运行 CMake 或 ndk-build,并将共享的库打包到 APK 中
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}
dependencies {
...
}
2.CMakeLists.txt脚本文件的编写
CMake 构建脚本是一个纯文本文件,必须将其命名为 CMakeLists.txt,一般放在项目根目录(app的根目录),后边会附上我测试成功的CMakeLists.txt,
CMakeLists Android官方教程
add_library():
该命令用于向CMake构建脚本添加源文件和库,它有三个参数,每个参数的解释如下
add_library(
//这个参数指定你的源文件被编译或者库被引入后的名字,可以指
//定任意你觉得合适的名字
native-lib
//第二个参数有STATIC 和SHARED两种选择,SHARED表示会编
//译成动态库,STATIC 表示静态库
SHARED
//这个位置用于指定源文件的相对路径(相对于CMakeLists.txt的
//路径),或者如果你是在引入其他库,那么这里指定IMPORTED
//属性
src/main/cpp/native-lib.cpp )
set_target_properties:
如果你add_library引入的是已经编译好的库文件,那么你需要通过set_target_properties指定被引入的库文件的路径
//这两个一一对应,这两个命令结合可以引入一个so库,一个so库对应这两个命令
add_library(
avcodec
SHARED
IMPORTED)
set_target_properties(
//指定是给谁设置属性,这里是上边add的avcodec
avcodec
//指定是设置什么样的属性,这里是引入的路径,是一个相对路径
PROPERTIES IMPORTED_LOCATION
//这里具体指定相对于脚本文件的路径
${FFMPEG_DIR}/libavcodec.so)
include_directories():
通过上边两个命令,库文件会被添加进来,这些库一般会依赖一些头文件,这时我们可以通过include_directories来指定头文件的位置,确保 CMake 在编译时可以定位到头文件
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/include)
以ffmpeg的编译为例,我们会add很多的so库,add_library了好多次,将所有需要的so添加,我们还add了自己的源文件,最终我们指定了这些源文件被编译成ffmpeg的so
add_library(
ffmpeg
SHARED
${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/native-lib.cpp )
这些源文件依赖于添加的ffmpeg中的库,所以最终我们要把我们自己编译的库和其他这些so链接到一块,这时就需要命令
target_link_libraries(
ffmpeg
avcodec
avfilter
avformat
avutil
swresample
swscale
${log-lib} )
看到这里有个这样的引入${log-lib},这个库是ndk中提供的,通过find_library命令引入
find_library
Android NDK 提供了一套实用的原生 API 和库。通过将NDK 库包含到项目的 CMakeLists.txt脚本文件中,预构建的 NDK 库已经存在于 Android 平台上,因此,无需再构建或将其打包到 APK 中。由于 NDK 库已经是 CMake 搜索路径的一部分,甚至不需要在本地 NDK 安装中指定库的位置 - 只需要向 CMake 提供希望使用的库的名称,并将其关联到自己的原生库上即可。
将 find_library()命令添加到您的 CMake 构建脚本中以定位 NDK 库,并将其路径存储为一个变量。可以使用此变量在构建脚本的其他部分引用 NDK 库。以下示例可以定位Android 特定的日志支持库并将其路径存储在 log-lib 中
find_library( log-lib log )
3.编译ffmpeg的CMakeLists.txt完整脚本
#指定Cmake构建工具的最低版本
cmake_minimum_required(VERSION 3.4.1)
#设置头文件路径
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/include)
#设置FFmpeg库路径变量
#CMAKE_CURRENT_SOURCE_DIR,指的是当前处理的 CMakeLists.txt 所在的路径,CMAKE_SOURCE_DIR,不论采用何种编译方式,都是工程顶层目录
set(FFMPEG_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libs/${ANDROID_ABI})
#添加avcodec
add_library(avcodec SHARED IMPORTED)
set_target_properties(avcodec PROPERTIES IMPORTED_LOCATION ${FFMPEG_DIR}/libavcodec.so)
#添加avfilter
add_library(avfilter SHARED IMPORTED)
set_target_properties(avfilter PROPERTIES IMPORTED_LOCATION ${FFMPEG_DIR}/libavfilter.so)
#添加avformat
add_library(avformat SHARED IMPORTED)
set_target_properties(avformat PROPERTIES IMPORTED_LOCATION ${FFMPEG_DIR}/libavformat.so)
#添加avutil
add_library(avutil SHARED IMPORTED)
set_target_properties(avutil PROPERTIES IMPORTED_LOCATION ${FFMPEG_DIR}/libavutil.so)
#添加swresample
add_library(swresample SHARED IMPORTED)
set_target_properties(swresample PROPERTIES IMPORTED_LOCATION ${FFMPEG_DIR}/libswresample.so)
#添加swscale
add_library(swscale SHARED IMPORTED)
set_target_properties(swscale PROPERTIES IMPORTED_LOCATION ${FFMPEG_DIR}/libswscale.so)
add_library( ffmpeg SHARED ${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/native-lib.cpp )
find_library( log-lib log )
target_link_libraries( ffmpeg avcodec avfilter avformat avutil swresample swscale ${log-lib} )