我正在修改SDK示例包中的CUDA视频编码器(NVCUVENC)编码样本,以便数据不是来自外部yuv文件(如示例中所做),而是来自纹理填充的cudaArray.
因此,对帧进行编码的关键API方法是:
int NVENCAPI NVEncodeFrame(NVEncoder hNVEncoder, NVVE_EncodeFrameParams *pFrmIn, unsigned long flag, void *pData);
Run Code Online (Sandbox Code Playgroud)
如果我把它弄好了:
CUdeviceptr dptr_VideoFrame
Run Code Online (Sandbox Code Playgroud)
应该将数据传递给encode.But我真的不明白如何将它与GPU上的一些纹理数据连接.示例源代码非常含糊,因为它与CPU yuv文件输入一起工作.
例如,在main.cpp,第555-560行中,有以下块:
// If dptrVideoFrame is NULL, then we assume that frames come from system memory, otherwise it comes from GPU memory
// VideoEncoder.cpp, EncodeFrame() will automatically copy it to GPU Device memory, if GPU device input is specified
if (pCudaEncoder->EncodeFrame(efparams, dptrVideoFrame, cuCtxLock) == false)
{
printf("\nEncodeFrame() failed to encode frame\n");
}
Run Code Online (Sandbox Code Playgroud)
所以,从评论来看,似乎dptrVideoFrame应该填充来自设备的yuv数据来编码帧.但是没有地方可以解释如何这样做.
更新:
我想分享一些发现.首先,我设法对帧缓冲区纹理中的数据进行编码.现在的问题是输出视频是一团糟.
这是理想的结果:
这是我做的:
在OpenGL方面,我有2个自定义FBO - 首先将场景正常渲染到其中.然后,第一个FBO的纹理用于将屏幕四边形呈现为第二个FBO,在片段着色器中进行RGB - > YUV转换.
附加到第二个FBO的纹理然后映射到CUDA资源.然后我像这样编码当前纹理:
void CUDAEncoder::Encode(){
NVVE_EncodeFrameParams efparams;
efparams.Height = sEncoderParams.iOutputSize[1];
efparams.Width = sEncoderParams.iOutputSize[0];
efparams.Pitch = (sEncoderParams.nDeviceMemPitch ? sEncoderParams.nDeviceMemPitch : sEncoderParams.iOutputSize[0]);
efparams.PictureStruc = (NVVE_PicStruct)sEncoderParams.iPictureType;
efparams.SurfFmt = (NVVE_SurfaceFormat)sEncoderParams.iSurfaceFormat;
efparams.progressiveFrame = (sEncoderParams.iSurfaceFormat == 3) ? 1 : 0;
efparams.repeatFirstField = 0;
efparams.topfieldfirst = (sEncoderParams.iSurfaceFormat == 1) ? 1 : 0;
if(_curFrame > _framesTotal){
efparams.bLast=1;
}else{
efparams.bLast=0;
}
//----------- get cuda array from the texture resource -------------//
checkCudaErrorsDrv(cuGraphicsMapResources(1,&_cutexResource,NULL));
checkCudaErrorsDrv(cuGraphicsSubResourceGetMappedArray(&_cutexArray,_cutexResource,0,0));
/////////// copy data into dptrvideo frame //////////
// LUMA based on CUDA SDK sample//////////////
CUDA_MEMCPY2D pcopy;
memset((void *)&pcopy, 0, sizeof(pcopy));
pcopy.srcXInBytes = 0;
pcopy.srcY = 0;
pcopy.srcHost= NULL;
pcopy.srcDevice= 0;
pcopy.srcPitch =efparams.Width;
pcopy.srcArray= _cutexArray;///SOME DEVICE ARRAY!!!!!!!!!!!!! <--------- to figure out how to fill this.
/// destination //////
pcopy.dstXInBytes = 0;
pcopy.dstY = 0;
pcopy.dstHost = 0;
pcopy.dstArray = 0;
pcopy.dstDevice=dptrVideoFrame;
pcopy.dstPitch = sEncoderParams.nDeviceMemPitch;
pcopy.WidthInBytes = sEncoderParams.iInputSize[0];
pcopy.Height = sEncoderParams.iInputSize[1];
pcopy.srcMemoryType=CU_MEMORYTYPE_ARRAY;
pcopy.dstMemoryType=CU_MEMORYTYPE_DEVICE;
// CHROMA based on CUDA SDK sample/////
CUDA_MEMCPY2D pcChroma;
memset((void *)&pcChroma, 0, sizeof(pcChroma));
pcChroma.srcXInBytes = 0;
pcChroma.srcY = 0;// if I uncomment this line I get error from cuda for incorrect value.It does work in CUDA SDK original sample SAMPLE//sEncoderParams.iInputSize[1] << 1; // U/V chroma offset
pcChroma.srcHost = NULL;
pcChroma.srcDevice = 0;
pcChroma.srcArray = _cutexArray;
pcChroma.srcPitch = efparams.Width >> 1; // chroma is subsampled by 2 (but it has U/V are next to each other)
pcChroma.dstXInBytes = 0;
pcChroma.dstY = sEncoderParams.iInputSize[1] << 1; // chroma offset (srcY*srcPitch now points to the chroma planes)
pcChroma.dstHost = 0;
pcChroma.dstDevice = dptrVideoFrame;
pcChroma.dstArray = 0;
pcChroma.dstPitch = sEncoderParams.nDeviceMemPitch >> 1;
pcChroma.WidthInBytes = sEncoderParams.iInputSize[0] >> 1;
pcChroma.Height = sEncoderParams.iInputSize[1]; // U/V are sent together
pcChroma.srcMemoryType = CU_MEMORYTYPE_ARRAY;
pcChroma.dstMemoryType = CU_MEMORYTYPE_DEVICE;
checkCudaErrorsDrv(cuvidCtxLock(cuCtxLock, 0));
checkCudaErrorsDrv( cuMemcpy2D(&pcopy));
checkCudaErrorsDrv( cuMemcpy2D(&pcChroma));
checkCudaErrorsDrv(cuvidCtxUnlock(cuCtxLock, 0));
//=============================================
// If dptrVideoFrame is NULL, then we assume that frames come from system memory, otherwise it comes from GPU memory
// VideoEncoder.cpp, EncodeFrame() will automatically copy it to GPU Device memory, if GPU device input is specified
if (_encoder->EncodeFrame(efparams, dptrVideoFrame, cuCtxLock) == false)
{
printf("\nEncodeFrame() failed to encode frame\n");
}
checkCudaErrorsDrv(cuGraphicsUnmapResources(1, &_cutexResource, NULL));
// computeFPS();
if(_curFrame > _framesTotal){
_encoder->Stop();
exit(0);
}
_curFrame++;
}
Run Code Online (Sandbox Code Playgroud)
我从CUDA SDK Encoder样本中包含的.cfg文件中设置了Encoder params.所以我在这里使用704x480-h264.cfg设置.我尝试了所有这些并且总是得到同样丑陋的结果.
我怀疑问题是在CUDA_MEMCPY2D中的某个位置用于亮度和色度对象参数设置.可能是错误的音高,宽度,高度尺寸.我将视口设置为与视频大小相同(704,480)并将params与CUDA SDK示例中使用的参数进行比较但是不知道问题出在哪里.任何人 ?
首先:我把 Cuda Video Encoder 搞乱了,遇到了很多麻烦。但在我看来,好像你将其转换为 Yuv 值,但作为一对一的像素转换(如 AYUV 4:4:4)。据我所知,您需要一种具有填充和压缩功能的正确 YUV(多个像素的颜色值,如 4:2:0)。可以在这里看到 YUV 对齐的详细概述:
http://msdn.microsoft.com/en-us/library/windows/desktop/dd206750(v=vs.85).aspx
据我记得你必须对 Cuda 编码器使用 NV12 对齐。