mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-01-29 09:01:54 +08:00
init
This commit is contained in:
860
libs/hwcodec/externals/Video_Codec_SDK_12.1.14/Samples/NvCodec/NvDecoder/NvDecoder.cpp
vendored
Normal file
860
libs/hwcodec/externals/Video_Codec_SDK_12.1.14/Samples/NvCodec/NvDecoder/NvDecoder.cpp
vendored
Normal 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(), ×tamp[0], ×tamp[1]);
|
||||
}
|
||||
386
libs/hwcodec/externals/Video_Codec_SDK_12.1.14/Samples/NvCodec/NvDecoder/NvDecoder.h
vendored
Normal file
386
libs/hwcodec/externals/Video_Codec_SDK_12.1.14/Samples/NvCodec/NvDecoder/NvDecoder.h
vendored
Normal 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 = {};
|
||||
};
|
||||
1037
libs/hwcodec/externals/Video_Codec_SDK_12.1.14/Samples/NvCodec/NvEncoder/NvEncoder.cpp
vendored
Normal file
1037
libs/hwcodec/externals/Video_Codec_SDK_12.1.14/Samples/NvCodec/NvEncoder/NvEncoder.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
483
libs/hwcodec/externals/Video_Codec_SDK_12.1.14/Samples/NvCodec/NvEncoder/NvEncoder.h
vendored
Normal file
483
libs/hwcodec/externals/Video_Codec_SDK_12.1.14/Samples/NvCodec/NvEncoder/NvEncoder.h
vendored
Normal 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;
|
||||
};
|
||||
163
libs/hwcodec/externals/Video_Codec_SDK_12.1.14/Samples/NvCodec/NvEncoder/NvEncoderD3D11.cpp
vendored
Normal file
163
libs/hwcodec/externals/Video_Codec_SDK_12.1.14/Samples/NvCodec/NvEncoder/NvEncoderD3D11.cpp
vendored
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
71
libs/hwcodec/externals/Video_Codec_SDK_12.1.14/Samples/NvCodec/NvEncoder/NvEncoderD3D11.h
vendored
Normal file
71
libs/hwcodec/externals/Video_Codec_SDK_12.1.14/Samples/NvCodec/NvEncoder/NvEncoderD3D11.h
vendored
Normal 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;
|
||||
};
|
||||
Reference in New Issue
Block a user