C++——调用OpenCV和NVIDIA Video Codec SDK库实现使用GPU硬解码MP4视频文件_nvidia video codec sdk 安装
系列文章目录
参考博客
参考博客
参考博客
文章目录
- 系列文章目录
- 前言
- 一、下载安装NVIDIA Video Codec SDK
-
- 1、下载NVIDIA Video Codec SDK
- 2、安装NVIDIA Video Codec SDK
- 二、下载编译安装OpenCV
-
- 1、下载OpenCV
- 2、编译安装OpenCV
- 3、配置环境变量
- 4、报错解决
-
- 报错一:
- 三、测试代码
-
- 1、测试一
- 2、测试二
- 3、makefile文件参考
前言
NVIDIA Video Codec SDK是一个用于视频编码和解码的开发工具包,它允许开发人员利用NVIDIA GPU的硬件加速功能来处理视频流。该SDK提供了一套全面的API集,支持在Windows和Linux平台上进行硬件加速视频编码和解码。
功能和特性:
硬件加速:NVIDIA Video Codec SDK包含基于硬件的视频编码器和解码器,这些组件独立于CUDA核心,能够提供全加速的视频编码和解码功能。这不仅可以提高处理速度,还能减少CPU和计算引擎的负担,使得它们可以执行其他操作。
支持的编解码器:该SDK支持多种编解码器,包括H.264、HEVC(H.265)和AV1。其中,HEVC支持最高8K的编码,而AV1编码则提供了超高质量(UHQ)模式,进一步提升了编码质量。
性能优化:SDK中的NVENC和NVDEC引擎支持比实时更快的视频处理速度,适用于视频转码、视频数据压缩/解压缩、游戏直播、虚拟桌面、云游戏等多种应用。此外,最新的GPU架构如Blackwell进一步增强了NVENC和NVDEC的性能和质量。
NVIDIA Video Codec SDK广泛应用于多种场景,包括:
视频转码:快速转换视频格式和分辨率。
视频直播:在直播平台上实现高效的视频编码和解码。
游戏和虚拟现实:优化游戏和虚拟现实应用的视频处理。
安全视频播放:在安全监控和视频会议中提供高质量的视频处理。
通过使用NVIDIA Video Codec SDK,开发人员可以充分利用NVIDIA GPU的硬件加速功能,提高视频处理效率和质量,满足各种应用的需求。
一、下载安装NVIDIA Video Codec SDK
1、下载NVIDIA Video Codec SDK
NVIDIA Video Codec SDK下载路径
NVIDIA Video Codec SDK库的不同版本需要严格对应当前使用的NVIDIA驱动版本和CUDA版本。
如果你的NVIDIA驱动和CUDA版本很新,可以下载最新版本的NVIDIA Video Codec SDK库。
下载得到Video_Codec_SDK_xxx.zip的压缩包,解压后的目录有Read_Me.pdf文档,文档中有该版本SDK对驱动与CUDA最低版本的要求,从而检查这个NVIDIA Video Codec SDK库是否符合你的要求。
如果你的NVIDIA驱动和CUDA是旧版本,可以下载旧版的NVIDIA Video Codec SDK库。
下载得到Video_Codec_SDK_xxx.zip的压缩包,解压后的目录有Read_Me.pdf文档,文档中有该版本SDK对驱动与CUDA最低版本的要求,从而检查这个NVIDIA Video Codec SDK库是否符合你的要求。
如果驱动版本或者CUDA不满足要求,可更新版本或者下载更低版本的nvidia-video-codec-sdk。
2、安装NVIDIA Video Codec SDK
安装nvidia-video-codec-sdk,其实在GPU驱动安装过程中,已经将nvidai-video-codec-sdk的库文件进行了安装,一般安装在/usr/lib/x86_64-linux-gnu/目录下,存在libnvcuvid.so、libnvidia-encode.so等库文件。
将头文件拷贝至cuda头文件目录下:
cp Video_Codec_SDK_xxx/Interface/* /usr/local/cuda/include/
注意:上述只用了nvidia-video-codec-sdk中的头文件,而没有使用nvidia-video-codec-sdk中的libnvcuvid.so、libnvidia-encode.so库,原因是在安装显卡驱动的时候会默认安装与驱动版本兼容的libnvcuvid.so、libnvidia-encode.so,而nvidia-video-codec-sdk中的库很可能与我们安装的显卡驱动版本不一致,如果使用了nvidia-video-codec-sdk中的libnvcuvid.so、ibnvidia-encode.so编译的时候,可能不会有问题,但是运行时很可能会因为与驱动版本不兼容而报错,因此不需要将nvidia-video-codec-sdk中的libnvcuvid.so、ibnvidia-encode.so库复制到cuda库中,在配置opencv时会另外把这些库编译进去的。这个可谓是Nvidia的天坑,一定要注意。
二、下载编译安装OpenCV
1、下载OpenCV
如果是首次下载编译安装opencv库,大部分可以参考以下博客链接,但是有小部分需要修改:
下载编译安装OpenCV参考博客
注意,与以上参考博客不同的是,在cmake-gui编译阶段,需要增加一些对应的CUDA选项以正确启用CUDA支持。
如果已经有下载编译安装好的opencv库,也不需要删除重新安装,只需要重复cmake-gui编译,补上CUDA选项以正确启用CUDA支持,重新安装即可。
2、编译安装OpenCV
打开cmake-gui可视化界面。
如果之前没有安装过就根据以上参考博客完成基础配置,再增加CUDA配置即可。
如果已经安装过,那么cmake-gui应该会有你之前的配置记录,直接增加CUDA配置即可。
# 需要增加的CUDA配置 -D ENABLE_FAST_MATH=1 \\ -D CUDA_FAST_MATH=1 \\ -D WITH_CUDA=ON \\ -D WITH_CUDNN=ON \\ -D WITH_CUDEV=ON \\ -D WITH_NVCUVID=ON \\ -D CUDA_ARCH_BIN=3.0;3.5;3.7;5.0;5.2;6.0;6.1;7.0;7.5 \\
然后点击configure按键,如果没有错误那就继续,有就参考下面的报错解决。
最后点击Generate按键,写入build文件夹生成配置文件。
在build目录下打开终端,输入:
make -j4
编译成功之后,再输入:
sudo make install
3、配置环境变量
终端打开
sudo gedit ~/.bashrc
文件底部写入
export PKG_CONFIG_PATH=/usr/local/opencv/opencv.xxx/lib/pkgconfigexport LD_LIBRARY_PATH=/usr/local/opencv/opencv.xxx/lib
使配置文件生效
source ~/.bashrc
如果想使用之前的版本,在~/.bashrc中注释掉增加的内容,然后source ~/.bashrc即可。
查询OpenCV版本
pkg-config --modversion opencvpkg-config --modversion opencv4
4、报错解决
报错一:
error: (-213:The function/feature is not implemented) The called functionality is disabled for current build or platform in function \'throw_no_cuda\'
可能是OpenCV编译时未正确启用CUDA支持,或运行时环境配置不完整。需要注意cmake-gui阶段是否配置正确,比如WITH_NVCUVID=ON是否打开等。
三、测试代码
1、测试一
测试opencv GPU解码和CPU解码
#include #include #include #include #include #include int gpu_test() { cv::cuda::printCudaDeviceInfo(cv::cuda::getDevice()); int count = cv::cuda::getCudaEnabledDeviceCount(); printf(\"GPU Device Count : %d \\n\", count); const std::string filename = \"/xxx.mp4\"; cv::Ptr<cv::cudacodec::VideoReader> reader = cv::cudacodec::createVideoReader(filename); cv::cuda::GpuMat gpu_frame; int frame_id = 0; std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); while (reader->nextFrame(gpu_frame)) { frame_id = frame_id + 1; } std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now(); std::chrono::duration<double> time_useds = std::chrono::duration_cast<std::chrono::duration<double>>(end - start); double time_ms = time_useds.count() * 1000.0f; double fps = double(frame_id) / time_ms * 1000.0f; printf(\"GPU test took time: %f ms, frames: %d , FPS: %f\\n\", time_ms, frame_id, fps); reader.release(); return 0;} int cpu_test(){ const std::string filename = \"/xxx.mp4\"; cv::VideoCapture capture; capture.open(filename); if (!capture.isOpened()) { printf(\"Open video failed !!! \\n\"); return -1; } int width = (int)capture.get(cv::CAP_PROP_FRAME_WIDTH); int height = (int)capture.get(cv::CAP_PROP_FRAME_HEIGHT); printf(\"src video width: %d , %d \\n\", width, height); cv::Mat frame; int frame_id = 0; std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); while (capture.read(frame)) { frame_id = frame_id + 1; } std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now(); std::chrono::duration<double> time_useds = std::chrono::duration_cast<std::chrono::duration<double>>(end - start); double time_ms = time_useds.count() * 1000.0f; double fps = double(frame_id) / time_ms * 1000.0f; printf(\"CPU test took time: %f ms, frames: %d , FPS: %f\\n\", time_ms, frame_id, fps); capture.release(); return 0;} int main(int argc, char const *argv[]){ gpu_test(); cpu_test(); return 0;}
2、测试二
测试GPU解码与图片显示
#include #include #include #include #include int main() { // MP4文件路径 std::string video_path = \"/xxx.mp4\"; try { // 创建GPU解码器 cv::Ptr<cv::cudacodec::VideoReader> d_reader = cv::cudacodec::createVideoReader(video_path); // 初始化一个GpuMat对象用于检查视频是否可读 cv::cuda::GpuMat d_frame; if (!d_reader->nextFrame(d_frame)) { std::cerr << \"无法打开视频文件或视频文件为空: \" << video_path << std::endl; return -1; } // 获取视频信息 cv::cudacodec::FormatInfo format_info = d_reader->format(); std::cout << \"视频宽度: \" << format_info.width << std::endl; std::cout << \"视频高度: \" << format_info.height << std::endl; // 获取帧率需要使用VideoCapture cv::VideoCapture cap(video_path); double fps = cap.get(cv::CAP_PROP_FPS); std::cout << \"帧率: \" << fps << std::endl; // 获取总帧数 double total_frames = cap.get(cv::CAP_PROP_FRAME_COUNT); std::cout << \"总帧数: \" << total_frames << std::endl; cap.release(); // 创建窗口 cv::namedWindow(\"GPU解码视频\", cv::WINDOW_NORMAL); // 解码循环 int frame_count = 0; while (true) { // 读取下一帧到GPU内存 if (!d_reader->nextFrame(d_frame)) { std::cout << \"视频结束或解码完成\" << std::endl; break; } frame_count++; std::cout << \"解码帧: \" << frame_count << std::endl; // 如果需要CPU处理,将帧从GPU下载到CPU cv::Mat frame; d_frame.download(frame); // 显示帧 cv::imshow(\"GPU解码视频\", frame); // 按ESC退出 if (cv::waitKey(1) == 27) { break; } } // 关闭窗口 cv::destroyAllWindows(); } catch (const cv::Exception& e) { std::cerr << \"OpenCV异常: \" << e.what() << std::endl; return -1; } catch (const std::exception& e) { std::cerr << \"标准异常: \" << e.what() << std::endl; return -1; } catch (...) { std::cerr << \"未知异常\" << std::endl; return -1; } return 0;}
3、makefile文件参考
# 定义编译器CXX=g++# 定义编译选项CXXFLAGS=-std=c++11 -Wall -I/usr/local/opencv/opencv4.4/include/opencv4# 定义链接器选项# 添加了cudacodec、cudaarithm、cudafilters等CUDA相关库LDFLAGS=-L/usr/local/opencv/opencv4.4/lib \\ -lopencv_core -lopencv_imgproc -lopencv_imgcodecs \\ -lopencv_highgui -lopencv_videoio \\ -lopencv_cudacodec -lopencv_cudaarithm -lopencv_cudafilters \\ -lopencv_cudaimgproc -lopencv_cudawarping \\-L/usr/local/cuda/lib64 \\-lnvcuvid -lnvidia-encode -lcudart# 定义目标文件名TARGET=main# 定义源文件SRCS=$(wildcard opencv_gpu_decode.cpp)# 定义对象文件OBJS=$(SRCS:.cpp=.o)# 默认目标all: $(TARGET)# 链接对象文件生成目标文件$(TARGET): $(OBJS)$(CXX) -o $@ $^ $(LDFLAGS)# 编译源文件生成对象文件%.o: %.cpp$(CXX) $(CXXFLAGS) -c $< -o $@# 清理编译生成的文件clean:rm -f $(OBJS) $(TARGET)