所需头文件
#include<gl/glut.h>
#include "cuda_gl_interop.h"
涉及变量
cudaGraphicsResource_t cudaResource[1];//CUDA图像资源对象,用以联系OpenGL与CUDA
GLuint textureID[1];//OpenGL纹理上下文
cudaArray* devArray;//cuda共享数据区指针
操作流程
glEnable(GL_TEXTURE_2D);//开启2d上下文,告诉OpenGL要绘制的是2d的纹理
glGenTextures(1, textureID);//生成1个纹理上下文对象
glBindTexture(GL_TEXTURE_2D, textureID[0]);//绑定2d纹理上下文
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);//设置2d纹理的插值方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1920, 1080, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);//生成2d纹理,对应当前的2d纹理上下文
//在CUDA中注册这2d纹理上下文,这样CUDA就知道有这么个玩意存在
cudaError_t err = cudaGraphicsGLRegisterImage(&cudaResource[0], textureID[0], GL_TEXTURE_2D, cudaGraphicsRegisterFlagsWriteDiscard);
if (err != cudaSuccess)
{
std::cout << "cudaGraphicsGLRegisterImage: " << err << "Line: " << __LINE__;
return -1;
}
//告诉CUDA运行时映射这个共享资源
cudaGraphicsMapResources(1, cudaResource, 0);
//获得共享资源的指针,指针类型为cudaArray
cudaGraphicsSubResourceGetMappedArray(&devArray, cudaResource[0], 0, 0);
//拷贝图像数据到纹理中去
cudaMemcpyToArray(devArray, 0, 0, (void*)dpFrame, dec->GetWidth()*dec->GetHeight() * sizeof(uchar4), cudaMemcpyDeviceToDevice);
//解除锁定,让OpenGL操作
cudaGraphicsUnmapResources(1, &cudaResource[0], 0);
//可以用OpenGL在这里绘图了!
myDisplay();
示例程序
以之前这篇文章为例,可以改为OpenGL进行显示,代码如下:
#include <cuda.h>
#include <iostream>
#include "NvDecoder/NvDecoder.h"
#include "../Utils/NvCodecUtils.h"
#include "../Utils/FFmpegDemuxer.h"
#include "../Common/AppDecUtils.h"
#include "../Utils/ColorSpace.h"
#include<gl/glut.h>
#include "cuda_gl_interop.h"
// 初始化两个Texture并绑定
cudaGraphicsResource_t cudaResource[1];
GLuint textureID[1];
cudaArray* devArray;
simplelogger::Logger* logger = simplelogger::LoggerFactory::CreateConsoleLogger();
void myDisplay(void)
{
//将纹理映射到四边形上
glBegin(GL_QUADS);
//纹理的坐标和四边形顶点的对应,可以通过设置四边形的位置调整图像在窗体的位置
glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, 1.0, 0.0);
glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, -1.0, 0.0);
glTexCoord2f(1.0, 1.0); glVertex3f(1.0, -1.0, 0.0);
glTexCoord2f(1.0, 0.0); glVertex3f(1.0, 1.0, 0.0);
glEnd();
glFlush();
}
class HWdecode
{
public:
//参数对象
FFmpegDemuxer *demuxer = NULL;//FFMPEG对象
CUcontext cuContext;//Cuda设备
NvDecoder *dec = NULL;//Cuda解码对象
CUdeviceptr dpFrame = 0;//数据存储对象初始化
int hwinit()//显卡设备初始化
{
int iGpu = 0;//选择解码播放的GPU,只有一个的话直接设置为0就行
//检测硬件GPU设备
ck(cuInit(0));
int nGpu = 0;
ck(cuDeviceGetCount(&nGpu));
if (iGpu < 0 || iGpu >= nGpu)
{
std::cout << "没有相应的GPU" << std::endl;
return 0;
}
//获得CUDA对象
CUdevice cuDevice = 0;
ck(cuDeviceGet(&cuDevice, iGpu));
char szDeviceName[80];
ck(cuDeviceGetName(szDeviceName, sizeof(szDeviceName), cuDevice));
std::cout << "GPU in use: " << szDeviceName << std::endl;
ck(cuCtxCreate(&cuContext, CU_CTX_SCHED_BLOCKING_SYNC, cuDevice));
return 1;
}
void ffmpeginit(char* url)//ffmpeg对象初始化
{
demuxer = new FFmpegDemuxer(url);
}
void cudadecoderinit()//cuda解码对象初始化
{
//RGBA帧存储显存初始化
ck(cuMemAlloc(&dpFrame, demuxer->GetWidth() * demuxer->GetHeight() * 4));
//解码器初始化
dec = new NvDecoder(cuContext, (*demuxer).GetWidth(), (*demuxer).GetHeight(), true, FFmpeg2NvCodecId((*demuxer).GetVideoCodec()));
}
void decAndshow()//解码和播放
{
int nVideoBytes = 0, nFrameReturned = 0, nFrame = 0;
uint8_t* pVideo = NULL, ** ppFrame;
long bt = clock();
do
{
demuxer->Demux(&pVideo, &nVideoBytes);//获取一帧视频数据
dec->Decode(pVideo, nVideoBytes, &ppFrame, &nFrameReturned);//解码这一帧数据
//if (!nFrame && nFrameReturned)
// LOG(INFO) << HWD.dec->GetVideoInfo();
for (int i = 0; i < nFrameReturned; i++)
{
//对解码出来的图像数据类型做转换,转到BGRA
if (dec->GetOutputFormat() == cudaVideoSurfaceFormat_YUV444)
YUV444ToColor32<RGBA32>((uint8_t*)ppFrame[i], dec->GetWidth(), (uint8_t*)dpFrame, 4 * dec->GetWidth(), dec->GetWidth(), dec->GetHeight());
else // default assumed as NV12
Nv12ToColor32<RGBA32>((uint8_t*)ppFrame[i], dec->GetWidth(), (uint8_t*)dpFrame, 4 * dec->GetWidth(), dec->GetWidth(), dec->GetHeight());
//获得共享资源的指针,指针类型为cudaArray
cudaGraphicsSubResourceGetMappedArray(&devArray, cudaResource[0], 0, 0);
//拷贝图像数据到纹理中去
cudaMemcpyToArray(devArray, 0, 0, (void*)dpFrame, dec->GetWidth()*dec->GetHeight() * sizeof(uchar4), cudaMemcpyDeviceToDevice);
//解除锁定,让OpenGL操作
cudaGraphicsUnmapResources(1, &cudaResource[0], 0);
myDisplay();
}
nFrame += nFrameReturned;
} while (nVideoBytes);
ck(cuMemFree(dpFrame));
std::cout << "Total frame decoded: " << nFrame << std::endl;
std::cout << "花费时间:" << clock() - bt << "ms" << std::endl;
std::cout << "FPS:" << nFrame / ((clock() - bt) / 1000.0) << std::endl;
return;
}
};
int main(int argc, char *argv[])
{
//初始化OpenGL
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(720, 720);
glutInitWindowPosition(200, 200);
//创建窗口
glutCreateWindow("imgshow");
glEnable(GL_TEXTURE_2D);//开启2d上下文,告诉OpenGL要绘制的是2d的纹理
glGenTextures(1, textureID);//生成1个2d上下文对象
glBindTexture(GL_TEXTURE_2D, textureID[0]);//绑定2d上下文
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);//设置2d上下文对象的插值方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1920, 1080, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);//生成2d纹理,对应当前的2d上下文
//在CUDA中注册这2d上下文,这样CUDA就知道有这么个玩意存在
cudaError_t err = cudaGraphicsGLRegisterImage(&cudaResource[0], textureID[0], GL_TEXTURE_2D, cudaGraphicsRegisterFlagsWriteDiscard);
if (err != cudaSuccess)
{
std::cout << "cudaGraphicsGLRegisterImage: " << err << "Line: " << __LINE__;
return -1;
}
//告诉CUDA运行时映射这个共享资源
cudaGraphicsMapResources(1, cudaResource, 0);
HWdecode mhwdecoder;
if (!mhwdecoder.hwinit())
{
std::cout << "硬件初始化失败!" << std::endl;
return 0;
}
mhwdecoder.ffmpeginit("G:\\硬解码\\3.mp4");
mhwdecoder.cudadecoderinit();
mhwdecoder.decAndshow();
return 1;
}