CUDA和FFMPEG硬件解码视频流

本文详细介绍了如何使用CUDA和FFMPEG进行硬件解码H264格式的RTSP视频流,通过CUDA硬件解码核心原理和框架的解析,展示了如何将CPU解码任务转移到GPU。博客中提供了基于opencv的Mat图像数据获取方法,以及CUDA解码的性能优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文主要讲述了通过FFMPEG获取H264格式的RTSP流数据(也可以获取本地视频文件),并通过CUDA进行硬件解码的过程。其他博客给出的教程要么只是给出了伪代码,非常的模糊,要么是基于D3D进行显示,使得给出的源码非常复杂,而无法看出CUDA解码的核心框架,而本文将其他非核心部分剥离出去,视频播放部分通过opencv调用cv::mat显示。

当然本博客的工作也参考了其他博客的内容,CSDN上原创的东西比较难找,大部分都是转载的,所以大家还是积极的贡献力量吧。

本文将分为以下两个部分:
1.CUDA硬件解码核心原理和框架解释;
2.解码核心功能代码的实现

CUDA硬件解码核心原理和框架

做过FFMPEG解码开发的同学肯定都对以下函数比较熟悉avcodec_decode_video2(),该函数实现可以解码从视频流中获取的数据包AVPACKET转化为AV_FRAME,AV_FRAME中包含了解码后的数据。通过CUDA硬件进行解码,最核心的思想就通过回调函数形式来调用CUDA硬件解码接口,对该函数替换,将CPU解码功能转移到GPU中去。

博客给出了一个很好的基础性框架,本文也是借鉴了该博客,该博客中修改了原始的VideoSource,将视频流的获取改为了ffmpeg,而CUDA解码部分框架如下图

1.1 VideoSource

VideoSourceData中包含了CUvideoparser和FrameQueue,通过上图可以看出,CUvideoparser是在VideoDecoder基础上实现了接口的封装,而VideoSource则是通过CUvideoparser进行解码。FrameQueue是存储硬件解码后图像的队列,注意硬件解码完的图像是存放在GPU显存里面了,而VideoDecoder中函数mapFrame,可完成从显存到内存的映射。

1.2 VideoParser

VideoParser中最重要的是三个回调函数,static int CUDAAPI HandleVideoSequence(void *pUserData, CUVIDEOFORMAT *pFormat), HandlePictureDecode(void *pUserData, CUVIDPICPARAMS *pPicParams),HandlePictureDisplay(void *pUserData, CUVIDPARSERDISPINFO *pPicParams),实现对视频格式变换、视频解码、解码后显示等处理功能。HandleVideoSequence主要负责视频格式进行校验,没有实现其他功能,解码函数HandlePictureDecode调用的就是VideoDecoder的解码函数(CUDA的接口),显示函数HandlePictureDisplay完成了解码后GPU图像进入FrameQueue。

1.3 VideoDecoder

该类是最核心的硬件解码功能类,CUVIDDECODECREATEINFO oVideoDecodeCreateInfo_是创建解码信息结构体,CUvideodecoder oDecoder_是最内核的CUDA硬件解码器,VideoParser的解码功能实际上是在CUvideodecoder解码内核上封装实现的(层层封装导致源码有点复杂,所以想看懂实现机制需要有点耐心)。

2 核心解码模块的实现

示例中NvDecodeD3D9.cpp实现了D3D环境的创建,CUDA模块的初始化,其中取视频帧图像显示的函数如下,该函数实现了从解码图像队列取出图像(实际上是显存指针),完成格式转换(NV12到ARGB),最后映射到D3D的Texture进行显示等功能,代码中我给出了关键部位的解释。

bool copyDecodedFrameToTexture(unsigned int &nRepeats, int bUseInterop, int *pbIsProgressive)
{
    CUVIDPARSERDISPINFO oDisplayInfo;

    if (g_pFrameQueue->dequeue(&oDisplayInfo))
    {
    CCtxAutoLock lck(g_CtxLock);
    // Push the current CUDA context (only if we are using CUDA decoding path)
    CUresult result = cuCtxPushCurrent(g_oContext);
    //创建解码图像的显存指针,注意存储的是NV12格式的
    CUdeviceptr  pDecodedFrame[3] = { 0, 0, 0 }; 
    //用于解码图像后进行格式转换
    CUdeviceptr  pInteropFrame[3] = { 0, 0, 0 };

    *pbIsProgressive = oDisplayInfo.progressive_frame;
    g_bIsProgressive = oDisplayInfo.progressive_frame ? true : false;

    int num_fields = 1;
    if (g_bUseVsync) {            
        num_fields = std::min(2 + oDisplayInfo.repeat_first_field, 3);            
    }
    nRepeats = num_fields;

    CUVIDPROCPARAMS oVideoProcessingParamete
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值