环境
win7、jdk8、Andriod studio3.5、 NDK21
写在前
- 采用编译好的ffmpeg动态库文件(多个动态文件)
- Android studio3.5创建navtive c++工程已经配置好jni demo,不用考虑gradle和cmake协调配置,非常方便
- 不同版本的Android studio创建的C+项目的CMakeLists.txt放置路径不一样,多用message命令确认内置变量的值
简单总结
一般NDK开发时会有三方面报错
- 编辑C/C++代码时,找不到头文件。说明CMakeLists.txt配置有漏
- build构建时,链接linked报错。说明CMakeLists.txt配置有漏
- 运行时找不到so文件,闪退,这是外部so文件没有打包如apk中。说明build.gradle配置有漏。或者将外部so文件放入main/jniLibs/{ABI版本}中
具体的解决方案看本文最后
使用静态文件 vs 使用动态文件
- 使用静态文件在build构建时会编入内部so文件中;而使用动态文件,需要把外部so文件打包如spk中
- 静态文件会缺少uncompress等函数定义,需要添加压缩库(Z);使用动态文件则不用
操作步骤-详细:
-
AS添加C++工程
-
拷贝ffmpeg文件到AS工程,目录结构如下
修改app的gradle.build
apply plugin: 'com.android.application'
android {
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "com.example.mmffndk"
minSdkVersion 21
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags ""
abiFilters 'armeabi-v7a' //ABIs [armeabi] are not supported for platform. Supported ABIs are [arm64-v8a, armeabi-v7a, x86, x86_64].
}
}
ndk{ //修改点2
abiFilters 'armeabi-v7a'
}
sourceSets {
main {
jniLibs.srcDirs = ['libs'] //jniLibs打包时会查找libs/${ANDROID_ARCH_ABI}/*.so
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
version "3.10.2"
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
}
- 修改CMakeLists.txt
- 需要注意:
1) 不同版本的android studio生成项目的CMakeLists.txt路径不同,所以CMAKE_SOURCE_DIR的值不同
2)使用ffmpeg动态库,已经包含压缩等。所以target_link_libraries不需要添加 -lz
cmake_minimum_required(VERSION 3.4.1)
# 指定引入文件头路径 , include文件夹里放置了ffmpeg的.h头文件
include_directories(${CMAKE_SOURCE_DIR}/ffmpeg/include)
# 指定 .so共享库目录 (相当于添加 CXXFLAGS=-Lpath)
link_directories(${PROJECT_SOURCE_DIR}/../../../libs/${CMAKE_ANDROID_ARCH_ABI})
# 指定 c/c++ 源文件目录
aux_source_directory(${CMAKE_SOURCE_DIR} cpp-list)
message(STATUS "cpp-list = ${cpp-list}")
add_library( # Sets the name of the library.
native-lib11
SHARED
${cpp-list})
find_library( # Sets the name of the path variable.
log-lib
log)
target_link_libraries( # Specifies the target library.
native-lib11
avcodec avdevice avfilter avformat avutil swresample swscale
android
${log-lib})
- 点击 build->rebuild project ,项目构建完成后,打开 apk分析器(build ->apk analyze然后选择apk)
由于我们在app:build.gradle里配置 abiFilters=armeabi-v7a,而且配置了jniLibs.srcDirs = 'libs'。所以apk打包时会在lib下创建armeabi-v7a文件夹,并且把项目中libs目录下armeabi-v7a里所有so文件拷贝过来。 同样的,如果配置 abiFilters='armeabi-v7a'和'arm64-v8a', 则会在apk的lib下创建armeabi-v7a和arm64-v8a两个文件夹,并且把项目中的libs目录下对应的文件夹拷贝过来。如果项目中没有libs/arm64-v8a目录,则build的时候会报错
常见错误
- 头文件找不到
-
现象:编辑c/cpp代码时报错 cannot find 'libavcodec'
- 原因:头文件路径错误
- 解决方案:
①先在 CmakeLists.txt 里添加:include_directories(${CMAKE_SOURCE_DIR}/ffmpeg/include)
②点击 build->refresh linked C++ projects
- 链接错误:linker command failed with exit code 1 (use -v to see invocation)
- 现象:build构建时报错:undefined reference to 'avutil_configuration'
[2/2] Linking CXX shared library G:\learn\androidlearn\workstation\Mmffndk\app\build\intermediates\cmake\debug\obj\armeabi-v7a\libnative-lib.so
FAILED: G:/android/workstation/Mmffndk/app/build/intermediates/cmake/debug/obj/armeabi-v7a/libnative-lib.so
cmd.exe /C "cd . && G:\learn\androidlearn\studio\sdk\ndk\21.0.6113669\toolchains\llvm\prebuilt\windows-x86_64\bin\clang++.exe --target=armv7-none-linux-androideabi21 --gcc-toolchain=G:/android/studio/sdk/ndk/21.0.6113669/toolchains/llvm/prebuilt/windows-x86_64 --sysroot=G:/android/studio/sdk/ndk/21.0.6113669/toolchains/llvm/prebuilt/windows-x86_64/sysroot -fPIC -g -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 -march=armv7-a -mthumb -Wformat -Werror=format-security -O0 -fno-limit-debug-info -Wl,--exclude-libs,libgcc_real.a -Wl,--exclude-libs,libatomic.a -static-libstdc++ -Wl,--build-id -Wl,--fatal-warnings -Wl,--exclude-libs,libunwind.a -Wl,--no-undefined -Qunused-arguments -shared -Wl,-soname,libnative-lib.so -o G:\learn\androidlearn\workstation\Mmffndk\app\build\intermediates\cmake\debug\obj\armeabi-v7a\libnative-lib.so CMakeFiles/native-lib.dir/native-lib.cpp.o -lavformat -lavcodec -lavfilter -lavutil -lswresample -lswscale -landroid -llog -latomic -lm && cd ."
G:/android/studio/sdk/ndk/21.0.6113669/toolchains/llvm/prebuilt/windows-x86_64/lib/gcc/arm-linux-androideabi/4.9.x/../../../../arm-linux-androideabi/bin\ld: error: cannot find -lavformat
G:/android/studio/sdk/ndk/21.0.6113669/toolchains/llvm/prebuilt/windows-x86_64/lib/gcc/arm-linux-androideabi/4.9.x/../../../../arm-linux-androideabi/bin\ld: error: cannot find -lavcodec
G:/android/studio/sdk/ndk/21.0.6113669/toolchains/llvm/prebuilt/windows-x86_64/lib/gcc/arm-linux-androideabi/4.9.x/../../../../arm-linux-androideabi/bin\ld: error: cannot find -lavfilter
G:/android/studio/sdk/ndk/21.0.6113669/toolchains/llvm/prebuilt/windows-x86_64/lib/gcc/arm-linux-androideabi/4.9.x/../../../../arm-linux-androideabi/bin\ld: error: cannot find -lavutil
G:/android/studio/sdk/ndk/21.0.6113669/toolchains/llvm/prebuilt/windows-x86_64/lib/gcc/arm-linux-androideabi/4.9.x/../../../../arm-linux-androideabi/bin\ld: error: cannot find -lswresample
G:/android/studio/sdk/ndk/21.0.6113669/toolchains/llvm/prebuilt/windows-x86_64/lib/gcc/arm-linux-androideabi/4.9.x/../../../../arm-linux-androideabi/bin\ld: error: cannot find -lswscale
G:/android/workstation/Mmffndk/app/src/main/cpp/native-lib.cpp:27: error: undefined reference to 'avutil_configuration'
G:/android/workstation/Mmffndk/app/src/main/cpp/native-lib.cpp:40: error: undefined reference to 'av_register_all'
G:/android/workstation/Mmffndk/app/src/main/cpp/native-lib.cpp:42: error: undefined reference to 'avformat_alloc_context'
G:/android/workstation/Mmffndk/app/src/main/cpp/native-lib.cpp:52: error: undefined reference to 'av_dict_set'
G:/android/workstation/Mmffndk/app/src/main/cpp/native-lib.cpp:54: error: undefined reference to 'avformat_open_input'
G:/android/workstation/Mmffndk/app/src/main/cpp/native-lib.cpp:62: error: undefined reference to 'avformat_find_stream_info'
G:/android/workstation/Mmffndk/app/src/main/cpp/native-lib.cpp:87: error: undefined reference to 'avcodec_find_decoder'
G:/android/workstation/Mmffndk/app/src/main/cpp/native-lib.cpp:94: error: undefined reference to 'avcodec_open2'
G:/android/workstation/Mmffndk/app/src/main/cpp/native-lib.cpp:102: error: undefined reference to 'sws_getContext'
G:/android/workstation/Mmffndk/app/src/main/cpp/native-lib.cpp:108: error: undefined reference to 'av_malloc'
G:/android/workstation/Mmffndk/app/src/main/cpp/native-lib.cpp:110: error: undefined reference to 'av_init_packet'
G:/android/workstation/Mmffndk/app/src/main/cpp/native-lib.cpp:116: error: undefined reference to 'av_frame_alloc'
G:/android/workstation/Mmffndk/app/src/main/cpp/native-lib.cpp:119: error: undefined reference to 'av_frame_alloc'
G:/android/workstation/Mmffndk/app/src/main/cpp/native-lib.cpp:122: error: undefined reference to 'avpicture_get_size'
G:/android/workstation/Mmffndk/app/src/main/cpp/native-lib.cpp:121: error: undefined reference to 'av_malloc'
G:/android/workstation/Mmffndk/app/src/main/cpp/native-lib.cpp:125: error: undefined reference to 'avpicture_fill'
G:/android/workstation/Mmffndk/app/src/main/cpp/native-lib.cpp:136: error: undefined reference to 'av_read_frame'
G:/android/workstation/Mmffndk/app/src/main/cpp/native-lib.cpp:144: error: undefined reference to 'avcodec_decode_video2'
G:/android/workstation/Mmffndk/app/src/main/cpp/native-lib.cpp:155: error: undefined reference to 'sws_scale'
G:/android/workstation/Mmffndk/app/src/main/cpp/native-lib.cpp:179: error: undefined reference to 'av_free_packet'
G:/android/workstation/Mmffndk/app/src/main/cpp/native-lib.cpp:185: error: undefined reference to 'av_frame_free'
G:/android/workstation/Mmffndk/app/src/main/cpp/native-lib.cpp:186: error: undefined reference to 'av_frame_free'
G:/android/workstation/Mmffndk/app/src/main/cpp/native-lib.cpp:188: error: undefined reference to 'avcodec_close'
G:/android/workstation/Mmffndk/app/src/main/cpp/native-lib.cpp:189: error: undefined reference to 'avformat_free_context'
clang++: error: linker command failed with exit code 1 (use -v to see invocation)
- 原因:so文件没有引入
- 解决方法:在 CMakeLists.txt添加
# 指定 .so共享库目录 (如果是 .a静态文件,需要添加 CXXFLAGS)
link_directories(${PROJECT_SOURCE_DIR}/../../../libs/${CMAKE_ANDROID_ARCH_ABI})
- ffmpeg的so文件没有打入apk包
现象:打包没报错,运行时闪退,报错如下
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.mmffndk, PID: 7253
java.lang.UnsatisfiedLinkError: dlopen failed: library "libavformat.so" not found: needed by /data/app/~~VZddftg5_hnlp3FkuHdTxg==/com.example.mmffndk-NDSt_nrh6mmjLv39fko4gQ==/lib/arm/libnative-lib.so in namespace classloader-namespace
at java.lang.Runtime.loadLibrary0(Runtime.java:1087)
at java.lang.Runtime.loadLibrary0(Runtime.java:1008)
at java.lang.System.loadLibrary(System.java:1664)
at com.example.mmffndk.MainActivity.<clinit>(MainActivity.java:25)
at java.lang.Class.newInstance(Native Method)
at android.app.AppComponentFactory.instantiateActivity(AppComponentFactory.java:95)
at androidx.core.app.CoreComponentFactory.instantiateActivity(CoreComponentFactory.java:43)
at android.app.Instrumentation.newActivity(Instrumentation.java:1253)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3353)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
D/libEGL: loaded /vendor/lib/egl/libEGL_emulation.so
解决方案:
① 打开 apk analyze,确认ffmpeg相关的外部so文件没有打入apk中
② 确认app:build.gradle有没有报错