You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
786 lines
27 KiB
786 lines
27 KiB
// This file is part of OpenCV project. |
|
// It is subject to the license terms in the LICENSE file found in the top-level directory |
|
// of this distribution and at http://opencv.org/license.html. |
|
#include "precomp.hpp" |
|
|
|
#if defined(HAVE_NVCUVENC) |
|
#include "NvEncoder.h" |
|
|
|
namespace cv { namespace cudacodec { |
|
#ifndef _WIN32 |
|
#include <cstring> |
|
static inline bool operator==(const GUID& guid1, const GUID& guid2) { |
|
return !memcmp(&guid1, &guid2, sizeof(GUID)); |
|
} |
|
|
|
static inline bool operator!=(const GUID& guid1, const GUID& guid2) { |
|
return !(guid1 == guid2); |
|
} |
|
#endif |
|
|
|
NvEncoder::NvEncoder(NV_ENC_DEVICE_TYPE eDeviceType, void* pDevice, uint32_t nWidth, uint32_t nHeight, NV_ENC_BUFFER_FORMAT eBufferFormat, |
|
uint32_t nExtraOutputDelay) : |
|
m_hEncoder(nullptr), |
|
m_pDevice(pDevice), |
|
m_eDeviceType(eDeviceType), |
|
m_nWidth(nWidth), |
|
m_nHeight(nHeight), |
|
m_nMaxEncodeWidth(nWidth), |
|
m_nMaxEncodeHeight(nHeight), |
|
m_eBufferFormat(eBufferFormat), |
|
m_nExtraOutputDelay(nExtraOutputDelay) |
|
{ |
|
LoadNvEncApi(); |
|
|
|
if (!m_nvenc.nvEncOpenEncodeSession) |
|
{ |
|
m_nEncoderBuffer = 0; |
|
NVENC_THROW_ERROR("EncodeAPI not found", NV_ENC_ERR_NO_ENCODE_DEVICE); |
|
} |
|
|
|
NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS encodeSessionExParams = {}; |
|
encodeSessionExParams.version = NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER; |
|
encodeSessionExParams.device = m_pDevice; |
|
encodeSessionExParams.deviceType = m_eDeviceType; |
|
encodeSessionExParams.apiVersion = NVENCAPI_VERSION; |
|
void* hEncoder = NULL; |
|
NVENC_API_CALL(m_nvenc.nvEncOpenEncodeSessionEx(&encodeSessionExParams, &hEncoder)); |
|
m_hEncoder = hEncoder; |
|
} |
|
|
|
void NvEncoder::LoadNvEncApi() |
|
{ |
|
|
|
uint32_t version = 0; |
|
uint32_t currentVersion = (NVENCAPI_MAJOR_VERSION << 4) | NVENCAPI_MINOR_VERSION; |
|
NVENC_API_CALL(NvEncodeAPIGetMaxSupportedVersion(&version)); |
|
if (currentVersion > version) |
|
{ |
|
NVENC_THROW_ERROR("Current Driver Version does not support this NvEncodeAPI version, please upgrade driver", NV_ENC_ERR_INVALID_VERSION); |
|
} |
|
|
|
m_nvenc = {}; |
|
m_nvenc.version = NV_ENCODE_API_FUNCTION_LIST_VER; |
|
NVENC_API_CALL(NvEncodeAPICreateInstance(&m_nvenc)); |
|
} |
|
|
|
NvEncoder::~NvEncoder() |
|
{ |
|
DestroyHWEncoder(); |
|
} |
|
|
|
void NvEncoder::CreateDefaultEncoderParams(NV_ENC_INITIALIZE_PARAMS* pIntializeParams, GUID codecGuid, GUID presetGuid, NV_ENC_TUNING_INFO tuningInfo) |
|
{ |
|
if (!m_hEncoder) |
|
{ |
|
NVENC_THROW_ERROR("Encoder Initialization failed", NV_ENC_ERR_NO_ENCODE_DEVICE); |
|
return; |
|
} |
|
|
|
if (pIntializeParams == nullptr || pIntializeParams->encodeConfig == nullptr) |
|
{ |
|
NVENC_THROW_ERROR("pInitializeParams and pInitializeParams->encodeConfig can't be NULL", NV_ENC_ERR_INVALID_PTR); |
|
} |
|
|
|
memset(pIntializeParams->encodeConfig, 0, sizeof(NV_ENC_CONFIG)); |
|
auto pEncodeConfig = pIntializeParams->encodeConfig; |
|
memset(pIntializeParams, 0, sizeof(NV_ENC_INITIALIZE_PARAMS)); |
|
pIntializeParams->encodeConfig = pEncodeConfig; |
|
|
|
|
|
pIntializeParams->encodeConfig->version = NV_ENC_CONFIG_VER; |
|
pIntializeParams->version = NV_ENC_INITIALIZE_PARAMS_VER; |
|
|
|
pIntializeParams->encodeGUID = codecGuid; |
|
pIntializeParams->presetGUID = presetGuid; |
|
pIntializeParams->encodeWidth = m_nWidth; |
|
pIntializeParams->encodeHeight = m_nHeight; |
|
pIntializeParams->darWidth = m_nWidth; |
|
pIntializeParams->darHeight = m_nHeight; |
|
pIntializeParams->frameRateNum = 30; |
|
pIntializeParams->frameRateDen = 1; |
|
pIntializeParams->enablePTD = 1; |
|
pIntializeParams->reportSliceOffsets = 0; |
|
pIntializeParams->enableSubFrameWrite = 0; |
|
pIntializeParams->maxEncodeWidth = m_nWidth; |
|
pIntializeParams->maxEncodeHeight = m_nHeight; |
|
pIntializeParams->enableMEOnlyMode = false; |
|
pIntializeParams->enableOutputInVidmem = false; |
|
#if defined(_WIN32) |
|
pIntializeParams->enableEncodeAsync = GetCapabilityValue(codecGuid, NV_ENC_CAPS_ASYNC_ENCODE_SUPPORT); |
|
#endif |
|
pIntializeParams->tuningInfo = tuningInfo; |
|
NV_ENC_PRESET_CONFIG presetConfig = {}; |
|
presetConfig.version = NV_ENC_PRESET_CONFIG_VER; |
|
presetConfig.presetCfg.version = NV_ENC_CONFIG_VER; |
|
m_nvenc.nvEncGetEncodePresetConfigEx(m_hEncoder, codecGuid, presetGuid, tuningInfo, &presetConfig); |
|
memcpy(pIntializeParams->encodeConfig, &presetConfig.presetCfg, sizeof(NV_ENC_CONFIG)); |
|
|
|
if (pIntializeParams->encodeGUID == NV_ENC_CODEC_H264_GUID) |
|
{ |
|
if (m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444 || m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444_10BIT) |
|
{ |
|
pIntializeParams->encodeConfig->encodeCodecConfig.h264Config.chromaFormatIDC = 3; |
|
} |
|
pIntializeParams->encodeConfig->encodeCodecConfig.h264Config.idrPeriod = pIntializeParams->encodeConfig->gopLength; |
|
} |
|
else if (pIntializeParams->encodeGUID == NV_ENC_CODEC_HEVC_GUID) |
|
{ |
|
pIntializeParams->encodeConfig->encodeCodecConfig.hevcConfig.pixelBitDepthMinus8 = |
|
(m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV420_10BIT || m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444_10BIT) ? 2 : 0; |
|
if (m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444 || m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444_10BIT) |
|
{ |
|
pIntializeParams->encodeConfig->encodeCodecConfig.hevcConfig.chromaFormatIDC = 3; |
|
} |
|
pIntializeParams->encodeConfig->encodeCodecConfig.hevcConfig.idrPeriod = pIntializeParams->encodeConfig->gopLength; |
|
} |
|
|
|
return; |
|
} |
|
|
|
void NvEncoder::CreateEncoder(const NV_ENC_INITIALIZE_PARAMS* pEncoderParams) |
|
{ |
|
if (!m_hEncoder) |
|
{ |
|
NVENC_THROW_ERROR("Encoder Initialization failed", NV_ENC_ERR_NO_ENCODE_DEVICE); |
|
} |
|
|
|
if (!pEncoderParams) |
|
{ |
|
NVENC_THROW_ERROR("Invalid NV_ENC_INITIALIZE_PARAMS ptr", NV_ENC_ERR_INVALID_PTR); |
|
} |
|
|
|
if (pEncoderParams->encodeWidth == 0 || pEncoderParams->encodeHeight == 0) |
|
{ |
|
NVENC_THROW_ERROR("Invalid encoder width and height", NV_ENC_ERR_INVALID_PARAM); |
|
} |
|
|
|
if (pEncoderParams->encodeGUID != NV_ENC_CODEC_H264_GUID && pEncoderParams->encodeGUID != NV_ENC_CODEC_HEVC_GUID) |
|
{ |
|
NVENC_THROW_ERROR("Invalid codec guid", NV_ENC_ERR_INVALID_PARAM); |
|
} |
|
|
|
if (pEncoderParams->encodeGUID == NV_ENC_CODEC_H264_GUID) |
|
{ |
|
if (m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV420_10BIT || m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444_10BIT) |
|
{ |
|
NVENC_THROW_ERROR("10-bit format isn't supported by H264 encoder", NV_ENC_ERR_INVALID_PARAM); |
|
} |
|
} |
|
|
|
// set other necessary params if not set yet |
|
if (pEncoderParams->encodeGUID == NV_ENC_CODEC_H264_GUID) |
|
{ |
|
if ((m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444) && |
|
(pEncoderParams->encodeConfig->encodeCodecConfig.h264Config.chromaFormatIDC != 3)) |
|
{ |
|
NVENC_THROW_ERROR("Invalid ChromaFormatIDC", NV_ENC_ERR_INVALID_PARAM); |
|
} |
|
} |
|
|
|
if (pEncoderParams->encodeGUID == NV_ENC_CODEC_HEVC_GUID) |
|
{ |
|
bool yuv10BitFormat = (m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV420_10BIT || m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444_10BIT) ? true : false; |
|
if (yuv10BitFormat && pEncoderParams->encodeConfig->encodeCodecConfig.hevcConfig.pixelBitDepthMinus8 != 2) |
|
{ |
|
NVENC_THROW_ERROR("Invalid PixelBitdepth", NV_ENC_ERR_INVALID_PARAM); |
|
} |
|
|
|
if ((m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444 || m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444_10BIT) && |
|
(pEncoderParams->encodeConfig->encodeCodecConfig.hevcConfig.chromaFormatIDC != 3)) |
|
{ |
|
NVENC_THROW_ERROR("Invalid ChromaFormatIDC", NV_ENC_ERR_INVALID_PARAM); |
|
} |
|
} |
|
|
|
memcpy(&m_initializeParams, pEncoderParams, sizeof(m_initializeParams)); |
|
m_initializeParams.version = NV_ENC_INITIALIZE_PARAMS_VER; |
|
|
|
if (pEncoderParams->encodeConfig) |
|
{ |
|
memcpy(&m_encodeConfig, pEncoderParams->encodeConfig, sizeof(m_encodeConfig)); |
|
m_encodeConfig.version = NV_ENC_CONFIG_VER; |
|
} |
|
else |
|
{ |
|
NV_ENC_PRESET_CONFIG presetConfig = {}; |
|
presetConfig.version = NV_ENC_PRESET_CONFIG_VER; |
|
presetConfig.presetCfg.version = NV_ENC_CONFIG_VER; |
|
m_nvenc.nvEncGetEncodePresetConfigEx(m_hEncoder, pEncoderParams->encodeGUID, pEncoderParams->presetGUID, pEncoderParams->tuningInfo, &presetConfig); |
|
memcpy(&m_encodeConfig, &presetConfig.presetCfg, sizeof(NV_ENC_CONFIG)); |
|
} |
|
m_initializeParams.encodeConfig = &m_encodeConfig; |
|
NVENC_API_CALL(m_nvenc.nvEncInitializeEncoder(m_hEncoder, &m_initializeParams)); |
|
m_bEncoderInitialized = true; |
|
m_nWidth = m_initializeParams.encodeWidth; |
|
m_nHeight = m_initializeParams.encodeHeight; |
|
m_nMaxEncodeWidth = m_initializeParams.maxEncodeWidth; |
|
m_nMaxEncodeHeight = m_initializeParams.maxEncodeHeight; |
|
|
|
m_nEncoderBuffer = m_encodeConfig.frameIntervalP + m_encodeConfig.rcParams.lookaheadDepth + m_nExtraOutputDelay; |
|
m_nOutputDelay = m_nEncoderBuffer - 1; |
|
|
|
m_vpCompletionEvent.resize(m_nEncoderBuffer, nullptr); |
|
|
|
#if defined(_WIN32) |
|
for (uint32_t i = 0; i < m_vpCompletionEvent.size(); i++) |
|
{ |
|
m_vpCompletionEvent[i] = CreateEvent(NULL, FALSE, FALSE, NULL); |
|
NV_ENC_EVENT_PARAMS eventParams = { NV_ENC_EVENT_PARAMS_VER }; |
|
eventParams.completionEvent = m_vpCompletionEvent[i]; |
|
m_nvenc.nvEncRegisterAsyncEvent(m_hEncoder, &eventParams); |
|
} |
|
#endif |
|
|
|
m_vMappedInputBuffers.resize(m_nEncoderBuffer, nullptr); |
|
m_vBitstreamOutputBuffer.resize(m_nEncoderBuffer, nullptr); |
|
InitializeBitstreamBuffer(); |
|
AllocateInputBuffers(m_nEncoderBuffer); |
|
} |
|
|
|
void NvEncoder::DestroyEncoder() |
|
{ |
|
if (!m_hEncoder) |
|
{ |
|
return; |
|
} |
|
|
|
ReleaseInputBuffers(); |
|
|
|
DestroyHWEncoder(); |
|
} |
|
|
|
void NvEncoder::DestroyHWEncoder() |
|
{ |
|
if (!m_hEncoder) |
|
{ |
|
return; |
|
} |
|
|
|
#if defined(_WIN32) |
|
for (uint32_t i = 0; i < m_vpCompletionEvent.size(); i++) |
|
{ |
|
if (m_vpCompletionEvent[i]) |
|
{ |
|
NV_ENC_EVENT_PARAMS eventParams = { NV_ENC_EVENT_PARAMS_VER }; |
|
eventParams.completionEvent = m_vpCompletionEvent[i]; |
|
m_nvenc.nvEncUnregisterAsyncEvent(m_hEncoder, &eventParams); |
|
CloseHandle(m_vpCompletionEvent[i]); |
|
} |
|
} |
|
m_vpCompletionEvent.clear(); |
|
#endif |
|
|
|
DestroyBitstreamBuffer(); |
|
|
|
m_nvenc.nvEncDestroyEncoder(m_hEncoder); |
|
|
|
m_hEncoder = nullptr; |
|
|
|
m_bEncoderInitialized = false; |
|
} |
|
|
|
const NvEncInputFrame* NvEncoder::GetNextInputFrame() |
|
{ |
|
int i = m_iToSend % m_nEncoderBuffer; |
|
return &m_vInputFrames[i]; |
|
} |
|
|
|
void NvEncoder::MapResources(uint32_t bfrIdx) |
|
{ |
|
NV_ENC_MAP_INPUT_RESOURCE mapInputResource = {}; |
|
mapInputResource.version = NV_ENC_MAP_INPUT_RESOURCE_VER; |
|
mapInputResource.registeredResource = m_vRegisteredResources[bfrIdx]; |
|
NVENC_API_CALL(m_nvenc.nvEncMapInputResource(m_hEncoder, &mapInputResource)); |
|
m_vMappedInputBuffers[bfrIdx] = mapInputResource.mappedResource; |
|
} |
|
|
|
void NvEncoder::EncodeFrame(std::vector<std::vector<uint8_t>>& vPacket, NV_ENC_PIC_PARAMS* pPicParams) |
|
{ |
|
vPacket.clear(); |
|
if (!IsHWEncoderInitialized()) |
|
{ |
|
NVENC_THROW_ERROR("Encoder device not found", NV_ENC_ERR_NO_ENCODE_DEVICE); |
|
} |
|
|
|
int bfrIdx = m_iToSend % m_nEncoderBuffer; |
|
|
|
MapResources(bfrIdx); |
|
|
|
NVENCSTATUS nvStatus = DoEncode(m_vMappedInputBuffers[bfrIdx], m_vBitstreamOutputBuffer[bfrIdx], pPicParams); |
|
|
|
if (nvStatus == NV_ENC_SUCCESS || nvStatus == NV_ENC_ERR_NEED_MORE_INPUT) |
|
{ |
|
m_iToSend++; |
|
GetEncodedPacket(m_vBitstreamOutputBuffer, vPacket, true); |
|
} |
|
else |
|
{ |
|
NVENC_THROW_ERROR("nvEncEncodePicture API failed", nvStatus); |
|
} |
|
} |
|
|
|
void NvEncoder::GetSequenceParams(std::vector<uint8_t>& seqParams) |
|
{ |
|
uint8_t spsppsData[1024]; // Assume maximum spspps data is 1KB or less |
|
memset(spsppsData, 0, sizeof(spsppsData)); |
|
NV_ENC_SEQUENCE_PARAM_PAYLOAD payload = {}; |
|
payload.version = NV_ENC_SEQUENCE_PARAM_PAYLOAD_VER; |
|
uint32_t spsppsSize = 0; |
|
|
|
payload.spsppsBuffer = spsppsData; |
|
payload.inBufferSize = sizeof(spsppsData); |
|
payload.outSPSPPSPayloadSize = &spsppsSize; |
|
NVENC_API_CALL(m_nvenc.nvEncGetSequenceParams(m_hEncoder, &payload)); |
|
seqParams.clear(); |
|
seqParams.insert(seqParams.end(), &spsppsData[0], &spsppsData[spsppsSize]); |
|
} |
|
|
|
NVENCSTATUS NvEncoder::DoEncode(NV_ENC_INPUT_PTR inputBuffer, NV_ENC_OUTPUT_PTR outputBuffer, NV_ENC_PIC_PARAMS* pPicParams) |
|
{ |
|
NV_ENC_PIC_PARAMS picParams = {}; |
|
if (pPicParams) |
|
{ |
|
picParams = *pPicParams; |
|
} |
|
picParams.version = NV_ENC_PIC_PARAMS_VER; |
|
picParams.pictureStruct = NV_ENC_PIC_STRUCT_FRAME; |
|
picParams.inputBuffer = inputBuffer; |
|
picParams.bufferFmt = GetPixelFormat(); |
|
picParams.inputWidth = GetEncodeWidth(); |
|
picParams.inputHeight = GetEncodeHeight(); |
|
picParams.outputBitstream = outputBuffer; |
|
picParams.completionEvent = GetCompletionEvent(m_iToSend % m_nEncoderBuffer); |
|
NVENCSTATUS nvStatus = m_nvenc.nvEncEncodePicture(m_hEncoder, &picParams); |
|
|
|
return nvStatus; |
|
} |
|
|
|
void NvEncoder::SendEOS() |
|
{ |
|
NV_ENC_PIC_PARAMS picParams = {}; |
|
picParams.version = NV_ENC_PIC_PARAMS_VER; |
|
|
|
picParams.encodePicFlags = NV_ENC_PIC_FLAG_EOS; |
|
picParams.completionEvent = GetCompletionEvent(m_iToSend % m_nEncoderBuffer); |
|
NVENC_API_CALL(m_nvenc.nvEncEncodePicture(m_hEncoder, &picParams)); |
|
} |
|
|
|
void NvEncoder::EndEncode(std::vector<std::vector<uint8_t>>& vPacket) |
|
{ |
|
vPacket.clear(); |
|
if (!IsHWEncoderInitialized()) |
|
{ |
|
NVENC_THROW_ERROR("Encoder device not initialized", NV_ENC_ERR_ENCODER_NOT_INITIALIZED); |
|
} |
|
|
|
SendEOS(); |
|
|
|
GetEncodedPacket(m_vBitstreamOutputBuffer, vPacket, false); |
|
} |
|
|
|
void NvEncoder::GetEncodedPacket(std::vector<NV_ENC_OUTPUT_PTR>& vOutputBuffer, std::vector<std::vector<uint8_t>>& vPacket, bool bOutputDelay) |
|
{ |
|
unsigned i = 0; |
|
int iEnd = bOutputDelay ? m_iToSend - m_nOutputDelay : m_iToSend; |
|
for (; m_iGot < iEnd; m_iGot++) |
|
{ |
|
WaitForCompletionEvent(m_iGot % m_nEncoderBuffer); |
|
NV_ENC_LOCK_BITSTREAM lockBitstreamData = {}; |
|
lockBitstreamData.version = NV_ENC_LOCK_BITSTREAM_VER; |
|
lockBitstreamData.outputBitstream = vOutputBuffer[m_iGot % m_nEncoderBuffer]; |
|
lockBitstreamData.doNotWait = false; |
|
NVENC_API_CALL(m_nvenc.nvEncLockBitstream(m_hEncoder, &lockBitstreamData)); |
|
|
|
uint8_t* pData = (uint8_t*)lockBitstreamData.bitstreamBufferPtr; |
|
if (vPacket.size() < i + 1) |
|
{ |
|
vPacket.push_back(std::vector<uint8_t>()); |
|
} |
|
vPacket[i].clear(); |
|
vPacket[i].insert(vPacket[i].end(), &pData[0], &pData[lockBitstreamData.bitstreamSizeInBytes]); |
|
i++; |
|
|
|
NVENC_API_CALL(m_nvenc.nvEncUnlockBitstream(m_hEncoder, lockBitstreamData.outputBitstream)); |
|
|
|
if (m_vMappedInputBuffers[m_iGot % m_nEncoderBuffer]) |
|
{ |
|
NVENC_API_CALL(m_nvenc.nvEncUnmapInputResource(m_hEncoder, m_vMappedInputBuffers[m_iGot % m_nEncoderBuffer])); |
|
m_vMappedInputBuffers[m_iGot % m_nEncoderBuffer] = nullptr; |
|
} |
|
} |
|
} |
|
|
|
bool NvEncoder::Reconfigure(const NV_ENC_RECONFIGURE_PARAMS* pReconfigureParams) |
|
{ |
|
NVENC_API_CALL(m_nvenc.nvEncReconfigureEncoder(m_hEncoder, const_cast<NV_ENC_RECONFIGURE_PARAMS*>(pReconfigureParams))); |
|
|
|
memcpy(&m_initializeParams, &(pReconfigureParams->reInitEncodeParams), sizeof(m_initializeParams)); |
|
if (pReconfigureParams->reInitEncodeParams.encodeConfig) |
|
{ |
|
memcpy(&m_encodeConfig, pReconfigureParams->reInitEncodeParams.encodeConfig, sizeof(m_encodeConfig)); |
|
} |
|
|
|
m_nWidth = m_initializeParams.encodeWidth; |
|
m_nHeight = m_initializeParams.encodeHeight; |
|
m_nMaxEncodeWidth = m_initializeParams.maxEncodeWidth; |
|
m_nMaxEncodeHeight = m_initializeParams.maxEncodeHeight; |
|
|
|
return true; |
|
} |
|
|
|
NV_ENC_REGISTERED_PTR NvEncoder::RegisterResource(void* pBuffer, NV_ENC_INPUT_RESOURCE_TYPE eResourceType, |
|
int width, int height, int pitch, NV_ENC_BUFFER_FORMAT bufferFormat, NV_ENC_BUFFER_USAGE bufferUsage, |
|
NV_ENC_FENCE_POINT_D3D12* pInputFencePoint, NV_ENC_FENCE_POINT_D3D12* pOutputFencePoint) |
|
{ |
|
NV_ENC_REGISTER_RESOURCE registerResource = {}; |
|
registerResource.version = NV_ENC_REGISTER_RESOURCE_VER; |
|
registerResource.resourceType = eResourceType; |
|
registerResource.resourceToRegister = pBuffer; |
|
registerResource.width = width; |
|
registerResource.height = height; |
|
registerResource.pitch = pitch; |
|
registerResource.bufferFormat = bufferFormat; |
|
registerResource.bufferUsage = bufferUsage; |
|
registerResource.pInputFencePoint = pInputFencePoint; |
|
registerResource.pOutputFencePoint = pOutputFencePoint; |
|
NVENC_API_CALL(m_nvenc.nvEncRegisterResource(m_hEncoder, ®isterResource)); |
|
|
|
return registerResource.registeredResource; |
|
} |
|
|
|
void NvEncoder::RegisterInputResources(std::vector<void*> inputframes, NV_ENC_INPUT_RESOURCE_TYPE eResourceType, |
|
int width, int height, int pitch, NV_ENC_BUFFER_FORMAT bufferFormat, bool bReferenceFrame) |
|
{ |
|
for (uint32_t i = 0; i < inputframes.size(); ++i) |
|
{ |
|
NV_ENC_REGISTERED_PTR registeredPtr = RegisterResource(inputframes[i], eResourceType, width, height, pitch, bufferFormat, NV_ENC_INPUT_IMAGE); |
|
|
|
std::vector<uint32_t> _chromaOffsets; |
|
NvEncoder::GetChromaSubPlaneOffsets(bufferFormat, pitch, height, _chromaOffsets); |
|
NvEncInputFrame inputframe = {}; |
|
inputframe.inputPtr = (void*)inputframes[i]; |
|
inputframe.chromaOffsets[0] = 0; |
|
inputframe.chromaOffsets[1] = 0; |
|
for (uint32_t ch = 0; ch < _chromaOffsets.size(); ch++) |
|
{ |
|
inputframe.chromaOffsets[ch] = _chromaOffsets[ch]; |
|
} |
|
inputframe.numChromaPlanes = NvEncoder::GetNumChromaPlanes(bufferFormat); |
|
inputframe.pitch = pitch; |
|
inputframe.chromaPitch = NvEncoder::GetChromaPitch(bufferFormat, pitch); |
|
inputframe.bufferFormat = bufferFormat; |
|
inputframe.resourceType = eResourceType; |
|
|
|
if (bReferenceFrame) |
|
{ |
|
m_vRegisteredResourcesForReference.push_back(registeredPtr); |
|
m_vReferenceFrames.push_back(inputframe); |
|
} |
|
else |
|
{ |
|
m_vRegisteredResources.push_back(registeredPtr); |
|
m_vInputFrames.push_back(inputframe); |
|
} |
|
} |
|
} |
|
|
|
void NvEncoder::FlushEncoder() |
|
{ |
|
try |
|
{ |
|
std::vector<std::vector<uint8_t>> vPacket; |
|
EndEncode(vPacket); |
|
} |
|
catch (...) |
|
{ |
|
|
|
} |
|
} |
|
|
|
void NvEncoder::UnregisterInputResources() |
|
{ |
|
FlushEncoder(); |
|
|
|
m_vMappedRefBuffers.clear(); |
|
|
|
for (uint32_t i = 0; i < m_vMappedInputBuffers.size(); ++i) |
|
{ |
|
if (m_vMappedInputBuffers[i]) |
|
{ |
|
m_nvenc.nvEncUnmapInputResource(m_hEncoder, m_vMappedInputBuffers[i]); |
|
} |
|
} |
|
m_vMappedInputBuffers.clear(); |
|
|
|
for (uint32_t i = 0; i < m_vRegisteredResources.size(); ++i) |
|
{ |
|
if (m_vRegisteredResources[i]) |
|
{ |
|
m_nvenc.nvEncUnregisterResource(m_hEncoder, m_vRegisteredResources[i]); |
|
} |
|
} |
|
m_vRegisteredResources.clear(); |
|
|
|
|
|
for (uint32_t i = 0; i < m_vRegisteredResourcesForReference.size(); ++i) |
|
{ |
|
if (m_vRegisteredResourcesForReference[i]) |
|
{ |
|
m_nvenc.nvEncUnregisterResource(m_hEncoder, m_vRegisteredResourcesForReference[i]); |
|
} |
|
} |
|
m_vRegisteredResourcesForReference.clear(); |
|
|
|
} |
|
|
|
|
|
void NvEncoder::WaitForCompletionEvent(int iEvent) |
|
{ |
|
#if defined(_WIN32) |
|
// Check if we are in async mode. If not, don't wait for event; |
|
NV_ENC_CONFIG sEncodeConfig = { 0 }; |
|
NV_ENC_INITIALIZE_PARAMS sInitializeParams = { 0 }; |
|
sInitializeParams.encodeConfig = &sEncodeConfig; |
|
GetInitializeParams(&sInitializeParams); |
|
|
|
if (0U == sInitializeParams.enableEncodeAsync) |
|
{ |
|
return; |
|
} |
|
#ifdef DEBUG |
|
WaitForSingleObject(m_vpCompletionEvent[iEvent], INFINITE); |
|
#else |
|
// wait for 20s which is infinite on terms of gpu time |
|
if (WaitForSingleObject(m_vpCompletionEvent[iEvent], 20000) == WAIT_FAILED) |
|
{ |
|
NVENC_THROW_ERROR("Failed to encode frame", NV_ENC_ERR_GENERIC); |
|
} |
|
#endif |
|
#endif |
|
} |
|
|
|
uint32_t NvEncoder::GetWidthInBytes(const NV_ENC_BUFFER_FORMAT bufferFormat, const uint32_t width) |
|
{ |
|
switch (bufferFormat) { |
|
case NV_ENC_BUFFER_FORMAT_NV12: |
|
case NV_ENC_BUFFER_FORMAT_YV12: |
|
case NV_ENC_BUFFER_FORMAT_IYUV: |
|
case NV_ENC_BUFFER_FORMAT_YUV444: |
|
return width; |
|
case NV_ENC_BUFFER_FORMAT_YUV420_10BIT: |
|
case NV_ENC_BUFFER_FORMAT_YUV444_10BIT: |
|
return width * 2; |
|
case NV_ENC_BUFFER_FORMAT_ARGB: |
|
case NV_ENC_BUFFER_FORMAT_ARGB10: |
|
case NV_ENC_BUFFER_FORMAT_AYUV: |
|
case NV_ENC_BUFFER_FORMAT_ABGR: |
|
case NV_ENC_BUFFER_FORMAT_ABGR10: |
|
return width * 4; |
|
default: |
|
NVENC_THROW_ERROR("Invalid Buffer format", NV_ENC_ERR_INVALID_PARAM); |
|
} |
|
} |
|
|
|
uint32_t NvEncoder::GetNumChromaPlanes(const NV_ENC_BUFFER_FORMAT bufferFormat) |
|
{ |
|
switch (bufferFormat) |
|
{ |
|
case NV_ENC_BUFFER_FORMAT_NV12: |
|
case NV_ENC_BUFFER_FORMAT_YUV420_10BIT: |
|
return 1; |
|
case NV_ENC_BUFFER_FORMAT_YV12: |
|
case NV_ENC_BUFFER_FORMAT_IYUV: |
|
case NV_ENC_BUFFER_FORMAT_YUV444: |
|
case NV_ENC_BUFFER_FORMAT_YUV444_10BIT: |
|
return 2; |
|
case NV_ENC_BUFFER_FORMAT_ARGB: |
|
case NV_ENC_BUFFER_FORMAT_ARGB10: |
|
case NV_ENC_BUFFER_FORMAT_AYUV: |
|
case NV_ENC_BUFFER_FORMAT_ABGR: |
|
case NV_ENC_BUFFER_FORMAT_ABGR10: |
|
return 0; |
|
default: |
|
NVENC_THROW_ERROR("Invalid Buffer format", NV_ENC_ERR_INVALID_PARAM); |
|
} |
|
} |
|
|
|
uint32_t NvEncoder::GetChromaPitch(const NV_ENC_BUFFER_FORMAT bufferFormat, const uint32_t lumaPitch) |
|
{ |
|
switch (bufferFormat) |
|
{ |
|
case NV_ENC_BUFFER_FORMAT_NV12: |
|
case NV_ENC_BUFFER_FORMAT_YUV420_10BIT: |
|
case NV_ENC_BUFFER_FORMAT_YUV444: |
|
case NV_ENC_BUFFER_FORMAT_YUV444_10BIT: |
|
return lumaPitch; |
|
case NV_ENC_BUFFER_FORMAT_YV12: |
|
case NV_ENC_BUFFER_FORMAT_IYUV: |
|
return (lumaPitch + 1) / 2; |
|
case NV_ENC_BUFFER_FORMAT_ARGB: |
|
case NV_ENC_BUFFER_FORMAT_ARGB10: |
|
case NV_ENC_BUFFER_FORMAT_AYUV: |
|
case NV_ENC_BUFFER_FORMAT_ABGR: |
|
case NV_ENC_BUFFER_FORMAT_ABGR10: |
|
return 0; |
|
default: |
|
NVENC_THROW_ERROR("Invalid Buffer format", NV_ENC_ERR_INVALID_PARAM); |
|
} |
|
} |
|
|
|
void NvEncoder::GetChromaSubPlaneOffsets(const NV_ENC_BUFFER_FORMAT bufferFormat, const uint32_t pitch, const uint32_t height, std::vector<uint32_t>& chromaOffsets) |
|
{ |
|
chromaOffsets.clear(); |
|
switch (bufferFormat) |
|
{ |
|
case NV_ENC_BUFFER_FORMAT_NV12: |
|
case NV_ENC_BUFFER_FORMAT_YUV420_10BIT: |
|
chromaOffsets.push_back(pitch * height); |
|
return; |
|
case NV_ENC_BUFFER_FORMAT_YV12: |
|
case NV_ENC_BUFFER_FORMAT_IYUV: |
|
chromaOffsets.push_back(pitch * height); |
|
chromaOffsets.push_back(chromaOffsets[0] + (NvEncoder::GetChromaPitch(bufferFormat, pitch) * GetChromaHeight(bufferFormat, height))); |
|
return; |
|
case NV_ENC_BUFFER_FORMAT_YUV444: |
|
case NV_ENC_BUFFER_FORMAT_YUV444_10BIT: |
|
chromaOffsets.push_back(pitch * height); |
|
chromaOffsets.push_back(chromaOffsets[0] + (pitch * height)); |
|
return; |
|
case NV_ENC_BUFFER_FORMAT_ARGB: |
|
case NV_ENC_BUFFER_FORMAT_ARGB10: |
|
case NV_ENC_BUFFER_FORMAT_AYUV: |
|
case NV_ENC_BUFFER_FORMAT_ABGR: |
|
case NV_ENC_BUFFER_FORMAT_ABGR10: |
|
return; |
|
default: |
|
NVENC_THROW_ERROR("Invalid Buffer format", NV_ENC_ERR_INVALID_PARAM); |
|
} |
|
} |
|
|
|
uint32_t NvEncoder::GetChromaHeight(const NV_ENC_BUFFER_FORMAT bufferFormat, const uint32_t lumaHeight) |
|
{ |
|
switch (bufferFormat) |
|
{ |
|
case NV_ENC_BUFFER_FORMAT_YV12: |
|
case NV_ENC_BUFFER_FORMAT_IYUV: |
|
case NV_ENC_BUFFER_FORMAT_NV12: |
|
case NV_ENC_BUFFER_FORMAT_YUV420_10BIT: |
|
return (lumaHeight + 1) / 2; |
|
case NV_ENC_BUFFER_FORMAT_YUV444: |
|
case NV_ENC_BUFFER_FORMAT_YUV444_10BIT: |
|
return lumaHeight; |
|
case NV_ENC_BUFFER_FORMAT_ARGB: |
|
case NV_ENC_BUFFER_FORMAT_ARGB10: |
|
case NV_ENC_BUFFER_FORMAT_AYUV: |
|
case NV_ENC_BUFFER_FORMAT_ABGR: |
|
case NV_ENC_BUFFER_FORMAT_ABGR10: |
|
return 0; |
|
default: |
|
NVENC_THROW_ERROR("Invalid Buffer format", NV_ENC_ERR_INVALID_PARAM); |
|
} |
|
} |
|
|
|
uint32_t NvEncoder::GetChromaWidthInBytes(const NV_ENC_BUFFER_FORMAT bufferFormat, const uint32_t lumaWidth) |
|
{ |
|
switch (bufferFormat) |
|
{ |
|
case NV_ENC_BUFFER_FORMAT_YV12: |
|
case NV_ENC_BUFFER_FORMAT_IYUV: |
|
return (lumaWidth + 1) / 2; |
|
case NV_ENC_BUFFER_FORMAT_NV12: |
|
return lumaWidth; |
|
case NV_ENC_BUFFER_FORMAT_YUV420_10BIT: |
|
return 2 * lumaWidth; |
|
case NV_ENC_BUFFER_FORMAT_YUV444: |
|
return lumaWidth; |
|
case NV_ENC_BUFFER_FORMAT_YUV444_10BIT: |
|
return 2 * lumaWidth; |
|
case NV_ENC_BUFFER_FORMAT_ARGB: |
|
case NV_ENC_BUFFER_FORMAT_ARGB10: |
|
case NV_ENC_BUFFER_FORMAT_AYUV: |
|
case NV_ENC_BUFFER_FORMAT_ABGR: |
|
case NV_ENC_BUFFER_FORMAT_ABGR10: |
|
return 0; |
|
default: |
|
NVENC_THROW_ERROR("Invalid Buffer format", NV_ENC_ERR_INVALID_PARAM); |
|
} |
|
} |
|
|
|
|
|
int NvEncoder::GetCapabilityValue(GUID guidCodec, NV_ENC_CAPS capsToQuery) |
|
{ |
|
if (!m_hEncoder) |
|
{ |
|
return 0; |
|
} |
|
NV_ENC_CAPS_PARAM capsParam = {}; |
|
capsParam.version = NV_ENC_CAPS_PARAM_VER; |
|
capsParam.capsToQuery = capsToQuery; |
|
int v; |
|
m_nvenc.nvEncGetEncodeCaps(m_hEncoder, guidCodec, &capsParam, &v); |
|
return v; |
|
} |
|
|
|
int NvEncoder::GetFrameSize() const |
|
{ |
|
switch (GetPixelFormat()) |
|
{ |
|
case NV_ENC_BUFFER_FORMAT_YV12: |
|
case NV_ENC_BUFFER_FORMAT_IYUV: |
|
case NV_ENC_BUFFER_FORMAT_NV12: |
|
return GetEncodeWidth() * (GetEncodeHeight() + (GetEncodeHeight() + 1) / 2); |
|
case NV_ENC_BUFFER_FORMAT_YUV420_10BIT: |
|
return 2 * GetEncodeWidth() * (GetEncodeHeight() + (GetEncodeHeight() + 1) / 2); |
|
case NV_ENC_BUFFER_FORMAT_YUV444: |
|
return GetEncodeWidth() * GetEncodeHeight() * 3; |
|
case NV_ENC_BUFFER_FORMAT_YUV444_10BIT: |
|
return 2 * GetEncodeWidth() * GetEncodeHeight() * 3; |
|
case NV_ENC_BUFFER_FORMAT_ARGB: |
|
case NV_ENC_BUFFER_FORMAT_ARGB10: |
|
case NV_ENC_BUFFER_FORMAT_AYUV: |
|
case NV_ENC_BUFFER_FORMAT_ABGR: |
|
case NV_ENC_BUFFER_FORMAT_ABGR10: |
|
return 4 * GetEncodeWidth() * GetEncodeHeight(); |
|
default: |
|
NVENC_THROW_ERROR("Invalid Buffer format", NV_ENC_ERR_INVALID_PARAM); |
|
} |
|
} |
|
|
|
void NvEncoder::GetInitializeParams(NV_ENC_INITIALIZE_PARAMS* pInitializeParams) |
|
{ |
|
if (!pInitializeParams || !pInitializeParams->encodeConfig) |
|
{ |
|
NVENC_THROW_ERROR("Both pInitializeParams and pInitializeParams->encodeConfig can't be NULL", NV_ENC_ERR_INVALID_PTR); |
|
} |
|
NV_ENC_CONFIG* pEncodeConfig = pInitializeParams->encodeConfig; |
|
*pEncodeConfig = m_encodeConfig; |
|
*pInitializeParams = m_initializeParams; |
|
pInitializeParams->encodeConfig = pEncodeConfig; |
|
} |
|
|
|
void NvEncoder::InitializeBitstreamBuffer() |
|
{ |
|
for (int i = 0; i < m_nEncoderBuffer; i++) |
|
{ |
|
NV_ENC_CREATE_BITSTREAM_BUFFER createBitstreamBuffer = {}; |
|
createBitstreamBuffer.version = NV_ENC_CREATE_BITSTREAM_BUFFER_VER; |
|
NVENC_API_CALL(m_nvenc.nvEncCreateBitstreamBuffer(m_hEncoder, &createBitstreamBuffer)); |
|
m_vBitstreamOutputBuffer[i] = createBitstreamBuffer.bitstreamBuffer; |
|
} |
|
} |
|
|
|
void NvEncoder::DestroyBitstreamBuffer() |
|
{ |
|
for (uint32_t i = 0; i < m_vBitstreamOutputBuffer.size(); i++) |
|
{ |
|
if (m_vBitstreamOutputBuffer[i]) |
|
{ |
|
m_nvenc.nvEncDestroyBitstreamBuffer(m_hEncoder, m_vBitstreamOutputBuffer[i]); |
|
} |
|
} |
|
|
|
m_vBitstreamOutputBuffer.clear(); |
|
} |
|
}} |
|
#endif |