NVencs输出比特流不可读

问题描述:

我有一个与Nvidias NVenc API相关的问题。我想使用API​​来编码一些OpenGL图形。我的问题是,API在整个程序中报告没有错误,一切似乎都很好。但是生成的输出不可被例如VLC。如果我尝试播放生成的文件,VLC将闪烁约0.5秒的黑屏,然后结束播放。 视频的长度为0,Vid的大小似乎也很小。 分辨率为1280 * 720,而5secs录音的大小只有700kb。这是现实的吗?NVencs输出比特流不可读

应用程序的流程如下:

  1. 渲染到次帧缓冲区
  2. 下载Framebuffer的两个公益组织中的一个(glReadPixels())
  3. 地图的前一帧的PBO,至得到Cuda可以理解的指针。
  4. 根据this(第18页)调用一个简单的CudaKernel,将OpenGLs RGBA转换为ARGB,NVenc应该可以理解这一点。内核读取PBO的内容并将转换后的内容写入CudaArray(使用cudaMalloc创建),该CudaArray使用NVenc注册为InputResource。
  5. 转换后的数组的内容被编码。一个完成事件加上相应的输出比特流缓冲区得到排队。
  6. 辅助线程监听排队的输出事件,如果发送了一个事件,输出位流被映射并写入硬盘。

NVenc-编码器的initializion:

InitParams* ip = new InitParams(); 
m_initParams = ip; 
memset(ip, 0, sizeof(InitParams)); 
ip->version = NV_ENC_INITIALIZE_PARAMS_VER; 
ip->encodeGUID = m_encoderGuid; //Used Codec 
ip->encodeWidth = width; // Frame Width 
ip->encodeHeight = height; // Frame Height 
ip->maxEncodeWidth = 0; // Zero means no dynamic res changes 
ip->maxEncodeHeight = 0; 
ip->darWidth = width; // Aspect Ratio 
ip->darHeight = height; 
ip->frameRateNum = 60; // 60 fps 
ip->frameRateDen = 1; 
ip->reportSliceOffsets = 0; // According to programming guide 
ip->enableSubFrameWrite = 0; 
ip->presetGUID = m_presetGuid; // Used Preset for Encoder Config 

NV_ENC_PRESET_CONFIG presetCfg; // Load the Preset Config 
memset(&presetCfg, 0, sizeof(NV_ENC_PRESET_CONFIG)); 
presetCfg.version = NV_ENC_PRESET_CONFIG_VER; 
presetCfg.presetCfg.version = NV_ENC_CONFIG_VER; 
CheckApiError(m_apiFunctions.nvEncGetEncodePresetConfig(m_Encoder, 
    m_encoderGuid, m_presetGuid, &presetCfg)); 
memcpy(&m_encodingConfig, &presetCfg.presetCfg, sizeof(NV_ENC_CONFIG)); 
// And add information about Bitrate etc 
m_encodingConfig.rcParams.averageBitRate = 500000; 
m_encodingConfig.rcParams.maxBitRate = 600000; 
m_encodingConfig.rcParams.rateControlMode = NV_ENC_PARAMS_RC_MODE::NV_ENC_PARAMS_RC_CBR; 
ip->encodeConfig = &m_encodingConfig; 
ip->enableEncodeAsync = 1; // Async Encoding 
ip->enablePTD = 1; // Encoder handles picture ordering 

CudaResource登记

m_cuContext->SetCurrent(); // Make the clients cuCtx current 
NV_ENC_REGISTER_RESOURCE res; 
memset(&res, 0, sizeof(NV_ENC_REGISTER_RESOURCE)); 
NV_ENC_REGISTERED_PTR resPtr; // handle to the cuda resource for future use 
res.bufferFormat = m_inputFormat; // Format is ARGB 
res.height = m_height; 
res.width = m_width; 
// NOTE: I've set the pitch to the width of the frame, because the resource is a non-pitched 
//cudaArray. Is this correct? Pitch = 0 would produce no output. 
res.pitch = pitch; 
res.resourceToRegister = (void*) (uintptr_t) resourceToRegister; //CUdevptr to resource 
res.resourceType = 
    NV_ENC_INPUT_RESOURCE_TYPE::NV_ENC_INPUT_RESOURCE_TYPE_CUDADEVICEPTR; 
