Linux 下交叉编译opencv4.5.5 android JNI基于 FFMPEG 的 VideoIO模块

可以参考这位大哥的教程:
https://zhuanlan.zhihu.com/p/377407799

Prepare:

<1>下载opencv4.5.5源码下载地址

<2>下载ffmpeg4.4.2源码下载地址

每一个版本的opencv 都有对应的ffmpeg 的版本 ,opencv 目录opencv-4.5.5/3rdparty/ffmpeg下 ffmpeg.cmake里的ffmpeg_version.cmake可以看出对应的版本
<3>下载ndk 23.1.7779620下载地址

下载ndk网站https://developer.android.com/ndk/downloads

<4>下载android sdk

Sdk通过android studio下载或者通过sdkmanager

https://developer.android.com/studio/command-line/sdkmanager

#path使用指定的 SDK 路径
sdkmanager --sdk_root=path "platform-tools" "platforms;android-24" "build-tools;30.0.2"

备注:不需要生成libopencv_java4.so,就可以不用设置android sdk,把BUILD_ANDROID_PROJECTS和BUILD_FAT_JAVA_LIB、BUILD_JAVA都关掉。

编译ffmpeg

根据自己ndk 路径设置

#根据自己ndk 路径设置
export TOOLCHAIN=/android-ndk-r23b/toolchains/llvm/prebuilt/linux-x86_64
export TARGET=aarch64-linux-android
export API=24
export AR=$TOOLCHAIN/bin/llvm-ar
export CC=$TOOLCHAIN/bin/$TARGET$API-clang
export AS=$CCexport CXX=$TOOLCHAIN/bin/$TARGET$API-clang++
export LD=$TOOLCHAIN/bin/ld
export RANLIB=$TOOLCHAIN/bin/llvm-ranlib
export STRIP=$TOOLCHAIN/bin/llvm-strip

./configure \
--prefix=./build \
--target-os=android \
--enable-shared \
--enable-ffmpeg \
--enable-cross-compile \
--enable-ffplay \
--enable-ffprobe \
--enable-demuxer=rtsp \
--enable-demuxer=rtp \
--enable-protocol=tcp \
--enable-protocol=udp \
--enable-network \
--disable-doc \
--disable-avdevice \
--disable-doc \
--disable-symver \
--disable-asm \
--arch=aarch64 \
--disable-stripping \
--enable-avresample \
--enable-small \
--enable-gpl --disable-debug \
--cc=$CC \
--as=$AS \
--extra-cflags="-fpic"


#有的教程cflags 添加了这些,不理解什么意思,自己编译没有加这些cflag
--extra-cflags=$CFLAG
CFLAG="-D__ANDROID_API__=$API -U_FILE_OFFSET_BITS -DBIONIC_IOCTL_NO_SIGNEDNESS_OVERLOAD -Os -fPIC -DANDROID -D__thumb__ -mthumb -Wfatal-errors -Wno-deprecated -mfloat-abi=softfp -marm"

编译opencv

<1> 更改opencv-4.5.5目录下的CMakeLists.txt

1.让with_ffmpeg 默认开启

#注释这三行
## OCV_OPTION(WITH_FFMPEG "Include FFMPEG support" (NOT ANDROID)
#   VISIBLE_IF NOT IOS AND NOT WINRT
#   VERIFY HAVE_FFMPEG)
#添加下面这一行
OCV_OPTION(WITH_FFMPEG "Include FFMPEG support" ON
VISIBLE_IF NOT IOS AND NOT WINRT
VERIFY HAVE_FFMPEG) #默认开启

2.添加 find_package(PkgConfig QUIET)

