// 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 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>& 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& 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>& 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& vOutputBuffer, std::vector>& 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()); } 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(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 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 _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> 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& 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