環(huán)境
win7或链、jdk8隙畜、Andriod studio3.5尉剩、 NDK21
寫在前
- 采用編譯好的ffmpeg動態(tài)庫文件(多個動態(tài)文件)
- Android studio3.5創(chuàng)建navtive c++工程已經(jīng)配置好jni demo间坐,不用考慮gradle和cmake協(xié)調配置,非常方便
- 不同版本的Android studio創(chuàng)建的C+項目的CMakeLists.txt放置路徑不一樣少孝,多用message命令確認內(nèi)置變量的值
簡單總結
一般NDK開發(fā)時會有三方面報錯
- 編輯C/C++代碼時,找不到頭文件熬苍。說明CMakeLists.txt配置有漏
- build構建時稍走,鏈接linked報錯。說明CMakeLists.txt配置有漏
- 運行時找不到so文件柴底,閃退婿脸,這是外部so文件沒有打包如apk中。說明build.gradle配置有漏柄驻『鳎或者將外部so文件放入main/jniLibs/{ABI版本}中
具體的解決方案看本文最后
使用靜態(tài)文件 vs 使用動態(tài)文件
- 使用靜態(tài)文件在build構建時會編入內(nèi)部so文件中;而使用動態(tài)文件鸿脓,需要把外部so文件打包如spk中
- 靜態(tài)文件會缺少uncompress等函數(shù)定義抑钟,需要添加壓縮庫(Z);使用動態(tài)文件則不用
操作步驟-詳細:
-
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動態(tài)庫,已經(jīng)包含壓縮等拨黔。所以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下創(chuàng)建armeabi-v7a文件夾徽曲,并且把項目中l(wèi)ibs目錄下armeabi-v7a里所有so文件拷貝過來。 同樣的麸塞,如果配置 abiFilters='armeabi-v7a'和'arm64-v8a'秃臣, 則會在apk的lib下創(chuàng)建armeabi-v7a和arm64-v8a兩個文件夾,并且把項目中的libs目錄下對應的文件夾拷貝過來喘垂。如果項目中沒有l(wèi)ibs/arm64-v8a目錄甜刻,則build的時候會報錯
常見錯誤
- 頭文件找不到
-
現(xiàn)象:編輯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)
- 現(xiàn)象: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靜態(tài)文件,需要添加 CXXFLAGS)
link_directories(${PROJECT_SOURCE_DIR}/../../../libs/${CMAKE_ANDROID_ARCH_ABI})
- ffmpeg的so文件沒有打入apk包
現(xiàn)象:打包沒報錯正勒,運行時閃退得院,報錯如下
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有沒有報錯