本章是在nvidia_video_sdk_6.0.1的基础之上做封装的,我研究了其中的NvDecodeGL工程;由于自己工作会遇到显示多路rtsp视频流及解码的情况,所以进行了研究。

网上有其它的介绍ffmpeg和nvdia结合解码视频的文章,这里我将其实现了,并将官方的代码进行了精简和封装,封装后使用方法相当简单,示例如下

#include "NvDecode.h"
#include "opencv.hpp"
#include <iostream>int main()
{NvDecode decod;//rtsp://admin:123ABCabc@192.168.2.236:554/Streaming/Channels/102?transportmode=unicast&profile=Profile_1//rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.movdecod.start(std::string("rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov"));unsigned char *rgbaPtr = nullptr;int width = 0, height = 0;unsigned long long timestamp = 0;while (!decod.m_pFrameQueue->isEndOfDecode()) //到了视频末尾退出循环{if (decod.deQueueFrame(&rgbaPtr, &width, &height, timestamp)) {cv::Mat frame(height, width, CV_8UC4, rgbaPtr);cv::imshow("video", frame);cv::waitKey(30);}else {cv::waitKey(20); //如果队列里面没有视频帧就等待一下continue;}}return 0;
}

我是使用的opencv进行显示的,出来的结果已经是rgba了,具体是bgra还是rgba还有点懵,opencv能播放,应该是bgra吧。但是官方的cuda函数写的是rgba。先不管这个了。

其中进行了大量的调试,官方的工程功能众多,主要说下流程吧

一、初始化设备、获取cuda的格式转换函数

 __cu(cuInit(0, __CUDA_API_VERSION, hHandleDriver));__cu(cuvidInit(0));__cu(cuDeviceGet(&device, 0)); //使用0号显卡__cu(cuCtxCreate(&cudaCtx, CU_CTX_SCHED_AUTO, device));__cu(cuvidCtxLockCreate(&ctxLock, cudaCtx));m_pFrameQueue = new CUVIDFrameQueue(ctxLock);CUresult oResult;//..\\3rd\\common\\kernels\\ptx\\NV12ToARGB_drvapi_Win32.ptxoResult = cuModuleLoad(&module, "..\\3rd\\common\\kernels\\ptx\\NV12ToARGB_drvapi_Win32.ptx");if (oResult != CUDA_SUCCESS) {std::cout << "load module failed error: " << oResult << std::endl;exit(-1);}oResult = cuModuleGetFunction(&g_kernelNV12toARGB, module, "NV12ToARGB_drvapi");if (oResult != CUDA_SUCCESS) {std::cout << "get cuda func NV12ToARGB_drvapi failed" << std::endl;exit(-1);}

二、ffmpeg的初始化,并将视频信息和nvdia创建视频信息的数据进行匹配,这个网上多,主不说了

三、在解码线程中循环的解码,将读取到的每一帧构造一个CUVIDSOURCEDATAPACKET并调用cuvidParseVideoData函数进行解码。

四、将解码的结果使用cuda函数转换为rgba格式。我使用的是官方的FrameQueue做为解码和显示的桥梁,当显示线程暂停到某一帧时,队列为满,解码线程将会等待。官方代码如下