# ----------------------------------------------------------------------------
#       CHECK FOR SYSTEM LIBRARIES, OPTIONS, ETC..
#       加载pkg-config(环境变量默认没有,所以这个地方的条件进不去)
#       CMAKE_CROSSCOMPILING 这是交叉编译变量,所有开启交叉编译后这个变量是有值的
# ----------------------------------------------------------------------------
if(UNIX)
if(NOT APPLE_FRAMEWORK OR OPENCV_ENABLE_PKG_CONFIG)
if(CMAKE_CROSSCOMPILING AND NOT DEFINED ENV{PKG_CONFIG_LIBDIR} AND NOT DEFINED ENV{PKG_CONFIG_SYSROOT_DIR}
   AND NOT OPENCV_ENABLE_PKG_CONFIG
)
 if(NOT PkgConfig_FOUND)
   message(STATUS "OpenCV disables pkg-config to avoid using of host libraries. Consider using PKG_CONFIG_LIBDIR to specify target SYSROOT")
 elseif(OPENCV_SKIP_PKG_CONFIG_WARNING)
   message(WARNING "pkg-config is enabled in cross-compilation mode without defining of PKG_CONFIG_LIBDIR environment variable. This may lead to misconfigured host-based dependencies.")
 endif()
elseif(OPENCV_DISABLE_PKG_CONFIG)
 if(PkgConfig_FOUND)
   message(WARNING "OPENCV_DISABLE_PKG_CONFIG flag has no effect")
 endif()
else()
 find_package(PkgConfig QUIET)
endif()
endif()
#-------------------------------添加下一行
find_package(PkgConfig QUIET) 

3.添加ANDROID 条件(不改也无所谓),这行只是一个展示

# ========================== VIDEO IO ==========================
status("")
status("  Video I/O:")

if(WITH_1394 OR HAVE_DC1394_2)
status("    DC1394:" HAVE_DC1394_2 THEN "YES (${DC1394_2_VERSION})" ELSE NO)
endif()

if(WITH_FFMPEG OR HAVE_FFMPEG)
if(OPENCV_FFMPEG_USE_FIND_PACKAGE)
status("    FFMPEG:"       HAVE_FFMPEG         THEN "YES (find_package)"                       ELSE "NO (find_package)")
elseif(WIN32 OR ANDROID)   ####在这一行添加OR ANDROID
status("    FFMPEG:"       HAVE_FFMPEG         THEN "YES (prebuilt binaries)"                  ELSE NO)
else()
status("    FFMPEG:"       HAVE_FFMPEG         THEN YES ELSE NO)
endif()
status("      avcodec:"      FFMPEG_libavcodec_VERSION    THEN "YES (${FFMPEG_libavcodec_VERSION})"    ELSE NO)
status("      avformat:"     FFMPEG_libavformat_VERSION   THEN "YES (${FFMPEG_libavformat_VERSION})"   ELSE NO)
status("      avutil:"       FFMPEG_libavutil_VERSION     THEN "YES (${FFMPEG_libavutil_VERSION})"     ELSE NO)
status("      swscale:"      FFMPEG_libswscale_VERSION    THEN "YES (${FFMPEG_libswscale_VERSION})"    ELSE NO)
status("      avresample:"   FFMPEG_libavresample_VERSION THEN "YES (${FFMPEG_libavresample_VERSION})" ELSE NO)
endif()

<2> 修改opencv-4.5.5/modules/videoio/cmake目录下的detect_ffmpeg.cmake

1.这一条可以不用管,只是加了一个注释

# --- FFMPEG --- 通过find_pack查找ffmpeg 编译安装的ffmpeg 默认不支持cmake查找,默认是通过pkg-config查找
if(NOT HAVE_FFMPEG AND OPENCV_FFMPEG_USE_FIND_PACKAGE)
if(OPENCV_FFMPEG_USE_FIND_PACKAGE STREQUAL "1" OR OPENCV_FFMPEG_USE_FIND_PACKAGE STREQUAL "ON")
set(OPENCV_FFMPEG_USE_FIND_PACKAGE "FFMPEG")
endif()
find_package(${OPENCV_FFMPEG_USE_FIND_PACKAGE}) # Required components: AVCODEC AVFORMAT AVUTIL SWSCALE
if(FFMPEG_FOUND OR FFmpeg_FOUND)
set(HAVE_FFMPEG TRUE)
endif()
endif()


