19

我正在修改SDK 示例包中的CUDA 视频编码器(NVCUVENC) 编码示例,以便数据不是来自外部 yuv 文件(如示例中所做的那样),而是来自从纹理填充的 cudaArray。

因此,对帧进行编码的关键 API 方法是:

int NVENCAPI NVEncodeFrame(NVEncoder hNVEncoder, NVVE_EncodeFrameParams *pFrmIn, unsigned long flag, void *pData); 

如果我做对了参数:

CUdeviceptr dptr_VideoFrame

应该将数据传递给编码。但我真的不明白如何将它与 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");
    }

因此,从评论看来, 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++;

}

我从 CUDA SDK 编码器示例中包含的 .cfg 文件中设置了编码器参数。所以在这里我使用 704x480-h264.cfg 设置。我尝试了所有这些,并且总是得到同样丑陋的结果。

我怀疑问题出在 CUDA_MEMCPY2D 中,用于亮度和色度对象参数设置。可能是错误的间距、宽度、高度尺寸。我将视口设置为与视频 (704,480) 相同的大小,并将参数与 CUDA SDK 示例中使用的参数进行比较,但是不知道问题出在哪里。任何人 ?

4

2 回答 2

2

首先:我搞砸了 Cuda Video Encoder,遇到了很多麻烦。但在我看来,好像您将其转换为 Yuv 值,但作为一对一的像素转换(如 AYUV 4:4:4)。Afaik 您需要具有填充和压缩的正确类型的 YUV(多个像素的颜色值,例如 4:2:0)。可以在这里看到 YUV 对齐的一个很好的概述:

http://msdn.microsoft.com/en-us/library/windows/desktop/dd206750(v=vs.85).aspx

据我记得你必须对 Cuda 编码器使用 NV12 对齐。

于 2013-04-17T12:48:08.463 回答
1

nvEncoder 应用程序用于编解码器转换,用于通过 GPU 处理其使用的 cuda 并与使用 nvEncoder 的 API 的硬件通信。在此应用程序逻辑中,读取输入缓冲区中的 yuv 数据并将该内容存储在内存中,然后开始对帧进行编码。并将编码帧并行写入输出文件。

输入缓冲区的处理在 nvRead 函数中可用,它在 nvFileIO.h 中可用

任何其他需要的帮助在这里留言...

于 2015-02-23T09:36:07.297 回答