一、背景
之前使用 opencv-python 录制摄像头视频一直存在一个问题,录出的视频文件体积太大,主要问题在于没法控制码率,录制工业相机1小时视频,体积最大能到10G+,码率飙到 20000kbps+,实在恐怖。当时搜遍全网也没找到解决方法,最常见的在 cv2.VideoCapture() 中设置 bitrate 参数也是行不通。
opencv 的 cv2.VideoWriter() 并没有直接提供参数来设置视频的码率。码率通常是编解码器的属性,而 cv2.VideoWriter() 的设计比较简单,没有暴露这些细节。但可以通过指定合适的编解码器和调整视频的分辨率和帧率来间接影响视频的码率。如果需要更细粒度的控制,建议使用更专业的视频处理库,比如将 GStreamer 管道与 OpenCV 结合,这样可以设置视频的更多属性,包括码率。GStreamer 跟 ffmpeg 一样,也是一个多媒体框架,可以实现采集,编码,解码,渲染,滤镜等媒体解决方案,且支持跨平台实现。
无奈 opencv 默认关闭了 GStreamer 功能,当时也就没再深究,虽然视频体积大但也就凑合用了。直到最近,趁着手头工作不多,决定抽出一部分时间好好搞一下 opencv,通过源码编译的方式开启 GStreamer 支持,尝试使用 GStreamer 的方式录制视频,设置码率。本文先着重介绍一下如何开启 opencv 的 GStreamer 支持。
二、编译安装
2.1 准备工作
本部分主要参考 [1]
2.1.1 工具
gcc、x264、xvidcore、zlib、ffmpeg、opencv
2.1.2 工作目录
将所有工具解压到同一个目录下
同时新建一个文件夹用于存放后面编译好的文件
mkdir {builddir}
2.1.3 安装插件
sudo apt install make cmake cmake-gui pkg-config
2.1.4 创建虚拟环境
可以安装 anaconda 创建虚拟环境,或使用系统 python 创建(具体方法可参考上篇文章),由于本人要在原有 conda 虚拟环境中使用 opencv ,所以就直接使用原有 conda 环境。
2.2 编译组件
2.2.1 配置 gcc
sudo vi /etc/profile
export PATH=$PATH:/path/to/your/gcc/bin
source /etc/profile
配置完成后输入
aarch64-linux-gnu-gcc -v
输出如下内容表示配置成功
2.2.2 编译 zlib
(1)进入目录,进行编译配置
cd /path/to/your/zlib/dir
./configure --prefix=/path/to/your/{builddir}
(2)修改编译文件
vi Makefile
(3)修改内容
CC=aarch64-linux-gnu-gcc
LDSHARED=aarch64-linux-gnu-gcc -shared -Wl,-soname,libz.so.1,--version-script,zlib.map
AR=aarch64-linux-gnu-ar
RANLIB=aarch64-linux-gnu-ranlib
(4)开始编译
make -j4 && sudo make install
2.2.3 编译 x264
(1)进入目录,编译配置
cd /path/to/your/x264/dir
./configure --enable-shared --host=aarch64-linux-gnu --prefix=/path/to/your/{builddir} --enable-pic --disable-assembly
(2)开始编译
make -j4 && sudo make install
2.2.4 编译 xvidcore
(1)进入目录,编译配置
cd /path/to/your/{xvidcore_dir}/xvidcore/build/generic
./configure --prefix=/path/to/your/{builddir} --host=aarch64-linux-gnu --disable-assembly
(2)开始编译
make -j4 && sudo make install
2.2.5 编译安装 ffmpeg
* 本步骤如果有问题可参考 3.1
(1)进入目录,编译配置
cd /path/to/your/ffmpeg/dir
./configure --prefix=/path/to/your/{builddir} --enable-shared --disable-static --enable-gpl --enable-cross-compile --arch=aarch64 --disable-stripping --target-os=linux --enable-libx264 --enable-libxvid --enable-swscale --extra-ldflags=-L/path/to/your/{builddir}/lib --extra-cflags=-I/path/to/your/{builddir}/include --pkg-config=PKGCONFIG --ar=aarch64-linux-gnu-ar --ranlib=aarch64-linux-gnu-ranlib
忽略如下两条警告
WARNING: PKGCONFIG not found, library detection may fail.
WARNING: using libx264 without pkg-config
(2)开始编译
make -j4 && sudo make install
2.3 编译安装 opencv
2.3.1 安装所需环境包
(1)安装编译环境、gtk 包和相关编解码库
* 本步骤如有问题可参考 3.2
sudo apt install cmake build-essential libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev libtbb2 libtbb-dev libjpeg-dev libpng-dev libtiff-dev libdc1394-22-dev libjasper-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev
(2)安装 python-numpy (可选)
如果安装了 rknn_toolkit(or lite) 则不需要再安装,否则会导致 rknn_toolkit(or lite) 无法使用 [4],由于本人虚拟环境中已安装了 numpy,所以不用再安装
sudo apt install python-numpy
2.3.2 编译安装
(1)新建编译目录
cd /path/to/your/opencv/dir
mkdir build && cd build
(2)编译配置
vi toolchain.cmake
输入如下内容
set( CMAKE_SYSTEM_NAME Linux )
set( CMAKE_SYSTEM_PROCESSOR arm64 )
set( CMAKE_C_COMPILER aarch64-linux-gnu-gcc )
set( CMAKE_CXX_COMPILER aarch64-linux-gnu-g++ )
set( CMAKE_FIND_ROOT_PATH "/path/to/your/{builddir}" ) # 改成自己的编译文件路径
set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER )
set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY )
set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY )
set(CMAKE_C_FLAGS -Wl,-rpath-link=/path/to/your/{builddir}/lib) # 改成自己的编译文件路径
set(CMAKE_CXX_FLAGS -Wl,-rpath-link=/path/to/your/{builddir}/lib) # 改成自己的编译文件路径
编译配置
cmake -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake ../
此时 FFMPEG 的选项应该为 NO
(3)配置环境变量
sudo vi /etc/profile
export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/path/to/your/{builddir}/lib/pkgconfig
source /etc/profile
(4)cmake-gui
打开 cmake-gui,配置 pkg_config 路径,点 Add_Entry,增加 OPENCV_ENABLE_PKG_CONFIG 选项并打勾
完成后点击 Configure,此时 FFMPEG 选项应该变为 YES 了
点击 Generator 后退出,再次编译配置[10]
cmake -D CMAKE_BUILD_TYPE=RELEASE \
-D CMAKE_INSTALL_PREFIX=/usr/local \
-D WITH_GSTREAMER=ON \ # 开启GStreamer
-D WITH_FFMPEG=ON \ # 开启 ffmpeg
-D BUILD_opencv_python3=ON \ # 开启python3
-D BUILD_opencv_python2=OFF \
-D BUILD_EXAMPLES=OFF \
-D BUILD_opencv_python3=yes \
-D BUILD_opencv_python2=no \# 以下跟python解释器相关的配置要设置成虚拟环境中的python解释器,否则无法调用cv2
-D PYTHON3_EXECUTABLE=/home/rockchip/miniconda3/envs/ulai/bin/python3.9 \
-D PYTHON3_INCLUDE_DIR=/home/rockchip/miniconda3/envs/ulai/include/python3.9 \
-D PYTHON3_LIBRARY=/home/rockchip/miniconda3/envs/ulai/lib/libpython3.9.so \
-D PYTHON3_NUMPY_INCLUDE_DIRS=/home/rockchip/miniconda3/envs/ulai/lib/python3.9/site-packages/numpy/core/include \
-D PYTHON3_PACKAGES_PATH=/home/rockchip/miniconda3/envs/ulai/lib/python3.9/site-packages ..
(5)编译安装
编译过程大概需要半小时左右,如果上述过程全都配置正确,那编译过程应该没有问题,期间遇到一个问题,可参考3.4。
2.4 调用 cv2
opencv安装完成后,会生成一个 cv2.xxx.so 文件,当我们 import cv2 的时候就会调用该文件。可以先尝试在虚拟环境中执行看看
import cv2
print(cv2.getBuildInformation())
如果输出信息中的 FFMPEG 和 GStreamer 选项都为 YES,则说明我们重新编译的 opencv 已被成功调用。
如果显示为 NO,可能是系统没有找到那个 cv2.xxx.so 文件,需要创建一个链接[10](因为我编译安装后能直接使用,所以该步没有验证过)。
$ ln -s ln -s /path/to/your/.so cv2.so
其中那个.so 文件路径可以在 cmake 的输出信息中的 "packages path" 中找到,比如我的路径是:
/home/rockchip/miniconda3/envs/ulai/lib/python3.9/site-packages/cv2/python-3.9/cv2.cpython-39-aarch64-linux-gnu.so
三、问题
3.1 解决 "ffmpeg command not found"
原本的系统中已安装了 ffmpeg,但在编译 opencv 时FFMPEG选项始终为 NO,怀疑跟 ffmpeg 安装有关,所以卸载 [2]了原本的 ffmpeg 重新编译安装。但安装完后又出现了另一个问题,输入 ffmpeg -version 报错:"ffmpeg command not found",说明系统没有找到可用的 ffmpeg,需要配置一下[3]。
(1)配置路径
首先按照步骤 2.5 安装完 ffmpeg 后,找到 ffmpeg 的 lib 路径,即编译文件夹 {builddir} 的 lib 目录
将 lib 路径写入配置文件
sudo vi /etc/ld.so.conf.d/ffmpeg.conf
(2)保存后更新配置
sudo ldconfig
(3)配置环境变量
告诉系统 ffmpeg 执行文件在哪
sudo vi /etc/profile
写入内容
PATH=/path/to/your/ffmpeg/dir/bin/:$PATH
令配置生效
source /etc/profile
输入ffmpeg -version 查看是否可用
3.2 解决 "unable to locate package libjasper-dev"
在进行步骤 2.3.1 时安装报错:"unable to locate package libjasper-dev",提示找不到该库
网上普遍的解决方法是更新 apt 源,添加证书,实测下来该方法对我无效,但还是记录一下
sudo add-apt-repository 'deb http://security.ubuntu.com/ubuntu xenial-security main'
sudo apt-key adv --refresh-keys --keyserver keyserver.ubuntu.com
sudo apt update
sudo apt install libjasper1 libjasper-dev
最后找到了对本人有效的解决方法 [5]
配置完成后再次安装
同时注意原 apt 源最好换成国内源,比如阿里云,否则可能会报其它安装错误
3.3 解决 opencv 编译时 FFMPEG 选项始终为 NO
我只能说这是一个大坑,光解决这个问题就花了两天,因为本人对编译相关知识不熟悉,所以解决起来确实很费劲,找了网上N多资料大都无济于事。首先参考[8]知道了是跟 ffmpeg 路径有关,也跟着文章中说的做了但没用,最后还是跟着[1]中的方法一步步来,期间遇到了 3.1 中的问题,搞了小半天,好在最终终于解决了。
3.4 解决 "C++: fatal error: Killed signal terminated program cc1plus"
编译 opencv 过程中出现错误:"C++: fatal error: Killed signal terminated program cc1plus",导致编译中止。该问题是系统内存不足引起的,可以通过创建 swap 分区,增加虚拟内存来解决[9]。
(1)创建并激活 swap 分区
# 创建分区路径
sudo mkdir -p /var/cache/swap/
# 设置分区的大小
# bs=64M是块大小,count=64是块数量,所以swap空间大小是bs*count=4096MB=4GB
sudo dd if=/dev/zero of=/var/cache/swap/swap0 bs=64M count=64
# 设置该目录权限
sudo chmod 0600 /var/cache/swap/swap0
# 创建SWAP文件
sudo mkswap /var/cache/swap/swap0
# 激活SWAP文件
sudo swapon /var/cache/swap/swap0
# 查看 swap 信息是否正确
sudo swapon -s
第2步执行很慢,耐心等待一下,我看评论区有人说系统卡死,可能是因为步骤2没有执行完,好在我没有遇到卡死问题。
创建分区前
创建分区后
(2)删除交换分区
sudo swapoff /var/cache/swap/swap0
sudo rm /var/cache/swap/swap0
(3)释放空间
sudo swapoff -a
四、总结
有一说一这个 opencv 搞起来还真是挺费劲的,尤其本人是第一次搞这种源码编译,可谓是一步一个坑,好在最后是跌跌撞撞的走完了,其中有很多过程和 bug 没有截图保存,也可能漏掉了一些细节,但整体流程已经尽可能记录下来了,没想到一个小小的功能配置竟然搞了这么久,也算是一个边学习边积累经验的过程。背景中提到的两个目标已经完成一个了,下一篇文章会记录如何将 opencv 和GStreamer 结合达到录制视频的功能。
参考资料
[1] Ubuntu交叉编译aarch64的ffmpeg_aarch64-linux-gnu-pkg-config-优快云博客
[2] 如何彻底卸载Linux上的FFmpeg?(附详细步骤)
[3] ffmpeg安装Linux | 解决 ‘ffmpeg command not found‘ 错误-优快云博客
[4] OpenCV 编译和安装 — Firefly Wiki
[5] Unable to locate package libjasper-dev——stackoverflow
[6] jetson nano手动编译opencv-python4.5.5支持gstreamer-优快云文库
[7] 从源码安装OpenCV,使用python3生成opencv-python(cv2)的接口_python源码构建opencv后续import-优快云博客
[8] 交叉编译opencv时候ffmpeg选项始终为no的解决方法_opencv ffmpeg no-优快云博客
[9] 【问题解决】C++: fatal error: Killed signal terminated program cc1plus-优快云博客