opencv、ffmpeg使用nvidia-video-codec-sdk编解码

opencv很早就支持cuda加速,但是一般用于图像处理模块。

在视频读(包含实时视频流)写上,opencv可以使用ffmpeg作为后端进行编解码,通常是cpu软编解。 如果ffmpeg的编译支持gpu硬编解,那么opencv的接口就直接支持硬件编解码了。

1、ffmpeg avcodec库是否支持cuda编解码

1.1、系统库直接支持

如果不想安装一堆依赖软件,可以直接下载 static 版本 下载链接 ffmpeg

linux下使用ffmpeg库,可能直接使用系统直接安装的libavcodec库(ubuntu下使用 apt install livabcodec-dev),可以直接使用 ffmpeg 工具查看)

执行 ffmpeg -codes | grep cuvid,输出有

DEV.LS h264                 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10 (decoders: h264 h264_v4l2m2m h264_qsv h264_cuvid ) (encoders: libx264 libx264rgb h264_nvenc h264_o            mx h264_qsv h264_v4l2m2m h264_vaapi nvenc nvenc_h264 )
 DEV.L. hevc                 H.265 / HEVC (High Efficiency Video Coding) (decoders: hevc hevc_qsv hevc_v4l2m2m hevc_cuvid ) (encoders: libx265 nvenc_hevc hevc_nvenc hevc            _qsv hevc_v4l2m2m hevc_vaapi )
 DEVIL. mjpeg                Motion JPEG (decoders: mjpeg mjpeg_cuvid mjpeg_qsv ) (encoders: mjpeg mjpeg_qsv mjpeg_vaapi )
 DEV.L. mpeg1video           MPEG-1 video (decoders: mpeg1video mpeg1_v4l2m2m mpeg1_cuvid )
 DEV.L. mpeg2video           MPEG-2 video (decoders: mpeg2video mpegvideo mpeg2_v4l2m2m mpeg2_qsv mpeg2_cuvid ) (encoders: mpeg2video mpeg2_qsv mpeg2_vaapi )
 DEV.L. mpeg4                MPEG-4 part 2 (decoders: mpeg4 mpeg4_v4l2m2m mpeg4_cuvid ) (encoders: mpeg4 libxvid mpeg4_omx mpeg4_v4l2m2m )
 D.V.L. vc1                  SMPTE VC-1 (decoders: vc1 vc1_qsv vc1_v4l2m2m vc1_cuvid )
 DEV.L. vp8                  On2 VP8 (decoders: vp8 vp8_v4l2m2m libvpx vp8_cuvid vp8_qsv ) (encoders: libvpx vp8_v4l2m2m vp8_vaapi )
 DEV.L. vp9                  Google VP9 (decoders: vp9 vp9_v4l2m2m libvpx-vp9 vp9_cuvid vp9_qsv ) (encoders: libvpx-vp9 vp9_vaapi vp9_qsv )

可以看到decoder支持解码的编码格式很多;同时,encoder下有nvenc同样支持很多编码格式。

此时linux下系统自带的avcodec是支持cuda编解码加速的,使用是可以直接指定使用硬件加速了,例如

// AVCodec* codec = avcodec_find_encoder(AV_CODEC_ID_H264);
AVCodec* codec = avcodec_find_encoder_by_name("h264_nvenc");

windows下的支持继续看下一节。

1.2、系统库不支持

当执行 ffmpeg -codes | grep cuvid,说明系统自带的ffmpeg库不支持硬件加速。就需要自己从源码编译了。

windows下是是否支持硬件加速, win和linux使用源码编译支持硬件加速参看博客 【ffmpeg学习 源代码编译、英伟达硬件加速】 ,也可参考官网说明 【Using FFmpeg with NVIDIA GPU Hardware Acceleration】

2、直接opencv编译源码支持硬件加速

这里主要针对opencv在cpu版本下 cv::VideoCapture、cv::VideoWriter,使用cuda加速的版本cv::cudacodec::VideoReadercv::cudacodec::VideoWriter

2.1、编译支持

opencv编译默认开启了 WITH_NVCUVID,但是编译还需要依赖nvidia针对视频编解码提供的 NVIDIA VIDEO CODEC SDK 库,该包含两个硬件加速接口:

  • 用于视频编码加速的 NVENCODE API
  • 用于视频解码加速的 NVDECODE API(旧称 NVCUVID API)

对不同视频编码格式的加速支持也能在该网页上查看。最新版为Video_Codec_SDK_12.0.16,基本环境要求

  • Windows: Driver version 522.25 or higher
  • Linux: Driver version 520.56.06 or higher
  • CUDA 11.0 or higher Toolkit

系统默认安装的ffmpeg4.4、以及cuda强制使用 Video Codec SDK 8.1及以上版本, 因此如果使用新版本cuad或者 系统直接安装ffmpeg4.4以上,是可以直接使用该加速库的。

linux编译opencv要想支持使用cv::cudacodec::VideoReadercv::cudacodec::VideoWriter,必须先下载 Video Codec SDK,之后将解压后的头文件目\Interface的内内容全部复制到cuda的include目录。

之后opencv编译时,必须出现如下 NVCUVID 内容:

  NVIDIA CUDA:                   YES (ver 11.2, CUFFT CUBLAS NVCUVID FAST_MATH)
    NVIDIA GPU arch:             52 61 70 75 86
    NVIDIA PTX archs:

  cuDNN:                         YES (ver 8.1.1)

2.2、测试

这里以读取视频即使用解码功能为例,直接给出源代码,

#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 = "rtmp://192.168.3.100:1935/live/xcp1";
    /// 没有使用opengl编译需要关闭
    //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;
}

cmake文件内容

cmake_minimum_required(VERSION 3.18)
project(test)
set(CMAKE_BUILD_TYPE "Release")

# opencv
set(OpenCV_DIR "/softwares/opencv/opencv-4.6.0/install/lib/cmake/opencv4/")
find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})

message(STATUS "Found OpenCV version ${OpenCV_VERSION}")

link_libraries( ${OpenCV_LIBS} )
add_executable( ${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME}  
  ${OpenCV_LIBS}  
)

测试时的软硬件环境如下:cpu 11700, rtx 3080,cuda 11.2,cudnn 8.1.1。

2.2.1、测试1280x720, 3964 kb/s, 29.97 fps

Results:
CPU : Avg : 0.728801 ms FPS : 1372.12
GPU : Avg : 0.506997 ms FPS : 1972.4

2.2.2、测试2048x1080, q=2-31, 2785 kb/s, 25 fps

Results:
CPU : Avg : 2.83558 ms FPS : 352.662
GPU : Avg : 0.0964484 ms FPS : 10368.2

两个测试对比,在720p时,区别不明显。当视频分辨率提高到2k时,cpu解码效率明显下降,并且gpu效率是cpu的30倍,差异明显。

这里比较中两个视频后者相对,分辨率提高,但是码率下降了。 cpu从 1372帧骤降到253帧,很正常;但是 gpu反而从1972提高到了10368,简单看有点反常理,应该同时相对降低才对。可能是由于gpu吞吐提升,提高了使用效率。

另外,实际使用是,gpu方式使用cv::cuda::GpuMat,如果腾挪到cv::Mat上可能还存在一点耗时,需要根据事情情况选择使用。 另外,图像处理也cuda加速的,例如模块opencv_cudaimgproc、opencv_cudafeatures2d等。

猜你喜欢

转载自blog.csdn.net/wanggao_1990/article/details/130420462