Android 编译 FFmpeg 产生「error undefined reference to ‘av_register_all()」等错误的多种原因

in 在路上

本文主要内容:讲述遇到集成 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']
        }
    }
标签:, , , ,
© 2018 一隅 - Powered by Wordpress / Theme: Tabinikki