opencv+nvcodec实现视频硬解码
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 系统配置
- 前言`
- 一、NVCODEC是什么?
- 二、OpenCV编译
- 1.安装Driver&CUDA
- 2.编译OpenCV
- 总结
系统配置
操作系统:Ubuntu18.04
硬件架构:x86_64
OpenCV:4.5.1
FFmpeg:4.4.2
CUDA:11.2
前言`
最近遇到一个新项目,AI推理在CUDA
上,为了方便和节省成本的考虑决定研究下NVCODEC
模块。根据NVIDIA
官网的说法显卡具有独立的编码
和解码
模块,所以理论上编码
和解码
是独立互不干涉的。以前的项目都只是把显卡当成推理工具,没有将它的编解码
功能利用起来,本身也是一种浪费。事实真的有这么丝滑吗?看到这里,如果你真的觉得黄一刀
是白叫的那就真的是too young to simple
了,事实究竟如何,请听我娓娓道来。
一、NVCODEC是什么?
NVCODEC全称是 NVIDIA VIDEO CODEC,是NVIDIA为绝大多数显卡配置的硬件编解码单元。不一定是所有显卡都有,一般来讲越新的显卡硬件编解码就越强,支持的格式也就越新。比如,拿消费端的显卡来讲,只有RTX3000
以上的显卡才支持AV1
这种新格式,像RTX2000
以下的显卡目前无缘。当然以后还有可能会出更多格式。目前我所知道的NVIDIA解码支持两种方式:
1、NVCODEC编解码
这种解码方式是我们这篇文章要讲的主角,也就是硬件编解码。这里的硬件编解码指的是利用专门运算单元
来完成编解码过程,这种专门运算单元
被设计出来就是固定只干这一种活的硬件,跟软解码最大的差别是,软编解码是利用通用运算单元
来硬算
。
2、CUDA编解码
通常情况下CUDA
是拿来做AI推理
的,但是有些特殊情况下CUDA
也是可以直接拿来编解码
的,这个时候CUDA
就是通用运算单元
了,因为它不是专门设计来做编解码
工作的,属于被迫上岗。
我们看看NVIDIA自己怎么介绍自家的东西的:
NVIDIA GPUs contain one or more hardware-based decoder and encoder(s) (separate from the CUDA cores) which provides fully-accelerated hardware-based video decoding and encoding for several popular codecs. With decoding/encoding offloaded, the graphics engine and the CPU are free for other operations.
GPU hardware accelerator engines for video decoding (referred to as NVDEC) and video encoding (referred to as NVENC) support faster than real-time video processing which makes them suitable to be used for transcoding applications, in addition to video playback.
从上图我们看出,CUDA
和编解码
是独立的硬件,所以他们是分别独立工作的,不会相互干扰。
好了,闲话已经说的差不多了,接下来我们进入正题。将opencv
和nvcodec
结合起来完成视频的解码,并将解码后的GpuMat
送入CUDA
推理。
二、OpenCV编译
1.安装Driver&CUDA
这个地方要重点讲一下,安装CUDA
的时候是可以顺手把Driver
装上的,这里我推荐用这种方式,因为这种方式安装的驱动自带NVCODEC
的库和头文件,这样的话我们就不用专门去下载NVIDIA VIDEO CODEC SDK
了。而且我发现NVIDIA VIDEO CODEC SDK
和Driver
有版本依赖,弄不好会出各种奇葩问题。所以,这里讲的所有教程都是基于同时安装CUDA
+Driver
的,至于怎么安装CUDA
和Driver
请大家自行搜索教程,由于篇幅有限这里不做详述。
驱动安装好之后需要检验是否正确,如果Driver
安装正确执行nvidia-smi
会出现Driver
的详细信息,如下图所示:
这里我们说说最关键的两个信息Driver Version:460.27.04
,这个意思是你安装的显卡驱动版本是460.27.04
;CUDA Version:11.2
,这个的意思是和Driver
配套的CUDA版本是11.2
,就算你不装CUDA
这个信息也会显示的。我建议你接受它的建议,就装那个版本的·CUDA·,除非有特殊需要,你就需要去NVIDIA
的官网查询下具体支持的CUDA
版本了。众所周知,Driver
和CUDA
版本是存在依赖关系的,乱装是要出事情的。
接下来验证CUDA
是不是装好了,命令行输入nvcc -V
会看到以下的提示信息,说明CUDA
安装成功了,
nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2020 NVIDIA Corporation
Built on Mon_Nov_30_19:08:53_PST_2020
Cuda compilation tools, release 11.2, V11.2.67
Build cuda_11.2.r11.2/compiler.29373293_0
如果报了类似于command not fount
一类的错误就说明CUDA
没有装对或者环境变量没配置对,请检查下操作是否正确。
差点忘了,CuDNN
也是需要的,千万别忘了装。CuDNN
的验证比较简单,那就是不需要验证,你从官网下载3个deb
包全部成功安装后就说明CuDNN
安装成功了,不要有所怀疑。
2.编译OpenCV
下载opencv
源代码这一步就不赘述了,这里我下载的是opencv-4.5.1.zip
或者opencv-4.5.1.tar.gz
两个包本质没有差别,只是压缩方式有差别罢了,没有影响。我们这里是需要opencv_contrib
的,所以需要下载opencv_contrib-4.5.1.zip
或opencv_contrib-4.5.1.tar.gz
,这两个压缩包也是一样的,一样用。
在这里要特别注意下opencv和opencv_contrib是有版本对应关系的,不能下错。
opencv的编译我不在这里详述了,网络上一抓一大把,这里我贴出来我的配置。
cmake -D CMAKE_BUILD_TYPE=RELEASE \-D CMAKE_INSTALL_PREFIX=/usr/local \-D ENABLE_PRECOMPILED_HEADERS=OFF \-D INSTALL_C_EXAMPLES=OFF \-D INSTALL_PYTHON_EXAMPLES=OFF \-D BUILD_opencv_python2=OFF \-D BUILD_opencv_python3=ON \-D PYTHON_DEFAULT_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") \-D PYTHON3_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") \-D PYTHON3_NUMPY_INCLUDE_DIRS=$(python3 -c "import numpy; print (numpy.get_include())") \-D PYTHON3_PACKAGES_PATH=$(python3 -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())") \-D WITH_TBB=ON \-D BUILD_TBB=ON \-D ENABLE_FAST_MATH=1 \-D CUDA_FAST_MATH=1 \-D WITH_CUBLAS=1 \-D WITH_V4L=ON \-D WITH_LIBV4L=ON \-D WITH_CUDA=ON \-D WITH_CUDNN=ON \-D WITH_GTK_2_X=ON \-D WITH_NVCUVID=ON \-D WITH_OPENGL=ON \-D WITH_FFMPEG=ON \-D CUDA_ARCH_BIN=7.5 \-D OPENCV_EXTRA_MODULES_PATH=../../opencv_contrib-4.5.1/modules .
由于这里我用到了python cv2
所以我编译了python
模块。我的需求里面有拉流的需要,编译了ffmpeg
模块。CUDA_ARCH_BIN=7.5
不同的显卡不一样,我的显卡是RTX2080Ti
,我是7.5,具体值都可以在NVIDIA官网查到。WITH_NVCUVID=ON
这个一定要加上,这个就是编解码模块。
友情提示:
有个问题需要特别注意下,这种方式OpenCV
是找不到NVCUVID
的,原因是以前NVCUVID
和CUDA
放在一起的,后来NVIDIA
将NVCUVID
独立出来了,OpenCV
的检测方式还是老的方式,所以找不到NVCUVID
,解决方法也很简单,你只需要到opencv-4.5.1/cmake
文件夹里找到OpenCVDetectCUDA.cmake
if(WITH_NVCUVID)macro(ocv_cuda_SEARCH_NVCUVID_HEADER _filename _result)# place header file under CUDA_TOOLKIT_TARGET_DIR or CUDA_TOOLKIT_ROOT_DIRfind_path(_header_result${_filename}PATHS "${CUDA_TOOLKIT_TARGET_DIR}" "${CUDA_TOOLKIT_ROOT_DIR}"ENV CUDA_PATHENV CUDA_INC_PATHPATH_SUFFIXES includeNO_DEFAULT_PATH)if("x${_header_result}" STREQUAL "x_header_result-NOTFOUND")set(${_result} 0)else()set(${_result} 1)endif()unset(_header_result CACHE)endmacro()ocv_cuda_SEARCH_NVCUVID_HEADER("nvcuvid.h" HAVE_NVCUVID_HEADER)ocv_cuda_SEARCH_NVCUVID_HEADER("dynlink_nvcuvid.h" HAVE_DYNLINK_NVCUVID_HEADER)find_cuda_helper_libs(nvcuvid)if(WIN32)find_cuda_helper_libs(nvcuvenc)endif()if(CUDA_nvcuvid_LIBRARY AND (${HAVE_NVCUVID_HEADER} OR ${HAVE_DYNLINK_NVCUVID_HEADER}))# make sure to have both header and library before enablingset(HAVE_NVCUVID 1)endif()if(CUDA_nvcuvenc_LIBRARY)set(HAVE_NVCUVENC 1)endif()endif()
注意这一句
PATHS "${CUDA_TOOLKIT_TARGET_DIR}" "${CUDA_TOOLKIT_ROOT_DIR}"
是不是感觉很熟悉,这就是CUDA
的安装目录,由于现在NVCUVID
和CUDA
不在一个目录了,所以只需要改成下面这样
PATHS "${CUDA_TOOLKIT_TARGET_DIR}" "${CUDA_TOOLKIT_ROOT_DIR}" "/usr/include"
/usr/include是NVCUVID的头文件位置,你的在哪里就写什么目录就行了,改完执行cmake配置
如果cmake
出错就缺什么安装什么就行了,这个我是亲身体验的,没有问题的。假如没有任何错误,你看到的应该是这样的:
Video I/O:DC1394: NOFFMPEG: YESavcodec: YES (58.134.100)avformat: YES (58.76.100)avutil: YES (56.70.100)swscale: YES (5.9.100)avresample: YES (4.0.0)GStreamer: YES (1.14.5)v4l/v4l2: YES (linux/videodev2.h)
NVIDIA CUDA: YES (ver 11.2, CUFFT CUBLAS NVCUVID FAST_MATH)NVIDIA GPU arch: 75NVIDIA PTX archs:cuDNN: YES (ver 8.1.0)Python 3:Interpreter: /usr/bin/python3 (ver 3.6.9)Libraries: /usr/lib/x86_64-linux-gnu/libpython3.6m.so (ver 3.6.9)numpy: /usr/local/lib/python3.6/dist-packages/numpy/core/include (ver 1.19.5)install path: /usr/lib/python3/dist-packages/cv2/python-3.6Python (for build): /usr/bin/python3
FFMPEG
一定都要是YES
,不能是NO
;NVIDIA CUDA: YES (ver 11.2, CUFFT CUBLAS NVCUVID FAST_MATH)
这一行一定要有NVCUVID
,不然就是错误了。配置好后执行make -j$(nproc)
等待编译完成执行sudo make install
就可以了。这里有个地方需要注意,一定要把旧的opencv
卸载干净,不然就会引发冲突或者未知错误。
注:ffmpeg的安装可以参考这篇文章
解决opencv源代码编译找不到ffmpeg
到这里我们的教程就算完了,如果一切顺利的话就可以使用NVCODEC
来处理视频流了,RTSP
格式的实时视频也是支持的,取出来的帧保存在GpuMat
里面,可以送进去推理了。实测CPU
解码帧率只有30
左右(和CPU
性能相关),GPU
解码帧率7600
多(和解码器
性能相关),差距还是蛮大的。下面贴出测试代码:
opencv_test.cpp
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <numeric>
#include "opencv2/opencv_modules.hpp"
#include <opencv2/core/utility.hpp>
#include <opencv2/core.hpp>
#include <opencv2/core/opengl.hpp>
#include <opencv2/cudacodec.hpp>
#include <opencv2/highgui.hpp>int main(int argc, const char* argv[])
{std::cout<<cv::getBuildInformation()<<std::endl;//将这个流改成你自己的const std::string fname = "rtsp://admin:Wat0ne123@10.0.20.249";cv::cuda::setGlDevice();//cv::cuda::setGlDevice(1);cv::Mat frame;cv::VideoCapture reader(fname);cv::cuda::GpuMat d_frame;cv::Ptr<cv::cudacodec::VideoReader> d_reader = cv::cudacodec::createVideoReader(fname);cv::TickMeter tm;std::vector<double> cpu_times;std::vector<double> gpu_times;for (int i = 0;i<100;i++){tm.reset(); tm.start();if (!reader.read(frame))break;tm.stop();cpu_times.push_back(tm.getTimeMilli());tm.reset(); tm.start();if (!d_reader->nextFrame(d_frame))break;tm.stop();gpu_times.push_back(tm.getTimeMilli());}if (!cpu_times.empty() && !gpu_times.empty()){std::cout << std::endl << "Results:" << std::endl;std::sort(cpu_times.begin(), cpu_times.end());std::sort(gpu_times.begin(), gpu_times.end());double cpu_avg = std::accumulate(cpu_times.begin(), cpu_times.end(), 0.0) / cpu_times.size();double gpu_avg = std::accumulate(gpu_times.begin(), gpu_times.end(), 0.0) / gpu_times.size();std::cout << "CPU : Avg : " << cpu_avg << " ms FPS : " << 1000.0 / cpu_avg << std::endl;std::cout << "GPU : Avg : " << gpu_avg << " ms FPS : " << 1000.0 / gpu_avg << std::endl;}return 0;
}
// #endif
CMakeLists.txt
cmake_minimum_required(VERSION 3.0.2)
project(opencv_test)
SET(CMAKE_BUILD_TYPE "Debug")
include_directories(include)
find_package( OpenCV REQUIRED )
#find_package(OpenGL REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS}#${OPENGL_INCLUDE_DIR})
add_executable( opencv_test opencv_test.cpp )
#add_executable( opencv_test gpu_mat.cpp )
target_link_libraries( opencv_test${OpenCV_LIBS}#${OPENGL_LIBRARIES})
mkdir build && cd build
cmake ..
make
./opencv_test
特别说明:
//默认执行设备,如果是单显卡请忽略,如果多显卡需要指定哪一个设备执行,默认是0cv::cuda::setGlDevice();//cv::cuda::setGlDevice(1);
总结
写到这里不得不感叹下,虽然只有区区几行命令却足足搞了一个星期。期间什么奇葩的错误都遇到了,甚至有些我认为是不必要出现的。
最典型的一个例子就是NVCODEC SDK,很多博主都说要从官网下载然后复制到系统目录,一开始我是这么做的,看起来好像全程没有遇到问题,直到最后一步跑程序的时候,终于所有的错误都来了,甚至遇到的最奇葩的问题是每次执行程序返回的错误码都是不一样的,这种BUG我也是生平仅见。最后甚至连Deiver都需要重新安装来解决。
事实真的如此吗?我真的需要每一步都照抄别人吗?实际上下载的CUDA Driver里面就有NVCODEC的SDK,不需要去官方下。只不过安装位置被从CUDA目录移除出来了,只需要修改opencv的检测方式就行了。
opencv+nvcodec实现视频硬解码相关推荐
- MediaCodec在Android视频硬解码组件的应用
https://yq.aliyun.com/articles/632892 云栖社区> 博客列表> 正文 MediaCodec在Android视频硬解码组件的应用 cheenc 2018- ...
- android硬编码封装mp4,【Android 音视频开发打怪升级:音视频硬解码篇】四、音视频解封和封装:生成一个MP4...
[声 明] 首先,这一系列文章均基于自己的理解和实践,可能有不对的地方,欢迎大家指正. 其次,这是一个入门系列,涉及的知识也仅限于够用,深入的知识网上也有许许多多的博文供大家学习了. 最后,写文章过程 ...
- 如何使用ffmpeg为Mac进行视频硬解码/硬编码(在Qt环境)
如何使用ffmpeg为Mac进行视频硬解码/硬编码(在Qt环境) 科普 前期准备 安装ffmpeg 将ffmpeg的库文件添加到Qt项目的.pro文件中 在源文件用引入头文件 第一步:先查看本机支持哪 ...
- 【Android 音视频开发-音视频硬解码篇】1.音视频基础知识
这是一个入门系列,涉及的知识也仅限于够用. 最后,写文章过程中,会借鉴参考其他人分享的文章,会在文章最后列出,感谢这些作者的分享. 本文你可以了解到 作为开篇的文章,我们先来看看音视频由什么构成的,以 ...
- 音视频硬解码篇——音视频基础知识
时至今日,短视频App可谓是如日中天,一片兴兴向荣.随着短视频的兴起,音视频开发也越来越受到重视,但是由于音视频开发涉及知识面比较广,入门门槛相对较高,让许许多多开发者望而生畏. 一.视频是什么? 不 ...
- 全志linux视频硬解码,全志V5人工智能视频处理开发板
原标题:全志V5人工智能视频处理开发板 一.应用场合 全志V5是一款高性价比的人工智能视频处理开发板. 基于全志V5 V100设计,接口丰富,运行于Linux操作系统. 内置人工智能加速引擎和全志IS ...
- 手机视频硬解码和软解码的区别
在手机评测视频播放能力的时候经常会提到"硬解码"和"软解码",但是很多人不太明白是什么意思,其实问题很简单.大家都知道PC都有CPU和GPU(显卡),在手机上也 ...
- [RK3568 Android12] MPP视频硬解码
1: RK文档中对gstreamer以及插件介绍
- ffmpeg + cuda(cuvid) 硬解码+像素格式转换(cpu主导)实战
注意: VAAPI 是inter gpu 提供的硬编解码接口 VDPAU 是 video decode present api for unix nvdec / ncvid 都是nivida产出的硬解 ...
最新文章
- 【云安全与同态加密_调研分析(3)】国内云安全组织及标准——By Me
- html点击旋转180,关于点击三角丝滑旋转180度css3 jq处理方法
- io读取一个文件再写入socket技术_JAVA中IO与NIO面试题
- 操作系统(4) -- 文件管理
- oracle startup作用,【学习笔记】Oracle打补丁后startup migrate、startup upgrade区别分析...
- ramfs, rootfs and initramfs
- list删除重复元素
- AndroidStudio_排坑指南_随时更新---Android原生开发工作笔记231
- MySQL----获取当前日期当月第一天,最后一天
- 2021年5月9日,是第108个母亲节,祝福所有的母亲节日快乐
- Struts 2中文件上传
- .NET企业级应用架构设计的技术选型
- 搞科研身体才是革命的本钱。
- JavaScript 启动性能瓶颈分析与解决方案
- 诺基亚n1平板电脑刷机教程_诺基亚n1平板电脑刷机教程_诺基亚N1 完整包线刷升级或救砖教程(不分台版;国行)......
- 怎么查oa系统的服务器地址,oa系统服务器地址如何查
- php 批量上传多个文件,小白浅析PHP中的单文件、多文件、批量上传
- HTML5CSS3知识点总结(1)
- jQuery简易的购物车
- 华丽而实用的Java图表应用