NVENC Nvidia编码器D3D11错误的图像尺寸和其他问题

时间:2016-11-05 21:26:32

标签: c++ h.264 directx-11 nvenc

首先要了解一些基本信息:

OS:Win7 64Bit | GPU:GTX970

我有一个ID3D11Texture2D,我想编码。

我想通过 nvEncRegisterResource 直接使用纹理,但似乎我只能传递D3D9而没有D3D11纹理。否则我会得到 NV_ENC_ERR_UNIMPLEMENTED

之前,我创建了一个输入缓冲区并手动填充。

纹理的格式为 DXGI_FORMAT_R8G8B8A8_UNORM 。格式 DXGI_FORMAT_NV12 可能仅从Windows 8开始。

输入缓冲区格式为 NV_ENC_BUFFER_FORMAT_ARGB 。这也应该是每个颜色通道8位。由于alpha值互换,我预计会出现错误的图片,但仍然需要对其进行编码。

到目前为止我的流程:

  • 创建ID3D11Texture2D渲染纹理
  • 创建ID3D11Texture2D分段纹理
  • 使用nvEncCreateInputBuffer创建NVENC输入缓冲区
  • 使用nvEncCreateBitstreamBuffer创建NVENC输出缓冲区

::更新例程::

  • 渲染渲染纹理
  • CopyResource()到分段纹理
  • 从分段纹理填写NVENC输入缓冲区
  • 编码框架
  • 从NVENC输出缓冲区获取数据

如您所见,我每次更新只编码一帧。

到目前为止,所有内容都可以正常运行,但如果我看一下图片大小是错误的。

enter image description here

那应该是1280 x 720.如果我还尝试传递多个帧,那么我会得到一个损坏的文件。

据我所知,在H.264中我的第一帧是I帧,以下应该是P帧。

现在我的代码:

::创建输入缓冲区

NVENCSTATUS RenderManager::CreateInputBuffer()
{
    NVENCSTATUS nvStatus = NV_ENC_SUCCESS;

    NV_ENC_CREATE_INPUT_BUFFER createInputBufferParams = {};
    createInputBufferParams.version = NV_ENC_CREATE_INPUT_BUFFER_VER;
    createInputBufferParams.width = m_width;
    createInputBufferParams.height = m_height;
    createInputBufferParams.memoryHeap = NV_ENC_MEMORY_HEAP_SYSMEM_CACHED;
    createInputBufferParams.bufferFmt = NV_ENC_BUFFER_FORMAT_ARGB;

    nvStatus = m_pEncodeAPI->nvEncCreateInputBuffer(m_pEncoder, &createInputBufferParams);
    if (nvStatus != NV_ENC_SUCCESS) return nvStatus;

    m_pEncoderInputBuffer = createInputBufferParams.inputBuffer;

    return nvStatus;
}

::创建输出缓冲区

NVENCSTATUS RenderManager::CreateOutputBuffer()
{
    NVENCSTATUS nvStatus = NV_ENC_SUCCESS;

    NV_ENC_CREATE_BITSTREAM_BUFFER createBitstreamBufferParams = {};
    createBitstreamBufferParams.version = NV_ENC_CREATE_BITSTREAM_BUFFER_VER;
    createBitstreamBufferParams.size = 2 * 1024 * 1024;
    createBitstreamBufferParams.memoryHeap = NV_ENC_MEMORY_HEAP_SYSMEM_CACHED;

    nvStatus = m_pEncodeAPI->nvEncCreateBitstreamBuffer(m_pEncoder, &createBitstreamBufferParams);
    if (nvStatus != NV_ENC_SUCCESS) return nvStatus;

    m_pEncoderOutputBuffer = createBitstreamBufferParams.bitstreamBuffer;

    return nvStatus;
}

::填充输入缓冲区