#windows 去下载dll
# if(NOT HAVE_FFMPEG AND WIN32 AND NOT ARM AND NOT OPENCV_FFMPEG_SKIP_DOWNLOAD)
#   include("${OpenCV_SOURCE_DIR}/3rdparty/ffmpeg/ffmpeg.cmake")
#   download_win_ffmpeg(FFMPEG_CMAKE_SCRIPT)
#   if(FFMPEG_CMAKE_SCRIPT)
#     include("${FFMPEG_CMAKE_SCRIPT}")
#     set(HAVE_FFMPEG TRUE)
#     set(HAVE_FFMPEG_WRAPPER TRUE)
#   endif()
# endif()

2.指定ffmepg库路径

因为指定了PKG_CONFIG_PATH这个环境变量后,cmake 通过pkg-config ,然后pkg_check_modules 找不出库的全路径(为什么,目前也没有弄清楚)
根据自己编译ffmpeg 库目录设置

-------------------定位到这行-------------------
if(NOT HAVE_FFMPEG AND PKG_CONFIG_FOUND)
#函数定义在cmake/OpenCVUtils.cmake
ocv_check_modules(FFMPEG libavcodec libavformat libavutil libswscale)
#这个地方为什么要手动指定,没有弄清楚
#指定PKG-CONFIG-PATH 后pkgcfg_lib_xxxx_xxxx是一个no found的状态
#FFMPEG_LIBRARY_DIRS 这变量是pkg_check_modules找出来的
set(pkgcfg_lib_FFMPEG_avformat ${FFMPEG_LIBRARY_DIRS}/libavformat.a)
set(pkgcfg_lib_FFMPEG_avcodec ${FFMPEG_LIBRARY_DIRS}/libavcodec.a)
set(pkgcfg_lib_FFMPEG_avutil ${FFMPEG_LIBRARY_DIRS}/libavutil.a)
set(pkgcfg_lib_FFMPEG_swscale ${FFMPEG_LIBRARY_DIRS}/libswscale.a)
set(pkgcfg_lib_FFMPEG_swresample ${FFMPEG_LIBRARY_DIRS}/libswresample.a)
#手动指定了后需要重新设置FFMPEG_LIBRARIES 、FFMPEG_LINK_LIBRARIES这两个变量
set(FFMPEG_LIBRARIES ${pkgcfg_lib_FFMPEG_avformat} ${pkgcfg_lib_FFMPEG_avcodec} ${pkgcfg_lib_FFMPEG_avutil} ${pkgcfg_lib_FFMPEG_swscale} ${pkgcfg_lib_FFMPEG_swresample})
set(FFMPEG_LINK_LIBRARIES ${FFMPEG_LIBRARIES})
if(FFMPEG_FOUND)
ocv_check_modules(FFMPEG_libavresample libavresample) # optional
#-------------------------------也要重新设置----------------------------
set(pkgcfg_lib_FFMPEG_libavresample_avresample ${FFMPEG_LIBRARY_DIRS}/libavresample.a)
set(FFMPEG_libavresample_LIBRARIES ${pkgcfg_lib_FFMPEG_libavresample_avresample})
#----------------------------------------------------------------------
if(FFMPEG_libavresample_FOUND)
 list(APPEND FFMPEG_LIBRARIES ${FFMPEG_libavresample_LIBRARIES})
 list(APPEND _used_ffmpeg_libraries libavresample)
endif()
set(HAVE_FFMPEG TRUE)
else()
set(_missing_ffmpeg_libraries "")
foreach (ffmpeg_lib ${_required_ffmpeg_libraries})
 if (NOT FFMPEG_${ffmpeg_lib}_FOUND)
   list(APPEND _missing_ffmpeg_libraries ${ffmpeg_lib})
 endif()
endforeach ()
message(STATUS "FFMPEG is disabled. Required libraries: ${_required_ffmpeg_libraries}."
       " Missing libraries: ${_missing_ffmpeg_libraries}")
