1. 为什么要在 Ubuntu 上源码编译 OpenCV C 并打通 FFmpeg很多做计算机视觉的朋友第一次在 Ubuntu 上装 OpenCV 都是直接一句sudo apt install libopencv-dev完事。装完写个读图片的 Demo 跑得挺欢结果一碰视频就傻眼了cv::VideoCapture打开本地 mp4 返回 false或者能打开但读出来的帧全是空的再或者编译时报一堆undefined reference to avcodec_...的链接错误。这个坑我踩过不止一次根子就在于系统源里的 OpenCV 包出于各种原因往往没有把 FFmpeg 后端完整编进去或者编进去了但版本对不上。所以这篇东西要解决的核心问题就一个在 Ubuntu 上从源码编译一套带 FFmpeg 支持的 OpenCV C 库让VideoCapture、VideoWriter能正常读写各种视频格式。这件事看起来只是几条 cmake 命令但里面涉及依赖顺序、编解码器开关、pkg-config 路径、运行时库查找等一堆细节任何一个环节没弄对最后都会以“编译过了但跑不起来”的形式还给你。适合谁来参考如果你正在做视频分析、目标检测、行为识别这类需要逐帧处理视频的项目或者你要把算法部署到一台干净的 Ubuntu 机器上那这套流程基本是绕不开的。哪怕你只是想让 OpenCV 能读个摄像头、存个 mp4把 FFmpeg 编进去也是最稳妥的做法。下面我按“整体思路 → 依赖细节 → 实操编译 → 排错”这个顺序把整套流程掰开揉碎讲一遍参数和踩坑点都会给到你可以直接照着抄。2. 整体方案设计与选型思路拆解2.1 为什么不用 apt 装现成的非要源码编译先把这个问题说清楚不然很多人会觉得我多此一举。Ubuntu 官方源里的libopencv-dev它的编译配置是发行版维护者定的为了兼容性和体积通常会砍掉一部分不常用的编解码支持而且它链接的 FFmpeg 版本是系统里那一套。问题在于系统 FFmpeg 版本可能偏老某些新编码格式比如较新的 H.265 封装、AV1支持不全你项目里如果自己又装了一套新版 FFmpeg就会出现两套库打架运行时libavcodec.so版本冲突程序直接段错误最关键的是你没法控制 OpenCV 到底开了哪些后端出了问题只能猜。源码编译的好处是一切可控FFmpeg 用哪个版本、OpenCV 开哪些模块、装到哪个路径全在你手里。代价就是要多花点时间配依赖但这是一次性的投入后面省心。2.2 依赖链条的先后顺序不能乱这套东西的依赖关系是一条链顺序错了就白干FFmpeg提供编解码能力→ OpenCV调用 FFmpeg 的 API 读写视频→ 你的 C 程序调用 OpenCV所以正确的顺序是先编 FFmpeg再编 OpenCV最后编你的程序。很多人图省事用 apt 装 FFmpeg 的开发包libavcodec-dev那一套也能用但版本受限于发行版。我的建议是如果你对视频格式要求不高apt 装 FFmpeg 开发包完全够用省时间如果你要处理比较新的编码或者需要特定版本那就源码编 FFmpeg。下面两条路我都会讲到。2.3 OpenCV 版本怎么选截至我写这篇的时候OpenCV 4.x 是主流4.5 以后的版本对 FFmpeg 的支持都比较成熟。选版本有个原则别追最新选一个稳定的小版本。比如 4.8.0、4.9.0 这种社区验证充分网上能查到的坑也多。太新的版本可能刚合并了某些改动编译时反而容易遇到奇怪的报错。我下面以 4.8.0 为例其他 4.x 版本流程基本一致。3. 编译前的依赖准备与关键细节3.1 基础编译工具链Ubuntu 上编译任何 C 大项目这几样是标配先装上sudo apt update sudo apt install -y build-essential cmake git pkg-configbuild-essential里包含了 gcc、g、makecmake是 OpenCV 的构建系统pkg-config后面用来帮 OpenCV 找到 FFmpeg 的库路径这个很关键少了它 cmake 可能找不到 FFmpeg。3.2 图像与视频相关的依赖库OpenCV 不只是视频图像编解码也要依赖一堆库。这些库不装cmake 配置阶段会报一堆NO虽然不一定致命但功能会残缺sudo apt install -y libjpeg-dev libpng-dev libtiff-dev sudo apt install -y libavcodec-dev libavformat-dev libswscale-dev libavutil-dev sudo apt install -y libgtk-3-dev这里重点说下第二行的四个包它们就是 FFmpeg 的开发库libavcodec负责编解码libavformat负责封装格式mp4、avi 这些容器libswscale负责图像缩放和像素格式转换libavutil是公共工具库。OpenCV 的 FFmpeg 后端就是靠这四个库工作的缺一个都编不出视频支持。libgtk-3-dev是给imshow这类 GUI 函数用的如果你只在服务器上跑无界面程序可以不装但装了没坏处。3.3 可选但强烈建议装的加速库如果你要做实时视频处理性能很重要建议把下面这些也装上sudo apt install -y libtbb-dev libeigen3-devlibtbb-dev是 Intel 的线程构建块OpenCV 的很多并行算法会用它装了之后多核利用率明显提升。libeigen3-dev提供矩阵运算支持做标定、几何变换时会用到。3.4 一个容易忽略的坑pkg-config 路径apt 装的 FFmpeg 开发包.pc文件一般在/usr/lib/x86_64-linux-gnu/pkgconfig/下。你可以验证一下pkg-config --modversion libavcodec如果输出了版本号说明路径没问题。如果报Package libavcodec was not found那要么是没装开发包要么是PKG_CONFIG_PATH没包含对应目录。这时候可以手动加export PKG_CONFIG_PATH/usr/lib/x86_64-linux-gnu/pkgconfig:$PKG_CONFIG_PATH提示这个环境变量只在当前终端有效如果你后面要重新开终端编译记得重新 export或者写进~/.bashrc。4. 实操编译 OpenCV 的完整流程4.1 下载源码找个空间够的目录OpenCV 源码加上 build 目录几个 G 是要的cd ~ git clone https://github.com/opencv/opencv.git cd opencv git checkout 4.8.0用 git 的好处是可以随时切版本。如果你网络不方便也可以下 release 的 zip 包效果一样。切到 4.8.0 这个 tag 之后代码就是稳定的。4.2 创建 build 目录并配置 cmake这一步是整个流程的核心参数配错了后面全白搭。先建目录mkdir build cd build然后跑 cmake我把关键参数都列出来并解释cmake -D CMAKE_BUILD_TYPERELEASE \ -D CMAKE_INSTALL_PREFIX/usr/local \ -D WITH_FFMPEGON \ -D WITH_GSTREAMEROFF \ -D WITH_V4LON \ -D WITH_GTKON \ -D BUILD_EXAMPLESOFF \ -D BUILD_opencv_python3OFF \ -D BUILD_TESTSOFF \ -D BUILD_PERF_TESTSOFF \ ..逐个说CMAKE_BUILD_TYPERELEASE开优化编出来的库跑得快。调试阶段可以换 Debug但会慢很多。CMAKE_INSTALL_PREFIX/usr/local装到系统路径这样编译你的程序时不用额外指定 include 和 lib 路径。WITH_FFMPEGON这是最关键的一行显式打开 FFmpeg 支持。虽然默认一般是 ON但写出来心里踏实。WITH_GSTREAMEROFFGStreamer 是另一套多媒体框架和 FFmpeg 功能有重叠。如果你不用它关掉能减少依赖冲突的可能。WITH_V4LONVideo4Linux读摄像头用的做实时采集必须开。BUILD_opencv_python3OFF如果你只用 C关掉 Python 绑定能省不少编译时间。BUILD_TESTS、BUILD_PERF_TESTS测试代码编译它们纯属浪费时间关掉。4.3 检查 cmake 输出里的 FFmpeg 状态cmake 跑完会打印一大段配置摘要一定要仔细看这一段-- Video I/O: -- DC1394: NO -- FFMPEG: YES -- avcodec: YES (58.134.100) -- avformat: YES (58.76.100) -- avutil: YES (56.70.100) -- swscale: YES (5.9.100) -- avresample: NO只要看到FFMPEG: YES以及下面 avcodec、avformat、avutil、swscale 都是 YES就说明配置成功了。如果 FFMPEG 是 NO别急着往下编先回去检查 3.2 节的开发包装没装、pkg-config 能不能找到。这一步没确认就编译编完发现没视频支持只能重来。4.4 开始编译确认配置无误后用多核编译加速。先看下你机器几个核nproc假设是 8 核就make -j8编译时间取决于机器8 核大概十几到二十分钟。这里有个经验-j后面的数字别超过物理核心数太多否则内存可能爆掉尤其是虚拟机。如果编译中途报virtual memory exhausted就把-j8降到-j4甚至-j2重来。4.5 安装并刷新库缓存编译完成后sudo make install sudo ldconfigldconfig这步很多人会忘它的作用是刷新系统的动态链接库缓存让新装的/usr/local/lib下的库能被找到。不跑这一步你的程序运行时可能报error while loading shared libraries: libopencv_core.so.4.8。5. 验证 FFmpeg 支持是否真的生效5.1 写一个最小验证程序装完了别急着庆祝先写个程序验证视频读写。新建test_video.cpp#include opencv2/opencv.hpp #include iostream int main() { std::cout cv::getBuildInformation() std::endl; cv::VideoCapture cap(test.mp4); if (!cap.isOpened()) { std::cerr 无法打开视频文件 std::endl; return -1; } cv::Mat frame; int count 0; while (cap.read(frame)) { count; } std::cout 共读取 count 帧 std::endl; cap.release(); return 0; }编译g test_video.cpp -o test_video pkg-config --cflags --libs opencv4注意pkg-config --cflags --libs opencv4这个写法它自动帮你把 include 路径和链接库都带上了比手写-I和-l省事得多。前提是 OpenCV 安装时生成了opencv4.pc文件源码编译默认会生成。5.2 看 getBuildInformation 的输出运行程序第一段会打印 OpenCV 的构建信息。在里面搜FFMPEG确认是 YES。同时看Video I/O那一节确认后端列表里有 FFMPEG。这是最权威的验证方式比任何猜测都靠谱。5.3 用 VideoWriter 反向验证编码光能读还不够写视频也要验证。加一段cv::VideoWriter writer(out.mp4, cv::VideoWriter::fourcc(m, p, 4, v), 25.0, cv::Size(640, 480)); if (!writer.isOpened()) { std::cerr VideoWriter 打开失败 std::endl; return -1; } for (int i 0; i 100; i) { cv::Mat img(480, 640, CV_8UC3, cv::Scalar(i % 255, 100, 200)); writer.write(img); } writer.release();fourcc(m,p,4,v)是 MPEG-4 编码兼容性好。如果isOpened()返回 true 且能生成文件说明编码链路也是通的。如果这里失败多半是 FFmpeg 的编码器没编进去回去检查 cmake 配置。6. 常见问题与排查技巧实录6.1 编译期问题速查现象可能原因解决办法cmake 报 FFMPEG: NO缺开发包或 pkg-config 找不到装 libav*-dev检查 PKG_CONFIG_PATHmake 报内存不足并行度太高降低 -j 数值链接报 undefined reference to avcodec链接顺序或库缺失确认 WITH_FFMPEGON重装 FFmpeg 开发包找不到 opencv2/opencv.hppinclude 路径没配用 pkg-config --cflags opencv46.2 运行期问题排查问题一程序启动就报找不到 libopencv_core.so.4.8这是ldconfig没跑或者/usr/local/lib不在库搜索路径里。先跑sudo ldconfig还不行就检查/etc/ld.so.conf.d/下有没有包含/usr/local/lib的配置文件。没有就新建一个echo /usr/local/lib | sudo tee /etc/ld.so.conf.d/opencv.conf sudo ldconfig问题二能打开视频但读出来全是空帧这种情况通常是像素格式转换出了问题根源在libswscale版本不匹配。如果你系统里同时存在 apt 装的 FFmpeg 和源码编的 FFmpeg运行时可能加载了错误的那套。用ldd看你的程序实际链接了哪个ldd test_video | grep avcodec如果指向的不是你期望的路径就要调整LD_LIBRARY_PATH或者干脆卸掉冲突的那套。问题三VideoWriter 能打开但写出的文件是 0 字节多半是编码器没找到。FFmpeg 的某些编码器比如 H.264 的libx264需要额外的库支持。如果你需要写 H.264得确保系统装了libx264-dev并且 FFmpeg 编译时开了--enable-libx264。用 apt 的 FFmpeg 开发包一般没这个问题源码编的话要留意。6.3 几条独家避坑经验第一编译前先make clean。如果你之前编过一次失败了改配置重编之前一定要清干净否则残留的缓存会让新配置不生效表现为“明明改了参数结果还是老样子”。第二别在编译 OpenCV 的同时跑其他吃内存的大任务。OpenCV 编译峰值内存占用很高尤其是开了-j多核内存不够直接 OOM前面的编译全废。第三装完之后保留 build 目录。万一后面要加模块或者改配置不用重新下载源码直接在原 build 目录改 cmake 参数重编就行省很多时间。第四版本要锁死。OpenCV 和 FFmpeg 的版本组合是有兼容性讲究的今天编好了能用明天手贱升级了系统 FFmpeg可能就跑不起来了。生产环境建议把版本号记下来甚至把编好的库打包备份。7. 把库集成到你自己的项目里7.1 CMakeLists.txt 的标准写法实际项目里很少用 g 手敲命令都是用 CMake 管理。一个能正确找到 OpenCV 的CMakeLists.txt长这样cmake_minimum_required(VERSION 3.10) project(video_demo) set(CMAKE_CXX_STANDARD 14) find_package(OpenCV REQUIRED) include_directories(${OpenCV_INCLUDE_DIRS}) add_executable(video_demo main.cpp) target_link_libraries(video_demo ${OpenCV_LIBS})find_package(OpenCV REQUIRED)会自动去找 OpenCV 的配置文件找到后${OpenCV_LIBS}里就包含了所有需要的链接库包括 FFmpeg 相关的间接依赖。这套写法比手写 pkg-config 更规范也更容易跨平台。7.2 确认 find_package 找到的是你编的那套系统里如果有多套 OpenCVfind_package可能找到错的那个。可以在 CMakeLists 里打印一下message(STATUS OpenCV dir: ${OpenCV_DIR}) message(STATUS OpenCV version: ${OpenCV_VERSION})如果路径不是/usr/local就要在 cmake 命令里指定-DOpenCV_DIR/usr/local/lib/cmake/opencv4强制它用你编的那套。7.3 部署到其他机器时的注意事项你编好的程序要拿到另一台机器跑那台机器上也得有对应的 OpenCV 和 FFmpeg 运行库。最省事的办法是把/usr/local/lib下的 OpenCV 库和系统里的 FFmpeg 运行库一起打包用LD_LIBRARY_PATH指定加载路径。或者干脆静态链接但 OpenCV 静态链接体积巨大一般不建议。我个人在实际操作中的体会是这套流程最耗时间的不是编译本身而是排查“为什么 FFmpeg 没生效”。所以强烈建议在 cmake 配置阶段就把 FFMPEG 的状态确认清楚别等到编完跑程序才发现问题。另外如果你只是做原型验证apt 装 FFmpeg 开发包 源码编 OpenCV 这个组合是最省心的既保证了 OpenCV 的可控性又不用折腾 FFmpeg 本身的编译。等真正遇到 apt 版本满足不了的编码需求再考虑源码编 FFmpeg 也不迟。最后分享一个小技巧把整个编译过程写成一个 shell 脚本参数都固化进去下次换机器直接跑脚本比手动敲命令靠谱得多也方便团队里其他人复现。
阅读完成 · 觉得有帮助?