NVENCSTATUS RenderManager::WriteInputBuffer(ID3D11Texture2D* pTexture)
{
    NVENCSTATUS nvStatus = NV_ENC_SUCCESS;
    HRESULT result = S_OK;

    // get data from staging texture
    D3D11_MAPPED_SUBRESOURCE mappedResource;
    result = m_pContext->Map(pTexture, 0, D3D11_MAP_READ, 0, &mappedResource);
    if (FAILED(result)) return NV_ENC_ERR_GENERIC;

    m_pContext->Unmap(pTexture, 0);

    // lock input buffer
    NV_ENC_LOCK_INPUT_BUFFER lockInputBufferParams = {};
    lockInputBufferParams.version = NV_ENC_LOCK_INPUT_BUFFER_VER;
    lockInputBufferParams.inputBuffer = m_pEncoderInputBuffer;

    nvStatus = m_pEncodeAPI->nvEncLockInputBuffer(m_pEncoder, &lockInputBufferParams);
    if (nvStatus != NV_ENC_SUCCESS) return nvStatus;

    unsigned int pitch = lockInputBufferParams.pitch;

    //ToDo: Convert R8G8B8A8 to A8R8G8B8

    // write into buffer
    memcpy(lockInputBufferParams.bufferDataPtr, mappedResource.pData, m_height * mappedResource.RowPitch);

    // unlock input buffer
    nvStatus = m_pEncodeAPI->nvEncUnlockInputBuffer(m_pEncoder, m_pEncoderInputBuffer);

    return nvStatus;
}

::编码框架

NVENCSTATUS RenderManager::EncodeFrame()
{
    NVENCSTATUS nvStatus = NV_ENC_SUCCESS;

    int8_t* qpDeltaMapArray = NULL;
    unsigned int qpDeltaMapArraySize = 0;

    NV_ENC_PIC_PARAMS encPicParams = {};
    encPicParams.version = NV_ENC_PIC_PARAMS_VER;
    encPicParams.inputWidth = m_width;
    encPicParams.inputHeight = m_height;
    encPicParams.inputBuffer = m_pEncoderInputBuffer;
    encPicParams.outputBitstream = m_pEncoderOutputBuffer;
    encPicParams.bufferFmt = NV_ENC_BUFFER_FORMAT_ARGB;
    encPicParams.pictureStruct = NV_ENC_PIC_STRUCT_FRAME;
    encPicParams.qpDeltaMap = qpDeltaMapArray;
    encPicParams.qpDeltaMapSize = qpDeltaMapArraySize;

    nvStatus = m_pEncodeAPI->nvEncEncodePicture(m_pEncoder, &encPicParams);

    return nvStatus;
}

::从输出缓冲区读取

NVENCSTATUS RenderManager::ReadOutputBuffer()
{
    NVENCSTATUS nvStatus = NV_ENC_SUCCESS;

    // lock output buffer
    NV_ENC_LOCK_BITSTREAM lockBitstreamBufferParams = {};
    lockBitstreamBufferParams.version = NV_ENC_LOCK_BITSTREAM_VER;
    lockBitstreamBufferParams.doNotWait = 0;
    lockBitstreamBufferParams.outputBitstream = m_pEncoderOutputBuffer;

    nvStatus = m_pEncodeAPI->nvEncLockBitstream(m_pEncoder, &lockBitstreamBufferParams);
    if (nvStatus != NV_ENC_SUCCESS) return nvStatus;

    void* pData = lockBitstreamBufferParams.bitstreamBufferPtr;
    unsigned int size = lockBitstreamBufferParams.bitstreamSizeInBytes;

    // read from buffer
    PlatformManager::SaveToFile("TEST", static_cast<char*>(pData), size);

    // unlock output buffer
    nvStatus = m_pEncodeAPI->nvEncUnlockBitstream(m_pEncoder, m_pEncoderOutputBuffer);

    return nvStatus;
}

m_width和m_height始终为1280和720.

为什么图像尺寸错误以及如何用多个帧填充输入缓冲区?

非常感谢你的帮助,

亚历山大

0 个答案:

没有答案