NVencs输出位流不可读
NVencs Output Bitstream is not readable
我有一个问题与Nvidias NVenc API有关。我想使用API对一些OpenGL图形进行编码。我的问题是,API在整个程序中没有报告错误,一切似乎都很好。但是生成的输出是不可读的,例如VLC。如果我尝试播放生成的文件,VLC会在黑屏上闪烁大约0.5秒,然后结束播放。视频的长度为0,视频的大小似乎也很小。分辨率为1280*720,5秒记录的大小仅为700kb。这现实吗?
应用程序的流程如下:
- 渲染到辅助帧缓冲区
- 将Framebuffer下载到两个PBO之一(glReadPixels())
- 映射上一帧的PBO,以获得Cuda可以理解的指针
- 调用一个简单的CudaKernel,将OpenGL的RGBA转换为ARGB,NVenc应该可以理解这一点(第18页)。内核读取PBO的内容,并将转换后的内容写入CudaArray(使用cudaMalloc创建),该数组在NVenc注册为InputResource
- 转换后的数组的内容将被编码。一个完成事件加上相应的输出比特流缓冲区进入队列
- 辅助线程侦听排队的输出事件,如果有一个事件被发出信号,则输出比特流被映射并写入hdd
NVenc编码器的初始化:
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
每一个小提示,在哪里看,都是非常感激的。我想不出可能出了什么问题。
非常感谢!
在英伟达论坛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点)。英伟达枚举表示编码器期望通道为ARGB格式,但实际上ment是BGRA,因此始终为255的alpha通道污染了蓝色通道。
编辑:这可能是由于NVidia在内部使用little-endian。我在写我的像素数据到字节数组,选择其他类型(如int32)可能会允许传递实际的ARGB数据。
相关文章:
- 使用 boost 进行标记化会给出相同的输出
- 输出错误,问题是找到总和5000位数字cpp
- 捕获标准输出以压缩并使用 CTRL-C 中断会给出损坏的 zip 文件
- 编译并运行后,输出未出现在 Visual Studio 代码中
- 当字符串是某个单词时给出输出?
- 可能我知道为什么这段代码没有给出任何输出吗?
- 阶乘问题在 c++ 中给出错误的输出
- 为什么这个scanf()的两个不同的输入会给出两个不同的输出?
- 大数的阶乘给出错误的输出
- 为什么这两个版本的代码给出不同的输出
- 生成成功,但不会给出正确的输出
- 为什么两种不同的对象初始化方式给出不同的输出
- 字符串数组上的 sizeof 运算符以 C++ 为单位给出不同的输出
- 在 c++ 中输出 64 位整数
- 数组为此合并排序函数提供了正确的输出,但向量给出了不正确的输出.出了什么问题?
- 从 2 位输入输出 4 位数字年份
- 在 int 数组中打印出位
- 子输出出现在主进程使用 system() 调用它之前
- ImageMagick c++ API输出16位灰度png
- 位移位数字给出错误的输出' std::cout '