unset(_missing_ffmpeg_libraries)
endif()
endif()

3.下面会编译测试代码,但是会编译失败,然后会设置HAVE_FFMPEG FALSE,所以要注释这一行

为什么会编译失败(还没有理解,好像是库没有链接上)(这个代码编译通过一次,然后第二次重新改cmake 后然后就一直失败)

if(HAVE_FFMPEG AND NOT HAVE_FFMPEG_WRAPPER AND NOT OPENCV_FFMPEG_SKIP_BUILD_CHECK)
message(FFMPEG_LIBRARIES:::${FFMPEG_LIBRARIES})
try_compile(__VALID_FFMPEG
 "${OpenCV_BINARY_DIR}"
 "${OpenCV_SOURCE_DIR}/cmake/checks/ffmpeg_test.cpp"
 CMAKE_FLAGS "-DINCLUDE_DIRECTORIES:STRING=${FFMPEG_INCLUDE_DIRS}"
             "-DLINK_LIBRARIES:STRING=${FFMPEG_LIBRARIES}"
 OUTPUT_VARIABLE TRY_OUT
)
if(NOT __VALID_FFMPEG)
#message(FATAL_ERROR "FFMPEG: test check build log:\n${TRY_OUT}")
message(STATUS "WARNING: Can't build ffmpeg test code")
#--------------------这一行-----------------------------
#set(HAVE_FFMPEG FALSE)
endif()
endif()

<3>修改opencv-4.5.5/modules/java/CMakeLists.txt

因为build android project的时候会出现ndk not configure,
配置了ndk环境变量ANDROID_NDK、NDK_ROOT 还是会出现这个错误(大概是gradle文件配置的问题)
所以注释掉build android project
不能直接通过BUILD_ANDROID_PROJECTS OFF,关掉后里面就不会生成libopencv_java4.so

*****************定位到这一行*****************
if(ANDROID)
message(" don't generates android project")
#add_subdirectory(android_sdk)  # generates ${the_module}_android target
else()
add_subdirectory(jar)  # generates ${the_module}_jar target
endif()

<4>设置环境变量

PKG_CONFIG_PATH这个环境变量是让pkg-config去找到自己刚刚编译的ffmpeg 库
ANDROID_HOMEANDROID_SDK_ROOTANDROID_SDK这三个环境变量是android sdk的,不需要libopencv_java4.so可以不需要指定

export PKG_CONFIG_PATH=/root/tang/ndk/ffmpeg-4.4.1/build/lib/pkgconfig
export ANDROID_HOME=/root/tang/ndk/cmdline-tools/android_sdk
export ANDROID_SDK_ROOT=/root/tang/ndk/cmdline-tools/android_sdk
export ANDROID_SDK=/root/tang/ndk/cmdline-tools/android_sdk

评论:

