项目目的是为了能够再android 上面通过opencv 的cv::VideoCapture 读取视频,但实际上opencv 不支持这样做,具体是通过cmake 中设置的;opencv 读视频默认是通过 ffmpeg 实现的,调研发现 ffmepg 可以编译成android 的库,所以编译步骤是:编译android 版本的 ffmpeg,编译 包含 ffmepg 的 opencv
环境条件:Android的 ABI= arm64-v8,API = android-27(android 8.1)
编译器:android-ndk-r16
- 编译android 版本的 ffmpeg
参考了 https://github.com/eudora-jia/android-opencv-ffmpeg
上面作者使用的是ffmpeg 编译的是动态库,为了方便测试,这里编译成动态库;为了编译方便,直接下载的ffmpeg 编译脚本configure 下 使用build.sh 进行编译
make clean
#自己的ndk路径(相对路径或绝对路径都可以)
export MY_TOOLCHAIN=/home/panxiaogong/3rdparty/android-ndk-r16b/myarm64-andorid-toolchain
#最终的库生成位置为上一级目录的 simplefflib
export PREFIX=../simplefflib
build_one(){
./configure --target-os=android --prefix=$PREFIX \
--enable-cross-compile \
--enable-runtime-cpudetect \
--disable-asm \
--disable-x86asm \
--disable-network \
--disable-doc \
--arch=aarch64 \
--cc=$MY_TOOLCHAIN/bin/clang \
--cxx=$MY_TOOLCHAIN/bin/clang++ \
--disable-stripping \
--nm=$MY_TOOLCHAIN/aarch64-linux-android/bin/nm \
--sysroot=$MY_TOOLCHAIN/sysroot \
--enable-jni \
--enable-mediacodec \
--enable-avresample \
--enable-gpl --enable-shared --enable-small \
--disable-ffprobe --disable-ffplay --disable-debug \
--extra-cflags="-fPIC -D__thumb__ -mthumb -Wfatal-errors -Wno-deprecated -mfloat-abi=softfp -marm -D__ANDROID_API__=21 -march=armv8-a"
}
#make
#make install
build_one
其中我是用的是ndk 的独立工具链,编译器是clang,后面任何编译统一使用clang
--disable-network 屏蔽的是以下错误:
libavformat/udp.c:290:28: error: member reference base type '__be32' (aka 'u
--extra-cflags 中的 -D__ANDROID_API__=21 ,(和板子的 android-27 不冲突)为了屏蔽由于ndk 的原因爆出的以下错误:
undefined reference to `stderr'
参考 https://stackoverflow.com/questions/39322852/android-ndk-undefined-reference-to-stderr
编译过程:
./configure --prefix=/your/install/path
./build.sh
make -j
make install
编译 android 的 opencv
编译脚本如下 build.sh:
#!/usr/bin/env bash
set -eu
# shellcheck source=/dev/null
#. "$(dirname "$0")/../config.sh"
OPENCV_ROOT=/your/home/android/opencv-3.1.0
BUILD_DIR=/your/home/android/opencv-3.1.0/build_android4ffmpeg
ANDROID_ABI=arm64-v8a
NDK_ROOT=/your/home/android-ndk-r16b
INSTALL_DIR=/your/home/android/opencv-3.1.0/build_install4ffmpeg
if [ "${ANDROID_ABI}" = "armeabi" ]; then
API_LEVEL=19
else
API_LEVEL=27
fi
rm -rf "${BUILD_DIR}"
mkdir -p "${BUILD_DIR}"
pushd "${BUILD_DIR}"
#-D WITH_OPENMP=ON \ opencv 只是为了读取图像 没有必要启用
cmake -DCMAKE_BUILD_WITH_INSTALL_RPATH=ON \
-DCMAKE_TOOLCHAIN_FILE="${NDK_ROOT}/build/cmake/android.toolchain.cmake" \
-DANDROID_NDK="${NDK_ROOT}" \
-DANDROID_NATIVE_API_LEVEL=${API_LEVEL} \
-DANDROID_ABI="${ANDROID_ABI}" \
-DANDROID_STL=c++_shared \
-DCMAKE_BUILD_TYPE=Debug \
-D WITH_CUDA=OFF \
-D WITH_MATLAB=OFF \
-D WITH_FFMPEG=ON \
-D BUILD_ANDROID_EXAMPLES=OFF \
-D BUILD_DOCS=OFF \
-D BUILD_PERF_TESTS=OFF \
-D BUILD_TESTS=OFF \
-D BUILD_WITH_DEBUG_INFO=ON \
-DCMAKE_INSTALL_PREFIX="${INSTALL_DIR}/opencv" \
..
#make -j
#rm -rf "${INSTALL_DIR}/opencv"
#make install/strip
#git clean -fd 2> /dev/null || true
popd
其中 -D WITH_FFMPEG=ON ,是编译时添加ffmpeg,设置这个远远不够,需要继续更改opencv 的CmakeLists.txt 脚本,
再CmakeLists.txt 里面搜索WITH_FFMPEG 到此处
#更改为android 支持ffmpeg
OCV_OPTION(WITH_FFMPEG "Include FFMPEG support" ON IF ( NOT IOS AND NOT WINRT) )
然后进入opencv 的cmake 文件夹,打开OpenCVFindLibsVideo.cmake 然后修改为:
ocv_clear_vars(HAVE_FFMPEG HAVE_FFMPEG_CODEC HAVE_FFMPEG_FORMAT HAVE_FFMPEG_UTIL HAVE_FFMPEG_SWSCALE HAVE_FFMPEG_RESAMPLE HAVE_GENTOO_FFMPEG HAVE_FFMPEG_FFMPEG)
if(WITH_FFMPEG)
if(WIN32 AND NOT ARM) #为了ffmepg 编译进去,这里暂时注释掉官方的写法 2019-03-17 pxg
include("${OpenCV_SOURCE_DIR}/3rdparty/ffmpeg/ffmpeg.cmake")
elseif(TRUE)
CHECK_MODULE(libavcodec HAVE_FFMPEG_CODEC)
CHECK_MODULE(libavformat HAVE_FFMPEG_FORMAT)
CHECK_MODULE(libavutil HAVE_FFMPEG_UTIL)
CHECK_MODULE(libswscale HAVE_FFMPEG_SWSCALE)
CHECK_MODULE(libavresample HAVE_FFMPEG_RESAMPLE)
CHECK_INCLUDE_FILE(libavformat/avformat.h HAVE_GENTOO_FFMPEG)
CHECK_INCLUDE_FILE(ffmpeg/avformat.h HAVE_FFMPEG_FFMPEG)
#if(NOT HAVE_GENTOO_FFMPEG AND NOT HAVE_FFMPEG_FFMPEG)
# if(EXISTS /usr/include/ffmpeg/libavformat/avformat.h OR HAVE_FFMPEG_SWSCALE)
# set(HAVE_GENTOO_FFMPEG TRUE)
# endif()
#endif()
#if(HAVE_FFMPEG_CODEC AND HAVE_FFMPEG_FORMAT AND HAVE_FFMPEG_UTIL AND HAVE_FFMPEG_SWSCALE)
# set(HAVE_FFMPEG TRUE)
#endif()
if(HAVE_FFMPEG)
# Find the bzip2 library because it is required on some systems
FIND_LIBRARY(BZIP2_LIBRARIES NAMES bz2 bzip2)
if(NOT BZIP2_LIBRARIES)
# Do an other trial
FIND_FILE(BZIP2_LIBRARIES NAMES libbz2.so.1 PATHS /lib)
endif()
else()
MESSAGE(STATUS "start to find ffmpeg include")
#find_path(FFMPEG_INCLUDE_DIR "libavformat/avformat.h"
# PATHS /your/path/android/ffmpeg4opencv/simplefflib PATH_SUFFIXES include
# DOC "The path to FFMPEG headers")
set(FFMPEG_INCLUDE_DIR /your/path/android/ffmpeg4opencv/simplefflib/include)
if(FFMPEG_INCLUDE_DIR)
set(HAVE_GENTOO_FFMPEG TRUE)
set(FFMPEG_LIB_DIR "${FFMPEG_INCLUDE_DIR}/../lib" CACHE PATH "Full path of FFMPEG library directory")
#find_library(FFMPEG_CODEC_LIB "avcodec" HINTS "${FFMPEG_LIB_DIR}")
#find_library(FFMPEG_FORMAT_LIB "avformat" HINTS "${FFMPEG_LIB_DIR}")
#find_library(FFMPEG_UTIL_LIB "avutil" HINTS "${FFMPEG_LIB_DIR}")
#find_library(FFMPEG_SWSCALE_LIB "swscale" HINTS "${FFMPEG_LIB_DIR}")
#find_library(FFMPEG_RESAMPLE_LIB "avresample" HINTS "${FFMPEG_LIB_DIR}")
#if(FFMPEG_CODEC_LIB)
# MESSAGE(STATUS "FFMPEG_CODEC_LIB found")
set(HAVE_FFMPEG_CODEC 1)
#endif()
#if(FFMPEG_FORMAT_LIB)
set(HAVE_FFMPEG_FORMAT 1)
#endif()
#if(FFMPEG_UTIL_LIB)
set(HAVE_FFMPEG_UTIL 1)
#endif()
#if(FFMPEG_SWSCALE_LIB)
set(HAVE_FFMPEG_SWSCALE 1)
#endif()
set(HAVE_FFMPEG_RESAMPLE 1)
if(FFMPEG_CODEC_LIB AND FFMPEG_FORMAT_LIB AND
FFMPEG_UTIL_LIB AND FFMPEG_SWSCALE_LIB)
set(ALIASOF_libavcodec_VERSION "Unknown")
set(ALIASOF_libavformat_VERSION "Unknown")
set(ALIASOF_libavutil_VERSION "Unknown")
set(ALIASOF_libswscale_VERSION "Unknown")
set(HAVE_FFMPEG 1)
if(FFMPEG_RESAMPLE_LIB)
set(HAVE_FFMPEG_RESAMPLE 1)
set(ALIASOF_libavresample_VERSION "Unknown")
endif()
endif()
endif(FFMPEG_INCLUDE_DIR)
SET(HAVE_FFMPEG TRUE)
if(HAVE_FFMPEG)
set(VIDEOIO_LIBRARIES ${VIDEOIO_LIBRARIES} "${FFMPEG_LIB_DIR}/libavcodec.a" "${FFMPEG_LIB_DIR}/libswresample.a"
"${FFMPEG_LIB_DIR}/libavformat.a" "${FFMPEG_LIB_DIR}/libavutil.a"
"${FFMPEG_LIB_DIR}/libswscale.a")
if(HAVE_FFMPEG_RESAMPLE)
set(VIDEOIO_LIBRARIES ${VIDEOIO_LIBRARIES} "${FFMPEG_LIB_DIR}/libavresample.a")
MESSAGE(STATUS "${VIDEOIO_LIBRARIES}")
endif()
ocv_include_directories(${FFMPEG_INCLUDE_DIR})
endif(HAVE_FFMPEG)
endif()
endif()
endif(WITH_FFMPEG)
之后执行build.sh 之后即开始编译
编译sample
实例代码如下:
#include <opencv2/opencv.hpp>
#include <iostream>
int main(int argc, char* argv[])
{
std::string videoFileName(argv[1]);
cv::VideoCapture capture(videoFileName);
if (!capture.isOpened()) {
capture.release();
return 0;
}
printf("read video successfully\n");
return 0;
}
编译脚本build.sh 如下
NDK_ROOT=/your/home/android-ndk-r16b
rm -rf build-android-aarch64
mkdir -p build-android-aarch64
pushd build-android-aarch64
cmake -DCMAKE_TOOLCHAIN_FILE=$NDK_ROOT/build/cmake/android.toolchain.cmake \
-DANDROID_ABI="arm64-v8a" \
-DCMAKE_BUILD_TYPE=Debug \
-DANDROID_STL=c++_shared \
-DANDROID_PLATFORM=android-27 \
..
make -j
#make install
popd
CMakeLists.txt 如下:注意 需要添加ffmpeg 的静态库
cmake_minimum_required(VERSION 3.14)
SET(DEMO video_reader)
PROJECT(${DEMO})
SET(NDK_DIR $ENV{HOME}/android-ndk-r16b)
SET(OMP_DIR ${NDK_DIR}/toolchains/llvm/prebuilt/linux-x86_64/lib64/clang/5.0.300080/lib/linux/aarch64)
SET(OpenCV_DIR $ENV{HOME}/opencv-3.1.0/build_install4ffmpeg/opencv/sdk/native/jni)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti -fPIC -Wall -fopenmp -O3 ")
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -Wall -O3 ")
#-fno-rtti 这个flag 可以解决 error: undefined reference to 'typeinfo for http://www.cnblogs.com/beautiful-scenery/p/3589529.html?utm_source=tuicool
# 但可能错过例如 undefined reference to `__kmpc_for_static_init_4' 等未知符号名错误
#openmp 开启多线程加速
option(_OPENMP "openmp support" ON)
if(_OPENMP)
find_package(OpenMP)
# For CMake < 3.9, we need to make the target ourselves
if(NOT TARGET OpenMP::OpenMP_CXX)
find_package(Threads REQUIRED)
add_library(OpenMP::OpenMP_CXX IMPORTED INTERFACE)
set_property(TARGET OpenMP::OpenMP_CXX
PROPERTY INTERFACE_COMPILE_OPTIONS ${OpenMP_CXX_FLAGS})
# Only works if the same flag is passed to the linker; use CMake 3.9+ otherwise (Intel, AppleClang)
set_property(TARGET OpenMP::OpenMP_CXX
PROPERTY INTERFACE_LINK_LIBRARIES ${OpenMP_CXX_FLAGS} Threads::Threads)
endif()
endif()
#使用opencv config.cmake
find_package(OpenCV QUIET COMPONENTS core highgui imgproc imgcodec video)
if (NOT OpenCV_FOUND)
find_package(OpenCV REQUIRED COMPONENTS core highgui imgproc imgcodecs)
endif()
add_definitions(-std=c++11)
INCLUDE_DIRECTORIES(${OpenCV_DIR}/jni/include)
#INCLUDE_DIRECTORIES(${DLIB_DIR}/include)
#INCLUDE_DIRECTORIES(${NCNN_DIR}/include)
INCLUDE_DIRECTORIES(include)
#LINK_DIRECTORIES(${DLIB_DIR}/lib)
#LINK_DIRECTORIES(${NCNN_DIR}/lib)
#link_directories(./)
#LINK_DIRECTORIES(build-android-aarch64)
link_libraries(log)
link_libraries(${OpenCV_LIBS})
link_libraries(${NDK_DIR}/toolchains/llvm/prebuilt/linux-x86_64/lib64/clang/5.0.300080/lib/linux/aarch64/libomp.a)
aux_source_directory(src SRC_PATH)
#ADD_LIBRARY(drivers SHARED ${SRC_PATH})
add_executable(${DEMO} src/demo.cpp)
set_property(TARGET ${DEMO} PROPERTY POSITION_INDEPENDENT_CODE TRUE)
TARGET_LINK_LIBRARIES(${DEMO} /your/home/ffmpeg4opencv/simplefflib/include/../lib/libavcodec.a)
TARGET_LINK_LIBRARIES(${DEMO} /your/home/3rdparty/android/ffmpeg4opencv/simplefflib/include/../lib/libswresample.a)
TARGET_LINK_LIBRARIES(${DEMO} /your/home/3rdparty/android/ffmpeg4opencv/simplefflib/include/../lib/libavutil.a)