From 32f854c159515885d0c221818f05832b67d3e3da Mon Sep 17 00:00:00 2001 From: Aryan Salmanpour Date: Wed, 1 Nov 2023 09:22:46 -0400 Subject: [PATCH] Add support for rocDecCreateDecoder API (#28) * Add support for rocDecCreateDecoder API * check the decoder config support before initializing the vaapi * rename RocdecDecoderCreateInfo to RocDecoderCreateInfo * fix a typo --- api/rocdecode.h | 18 ++--- src/rocdecode/dec_handle.h | 2 +- src/rocdecode/roc_decoder.cpp | 36 +++++----- src/rocdecode/roc_decoder.h | 9 +-- src/rocdecode/roc_decoder_caps.h | 24 ++++++- src/rocdecode/rocdecode_api.cpp | 11 +-- src/rocdecode/vaapi/vaapi_videodecoder.cpp | 82 +++++++++++++++++++--- src/rocdecode/vaapi/vaapi_videodecoder.h | 63 +++++++++-------- utils/rocvideodecode/roc_video_dec.cpp | 2 +- 9 files changed, 170 insertions(+), 77 deletions(-) diff --git a/api/rocdecode.h b/api/rocdecode.h index b562bcf8e2..b3c9d3b7cd 100644 --- a/api/rocdecode.h +++ b/api/rocdecode.h @@ -102,7 +102,7 @@ typedef enum rocDecVideoCodec_enum { /*********************************************************************************/ //! \enum rocDecVideoSurfaceFormat //! Video surface format enums used for output format of decoded output -//! These enums are used in RocdecDecoderCreateInfo structure +//! These enums are used in RocDecoderCreateInfo structure /*********************************************************************************/ typedef enum rocDecVideoSurfaceFormat_enum { rocDecVideoSurfaceFormat_NV12=0, /**< Semi-Planar YUV [Y plane followed by interleaved UV plane] */ @@ -163,10 +163,12 @@ typedef struct _RocdecDecodeCaps { } RocdecDecodeCaps; /**************************************************************************************************************/ -//! \struct RocdecDecoderCreateInfo +//! \struct RocDecoderCreateInfo //! This structure is used in rocDecCreateDecoder API /**************************************************************************************************************/ -typedef struct _RocdecDecoderCreateInfo { +typedef struct _RocDecoderCreateInfo { + uint8_t deviceid; /**< IN: the device id for which a decoder should be created + 0 for the first device, 1 for the second device on the system, etc.*/ uint32_t ulWidth; /**< IN: Coded sequence width in pixels */ uint32_t ulHeight; /**< IN: Coded sequence height in pixels */ uint32_t ulNumDecodeSurfaces; /**< IN: Maximum number of internal decode surfaces */ @@ -207,7 +209,7 @@ typedef struct _RocdecDecoderCreateInfo { } target_rect; uint32_t Reserved2[4]; /**< Reserved for future use - set to zero */ -} RocdecDecoderCreateInfo; +} RocDecoderCreateInfo; /*********************************************************************************************************/ //! \struct RocdecDecodeStatus @@ -226,8 +228,8 @@ typedef struct _RocdecDecodeStatus { //! This structure is used in rocDecReconfigureDecoder() API /****************************************************/ typedef struct _RocdecReconfigureDecoderInfo { - uint32_t ulWidth; /**< IN: Coded sequence width in pixels, MUST be < = ulMaxWidth defined at RocdecDecoderCreateInfo */ - uint32_t ulHeight; /**< IN: Coded sequence height in pixels, MUST be < = ulMaxHeight defined at RocdecDecoderCreateInfo */ + uint32_t ulWidth; /**< IN: Coded sequence width in pixels, MUST be < = ulMaxWidth defined at RocDecoderCreateInfo */ + uint32_t ulHeight; /**< IN: Coded sequence height in pixels, MUST be < = ulMaxHeight defined at RocDecoderCreateInfo */ uint32_t ulTargetWidth; /**< IN: Post processed output width */ uint32_t ulTargetHeight; /**< IN: Post Processed output height */ uint32_t ulNumDecodeSurfaces; /**< IN: Maximum number of internal decode surfaces */ @@ -701,10 +703,10 @@ typedef struct _RocdecProcParams /***********************************************************************************************************/ /*****************************************************************************************************/ -//! \fn rocDecStatus ROCDECAPI rocDecCreateDecoder(rocDecDecoderHandle *phDecoder, RocdecDecoderCreateInfo *pdci) +//! \fn rocDecStatus ROCDECAPI rocDecCreateDecoder(rocDecDecoderHandle *phDecoder, RocDecoderCreateInfo *pdci) //! Create the decoder object based on pdci. A handle to the created decoder is returned /*****************************************************************************************************/ -extern rocDecStatus ROCDECAPI rocDecCreateDecoder(rocDecDecoderHandle *phDecoder, RocdecDecoderCreateInfo *pdci); +extern rocDecStatus ROCDECAPI rocDecCreateDecoder(rocDecDecoderHandle *phDecoder, RocDecoderCreateInfo *pdci); /*****************************************************************************************************/ //! \fn rocDecStatus ROCDECAPI rocDecDestroyDecoder(rocDecDecoderHandle hDecoder) diff --git a/src/rocdecode/dec_handle.h b/src/rocdecode/dec_handle.h index 961f5cafa4..fb59ff2410 100644 --- a/src/rocdecode/dec_handle.h +++ b/src/rocdecode/dec_handle.h @@ -32,7 +32,7 @@ THE SOFTWARE. */ struct DecHandle { - explicit DecHandle() : roc_decoder(std::make_shared()) {}; //constructor + explicit DecHandle(RocDecoderCreateInfo& decoder_create_info) : roc_decoder(std::make_shared(decoder_create_info)) {}; //constructor ~DecHandle() { clear_errors(); } std::shared_ptr roc_decoder; // class instantiation bool no_error() { return error.empty(); } diff --git a/src/rocdecode/roc_decoder.cpp b/src/rocdecode/roc_decoder.cpp index 24ba40a264..ed41ade79b 100644 --- a/src/rocdecode/roc_decoder.cpp +++ b/src/rocdecode/roc_decoder.cpp @@ -23,14 +23,7 @@ THE SOFTWARE. #include "../commons.h" #include "roc_decoder.h" -RocDecoder::RocDecoder(int device_id):device_id_ {device_id}, num_devices_{0} { - // todo:: - if (ROCDEC_SUCCESS != initHIP(device_id_)) { - THROW("Failed to initilize the HIP"); - } - initDRMnodes(); - -} +RocDecoder::RocDecoder(RocDecoderCreateInfo& decoder_create_info): va_video_decoder_{decoder_create_info}, device_id_{decoder_create_info.deviceid} {} RocDecoder::~RocDecoder() { // todo:: @@ -43,6 +36,23 @@ RocDecoder::RocDecoder(int device_id):device_id_ {device_id}, num_devices_{0} { } } + rocDecStatus RocDecoder::InitializeDecoder() { + rocDecStatus rocdec_status = ROCDEC_SUCCESS; + rocdec_status = InitHIP(device_id_); + if (rocdec_status != ROCDEC_SUCCESS) { + ERR("ERROR: Failed to initilize the HIP!" + TOSTR(rocdec_status)); + return rocdec_status; + } + + rocdec_status = va_video_decoder_.InitializeDecoder(hip_dev_prop_.gcnArchName); + if (rocdec_status != ROCDEC_SUCCESS) { + ERR("ERROR: Failed to initilize the VAAPI Video decoder!" + TOSTR(rocdec_status)); + return rocdec_status; + } + + return rocdec_status; + } + rocDecStatus RocDecoder::decodeFrame(RocdecPicParams *pPicParams) { // todo:: return appropriate decStatus if fails // call funsction to do va-api decoding using the picture parameters structure @@ -80,7 +90,7 @@ rocDecStatus RocDecoder::unMapVideoFrame(void *pMappedDevPtr) { } -rocDecStatus RocDecoder::initHIP(int device_id) { +rocDecStatus RocDecoder::InitHIP(int device_id) { hipError_t hipStatus = hipSuccess; hipStatus = hipGetDeviceCount(&num_devices_); rocDecStatus decStatus = ROCDEC_SUCCESS; @@ -115,11 +125,3 @@ rocDecStatus RocDecoder::initHIP(int device_id) { } return decStatus; } - -void RocDecoder::initDRMnodes() { - // build the DRM render node names - for (int i = 0; i < num_devices_; i++) { - drm_nodes_.push_back("/dev/dri/renderD" + std::to_string(128 + i)); - } -} - diff --git a/src/rocdecode/roc_decoder.h b/src/rocdecode/roc_decoder.h index e2dae0ea53..6c756ef290 100644 --- a/src/rocdecode/roc_decoder.h +++ b/src/rocdecode/roc_decoder.h @@ -32,11 +32,13 @@ THE SOFTWARE. #include #include "../api/rocdecode.h" #include +#include "vaapi/vaapi_videodecoder.h" class RocDecoder { public: - RocDecoder(int device_id = 0); + RocDecoder(RocDecoderCreateInfo &decoder_create_info); ~RocDecoder(); + rocDecStatus InitializeDecoder(); rocDecStatus decodeFrame(RocdecPicParams *pPicParams); rocDecStatus getDecodeStatus(int nPicIdx, RocdecDecodeStatus* pDecodeStatus); rocDecStatus reconfigureDecoder(RocdecReconfigureDecoderInfo *pDecReconfigParams); @@ -44,11 +46,10 @@ public: rocDecStatus unMapVideoFrame(void *pMappedDevPtr); private: - rocDecStatus initHIP(int device_id); - void initDRMnodes(); + rocDecStatus InitHIP(int device_id); int num_devices_; int device_id_; + VaapiVideoDecoder va_video_decoder_; hipDeviceProp_t hip_dev_prop_; hipStream_t hip_stream_; - std::vector drm_nodes_; }; \ No newline at end of file diff --git a/src/rocdecode/roc_decoder_caps.h b/src/rocdecode/roc_decoder_caps.h index 7ba86280fb..5aba59972c 100644 --- a/src/rocdecode/roc_decoder_caps.h +++ b/src/rocdecode/roc_decoder_caps.h @@ -49,7 +49,7 @@ struct VcnCodecsSpec { // The RocDecVcnCodecSpec singleton class for providing access to the the vcn_spec_table class RocDecVcnCodecSpec { public: - static RocDecVcnCodecSpec& GetInastance() { + static RocDecVcnCodecSpec& GetInstance() { static RocDecVcnCodecSpec instance; return instance; } @@ -82,6 +82,28 @@ public: return ROCDEC_DEVICE_INVALID; } } + bool IsCodecConfigSupported(std::string gcn_arch_name, rocDecVideoCodec codec_type, rocDecVideoChromaFormat chroma_format, uint32_t bit_depth_minus8, rocDecVideoSurfaceFormat output_format) { + std::lock_guard lock(mutex); + auto it = vcn_spec_table.find(gcn_arch_name); + if (it != vcn_spec_table.end()) { + const VcnCodecsSpec& vcn_spec = it->second; + auto it1 = vcn_spec.codecs_spec.find(codec_type); + if (it1 != vcn_spec.codecs_spec.end()) { + const CodecSpec& codec_spec = it1->second; + auto it_chroma_format = std::find(codec_spec.chroma_format.begin(), codec_spec.chroma_format.end(), chroma_format); + auto it_bitdepth_minus8 = std::find(codec_spec.bitdepth_minus8.begin(), codec_spec.bitdepth_minus8.end(), bit_depth_minus8); + if (it_chroma_format != codec_spec.chroma_format.end() && it_bitdepth_minus8 != codec_spec.bitdepth_minus8.end()) { + return (codec_spec.output_format_mask & (static_cast(output_format) + 1)); + } else { + return false; + } + } else { + return false; + } + } else { + return false; + } + } private: std::unordered_map vcn_spec_table; std::mutex mutex; diff --git a/src/rocdecode/rocdecode_api.cpp b/src/rocdecode/rocdecode_api.cpp index 158c07a461..73b9b1ce61 100644 --- a/src/rocdecode/rocdecode_api.cpp +++ b/src/rocdecode/rocdecode_api.cpp @@ -26,20 +26,21 @@ THE SOFTWARE. /*****************************************************************************************************/ -//! \fn rocDecStatus ROCDECAPI rocDecCreateDecoder(rocDecDecoderHandle *phDecoder, RocdecDecoderCreateInfo *pdci) +//! \fn rocDecStatus ROCDECAPI rocDecCreateDecoder(rocDecDecoderHandle *phDecoder, RocDecoderCreateInfo *pdci) //! Create the decoder object based on pdci. A handle to the created decoder is returned /*****************************************************************************************************/ rocDecStatus ROCDECAPI -rocDecCreateDecoder(rocDecDecoderHandle *phDecoder, RocdecDecoderCreateInfo *pdci) { +rocDecCreateDecoder(rocDecDecoderHandle *phDecoder, RocDecoderCreateInfo *pdci) { rocDecDecoderHandle handle = nullptr; try { - handle = new DecHandle(); + handle = new DecHandle(*pdci); } catch(const std::exception& e) { ERR( STR("Failed to init the rocDecode handle, ") + STR(e.what())) + return ROCDEC_NOT_INITIALIZED; } *phDecoder = handle; - return ROCDEC_SUCCESS; + return static_cast(handle)->roc_decoder->InitializeDecoder(); } /*****************************************************************************************************/ @@ -87,7 +88,7 @@ rocDecGetDecoderCaps(RocdecDecodeCaps *pdc) { return ROCDEC_DEVICE_INVALID; } - RocDecVcnCodecSpec& vcn_codec_spec = RocDecVcnCodecSpec::GetInastance(); + RocDecVcnCodecSpec& vcn_codec_spec = RocDecVcnCodecSpec::GetInstance(); return vcn_codec_spec.GetDecoderCaps(hip_dev_prop.gcnArchName, pdc); } diff --git a/src/rocdecode/vaapi/vaapi_videodecoder.cpp b/src/rocdecode/vaapi/vaapi_videodecoder.cpp index af761397bd..8465a80d02 100644 --- a/src/rocdecode/vaapi/vaapi_videodecoder.cpp +++ b/src/rocdecode/vaapi/vaapi_videodecoder.cpp @@ -20,13 +20,77 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include -#include -#include -#include -#include -#include -#include - -#include "../commons.h" #include "vaapi_videodecoder.h" + +VaapiVideoDecoder::VaapiVideoDecoder(RocDecoderCreateInfo &decoder_create_info) : decoder_create_info_{decoder_create_info}, + drm_fd_{-1}, va_display_{0}, va_config_attrib_{{}}, va_config_id_{0}, va_profile_ {VAProfileNone} {}; + +VaapiVideoDecoder::~VaapiVideoDecoder() { + if (drm_fd_ != -1) { + close(drm_fd_); + } + if (va_display_) { + if (va_config_id_) + vaDestroyConfig(va_display_, va_config_id_); + vaTerminate(va_display_); + } +} + +rocDecStatus VaapiVideoDecoder::InitializeDecoder(std::string gcn_arch_name) { + rocDecStatus rocdec_status = ROCDEC_SUCCESS; + + //Before initializing the VAAPI, first check to see if the requested codec config is supported + RocDecVcnCodecSpec& vcn_codec_spec = RocDecVcnCodecSpec::GetInstance(); + if (!vcn_codec_spec.IsCodecConfigSupported(gcn_arch_name, decoder_create_info_.CodecType, decoder_create_info_.ChromaFormat, + decoder_create_info_.bitDepthMinus8, decoder_create_info_.OutputFormat)) { + ERR("ERROR: the codec config combination is not supported!"); + return ROCDEC_NOT_SUPPORTED; + } + rocdec_status = InitVAAPI(); + if (rocdec_status != ROCDEC_SUCCESS) { + ERR("ERROR: Failed to initilize the VAAPI!" + TOSTR(rocdec_status)); + return rocdec_status; + } + rocdec_status = CreateDecoderConfig(); + if (rocdec_status != ROCDEC_SUCCESS) { + ERR("ERROR: Failed to create a VAAPI decoder configuration" + TOSTR(rocdec_status)); + return rocdec_status; + } + return rocdec_status; +} + +rocDecStatus VaapiVideoDecoder::InitVAAPI() { + std::string drm_node = "/dev/dri/renderD" + std::to_string(128 + decoder_create_info_.deviceid); + drm_fd_ = open(drm_node.c_str(), O_RDWR); + if (drm_fd_ < 0) { + ERR("ERROR: failed to open drm node " + drm_node); + return ROCDEC_NOT_INITIALIZED; + } + va_display_ = vaGetDisplayDRM(drm_fd_); + vaSetInfoCallback(va_display_, NULL, NULL); + int major_version = 0, minor_version = 0; + CHECK_VAAPI(vaInitialize(va_display_, &major_version, &minor_version)); + return ROCDEC_SUCCESS; +} + +rocDecStatus VaapiVideoDecoder::CreateDecoderConfig() { + switch (decoder_create_info_.CodecType) { + case rocDecVideoCodec_HEVC: + if (decoder_create_info_.bitDepthMinus8 == 0) { + va_profile_ = VAProfileHEVCMain; + } else if (decoder_create_info_.bitDepthMinus8 == 2) { + va_profile_ = VAProfileHEVCMain10; + } + break; + case rocDecVideoCodec_H264: + va_profile_ = VAProfileH264Main; + break; + default: + ERR("ERROR: the codec type is not supported!"); + return ROCDEC_NOT_SUPPORTED; + } + va_config_attrib_.type = VAConfigAttribRTFormat; + CHECK_VAAPI(vaGetConfigAttributes(va_display_, va_profile_, VAEntrypointVLD, &va_config_attrib_, 1)); + CHECK_VAAPI(vaCreateConfig(va_display_, va_profile_, VAEntrypointVLD, &va_config_attrib_, 1, &va_config_id_)); + return ROCDEC_SUCCESS; +} diff --git a/src/rocdecode/vaapi/vaapi_videodecoder.h b/src/rocdecode/vaapi/vaapi_videodecoder.h index 772ad0e16e..3d6529adb0 100644 --- a/src/rocdecode/vaapi/vaapi_videodecoder.h +++ b/src/rocdecode/vaapi/vaapi_videodecoder.h @@ -22,38 +22,39 @@ THE SOFTWARE. #pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include "../roc_decoder_caps.h" +#include "../../commons.h" +#include "../../../api/rocdecode.h" -// class implementing highlevel vaapi decoder -/***********************************************************************************************************/ -//! requirement is as follows -//! -//! In order to minimize decode latencies, there should be always at least enough pictures (min 2) in the decode -//! queue at any time, in order to make sure that all VCN decode engines are always busy. -//! In addition to the reqular create and destroy: the decoder needs to have a task-Q for submitting decoding jobs from the high level -//! -//! Overall data flow: -//! - GetCaps(...) -//! - CreateVideoDecoder(...) -//! - For each picture: -//! + submitDecodeTask(0) /* submit first frame for decoding */ -//! + ... /* submit next frame for decoding */ -//! + submitDecodeTask(N) /* N is determined based on available HW decode engines in the system */ -//! -//! + QueryStatus(N-4) /* Query the decode status of N-4 frame */ -//! + MapVideoFrame(N-4) -//! + do some processing in HIP -//! + UnmapVideoFrame(N-4) -//! + submitDecodeTask(N+1) -//! + MapVideoFrame(N-3) -//! + ... -//! - DestroyVideoDecoder(...) -//! -//! NOTE: -//! - The decoder has to maintain a Q for decode jobs with associated picture buffers -//! - An intenal thread has to pick up the next available job: If none wait for the Q to fill -/***********************************************************************************************************/ -class vaapiVideoDecoder { +#define CHECK_VAAPI(call) { \ + VAStatus va_status = (call); \ + if (va_status != VA_STATUS_SUCCESS) { \ + std::cout << "VAAPI failure: 'status#" << va_status << "' at " << __FILE__ << ":" << __LINE__ << std::endl;\ + return ROCDEC_RUNTIME_ERROR; \ + } \ +} + +class VaapiVideoDecoder { public: - + VaapiVideoDecoder(RocDecoderCreateInfo &decoder_create_info); + ~VaapiVideoDecoder(); + rocDecStatus InitializeDecoder(std::string gcn_arch_name); +private: + RocDecoderCreateInfo decoder_create_info_; + int drm_fd_; + VADisplay va_display_; + VAConfigAttrib va_config_attrib_; + VAConfigID va_config_id_; + VAProfile va_profile_; + rocDecStatus InitVAAPI(); + rocDecStatus CreateDecoderConfig(); }; \ No newline at end of file diff --git a/utils/rocvideodecode/roc_video_dec.cpp b/utils/rocvideodecode/roc_video_dec.cpp index 5c29f0b23a..a2e622896d 100644 --- a/utils/rocvideodecode/roc_video_dec.cpp +++ b/utils/rocvideodecode/roc_video_dec.cpp @@ -257,7 +257,7 @@ int RocVideoDecoder::HandleVideoSequence(RocdecVideoFormat *pVideoFormat) { } video_format_ = *pVideoFormat; - RocdecDecoderCreateInfo videoDecodeCreateInfo = { 0 }; + RocDecoderCreateInfo videoDecodeCreateInfo = { 0 }; videoDecodeCreateInfo.CodecType = pVideoFormat->codec; videoDecodeCreateInfo.ChromaFormat = pVideoFormat->chroma_format; videoDecodeCreateInfo.OutputFormat = video_surface_format_;