res.version = NV_ENC_REGISTER_RESOURCE_VER; 
CheckApiError(m_apiFunctions.nvEncRegisterResource(m_Encoder, &res)); 
m_registeredInputResources.push_back(res.registeredResource); 

编码

m_cuContext->SetCurrent(); // Make Clients context current 
MapInputResource(id); //Map the CudaInputResource 
NV_ENC_PIC_PARAMS temp; 
memset(&temp, 0, sizeof(NV_ENC_PIC_PARAMS)); 
temp.version = NV_ENC_PIC_PARAMS_VER; 
unsigned int currentBufferAndEvent = m_counter % m_registeredEvents.size(); //Counter is inc'ed in every Frame 
temp.bufferFmt = m_currentlyMappedInputBuffer.mappedBufferFmt; 
temp.inputBuffer = m_currentlyMappedInputBuffer.mappedResource; //got set by MapInputResource 
temp.completionEvent = m_registeredEvents[currentBufferAndEvent]; 
temp.outputBitstream = m_registeredOutputBuffers[currentBufferAndEvent]; 
temp.inputWidth = m_width; 
temp.inputHeight = m_height; 
temp.inputPitch = m_width; 
temp.inputTimeStamp = m_counter; 
temp.pictureStruct = NV_ENC_PIC_STRUCT_FRAME; // According to samples 
temp.qpDeltaMap = NULL; 
temp.qpDeltaMapSize = 0; 

EventWithId latestEvent(currentBufferAndEvent, 
    m_registeredEvents[currentBufferAndEvent]); 
PushBackEncodeEvent(latestEvent); // Store the Event with its ID in a Queue 

CheckApiError(m_apiFunctions.nvEncEncodePicture(m_Encoder, &temp)); 
m_counter++; 
UnmapInputResource(id); // Unmap 

每一个小小的提示,在哪里看,都非常感谢。我正在想出什么可能是错误的。

非常感谢!

+0

你没有给出太多的细节,但听起来像处理RAW码流时VLC的常见问题:如果没有告诉编解码器,VLC无法播放它们。为此,请尝试为文件提供正确的结尾,例如用于h264编解码器的“filename.h264”。 – kunzmi

+0

好的,如果我这样做,我会得到以下结果:[点击](https://s31.postimg.org/vvgbiqg0b/Encoded_File.png)。 – Christoph

+0

似乎有些问题已经在[交叉发布]中进行了整理(https://devtalk.nvidia.com/default/topic/953041/gpu-accelerated-libraries/nvencs-output-bitstream-is-not-readable /)。 –

在nvidia论坛的hall822的帮助下,我设法解决了这个问题。

主要的错误是我注册了我的cuda资源,其音调等于帧的大小。我正在使用Framebuffer-Renderbuffer来绘制我的内容。这个数据是一个普通的,未修改的数组。我的第一个想法是,让一个等于零的音高失败。编码器什么也没做。接下来的想法是将其设置为帧的宽度,图像的四分之一被编码。

// NOTE: I've set the pitch to the width of the frame, because the resource is a non-pitched 
//cudaArray. Is this correct? Pitch = 0 would produce no output. 
res.pitch = pitch; 

回答这个问题:是的,它是正确的。但是音调是以字节来衡量的。因为我编码RGBA帧,所以正确的音调必须是FRAME_WIDTH * 4

第二个错误是我的颜色通道不正确(请参阅我的开场白中的第4点)。NVidia枚举说编码器期望ARGB格式的通道,但实际上是BGRA,所以总是255的阿尔法通道污染了蓝色通道。

编辑:这可能是由于NVidia在内部使用小端。我正在将我的像素数据写入 到一个字节数组,选择像int32这样的其他类型可能允许传递实际的ARGB数据。

+0

这是非常有趣的,因为它可能是我有一个问题的真正答案,我发布并回答(或认为我回答)在这上:http://*.com/questions/32924056/nvencregisterresource-fails-与-23。在我的代码中,我在注册CUDA资源时设置了参数,但我从未尝试调用'nvEncEncodePicture()'来设置'inputPitch'。在我的情况下,该设置因此保持为零,这对我而言是有效的,但只适用于 JPNotADragon