另一种编译libopencv_java4.so的方法(通过ant)
这种需要安装java环境,还要改cmake文件。(这里面没有介绍0

#下面通过ant 编译需要设置的(当前不需要)
export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64
export ANDROID_NDK=/root/tang/ndk/android-ndk-r23b
export NDK_ROOT=/root/tang/ndk/android-ndk-r23b

set(JAVA_AWT_LIBRARY "$ENV{JAVA_HOME}/lib/libjawt.so")
set(JAVA_JVM_LIBRARY "$ENV{JAVA_HOME}/lib/server/libjvm.so")

set(JAVA_INCLUDE_PATH "$ENV{JAVA_HOME}/include")
set(JAVA_INCLUDE_PATH2 "$ENV{JAVA_HOME}/include/linux")
set(JAVA_AWT_INCLUDE_PATH "$ENV{JAVA_HOME}/include")

执行编译

CMAKE_TOOLCHAIN_FILEANDROID_NDK 这两个路径根据自己ndk目录设置
CMAKE_INSTALL_PREFIX这个是自己的make install 后的目录

cmake -D CMAKE_TOOLCHAIN_FILE=/root/tang/ndk/android-ndk-r23b/build/cmake/android.toolchain.cmake \
-D CMAKE_INSTALL_PREFIX=./mm_install \
-D CMAKE_BUILD_TYPE=RELEASE \
-D ANDROID_ABI=arm64-v8a \
-D ANDROID_PLATFORM=24 \
-D ANDROID_NDK=/root/tang/ndk/android-ndk-r23b \
-D BUILD_ANDROID_PROJECTS=ON \
-D BUILD_ANDROID_EXAMPLES=OFF \
-D BUILD_SHARED_LIBS=OFF \
-D BUILD_FAT_JAVA_LIB=ON  \
-D BUILD_JAVA=ON  \
-D ANDROID_STL=c++_static \
-D ANDROID_ARM_NEON=ON \
-D WITH_FFMPEG=ON \
-D CMAKE_CXX_FLAGS="-llog" .. 

#要确认videoIO 模块是否是ON
make -jnproc
make install

评论:
如果编译过程中找不到ffmpeg,就需要更改ffmpeg里pkg-config文件把里面的路径改成绝对路径

更改OpenCVModules.cmake

因为ffmpeg库 的目录指定的是绝对路径,所以更改sdk/native/jni/abi-arm64-v8a/OpenCVModules.cmake
我是把ffmepg库移入到sdk/native/3rdparty/libs/arm64-v8a这个目录

======================定位到这个地方======================
set_target_properties(ocv.3rdparty.ffmpeg PROPERTIES
  INTERFACE_COMPILE_DEFINITIONS "HAVE_FFMPEG"
  INTERFACE_LINK_LIBRARIES "/root/tang/ndk/ffmpeg-4.4.1/build/lib/libavformat.a;/root/tang/ndk/ffmpeg-4.4.1/build/lib/libavcodec.a;/root/tang/ndk/ffmpeg-4.4.1/build/lib/libavutil.a;/root/tang/ndk/ffmpeg-4.4.1/build/lib/libswscale.a;/root/tang/ndk/ffmpeg-4.4.1/build/lib/libswresample.a;/root/tang/ndk/ffmpeg-4.4.1/build/lib/libavresample.a"
)
======================改成下面的======================
#我是把ffmpeg库放在和liblibprotubuf.a一个目录
set_target_properties(ocv.3rdparty.ffmpeg PROPERTIES
  INTERFACE_COMPILE_DEFINITIONS "HAVE_FFMPEG"
  INTERFACE_LINK_LIBRARIES  "${_IMPORT_PREFIX}/sdk/native/3rdparty/libs/${ANDROID_ABI}/libavformat.a;${_IMPORT_PREFIX}/sdk/native/3rdparty/libs/${ANDROID_ABI}/libavcodec.a;${_IMPORT_PREFIX}/sdk/native/3rdparty/libs/${ANDROID_ABI}/libavutil.a;${_IMPORT_PREFIX}/sdk/native/3rdparty/libs/${ANDROID_ABI}/libswscale.a;${_IMPORT_PREFIX}/sdk/native/3rdparty/libs/${ANDROID_ABI}/libswresample.a";
   )

或者删除下面位置的**\$**,然后自己在工程里面链接ffmpeg 库

set_target_properties(opencv_videoio PROPERTIES
  INTERFACE_LINK_LIBRARIES "opencv_core;opencv_imgproc;opencv_imgcodecs;opencv_core;opencv_imgproc;opencv_imgcodecs;\$<LINK_ONLY:dl>;\$<LINK_ONLY:m>;\$<LINK_ONLY:log>;\$<LINK_ONLY:tegra_hal>;\$<LINK_ONLY:ocv.3rdparty.ffmpeg>;\$<LINK_ONLY:ocv.3rdparty.android_mediandk>;\$<LINK_ONLY:ocv.3rdparty.android_native_camera>"
)

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(1)
扎眼的阳光的头像扎眼的阳光普通用户
上一篇 2022年4月20日
下一篇 2022年4月20日

相关推荐