操作系统:Ubuntu18.04
硬件架构:X86_64
OpenCV版本:4.5.1或3.4.16
最近在研究OpenCV结合CUVIDEC解码视频流,就使用OpenCV源代码编译。结果发现无论如何都找不到ffmpeg
。经过一系列研究终于找到了原因,记录如下。
OpenCV4和OpenCV3编译当选择WITH_FFMPEG
选项的时候不能找到ffmpeg
模块。
OpenCV-3.4.16和OpenCV-4.5.1两个版本略有差别,这个问题的答案我们可以在OpenCV
的cmake
文件里面找到答案。
先来看看opencv-3.4.16
的cmake
文件,在opencv-3.4.16
的cmake
文件夹下面找到OpenCVFindLibsVideo.cmake
# --- FFMPEG ---
ocv_clear_vars(HAVE_FFMPEG)
if(WITH_FFMPEG) # try FFmpeg autodetection
if(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)
else()
message(STATUS "Can't find FFmpeg via find_package(${OPENCV_FFMPEG_USE_FIND_PACKAGE})")
endif()
elseif(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)
set(HAVE_FFMPEG TRUE)
set(HAVE_FFMPEG_WRAPPER 1)
include("${FFMPEG_CMAKE_SCRIPT}")
endif()
elseif(PKG_CONFIG_FOUND)
ocv_check_modules(FFMPEG libavcodec libavformat libavutil libswscale)
ocv_check_modules(FFMPEG_libavresample libavresample)
if(FFMPEG_libavresample_FOUND)
ocv_append_build_options(FFMPEG FFMPEG_libavresample)
endif()
else()
message(STATUS "Can't find ffmpeg - 'pkg-config' utility is missing")
endif()
endif()
if(HAVE_FFMPEG
AND NOT HAVE_FFMPEG_WRAPPER
)
try_compile(__VALID_FFMPEG
"${OpenCV_BINARY_DIR}"
"${OpenCV_SOURCE_DIR}/cmake/checks/ffmpeg_test.cpp"
CMAKE_FLAGS "-DINCLUDE_DIRECTORIES:STRING=${FFMPEG_INCLUDE_DIRS}"
"-DLINK_DIRECTORIES:STRING=${FFMPEG_LIBRARY_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)
else()
ocv_append_build_options(VIDEOIO FFMPEG)
endif()
endif()
注意看find_package(${OPENCV_FFMPEG_USE_FIND_PACKAGE})
这句,这个是核心,它是通过pkg-config来查找指定的包的,可以把它理解成一个linux
包管理器。我们这里不去过多探究它的原理,感兴趣的小伙伴可以私下里拓展下。
这个时候我们打开/usr/lib/x86_64-linux-gnu/pkgconfig
这个文件夹下面,这个就是pkg-config
的文件夹,里面有libavcodec.pc
、libavformat.pc
、libavutil.pc
等等.pc
文件,所有借助pkg-config
查询的包信息都在这里面。
接下来我们看看opencv-4.5.1
的cmake
文件,和opencv-3.4.16
不同的是opencv-4.5.1
的cmake
不在OpenCVFindLibsVideo.cmake
这个文件里面了,移到了别的地方。还是老样子先去OpenCV
官网下载源代码并解压,接着打开opencv-4.5.1/modules/videoio/cmake/detect_ffmpeg.cmake
,你会看到以下内容:
# --- FFMPEG ---
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()
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(FFMPEG_libavcodec_VERSION ${FFMPEG_libavcodec_VERSION} PARENT_SCOPE) # info
set(FFMPEG_libavformat_VERSION ${FFMPEG_libavformat_VERSION} PARENT_SCOPE) # info
set(FFMPEG_libavutil_VERSION ${FFMPEG_libavutil_VERSION} PARENT_SCOPE) # info
set(FFMPEG_libswscale_VERSION ${FFMPEG_libswscale_VERSION} PARENT_SCOPE) # info
set(FFMPEG_libavresample_VERSION ${FFMPEG_libavresample_VERSION} PARENT_SCOPE) # info
set(HAVE_FFMPEG TRUE)
set(HAVE_FFMPEG_WRAPPER TRUE)
endif()
endif()
set(_required_ffmpeg_libraries libavcodec libavformat libavutil libswscale)
set(_used_ffmpeg_libraries ${_required_ffmpeg_libraries})
if(NOT HAVE_FFMPEG AND PKG_CONFIG_FOUND)
ocv_check_modules(FFMPEG libavcodec libavformat libavutil libswscale)
if(FFMPEG_FOUND)
ocv_check_modules(FFMPEG_libavresample libavresample) # optional
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()
#=================================
# Versions check.
if(HAVE_FFMPEG AND NOT HAVE_FFMPEG_WRAPPER)
set(_min_libavcodec_version 54.35.0)
set(_min_libavformat_version 54.20.4)
set(_min_libavutil_version 52.3.0)
set(_min_libswscale_version 2.1.1)
set(_min_libavresample_version 1.0.1)
foreach(ffmpeg_lib ${_used_ffmpeg_libraries})
if(FFMPEG_${ffmpeg_lib}_VERSION VERSION_LESS _min_${ffmpeg_lib}_version)
message(STATUS "FFMPEG is disabled. Can't find suitable ${ffmpeg_lib} library"
" (minimal ${_min_${ffmpeg_lib}_version}, found ${FFMPEG_${ffmpeg_lib}_VERSION}).")
set(HAVE_FFMPEG FALSE)
endif()
endforeach()
if(NOT HAVE_FFMPEG)
message(STATUS "FFMPEG libraries version check failed "
"(minimal libav release 9.20, minimal FFMPEG release 1.1.16).")
endif()
unset(_min_libavcodec_version)
unset(_min_libavformat_version)
unset(_min_libavutil_version)
unset(_min_libswscale_version)
unset(_min_libavresample_version)
endif()
#==================================
if(HAVE_FFMPEG AND NOT HAVE_FFMPEG_WRAPPER AND NOT OPENCV_FFMPEG_SKIP_BUILD_CHECK)
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()
#==================================
unset(_required_ffmpeg_libraries)
unset(_used_ffmpeg_libraries)
if(HAVE_FFMPEG_WRAPPER)
ocv_add_external_target(ffmpeg "" "" "HAVE_FFMPEG_WRAPPER")
elseif(HAVE_FFMPEG)
ocv_add_external_target(ffmpeg "${FFMPEG_INCLUDE_DIRS}" "${FFMPEG_LIBRARIES}" "HAVE_FFMPEG")
endif()
set(HAVE_FFMPEG ${HAVE_FFMPEG} PARENT_SCOPE)
我们不去逐行分析这个文件,我们只分析重点。还是那句话find_package(${OPENCV_FFMPEG_USE_FIND_PACKAGE})
,opencv-4.5.1
这个版本还是通过pkg-config
来查找计算机上的ffmpeg
的,包括ffmpeg依赖的库,所以问题就找到了。
这里直接说我自己的解决方案,解决方案有可能不止一个。
1、OpenCV-3.4.16
照理说3.4.2版本或者其他版本也是通用的,我没有测试所有的版本。如果你的电脑可以联网可以通过apt
安装ffmpeg
方法是:
sudo apt update
sudo apt install -y ffmpeg
只安装ffmpeg
还是不够的,我们还需要安装libavcodec-dev
、libavformat-dev
、libavutil-dev
、libavfilter-dev
、 libavresample-dev
、 libswresample-dev
、 libswscale-dev
开发环境,注意一定要是开发环境,而不是运行库,运行库是不带头文件
的,不能用于编译源代码。
这个时候我们去/usr/include
找到刚刚安装的这些运行库的头文件,在/usr/include
里面创建一个文件夹ffmpeg
,将刚才的诸如libavcodec
这些文件夹下面的所有头文件都复制到/usr/include/ffmpeg
文件夹下面,注意不要复制整个文件夹,要复制文件夹里面的所有头文件。完了之后再编译OpenCV
并加上WITH_FFMPEG
如果出现下图所示,说明对了。
注:必须是YES,不能是NO,如果是NO就检查下操作是不是有误。这个版本是ffmpeg-4.4.2的版本,Ubuntu18.04原生安装的是ffmpeg-3.4.8或ffmpeg-3.4.6版本,对于我来说用起来没影响。
2、OpenCV-4.5.1
这个版本的OpenCV
编译比较麻烦点,这里我没有用apt
去安装ffmpeg
而是选择用ffmpeg-4.4.2
的源代码编译ffmpeg
。关于ffmpeg
的源代码编译这里我不详述了,大家可以自行去查询下资料。我这里只编译我用到的功能。
首先到ffmpeg
的官网下载源代码,这里我选的是ffmpeg-4.4.2
,其他的版本我没有测试过,大家可以自行测试,理论上4.x.x
的版本应该都是可以的。
./configure --enable-shared --enable-avresample --prefix=/usr/
make -j$(nproc)
sudo make install
注:--prefix=/usr/ 这句话一定要这么写,不能安装到/usr/local目录下,OpenCV可能会找不到ffmpeg
最后编译OpenCV
并加上WITH_FFMPEG
如果出现下图所示,说明对了。
大体的方法就是上面那些了,有疑问的童鞋可以在下面提问。