bool
FrameQueue::waitUntilFrameAvailable(int nPictureIndex)
{while (isInUse(nPictureIndex)){Sleep(1);   // Decoder is getting too far ahead from displayif (isEndOfDecode())return false;}return true;
}

当显示线程取到一帧时,我是这样做转换的,此外研究了很久,删减了官方的大量封装,我只是要将图片转换为rgba格式,在GPU中完成,可以更大减少CPU的占用。

bool NvDecode::deQueueFrame(unsigned char ** ptr, int *width, int *height, unsigned long long *timestamp)
{CUVIDPARSERDISPINFO pInfo;if (!(m_pFrameQueue->isEndOfDecode() && m_pFrameQueue->isEmpty())) {if (m_pFrameQueue->dequeue(&pInfo)) {CCtxAutoLock lck(ctxLock);cuCtxPushCurrent(cudaCtx);CUdeviceptr pDecodedFrame[2] = { 0,0 };CUdeviceptr pInteropFrame[2] = { 0,0 };int distinct_fields = 1;if (!pInfo.progressive_frame && pInfo.repeat_first_field <= 1) {distinct_fields = 2;}for (int active_field = 0; active_field < distinct_fields; active_field++) {CUVIDPROCPARAMS oVPP = { 0 };oVPP.progressive_frame = pInfo.progressive_frame;oVPP.top_field_first = pInfo.top_field_first;oVPP.unpaired_field = (distinct_fields == 1);oVPP.second_field = active_field;unsigned int nDecodedPitch = 0; //将解码后的原始帧映射出来,nDecodePitch表示原来帧空间的每行所点字节,不一定是视频宽度,不知道的要去了解下cuda矩阵内存分配了if (cuvidMapVideoFrame(m_videoDecoder, pInfo.picture_index, &pDecodedFrame[active_field], &nDecodedPitch, &oVPP) != CUDA_SUCCESS) {m_pFrameQueue->releaseFrame(&pInfo);cuCtxPopCurrent(NULL);return false;}if (isFirstFrame) { //如果是第一帧需要初始化gpu上下文中的全局变量,实际是alapha透明度*ptr = rgbaBuf;*width = targetWidth;*height = targetHeight;float hueColorSpaceMat[9];setColorSpaceMatrix(ITU601, hueColorSpaceMat, 0.0f);updateConstantMemory_drvapi(module, hueColorSpaceMat);isFirstFrame = false;}pInteropFrame[active_field] = g_pInteropFrame; //设置转换为rgba格式的内存地址int dstPictch = targetWidth * 4;  //设置rgba格式的行宽,一个像素占4个字节,所以是targetWidth*4dim3 block(32, 16, 1); //blck和grid是从官方代码调试时获取的,我只要能解视频所以就直接拿底层的数据了dim3 grid((targetWidth + (2 * block.x - 1)) / (2 * block.x), (targetHeight + (block.y - 1)) / block.y, 1);void *args[] = { &pDecodedFrame[active_field], &nDecodedPitch, //传入的参数&pInteropFrame[active_field], &dstPictch,&targetWidth, &targetHeight};CUresult oRes = cuLaunchKernel(g_kernelNV12toARGB, grid.x, grid.y, grid.z,//这里调用cuda的函数完成转换block.x, block.y, block.z,0, 0, args, NULL);if (oRes != CUDA_SUCCESS) {std::cout << "launchKernel failed,status" << oRes << std::endl;return false;}//这里将转换的结果从显存拷贝到内存,这个技术点研究了很久。。。checkCudaErrors(cuMemcpyDtoH(rgbaBuf, pInteropFrame[active_field], dstPictch * targetHeight));cuvidUnmapVideoFrame(m_videoDecoder, pDecodedFrame[active_field]);}cuCtxPopCurrent(NULL);*timestamp = pInfo.timestamp;m_pFrameQueue->releaseFrame(&pInfo);return true;}}return false;
}

将解码的结果进行格式转换,并取出来的核心代码就是上面了。经自己电脑验证vlc推送的udp形式的rtsp流不能调用cuvidDecodePicture进行解码,但公网上的可以,我试了一个rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov;如果是视频文件的话也可以,用这个硬解码速度很快的,第二个公网上的偶而有些卡,估计是推送的网络速度吧。

这次完成了用nvdia显示硬解码,并且在GPU中从nv12转换到rgba,上层应用就可以直接显示使用了,极大的减轻了cpu的负担;udp不能解的原因暂时还不知道。

整个工程缩略图如下

需要工程代码的可以下载,需要积分罗,我搞了很久,赏个脸。到此下载

使用ffmpeg、nvdia解码rtsp视频流,cuda做NV12-RGBA转换相关推荐

  1. FFmpeg中拉取rtsp视频流并缩放显示测试代码

    之前在https://blog.csdn.net/fengbingchun/article/details/92198857中给出过仅拉取rtsp视频流的测试代码,这里在此代码的基础上进行扩充,包括设 ...

  2. ffmpeg 硬件解码rtsp流_树莓派使用硬件加速视频转码

    现在随着智能设备普及以及宽带的升级,越来越的的视频素材在不断的产生.无论是我们自己拍摄的视频,还是从网上收集来的电影.电视剧,并不是全部都值得我们保存最高清的版本.打个比方,比如你下载了一个 1080 ...

  3. ffmpeg+nvidia解码SDK+GPU实现视频流硬解码成Mat

    方法原理 rtsp流解码方式分为两种:硬解码和软解码.软解码一般通过ffmpeg编解码库实现,但是cpu占用率很高,解码一路1080p视频cpu占用率达到70%左右,对实际应用来说,严重影响机器最大解 ...

  4. FFMPEG 播放 RTSP视频流

    功能简介: 使用QT+FFMPEG实现了RTSP视频流播放的基础操作,点击按钮后,将拉取指定地址的RTSP流,并在QT界面中通过Label显示 开发环境: 系统环境:Ubuntu QT:5.12.12 ...

  5. LIVE555再学习 -- FFmpeg + live555实现RTSP直播

    一.简单实验 首先先简单的实验了一下. 运行 FFmpeg 将 MP4文件转成 264 文件 ffmpeg -i Tai.mp4 test.264 执行 testOnDemandRTSPServer ...

  6. RTSP视频流显示(海康威视)

    RTSP视频流显示(海康威视) VLC 网页显示 VLC-Qt库 SDK(C++) 本文使用环境如下: SDK下载. 编写应用 ffmpeg+Nginx 直接使用ffmpeg解码视频 AppEmit ...

  7. Windows笔记本本地摄像头提供Rtsp视频流服务

    一.背景 本地测试CV,需要使用RTSP视频流进行测试,所以需要在本地Windows10笔记本摄像头提供RTSP视频流服务. Windows 环境下使用 FFmpeg 推送本地摄像头为RTSP流,并使 ...

  8. CUDA和FFMPEG硬件解码视频流

    本文主要讲述了通过FFMPEG获取H264格式的RTSP流数据(也可以获取本地视频文件),并通过CUDA进行硬件解码的过程.其他博客给出的教程要么只是给出了伪代码,非常的模糊,要么是基于D3D进行显示 ...

  9. 基于FFmpeg 实现RTSP, 音视频编解码,视频流添加文字,音视频合成MP4

    前言: 最近闲这没事,整理了一下之前开发过的音视频编解码库,主要基于ffmpeg,实现音视频的编解码.视频流添加文字,音视频同步到MP4等功能.有需要的小伙伴可以参考参考,如果写的有什么不对的地方,欢 ...

最新文章

  1. airflow使用_使用AirFlow,SAS Viya和Docker像Pro一样自动化ML模型
  2. 8255数码管显示0到9_汇编语言--键盘扫描及显示实验(含代码解释)
  3. python redis pipeline使用方法_Redis中的管道Pipeline操作
  4. JavaScript依赖注入的实现思路
  5. 为实现电动车长途旅行,特斯拉超级充电站将大幅升级
  6. Unity3D场景漫游以及碰撞防止反弹
  7. cs231n学习记录
  8. Android开发环境搭建(基于Android Studio)
  9. C# 第五章『面向对象』◆第9节:抽象类和密封类
  10. bitset java 源码_【JAVA】BitSet的源码研究
  11. 线性代数_1、二阶、三阶行列式、排列、逆序
  12. sklearn.metrics.multilabel_confusion_matrix
  13. 2010年北京大学软件与微电子学院毕业生就业去向(官方不完全统计)
  14. 基于 Apache APISIX,爱奇艺 API 网关的更新与落地实践
  15. 个人对软件工程的期望及个人目标
  16. 中关村回收贩曝黑幕:翻新一部iPhone4可赚2000元
  17. Solace 备份/恢复solace配置
  18. android新闻客户端实验报告,Android 新闻客户端学习笔记
  19. FPGADesigner《FPGA数字信号处理系列》目录与传送门
  20. jquery 做一个小的倒计时效果

热门文章

  1. python 脚本分析dns日志计算前几位的请求数域名
  2. 雪球网爬取上市公司信息(一):爬取上市公司代号
  3. Centos7登陆颜色修改#PS1
  4. 修改PPT母板中的LOGO
  5. 依赖倒转原则与里氏代换原则
  6. 属性动画+购物车+结算
  7. 程序员在群里“匿名”骂老板,第二天被开除,聊天记录曝光
  8. k8s client-go 出现错误net/http: TLS handshake timeout
  9. JAVA星期健身计划:If语句和Switch语句
  10. word文件点击打印没反应