使用ffmpeg、nvdia解码rtsp视频流,cuda做NV12-RGBA转换
本章是在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转换相关推荐
- FFmpeg中拉取rtsp视频流并缩放显示测试代码
之前在https://blog.csdn.net/fengbingchun/article/details/92198857中给出过仅拉取rtsp视频流的测试代码,这里在此代码的基础上进行扩充,包括设 ...
- ffmpeg 硬件解码rtsp流_树莓派使用硬件加速视频转码
现在随着智能设备普及以及宽带的升级,越来越的的视频素材在不断的产生.无论是我们自己拍摄的视频,还是从网上收集来的电影.电视剧,并不是全部都值得我们保存最高清的版本.打个比方,比如你下载了一个 1080 ...
- ffmpeg+nvidia解码SDK+GPU实现视频流硬解码成Mat
方法原理 rtsp流解码方式分为两种:硬解码和软解码.软解码一般通过ffmpeg编解码库实现,但是cpu占用率很高,解码一路1080p视频cpu占用率达到70%左右,对实际应用来说,严重影响机器最大解 ...
- FFMPEG 播放 RTSP视频流
功能简介: 使用QT+FFMPEG实现了RTSP视频流播放的基础操作,点击按钮后,将拉取指定地址的RTSP流,并在QT界面中通过Label显示 开发环境: 系统环境:Ubuntu QT:5.12.12 ...
- LIVE555再学习 -- FFmpeg + live555实现RTSP直播
一.简单实验 首先先简单的实验了一下. 运行 FFmpeg 将 MP4文件转成 264 文件 ffmpeg -i Tai.mp4 test.264 执行 testOnDemandRTSPServer ...
- RTSP视频流显示(海康威视)
RTSP视频流显示(海康威视) VLC 网页显示 VLC-Qt库 SDK(C++) 本文使用环境如下: SDK下载. 编写应用 ffmpeg+Nginx 直接使用ffmpeg解码视频 AppEmit ...
- Windows笔记本本地摄像头提供Rtsp视频流服务
一.背景 本地测试CV,需要使用RTSP视频流进行测试,所以需要在本地Windows10笔记本摄像头提供RTSP视频流服务. Windows 环境下使用 FFmpeg 推送本地摄像头为RTSP流,并使 ...
- CUDA和FFMPEG硬件解码视频流
本文主要讲述了通过FFMPEG获取H264格式的RTSP流数据(也可以获取本地视频文件),并通过CUDA进行硬件解码的过程.其他博客给出的教程要么只是给出了伪代码,非常的模糊,要么是基于D3D进行显示 ...
- 基于FFmpeg 实现RTSP, 音视频编解码,视频流添加文字,音视频合成MP4
前言: 最近闲这没事,整理了一下之前开发过的音视频编解码库,主要基于ffmpeg,实现音视频的编解码.视频流添加文字,音视频同步到MP4等功能.有需要的小伙伴可以参考参考,如果写的有什么不对的地方,欢 ...
最新文章
- airflow使用_使用AirFlow,SAS Viya和Docker像Pro一样自动化ML模型
- 8255数码管显示0到9_汇编语言--键盘扫描及显示实验(含代码解释)
- python redis pipeline使用方法_Redis中的管道Pipeline操作
- JavaScript依赖注入的实现思路
- 为实现电动车长途旅行,特斯拉超级充电站将大幅升级
- Unity3D场景漫游以及碰撞防止反弹
- cs231n学习记录
- Android开发环境搭建(基于Android Studio)
- C# 第五章『面向对象』◆第9节:抽象类和密封类
- bitset java 源码_【JAVA】BitSet的源码研究
- 线性代数_1、二阶、三阶行列式、排列、逆序
- sklearn.metrics.multilabel_confusion_matrix
- 2010年北京大学软件与微电子学院毕业生就业去向(官方不完全统计)
- 基于 Apache APISIX,爱奇艺 API 网关的更新与落地实践
- 个人对软件工程的期望及个人目标
- 中关村回收贩曝黑幕:翻新一部iPhone4可赚2000元
- Solace 备份/恢复solace配置
- android新闻客户端实验报告,Android 新闻客户端学习笔记
- FPGADesigner《FPGA数字信号处理系列》目录与传送门
- jquery 做一个小的倒计时效果