本文主要内容:讲述遇到集成 NDK 时遇到的「error: undefined reference to」的解决方案
集成 NDK 有很多方式,比如 ndk-build 后把 so 库直接链接使用,或者通过 CMake 的方式链接,通常推荐后者,因为这样编写调用 native 方法方便,不需要像前者每次需要重新打包。
在实现 FFmpeg 播放视频功能时,我参考 Android Studio FFmpeg视频解码播放 该文章操作后,始终无法成功编译,按照网上的种种解决方案(比如用 C 的方式引入头文件)都无法解决,最后一步一步跟能成功运行的项目对照后终于解决。
具体报错情况为一堆 FFmpeg 相关的方法提示 「error: undefined reference to XXXX」。网上的解决方案基本都为用「extern “C”{}」包裹,原因是 FFmpeg 是一共纯 C 的项目,C++ 想正确调用 C 代码则需使用这样的方式,具体示例如下:
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libswresample/swresample.h"
#include "libavutil/opt.h"
#include "libavutil/imgutils.h"
}
该方案能解决一部分人问题,于我却无用,随后我检查了各种引入方式,以及 NDK 的语法后,确定问题其实是出在 CMakelists.txt 文件上。
先附上正确的 CMake 文件:
cmake_minimum_required(VERSION 3.4.1)
set(distribution_DIR ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI})
include_directories(${CMAKE_SOURCE_DIR}/src/main/cpp/include)
add_library( avutil-55
SHARED
IMPORTED )
set_target_properties( avutil-55
PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/libavutil-55.so )
add_library( swresample-2
SHARED
IMPORTED )
set_target_properties( swresample-2
PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/libswresample-2.so )
add_library( avcodec-57
SHARED
IMPORTED )
set_target_properties( avcodec-57
PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/libavcodec-57.so )
add_library( avfilter-6
SHARED
IMPORTED)
set_target_properties( avfilter-6
PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/libavfilter-6.so )
add_library( swscale-4
SHARED
IMPORTED)
set_target_properties( swscale-4
PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/libswscale-4.so )
add_library( avformat-57
SHARED
IMPORTED)
set_target_properties( avformat-57
PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/libavformat-57.so )
#add_library( native-lib
# SHARED
# src/main/cpp/native-lib.cpp )
add_library( ffmpeg-lib
SHARED
${CMAKE_SOURCE_DIR}/src/main/cpp/ffmpeg-lib.cpp )
#find_library( log-lib
# log )
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
target_link_libraries(
ffmpeg-lib
avutil-55
swresample-2
avcodec-57
avfilter-6
swscale-4
avformat-57
log
android
)
我犯的错误之一是为了区分我自己新建了 ffmpeg-lib 来写相关的代码,却两次 add_library 了,实际上写法应该是同一个 add_library,最后一部分引入多个 cpp 文件。
犯的错误之二是第一次接触 CMake,因此代码是照着别人改的,但是 target_link_libraries 的时候少链接了一个 so库。
这里的错误其实挺隐蔽的,因为之前集成 FFmpeg 的时候项目 Hello world 时一切正常,而后具体写代码时却出问题了,因此一直把注意力集中在代码上面。我搜到的网上的所有相关资料都没有提示可能与 CMake 有关,因此写下此文供后人参考。
顺带补充一个尴尬的小错误,在运行的时候 loadLibrary 时提示「java.lang.UnsatisfiedLinkError: dlopen failed: library “libavcodec-58.so” not found」,我一度以为又是我 CMake 写错了,结果发现是因为最开始把 so 库放到了 libs 文件夹下,后来在排查上面错误时改到了 src/main/jniLibs 底下,而 gradle 文件没有同步更改,gradle 的 jniLibs.srcDirs 修改目录即可:
sourceSets {
main {
jniLibs.srcDirs = ['src/main/jniLibs']
}
}
Metal
Extensions
Cheese
Dong
Unbranded Plastic Table
eyeballs
matrix
Applications
Plaza
Cayman Islands Dollar
invoice
Program
Synchronised
Money Market Account
mesh
compress
Avon
Malagasy Ariary
Fish
responsive
Intelligent Plastic Salad
SDD
Analyst
Home Loan Account
evolve
Motorway
Ohio
function
HTTP
haptic
Data
COM
Toys
Virtual
Tuna
alliance
JSON
optical
collaboration
end-to-end
systematic
Borders
hack
Secured
Factors
Plastic
1080p
niches
matrix
Mouse
Tasty
Balanced
PNG
Toys
Refined Steel Ball
Baby
Loaf
Trafficway
infrastructures
hack
Toys
exuding
wireless
solution
Small Fresh Computer
Spring
GB