commit
9d84eaed80
13 changed files with 2277 additions and 1121 deletions
@ -1,10 +0,0 @@ |
|||||||
#ifdef HAVE_OPENCV_CUDACODEC |
|
||||||
|
|
||||||
#include "opencv2/cudacodec.hpp" |
|
||||||
|
|
||||||
typedef cudacodec::EncoderCallBack::PicType EncoderCallBack_PicType; |
|
||||||
|
|
||||||
CV_PY_TO_CLASS(cudacodec::EncoderParams); |
|
||||||
CV_PY_FROM_CLASS(cudacodec::EncoderParams); |
|
||||||
|
|
||||||
#endif |
|
@ -0,0 +1,786 @@ |
|||||||
|
// 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 |
@ -0,0 +1,377 @@ |
|||||||
|
// 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.
|
||||||
|
|
||||||
|
#ifndef OPENCV_NVENCODER_HPP |
||||||
|
#define OPENCV_NVENCODER_HPP |
||||||
|
#include <vector> |
||||||
|
#include "nvEncodeAPI.h" |
||||||
|
#include <stdint.h> |
||||||
|
#include <mutex> |
||||||
|
#include <string> |
||||||
|
#include <iostream> |
||||||
|
#include <sstream> |
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
namespace cv { namespace cudacodec { |
||||||
|
|
||||||
|
#define NVENC_THROW_ERROR( errorStr, errorCode ) \ |
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
cv::String msg = cv::format("%s [Code = %d]", errorStr, errorCode); \
|
||||||
|
cv::error(cv::Error::GpuApiCallError, msg, __FUNCTION__, __FILE__, __LINE__); \
|
||||||
|
} while (0) |
||||||
|
|
||||||
|
|
||||||
|
#define NVENC_API_CALL( nvencAPI ) \ |
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
NVENCSTATUS errorCode = nvencAPI; \
|
||||||
|
if( errorCode != NV_ENC_SUCCESS) \
|
||||||
|
{ \
|
||||||
|
cv::String msg = cv::format("NVENC returned error [Code = %d]", errorCode); \
|
||||||
|
cv::error(cv::Error::GpuApiCallError, msg, __FUNCTION__, __FILE__, __LINE__); \
|
||||||
|
} \
|
||||||
|
} while (0) |
||||||
|
|
||||||
|
struct NvEncInputFrame |
||||||
|
{ |
||||||
|
void* inputPtr = nullptr; |
||||||
|
uint32_t chromaOffsets[2]; |
||||||
|
uint32_t numChromaPlanes; |
||||||
|
uint32_t pitch; |
||||||
|
uint32_t chromaPitch; |
||||||
|
NV_ENC_BUFFER_FORMAT bufferFormat; |
||||||
|
NV_ENC_INPUT_RESOURCE_TYPE resourceType; |
||||||
|
}; |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Shared base class for different encoder interfaces. |
||||||
|
*/ |
||||||
|
class NvEncoder |
||||||
|
{ |
||||||
|
public: |
||||||
|
/**
|
||||||
|
* @brief This function is used to initialize the encoder session. |
||||||
|
* Application must call this function to initialize the encoder, before |
||||||
|
* starting to encode any frames. |
||||||
|
*/ |
||||||
|
virtual void CreateEncoder(const NV_ENC_INITIALIZE_PARAMS* pEncodeParams); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function is used to destroy the encoder session. |
||||||
|
* Application must call this function to destroy the encoder session and |
||||||
|
* clean up any allocated resources. The application must call EndEncode() |
||||||
|
* function to get any queued encoded frames before calling DestroyEncoder(). |
||||||
|
*/ |
||||||
|
virtual void DestroyEncoder(); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function is used to reconfigure an existing encoder session. |
||||||
|
* Application can use this function to dynamically change the bitrate, |
||||||
|
* resolution and other QOS parameters. If the application changes the |
||||||
|
* resolution, it must set NV_ENC_RECONFIGURE_PARAMS::forceIDR. |
||||||
|
*/ |
||||||
|
bool Reconfigure(const NV_ENC_RECONFIGURE_PARAMS* pReconfigureParams); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function is used to get the next available input buffer. |
||||||
|
* Applications must call this function to obtain a pointer to the next |
||||||
|
* input buffer. The application must copy the uncompressed data to the |
||||||
|
* input buffer and then call EncodeFrame() function to encode it. |
||||||
|
*/ |
||||||
|
const NvEncInputFrame* GetNextInputFrame(); |
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function is used to encode a frame. |
||||||
|
* Applications must call EncodeFrame() function to encode the uncompressed |
||||||
|
* data, which has been copied to an input buffer obtained from the |
||||||
|
* GetNextInputFrame() function. |
||||||
|
*/ |
||||||
|
virtual void EncodeFrame(std::vector<std::vector<uint8_t>>& vPacket, NV_ENC_PIC_PARAMS* pPicParams = nullptr); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function to flush the encoder queue. |
||||||
|
* The encoder might be queuing frames for B picture encoding or lookahead; |
||||||
|
* the application must call EndEncode() to get all the queued encoded frames |
||||||
|
* from the encoder. The application must call this function before destroying |
||||||
|
* an encoder session. |
||||||
|
*/ |
||||||
|
virtual void EndEncode(std::vector<std::vector<uint8_t>>& vPacket); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function is used to query hardware encoder capabilities. |
||||||
|
* Applications can call this function to query capabilities like maximum encode |
||||||
|
* dimensions, support for lookahead or the ME-only mode etc. |
||||||
|
*/ |
||||||
|
int GetCapabilityValue(GUID guidCodec, NV_ENC_CAPS capsToQuery); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function is used to get the current device on which encoder is running. |
||||||
|
*/ |
||||||
|
void* GetDevice() const { return m_pDevice; } |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function is used to get the current device type which encoder is running. |
||||||
|
*/ |
||||||
|
NV_ENC_DEVICE_TYPE GetDeviceType() const { return m_eDeviceType; } |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function is used to get the current encode width. |
||||||
|
* The encode width can be modified by Reconfigure() function. |
||||||
|
*/ |
||||||
|
int GetEncodeWidth() const { return m_nWidth; } |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function is used to get the current encode height. |
||||||
|
* The encode height can be modified by Reconfigure() function. |
||||||
|
*/ |
||||||
|
int GetEncodeHeight() const { return m_nHeight; } |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function is used to get the current frame size based on pixel format. |
||||||
|
*/ |
||||||
|
int GetFrameSize() const; |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function is used to initialize config parameters based on |
||||||
|
* given codec and preset guids. |
||||||
|
* The application can call this function to get the default configuration |
||||||
|
* for a certain preset. The application can either use these parameters |
||||||
|
* directly or override them with application-specific settings before |
||||||
|
* using them in CreateEncoder() function. |
||||||
|
*/ |
||||||
|
void CreateDefaultEncoderParams(NV_ENC_INITIALIZE_PARAMS* pIntializeParams, GUID codecGuid, GUID presetGuid, NV_ENC_TUNING_INFO tuningInfo = NV_ENC_TUNING_INFO_UNDEFINED); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function is used to get the current initialization parameters, |
||||||
|
* which had been used to configure the encoder session. |
||||||
|
* The initialization parameters are modified if the application calls |
||||||
|
* Reconfigure() function. |
||||||
|
*/ |
||||||
|
void GetInitializeParams(NV_ENC_INITIALIZE_PARAMS* pInitializeParams); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function is used to get sequence and picture parameter headers. |
||||||
|
* Application can call this function after encoder is initialized to get SPS and PPS |
||||||
|
* nalus for the current encoder instance. The sequence header data might change when |
||||||
|
* application calls Reconfigure() function. |
||||||
|
*/ |
||||||
|
void GetSequenceParams(std::vector<uint8_t>& seqParams); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief NvEncoder class virtual destructor. |
||||||
|
*/ |
||||||
|
virtual ~NvEncoder(); |
||||||
|
|
||||||
|
public: |
||||||
|
/**
|
||||||
|
* @brief This a static function to get chroma offsets for YUV planar formats. |
||||||
|
*/ |
||||||
|
static void GetChromaSubPlaneOffsets(const NV_ENC_BUFFER_FORMAT bufferFormat, const uint32_t pitch, |
||||||
|
const uint32_t height, std::vector<uint32_t>& chromaOffsets); |
||||||
|
/**
|
||||||
|
* @brief This a static function to get the chroma plane pitch for YUV planar formats. |
||||||
|
*/ |
||||||
|
static uint32_t GetChromaPitch(const NV_ENC_BUFFER_FORMAT bufferFormat, const uint32_t lumaPitch); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This a static function to get the number of chroma planes for YUV planar formats. |
||||||
|
*/ |
||||||
|
static uint32_t GetNumChromaPlanes(const NV_ENC_BUFFER_FORMAT bufferFormat); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This a static function to get the chroma plane width in bytes for YUV planar formats. |
||||||
|
*/ |
||||||
|
static uint32_t GetChromaWidthInBytes(const NV_ENC_BUFFER_FORMAT bufferFormat, const uint32_t lumaWidth); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This a static function to get the chroma planes height in bytes for YUV planar formats. |
||||||
|
*/ |
||||||
|
static uint32_t GetChromaHeight(const NV_ENC_BUFFER_FORMAT bufferFormat, const uint32_t lumaHeight); |
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This a static function to get the width in bytes for the frame. |
||||||
|
* For YUV planar format this is the width in bytes of the luma plane. |
||||||
|
*/ |
||||||
|
static uint32_t GetWidthInBytes(const NV_ENC_BUFFER_FORMAT bufferFormat, const uint32_t width); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function returns the number of allocated buffers. |
||||||
|
*/ |
||||||
|
uint32_t GetEncoderBufferCount() const { return m_nEncoderBuffer; } |
||||||
|
protected: |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief NvEncoder class constructor. |
||||||
|
* NvEncoder class constructor cannot be called directly by the application. |
||||||
|
*/ |
||||||
|
NvEncoder(NV_ENC_DEVICE_TYPE eDeviceType, void* pDevice, uint32_t nWidth, uint32_t nHeight, |
||||||
|
NV_ENC_BUFFER_FORMAT eBufferFormat, uint32_t nOutputDelay); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function is used to check if hardware encoder is properly initialized. |
||||||
|
*/ |
||||||
|
bool IsHWEncoderInitialized() const { return m_hEncoder != NULL && m_bEncoderInitialized; } |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function is used to register CUDA, D3D or OpenGL input buffers with NvEncodeAPI. |
||||||
|
* This is non public function and is called by derived class for allocating |
||||||
|
* and registering input buffers. |
||||||
|
*/ |
||||||
|
void RegisterInputResources(std::vector<void*> inputframes, NV_ENC_INPUT_RESOURCE_TYPE eResourceType, |
||||||
|
int width, int height, int pitch, NV_ENC_BUFFER_FORMAT bufferFormat, bool bReferenceFrame = false); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function is used to unregister resources which had been previously registered for encoding |
||||||
|
* using RegisterInputResources() function. |
||||||
|
*/ |
||||||
|
void UnregisterInputResources(); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function is used to register CUDA, D3D or OpenGL input or output buffers with NvEncodeAPI. |
||||||
|
*/ |
||||||
|
NV_ENC_REGISTERED_PTR 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_INPUT_IMAGE, |
||||||
|
NV_ENC_FENCE_POINT_D3D12* pInputFencePoint = NULL, NV_ENC_FENCE_POINT_D3D12* pOutputFencePoint = NULL); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function returns maximum width used to open the encoder session. |
||||||
|
* All encode input buffers are allocated using maximum dimensions. |
||||||
|
*/ |
||||||
|
uint32_t GetMaxEncodeWidth() const { return m_nMaxEncodeWidth; } |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function returns maximum height used to open the encoder session. |
||||||
|
* All encode input buffers are allocated using maximum dimensions. |
||||||
|
*/ |
||||||
|
uint32_t GetMaxEncodeHeight() const { return m_nMaxEncodeHeight; } |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function returns the completion event. |
||||||
|
*/ |
||||||
|
void* GetCompletionEvent(uint32_t eventIdx) { return (m_vpCompletionEvent.size() == m_nEncoderBuffer) ? m_vpCompletionEvent[eventIdx] : nullptr; } |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function returns the current pixel format. |
||||||
|
*/ |
||||||
|
NV_ENC_BUFFER_FORMAT GetPixelFormat() const { return m_eBufferFormat; } |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function is used to submit the encode commands to the |
||||||
|
* NVENC hardware. |
||||||
|
*/ |
||||||
|
NVENCSTATUS DoEncode(NV_ENC_INPUT_PTR inputBuffer, NV_ENC_OUTPUT_PTR outputBuffer, NV_ENC_PIC_PARAMS* pPicParams); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function is used to submit the encode commands to the |
||||||
|
* NVENC hardware for ME only mode. |
||||||
|
*/ |
||||||
|
//NVENCSTATUS DoMotionEstimation(NV_ENC_INPUT_PTR inputBuffer, NV_ENC_INPUT_PTR inputBufferForReference, NV_ENC_OUTPUT_PTR outputBuffer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function is used to map the input buffers to NvEncodeAPI. |
||||||
|
*/ |
||||||
|
void MapResources(uint32_t bfrIdx); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function is used to wait for completion of encode command. |
||||||
|
*/ |
||||||
|
void WaitForCompletionEvent(int iEvent); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function is used to send EOS to HW encoder. |
||||||
|
*/ |
||||||
|
void SendEOS(); |
||||||
|
|
||||||
|
private: |
||||||
|
/**
|
||||||
|
* @brief This is a private function which is used to check if there is any |
||||||
|
buffering done by encoder. |
||||||
|
* The encoder generally buffers data to encode B frames or for lookahead |
||||||
|
* or pipelining. |
||||||
|
*/ |
||||||
|
bool IsZeroDelay() { return m_nOutputDelay == 0; } |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is a private function which is used to load the encode api shared library. |
||||||
|
*/ |
||||||
|
void LoadNvEncApi(); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is a private function which is used to get the output packets |
||||||
|
* from the encoder HW. |
||||||
|
* This is called by DoEncode() function. If there is buffering enabled, |
||||||
|
* this may return without any output data. |
||||||
|
*/ |
||||||
|
void GetEncodedPacket(std::vector<NV_ENC_OUTPUT_PTR>& vOutputBuffer, std::vector<std::vector<uint8_t>>& vPacket, bool bOutputDelay); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is a private function which is used to initialize the bitstream buffers. |
||||||
|
* This is only used in the encoding mode. |
||||||
|
*/ |
||||||
|
void InitializeBitstreamBuffer(); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is a private function which is used to destroy the bitstream buffers. |
||||||
|
* This is only used in the encoding mode. |
||||||
|
*/ |
||||||
|
void DestroyBitstreamBuffer(); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is a private function which is used to destroy HW encoder. |
||||||
|
*/ |
||||||
|
void DestroyHWEncoder(); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function is used to flush the encoder queue. |
||||||
|
*/ |
||||||
|
void FlushEncoder(); |
||||||
|
|
||||||
|
private: |
||||||
|
/**
|
||||||
|
* @brief This is a pure virtual function which is used to allocate input buffers. |
||||||
|
* The derived classes must implement this function. |
||||||
|
*/ |
||||||
|
virtual void AllocateInputBuffers(int32_t numInputBuffers) = 0; |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is a pure virtual function which is used to destroy input buffers. |
||||||
|
* The derived classes must implement this function. |
||||||
|
*/ |
||||||
|
virtual void ReleaseInputBuffers() = 0; |
||||||
|
|
||||||
|
protected: |
||||||
|
void* m_hEncoder = nullptr; |
||||||
|
NV_ENCODE_API_FUNCTION_LIST m_nvenc; |
||||||
|
std::vector<NvEncInputFrame> m_vInputFrames; |
||||||
|
std::vector<NV_ENC_REGISTERED_PTR> m_vRegisteredResources; |
||||||
|
std::vector<NvEncInputFrame> m_vReferenceFrames; |
||||||
|
std::vector<NV_ENC_REGISTERED_PTR> m_vRegisteredResourcesForReference; |
||||||
|
std::vector<NV_ENC_INPUT_PTR> m_vMappedInputBuffers; |
||||||
|
std::vector<NV_ENC_INPUT_PTR> m_vMappedRefBuffers; |
||||||
|
std::vector<void*> m_vpCompletionEvent; |
||||||
|
|
||||||
|
int32_t m_iToSend = 0; |
||||||
|
int32_t m_iGot = 0; |
||||||
|
int32_t m_nEncoderBuffer = 0; |
||||||
|
int32_t m_nOutputDelay = 0; |
||||||
|
|
||||||
|
private: |
||||||
|
void* m_pDevice; |
||||||
|
NV_ENC_DEVICE_TYPE m_eDeviceType; |
||||||
|
uint32_t m_nWidth; |
||||||
|
uint32_t m_nHeight; |
||||||
|
uint32_t m_nMaxEncodeWidth = 0; |
||||||
|
uint32_t m_nMaxEncodeHeight = 0; |
||||||
|
NV_ENC_BUFFER_FORMAT m_eBufferFormat; |
||||||
|
NV_ENC_INITIALIZE_PARAMS m_initializeParams = {}; |
||||||
|
NV_ENC_CONFIG m_encodeConfig = {}; |
||||||
|
bool m_bEncoderInitialized = false; |
||||||
|
uint32_t m_nExtraOutputDelay = 3; // To ensure encode and graphics can work in parallel, m_nExtraOutputDelay should be set to at least 1
|
||||||
|
std::vector<NV_ENC_OUTPUT_PTR> m_vBitstreamOutputBuffer; |
||||||
|
}; |
||||||
|
}} |
||||||
|
#endif |
@ -0,0 +1,196 @@ |
|||||||
|
// 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 "NvEncoderCuda.h" |
||||||
|
|
||||||
|
namespace cv { namespace cudacodec { |
||||||
|
NvEncoderCuda::NvEncoderCuda(CUcontext cuContext, uint32_t nWidth, uint32_t nHeight, NV_ENC_BUFFER_FORMAT eBufferFormat, |
||||||
|
uint32_t nExtraOutputDelay) : |
||||||
|
NvEncoder(NV_ENC_DEVICE_TYPE_CUDA, cuContext, nWidth, nHeight, eBufferFormat, nExtraOutputDelay), |
||||||
|
m_cuContext(cuContext) |
||||||
|
{ |
||||||
|
if (!m_hEncoder) |
||||||
|
{ |
||||||
|
NVENC_THROW_ERROR("Encoder Initialization failed", NV_ENC_ERR_INVALID_DEVICE); |
||||||
|
} |
||||||
|
|
||||||
|
if (!m_cuContext) |
||||||
|
{ |
||||||
|
NVENC_THROW_ERROR("Invalid Cuda Context", NV_ENC_ERR_INVALID_DEVICE); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
NvEncoderCuda::~NvEncoderCuda() |
||||||
|
{ |
||||||
|
ReleaseCudaResources(); |
||||||
|
} |
||||||
|
|
||||||
|
void NvEncoderCuda::AllocateInputBuffers(int32_t numInputBuffers) |
||||||
|
{ |
||||||
|
if (!IsHWEncoderInitialized()) |
||||||
|
{ |
||||||
|
NVENC_THROW_ERROR("Encoder intialization failed", NV_ENC_ERR_ENCODER_NOT_INITIALIZED); |
||||||
|
} |
||||||
|
|
||||||
|
cuSafeCall(cuCtxPushCurrent(m_cuContext)); |
||||||
|
std::vector<void*> inputFrames; |
||||||
|
for (int i = 0; i < numInputBuffers; i++) |
||||||
|
{ |
||||||
|
CUdeviceptr pDeviceFrame; |
||||||
|
uint32_t chromaHeight = GetNumChromaPlanes(GetPixelFormat()) * GetChromaHeight(GetPixelFormat(), GetMaxEncodeHeight()); |
||||||
|
if (GetPixelFormat() == NV_ENC_BUFFER_FORMAT_YV12 || GetPixelFormat() == NV_ENC_BUFFER_FORMAT_IYUV) |
||||||
|
chromaHeight = GetChromaHeight(GetPixelFormat(), GetMaxEncodeHeight()); |
||||||
|
cuSafeCall(cuMemAllocPitch((CUdeviceptr*)&pDeviceFrame, |
||||||
|
&m_cudaPitch, |
||||||
|
GetWidthInBytes(GetPixelFormat(), GetMaxEncodeWidth()), |
||||||
|
GetMaxEncodeHeight() + chromaHeight, 16)); |
||||||
|
inputFrames.push_back((void*)pDeviceFrame); |
||||||
|
} |
||||||
|
cuSafeCall(cuCtxPopCurrent(NULL)); |
||||||
|
|
||||||
|
RegisterInputResources(inputFrames, |
||||||
|
NV_ENC_INPUT_RESOURCE_TYPE_CUDADEVICEPTR, |
||||||
|
GetMaxEncodeWidth(), |
||||||
|
GetMaxEncodeHeight(), |
||||||
|
(int)m_cudaPitch, |
||||||
|
GetPixelFormat(), |
||||||
|
false); |
||||||
|
} |
||||||
|
|
||||||
|
void NvEncoderCuda::SetIOCudaStreams(NV_ENC_CUSTREAM_PTR inputStream, NV_ENC_CUSTREAM_PTR outputStream) |
||||||
|
{ |
||||||
|
NVENC_API_CALL(m_nvenc.nvEncSetIOCudaStreams(m_hEncoder, inputStream, outputStream)); |
||||||
|
} |
||||||
|
|
||||||
|
void NvEncoderCuda::ReleaseInputBuffers() |
||||||
|
{ |
||||||
|
ReleaseCudaResources(); |
||||||
|
} |
||||||
|
|
||||||
|
void NvEncoderCuda::ReleaseCudaResources() |
||||||
|
{ |
||||||
|
if (!m_hEncoder) |
||||||
|
{ |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (!m_cuContext) |
||||||
|
{ |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
UnregisterInputResources(); |
||||||
|
|
||||||
|
cuCtxPushCurrent(m_cuContext); |
||||||
|
|
||||||
|
for (uint32_t i = 0; i < m_vInputFrames.size(); ++i) |
||||||
|
{ |
||||||
|
if (m_vInputFrames[i].inputPtr) |
||||||
|
{ |
||||||
|
cuMemFree(reinterpret_cast<CUdeviceptr>(m_vInputFrames[i].inputPtr)); |
||||||
|
} |
||||||
|
} |
||||||
|
m_vInputFrames.clear(); |
||||||
|
|
||||||
|
for (uint32_t i = 0; i < m_vReferenceFrames.size(); ++i) |
||||||
|
{ |
||||||
|
if (m_vReferenceFrames[i].inputPtr) |
||||||
|
{ |
||||||
|
cuMemFree(reinterpret_cast<CUdeviceptr>(m_vReferenceFrames[i].inputPtr)); |
||||||
|
} |
||||||
|
} |
||||||
|
m_vReferenceFrames.clear(); |
||||||
|
|
||||||
|
cuCtxPopCurrent(NULL); |
||||||
|
m_cuContext = nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
void NvEncoderCuda::CopyToDeviceFrame(CUcontext device, |
||||||
|
void* pSrcFrame, |
||||||
|
uint32_t nSrcPitch, |
||||||
|
CUdeviceptr pDstFrame, |
||||||
|
uint32_t dstPitch, |
||||||
|
int width, |
||||||
|
int height, |
||||||
|
CUmemorytype srcMemoryType, |
||||||
|
NV_ENC_BUFFER_FORMAT pixelFormat, |
||||||
|
const uint32_t dstChromaOffsets[], |
||||||
|
uint32_t numChromaPlanes, |
||||||
|
bool bUnAlignedDeviceCopy, |
||||||
|
CUstream stream) |
||||||
|
{ |
||||||
|
if (srcMemoryType != CU_MEMORYTYPE_HOST && srcMemoryType != CU_MEMORYTYPE_DEVICE) |
||||||
|
{ |
||||||
|
NVENC_THROW_ERROR("Invalid source memory type for copy", NV_ENC_ERR_INVALID_PARAM); |
||||||
|
} |
||||||
|
|
||||||
|
cuSafeCall(cuCtxPushCurrent(device)); |
||||||
|
|
||||||
|
uint32_t srcPitch = nSrcPitch ? nSrcPitch : NvEncoder::GetWidthInBytes(pixelFormat, width); |
||||||
|
CUDA_MEMCPY2D m = {}; |
||||||
|
m.srcMemoryType = srcMemoryType; |
||||||
|
if (srcMemoryType == CU_MEMORYTYPE_HOST) |
||||||
|
{ |
||||||
|
m.srcHost = pSrcFrame; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
m.srcDevice = (CUdeviceptr)pSrcFrame; |
||||||
|
} |
||||||
|
m.srcPitch = srcPitch; |
||||||
|
m.dstMemoryType = CU_MEMORYTYPE_DEVICE; |
||||||
|
m.dstDevice = pDstFrame; |
||||||
|
m.dstPitch = dstPitch; |
||||||
|
m.WidthInBytes = NvEncoder::GetWidthInBytes(pixelFormat, width); |
||||||
|
m.Height = height; |
||||||
|
if (bUnAlignedDeviceCopy && srcMemoryType == CU_MEMORYTYPE_DEVICE) |
||||||
|
{ |
||||||
|
cuSafeCall(cuMemcpy2DUnaligned(&m)); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
cuSafeCall(stream == NULL ? cuMemcpy2D(&m) : cuMemcpy2DAsync(&m, stream)); |
||||||
|
} |
||||||
|
|
||||||
|
std::vector<uint32_t> srcChromaOffsets; |
||||||
|
NvEncoder::GetChromaSubPlaneOffsets(pixelFormat, srcPitch, height, srcChromaOffsets); |
||||||
|
uint32_t chromaHeight = NvEncoder::GetChromaHeight(pixelFormat, height); |
||||||
|
uint32_t destChromaPitch = NvEncoder::GetChromaPitch(pixelFormat, dstPitch); |
||||||
|
uint32_t srcChromaPitch = NvEncoder::GetChromaPitch(pixelFormat, srcPitch); |
||||||
|
uint32_t chromaWidthInBytes = NvEncoder::GetChromaWidthInBytes(pixelFormat, width); |
||||||
|
|
||||||
|
for (uint32_t i = 0; i < numChromaPlanes; ++i) |
||||||
|
{ |
||||||
|
if (chromaHeight) |
||||||
|
{ |
||||||
|
if (srcMemoryType == CU_MEMORYTYPE_HOST) |
||||||
|
{ |
||||||
|
m.srcHost = ((uint8_t*)pSrcFrame + srcChromaOffsets[i]); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
m.srcDevice = (CUdeviceptr)((uint8_t*)pSrcFrame + srcChromaOffsets[i]); |
||||||
|
} |
||||||
|
m.srcPitch = srcChromaPitch; |
||||||
|
|
||||||
|
m.dstDevice = (CUdeviceptr)((uint8_t*)pDstFrame + dstChromaOffsets[i]); |
||||||
|
m.dstPitch = destChromaPitch; |
||||||
|
m.WidthInBytes = chromaWidthInBytes; |
||||||
|
m.Height = chromaHeight; |
||||||
|
if (bUnAlignedDeviceCopy && srcMemoryType == CU_MEMORYTYPE_DEVICE) |
||||||
|
{ |
||||||
|
cuSafeCall(cuMemcpy2DUnaligned(&m)); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
cuSafeCall(stream == NULL ? cuMemcpy2D(&m) : cuMemcpy2DAsync(&m, stream)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
cuSafeCall(cuCtxPopCurrent(NULL)); |
||||||
|
} |
||||||
|
}} |
||||||
|
#endif |
@ -0,0 +1,75 @@ |
|||||||
|
// 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.
|
||||||
|
|
||||||
|
#ifndef OPENCV_NVENCODERCUDA_HPP |
||||||
|
#define OPENCV_NVENCODERCUDA_HPP |
||||||
|
#include <vector> |
||||||
|
#include <stdint.h> |
||||||
|
#include <mutex> |
||||||
|
#include <cuda.h> |
||||||
|
#include "NvEncoder.h" |
||||||
|
|
||||||
|
namespace cv { namespace cudacodec { |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Encoder for CUDA device memory. |
||||||
|
*/ |
||||||
|
class NvEncoderCuda : public NvEncoder |
||||||
|
{ |
||||||
|
public: |
||||||
|
NvEncoderCuda(CUcontext cuContext, uint32_t nWidth, uint32_t nHeight, NV_ENC_BUFFER_FORMAT eBufferFormat, |
||||||
|
uint32_t nExtraOutputDelay = 3); |
||||||
|
virtual ~NvEncoderCuda(); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is a static function to copy input data from host memory to device memory. |
||||||
|
* This function assumes YUV plane is a single contiguous memory segment. |
||||||
|
*/ |
||||||
|
static void CopyToDeviceFrame(CUcontext device, |
||||||
|
void* pSrcFrame, |
||||||
|
uint32_t nSrcPitch, |
||||||
|
CUdeviceptr pDstFrame, |
||||||
|
uint32_t dstPitch, |
||||||
|
int width, |
||||||
|
int height, |
||||||
|
CUmemorytype srcMemoryType, |
||||||
|
NV_ENC_BUFFER_FORMAT pixelFormat, |
||||||
|
const uint32_t dstChromaOffsets[], |
||||||
|
uint32_t numChromaPlanes, |
||||||
|
bool bUnAlignedDeviceCopy = false, |
||||||
|
CUstream stream = NULL); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function sets input and output CUDA streams |
||||||
|
*/ |
||||||
|
void SetIOCudaStreams(NV_ENC_CUSTREAM_PTR inputStream, NV_ENC_CUSTREAM_PTR outputStream); |
||||||
|
|
||||||
|
protected: |
||||||
|
/**
|
||||||
|
* @brief This function is used to release the input buffers allocated for encoding. |
||||||
|
* This function is an override of virtual function NvEncoder::ReleaseInputBuffers(). |
||||||
|
*/ |
||||||
|
virtual void ReleaseInputBuffers() override; |
||||||
|
|
||||||
|
private: |
||||||
|
/**
|
||||||
|
* @brief This function is used to allocate input buffers for encoding. |
||||||
|
* This function is an override of virtual function NvEncoder::AllocateInputBuffers(). |
||||||
|
*/ |
||||||
|
virtual void AllocateInputBuffers(int32_t numInputBuffers) override; |
||||||
|
|
||||||
|
private: |
||||||
|
/**
|
||||||
|
* @brief This is a private function to release CUDA device memory used for encoding. |
||||||
|
*/ |
||||||
|
void ReleaseCudaResources(); |
||||||
|
|
||||||
|
protected: |
||||||
|
CUcontext m_cuContext; |
||||||
|
|
||||||
|
private: |
||||||
|
size_t m_cudaPitch = 0; |
||||||
|
}; |
||||||
|
}} |
||||||
|
#endif |
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue