This commit is contained in:
mofeng-git
2025-12-28 18:19:16 +08:00
commit d143d158e4
771 changed files with 220548 additions and 0 deletions

View File

@@ -0,0 +1,860 @@
/*
* This copyright notice applies to this header file only:
*
* Copyright (c) 2010-2023 NVIDIA Corporation
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the software, and to permit persons to whom the
* software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include <iostream>
#include <algorithm>
#include <chrono>
#include <cmath>
#include "NvDecoder/NvDecoder.h"
#define START_TIMER auto start = std::chrono::high_resolution_clock::now();
#define STOP_TIMER(print_message) int64_t elapsedTime = std::chrono::duration_cast<std::chrono::milliseconds>( \
std::chrono::high_resolution_clock::now() - start).count(); \
std::cout << print_message << \
elapsedTime \
<< " ms " << std::endl;
#define CUDA_DRVAPI_CALL( call ) \
do \
{ \
CUresult err__ = m_cudl->call; \
if (err__ != CUDA_SUCCESS) \
{ \
const char *szErrName = NULL; \
m_cudl->cuGetErrorName(err__, &szErrName); \
std::ostringstream errorLog; \
errorLog << "CUDA driver API error " << szErrName ; \
throw NVDECException::makeNVDECException(errorLog.str(), err__, __FUNCTION__, __FILE__, __LINE__); \
} \
} \
while (0)
static const char * GetVideoCodecString(cudaVideoCodec eCodec) {
static struct {
cudaVideoCodec eCodec;
const char *name;
} aCodecName [] = {
{ cudaVideoCodec_MPEG1, "MPEG-1" },
{ cudaVideoCodec_MPEG2, "MPEG-2" },
{ cudaVideoCodec_MPEG4, "MPEG-4 (ASP)" },
{ cudaVideoCodec_VC1, "VC-1/WMV" },
{ cudaVideoCodec_H264, "AVC/H.264" },
{ cudaVideoCodec_JPEG, "M-JPEG" },
{ cudaVideoCodec_H264_SVC, "H.264/SVC" },
{ cudaVideoCodec_H264_MVC, "H.264/MVC" },
{ cudaVideoCodec_HEVC, "H.265/HEVC" },
{ cudaVideoCodec_VP8, "VP8" },
{ cudaVideoCodec_VP9, "VP9" },
{ cudaVideoCodec_AV1, "AV1" },
{ cudaVideoCodec_NumCodecs, "Invalid" },
{ cudaVideoCodec_YUV420, "YUV 4:2:0" },
{ cudaVideoCodec_YV12, "YV12 4:2:0" },
{ cudaVideoCodec_NV12, "NV12 4:2:0" },
{ cudaVideoCodec_YUYV, "YUYV 4:2:2" },
{ cudaVideoCodec_UYVY, "UYVY 4:2:2" },
};
if (eCodec >= 0 && eCodec <= cudaVideoCodec_NumCodecs) {
return aCodecName[eCodec].name;
}
for (int i = cudaVideoCodec_NumCodecs + 1; i < sizeof(aCodecName) / sizeof(aCodecName[0]); i++) {
if (eCodec == aCodecName[i].eCodec) {
return aCodecName[eCodec].name;
}
}
return "Unknown";
}
static const char * GetVideoChromaFormatString(cudaVideoChromaFormat eChromaFormat) {
static struct {
cudaVideoChromaFormat eChromaFormat;
const char *name;
} aChromaFormatName[] = {
{ cudaVideoChromaFormat_Monochrome, "YUV 400 (Monochrome)" },
{ cudaVideoChromaFormat_420, "YUV 420" },
{ cudaVideoChromaFormat_422, "YUV 422" },
{ cudaVideoChromaFormat_444, "YUV 444" },
};
if (eChromaFormat >= 0 && eChromaFormat < sizeof(aChromaFormatName) / sizeof(aChromaFormatName[0])) {
return aChromaFormatName[eChromaFormat].name;
}
return "Unknown";
}
static float GetChromaHeightFactor(cudaVideoSurfaceFormat eSurfaceFormat)
{
float factor = 0.5;
switch (eSurfaceFormat)
{
case cudaVideoSurfaceFormat_NV12:
case cudaVideoSurfaceFormat_P016:
factor = 0.5;
break;
case cudaVideoSurfaceFormat_YUV444:
case cudaVideoSurfaceFormat_YUV444_16Bit:
factor = 1.0;
break;
}
return factor;
}
static int GetChromaPlaneCount(cudaVideoSurfaceFormat eSurfaceFormat)
{
int numPlane = 1;
switch (eSurfaceFormat)
{
case cudaVideoSurfaceFormat_NV12:
case cudaVideoSurfaceFormat_P016:
numPlane = 1;
break;
case cudaVideoSurfaceFormat_YUV444:
case cudaVideoSurfaceFormat_YUV444_16Bit:
numPlane = 2;
break;
}
return numPlane;
}
std::map<int, int64_t> NvDecoder::sessionOverHead = { {0,0}, {1,0} };
/**
* @brief This function is used to get codec string from codec id
*/
const char *NvDecoder::GetCodecString(cudaVideoCodec eCodec)
{
return GetVideoCodecString(eCodec);
}
/* Called when the parser encounters sequence header for AV1 SVC content
* return value interpretation:
* < 0 : fail, >=0: succeeded (bit 0-9: currOperatingPoint, bit 10-10: bDispAllLayer, bit 11-30: reserved, must be set 0)
*/
int NvDecoder::GetOperatingPoint(CUVIDOPERATINGPOINTINFO *pOPInfo)
{
if (pOPInfo->codec == cudaVideoCodec_AV1)
{
if (pOPInfo->av1.operating_points_cnt > 1)
{
// clip has SVC enabled
if (m_nOperatingPoint >= pOPInfo->av1.operating_points_cnt)
m_nOperatingPoint = 0;
printf("AV1 SVC clip: operating point count %d ", pOPInfo->av1.operating_points_cnt);
printf("Selected operating point: %d, IDC 0x%x bOutputAllLayers %d\n", m_nOperatingPoint, pOPInfo->av1.operating_points_idc[m_nOperatingPoint], m_bDispAllLayers);
return (m_nOperatingPoint | (m_bDispAllLayers << 10));
}
}
return -1;
}
/* Return value from HandleVideoSequence() are interpreted as :
* 0: fail, 1: succeeded, > 1: override dpb size of parser (set by CUVIDPARSERPARAMS::ulMaxNumDecodeSurfaces while creating parser)
*/
int NvDecoder::HandleVideoSequence(CUVIDEOFORMAT *pVideoFormat)
{
START_TIMER
m_videoInfo.str("");
m_videoInfo.clear();
m_videoInfo << "Video Input Information" << std::endl
<< "\tCodec : " << GetVideoCodecString(pVideoFormat->codec) << std::endl
<< "\tFrame rate : " << pVideoFormat->frame_rate.numerator << "/" << pVideoFormat->frame_rate.denominator
<< " = " << 1.0 * pVideoFormat->frame_rate.numerator / pVideoFormat->frame_rate.denominator << " fps" << std::endl
<< "\tSequence : " << (pVideoFormat->progressive_sequence ? "Progressive" : "Interlaced") << std::endl
<< "\tCoded size : [" << pVideoFormat->coded_width << ", " << pVideoFormat->coded_height << "]" << std::endl
<< "\tDisplay area : [" << pVideoFormat->display_area.left << ", " << pVideoFormat->display_area.top << ", "
<< pVideoFormat->display_area.right << ", " << pVideoFormat->display_area.bottom << "]" << std::endl
<< "\tChroma : " << GetVideoChromaFormatString(pVideoFormat->chroma_format) << std::endl
<< "\tBit depth : " << pVideoFormat->bit_depth_luma_minus8 + 8
;
m_videoInfo << std::endl;
m_latestVideoFormat = *pVideoFormat;
int nDecodeSurface = pVideoFormat->min_num_decode_surfaces;
CUVIDDECODECAPS decodecaps;
memset(&decodecaps, 0, sizeof(decodecaps));
decodecaps.eCodecType = pVideoFormat->codec;
decodecaps.eChromaFormat = pVideoFormat->chroma_format;
decodecaps.nBitDepthMinus8 = pVideoFormat->bit_depth_luma_minus8;
CUDA_DRVAPI_CALL(cuCtxPushCurrent(m_cuContext));
NVDEC_API_CALL(cuvidGetDecoderCaps(&decodecaps));
CUDA_DRVAPI_CALL(cuCtxPopCurrent(NULL));
if(!decodecaps.bIsSupported){
NVDEC_THROW_ERROR("Codec not supported on this GPU", CUDA_ERROR_NOT_SUPPORTED);
return nDecodeSurface;
}
if ((pVideoFormat->coded_width > decodecaps.nMaxWidth) ||
(pVideoFormat->coded_height > decodecaps.nMaxHeight)){
std::ostringstream errorString;
errorString << std::endl
<< "Resolution : " << pVideoFormat->coded_width << "x" << pVideoFormat->coded_height << std::endl
<< "Max Supported (wxh) : " << decodecaps.nMaxWidth << "x" << decodecaps.nMaxHeight << std::endl
<< "Resolution not supported on this GPU";
const std::string cErr = errorString.str();
NVDEC_THROW_ERROR(cErr, CUDA_ERROR_NOT_SUPPORTED);
return nDecodeSurface;
}
if ((pVideoFormat->coded_width>>4)*(pVideoFormat->coded_height>>4) > decodecaps.nMaxMBCount){
std::ostringstream errorString;
errorString << std::endl
<< "MBCount : " << (pVideoFormat->coded_width >> 4)*(pVideoFormat->coded_height >> 4) << std::endl
<< "Max Supported mbcnt : " << decodecaps.nMaxMBCount << std::endl
<< "MBCount not supported on this GPU";
const std::string cErr = errorString.str();
NVDEC_THROW_ERROR(cErr, CUDA_ERROR_NOT_SUPPORTED);
return nDecodeSurface;
}
if (m_nWidth && m_nLumaHeight && m_nChromaHeight) {
// cuvidCreateDecoder() has been called before, and now there's possible config change
return ReconfigureDecoder(pVideoFormat);
}
// eCodec has been set in the constructor (for parser). Here it's set again for potential correction
m_eCodec = pVideoFormat->codec;
m_eChromaFormat = pVideoFormat->chroma_format;
m_nBitDepthMinus8 = pVideoFormat->bit_depth_luma_minus8;
m_nBPP = m_nBitDepthMinus8 > 0 ? 2 : 1;
// Set the output surface format same as chroma format
if (m_eChromaFormat == cudaVideoChromaFormat_420 || cudaVideoChromaFormat_Monochrome)
m_eOutputFormat = pVideoFormat->bit_depth_luma_minus8 ? cudaVideoSurfaceFormat_P016 : cudaVideoSurfaceFormat_NV12;
else if (m_eChromaFormat == cudaVideoChromaFormat_444)
m_eOutputFormat = pVideoFormat->bit_depth_luma_minus8 ? cudaVideoSurfaceFormat_YUV444_16Bit : cudaVideoSurfaceFormat_YUV444;
else if (m_eChromaFormat == cudaVideoChromaFormat_422)
m_eOutputFormat = cudaVideoSurfaceFormat_NV12; // no 4:2:2 output format supported yet so make 420 default
// Check if output format supported. If not, check falback options
if (!(decodecaps.nOutputFormatMask & (1 << m_eOutputFormat)))
{
if (decodecaps.nOutputFormatMask & (1 << cudaVideoSurfaceFormat_NV12))
m_eOutputFormat = cudaVideoSurfaceFormat_NV12;
else if (decodecaps.nOutputFormatMask & (1 << cudaVideoSurfaceFormat_P016))
m_eOutputFormat = cudaVideoSurfaceFormat_P016;
else if (decodecaps.nOutputFormatMask & (1 << cudaVideoSurfaceFormat_YUV444))
m_eOutputFormat = cudaVideoSurfaceFormat_YUV444;
else if (decodecaps.nOutputFormatMask & (1 << cudaVideoSurfaceFormat_YUV444_16Bit))
m_eOutputFormat = cudaVideoSurfaceFormat_YUV444_16Bit;
else
NVDEC_THROW_ERROR("No supported output format found", CUDA_ERROR_NOT_SUPPORTED);
}
m_videoFormat = *pVideoFormat;
CUVIDDECODECREATEINFO videoDecodeCreateInfo = { 0 };
videoDecodeCreateInfo.CodecType = pVideoFormat->codec;
videoDecodeCreateInfo.ChromaFormat = pVideoFormat->chroma_format;
videoDecodeCreateInfo.OutputFormat = m_eOutputFormat;
videoDecodeCreateInfo.bitDepthMinus8 = pVideoFormat->bit_depth_luma_minus8;
if (pVideoFormat->progressive_sequence)
videoDecodeCreateInfo.DeinterlaceMode = cudaVideoDeinterlaceMode_Weave;
else
videoDecodeCreateInfo.DeinterlaceMode = cudaVideoDeinterlaceMode_Adaptive;
videoDecodeCreateInfo.ulNumOutputSurfaces = 2;
// With PreferCUVID, JPEG is still decoded by CUDA while video is decoded by NVDEC hardware
videoDecodeCreateInfo.ulCreationFlags = cudaVideoCreate_PreferCUVID;
videoDecodeCreateInfo.ulNumDecodeSurfaces = nDecodeSurface;
videoDecodeCreateInfo.vidLock = m_ctxLock;
videoDecodeCreateInfo.ulWidth = pVideoFormat->coded_width;
videoDecodeCreateInfo.ulHeight = pVideoFormat->coded_height;
// AV1 has max width/height of sequence in sequence header
if (pVideoFormat->codec == cudaVideoCodec_AV1 && pVideoFormat->seqhdr_data_length > 0)
{
// dont overwrite if it is already set from cmdline or reconfig.txt
if (!(m_nMaxWidth > pVideoFormat->coded_width || m_nMaxHeight > pVideoFormat->coded_height))
{
CUVIDEOFORMATEX *vidFormatEx = (CUVIDEOFORMATEX *)pVideoFormat;
m_nMaxWidth = vidFormatEx->av1.max_width;
m_nMaxHeight = vidFormatEx->av1.max_height;
}
}
if (m_nMaxWidth < (int)pVideoFormat->coded_width)
m_nMaxWidth = pVideoFormat->coded_width;
if (m_nMaxHeight < (int)pVideoFormat->coded_height)
m_nMaxHeight = pVideoFormat->coded_height;
videoDecodeCreateInfo.ulMaxWidth = m_nMaxWidth;
videoDecodeCreateInfo.ulMaxHeight = m_nMaxHeight;
if (!(m_cropRect.r && m_cropRect.b) && !(m_resizeDim.w && m_resizeDim.h)) {
m_nWidth = pVideoFormat->display_area.right - pVideoFormat->display_area.left;
m_nLumaHeight = pVideoFormat->display_area.bottom - pVideoFormat->display_area.top;
videoDecodeCreateInfo.ulTargetWidth = pVideoFormat->coded_width;
videoDecodeCreateInfo.ulTargetHeight = pVideoFormat->coded_height;
} else {
if (m_resizeDim.w && m_resizeDim.h) {
videoDecodeCreateInfo.display_area.left = pVideoFormat->display_area.left;
videoDecodeCreateInfo.display_area.top = pVideoFormat->display_area.top;
videoDecodeCreateInfo.display_area.right = pVideoFormat->display_area.right;
videoDecodeCreateInfo.display_area.bottom = pVideoFormat->display_area.bottom;
m_nWidth = m_resizeDim.w;
m_nLumaHeight = m_resizeDim.h;
}
if (m_cropRect.r && m_cropRect.b) {
videoDecodeCreateInfo.display_area.left = m_cropRect.l;
videoDecodeCreateInfo.display_area.top = m_cropRect.t;
videoDecodeCreateInfo.display_area.right = m_cropRect.r;
videoDecodeCreateInfo.display_area.bottom = m_cropRect.b;
m_nWidth = m_cropRect.r - m_cropRect.l;
m_nLumaHeight = m_cropRect.b - m_cropRect.t;
}
videoDecodeCreateInfo.ulTargetWidth = m_nWidth;
videoDecodeCreateInfo.ulTargetHeight = m_nLumaHeight;
}
m_nChromaHeight = (int)(ceil(m_nLumaHeight * GetChromaHeightFactor(m_eOutputFormat)));
m_nNumChromaPlanes = GetChromaPlaneCount(m_eOutputFormat);
m_nSurfaceHeight = videoDecodeCreateInfo.ulTargetHeight;
m_nSurfaceWidth = videoDecodeCreateInfo.ulTargetWidth;
m_displayRect.b = videoDecodeCreateInfo.display_area.bottom;
m_displayRect.t = videoDecodeCreateInfo.display_area.top;
m_displayRect.l = videoDecodeCreateInfo.display_area.left;
m_displayRect.r = videoDecodeCreateInfo.display_area.right;
m_videoInfo << "Video Decoding Params:" << std::endl
<< "\tNum Surfaces : " << videoDecodeCreateInfo.ulNumDecodeSurfaces << std::endl
<< "\tCrop : [" << videoDecodeCreateInfo.display_area.left << ", " << videoDecodeCreateInfo.display_area.top << ", "
<< videoDecodeCreateInfo.display_area.right << ", " << videoDecodeCreateInfo.display_area.bottom << "]" << std::endl
<< "\tResize : " << videoDecodeCreateInfo.ulTargetWidth << "x" << videoDecodeCreateInfo.ulTargetHeight << std::endl
<< "\tDeinterlace : " << std::vector<const char *>{"Weave", "Bob", "Adaptive"}[videoDecodeCreateInfo.DeinterlaceMode]
;
m_videoInfo << std::endl;
CUDA_DRVAPI_CALL(cuCtxPushCurrent(m_cuContext));
NVDEC_API_CALL(cuvidCreateDecoder(&m_hDecoder, &videoDecodeCreateInfo));
CUDA_DRVAPI_CALL(cuCtxPopCurrent(NULL));
STOP_TIMER("Session Initialization Time: ");
NvDecoder::addDecoderSessionOverHead(getDecoderSessionID(), elapsedTime);
return nDecodeSurface;
}
int NvDecoder::ReconfigureDecoder(CUVIDEOFORMAT *pVideoFormat)
{
if (pVideoFormat->bit_depth_luma_minus8 != m_videoFormat.bit_depth_luma_minus8 || pVideoFormat->bit_depth_chroma_minus8 != m_videoFormat.bit_depth_chroma_minus8){
NVDEC_THROW_ERROR("Reconfigure Not supported for bit depth change", CUDA_ERROR_NOT_SUPPORTED);
}
if (pVideoFormat->chroma_format != m_videoFormat.chroma_format) {
NVDEC_THROW_ERROR("Reconfigure Not supported for chroma format change", CUDA_ERROR_NOT_SUPPORTED);
}
bool bDecodeResChange = !(pVideoFormat->coded_width == m_videoFormat.coded_width && pVideoFormat->coded_height == m_videoFormat.coded_height);
bool bDisplayRectChange = !(pVideoFormat->display_area.bottom == m_videoFormat.display_area.bottom && pVideoFormat->display_area.top == m_videoFormat.display_area.top \
&& pVideoFormat->display_area.left == m_videoFormat.display_area.left && pVideoFormat->display_area.right == m_videoFormat.display_area.right);
int nDecodeSurface = pVideoFormat->min_num_decode_surfaces;
if ((pVideoFormat->coded_width > m_nMaxWidth) || (pVideoFormat->coded_height > m_nMaxHeight)) {
// For VP9, let driver handle the change if new width/height > maxwidth/maxheight
if ((m_eCodec != cudaVideoCodec_VP9) || m_bReconfigExternal)
{
NVDEC_THROW_ERROR("Reconfigure Not supported when width/height > maxwidth/maxheight", CUDA_ERROR_NOT_SUPPORTED);
}
return 1;
}
if (!bDecodeResChange && !m_bReconfigExtPPChange) {
// if the coded_width/coded_height hasn't changed but display resolution has changed, then need to update width/height for
// correct output without cropping. Example : 1920x1080 vs 1920x1088
if (bDisplayRectChange)
{
m_nWidth = pVideoFormat->display_area.right - pVideoFormat->display_area.left;
m_nLumaHeight = pVideoFormat->display_area.bottom - pVideoFormat->display_area.top;
m_nChromaHeight = (int)ceil(m_nLumaHeight * GetChromaHeightFactor(m_eOutputFormat));
m_nNumChromaPlanes = GetChromaPlaneCount(m_eOutputFormat);
}
// no need for reconfigureDecoder(). Just return
return 1;
}
CUVIDRECONFIGUREDECODERINFO reconfigParams = { 0 };
reconfigParams.ulWidth = m_videoFormat.coded_width = pVideoFormat->coded_width;
reconfigParams.ulHeight = m_videoFormat.coded_height = pVideoFormat->coded_height;
// Dont change display rect and get scaled output from decoder. This will help display app to present apps smoothly
reconfigParams.display_area.bottom = m_displayRect.b;
reconfigParams.display_area.top = m_displayRect.t;
reconfigParams.display_area.left = m_displayRect.l;
reconfigParams.display_area.right = m_displayRect.r;
reconfigParams.ulTargetWidth = m_nSurfaceWidth;
reconfigParams.ulTargetHeight = m_nSurfaceHeight;
// If external reconfigure is called along with resolution change even if post processing params is not changed,
// do full reconfigure params update
if ((m_bReconfigExternal && bDecodeResChange) || m_bReconfigExtPPChange) {
// update display rect and target resolution if requested explicitely
m_bReconfigExternal = false;
m_bReconfigExtPPChange = false;
m_videoFormat = *pVideoFormat;
if (!(m_cropRect.r && m_cropRect.b) && !(m_resizeDim.w && m_resizeDim.h)) {
m_nWidth = pVideoFormat->display_area.right - pVideoFormat->display_area.left;
m_nLumaHeight = pVideoFormat->display_area.bottom - pVideoFormat->display_area.top;
reconfigParams.ulTargetWidth = pVideoFormat->coded_width;
reconfigParams.ulTargetHeight = pVideoFormat->coded_height;
}
else {
if (m_resizeDim.w && m_resizeDim.h) {
reconfigParams.display_area.left = pVideoFormat->display_area.left;
reconfigParams.display_area.top = pVideoFormat->display_area.top;
reconfigParams.display_area.right = pVideoFormat->display_area.right;
reconfigParams.display_area.bottom = pVideoFormat->display_area.bottom;
m_nWidth = m_resizeDim.w;
m_nLumaHeight = m_resizeDim.h;
}
if (m_cropRect.r && m_cropRect.b) {
reconfigParams.display_area.left = m_cropRect.l;
reconfigParams.display_area.top = m_cropRect.t;
reconfigParams.display_area.right = m_cropRect.r;
reconfigParams.display_area.bottom = m_cropRect.b;
m_nWidth = m_cropRect.r - m_cropRect.l;
m_nLumaHeight = m_cropRect.b - m_cropRect.t;
}
reconfigParams.ulTargetWidth = m_nWidth;
reconfigParams.ulTargetHeight = m_nLumaHeight;
}
m_nChromaHeight = (int)ceil(m_nLumaHeight * GetChromaHeightFactor(m_eOutputFormat));
m_nNumChromaPlanes = GetChromaPlaneCount(m_eOutputFormat);
m_nSurfaceHeight = reconfigParams.ulTargetHeight;
m_nSurfaceWidth = reconfigParams.ulTargetWidth;
m_displayRect.b = reconfigParams.display_area.bottom;
m_displayRect.t = reconfigParams.display_area.top;
m_displayRect.l = reconfigParams.display_area.left;
m_displayRect.r = reconfigParams.display_area.right;
}
reconfigParams.ulNumDecodeSurfaces = nDecodeSurface;
START_TIMER
CUDA_DRVAPI_CALL(cuCtxPushCurrent(m_cuContext));
NVDEC_API_CALL(cuvidReconfigureDecoder(m_hDecoder, &reconfigParams));
CUDA_DRVAPI_CALL(cuCtxPopCurrent(NULL));
STOP_TIMER("Session Reconfigure Time: ");
return nDecodeSurface;
}
int NvDecoder::setReconfigParams(const Rect *pCropRect, const Dim *pResizeDim)
{
m_bReconfigExternal = true;
m_bReconfigExtPPChange = false;
if (pCropRect)
{
if (!((pCropRect->t == m_cropRect.t) && (pCropRect->l == m_cropRect.l) &&
(pCropRect->b == m_cropRect.b) && (pCropRect->r == m_cropRect.r)))
{
m_bReconfigExtPPChange = true;
m_cropRect = *pCropRect;
}
}
if (pResizeDim)
{
if (!((pResizeDim->w == m_resizeDim.w) && (pResizeDim->h == m_resizeDim.h)))
{
m_bReconfigExtPPChange = true;
m_resizeDim = *pResizeDim;
}
}
// Clear existing output buffers of different size
uint8_t *pFrame = NULL;
while (!m_vpFrame.empty())
{
pFrame = m_vpFrame.back();
m_vpFrame.pop_back();
if (m_bUseDeviceFrame)
{
CUDA_DRVAPI_CALL(cuCtxPushCurrent(m_cuContext));
CUDA_DRVAPI_CALL(cuMemFree((CUdeviceptr)pFrame));
CUDA_DRVAPI_CALL(cuCtxPopCurrent(NULL));
}
else
{
delete pFrame;
}
}
return 1;
}
/* Return value from HandlePictureDecode() are interpreted as:
* 0: fail, >=1: succeeded
*/
int NvDecoder::HandlePictureDecode(CUVIDPICPARAMS *pPicParams) {
if (!m_hDecoder)
{
NVDEC_THROW_ERROR("Decoder not initialized.", CUDA_ERROR_NOT_INITIALIZED);
return false;
}
m_nPicNumInDecodeOrder[pPicParams->CurrPicIdx] = m_nDecodePicCnt++;
CUDA_DRVAPI_CALL(cuCtxPushCurrent(m_cuContext));
NVDEC_API_CALL(cuvidDecodePicture(m_hDecoder, pPicParams));
if (m_bForce_zero_latency && ((!pPicParams->field_pic_flag) || (pPicParams->second_field)))
{
CUVIDPARSERDISPINFO dispInfo;
memset(&dispInfo, 0, sizeof(dispInfo));
dispInfo.picture_index = pPicParams->CurrPicIdx;
dispInfo.progressive_frame = !pPicParams->field_pic_flag;
dispInfo.top_field_first = pPicParams->bottom_field_flag ^ 1;
HandlePictureDisplay(&dispInfo);
}
CUDA_DRVAPI_CALL(cuCtxPopCurrent(NULL));
return 1;
}
/* Return value from HandlePictureDisplay() are interpreted as:
* 0: fail, >=1: succeeded
*/
int NvDecoder::HandlePictureDisplay(CUVIDPARSERDISPINFO *pDispInfo) {
CUVIDPROCPARAMS videoProcessingParameters = {};
videoProcessingParameters.progressive_frame = pDispInfo->progressive_frame;
videoProcessingParameters.second_field = pDispInfo->repeat_first_field + 1;
videoProcessingParameters.top_field_first = pDispInfo->top_field_first;
videoProcessingParameters.unpaired_field = pDispInfo->repeat_first_field < 0;
videoProcessingParameters.output_stream = m_cuvidStream;
if (m_bExtractSEIMessage)
{
if (m_SEIMessagesDisplayOrder[pDispInfo->picture_index].pSEIData)
{
// Write SEI Message
uint8_t *seiBuffer = (uint8_t *)(m_SEIMessagesDisplayOrder[pDispInfo->picture_index].pSEIData);
uint32_t seiNumMessages = m_SEIMessagesDisplayOrder[pDispInfo->picture_index].sei_message_count;
CUSEIMESSAGE *seiMessagesInfo = m_SEIMessagesDisplayOrder[pDispInfo->picture_index].pSEIMessage;
if (m_fpSEI)
{
for (uint32_t i = 0; i < seiNumMessages; i++)
{
if (m_eCodec == cudaVideoCodec_H264 || cudaVideoCodec_H264_SVC || cudaVideoCodec_H264_MVC || cudaVideoCodec_HEVC)
{
switch (seiMessagesInfo[i].sei_message_type)
{
case SEI_TYPE_TIME_CODE:
{
HEVCSEITIMECODE *timecode = (HEVCSEITIMECODE *)seiBuffer;
fwrite(timecode, sizeof(HEVCSEITIMECODE), 1, m_fpSEI);
}
break;
case SEI_TYPE_USER_DATA_UNREGISTERED:
{
fwrite(seiBuffer, seiMessagesInfo[i].sei_message_size, 1, m_fpSEI);
}
break;
}
}
if (m_eCodec == cudaVideoCodec_AV1)
{
fwrite(seiBuffer, seiMessagesInfo[i].sei_message_size, 1, m_fpSEI);
}
seiBuffer += seiMessagesInfo[i].sei_message_size;
}
}
free(m_SEIMessagesDisplayOrder[pDispInfo->picture_index].pSEIData);
free(m_SEIMessagesDisplayOrder[pDispInfo->picture_index].pSEIMessage);
}
}
CUdeviceptr dpSrcFrame = 0;
unsigned int nSrcPitch = 0;
CUDA_DRVAPI_CALL(cuCtxPushCurrent(m_cuContext));
NVDEC_API_CALL(cuvidMapVideoFrame(m_hDecoder, pDispInfo->picture_index, &dpSrcFrame,
&nSrcPitch, &videoProcessingParameters));
CUVIDGETDECODESTATUS DecodeStatus;
memset(&DecodeStatus, 0, sizeof(DecodeStatus));
CUresult result = m_cvdl->cuvidGetDecodeStatus(m_hDecoder, pDispInfo->picture_index, &DecodeStatus);
if (result == CUDA_SUCCESS && (DecodeStatus.decodeStatus == cuvidDecodeStatus_Error || DecodeStatus.decodeStatus == cuvidDecodeStatus_Error_Concealed))
{
printf("Decode Error occurred for picture %d\n", m_nPicNumInDecodeOrder[pDispInfo->picture_index]);
}
uint8_t *pDecodedFrame = nullptr;
{
std::lock_guard<std::mutex> lock(m_mtxVPFrame);
if ((unsigned)++m_nDecodedFrame > m_vpFrame.size())
{
// Not enough frames in stock
m_nFrameAlloc++;
uint8_t *pFrame = NULL;
if (m_bUseDeviceFrame)
{
if (m_bDeviceFramePitched)
{
CUDA_DRVAPI_CALL(cuMemAllocPitch((CUdeviceptr *)&pFrame, &m_nDeviceFramePitch, GetWidth() * m_nBPP, m_nLumaHeight + (m_nChromaHeight * m_nNumChromaPlanes), 16));
}
else
{
CUDA_DRVAPI_CALL(cuMemAlloc((CUdeviceptr *)&pFrame, GetFrameSize()));
}
}
else
{
pFrame = new uint8_t[GetFrameSize()];
}
m_vpFrame.push_back(pFrame);
}
pDecodedFrame = m_vpFrame[m_nDecodedFrame - 1];
}
// Copy luma plane
CUDA_MEMCPY2D m = { 0 };
m.srcMemoryType = CU_MEMORYTYPE_DEVICE;
m.srcDevice = dpSrcFrame;
m.srcPitch = nSrcPitch;
m.dstMemoryType = m_bUseDeviceFrame ? CU_MEMORYTYPE_DEVICE : CU_MEMORYTYPE_HOST;
m.dstDevice = (CUdeviceptr)(m.dstHost = pDecodedFrame);
m.dstPitch = m_nDeviceFramePitch ? m_nDeviceFramePitch : GetWidth() * m_nBPP;
m.WidthInBytes = GetWidth() * m_nBPP;
m.Height = m_nLumaHeight;
CUDA_DRVAPI_CALL(cuMemcpy2DAsync(&m, m_cuvidStream));
// Copy chroma plane
// NVDEC output has luma height aligned by 2. Adjust chroma offset by aligning height
m.srcDevice = (CUdeviceptr)((uint8_t *)dpSrcFrame + m.srcPitch * ((m_nSurfaceHeight + 1) & ~1));
m.dstDevice = (CUdeviceptr)(m.dstHost = pDecodedFrame + m.dstPitch * m_nLumaHeight);
m.Height = m_nChromaHeight;
CUDA_DRVAPI_CALL(cuMemcpy2DAsync(&m, m_cuvidStream));
if (m_nNumChromaPlanes == 2)
{
m.srcDevice = (CUdeviceptr)((uint8_t *)dpSrcFrame + m.srcPitch * ((m_nSurfaceHeight + 1) & ~1) * 2);
m.dstDevice = (CUdeviceptr)(m.dstHost = pDecodedFrame + m.dstPitch * m_nLumaHeight * 2);
m.Height = m_nChromaHeight;
CUDA_DRVAPI_CALL(cuMemcpy2DAsync(&m, m_cuvidStream));
}
CUDA_DRVAPI_CALL(cuStreamSynchronize(m_cuvidStream));
CUDA_DRVAPI_CALL(cuCtxPopCurrent(NULL));
if ((int)m_vTimestamp.size() < m_nDecodedFrame) {
m_vTimestamp.resize(m_vpFrame.size());
}
m_vTimestamp[m_nDecodedFrame - 1] = pDispInfo->timestamp;
NVDEC_API_CALL(cuvidUnmapVideoFrame(m_hDecoder, dpSrcFrame));
return 1;
}
int NvDecoder::GetSEIMessage(CUVIDSEIMESSAGEINFO *pSEIMessageInfo)
{
uint32_t seiNumMessages = pSEIMessageInfo->sei_message_count;
CUSEIMESSAGE *seiMessagesInfo = pSEIMessageInfo->pSEIMessage;
size_t totalSEIBufferSize = 0;
if ((pSEIMessageInfo->picIdx < 0) || (pSEIMessageInfo->picIdx >= MAX_FRM_CNT))
{
printf("Invalid picture index (%d)\n", pSEIMessageInfo->picIdx);
return 0;
}
for (uint32_t i = 0; i < seiNumMessages; i++)
{
totalSEIBufferSize += seiMessagesInfo[i].sei_message_size;
}
if (!m_pCurrSEIMessage)
{
printf("Out of Memory, Allocation failed for m_pCurrSEIMessage\n");
return 0;
}
m_pCurrSEIMessage->pSEIData = malloc(totalSEIBufferSize);
if (!m_pCurrSEIMessage->pSEIData)
{
printf("Out of Memory, Allocation failed for SEI Buffer\n");
return 0;
}
memcpy(m_pCurrSEIMessage->pSEIData, pSEIMessageInfo->pSEIData, totalSEIBufferSize);
m_pCurrSEIMessage->pSEIMessage = (CUSEIMESSAGE *)malloc(sizeof(CUSEIMESSAGE) * seiNumMessages);
if (!m_pCurrSEIMessage->pSEIMessage)
{
free(m_pCurrSEIMessage->pSEIData);
m_pCurrSEIMessage->pSEIData = NULL;
return 0;
}
memcpy(m_pCurrSEIMessage->pSEIMessage, pSEIMessageInfo->pSEIMessage, sizeof(CUSEIMESSAGE) * seiNumMessages);
m_pCurrSEIMessage->sei_message_count = pSEIMessageInfo->sei_message_count;
m_SEIMessagesDisplayOrder[pSEIMessageInfo->picIdx] = *m_pCurrSEIMessage;
return 1;
}
NvDecoder::NvDecoder(CudaFunctions *cudl, CuvidFunctions *cvdl, CUcontext cuContext, bool bUseDeviceFrame, cudaVideoCodec eCodec, bool bLowLatency,
bool bDeviceFramePitched, const Rect *pCropRect, const Dim *pResizeDim, bool extract_user_SEI_Message,
int maxWidth, int maxHeight, unsigned int clkRate, bool force_zero_latency) :
m_cuContext(cuContext), m_bUseDeviceFrame(bUseDeviceFrame), m_eCodec(eCodec), m_bDeviceFramePitched(bDeviceFramePitched),
m_bExtractSEIMessage(extract_user_SEI_Message), m_nMaxWidth (maxWidth), m_nMaxHeight(maxHeight),
m_bForce_zero_latency(force_zero_latency)
{
if (pCropRect) m_cropRect = *pCropRect;
if (pResizeDim) m_resizeDim = *pResizeDim;
NVDEC_API_CALL(cuvidCtxLockCreate(&m_ctxLock, cuContext));
CUDA_DRVAPI_CALL(cuStreamCreate(&m_cuvidStream, CU_STREAM_DEFAULT));
decoderSessionID = 0;
if (m_bExtractSEIMessage)
{
m_fpSEI = fopen("sei_message.txt", "wb");
m_pCurrSEIMessage = new CUVIDSEIMESSAGEINFO;
memset(&m_SEIMessagesDisplayOrder, 0, sizeof(m_SEIMessagesDisplayOrder));
}
CUVIDPARSERPARAMS videoParserParameters = {};
videoParserParameters.CodecType = eCodec;
videoParserParameters.ulMaxNumDecodeSurfaces = 1;
videoParserParameters.ulClockRate = clkRate;
videoParserParameters.ulMaxDisplayDelay = bLowLatency ? 0 : 1;
videoParserParameters.pUserData = this;
videoParserParameters.pfnSequenceCallback = HandleVideoSequenceProc;
videoParserParameters.pfnDecodePicture = HandlePictureDecodeProc;
videoParserParameters.pfnDisplayPicture = m_bForce_zero_latency ? NULL : HandlePictureDisplayProc;
videoParserParameters.pfnGetOperatingPoint = HandleOperatingPointProc;
videoParserParameters.pfnGetSEIMsg = m_bExtractSEIMessage ? HandleSEIMessagesProc : NULL;
NVDEC_API_CALL(cuvidCreateVideoParser(&m_hParser, &videoParserParameters));
}
NvDecoder::~NvDecoder() {
START_TIMER
if (m_pCurrSEIMessage) {
delete m_pCurrSEIMessage;
m_pCurrSEIMessage = NULL;
}
if (m_fpSEI) {
fclose(m_fpSEI);
m_fpSEI = NULL;
}
if (m_hParser) {
m_cvdl->cuvidDestroyVideoParser(m_hParser);
}
m_cudl->cuCtxPushCurrent(m_cuContext);
if (m_hDecoder) {
m_cvdl->cuvidDestroyDecoder(m_hDecoder);
}
std::lock_guard<std::mutex> lock(m_mtxVPFrame);
for (uint8_t *pFrame : m_vpFrame)
{
if (m_bUseDeviceFrame)
{
m_cudl->cuMemFree((CUdeviceptr)pFrame);
}
else
{
delete[] pFrame;
}
}
m_cudl->cuCtxPopCurrent(NULL);
m_cvdl->cuvidCtxLockDestroy(m_ctxLock);
STOP_TIMER("Session Deinitialization Time: ");
NvDecoder::addDecoderSessionOverHead(getDecoderSessionID(), elapsedTime);
}
int NvDecoder::Decode(const uint8_t *pData, int nSize, int nFlags, int64_t nTimestamp)
{
m_nDecodedFrame = 0;
m_nDecodedFrameReturned = 0;
CUVIDSOURCEDATAPACKET packet = { 0 };
packet.payload = pData;
packet.payload_size = nSize;
packet.flags = nFlags | CUVID_PKT_TIMESTAMP;
packet.timestamp = nTimestamp;
if (!pData || nSize == 0) {
packet.flags |= CUVID_PKT_ENDOFSTREAM;
}
NVDEC_API_CALL(cuvidParseVideoData(m_hParser, &packet));
return m_nDecodedFrame;
}
uint8_t* NvDecoder::GetFrame(int64_t* pTimestamp)
{
if (m_nDecodedFrame > 0)
{
std::lock_guard<std::mutex> lock(m_mtxVPFrame);
m_nDecodedFrame--;
if (pTimestamp)
*pTimestamp = m_vTimestamp[m_nDecodedFrameReturned];
return m_vpFrame[m_nDecodedFrameReturned++];
}
return NULL;
}
uint8_t* NvDecoder::GetLockedFrame(int64_t* pTimestamp)
{
uint8_t *pFrame;
uint64_t timestamp;
if (m_nDecodedFrame > 0) {
std::lock_guard<std::mutex> lock(m_mtxVPFrame);
m_nDecodedFrame--;
pFrame = m_vpFrame[0];
m_vpFrame.erase(m_vpFrame.begin(), m_vpFrame.begin() + 1);
timestamp = m_vTimestamp[0];
m_vTimestamp.erase(m_vTimestamp.begin(), m_vTimestamp.begin() + 1);
if (pTimestamp)
*pTimestamp = timestamp;
return pFrame;
}
return NULL;
}
void NvDecoder::UnlockFrame(uint8_t **pFrame)
{
std::lock_guard<std::mutex> lock(m_mtxVPFrame);
m_vpFrame.insert(m_vpFrame.end(), &pFrame[0], &pFrame[1]);
// add a dummy entry for timestamp
uint64_t timestamp[2] = {0};
m_vTimestamp.insert(m_vTimestamp.end(), &timestamp[0], &timestamp[1]);
}

View File

@@ -0,0 +1,386 @@
/*
* This copyright notice applies to this header file only:
*
* Copyright (c) 2010-2023 NVIDIA Corporation
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the software, and to permit persons to whom the
* software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#pragma once
#include <assert.h>
#include <stdint.h>
#include <mutex>
#include <vector>
#include <string>
#include <iostream>
#include <sstream>
#include <string.h>
#include <dynlink_cuda.h>
#include <dynlink_nvcuvid.h>
#include <dynlink_loader.h>
#include "../Utils/NvCodecUtils.h"
#include <map>
#define MAX_FRM_CNT 32
typedef enum{
SEI_TYPE_TIME_CODE = 136,
SEI_TYPE_USER_DATA_UNREGISTERED = 5
}SEI_H264_HEVC_PAYLOAD_TYPE;
/**
* @brief Exception class for error reporting from the decode API.
*/
class NVDECException : public std::exception
{
public:
NVDECException(const std::string& errorStr, const CUresult errorCode)
: m_errorString(errorStr), m_errorCode(errorCode) {}
virtual ~NVDECException() throw() {}
virtual const char* what() const throw() { return m_errorString.c_str(); }
CUresult getErrorCode() const { return m_errorCode; }
const std::string& getErrorString() const { return m_errorString; }
static NVDECException makeNVDECException(const std::string& errorStr, const CUresult errorCode,
const std::string& functionName, const std::string& fileName, int lineNo);
private:
std::string m_errorString;
CUresult m_errorCode;
};
inline NVDECException NVDECException::makeNVDECException(const std::string& errorStr, const CUresult errorCode, const std::string& functionName,
const std::string& fileName, int lineNo)
{
std::ostringstream errorLog;
errorLog << functionName << " : " << errorStr << " at " << fileName << ":" << lineNo << std::endl;
NVDECException exception(errorLog.str(), errorCode);
return exception;
}
#define NVDEC_THROW_ERROR( errorStr, errorCode ) \
do \
{ \
throw NVDECException::makeNVDECException(errorStr, errorCode, __FUNCTION__, __FILE__, __LINE__); \
} while (0)
#define NVDEC_API_CALL( cuvidAPI ) \
do \
{ \
CUresult errorCode = m_cvdl->cuvidAPI; \
if( errorCode != CUDA_SUCCESS) \
{ \
std::ostringstream errorLog; \
errorLog << #cuvidAPI << " returned error " << errorCode; \
throw NVDECException::makeNVDECException(errorLog.str(), errorCode, __FUNCTION__, __FILE__, __LINE__); \
} \
} while (0)
struct Rect {
int l, t, r, b;
};
struct Dim {
int w, h;
};
/**
* @brief Base class for decoder interface.
*/
class NvDecoder {
public:
/**
* @brief This function is used to initialize the decoder session.
* Application must call this function to initialize the decoder, before
* starting to decode any frames.
*/
NvDecoder(CudaFunctions *cudl, CuvidFunctions *cvdl, CUcontext cuContext, bool bUseDeviceFrame, cudaVideoCodec eCodec, bool bLowLatency = false,
bool bDeviceFramePitched = false, const Rect *pCropRect = NULL, const Dim *pResizeDim = NULL,
bool extract_user_SEI_Message = false, int maxWidth = 0, int maxHeight = 0, unsigned int clkRate = 1000,
bool force_zero_latency = false);
~NvDecoder();
/**
* @brief This function is used to get the current CUDA context.
*/
CUcontext GetContext() { return m_cuContext; }
/**
* @brief This function is used to get the output frame width.
* NV12/P016 output format width is 2 byte aligned because of U and V interleave
*/
int GetWidth() { assert(m_nWidth); return (m_eOutputFormat == cudaVideoSurfaceFormat_NV12 || m_eOutputFormat == cudaVideoSurfaceFormat_P016)
? (m_nWidth + 1) & ~1 : m_nWidth; }
/**
* @brief This function is used to get the actual decode width
*/
int GetDecodeWidth() { assert(m_nWidth); return m_nWidth; }
/**
* @brief This function is used to get the output frame height (Luma height).
*/
int GetHeight() { assert(m_nLumaHeight); return m_nLumaHeight; }
/**
* @brief This function is used to get the current chroma height.
*/
int GetChromaHeight() { assert(m_nChromaHeight); return m_nChromaHeight; }
/**
* @brief This function is used to get the number of chroma planes.
*/
int GetNumChromaPlanes() { assert(m_nNumChromaPlanes); return m_nNumChromaPlanes; }
/**
* @brief This function is used to get the current frame size based on pixel format.
*/
int GetFrameSize() { assert(m_nWidth); return GetWidth() * (m_nLumaHeight + (m_nChromaHeight * m_nNumChromaPlanes)) * m_nBPP; }
/**
* @brief This function is used to get the current frame Luma plane size.
*/
int GetLumaPlaneSize() { assert(m_nWidth); return GetWidth() * m_nLumaHeight * m_nBPP; }
/**
* @brief This function is used to get the current frame chroma plane size.
*/
int GetChromaPlaneSize() { assert(m_nWidth); return GetWidth() * (m_nChromaHeight * m_nNumChromaPlanes) * m_nBPP; }
/**
* @brief This function is used to get the pitch of the device buffer holding the decoded frame.
*/
int GetDeviceFramePitch() { assert(m_nWidth); return m_nDeviceFramePitch ? (int)m_nDeviceFramePitch : GetWidth() * m_nBPP; }
/**
* @brief This function is used to get the bit depth associated with the pixel format.
*/
int GetBitDepth() { assert(m_nWidth); return m_nBitDepthMinus8 + 8; }
/**
* @brief This function is used to get the bytes used per pixel.
*/
int GetBPP() { assert(m_nWidth); return m_nBPP; }
/**
* @brief This function is used to get the YUV chroma format
*/
cudaVideoSurfaceFormat GetOutputFormat() { return m_eOutputFormat; }
/**
* @brief This function is used to get information about the video stream (codec, display parameters etc)
*/
CUVIDEOFORMAT GetVideoFormatInfo() { assert(m_nWidth); return m_videoFormat; }
/**
* @brief This function is used to get codec string from codec id
*/
const char *GetCodecString(cudaVideoCodec eCodec);
/**
* @brief This function is used to print information about the video stream
*/
std::string GetVideoInfo() const { return m_videoInfo.str(); }
/**
* @brief This function decodes a frame and returns the number of frames that are available for
* display. All frames that are available for display should be read before making a subsequent decode call.
* @param pData - pointer to the data buffer that is to be decoded
* @param nSize - size of the data buffer in bytes
* @param nFlags - CUvideopacketflags for setting decode options
* @param nTimestamp - presentation timestamp
*/
int Decode(const uint8_t *pData, int nSize, int nFlags = 0, int64_t nTimestamp = 0);
/**
* @brief This function returns a decoded frame and timestamp. This function should be called in a loop for
* fetching all the frames that are available for display.
*/
uint8_t* GetFrame(int64_t* pTimestamp = nullptr);
/**
* @brief This function decodes a frame and returns the locked frame buffers
* This makes the buffers available for use by the application without the buffers
* getting overwritten, even if subsequent decode calls are made. The frame buffers
* remain locked, until UnlockFrame() is called
*/
uint8_t* GetLockedFrame(int64_t* pTimestamp = nullptr);
/**
* @brief This function unlocks the frame buffer and makes the frame buffers available for write again
* @param ppFrame - pointer to array of frames that are to be unlocked
* @param nFrame - number of frames to be unlocked
*/
void UnlockFrame(uint8_t **pFrame);
/**
* @brief This function allows app to set decoder reconfig params
* @param pCropRect - cropping rectangle coordinates
* @param pResizeDim - width and height of resized output
*/
int setReconfigParams(const Rect * pCropRect, const Dim * pResizeDim);
/**
* @brief This function allows app to set operating point for AV1 SVC clips
* @param opPoint - operating point of an AV1 scalable bitstream
* @param bDispAllLayers - Output all decoded frames of an AV1 scalable bitstream
*/
void SetOperatingPoint(const uint32_t opPoint, const bool bDispAllLayers) { m_nOperatingPoint = opPoint; m_bDispAllLayers = bDispAllLayers; }
// start a timer
void startTimer() { m_stDecode_time.Start(); }
// stop the timer
double stopTimer() { return m_stDecode_time.Stop(); }
void setDecoderSessionID(int sessionID) { decoderSessionID = sessionID; }
int getDecoderSessionID() { return decoderSessionID; }
// Session overhead refers to decoder initialization and deinitialization time
static void addDecoderSessionOverHead(int sessionID, int64_t duration) { sessionOverHead[sessionID] += duration; }
static int64_t getDecoderSessionOverHead(int sessionID) { return sessionOverHead[sessionID]; }
unsigned int GetMaxWidth() { return m_nMaxWidth; }
unsigned int GetMaxHeight() { return m_nMaxHeight; }
CUVIDEOFORMAT GetLatestVideoFormat() { return m_latestVideoFormat; }
private:
int decoderSessionID; // Decoder session identifier. Used to gather session level stats.
static std::map<int, int64_t> sessionOverHead; // Records session overhead of initialization+deinitialization time. Format is (thread id, duration)
/**
* @brief Callback function to be registered for getting a callback when decoding of sequence starts
*/
static int CUDAAPI HandleVideoSequenceProc(void *pUserData, CUVIDEOFORMAT *pVideoFormat) { return ((NvDecoder *)pUserData)->HandleVideoSequence(pVideoFormat); }
/**
* @brief Callback function to be registered for getting a callback when a decoded frame is ready to be decoded
*/
static int CUDAAPI HandlePictureDecodeProc(void *pUserData, CUVIDPICPARAMS *pPicParams) { return ((NvDecoder *)pUserData)->HandlePictureDecode(pPicParams); }
/**
* @brief Callback function to be registered for getting a callback when a decoded frame is available for display
*/
static int CUDAAPI HandlePictureDisplayProc(void *pUserData, CUVIDPARSERDISPINFO *pDispInfo) { return ((NvDecoder *)pUserData)->HandlePictureDisplay(pDispInfo); }
/**
* @brief Callback function to be registered for getting a callback to get operating point when AV1 SVC sequence header start.
*/
static int CUDAAPI HandleOperatingPointProc(void *pUserData, CUVIDOPERATINGPOINTINFO *pOPInfo) { return ((NvDecoder *)pUserData)->GetOperatingPoint(pOPInfo); }
/**
* @brief Callback function to be registered for getting a callback when all the unregistered user SEI Messages are parsed for a frame.
*/
static int CUDAAPI HandleSEIMessagesProc(void *pUserData, CUVIDSEIMESSAGEINFO *pSEIMessageInfo) { return ((NvDecoder *)pUserData)->GetSEIMessage(pSEIMessageInfo); }
/**
* @brief This function gets called when a sequence is ready to be decoded. The function also gets called
when there is format change
*/
int HandleVideoSequence(CUVIDEOFORMAT *pVideoFormat);
/**
* @brief This function gets called when a picture is ready to be decoded. cuvidDecodePicture is called from this function
* to decode the picture
*/
int HandlePictureDecode(CUVIDPICPARAMS *pPicParams);
/**
* @brief This function gets called after a picture is decoded and available for display. Frames are fetched and stored in
internal buffer
*/
int HandlePictureDisplay(CUVIDPARSERDISPINFO *pDispInfo);
/**
* @brief This function gets called when AV1 sequence encounter more than one operating points
*/
int GetOperatingPoint(CUVIDOPERATINGPOINTINFO *pOPInfo);
/**
* @brief This function gets called when all unregistered user SEI messages are parsed for a frame
*/
int GetSEIMessage(CUVIDSEIMESSAGEINFO *pSEIMessageInfo);
/**
* @brief This function reconfigure decoder if there is a change in sequence params.
*/
int ReconfigureDecoder(CUVIDEOFORMAT *pVideoFormat);
private:
CudaFunctions *m_cudl = NULL;
CuvidFunctions *m_cvdl = NULL;
CUcontext m_cuContext = NULL;
CUvideoctxlock m_ctxLock;
CUvideoparser m_hParser = NULL;
CUvideodecoder m_hDecoder = NULL;
bool m_bUseDeviceFrame;
// dimension of the output
unsigned int m_nWidth = 0, m_nLumaHeight = 0, m_nChromaHeight = 0;
unsigned int m_nNumChromaPlanes = 0;
// height of the mapped surface
int m_nSurfaceHeight = 0;
int m_nSurfaceWidth = 0;
cudaVideoCodec m_eCodec = cudaVideoCodec_NumCodecs;
cudaVideoChromaFormat m_eChromaFormat = cudaVideoChromaFormat_420;
cudaVideoSurfaceFormat m_eOutputFormat = cudaVideoSurfaceFormat_NV12;
int m_nBitDepthMinus8 = 0;
int m_nBPP = 1;
CUVIDEOFORMAT m_videoFormat = {};
Rect m_displayRect = {};
// stock of frames
std::vector<uint8_t *> m_vpFrame;
// timestamps of decoded frames
std::vector<int64_t> m_vTimestamp;
int m_nDecodedFrame = 0, m_nDecodedFrameReturned = 0;
int m_nDecodePicCnt = 0, m_nPicNumInDecodeOrder[MAX_FRM_CNT];
CUVIDSEIMESSAGEINFO *m_pCurrSEIMessage = NULL;
CUVIDSEIMESSAGEINFO m_SEIMessagesDisplayOrder[MAX_FRM_CNT];
FILE *m_fpSEI = NULL;
bool m_bEndDecodeDone = false;
std::mutex m_mtxVPFrame;
int m_nFrameAlloc = 0;
CUstream m_cuvidStream = 0;
bool m_bDeviceFramePitched = false;
size_t m_nDeviceFramePitch = 0;
Rect m_cropRect = {};
Dim m_resizeDim = {};
std::ostringstream m_videoInfo;
unsigned int m_nMaxWidth = 0, m_nMaxHeight = 0;
bool m_bReconfigExternal = false;
bool m_bReconfigExtPPChange = false;
StopWatch m_stDecode_time;
unsigned int m_nOperatingPoint = 0;
bool m_bDispAllLayers = false;
// In H.264, there is an inherent display latency for video contents
// which do not have num_reorder_frames=0 in the VUI. This applies to
// All-Intra and IPPP sequences as well. If the user wants zero display
// latency for All-Intra and IPPP sequences, the below flag will enable
// the display callback immediately after the decode callback.
bool m_bForce_zero_latency = false;
bool m_bExtractSEIMessage = false;
// my variables
CUVIDEOFORMAT m_latestVideoFormat = {};
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,483 @@
/*
* This copyright notice applies to this header file only:
*
* Copyright (c) 2010-2023 NVIDIA Corporation
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the software, and to permit persons to whom the
* software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#pragma once
#include <vector>
#include "nvEncodeAPI.h"
#include <stdint.h>
#include <mutex>
#include <string>
#include <iostream>
#include <sstream>
#include <string.h>
#include "dynlink_loader.h"
#include "../Utils/NvCodecUtils.h"
struct NvPacket
{
std::vector<uint8_t> data;
NV_ENC_PIC_TYPE pictureType;
NvPacket(std::vector<uint8_t> data, NV_ENC_PIC_TYPE pictureType):
data(data), pictureType(pictureType)
{}
NvPacket(): data(std::vector<uint8_t>()), pictureType(NV_ENC_PIC_TYPE_UNKNOWN)
{}
};
/**
* @brief Exception class for error reporting from NvEncodeAPI calls.
*/
class NVENCException : public std::exception
{
public:
NVENCException(const std::string& errorStr, const NVENCSTATUS errorCode)
: m_errorString(errorStr), m_errorCode(errorCode) {}
virtual ~NVENCException() throw() {}
virtual const char* what() const throw() { return m_errorString.c_str(); }
NVENCSTATUS getErrorCode() const { return m_errorCode; }
const std::string& getErrorString() const { return m_errorString; }
static NVENCException makeNVENCException(const std::string& errorStr, const NVENCSTATUS errorCode,
const std::string& functionName, const std::string& fileName, int lineNo);
private:
std::string m_errorString;
NVENCSTATUS m_errorCode;
};
inline NVENCException NVENCException::makeNVENCException(const std::string& errorStr, const NVENCSTATUS errorCode, const std::string& functionName,
const std::string& fileName, int lineNo)
{
std::ostringstream errorLog;
errorLog << functionName << " : " << errorStr << " at " << fileName << ":" << lineNo << std::endl;
NVENCException exception(errorLog.str(), errorCode);
return exception;
}
#define NVENC_THROW_ERROR( errorStr, errorCode ) \
do \
{ \
throw NVENCException::makeNVENCException(errorStr, errorCode, __FUNCTION__, __FILE__, __LINE__); \
} while (0)
#define NVENC_API_CALL( nvencAPI ) \
do \
{ \
NVENCSTATUS errorCode = nvencAPI; \
if( errorCode != NV_ENC_SUCCESS) \
{ \
std::ostringstream errorLog; \
errorLog << #nvencAPI << " returned error " << errorCode; \
throw NVENCException::makeNVENCException(errorLog.str(), errorCode, __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<NvPacket> &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<NvPacket> &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 run motion estimation
* This is used to run motion estimation on a a pair of frames. The
* application must copy the reference frame data to the buffer obtained
* by calling GetNextReferenceFrame(), and copy the input frame data to
* the buffer obtained by calling GetNextInputFrame() before calling the
* RunMotionEstimation() function.
*/
void RunMotionEstimation(std::vector<uint8_t> &mvData);
/**
* @brief This function is used to get an available reference frame.
* Application must call this function to get a pointer to reference buffer,
* to be used in the subsequent RunMotionEstimation() function.
*/
const NvEncInputFrame* GetNextReferenceFrame();
/**
* @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; }
/*
* @brief This function returns initializeParams(width, height, fps etc).
*/
NV_ENC_INITIALIZE_PARAMS GetinitializeParams() const { return m_initializeParams; }
protected:
/**
* @brief NvEncoder class constructor.
* NvEncoder class constructor cannot be called directly by the application.
*/
NvEncoder(NvencFunctions *nvenc_dl, NV_ENC_DEVICE_TYPE eDeviceType, void *pDevice, uint32_t nWidth, uint32_t nHeight,
NV_ENC_BUFFER_FORMAT eBufferFormat, uint32_t nOutputDelay, bool bMotionEstimationOnly, bool bOutputInVideoMemory = false, bool bDX12Encode = false,
bool bUseIVFContainer = true);
/**
* @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);
/**
* @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<NvPacket> &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 initialize MV output buffers.
* This is only used in ME-only Mode.
*/
void InitializeMVOutputBuffer();
/**
* @brief This is a private function which is used to destroy MV output buffers.
* This is only used in ME-only Mode.
*/
void DestroyMVOutputBuffer();
/**
* @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:
bool m_bMotionEstimationOnly = false;
bool m_bOutputInVideoMemory = false;
bool m_bIsDX12Encode = false;
void *m_hEncoder = nullptr;
NV_ENCODE_API_FUNCTION_LIST m_nvenc;
NV_ENC_INITIALIZE_PARAMS m_initializeParams = {};
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;
IVFUtils m_IVFUtils;
bool m_bWriteIVFFileHeader = true;
bool m_bUseIVFContainer = true;
private:
NvencFunctions *m_nvenc_dl = NULL;
uint32_t m_nWidth;
uint32_t m_nHeight;
NV_ENC_BUFFER_FORMAT m_eBufferFormat;
void *m_pDevice;
NV_ENC_DEVICE_TYPE m_eDeviceType;
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;
std::vector<NV_ENC_OUTPUT_PTR> m_vMVDataOutputBuffer;
uint32_t m_nMaxEncodeWidth = 0;
uint32_t m_nMaxEncodeHeight = 0;
};

View File

@@ -0,0 +1,163 @@
/*
* This copyright notice applies to this header file only:
*
* Copyright (c) 2010-2023 NVIDIA Corporation
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the software, and to permit persons to whom the
* software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
// #ifndef WIN32
// #include <dlfcn.h>
// #endif
#include "NvEncoder/NvEncoderD3D11.h"
#include <D3D9Types.h>
#ifndef MAKEFOURCC
#define MAKEFOURCC(a,b,c,d) (((unsigned int)a) | (((unsigned int)b)<< 8) | (((unsigned int)c)<<16) | (((unsigned int)d)<<24) )
#endif
DXGI_FORMAT GetD3D11Format(NV_ENC_BUFFER_FORMAT eBufferFormat)
{
switch (eBufferFormat)
{
case NV_ENC_BUFFER_FORMAT_NV12:
return DXGI_FORMAT_NV12;
case NV_ENC_BUFFER_FORMAT_ARGB:
return DXGI_FORMAT_B8G8R8A8_UNORM;
default:
return DXGI_FORMAT_UNKNOWN;
}
}
NvEncoderD3D11::NvEncoderD3D11(CudaFunctions *cuda_dl, NvencFunctions *nvenc_dl, ID3D11Device* pD3D11Device, uint32_t nWidth, uint32_t nHeight,
NV_ENC_BUFFER_FORMAT eBufferFormat, uint32_t nExtraOutputDelay, bool bMotionEstimationOnly, bool bOutputInVideoMemory) :
NvEncoder(nvenc_dl, NV_ENC_DEVICE_TYPE_DIRECTX, pD3D11Device, nWidth, nHeight, eBufferFormat, nExtraOutputDelay, bMotionEstimationOnly, bOutputInVideoMemory)
{
if (!pD3D11Device)
{
NVENC_THROW_ERROR("Bad d3d11device ptr", NV_ENC_ERR_INVALID_PTR);
return;
}
if (GetD3D11Format(GetPixelFormat()) == DXGI_FORMAT_UNKNOWN)
{
NVENC_THROW_ERROR("Unsupported Buffer format", NV_ENC_ERR_INVALID_PARAM);
}
if (!m_hEncoder)
{
NVENC_THROW_ERROR("Encoder Initialization failed", NV_ENC_ERR_INVALID_DEVICE);
}
m_pD3D11Device = pD3D11Device;
m_pD3D11Device->AddRef();
m_pD3D11Device->GetImmediateContext(&m_pD3D11DeviceContext);
}
NvEncoderD3D11::~NvEncoderD3D11()
{
ReleaseD3D11Resources();
}
void NvEncoderD3D11::AllocateInputBuffers(int32_t numInputBuffers)
{
if (!IsHWEncoderInitialized())
{
NVENC_THROW_ERROR("Encoder intialization failed", NV_ENC_ERR_ENCODER_NOT_INITIALIZED);
}
// for MEOnly mode we need to allocate seperate set of buffers for reference frame
int numCount = m_bMotionEstimationOnly ? 2 : 1;
for (int count = 0; count < numCount; count++)
{
std::vector<void*> inputFrames;
for (int i = 0; i < numInputBuffers; i++)
{
ID3D11Texture2D *pInputTextures = NULL;
D3D11_TEXTURE2D_DESC desc;
ZeroMemory(&desc, sizeof(D3D11_TEXTURE2D_DESC));
desc.Width = GetMaxEncodeWidth();
desc.Height = GetMaxEncodeHeight();
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = GetD3D11Format(GetPixelFormat());
desc.SampleDesc.Count = 1;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = D3D11_BIND_RENDER_TARGET;
desc.CPUAccessFlags = 0;
if (m_pD3D11Device->CreateTexture2D(&desc, NULL, &pInputTextures) != S_OK)
{
NVENC_THROW_ERROR("Failed to create d3d11textures", NV_ENC_ERR_OUT_OF_MEMORY);
}
inputFrames.push_back(pInputTextures);
}
RegisterInputResources(inputFrames, NV_ENC_INPUT_RESOURCE_TYPE_DIRECTX,
GetMaxEncodeWidth(), GetMaxEncodeHeight(), 0, GetPixelFormat(), count == 1 ? true : false);
}
}
void NvEncoderD3D11::ReleaseInputBuffers()
{
ReleaseD3D11Resources();
}
void NvEncoderD3D11::ReleaseD3D11Resources()
{
if (!m_hEncoder)
{
return;
}
UnregisterInputResources();
for (uint32_t i = 0; i < m_vInputFrames.size(); ++i)
{
if (m_vInputFrames[i].inputPtr)
{
reinterpret_cast<ID3D11Texture2D*>(m_vInputFrames[i].inputPtr)->Release();
}
}
m_vInputFrames.clear();
for (uint32_t i = 0; i < m_vReferenceFrames.size(); ++i)
{
if (m_vReferenceFrames[i].inputPtr)
{
reinterpret_cast<ID3D11Texture2D*>(m_vReferenceFrames[i].inputPtr)->Release();
}
}
m_vReferenceFrames.clear();
if (m_pD3D11DeviceContext)
{
m_pD3D11DeviceContext->Release();
m_pD3D11DeviceContext = nullptr;
}
if (m_pD3D11Device)
{
m_pD3D11Device->Release();
m_pD3D11Device = nullptr;
}
}

View File

@@ -0,0 +1,71 @@
/*
* This copyright notice applies to this header file only:
*
* Copyright (c) 2010-2023 NVIDIA Corporation
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the software, and to permit persons to whom the
* software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#pragma once
#include <vector>
#include <stdint.h>
#include <mutex>
#include <unordered_map>
#include <d3d11.h>
#include "NvEncoder.h"
class NvEncoderD3D11 : public NvEncoder
{
public:
NvEncoderD3D11(CudaFunctions *cuda_dl, NvencFunctions *nvenc_dl, ID3D11Device* pD3D11Device, uint32_t nWidth, uint32_t nHeight, NV_ENC_BUFFER_FORMAT eBufferFormat,
uint32_t nExtraOutputDelay = 3, bool bMotionEstimationOnly = false, bool bOPInVideoMemory = false);
virtual ~NvEncoderD3D11();
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().
* This function creates ID3D11Texture2D textures which is used to accept input data.
* To obtain handle to input buffers application must call NvEncoder::GetNextInputFrame()
*/
virtual void AllocateInputBuffers(int32_t numInputBuffers) override;
private:
/**
* @brief This is a private function to release ID3D11Texture2D textures used for encoding.
*/
void ReleaseD3D11Resources();
protected:
ID3D11Device *m_pD3D11Device = nullptr;
private:
ID3D11DeviceContext* m_pD3D11DeviceContext = nullptr;
};

View File

@@ -0,0 +1,256 @@
/*
* This copyright notice applies to this header file only:
*
* Copyright (c) 2010-2023 NVIDIA Corporation
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the software, and to permit persons to whom the
* software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#pragma once
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <mutex>
#include <time.h>
#ifdef _WIN32
#include <winsock.h>
#include <windows.h>
#pragma comment(lib, "ws2_32.lib")
#undef ERROR
#else
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define SOCKET int
#define INVALID_SOCKET -1
#endif
enum LogLevel {
TRACE,
INFO,
WARNING,
ERROR,
FATAL
};
namespace simplelogger{
class Logger {
public:
Logger(LogLevel level, bool bPrintTimeStamp) : level(level), bPrintTimeStamp(bPrintTimeStamp) {}
virtual ~Logger() {}
virtual std::ostream& GetStream() = 0;
virtual void FlushStream() {}
bool ShouldLogFor(LogLevel l) {
return l >= level;
}
char* GetLead(LogLevel l, const char *szFile, int nLine, const char *szFunc) {
if (l < TRACE || l > FATAL) {
sprintf(szLead, "[?????] ");
return szLead;
}
const char *szLevels[] = {"TRACE", "INFO", "WARN", "ERROR", "FATAL"};
if (bPrintTimeStamp) {
time_t t = time(NULL);
struct tm *ptm = localtime(&t);
sprintf(szLead, "[%-5s][%02d:%02d:%02d] ",
szLevels[l], ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
} else {
sprintf(szLead, "[%-5s] ", szLevels[l]);
}
return szLead;
}
void EnterCriticalSection() {
mtx.lock();
}
void LeaveCriticalSection() {
mtx.unlock();
}
private:
LogLevel level;
char szLead[80];
bool bPrintTimeStamp;
std::mutex mtx;
};
class LoggerFactory {
public:
static Logger* CreateFileLogger(std::string strFilePath,
LogLevel level = INFO, bool bPrintTimeStamp = true) {
return new FileLogger(strFilePath, level, bPrintTimeStamp);
}
static Logger* CreateConsoleLogger(LogLevel level = INFO,
bool bPrintTimeStamp = true) {
return new ConsoleLogger(level, bPrintTimeStamp);
}
static Logger* CreateUdpLogger(char *szHost, unsigned uPort, LogLevel level = INFO,
bool bPrintTimeStamp = true) {
return new UdpLogger(szHost, uPort, level, bPrintTimeStamp);
}
private:
LoggerFactory() {}
class FileLogger : public Logger {
public:
FileLogger(std::string strFilePath, LogLevel level, bool bPrintTimeStamp)
: Logger(level, bPrintTimeStamp) {
pFileOut = new std::ofstream();
pFileOut->open(strFilePath.c_str());
}
~FileLogger() {
pFileOut->close();
}
std::ostream& GetStream() {
return *pFileOut;
}
private:
std::ofstream *pFileOut;
};
class ConsoleLogger : public Logger {
public:
ConsoleLogger(LogLevel level, bool bPrintTimeStamp)
: Logger(level, bPrintTimeStamp) {}
std::ostream& GetStream() {
return std::cout;
}
};
class UdpLogger : public Logger {
private:
class UdpOstream : public std::ostream {
public:
UdpOstream(char *szHost, unsigned short uPort) : std::ostream(&sb), socket(INVALID_SOCKET){
#ifdef _WIN32
WSADATA w;
if (WSAStartup(0x0101, &w) != 0) {
fprintf(stderr, "WSAStartup() failed.\n");
return;
}
#endif
socket = ::socket(AF_INET, SOCK_DGRAM, 0);
if (socket == INVALID_SOCKET) {
#ifdef _WIN32
WSACleanup();
#endif
fprintf(stderr, "socket() failed.\n");
return;
}
#ifdef _WIN32
unsigned int b1, b2, b3, b4;
sscanf(szHost, "%u.%u.%u.%u", &b1, &b2, &b3, &b4);
struct in_addr addr = {(unsigned char)b1, (unsigned char)b2, (unsigned char)b3, (unsigned char)b4};
#else
struct in_addr addr = {inet_addr(szHost)};
#endif
struct sockaddr_in s = {AF_INET, htons(uPort), addr};
server = s;
}
~UdpOstream() throw() {
if (socket == INVALID_SOCKET) {
return;
}
#ifdef _WIN32
closesocket(socket);
WSACleanup();
#else
close(socket);
#endif
}
void Flush() {
if (sendto(socket, sb.str().c_str(), (int)sb.str().length() + 1,
0, (struct sockaddr *)&server, (int)sizeof(sockaddr_in)) == -1) {
fprintf(stderr, "sendto() failed.\n");
}
sb.str("");
}
private:
std::stringbuf sb;
SOCKET socket;
struct sockaddr_in server;
};
public:
UdpLogger(char *szHost, unsigned uPort, LogLevel level, bool bPrintTimeStamp)
: Logger(level, bPrintTimeStamp), udpOut(szHost, (unsigned short)uPort) {}
UdpOstream& GetStream() {
return udpOut;
}
virtual void FlushStream() {
udpOut.Flush();
}
private:
UdpOstream udpOut;
};
};
class LogTransaction {
public:
LogTransaction(Logger *pLogger, LogLevel level, const char *szFile, const int nLine, const char *szFunc) : pLogger(pLogger), level(level) {
if (!pLogger) {
std::cout << "[-----] ";
return;
}
if (!pLogger->ShouldLogFor(level)) {
return;
}
pLogger->EnterCriticalSection();
pLogger->GetStream() << pLogger->GetLead(level, szFile, nLine, szFunc);
}
~LogTransaction() {
if (!pLogger) {
std::cout << std::endl;
return;
}
if (!pLogger->ShouldLogFor(level)) {
return;
}
pLogger->GetStream() << std::endl;
pLogger->FlushStream();
pLogger->LeaveCriticalSection();
if (level == FATAL) {
exit(1);
}
}
std::ostream& GetStream() {
if (!pLogger) {
return std::cout;
}
if (!pLogger->ShouldLogFor(level)) {
return ossNull;
}
return pLogger->GetStream();
}
private:
Logger *pLogger;
LogLevel level;
std::ostringstream ossNull;
};
}
extern simplelogger::Logger *logger;
#define LOG(level) simplelogger::LogTransaction(logger, level, __FILE__, __LINE__, __FUNCTION__).GetStream()

View File

@@ -0,0 +1,552 @@
/*
* This copyright notice applies to this header file only:
*
* Copyright (c) 2010-2023 NVIDIA Corporation
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the software, and to permit persons to whom the
* software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
//---------------------------------------------------------------------------
//! \file NvCodecUtils.h
//! \brief Miscellaneous classes and error checking functions.
//!
//! Used by Transcode/Encode samples apps for reading input files, mutithreading, performance measurement or colorspace conversion while decoding.
//---------------------------------------------------------------------------
#pragma once
#include <iomanip>
#include <chrono>
#include <sys/stat.h>
#include <assert.h>
#include <stdint.h>
#include <string.h>
#include "Logger.h"
#include <ios>
#include <sstream>
#include <thread>
#include <list>
#include <vector>
#include <condition_variable>
extern simplelogger::Logger *logger;
#ifdef __cuda_cuda_h__
inline bool check(CUresult e, int iLine, const char *szFile) {
if (e != CUDA_SUCCESS) {
const char *szErrName = NULL;
cuGetErrorName(e, &szErrName);
LOG(FATAL) << "CUDA driver API error " << szErrName << " at line " << iLine << " in file " << szFile;
return false;
}
return true;
}
#endif
#ifdef __CUDA_RUNTIME_H__
inline bool check(cudaError_t e, int iLine, const char *szFile) {
if (e != cudaSuccess) {
LOG(FATAL) << "CUDA runtime API error " << cudaGetErrorName(e) << " at line " << iLine << " in file " << szFile;
return false;
}
return true;
}
#endif
#ifdef _NV_ENCODEAPI_H_
inline bool check(NVENCSTATUS e, int iLine, const char *szFile) {
const char *aszErrName[] = {
"NV_ENC_SUCCESS",
"NV_ENC_ERR_NO_ENCODE_DEVICE",
"NV_ENC_ERR_UNSUPPORTED_DEVICE",
"NV_ENC_ERR_INVALID_ENCODERDEVICE",
"NV_ENC_ERR_INVALID_DEVICE",
"NV_ENC_ERR_DEVICE_NOT_EXIST",
"NV_ENC_ERR_INVALID_PTR",
"NV_ENC_ERR_INVALID_EVENT",
"NV_ENC_ERR_INVALID_PARAM",
"NV_ENC_ERR_INVALID_CALL",
"NV_ENC_ERR_OUT_OF_MEMORY",
"NV_ENC_ERR_ENCODER_NOT_INITIALIZED",
"NV_ENC_ERR_UNSUPPORTED_PARAM",
"NV_ENC_ERR_LOCK_BUSY",
"NV_ENC_ERR_NOT_ENOUGH_BUFFER",
"NV_ENC_ERR_INVALID_VERSION",
"NV_ENC_ERR_MAP_FAILED",
"NV_ENC_ERR_NEED_MORE_INPUT",
"NV_ENC_ERR_ENCODER_BUSY",
"NV_ENC_ERR_EVENT_NOT_REGISTERD",
"NV_ENC_ERR_GENERIC",
"NV_ENC_ERR_INCOMPATIBLE_CLIENT_KEY",
"NV_ENC_ERR_UNIMPLEMENTED",
"NV_ENC_ERR_RESOURCE_REGISTER_FAILED",
"NV_ENC_ERR_RESOURCE_NOT_REGISTERED",
"NV_ENC_ERR_RESOURCE_NOT_MAPPED",
};
if (e != NV_ENC_SUCCESS) {
LOG(FATAL) << "NVENC error " << aszErrName[e] << " at line " << iLine << " in file " << szFile;
return false;
}
return true;
}
#endif
#ifdef _WINERROR_
inline bool check(HRESULT e, int iLine, const char *szFile) {
if (e != S_OK) {
std::stringstream stream;
stream << std::hex << std::uppercase << e;
LOG(FATAL) << "HRESULT error 0x" << stream.str() << " at line " << iLine << " in file " << szFile;
return false;
}
return true;
}
#endif
#if defined(__gl_h_) || defined(__GL_H__)
inline bool check(GLenum e, int iLine, const char *szFile) {
if (e != 0) {
LOG(ERROR) << "GLenum error " << e << " at line " << iLine << " in file " << szFile;
return false;
}
return true;
}
#endif
inline bool check(int e, int iLine, const char *szFile) {
if (e < 0) {
LOG(ERROR) << "General error " << e << " at line " << iLine << " in file " << szFile;
return false;
}
return true;
}
#define ck(call) check(call, __LINE__, __FILE__)
#define MAKE_FOURCC( ch0, ch1, ch2, ch3 ) \
( (uint32_t)(uint8_t)(ch0) | ( (uint32_t)(uint8_t)(ch1) << 8 ) | \
( (uint32_t)(uint8_t)(ch2) << 16 ) | ( (uint32_t)(uint8_t)(ch3) << 24 ) )
/**
* @brief Wrapper class around std::thread
*/
class NvThread
{
public:
NvThread() = default;
NvThread(const NvThread&) = delete;
NvThread& operator=(const NvThread& other) = delete;
NvThread(std::thread&& thread) : t(std::move(thread))
{
}
NvThread(NvThread&& thread) : t(std::move(thread.t))
{
}
NvThread& operator=(NvThread&& other)
{
t = std::move(other.t);
return *this;
}
~NvThread()
{
join();
}
void join()
{
if (t.joinable())
{
t.join();
}
}
private:
std::thread t;
};
#ifndef _WIN32
#define _stricmp strcasecmp
#define _stat64 stat64
#endif
/**
* @brief Utility class to allocate buffer memory. Helps avoid I/O during the encode/decode loop in case of performance tests.
*/
class BufferedFileReader {
public:
/**
* @brief Constructor function to allocate appropriate memory and copy file contents into it
*/
BufferedFileReader(const char *szFileName, bool bPartial = false) {
struct _stat64 st;
if (_stat64(szFileName, &st) != 0) {
return;
}
nSize = st.st_size;
while (nSize) {
try {
pBuf = new uint8_t[(size_t)nSize];
if (nSize != st.st_size) {
LOG(WARNING) << "File is too large - only " << std::setprecision(4) << 100.0 * nSize / st.st_size << "% is loaded";
}
break;
} catch(std::bad_alloc) {
if (!bPartial) {
LOG(ERROR) << "Failed to allocate memory in BufferedReader";
return;
}
nSize = (uint32_t)(nSize * 0.9);
}
}
std::ifstream fpIn(szFileName, std::ifstream::in | std::ifstream::binary);
if (!fpIn)
{
LOG(ERROR) << "Unable to open input file: " << szFileName;
return;
}
std::streamsize nRead = fpIn.read(reinterpret_cast<char*>(pBuf), nSize).gcount();
fpIn.close();
assert(nRead == nSize);
}
~BufferedFileReader() {
if (pBuf) {
delete[] pBuf;
}
}
bool GetBuffer(uint8_t **ppBuf, uint64_t *pnSize) {
if (!pBuf) {
return false;
}
*ppBuf = pBuf;
*pnSize = nSize;
return true;
}
private:
uint8_t *pBuf = NULL;
uint64_t nSize = 0;
};
/**
* @brief Template class to facilitate color space conversion
*/
template<typename T>
class YuvConverter {
public:
YuvConverter(int nWidth, int nHeight) : nWidth(nWidth), nHeight(nHeight) {
pQuad = new T[((nWidth + 1) / 2) * ((nHeight + 1) / 2)];
}
~YuvConverter() {
delete[] pQuad;
}
void PlanarToUVInterleaved(T *pFrame, int nPitch = 0) {
if (nPitch == 0) {
nPitch = nWidth;
}
// sizes of source surface plane
int nSizePlaneY = nPitch * nHeight;
int nSizePlaneU = ((nPitch + 1) / 2) * ((nHeight + 1) / 2);
int nSizePlaneV = nSizePlaneU;
T *puv = pFrame + nSizePlaneY;
if (nPitch == nWidth) {
memcpy(pQuad, puv, nSizePlaneU * sizeof(T));
} else {
for (int i = 0; i < (nHeight + 1) / 2; i++) {
memcpy(pQuad + ((nWidth + 1) / 2) * i, puv + ((nPitch + 1) / 2) * i, ((nWidth + 1) / 2) * sizeof(T));
}
}
T *pv = puv + nSizePlaneU;
for (int y = 0; y < (nHeight + 1) / 2; y++) {
for (int x = 0; x < (nWidth + 1) / 2; x++) {
puv[y * nPitch + x * 2] = pQuad[y * ((nWidth + 1) / 2) + x];
puv[y * nPitch + x * 2 + 1] = pv[y * ((nPitch + 1) / 2) + x];
}
}
}
void UVInterleavedToPlanar(T *pFrame, int nPitch = 0) {
if (nPitch == 0) {
nPitch = nWidth;
}
// sizes of source surface plane
int nSizePlaneY = nPitch * nHeight;
int nSizePlaneU = ((nPitch + 1) / 2) * ((nHeight + 1) / 2);
int nSizePlaneV = nSizePlaneU;
T *puv = pFrame + nSizePlaneY,
*pu = puv,
*pv = puv + nSizePlaneU;
// split chroma from interleave to planar
for (int y = 0; y < (nHeight + 1) / 2; y++) {
for (int x = 0; x < (nWidth + 1) / 2; x++) {
pu[y * ((nPitch + 1) / 2) + x] = puv[y * nPitch + x * 2];
pQuad[y * ((nWidth + 1) / 2) + x] = puv[y * nPitch + x * 2 + 1];
}
}
if (nPitch == nWidth) {
memcpy(pv, pQuad, nSizePlaneV * sizeof(T));
} else {
for (int i = 0; i < (nHeight + 1) / 2; i++) {
memcpy(pv + ((nPitch + 1) / 2) * i, pQuad + ((nWidth + 1) / 2) * i, ((nWidth + 1) / 2) * sizeof(T));
}
}
}
private:
T *pQuad;
int nWidth, nHeight;
};
/**
* @brief Class for writing IVF format header for AV1 codec
*/
class IVFUtils {
public:
void WriteFileHeader(std::vector<uint8_t> &vPacket, uint32_t nFourCC, uint32_t nWidth, uint32_t nHeight, uint32_t nFrameRateNum, uint32_t nFrameRateDen, uint32_t nFrameCnt)
{
char header[32];
header[0] = 'D';
header[1] = 'K';
header[2] = 'I';
header[3] = 'F';
mem_put_le16(header + 4, 0); // version
mem_put_le16(header + 6, 32); // header size
mem_put_le32(header + 8, nFourCC); // fourcc
mem_put_le16(header + 12, nWidth); // width
mem_put_le16(header + 14, nHeight); // height
mem_put_le32(header + 16, nFrameRateNum); // rate
mem_put_le32(header + 20, nFrameRateDen); // scale
mem_put_le32(header + 24, nFrameCnt); // length
mem_put_le32(header + 28, 0); // unused
vPacket.insert(vPacket.end(), &header[0], &header[32]);
}
void WriteFrameHeader(std::vector<uint8_t> &vPacket, size_t nFrameSize, int64_t pts)
{
char header[12];
mem_put_le32(header, (int)nFrameSize);
mem_put_le32(header + 4, (int)(pts & 0xFFFFFFFF));
mem_put_le32(header + 8, (int)(pts >> 32));
vPacket.insert(vPacket.end(), &header[0], &header[12]);
}
private:
static inline void mem_put_le32(void *vmem, int val)
{
unsigned char *mem = (unsigned char *)vmem;
mem[0] = (unsigned char)((val >> 0) & 0xff);
mem[1] = (unsigned char)((val >> 8) & 0xff);
mem[2] = (unsigned char)((val >> 16) & 0xff);
mem[3] = (unsigned char)((val >> 24) & 0xff);
}
static inline void mem_put_le16(void *vmem, int val)
{
unsigned char *mem = (unsigned char *)vmem;
mem[0] = (unsigned char)((val >> 0) & 0xff);
mem[1] = (unsigned char)((val >> 8) & 0xff);
}
};
/**
* @brief Utility class to measure elapsed time in seconds between the block of executed code
*/
class StopWatch {
public:
void Start() {
t0 = std::chrono::high_resolution_clock::now();
}
double Stop() {
return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch() - t0.time_since_epoch()).count() / 1.0e9;
}
private:
std::chrono::high_resolution_clock::time_point t0;
};
template<typename T>
class ConcurrentQueue
{
public:
ConcurrentQueue() {}
ConcurrentQueue(size_t size) : maxSize(size) {}
ConcurrentQueue(const ConcurrentQueue&) = delete;
ConcurrentQueue& operator=(const ConcurrentQueue&) = delete;
void setSize(size_t s) {
maxSize = s;
}
void push_back(const T& value) {
// Do not use a std::lock_guard here. We will need to explicitly
// unlock before notify_one as the other waiting thread will
// automatically try to acquire mutex once it wakes up
// (which will happen on notify_one)
std::unique_lock<std::mutex> lock(m_mutex);
auto wasEmpty = m_List.empty();
while (full()) {
m_cond.wait(lock);
}
m_List.push_back(value);
if (wasEmpty && !m_List.empty()) {
lock.unlock();
m_cond.notify_one();
}
}
T pop_front() {
std::unique_lock<std::mutex> lock(m_mutex);
while (m_List.empty()) {
m_cond.wait(lock);
}
auto wasFull = full();
T data = std::move(m_List.front());
m_List.pop_front();
if (wasFull && !full()) {
lock.unlock();
m_cond.notify_one();
}
return data;
}
T front() {
std::unique_lock<std::mutex> lock(m_mutex);
while (m_List.empty()) {
m_cond.wait(lock);
}
return m_List.front();
}
size_t size() {
std::unique_lock<std::mutex> lock(m_mutex);
return m_List.size();
}
bool empty() {
std::unique_lock<std::mutex> lock(m_mutex);
return m_List.empty();
}
void clear() {
std::unique_lock<std::mutex> lock(m_mutex);
m_List.clear();
}
private:
bool full() {
if (maxSize > 0 && m_List.size() == maxSize)
return true;
return false;
}
private:
std::list<T> m_List;
std::mutex m_mutex;
std::condition_variable m_cond;
size_t maxSize;
};
inline void CheckInputFile(const char *szInFilePath) {
std::ifstream fpIn(szInFilePath, std::ios::in | std::ios::binary);
if (fpIn.fail()) {
std::ostringstream err;
err << "Unable to open input file: " << szInFilePath << std::endl;
throw std::invalid_argument(err.str());
}
}
inline void ValidateResolution(int nWidth, int nHeight) {
if (nWidth <= 0 || nHeight <= 0) {
std::ostringstream err;
err << "Please specify positive non zero resolution as -s WxH. Current resolution is " << nWidth << "x" << nHeight << std::endl;
throw std::invalid_argument(err.str());
}
}
template <class COLOR32>
void Nv12ToColor32(uint8_t *dpNv12, int nNv12Pitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix = 0);
template <class COLOR64>
void Nv12ToColor64(uint8_t *dpNv12, int nNv12Pitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix = 0);
template <class COLOR32>
void P016ToColor32(uint8_t *dpP016, int nP016Pitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix = 4);
template <class COLOR64>
void P016ToColor64(uint8_t *dpP016, int nP016Pitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix = 4);
template <class COLOR32>
void YUV444ToColor32(uint8_t *dpYUV444, int nPitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix = 0);
template <class COLOR64>
void YUV444ToColor64(uint8_t *dpYUV444, int nPitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix = 0);
template <class COLOR32>
void YUV444P16ToColor32(uint8_t *dpYUV444, int nPitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix = 4);
template <class COLOR64>
void YUV444P16ToColor64(uint8_t *dpYUV444, int nPitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix = 4);
template <class COLOR32>
void Nv12ToColorPlanar(uint8_t *dpNv12, int nNv12Pitch, uint8_t *dpBgrp, int nBgrpPitch, int nWidth, int nHeight, int iMatrix = 0);
template <class COLOR32>
void P016ToColorPlanar(uint8_t *dpP016, int nP016Pitch, uint8_t *dpBgrp, int nBgrpPitch, int nWidth, int nHeight, int iMatrix = 4);
template <class COLOR32>
void YUV444ToColorPlanar(uint8_t *dpYUV444, int nPitch, uint8_t *dpBgrp, int nBgrpPitch, int nWidth, int nHeight, int iMatrix = 0);
template <class COLOR32>
void YUV444P16ToColorPlanar(uint8_t *dpYUV444, int nPitch, uint8_t *dpBgrp, int nBgrpPitch, int nWidth, int nHeight, int iMatrix = 4);
void Bgra64ToP016(uint8_t *dpBgra, int nBgraPitch, uint8_t *dpP016, int nP016Pitch, int nWidth, int nHeight, int iMatrix = 4);
void ConvertUInt8ToUInt16(uint8_t *dpUInt8, uint16_t *dpUInt16, int nSrcPitch, int nDestPitch, int nWidth, int nHeight);
void ConvertUInt16ToUInt8(uint16_t *dpUInt16, uint8_t *dpUInt8, int nSrcPitch, int nDestPitch, int nWidth, int nHeight);
void ResizeNv12(unsigned char *dpDstNv12, int nDstPitch, int nDstWidth, int nDstHeight, unsigned char *dpSrcNv12, int nSrcPitch, int nSrcWidth, int nSrcHeight, unsigned char *dpDstNv12UV = nullptr);
void ResizeP016(unsigned char *dpDstP016, int nDstPitch, int nDstWidth, int nDstHeight, unsigned char *dpSrcP016, int nSrcPitch, int nSrcWidth, int nSrcHeight, unsigned char *dpDstP016UV = nullptr);
void ScaleYUV420(unsigned char *dpDstY, unsigned char* dpDstU, unsigned char* dpDstV, int nDstPitch, int nDstChromaPitch, int nDstWidth, int nDstHeight,
unsigned char *dpSrcY, unsigned char* dpSrcU, unsigned char* dpSrcV, int nSrcPitch, int nSrcChromaPitch, int nSrcWidth, int nSrcHeight, bool bSemiplanar);
#ifdef __cuda_cuda_h__
void ComputeCRC(uint8_t *pBuffer, uint32_t *crcValue, CUstream_st *outputCUStream);
#endif

View File

@@ -0,0 +1,836 @@
/*
* This copyright notice applies to this header file only:
*
* Copyright (c) 2010-2023 NVIDIA Corporation
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the software, and to permit persons to whom the
* software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#pragma once
#include <vector>
#include <string>
#include <algorithm>
#include <stdexcept>
#include <sstream>
#include <iterator>
#include <cstring>
#include <functional>
#include "../Utils/Logger.h"
extern simplelogger::Logger *logger;
#ifndef _WIN32
inline bool operator==(const GUID &guid1, const GUID &guid2) {
return !memcmp(&guid1, &guid2, sizeof(GUID));
}
inline bool operator!=(const GUID &guid1, const GUID &guid2) {
return !(guid1 == guid2);
}
#endif
/*
* Helper class for parsing generic encoder options and preparing encoder
* initialization parameters. This class also provides some utility methods
* which generate verbose descriptions of the provided set of encoder
* initialization parameters.
*/
class NvEncoderInitParam {
public:
NvEncoderInitParam(const char *szParam = "",
std::function<void(NV_ENC_INITIALIZE_PARAMS *pParams)> *pfuncInit = NULL, bool _bLowLatency = false)
: strParam(szParam), bLowLatency(_bLowLatency)
{
if (pfuncInit) {
funcInit = *pfuncInit;
}
std::transform(strParam.begin(), strParam.end(), strParam.begin(), tolower);
std::istringstream ss(strParam);
tokens = std::vector<std::string> {
std::istream_iterator<std::string>(ss),
std::istream_iterator<std::string>()
};
for (unsigned i = 0; i < tokens.size(); i++)
{
if (tokens[i] == "-codec" && ++i != tokens.size())
{
ParseString("-codec", tokens[i], vCodec, szCodecNames, &guidCodec);
continue;
}
if (tokens[i] == "-preset" && ++i != tokens.size()) {
ParseString("-preset", tokens[i], vPreset, szPresetNames, &guidPreset);
continue;
}
if (tokens[i] == "-tuninginfo" && ++i != tokens.size())
{
ParseString("-tuninginfo", tokens[i], vTuningInfo, szTuningInfoNames, &m_TuningInfo);
continue;
}
}
}
virtual ~NvEncoderInitParam() {}
virtual bool IsCodecH264() {
return GetEncodeGUID() == NV_ENC_CODEC_H264_GUID;
}
virtual bool IsCodecHEVC() {
return GetEncodeGUID() == NV_ENC_CODEC_HEVC_GUID;
}
virtual bool IsCodecAV1() {
return GetEncodeGUID() == NV_ENC_CODEC_AV1_GUID;
}
std::string GetHelpMessage(bool bMeOnly = false, bool bUnbuffered = false, bool bHide444 = false, bool bOutputInVidMem = false)
{
std::ostringstream oss;
if (bOutputInVidMem && bMeOnly)
{
oss << "-codec Codec: " << "h264" << std::endl;
}
else
{
oss << "-codec Codec: " << szCodecNames << std::endl;
}
oss << "-preset Preset: " << szPresetNames << std::endl
<< "-profile H264: " << szH264ProfileNames;
if (bOutputInVidMem && bMeOnly)
{
oss << std::endl;
}
else
{
oss << "; HEVC: " << szHevcProfileNames;
oss << "; AV1: " << szAV1ProfileNames << std::endl;
}
if (!bMeOnly)
{
if (bLowLatency == false)
oss << "-tuninginfo TuningInfo: " << szTuningInfoNames << std::endl;
else
oss << "-tuninginfo TuningInfo: " << szLowLatencyTuningInfoNames << std::endl;
oss << "-multipass Multipass: " << szMultipass << std::endl;
}
if (!bHide444 && !bLowLatency)
{
oss << "-444 (Only for RGB input) YUV444 encode. Not valid for AV1 Codec" << std::endl;
}
if (bMeOnly) return oss.str();
oss << "-fps Frame rate" << std::endl;
if (!bUnbuffered && !bLowLatency)
{
oss << "-bf Number of consecutive B-frames" << std::endl;
}
if (!bLowLatency)
{
oss << "-rc Rate control mode: " << szRcModeNames << std::endl
<< "-gop Length of GOP (Group of Pictures)" << std::endl
<< "-bitrate Average bit rate, can be in unit of 1, K, M" << std::endl
<< "Note: Fps or Average bit rate values for each session can be specified in the form of v1,v1,v3 (no space) for AppTransOneToN" << std::endl
<< " If the number of 'bitrate' or 'fps' values specified are less than the number of sessions, then the last specified value will be considered for the remaining sessions" << std::endl
<< "-maxbitrate Max bit rate, can be in unit of 1, K, M" << std::endl
<< "-vbvbufsize VBV buffer size in bits, can be in unit of 1, K, M" << std::endl
<< "-vbvinit VBV initial delay in bits, can be in unit of 1, K, M" << std::endl
<< "-aq Enable spatial AQ and set its stength (range 1-15, 0-auto)" << std::endl
<< "-temporalaq (No value) Enable temporal AQ" << std::endl
<< "-cq Target constant quality level for VBR mode (range 1-51, 0-auto)" << std::endl;
}
if (!bUnbuffered && !bLowLatency)
{
oss << "-lookahead Maximum depth of lookahead (range 0-(31 - number of B frames))" << std::endl;
}
oss << "-qmin Min QP value" << std::endl
<< "-qmax Max QP value" << std::endl
<< "-initqp Initial QP value" << std::endl;
if (!bLowLatency)
{
oss << "-constqp QP value for constqp rate control mode" << std::endl
<< "Note: QP value can be in the form of qp_of_P_B_I or qp_P,qp_B,qp_I (no space)" << std::endl;
}
if (bUnbuffered && !bLowLatency)
{
oss << "Note: Options -bf and -lookahead are unavailable for this app" << std::endl;
}
return oss.str();
}
/**
* @brief Generate and return a string describing the values of the main/common
* encoder initialization parameters
*/
std::string MainParamToString(const NV_ENC_INITIALIZE_PARAMS *pParams) {
std::ostringstream os;
os
<< "Encoding Parameters:"
<< std::endl << "\tcodec : " << ConvertValueToString(vCodec, szCodecNames, pParams->encodeGUID)
<< std::endl << "\tpreset : " << ConvertValueToString(vPreset, szPresetNames, pParams->presetGUID);
if (pParams->tuningInfo)
{
os << std::endl << "\ttuningInfo : " << ConvertValueToString(vTuningInfo, szTuningInfoNames, pParams->tuningInfo);
}
os
<< std::endl << "\tprofile : " << ConvertValueToString(vProfile, szProfileNames, pParams->encodeConfig->profileGUID)
<< std::endl << "\tchroma : " << ConvertValueToString(vChroma, szChromaNames, (pParams->encodeGUID == NV_ENC_CODEC_H264_GUID) ? pParams->encodeConfig->encodeCodecConfig.h264Config.chromaFormatIDC :
(pParams->encodeGUID == NV_ENC_CODEC_HEVC_GUID) ? pParams->encodeConfig->encodeCodecConfig.hevcConfig.chromaFormatIDC :
pParams->encodeConfig->encodeCodecConfig.av1Config.chromaFormatIDC)
<< std::endl << "\tbitdepth : " << ((pParams->encodeGUID == NV_ENC_CODEC_H264_GUID) ? 0 : (pParams->encodeGUID == NV_ENC_CODEC_HEVC_GUID) ?
pParams->encodeConfig->encodeCodecConfig.hevcConfig.pixelBitDepthMinus8 : pParams->encodeConfig->encodeCodecConfig.av1Config.pixelBitDepthMinus8) + 8
<< std::endl << "\trc : " << ConvertValueToString(vRcMode, szRcModeNames, pParams->encodeConfig->rcParams.rateControlMode)
;
if (pParams->encodeConfig->rcParams.rateControlMode == NV_ENC_PARAMS_RC_CONSTQP) {
os << " (P,B,I=" << pParams->encodeConfig->rcParams.constQP.qpInterP << "," << pParams->encodeConfig->rcParams.constQP.qpInterB << "," << pParams->encodeConfig->rcParams.constQP.qpIntra << ")";
}
os
<< std::endl << "\tfps : " << pParams->frameRateNum << "/" << pParams->frameRateDen
<< std::endl << "\tgop : " << (pParams->encodeConfig->gopLength == NVENC_INFINITE_GOPLENGTH ? "INF" : std::to_string(pParams->encodeConfig->gopLength))
<< std::endl << "\tbf : " << pParams->encodeConfig->frameIntervalP - 1
<< std::endl << "\tmultipass : " << pParams->encodeConfig->rcParams.multiPass
<< std::endl << "\tsize : " << pParams->encodeWidth << "x" << pParams->encodeHeight
<< std::endl << "\tbitrate : " << pParams->encodeConfig->rcParams.averageBitRate
<< std::endl << "\tmaxbitrate : " << pParams->encodeConfig->rcParams.maxBitRate
<< std::endl << "\tvbvbufsize : " << pParams->encodeConfig->rcParams.vbvBufferSize
<< std::endl << "\tvbvinit : " << pParams->encodeConfig->rcParams.vbvInitialDelay
<< std::endl << "\taq : " << (pParams->encodeConfig->rcParams.enableAQ ? (pParams->encodeConfig->rcParams.aqStrength ? std::to_string(pParams->encodeConfig->rcParams.aqStrength) : "auto") : "disabled")
<< std::endl << "\ttemporalaq : " << (pParams->encodeConfig->rcParams.enableTemporalAQ ? "enabled" : "disabled")
<< std::endl << "\tlookahead : " << (pParams->encodeConfig->rcParams.enableLookahead ? std::to_string(pParams->encodeConfig->rcParams.lookaheadDepth) : "disabled")
<< std::endl << "\tcq : " << (unsigned int)pParams->encodeConfig->rcParams.targetQuality
<< std::endl << "\tqmin : P,B,I=" << (int)pParams->encodeConfig->rcParams.minQP.qpInterP << "," << (int)pParams->encodeConfig->rcParams.minQP.qpInterB << "," << (int)pParams->encodeConfig->rcParams.minQP.qpIntra
<< std::endl << "\tqmax : P,B,I=" << (int)pParams->encodeConfig->rcParams.maxQP.qpInterP << "," << (int)pParams->encodeConfig->rcParams.maxQP.qpInterB << "," << (int)pParams->encodeConfig->rcParams.maxQP.qpIntra
<< std::endl << "\tinitqp : P,B,I=" << (int)pParams->encodeConfig->rcParams.initialRCQP.qpInterP << "," << (int)pParams->encodeConfig->rcParams.initialRCQP.qpInterB << "," << (int)pParams->encodeConfig->rcParams.initialRCQP.qpIntra
;
return os.str();
}
public:
virtual GUID GetEncodeGUID() { return guidCodec; }
virtual GUID GetPresetGUID() { return guidPreset; }
virtual NV_ENC_TUNING_INFO GetTuningInfo() { return m_TuningInfo; }
/*
* @brief Set encoder initialization parameters based on input options
* This method parses the tokens formed from the command line options
* provided to the application and sets the fields from NV_ENC_INITIALIZE_PARAMS
* based on the supplied values.
*/
virtual void setTransOneToN(bool isTransOneToN)
{
bTransOneToN = isTransOneToN;
}
virtual void SetInitParams(NV_ENC_INITIALIZE_PARAMS *pParams, NV_ENC_BUFFER_FORMAT eBufferFormat)
{
NV_ENC_CONFIG &config = *pParams->encodeConfig;
int nGOPOption = 0, nBFramesOption = 0;
for (unsigned i = 0; i < tokens.size(); i++)
{
if (
tokens[i] == "-codec" && ++i ||
tokens[i] == "-preset" && ++i ||
tokens[i] == "-tuninginfo" && ++i ||
tokens[i] == "-multipass" && ++i != tokens.size() && ParseString("-multipass", tokens[i], vMultiPass, szMultipass, &config.rcParams.multiPass) ||
tokens[i] == "-profile" && ++i != tokens.size() && (IsCodecH264() ?
ParseString("-profile", tokens[i], vH264Profile, szH264ProfileNames, &config.profileGUID) : IsCodecHEVC() ?
ParseString("-profile", tokens[i], vHevcProfile, szHevcProfileNames, &config.profileGUID) :
ParseString("-profile", tokens[i], vAV1Profile, szAV1ProfileNames, &config.profileGUID)) ||
tokens[i] == "-rc" && ++i != tokens.size() && ParseString("-rc", tokens[i], vRcMode, szRcModeNames, &config.rcParams.rateControlMode) ||
tokens[i] == "-fps" && ++i != tokens.size() && ParseInt("-fps", tokens[i], &pParams->frameRateNum) ||
tokens[i] == "-bf" && ++i != tokens.size() && ParseInt("-bf", tokens[i], &config.frameIntervalP) && ++config.frameIntervalP && ++nBFramesOption ||
tokens[i] == "-bitrate" && ++i != tokens.size() && ParseBitRate("-bitrate", tokens[i], &config.rcParams.averageBitRate) ||
tokens[i] == "-maxbitrate" && ++i != tokens.size() && ParseBitRate("-maxbitrate", tokens[i], &config.rcParams.maxBitRate) ||
tokens[i] == "-vbvbufsize" && ++i != tokens.size() && ParseBitRate("-vbvbufsize", tokens[i], &config.rcParams.vbvBufferSize) ||
tokens[i] == "-vbvinit" && ++i != tokens.size() && ParseBitRate("-vbvinit", tokens[i], &config.rcParams.vbvInitialDelay) ||
tokens[i] == "-cq" && ++i != tokens.size() && ParseInt("-cq", tokens[i], &config.rcParams.targetQuality) ||
tokens[i] == "-initqp" && ++i != tokens.size() && ParseQp("-initqp", tokens[i], &config.rcParams.initialRCQP) && (config.rcParams.enableInitialRCQP = true) ||
tokens[i] == "-qmin" && ++i != tokens.size() && ParseQp("-qmin", tokens[i], &config.rcParams.minQP) && (config.rcParams.enableMinQP = true) ||
tokens[i] == "-qmax" && ++i != tokens.size() && ParseQp("-qmax", tokens[i], &config.rcParams.maxQP) && (config.rcParams.enableMaxQP = true) ||
tokens[i] == "-constqp" && ++i != tokens.size() && ParseQp("-constqp", tokens[i], &config.rcParams.constQP) ||
tokens[i] == "-temporalaq" && (config.rcParams.enableTemporalAQ = true)
)
{
continue;
}
if (tokens[i] == "-lookahead" && ++i != tokens.size() && ParseInt("-lookahead", tokens[i], &config.rcParams.lookaheadDepth))
{
config.rcParams.enableLookahead = config.rcParams.lookaheadDepth > 0;
continue;
}
int aqStrength;
if (tokens[i] == "-aq" && ++i != tokens.size() && ParseInt("-aq", tokens[i], &aqStrength)) {
config.rcParams.enableAQ = true;
config.rcParams.aqStrength = aqStrength;
continue;
}
if (tokens[i] == "-gop" && ++i != tokens.size() && ParseInt("-gop", tokens[i], &config.gopLength))
{
nGOPOption = 1;
if (IsCodecH264())
{
config.encodeCodecConfig.h264Config.idrPeriod = config.gopLength;
}
else if (IsCodecHEVC())
{
config.encodeCodecConfig.hevcConfig.idrPeriod = config.gopLength;
}
else
{
config.encodeCodecConfig.av1Config.idrPeriod = config.gopLength;
}
continue;
}
if (tokens[i] == "-444")
{
if (IsCodecH264())
{
config.encodeCodecConfig.h264Config.chromaFormatIDC = 3;
}
else if (IsCodecHEVC())
{
config.encodeCodecConfig.hevcConfig.chromaFormatIDC = 3;
}
else
{
std::ostringstream errmessage;
errmessage << "Incorrect Parameter: YUV444 Input not supported with AV1 Codec" << std::endl;
throw std::invalid_argument(errmessage.str());
}
continue;
}
std::ostringstream errmessage;
errmessage << "Incorrect parameter: " << tokens[i] << std::endl;
errmessage << "Re-run the application with the -h option to get a list of the supported options.";
errmessage << std::endl;
throw std::invalid_argument(errmessage.str());
}
if (IsCodecHEVC())
{
if (eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV420_10BIT || eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444_10BIT)
{
config.encodeCodecConfig.hevcConfig.pixelBitDepthMinus8 = 2;
}
}
if (IsCodecAV1())
{
if (eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV420_10BIT)
{
config.encodeCodecConfig.av1Config.pixelBitDepthMinus8 = 2;
config.encodeCodecConfig.av1Config.inputPixelBitDepthMinus8 = 2;
}
}
if (nGOPOption && nBFramesOption && (config.gopLength < ((uint32_t)config.frameIntervalP)))
{
std::ostringstream errmessage;
errmessage << "gopLength (" << config.gopLength << ") must be greater or equal to frameIntervalP (number of B frames + 1) (" << config.frameIntervalP << ")\n";
throw std::invalid_argument(errmessage.str());
}
funcInit(pParams);
LOG(INFO) << NvEncoderInitParam().MainParamToString(pParams);
LOG(TRACE) << NvEncoderInitParam().FullParamToString(pParams);
}
private:
/*
* Helper methods for parsing tokens (generated by splitting the command line)
* and performing conversions to the appropriate target type/value.
*/
template<typename T>
bool ParseString(const std::string &strName, const std::string &strValue, const std::vector<T> &vValue, const std::string &strValueNames, T *pValue) {
std::vector<std::string> vstrValueName = split(strValueNames, ' ');
auto it = std::find(vstrValueName.begin(), vstrValueName.end(), strValue);
if (it == vstrValueName.end()) {
LOG(ERROR) << strName << " options: " << strValueNames;
return false;
}
*pValue = vValue[it - vstrValueName.begin()];
return true;
}
template<typename T>
std::string ConvertValueToString(const std::vector<T> &vValue, const std::string &strValueNames, T value) {
auto it = std::find(vValue.begin(), vValue.end(), value);
if (it == vValue.end()) {
LOG(ERROR) << "Invalid value. Can't convert to one of " << strValueNames;
return std::string();
}
return split(strValueNames, ' ')[it - vValue.begin()];
}
bool ParseBitRate(const std::string &strName, const std::string &strValue, unsigned *pBitRate) {
if(bTransOneToN)
{
std::vector<std::string> oneToNBitrate = split(strValue, ',');
std::string currBitrate;
if ((bitrateCnt + 1) > oneToNBitrate.size())
{
currBitrate = oneToNBitrate[oneToNBitrate.size() - 1];
}
else
{
currBitrate = oneToNBitrate[bitrateCnt];
bitrateCnt++;
}
try {
size_t l;
double r = std::stod(currBitrate, &l);
char c = currBitrate[l];
if (c != 0 && c != 'k' && c != 'm') {
LOG(ERROR) << strName << " units: 1, K, M (lower case also allowed)";
}
*pBitRate = (unsigned)((c == 'm' ? 1000000 : (c == 'k' ? 1000 : 1)) * r);
}
catch (std::invalid_argument) {
return false;
}
return true;
}
else
{
try {
size_t l;
double r = std::stod(strValue, &l);
char c = strValue[l];
if (c != 0 && c != 'k' && c != 'm') {
LOG(ERROR) << strName << " units: 1, K, M (lower case also allowed)";
}
*pBitRate = (unsigned)((c == 'm' ? 1000000 : (c == 'k' ? 1000 : 1)) * r);
}
catch (std::invalid_argument) {
return false;
}
return true;
}
}
template<typename T>
bool ParseInt(const std::string &strName, const std::string &strValue, T *pInt) {
if (bTransOneToN)
{
std::vector<std::string> oneToNFps = split(strValue, ',');
std::string currFps;
if ((fpsCnt + 1) > oneToNFps.size())
{
currFps = oneToNFps[oneToNFps.size() - 1];
}
else
{
currFps = oneToNFps[fpsCnt];
fpsCnt++;
}
try {
*pInt = std::stoi(currFps);
}
catch (std::invalid_argument) {
LOG(ERROR) << strName << " need a value of positive number";
return false;
}
return true;
}
else
{
try {
*pInt = std::stoi(strValue);
}
catch (std::invalid_argument) {
LOG(ERROR) << strName << " need a value of positive number";
return false;
}
return true;
}
}
bool ParseQp(const std::string &strName, const std::string &strValue, NV_ENC_QP *pQp) {
std::vector<std::string> vQp = split(strValue, ',');
try {
if (vQp.size() == 1) {
unsigned qp = (unsigned)std::stoi(vQp[0]);
*pQp = {qp, qp, qp};
} else if (vQp.size() == 3) {
*pQp = {(unsigned)std::stoi(vQp[0]), (unsigned)std::stoi(vQp[1]), (unsigned)std::stoi(vQp[2])};
} else {
LOG(ERROR) << strName << " qp_for_P_B_I or qp_P,qp_B,qp_I (no space is allowed)";
return false;
}
} catch (std::invalid_argument) {
return false;
}
return true;
}
std::vector<std::string> split(const std::string &s, char delim) {
std::stringstream ss(s);
std::string token;
std::vector<std::string> tokens;
while (getline(ss, token, delim)) {
tokens.push_back(token);
}
return tokens;
}
private:
std::string strParam;
std::function<void(NV_ENC_INITIALIZE_PARAMS *pParams)> funcInit = [](NV_ENC_INITIALIZE_PARAMS *pParams){};
std::vector<std::string> tokens;
GUID guidCodec = NV_ENC_CODEC_H264_GUID;
GUID guidPreset = NV_ENC_PRESET_P3_GUID;
NV_ENC_TUNING_INFO m_TuningInfo = NV_ENC_TUNING_INFO_HIGH_QUALITY;
bool bLowLatency = false;
uint32_t bitrateCnt = 0;
uint32_t fpsCnt = 0;
bool bTransOneToN = 0;
const char *szCodecNames = "h264 hevc av1";
std::vector<GUID> vCodec = std::vector<GUID> {
NV_ENC_CODEC_H264_GUID,
NV_ENC_CODEC_HEVC_GUID,
NV_ENC_CODEC_AV1_GUID
};
const char *szChromaNames = "yuv420 yuv444";
std::vector<uint32_t> vChroma = std::vector<uint32_t>
{
1, 3
};
const char *szPresetNames = "p1 p2 p3 p4 p5 p6 p7";
std::vector<GUID> vPreset = std::vector<GUID> {
NV_ENC_PRESET_P1_GUID,
NV_ENC_PRESET_P2_GUID,
NV_ENC_PRESET_P3_GUID,
NV_ENC_PRESET_P4_GUID,
NV_ENC_PRESET_P5_GUID,
NV_ENC_PRESET_P6_GUID,
NV_ENC_PRESET_P7_GUID,
};
const char *szH264ProfileNames = "baseline main high high444";
std::vector<GUID> vH264Profile = std::vector<GUID> {
NV_ENC_H264_PROFILE_BASELINE_GUID,
NV_ENC_H264_PROFILE_MAIN_GUID,
NV_ENC_H264_PROFILE_HIGH_GUID,
NV_ENC_H264_PROFILE_HIGH_444_GUID,
};
const char *szHevcProfileNames = "main main10 frext";
std::vector<GUID> vHevcProfile = std::vector<GUID> {
NV_ENC_HEVC_PROFILE_MAIN_GUID,
NV_ENC_HEVC_PROFILE_MAIN10_GUID,
NV_ENC_HEVC_PROFILE_FREXT_GUID,
};
const char *szAV1ProfileNames = "main";
std::vector<GUID> vAV1Profile = std::vector<GUID>{
NV_ENC_AV1_PROFILE_MAIN_GUID,
};
const char *szProfileNames = "(default) auto baseline(h264) main(h264) high(h264) high444(h264)"
" stereo(h264) progressiv_high(h264) constrained_high(h264)"
" main(hevc) main10(hevc) frext(hevc)"
" main(av1) high(av1)";
std::vector<GUID> vProfile = std::vector<GUID> {
GUID{},
NV_ENC_CODEC_PROFILE_AUTOSELECT_GUID,
NV_ENC_H264_PROFILE_BASELINE_GUID,
NV_ENC_H264_PROFILE_MAIN_GUID,
NV_ENC_H264_PROFILE_HIGH_GUID,
NV_ENC_H264_PROFILE_HIGH_444_GUID,
NV_ENC_H264_PROFILE_STEREO_GUID,
NV_ENC_H264_PROFILE_PROGRESSIVE_HIGH_GUID,
NV_ENC_H264_PROFILE_CONSTRAINED_HIGH_GUID,
NV_ENC_HEVC_PROFILE_MAIN_GUID,
NV_ENC_HEVC_PROFILE_MAIN10_GUID,
NV_ENC_HEVC_PROFILE_FREXT_GUID,
NV_ENC_AV1_PROFILE_MAIN_GUID,
};
const char *szLowLatencyTuningInfoNames = "lowlatency ultralowlatency";
const char *szTuningInfoNames = "hq lowlatency ultralowlatency lossless";
std::vector<NV_ENC_TUNING_INFO> vTuningInfo = std::vector<NV_ENC_TUNING_INFO>{
NV_ENC_TUNING_INFO_HIGH_QUALITY,
NV_ENC_TUNING_INFO_LOW_LATENCY,
NV_ENC_TUNING_INFO_ULTRA_LOW_LATENCY,
NV_ENC_TUNING_INFO_LOSSLESS
};
const char *szRcModeNames = "constqp vbr cbr";
std::vector<NV_ENC_PARAMS_RC_MODE> vRcMode = std::vector<NV_ENC_PARAMS_RC_MODE> {
NV_ENC_PARAMS_RC_CONSTQP,
NV_ENC_PARAMS_RC_VBR,
NV_ENC_PARAMS_RC_CBR,
};
const char *szMultipass = "disabled qres fullres";
std::vector<NV_ENC_MULTI_PASS> vMultiPass = std::vector<NV_ENC_MULTI_PASS>{
NV_ENC_MULTI_PASS_DISABLED,
NV_ENC_TWO_PASS_QUARTER_RESOLUTION,
NV_ENC_TWO_PASS_FULL_RESOLUTION,
};
const char *szQpMapModeNames = "disabled emphasis_level_map delta_qp_map qp_map";
std::vector<NV_ENC_QP_MAP_MODE> vQpMapMode = std::vector<NV_ENC_QP_MAP_MODE> {
NV_ENC_QP_MAP_DISABLED,
NV_ENC_QP_MAP_EMPHASIS,
NV_ENC_QP_MAP_DELTA,
NV_ENC_QP_MAP,
};
public:
/*
* Generates and returns a string describing the values for each field in
* the NV_ENC_INITIALIZE_PARAMS structure (i.e. a description of the entire
* set of initialization parameters supplied to the API).
*/
std::string FullParamToString(const NV_ENC_INITIALIZE_PARAMS *pInitializeParams) {
std::ostringstream os;
os << "NV_ENC_INITIALIZE_PARAMS:" << std::endl
<< "encodeGUID: " << ConvertValueToString(vCodec, szCodecNames, pInitializeParams->encodeGUID) << std::endl
<< "presetGUID: " << ConvertValueToString(vPreset, szPresetNames, pInitializeParams->presetGUID) << std::endl;
if (pInitializeParams->tuningInfo)
{
os << "tuningInfo: " << ConvertValueToString(vTuningInfo, szTuningInfoNames, pInitializeParams->tuningInfo) << std::endl;
}
os
<< "encodeWidth: " << pInitializeParams->encodeWidth << std::endl
<< "encodeHeight: " << pInitializeParams->encodeHeight << std::endl
<< "darWidth: " << pInitializeParams->darWidth << std::endl
<< "darHeight: " << pInitializeParams->darHeight << std::endl
<< "frameRateNum: " << pInitializeParams->frameRateNum << std::endl
<< "frameRateDen: " << pInitializeParams->frameRateDen << std::endl
<< "enableEncodeAsync: " << pInitializeParams->enableEncodeAsync << std::endl
<< "reportSliceOffsets: " << pInitializeParams->reportSliceOffsets << std::endl
<< "enableSubFrameWrite: " << pInitializeParams->enableSubFrameWrite << std::endl
<< "enableExternalMEHints: " << pInitializeParams->enableExternalMEHints << std::endl
<< "enableMEOnlyMode: " << pInitializeParams->enableMEOnlyMode << std::endl
<< "enableWeightedPrediction: " << pInitializeParams->enableWeightedPrediction << std::endl
<< "maxEncodeWidth: " << pInitializeParams->maxEncodeWidth << std::endl
<< "maxEncodeHeight: " << pInitializeParams->maxEncodeHeight << std::endl
<< "maxMEHintCountsPerBlock: " << pInitializeParams->maxMEHintCountsPerBlock << std::endl
;
NV_ENC_CONFIG *pConfig = pInitializeParams->encodeConfig;
os << "NV_ENC_CONFIG:" << std::endl
<< "profile: " << ConvertValueToString(vProfile, szProfileNames, pConfig->profileGUID) << std::endl
<< "gopLength: " << pConfig->gopLength << std::endl
<< "frameIntervalP: " << pConfig->frameIntervalP << std::endl
<< "monoChromeEncoding: " << pConfig->monoChromeEncoding << std::endl
<< "frameFieldMode: " << pConfig->frameFieldMode << std::endl
<< "mvPrecision: " << pConfig->mvPrecision << std::endl
<< "NV_ENC_RC_PARAMS:" << std::endl
<< " rateControlMode: 0x" << std::hex << pConfig->rcParams.rateControlMode << std::dec << std::endl
<< " constQP: " << pConfig->rcParams.constQP.qpInterP << ", " << pConfig->rcParams.constQP.qpInterB << ", " << pConfig->rcParams.constQP.qpIntra << std::endl
<< " averageBitRate: " << pConfig->rcParams.averageBitRate << std::endl
<< " maxBitRate: " << pConfig->rcParams.maxBitRate << std::endl
<< " vbvBufferSize: " << pConfig->rcParams.vbvBufferSize << std::endl
<< " vbvInitialDelay: " << pConfig->rcParams.vbvInitialDelay << std::endl
<< " enableMinQP: " << pConfig->rcParams.enableMinQP << std::endl
<< " enableMaxQP: " << pConfig->rcParams.enableMaxQP << std::endl
<< " enableInitialRCQP: " << pConfig->rcParams.enableInitialRCQP << std::endl
<< " enableAQ: " << pConfig->rcParams.enableAQ << std::endl
<< " qpMapMode: " << ConvertValueToString(vQpMapMode, szQpMapModeNames, pConfig->rcParams.qpMapMode) << std::endl
<< " multipass: " << ConvertValueToString(vMultiPass, szMultipass, pConfig->rcParams.multiPass) << std::endl
<< " enableLookahead: " << pConfig->rcParams.enableLookahead << std::endl
<< " disableIadapt: " << pConfig->rcParams.disableIadapt << std::endl
<< " disableBadapt: " << pConfig->rcParams.disableBadapt << std::endl
<< " enableTemporalAQ: " << pConfig->rcParams.enableTemporalAQ << std::endl
<< " zeroReorderDelay: " << pConfig->rcParams.zeroReorderDelay << std::endl
<< " enableNonRefP: " << pConfig->rcParams.enableNonRefP << std::endl
<< " strictGOPTarget: " << pConfig->rcParams.strictGOPTarget << std::endl
<< " aqStrength: " << pConfig->rcParams.aqStrength << std::endl
<< " minQP: " << pConfig->rcParams.minQP.qpInterP << ", " << pConfig->rcParams.minQP.qpInterB << ", " << pConfig->rcParams.minQP.qpIntra << std::endl
<< " maxQP: " << pConfig->rcParams.maxQP.qpInterP << ", " << pConfig->rcParams.maxQP.qpInterB << ", " << pConfig->rcParams.maxQP.qpIntra << std::endl
<< " initialRCQP: " << pConfig->rcParams.initialRCQP.qpInterP << ", " << pConfig->rcParams.initialRCQP.qpInterB << ", " << pConfig->rcParams.initialRCQP.qpIntra << std::endl
<< " temporallayerIdxMask: " << pConfig->rcParams.temporallayerIdxMask << std::endl
<< " temporalLayerQP: " << (int)pConfig->rcParams.temporalLayerQP[0] << ", " << (int)pConfig->rcParams.temporalLayerQP[1] << ", " << (int)pConfig->rcParams.temporalLayerQP[2] << ", " << (int)pConfig->rcParams.temporalLayerQP[3] << ", " << (int)pConfig->rcParams.temporalLayerQP[4] << ", " << (int)pConfig->rcParams.temporalLayerQP[5] << ", " << (int)pConfig->rcParams.temporalLayerQP[6] << ", " << (int)pConfig->rcParams.temporalLayerQP[7] << std::endl
<< " targetQuality: " << pConfig->rcParams.targetQuality << std::endl
<< " lookaheadDepth: " << pConfig->rcParams.lookaheadDepth << std::endl;
if (pInitializeParams->encodeGUID == NV_ENC_CODEC_H264_GUID) {
os
<< "NV_ENC_CODEC_CONFIG (H264):" << std::endl
<< " enableStereoMVC: " << pConfig->encodeCodecConfig.h264Config.enableStereoMVC << std::endl
<< " hierarchicalPFrames: " << pConfig->encodeCodecConfig.h264Config.hierarchicalPFrames << std::endl
<< " hierarchicalBFrames: " << pConfig->encodeCodecConfig.h264Config.hierarchicalBFrames << std::endl
<< " outputBufferingPeriodSEI: " << pConfig->encodeCodecConfig.h264Config.outputBufferingPeriodSEI << std::endl
<< " outputPictureTimingSEI: " << pConfig->encodeCodecConfig.h264Config.outputPictureTimingSEI << std::endl
<< " outputAUD: " << pConfig->encodeCodecConfig.h264Config.outputAUD << std::endl
<< " disableSPSPPS: " << pConfig->encodeCodecConfig.h264Config.disableSPSPPS << std::endl
<< " outputFramePackingSEI: " << pConfig->encodeCodecConfig.h264Config.outputFramePackingSEI << std::endl
<< " outputRecoveryPointSEI: " << pConfig->encodeCodecConfig.h264Config.outputRecoveryPointSEI << std::endl
<< " enableIntraRefresh: " << pConfig->encodeCodecConfig.h264Config.enableIntraRefresh << std::endl
<< " enableConstrainedEncoding: " << pConfig->encodeCodecConfig.h264Config.enableConstrainedEncoding << std::endl
<< " repeatSPSPPS: " << pConfig->encodeCodecConfig.h264Config.repeatSPSPPS << std::endl
<< " enableVFR: " << pConfig->encodeCodecConfig.h264Config.enableVFR << std::endl
<< " enableLTR: " << pConfig->encodeCodecConfig.h264Config.enableLTR << std::endl
<< " qpPrimeYZeroTransformBypassFlag: " << pConfig->encodeCodecConfig.h264Config.qpPrimeYZeroTransformBypassFlag << std::endl
<< " useConstrainedIntraPred: " << pConfig->encodeCodecConfig.h264Config.useConstrainedIntraPred << std::endl
<< " level: " << pConfig->encodeCodecConfig.h264Config.level << std::endl
<< " idrPeriod: " << pConfig->encodeCodecConfig.h264Config.idrPeriod << std::endl
<< " separateColourPlaneFlag: " << pConfig->encodeCodecConfig.h264Config.separateColourPlaneFlag << std::endl
<< " disableDeblockingFilterIDC: " << pConfig->encodeCodecConfig.h264Config.disableDeblockingFilterIDC << std::endl
<< " numTemporalLayers: " << pConfig->encodeCodecConfig.h264Config.numTemporalLayers << std::endl
<< " spsId: " << pConfig->encodeCodecConfig.h264Config.spsId << std::endl
<< " ppsId: " << pConfig->encodeCodecConfig.h264Config.ppsId << std::endl
<< " adaptiveTransformMode: " << pConfig->encodeCodecConfig.h264Config.adaptiveTransformMode << std::endl
<< " fmoMode: " << pConfig->encodeCodecConfig.h264Config.fmoMode << std::endl
<< " bdirectMode: " << pConfig->encodeCodecConfig.h264Config.bdirectMode << std::endl
<< " entropyCodingMode: " << pConfig->encodeCodecConfig.h264Config.entropyCodingMode << std::endl
<< " stereoMode: " << pConfig->encodeCodecConfig.h264Config.stereoMode << std::endl
<< " intraRefreshPeriod: " << pConfig->encodeCodecConfig.h264Config.intraRefreshPeriod << std::endl
<< " intraRefreshCnt: " << pConfig->encodeCodecConfig.h264Config.intraRefreshCnt << std::endl
<< " maxNumRefFrames: " << pConfig->encodeCodecConfig.h264Config.maxNumRefFrames << std::endl
<< " sliceMode: " << pConfig->encodeCodecConfig.h264Config.sliceMode << std::endl
<< " sliceModeData: " << pConfig->encodeCodecConfig.h264Config.sliceModeData << std::endl
<< " NV_ENC_CONFIG_H264_VUI_PARAMETERS:" << std::endl
<< " overscanInfoPresentFlag: " << pConfig->encodeCodecConfig.h264Config.h264VUIParameters.overscanInfoPresentFlag << std::endl
<< " overscanInfo: " << pConfig->encodeCodecConfig.h264Config.h264VUIParameters.overscanInfo << std::endl
<< " videoSignalTypePresentFlag: " << pConfig->encodeCodecConfig.h264Config.h264VUIParameters.videoSignalTypePresentFlag << std::endl
<< " videoFormat: " << pConfig->encodeCodecConfig.h264Config.h264VUIParameters.videoFormat << std::endl
<< " videoFullRangeFlag: " << pConfig->encodeCodecConfig.h264Config.h264VUIParameters.videoFullRangeFlag << std::endl
<< " colourDescriptionPresentFlag: " << pConfig->encodeCodecConfig.h264Config.h264VUIParameters.colourDescriptionPresentFlag << std::endl
<< " colourPrimaries: " << pConfig->encodeCodecConfig.h264Config.h264VUIParameters.colourPrimaries << std::endl
<< " transferCharacteristics: " << pConfig->encodeCodecConfig.h264Config.h264VUIParameters.transferCharacteristics << std::endl
<< " colourMatrix: " << pConfig->encodeCodecConfig.h264Config.h264VUIParameters.colourMatrix << std::endl
<< " chromaSampleLocationFlag: " << pConfig->encodeCodecConfig.h264Config.h264VUIParameters.chromaSampleLocationFlag << std::endl
<< " chromaSampleLocationTop: " << pConfig->encodeCodecConfig.h264Config.h264VUIParameters.chromaSampleLocationTop << std::endl
<< " chromaSampleLocationBot: " << pConfig->encodeCodecConfig.h264Config.h264VUIParameters.chromaSampleLocationBot << std::endl
<< " bitstreamRestrictionFlag: " << pConfig->encodeCodecConfig.h264Config.h264VUIParameters.bitstreamRestrictionFlag << std::endl
<< " ltrNumFrames: " << pConfig->encodeCodecConfig.h264Config.ltrNumFrames << std::endl
<< " ltrTrustMode: " << pConfig->encodeCodecConfig.h264Config.ltrTrustMode << std::endl
<< " chromaFormatIDC: " << pConfig->encodeCodecConfig.h264Config.chromaFormatIDC << std::endl
<< " maxTemporalLayers: " << pConfig->encodeCodecConfig.h264Config.maxTemporalLayers << std::endl;
} else if (pInitializeParams->encodeGUID == NV_ENC_CODEC_HEVC_GUID) {
os
<< "NV_ENC_CODEC_CONFIG (HEVC):" << std::endl
<< " level: " << pConfig->encodeCodecConfig.hevcConfig.level << std::endl
<< " tier: " << pConfig->encodeCodecConfig.hevcConfig.tier << std::endl
<< " minCUSize: " << pConfig->encodeCodecConfig.hevcConfig.minCUSize << std::endl
<< " maxCUSize: " << pConfig->encodeCodecConfig.hevcConfig.maxCUSize << std::endl
<< " useConstrainedIntraPred: " << pConfig->encodeCodecConfig.hevcConfig.useConstrainedIntraPred << std::endl
<< " disableDeblockAcrossSliceBoundary: " << pConfig->encodeCodecConfig.hevcConfig.disableDeblockAcrossSliceBoundary << std::endl
<< " outputBufferingPeriodSEI: " << pConfig->encodeCodecConfig.hevcConfig.outputBufferingPeriodSEI << std::endl
<< " outputPictureTimingSEI: " << pConfig->encodeCodecConfig.hevcConfig.outputPictureTimingSEI << std::endl
<< " outputAUD: " << pConfig->encodeCodecConfig.hevcConfig.outputAUD << std::endl
<< " enableLTR: " << pConfig->encodeCodecConfig.hevcConfig.enableLTR << std::endl
<< " disableSPSPPS: " << pConfig->encodeCodecConfig.hevcConfig.disableSPSPPS << std::endl
<< " repeatSPSPPS: " << pConfig->encodeCodecConfig.hevcConfig.repeatSPSPPS << std::endl
<< " enableIntraRefresh: " << pConfig->encodeCodecConfig.hevcConfig.enableIntraRefresh << std::endl
<< " chromaFormatIDC: " << pConfig->encodeCodecConfig.hevcConfig.chromaFormatIDC << std::endl
<< " pixelBitDepthMinus8: " << pConfig->encodeCodecConfig.hevcConfig.pixelBitDepthMinus8 << std::endl
<< " idrPeriod: " << pConfig->encodeCodecConfig.hevcConfig.idrPeriod << std::endl
<< " intraRefreshPeriod: " << pConfig->encodeCodecConfig.hevcConfig.intraRefreshPeriod << std::endl
<< " intraRefreshCnt: " << pConfig->encodeCodecConfig.hevcConfig.intraRefreshCnt << std::endl
<< " maxNumRefFramesInDPB: " << pConfig->encodeCodecConfig.hevcConfig.maxNumRefFramesInDPB << std::endl
<< " ltrNumFrames: " << pConfig->encodeCodecConfig.hevcConfig.ltrNumFrames << std::endl
<< " vpsId: " << pConfig->encodeCodecConfig.hevcConfig.vpsId << std::endl
<< " spsId: " << pConfig->encodeCodecConfig.hevcConfig.spsId << std::endl
<< " ppsId: " << pConfig->encodeCodecConfig.hevcConfig.ppsId << std::endl
<< " sliceMode: " << pConfig->encodeCodecConfig.hevcConfig.sliceMode << std::endl
<< " sliceModeData: " << pConfig->encodeCodecConfig.hevcConfig.sliceModeData << std::endl
<< " maxTemporalLayersMinus1: " << pConfig->encodeCodecConfig.hevcConfig.maxTemporalLayersMinus1 << std::endl
<< " NV_ENC_CONFIG_HEVC_VUI_PARAMETERS:" << std::endl
<< " overscanInfoPresentFlag: " << pConfig->encodeCodecConfig.hevcConfig.hevcVUIParameters.overscanInfoPresentFlag << std::endl
<< " overscanInfo: " << pConfig->encodeCodecConfig.hevcConfig.hevcVUIParameters.overscanInfo << std::endl
<< " videoSignalTypePresentFlag: " << pConfig->encodeCodecConfig.hevcConfig.hevcVUIParameters.videoSignalTypePresentFlag << std::endl
<< " videoFormat: " << pConfig->encodeCodecConfig.hevcConfig.hevcVUIParameters.videoFormat << std::endl
<< " videoFullRangeFlag: " << pConfig->encodeCodecConfig.hevcConfig.hevcVUIParameters.videoFullRangeFlag << std::endl
<< " colourDescriptionPresentFlag: " << pConfig->encodeCodecConfig.hevcConfig.hevcVUIParameters.colourDescriptionPresentFlag << std::endl
<< " colourPrimaries: " << pConfig->encodeCodecConfig.hevcConfig.hevcVUIParameters.colourPrimaries << std::endl
<< " transferCharacteristics: " << pConfig->encodeCodecConfig.hevcConfig.hevcVUIParameters.transferCharacteristics << std::endl
<< " colourMatrix: " << pConfig->encodeCodecConfig.hevcConfig.hevcVUIParameters.colourMatrix << std::endl
<< " chromaSampleLocationFlag: " << pConfig->encodeCodecConfig.hevcConfig.hevcVUIParameters.chromaSampleLocationFlag << std::endl
<< " chromaSampleLocationTop: " << pConfig->encodeCodecConfig.hevcConfig.hevcVUIParameters.chromaSampleLocationTop << std::endl
<< " chromaSampleLocationBot: " << pConfig->encodeCodecConfig.hevcConfig.hevcVUIParameters.chromaSampleLocationBot << std::endl
<< " bitstreamRestrictionFlag: " << pConfig->encodeCodecConfig.hevcConfig.hevcVUIParameters.bitstreamRestrictionFlag << std::endl
<< " ltrTrustMode: " << pConfig->encodeCodecConfig.hevcConfig.ltrTrustMode << std::endl;
} else if (pInitializeParams->encodeGUID == NV_ENC_CODEC_AV1_GUID) {
os
<< "NV_ENC_CODEC_CONFIG (AV1):" << std::endl
<< " level: " << pConfig->encodeCodecConfig.av1Config.level << std::endl
<< " tier: " << pConfig->encodeCodecConfig.av1Config.tier << std::endl
<< " minPartSize: " << pConfig->encodeCodecConfig.av1Config.minPartSize << std::endl
<< " maxPartSize: " << pConfig->encodeCodecConfig.av1Config.maxPartSize << std::endl
<< " outputAnnexBFormat: " << pConfig->encodeCodecConfig.av1Config.outputAnnexBFormat << std::endl
<< " enableTimingInfo: " << pConfig->encodeCodecConfig.av1Config.enableTimingInfo << std::endl
<< " enableDecoderModelInfo: " << pConfig->encodeCodecConfig.av1Config.enableDecoderModelInfo << std::endl
<< " enableFrameIdNumbers: " << pConfig->encodeCodecConfig.av1Config.enableFrameIdNumbers << std::endl
<< " disableSeqHdr: " << pConfig->encodeCodecConfig.av1Config.disableSeqHdr << std::endl
<< " repeatSeqHdr: " << pConfig->encodeCodecConfig.av1Config.repeatSeqHdr << std::endl
<< " enableIntraRefresh: " << pConfig->encodeCodecConfig.av1Config.enableIntraRefresh << std::endl
<< " chromaFormatIDC: " << pConfig->encodeCodecConfig.av1Config.chromaFormatIDC << std::endl
<< " enableBitstreamPadding: " << pConfig->encodeCodecConfig.av1Config.enableBitstreamPadding << std::endl
<< " enableCustomTileConfig: " << pConfig->encodeCodecConfig.av1Config.enableCustomTileConfig << std::endl
<< " enableFilmGrainParams: " << pConfig->encodeCodecConfig.av1Config.enableFilmGrainParams << std::endl
<< " inputPixelBitDepthMinus8: " << pConfig->encodeCodecConfig.av1Config.inputPixelBitDepthMinus8 << std::endl
<< " pixelBitDepthMinus8: " << pConfig->encodeCodecConfig.av1Config.pixelBitDepthMinus8 << std::endl
<< " idrPeriod: " << pConfig->encodeCodecConfig.av1Config.idrPeriod << std::endl
<< " intraRefreshPeriod: " << pConfig->encodeCodecConfig.av1Config.intraRefreshPeriod << std::endl
<< " intraRefreshCnt: " << pConfig->encodeCodecConfig.av1Config.intraRefreshCnt << std::endl
<< " maxNumRefFramesInDPB: " << pConfig->encodeCodecConfig.av1Config.maxNumRefFramesInDPB << std::endl
<< " numTileColumns: " << pConfig->encodeCodecConfig.av1Config.numTileColumns << std::endl
<< " numTileRows: " << pConfig->encodeCodecConfig.av1Config.numTileRows << std::endl
<< " maxTemporalLayersMinus1: " << pConfig->encodeCodecConfig.av1Config.maxTemporalLayersMinus1 << std::endl
<< " colorPrimaries: " << pConfig->encodeCodecConfig.av1Config.colorPrimaries << std::endl
<< " transferCharacteristics: " << pConfig->encodeCodecConfig.av1Config.transferCharacteristics << std::endl
<< " matrixCoefficients: " << pConfig->encodeCodecConfig.av1Config.matrixCoefficients << std::endl
<< " colorRange: " << pConfig->encodeCodecConfig.av1Config.colorRange << std::endl
<< " chromaSamplePosition: " << pConfig->encodeCodecConfig.av1Config.chromaSamplePosition << std::endl
<< " useBFramesAsRef: " << pConfig->encodeCodecConfig.av1Config.useBFramesAsRef << std::endl
<< " numFwdRefs: " << pConfig->encodeCodecConfig.av1Config.numFwdRefs << std::endl
<< " numBwdRefs: " << pConfig->encodeCodecConfig.av1Config.numBwdRefs << std::endl;
if (pConfig->encodeCodecConfig.av1Config.filmGrainParams != NULL)
{
os
<< " NV_ENC_FILM_GRAIN_PARAMS_AV1:" << std::endl
<< " applyGrain: " << pConfig->encodeCodecConfig.av1Config.filmGrainParams->applyGrain << std::endl
<< " chromaScalingFromLuma: " << pConfig->encodeCodecConfig.av1Config.filmGrainParams->chromaScalingFromLuma << std::endl
<< " overlapFlag: " << pConfig->encodeCodecConfig.av1Config.filmGrainParams->overlapFlag << std::endl
<< " clipToRestrictedRange: " << pConfig->encodeCodecConfig.av1Config.filmGrainParams->clipToRestrictedRange << std::endl
<< " grainScalingMinus8: " << pConfig->encodeCodecConfig.av1Config.filmGrainParams->grainScalingMinus8 << std::endl
<< " arCoeffLag: " << pConfig->encodeCodecConfig.av1Config.filmGrainParams->arCoeffLag << std::endl
<< " numYPoints: " << pConfig->encodeCodecConfig.av1Config.filmGrainParams->numYPoints << std::endl
<< " numCbPoints: " << pConfig->encodeCodecConfig.av1Config.filmGrainParams->numCbPoints << std::endl
<< " numCrPoints: " << pConfig->encodeCodecConfig.av1Config.filmGrainParams->numCrPoints << std::endl
<< " arCoeffShiftMinus6: " << pConfig->encodeCodecConfig.av1Config.filmGrainParams->arCoeffShiftMinus6 << std::endl
<< " grainScaleShift: " << pConfig->encodeCodecConfig.av1Config.filmGrainParams->grainScaleShift << std::endl
<< " cbMult: " << pConfig->encodeCodecConfig.av1Config.filmGrainParams->cbMult << std::endl
<< " cbLumaMult: " << pConfig->encodeCodecConfig.av1Config.filmGrainParams->cbLumaMult << std::endl
<< " cbOffset: " << pConfig->encodeCodecConfig.av1Config.filmGrainParams->cbOffset << std::endl
<< " crMult: " << pConfig->encodeCodecConfig.av1Config.filmGrainParams->crMult << std::endl
<< " crLumaMult: " << pConfig->encodeCodecConfig.av1Config.filmGrainParams->crLumaMult << std::endl
<< " crOffset: " << pConfig->encodeCodecConfig.av1Config.filmGrainParams->crOffset << std::endl;
}
}
return os.str();
}
};