1. 编译的原因
想在目标平台 gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf
上读取视频,作为算法 SDK 的输入数据;交叉编译了带 videoio
模块的 OpenCV
,遇到 VideoCapture
无法读取 .mp4
格式视频的问题。排查后发现:
-
尽管不指定 FFMPEG,VideoIO 模块也能编译出来,但对应的编解码器 FOURCC 只能编解码
.avi
格式的视频,无法满足实际需求。 -
videoio
模块可以有不同 backend,在 PC 上先前主要是 Windows 平台,使用了预编译的 ffmpeg dll,没有对编解码 backend 引起重视;而aarch64-none-linux-gnu
这样的嵌入式平台,没找到现成的FFMPEG
库(如果有,请留言告知),于是手动编译FFMPEG
,并在 OpenCV 的 cmake 构建中集成。
2. 环境
- Host: Ubuntu 18.04
- 交叉编译工具: rv1126 工具链 gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf
- cmake 3.20
3. ffmpeg
3.1. 下载编译
#!/bin/bash
OUT_DIR=../out_ffmpeg_rv1126
CROSS_PREFIX=/opt/tools/rk_docs_and_compiler/compiler/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/bin/arm-linux-gnueabihf-
FFMPENG_BRANCH=n4.2.2
MAKE_JOBE=40
#download
git clone https://git.ffmpeg.org/ffmpeg.git ffmpeg
cd ffmpeg
git checkout -b ${FFMPENG_BRANCH}
./configure \
--prefix=${OUT_DIR} \
--shlibdir=${OUT_DIR}/lib \
--disable-shared \
--disable-doc \
--arch=aarch64 \
--cross-prefix=${CROSS_PREFIX} \
--target-os=linux \
--disable-x86asm \
--disable-asm \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--enable-avresample
make -j${MAKE_JOBE}
make install
3.2. cmake支持
ffmpeg 官方不提供 cmake 构建支持,github 上找到的 ffmpeg cmake 写得很长但不起作用;简单起见,用如下 Python 代码生成 ffmpeg-config.cmake
(注意库版本为手动硬编码,库的顺序也很重要)
gen-ffmpeg-cmake-config.py
:
#!/usr/bin/env python
#coding: utf-8
"""
Generate ffmpeg-config.cmake
"""
# note: the order matters
# ref: https://blog.csdn.net/zijin0802034/article/details/52779791
ffmpeg_lib_desc = {
'libavformat': '58.71.100',
'libavdevice': '58.12.100',
'libavcodec': '58.129.100',
'libavutil': '56.67.100',
'libswscale': '5.8.100',
'libswresample': '3.8.100',
'libavfilter': '7.109.100',
'libavresample': '4.0.0',
}
content_lines = [
'set(ffmpeg_path "${CMAKE_CURRENT_LIST_DIR}")',
'',
'set(FFMPEG_EXEC_DIR "${ffmpeg_path}/bin")',
'set(FFMPEG_LIBDIR "${ffmpeg_path}/lib")',
'set(FFMPEG_INCLUDE_DIRS "${ffmpeg_path}/include")',
'',
]
# setup library names
content_lines.append('# library names')
content_lines.append(
'set(FFMPEG_LIBRARIES',
)
for key, value in ffmpeg_lib_desc.items():
lib_name = key
lib_version = value
content_lines.append(
' ${FFMPEG_LIBDIR}/' + lib_name + '.a'
)
content_lines.append(' pthread')
content_lines.append(')')
content_lines.append('')
# setup library found
content_lines.append('# found status')
for key, value in ffmpeg_lib_desc.items():
lib_name = key
content_lines.append(
'set(FFMPEG_{:s}_FOUND TRUE)'.format(lib_name)
)
content_lines.append('')
# setup library versions
content_lines.append('# library versions')
for key, value in ffmpeg_lib_desc.items():
lib_name = key
lib_version = value
content_lines.append(
'set(FFMPEG_{:s}_VERSION {:s})'.format(lib_name, lib_version)
)
content_lines.append('')
# final stuffs
content_lines.append('set(FFMPEG_FOUND TRUE)')
content_lines.append('set(FFMPEG_LIBS ${FFMPEG_LIBRARIES})')
# summary up and write
fout = open('ffmpeg-config.cmake', 'w')
for line in content_lines:
fout.write(line + '\n')
fout.close()
其中版本号的设定,是为了在 OpenCV 中找到 FFMPEG 后,能通过最低版本检查。
将这个文件,放在 ffmpeg 安装路径的根目录,并运行,得到 ffmpeg-config.cmake
,则后续可用 find_package(FFMPEG)
使用;
4. opencv
4.1. toolchain
交叉编译阶段用到的 toolchain 文件里,如果后续用的是 ffmpeg 动态库,则需要在 toolchain 里指定 rpath。本人最终用的静态库,因而未设 rpath。
如下脚本,保存为 build/aarch64-none-linux-gnu.toolchain.cmake
# message("begin child cmake")
# set cross-compiled system type, it's better not use the type which cmake cannot recognized.
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR ARM)
# when gcc-arm-aarch64-none-linux-gnu was installed, toolchain was available as below:
set(TOOLCHAIN_DIR "/opt/tools/rk_docs_and_compiler/compiler/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/bin")
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_DIR}/arm-linux-gnueabihf-g++)
set(CMAKE_C_COMPILER ${TOOLCHAIN_DIR}/arm-linux-gnueabihf-gcc)
# set searching rules for cross-compiler
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
# other needed options
#set(GNUEABIHF_C_FLAGS "-Wall -fPIC -mcpu=cortex-a9 -mfpu=neon-fp16 -mfloat-abi=hard -mthumb-interwork -marm" CACHE INTERNAL docstring)
#set(GNUEABIHF_C_FLAGS "-Wall -fPIC -Wl,-rpath-link=/home/zz/soft/ffmpeg/aarch64-none-linux-gnu/lib" CACHE INTERNAL docstring) # for shared libs, rpath required
set(GNUEABIHF_C_FLAGS "-Wall -fPIC" CACHE INTERNAL docstring)
set(GNUEABIHF_CXX_FLAGS "")
set(CMAKE_C_FLAGS "-march=armv7-a ${CMAKE_C_FLAGS} ${GNUEABIHF_C_FLAGS}" CACHE STRING "C flags" FORCE)
set(CMAKE_CXX_FLAGS "-march=armv7-a ${CMAKE_CXX_FLAGS} ${GNUEABIHF_C_FLAGS} ${NDK_CXX_FLAGS}" CACHE STRING "C++ flags" FORCE)
https://stackoverflow.com/questions/12427928/configure-and-build-opencv-to-custom-ffmpeg-install
4.2. 下载编译
用如下脚本来编译,保存为 build/aarch64-linux-gnu.sh
并执行
#!/bin/bash
OUT_DIR=../out_opencv_rv1126
OPENCV_MODULES_DIR=../opencv_contrib/modules/
FFMPEG_DIR=../out_ffmpeg_rv1126
OPENCV_BRANCH=4.5.0
MAKE_JOBE=40
#download
git clone --branch ${OPENCV_BRANCH} https://github.com/opencv/opencv.git
git clone --branch ${OPENCV_BRANCH} https://github.com/opencv/opencv_contrib.git
#编译
mkdir build_opencv
cd build_opencv
TOOLCHAIN=../cross.cmake
cmake -DCMAKE_TOOLCHAIN_FILE=$TOOLCHAIN \
-D CMAKE_BUILD_TYPE=Release \
-D CMAKE_INSTALL_PREFIX=${OUT_DIR} \
-D OPENCV_EXTRA_MODULES_PATH=${OPENCV_MODULES_DIR} \
-D WITH_FFMPEG=ON \
-D OPENCV_FFMPEG_USE_FIND_PACKAGE=ON \
-D OPENCV_FFMPEG_SKIP_BUILD_CHECK=ON \
-D FFMPEG_DIR=${FFMPEG_DIR} \
-D BUILD_TESTS=OFF \
-D BUILD_PERF_TESTS=OFF \
-D BUILD_opencv_apps=OFF \
-D WITH_PROTOBUF=OFF \
-D WITH_QUIRC=OFF \
-D WITH_1394=OFF \
-D WITH_CUDA=OFF \
-D WITH_OPENCL=OFF \
-D WITH_VTK=OFF \
-D WITH_MATLAB=OFF \
-D BUILD_DOCS=OFF \
-D BUILD_opencv_python3=OFF \
-D BUILD_opencv_python2=OFF \
-D WITH_IPP=OFF \
-D BUILD_ANDROID_PROJECTS=OFF \
-D BUILD_ANDROID_EXAMPLES=OFF \
-D BUILD_ANDROID_SERVICE=OFF \
-D BUILD_SHARED_LIBS=OFF \
-D OPENCV_FORCE_3RDPARTY_BUILD=ON \
-D BUILD_JAVA=OFF \
-D BUILD_FAT_JAVA_LIB=OFF \
-D OPENCV_GENERATE_PKGCONFIG=ON \
../opencv
make -j${MAKE_JOBE}
make install