diff --git a/projects/rocdecode/CMakeLists.txt b/projects/rocdecode/CMakeLists.txt index d6c8bbb6c6..7a20216dc0 100644 --- a/projects/rocdecode/CMakeLists.txt +++ b/projects/rocdecode/CMakeLists.txt @@ -132,7 +132,7 @@ if(HIP_FOUND AND FFMPEG_FOUND AND Libva_FOUND) target_link_libraries(${PROJECT_NAME} ${LINK_LIBRARY_LIST}) install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_LIBDIR}) - install(FILES api/rocdecode.h api/rocparser.h utils/video_demuxer.hpp + install(FILES api/rocdecode.h api/rocparser.h utils/video_demuxer.hpp utils/rocvideodecode/roc_video_dec.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}) message("-- ${White}AMD ROCm rocDecode -- CMAKE_CXX_FLAGS:${CMAKE_CXX_FLAGS}${ColourReset}") diff --git a/projects/rocdecode/api/rocdecode.h b/projects/rocdecode/api/rocdecode.h index 426e15bec9..784d6eaf9a 100644 --- a/projects/rocdecode/api/rocdecode.h +++ b/projects/rocdecode/api/rocdecode.h @@ -108,7 +108,7 @@ typedef enum rocDecVideoSurfaceFormat_enum { rocDecVideoSurfaceFormat_NV12=0, /**< Semi-Planar YUV [Y plane followed by interleaved UV plane] */ rocDecVideoSurfaceFormat_P016=1, /**< 16 bit Semi-Planar YUV [Y plane followed by interleaved UV plane]. Can be used for 10 bit(6LSB bits 0), 12 bit (4LSB bits 0) */ - rocDecVideoSurfaceFormat_YUV444=2, /**< Planar YUV [Y plane followed by U and V planes] */ + rocDecVideoSurfaceFormat_YUV444=2, /**< Planar YUV [Y plane followed by U and V planes] */ rocDecVideoSurfaceFormat_YUV444_16Bit=3, /**< 16 bit Planar YUV [Y plane followed by U and V planes]. Can be used for 10 bit(6LSB bits 0), 12 bit (4LSB bits 0) */ } rocDecVideoSurfaceFormat; @@ -125,6 +125,7 @@ typedef enum rocDecVideoChromaFormat_enum { rocDecVideoChromaFormat_444 /**< YUV 4:4:4 */ } rocDecVideoChromaFormat; + /*************************************************************************/ //! \enum rocDecDecodeStatus //! Decode status enums @@ -178,14 +179,14 @@ typedef struct _RocdecDecoderCreateInfo { uint32_t ulMaxWidth; /**< IN: Coded sequence max width in pixels used with reconfigure Decoder */ uint32_t ulMaxHeight; /**< IN: Coded sequence max height in pixels used with reconfigure Decoder */ /** - * IN: area of the frame that should be copied + * IN: area of the frame that should be displayed */ struct { int16_t left; int16_t top; int16_t right; int16_t bottom; - } roi_area; + } display_area; rocDecVideoSurfaceFormat OutputFormat; /**< IN: rocDecVideoSurfaceFormat_XXX */ uint32_t ulTargetWidth; /**< IN: Post-processed output width (Should be aligned to 2) */ @@ -203,7 +204,6 @@ typedef struct _RocdecDecoderCreateInfo { int16_t bottom; } target_rect; - uint32_t enableHistogram; /**< IN: enable histogram output, if supported */ uint32_t Reserved2[4]; /**< Reserved for future use - set to zero */ } RocdecDecoderCreateInfo; @@ -668,7 +668,7 @@ typedef struct _RocdecProcParams uint64_t raw_output_dptr; /**< IN: Output HIP device mem ptr for raw YUV extensions */ uint32_t raw_output_pitch; /**< IN: pitch in bytes of raw YUV output (should be aligned appropriately) */ uint32_t raw_output_format; /**< IN: Output YUV format (rocDecVideoCodec_enum) */ - hipStream_t output_hstream; /**< IN: stream object used by rocDecMapVideoFrame */ + hipStream_t output_hipstream; /**< IN: stream object used by rocDecMapVideoFrame */ uint32_t Reserved[16]; /**< Reserved for future use (set to zero) */ } RocdecProcParams; @@ -714,10 +714,10 @@ extern rocDecStatus ROCDECAPI rocDecDestroyDecoder(rocDecDecoderHandle hDecoder) //! \fn rocDecStatus ROCDECAPI rocdecGetDecoderCaps(RocdecDecodeCaps *pdc) //! Queries decode capabilities of AMD's VCN decoder based on CodecType, ChromaFormat and BitDepthMinus8 parameters. //! 1. Application fills IN parameters CodecType, ChromaFormat and BitDepthMinus8 of RocdecDecodeCaps structure -//! 2. On calling rocdecGetDecoderCaps, driver fills OUT parameters if the IN parameters are supported +//! 2. On calling rocdecGetDecoderCaps, driver fills OUT parameters (for GPU device) if the IN parameters are supported //! If IN parameters passed to the driver are not supported by AMD-VCN-HW, then all OUT params are set to 0. /**********************************************************************************************************************/ -extern rocDecStatus ROCDECAPI rocDecGetDecoderCaps(rocDecDecoderHandle hDecoder, RocdecDecodeCaps *pdc); +extern rocDecStatus ROCDECAPI rocDecGetDecoderCaps(RocdecDecodeCaps *pdc); /*****************************************************************************************************/ //! \fn rocDecStatus ROCDECAPI rocDecDecodeFrame(rocDecDecoderHandle hDecoder, RocdecPicParams *pPicParams) @@ -746,10 +746,10 @@ extern rocDecStatus ROCDECAPI rocDecReconfigureDecoder(rocDecDecoderHandle hDeco //! uint32_t *pDevMemPtr, uint32_t *pHorizontalPitch, //! RocdecProcParams *pVidPostprocParams); //! Post-process and map video frame corresponding to nPicIdx for use in HIP. Returns HIP device pointer and associated -//! pitch(horizontal stride) of the video frame. Returns device memory pointers for each plane (Y, U and V) seperately +//! pitch(horizontal stride) of the video frame. Returns device memory pointers and pitch for each plane (Y, U and V) seperately /************************************************************************************************************************/ extern rocDecStatus ROCDECAPI rocDecMapVideoFrame(rocDecDecoderHandle hDecoder, int nPicIdx, - void *pDevMemPtr[3], uint32_t *pHorizontalPitch[3], + void *pDevMemPtr[3], uint32_t (&pHorizontalPitch)[3], RocdecProcParams *pVidPostprocParams); /*****************************************************************************************************/ diff --git a/projects/rocdecode/api/rocparser.h b/projects/rocdecode/api/rocparser.h index 790ff97c6d..a3ffda0027 100644 --- a/projects/rocdecode/api/rocparser.h +++ b/projects/rocdecode/api/rocparser.h @@ -132,6 +132,21 @@ typedef struct { uint8_t raw_seqhdr_data[1024]; /**< OUT: Sequence header data */ } RocdecVideoFormatEx; +/***************************************************************/ +//! \enum RocdecVideoPacketFlags +//! Data packet flags +//! Used in RocdecSourceDataPacket structure +/***************************************************************/ +typedef enum { + ROCDEC_PKT_ENDOFSTREAM = 0x01, /**< Set when this is the last packet for this stream */ + ROCDEC_PKT_TIMESTAMP = 0x02, /**< Timestamp is valid */ + ROCDEC_PKT_DISCONTINUITY = 0x04, /**< Set when a discontinuity has to be signalled */ + ROCDEC_PKT_ENDOFPICTURE = 0x08, /**< Set when the packet contains exactly one frame or one field */ + ROCDEC_PKT_NOTIFY_EOS = 0x10, /**< If this flag is set along with ROCDEC_PKT_ENDOFSTREAM, an additional (dummy) + display callback will be invoked with null value of ROCDECPARSERDISPINFO which + should be interpreted as end of the stream. */ +} RocdecVideoPacketFlags; + /*****************************************************************************/ //! \ingroup STRUCTS //! \struct RocdecSourceDataPacket @@ -140,11 +155,11 @@ typedef struct { //! IN for rocDecParseVideoData /*****************************************************************************/ typedef struct _RocdecSourceDataPacket { - uint32_t flags; /**< IN: Combination of CUVID_PKT_XXX flags */ + uint32_t flags; /**< IN: Combination of ROCDEC_PKT_XXX flags */ uint32_t payload_size; /**< IN: number of bytes in the payload (may be zero if EOS flag is set) */ const uint8_t *payload; /**< IN: Pointer to packet payload data (may be NULL if EOS flag is set) */ RocdecTimeStamp pts; /**< IN: Presentation time stamp (10MHz clock), only valid if - CUVID_PKT_TIMESTAMP flag is set */ + ROCDEC_PKT_TIMESTAMP flag is set */ } RocdecSourceDataPacket; diff --git a/projects/rocdecode/samples/CMakeLists.txt b/projects/rocdecode/samples/CMakeLists.txt index 69dea23e9f..f1ceb7b1ef 100644 --- a/projects/rocdecode/samples/CMakeLists.txt +++ b/projects/rocdecode/samples/CMakeLists.txt @@ -22,6 +22,7 @@ # ############################################################################## cmake_minimum_required(VERSION 3.5) +set(ROC_VIDEO_DEC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../utils/rocvideodecode) # videoDecode add_test( NAME diff --git a/projects/rocdecode/samples/videoDecode/CMakeLists.txt b/projects/rocdecode/samples/videoDecode/CMakeLists.txt index 181ab18675..9d3dae2119 100644 --- a/projects/rocdecode/samples/videoDecode/CMakeLists.txt +++ b/projects/rocdecode/samples/videoDecode/CMakeLists.txt @@ -33,13 +33,13 @@ set(DEFAULT_AMDGPU_TARGETS "gfx803;gfx900;gfx906;gfx908;gfx90a;gfx940;gfx1030;gf set(AMDGPU_TARGETS "${DEFAULT_AMDGPU_TARGETS}" CACHE STRING "List of specific machine types for library to target") find_package(HIP QUIET) find_package(FFmpeg QUIET) -find_package(Libva QUIET) -if(HIP_FOUND AND FFMPEG_FOUND AND Libva_FOUND) +message("-- ${CMAKE_CURRENT_SOURCE_DIR}") +if(HIP_FOUND AND FFMPEG_FOUND) include_directories (${ROCM_PATH}/include/rocdecode) - list(APPEND SOURCES ${PROJECT_SOURCE_DIR} videodecode.cpp) + list(APPEND SOURCES ${PROJECT_SOURCE_DIR} videodecode.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../utils/rocvideodecode/roc_video_dec.cpp) add_executable(${PROJECT_NAME} ${SOURCES}) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++17") - target_link_libraries(${PROJECT_NAME} ${AVUTIL_LIBRARY} ${AVCODEC_LIBRARY} ${AVFORMAT_LIBRARY} Libva::va Libva::va_drm hip::device rocdecode) + target_link_libraries(${PROJECT_NAME} ${AVUTIL_LIBRARY} ${AVCODEC_LIBRARY} ${AVFORMAT_LIBRARY} hip::device rocdecode) else() if (NOT HIP_FOUND) message(FATAL_ERROR "-- ERROR!: HIP Not Found! - please install ROCm and HIP!") diff --git a/projects/rocdecode/src/parser/h264_parser.cpp b/projects/rocdecode/src/parser/h264_parser.cpp index be452feb8e..ed4bc28850 100644 --- a/projects/rocdecode/src/parser/h264_parser.cpp +++ b/projects/rocdecode/src/parser/h264_parser.cpp @@ -49,4 +49,14 @@ rocDecStatus H264VideoParser::Initialize(RocdecParserParams *pParams) { */ rocDecStatus H264VideoParser::ParseVideoData(RocdecSourceDataPacket *pData) { return ROCDEC_NOT_IMPLEMENTED; -} \ No newline at end of file +} + +/** + * @brief function to uninitialize h264 parser + * + * @return rocDecStatus + */ +rocDecStatus H264VideoParser::UnInitialize() { + //todo:: + return ROCDEC_NOT_IMPLEMENTED; +} diff --git a/projects/rocdecode/src/parser/h264_parser.h b/projects/rocdecode/src/parser/h264_parser.h index 80392926ed..8c27826ea5 100644 --- a/projects/rocdecode/src/parser/h264_parser.h +++ b/projects/rocdecode/src/parser/h264_parser.h @@ -45,6 +45,13 @@ public: * @return rocDecStatus: returns success on completion, else error_code for failure */ virtual rocDecStatus ParseVideoData(RocdecSourceDataPacket *pData); + /** + * @brief function to uninitialize h264 parser + * + * @return rocDecStatus + */ + virtual rocDecStatus UnInitialize(); // derived method + private: diff --git a/projects/rocdecode/src/parser/hevc_parser.cpp b/projects/rocdecode/src/parser/hevc_parser.cpp index ffc3e9f001..06189fefbf 100644 --- a/projects/rocdecode/src/parser/hevc_parser.cpp +++ b/projects/rocdecode/src/parser/hevc_parser.cpp @@ -43,6 +43,17 @@ rocDecStatus HEVCVideoParser::Initialize(RocdecParserParams *p_params) { return ROCDEC_SUCCESS; } +/** + * @brief function to uninitialize hevc parser + * + * @return rocDecStatus + */ +rocDecStatus HEVCVideoParser::UnInitialize() { + //todo:: do any uninitialization here + return ROCDEC_SUCCESS; +} + + rocDecStatus HEVCVideoParser::ParseVideoData(RocdecSourceDataPacket *p_data) { bool status = ParseFrameData(p_data->payload, p_data->payload_size); if (!status) { diff --git a/projects/rocdecode/src/parser/hevc_parser.h b/projects/rocdecode/src/parser/hevc_parser.h index 5d54897c39..965e5149b9 100644 --- a/projects/rocdecode/src/parser/hevc_parser.h +++ b/projects/rocdecode/src/parser/hevc_parser.h @@ -59,12 +59,20 @@ public: */ virtual rocDecStatus ParseVideoData(RocdecSourceDataPacket *p_data); - /*! \brief HEVCParser object destructor + /** + * @brief function to uninitialize hevc parser + * + * @return rocDecStatus + */ + virtual rocDecStatus UnInitialize(); // derived method + + /** + * @brief HEVCParser object destructor */ virtual ~HEVCVideoParser(); protected: - /*! \brief Enumerator for the NAL Unit types - ISO-IEC 14496-15-2004.pdf, page 14, table 1 " NAL unit types in elementary streams. + /*! \brief Enumerator for the NAL Unit types - ISO-IEC 14496-15-2004.pdf, page 14, table 1 " NAL unit types in elementary streams */ enum NalUnitType { NAL_UNIT_CODED_SLICE_TRAIL_N = 0, // 0 diff --git a/projects/rocdecode/src/parser/parser_handle.h b/projects/rocdecode/src/parser/parser_handle.h index 81ecd178ac..4bcf3ba7e7 100644 --- a/projects/rocdecode/src/parser/parser_handle.h +++ b/projects/rocdecode/src/parser/parser_handle.h @@ -36,6 +36,7 @@ public: const char* error_msg() { return error.c_str(); } void capture_error(const std::string& err_msg) { error = err_msg; } rocDecStatus ParseVideoData(RocdecSourceDataPacket *pPacket) { return roc_parser_->ParseVideoData(pPacket); } + rocDecStatus DestroyParser() { return destroy_parser(); }; private: std::shared_ptr roc_parser_ = nullptr; // class instantiation @@ -59,5 +60,15 @@ private: THROW("rocParser Initialization failed with error: "+ TOSTR(ret)); } } + rocDecStatus destroy_parser() { + rocDecStatus ret = ROCDEC_NOT_INITIALIZED; + if (roc_parser_ ) { + ret = roc_parser_->UnInitialize(); + if (ret != ROCDEC_SUCCESS) + THROW("rocParser UnInitialization failed with error: "+ TOSTR(ret)); + } + return ret; + } + std::string error; }; \ No newline at end of file diff --git a/projects/rocdecode/src/parser/roc_video_parser.h b/projects/rocdecode/src/parser/roc_video_parser.h index 64a123eb1f..0c10d78f0d 100644 --- a/projects/rocdecode/src/parser/roc_video_parser.h +++ b/projects/rocdecode/src/parser/roc_video_parser.h @@ -39,6 +39,7 @@ public: RocdecParserParams *GetParserParams() {return parser_params_;}; virtual rocDecStatus Initialize(RocdecParserParams *pParams); virtual rocDecStatus ParseVideoData(RocdecSourceDataPacket *pData) = 0; // pure virtual: implemented by derived class + virtual rocDecStatus UnInitialize() = 0; // pure virtual: implemented by derived class protected: RocdecParserParams *parser_params_ = nullptr; diff --git a/projects/rocdecode/src/parser/rocparser_api.cpp b/projects/rocdecode/src/parser/rocparser_api.cpp index 294e7b243e..1b088ed758 100644 --- a/projects/rocdecode/src/parser/rocparser_api.cpp +++ b/projects/rocdecode/src/parser/rocparser_api.cpp @@ -65,3 +65,23 @@ rocDecParseVideoData(RocdecVideoParser handle, RocdecSourceDataPacket *pPacket) } return ret; } + +/************************************************************************************************/ +//! \ingroup FUNCTS +//! \fn rocDecStatus ROCDECAPI rocDecDestroyVideoParser(RocdecVideoParser handle) +//! Destroy the video parser object +/************************************************************************************************/ +extern rocDecStatus ROCDECAPI +rocDecDestroyVideoParser(RocdecVideoParser handle) { + auto parser_hdl = static_cast (handle); + rocDecStatus ret; + try { + ret = parser_hdl->DestroyParser(); + } + catch(const std::exception& e) { + parser_hdl->capture_error(e.what()); + ERR(e.what()) + return ROCDEC_RUNTIME_ERROR; + } + return ret; +} diff --git a/projects/rocdecode/src/rocdecode/roc_decoder.cpp b/projects/rocdecode/src/rocdecode/roc_decoder.cpp index 8177e71a41..24ba40a264 100644 --- a/projects/rocdecode/src/rocdecode/roc_decoder.cpp +++ b/projects/rocdecode/src/rocdecode/roc_decoder.cpp @@ -42,13 +42,6 @@ RocDecoder::RocDecoder(int device_id):device_id_ {device_id}, num_devices_{0} { } } } -rocDecStatus RocDecoder::getDecoderCaps(RocdecDecodeCaps *pdc) { - // todo:: return appropriate decStatus if fails - //vaQueryConfigProfiles - // fill the RocdecDecodeCaps struct - // return status - return ROCDEC_NOT_IMPLEMENTED; -} rocDecStatus RocDecoder::decodeFrame(RocdecPicParams *pPicParams) { // todo:: return appropriate decStatus if fails @@ -73,7 +66,7 @@ rocDecStatus RocDecoder::reconfigureDecoder(RocdecReconfigureDecoderInfo *pDecRe } rocDecStatus RocDecoder::mapVideoFrame(int nPicIdx, void *pDevMemPtr[3], - unsigned int *pHorizontalPitch[3], RocdecProcParams *pVidPostprocParams) { + unsigned int pHorizontalPitch[3], RocdecProcParams *pVidPostprocParams) { // todo:: return appropriate decStatus // Post-process and map video frame corresponding to nPicIdx for use in HIP. Returns HIP device pointer and associated // pitch(horizontal stride) of the video frame. Returns device memory pointers for each plane (Y, U and V) seperately diff --git a/projects/rocdecode/src/rocdecode/roc_decoder.h b/projects/rocdecode/src/rocdecode/roc_decoder.h index 162f333b74..91a2289b93 100644 --- a/projects/rocdecode/src/rocdecode/roc_decoder.h +++ b/projects/rocdecode/src/rocdecode/roc_decoder.h @@ -38,11 +38,10 @@ class RocDecoder { public: RocDecoder(int device_id = 0); ~RocDecoder(); - rocDecStatus getDecoderCaps(RocdecDecodeCaps *pdc); rocDecStatus decodeFrame(RocdecPicParams *pPicParams); rocDecStatus getDecodeStatus(int nPicIdx, RocdecDecodeStatus* pDecodeStatus); rocDecStatus reconfigureDecoder(RocdecReconfigureDecoderInfo *pDecReconfigParams); - rocDecStatus mapVideoFrame(int nPicIdx, void *pDevMemPtr[3], unsigned int *pHorizontalPitch[3], RocdecProcParams *pVidPostprocParams); + rocDecStatus mapVideoFrame(int nPicIdx, void *pDevMemPtr[3], unsigned int pHorizontalPitch[3], RocdecProcParams *pVidPostprocParams); rocDecStatus unMapVideoFrame(void *pMappedDevPtr); private: diff --git a/projects/rocdecode/src/rocdecode/rocdecode_api.cpp b/projects/rocdecode/src/rocdecode/rocdecode_api.cpp index ed5c738acb..b33269241a 100644 --- a/projects/rocdecode/src/rocdecode/rocdecode_api.cpp +++ b/projects/rocdecode/src/rocdecode/rocdecode_api.cpp @@ -58,19 +58,9 @@ rocDecDestroyDecoder(rocDecDecoderHandle hDecoder) { //! 2. On calling rocdecGetDecoderCaps, driver fills OUT parameters if the IN parameters are supported //! If IN parameters passed to the driver are not supported by AMD-VCN-HW, then all OUT params are set to 0. /**********************************************************************************************************************/ -rocDecStatus ROCDECAPI -rocDecGetDecoderCaps(rocDecDecoderHandle hDecoder, RocdecDecodeCaps *pdc) { - auto handle = static_cast (hDecoder); - rocDecStatus ret; - try { - ret = handle->roc_decoder->getDecoderCaps(pdc); - } - catch(const std::exception& e) { - handle->capture_error(e.what()); - ERR(e.what()) - return ROCDEC_RUNTIME_ERROR; - } - return ret; +rocDecStatus ROCDECAPI +rocDecGetDecoderCaps(RocdecDecodeCaps *pdc) { + return ROCDEC_NOT_IMPLEMENTED; } /*****************************************************************************************************/ @@ -100,7 +90,7 @@ rocDecDecodeFrame(rocDecDecoderHandle hDecoder, RocdecPicParams *pPicParams) { //! API returns CUDA_ERROR_NOT_SUPPORTED error code for unsupported GPU or codec. /************************************************************************************************************/ rocDecStatus ROCDECAPI -RocdecGetDecodeStatus(rocDecDecoderHandle hDecoder, int nPicIdx, RocdecDecodeStatus* pDecodeStatus) { +rocDecGetDecodeStatus(rocDecDecoderHandle hDecoder, int nPicIdx, RocdecDecodeStatus* pDecodeStatus) { auto handle = static_cast (hDecoder); rocDecStatus ret; try { @@ -143,7 +133,7 @@ rocDecReconfigureDecoder(rocDecDecoderHandle hDecoder, RocdecReconfigureDecoderI /************************************************************************************************************************/ rocDecStatus ROCDECAPI rocDecMapVideoFrame(rocDecDecoderHandle hDecoder, int nPicIdx, - void *pDevMemPtr[3], unsigned int *pHorizontalPitch[3], RocdecProcParams *pVidPostprocParams) { + void *pDevMemPtr[3], uint32_t (&pHorizontalPitch)[3], RocdecProcParams *pVidPostprocParams) { auto handle = static_cast (hDecoder); rocDecStatus ret; try { diff --git a/projects/rocdecode/utils/rocvideodecode/commons.h b/projects/rocdecode/utils/rocvideodecode/commons.h new file mode 100644 index 0000000000..46aca6ba8f --- /dev/null +++ b/projects/rocdecode/utils/rocvideodecode/commons.h @@ -0,0 +1,51 @@ +/* +Copyright (c) 2023 - 2023 Advanced Micro Devices, Inc. All rights reserved. + +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 +#include +#include +#include + +#define TOSTR(X) std::to_string(static_cast(X)) +#define STR(X) std::string(X) + +#if DBGINFO +#define INFO(X) std::clog << "[INF] " << " {" << __func__ <<"} " << " " << X << std::endl; +#else +#define INFO(X) ; +#endif +#define ERR(X) std::cerr << "[ERR] " << " {" << __func__ <<"} " << " " << X << std::endl; + + +class rocVideoDecodeException : public std::exception { +public: + + explicit rocVideoDecodeException(const std::string& message):_message(message){} + virtual const char* what() const throw() override { + return _message.c_str(); + } +private: + std::string _message; +}; + +#define THROW(X) throw rocVideoDecodeException(" { "+std::string(__func__)+" } " + X); diff --git a/projects/rocdecode/utils/rocvideodecode/roc_video_dec.cpp b/projects/rocdecode/utils/rocvideodecode/roc_video_dec.cpp new file mode 100644 index 0000000000..5c29f0b23a --- /dev/null +++ b/projects/rocdecode/utils/rocvideodecode/roc_video_dec.cpp @@ -0,0 +1,663 @@ +/* +Copyright (c) 2023 - 2023 Advanced Micro Devices, Inc. All rights reserved. + +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 "roc_video_dec.h" + +RocVideoDecoder::RocVideoDecoder(int device_id, bool b_use_device_mem, rocDecVideoCodec codec, bool b_low_latency, bool device_frame_pitched, + const Rect *p_crop_rect, const Dim *p_resize_dim, bool extract_user_SEI_Message, int max_width, int max_height, + uint32_t clk_rate, bool force_zero_latency) : device_id_{device_id}, b_use_device_mem_(b_use_device_mem), codec_id_(codec), + b_low_latency_(b_low_latency), b_device_frame_pitched_(device_frame_pitched), b_extract_sei_message_(extract_user_SEI_Message), + max_width_ (max_width), max_height_(max_height), b_force_zero_latency_(force_zero_latency) { + + if (!InitHIP(device_id_)) { + THROW("Failed to initilize the HIP"); + } + if (p_crop_rect) crop_rect_ = *p_crop_rect; + if (p_resize_dim) resize_dim_ = *p_resize_dim; + if (b_extract_sei_message_) { + fp_sei_ = fopen("rocdec_sei_message.txt", "wb"); + curr_sei_message_ptr_ = new RocdecSeiMessageInfo; + memset(&sei_message_display_q_, 0, sizeof(sei_message_display_q_)); + } + // create rocdec videoparser + RocdecParserParams parser_params = {}; + parser_params.CodecType = codec_id_; + parser_params.ulMaxNumDecodeSurfaces = 1; + parser_params.ulClockRate = clk_rate; + parser_params.ulMaxDisplayDelay = b_low_latency ? 0 : 1; + parser_params.pUserData = this; + parser_params.pfnSequenceCallback = HandleVideoSequenceProc; + parser_params.pfnDecodePicture = HandlePictureDecodeProc; + parser_params.pfnDisplayPicture = b_force_zero_latency_ ? NULL : HandlePictureDisplayProc; + parser_params.pfnGetSEIMsg = b_extract_sei_message_ ? HandleSEIMessagesProc : NULL; + ROCDEC_API_CALL(rocDecCreateVideoParser(&rocdec_parser_, &parser_params)); +} + + +RocVideoDecoder::~RocVideoDecoder() { + if (curr_sei_message_ptr_) { + delete curr_sei_message_ptr_; + curr_sei_message_ptr_ = nullptr; + } + + if (fp_sei_) { + fclose(fp_sei_); + fp_sei_ = nullptr; + } + + if (rocdec_parser_) { + rocDecDestroyVideoParser(rocdec_parser_); + rocdec_parser_ = nullptr; + } + + if (roc_decoder_) { + rocDecDestroyDecoder(roc_decoder_); + roc_decoder_ = nullptr; + } + + if (hip_stream_) { + hipError_t hip_status = hipSuccess; + hip_status = hipStreamDestroy(hip_stream_); + if (hip_status != hipSuccess) { + std::cout << "ERROR: hipStream_Destroy failed! (" << hip_status << ")" << std::endl; + } + } +} + +static const char * GetVideoCodecString(rocDecVideoCodec eCodec) { + static struct { + rocDecVideoCodec eCodec; + const char *name; + } aCodecName [] = { + { rocDecVideoCodec_MPEG1, "MPEG-1" }, + { rocDecVideoCodec_MPEG2, "MPEG-2" }, + { rocDecVideoCodec_MPEG4, "MPEG-4 (ASP)" }, + { rocDecVideoCodec_H264, "AVC/H.264" }, + { rocDecVideoCodec_JPEG, "M-JPEG" }, + { rocDecVideoCodec_HEVC, "H.265/HEVC" }, + { rocDecVideoCodec_VP8, "VP8" }, + { rocDecVideoCodec_VP9, "VP9" }, + { rocDecVideoCodec_AV1, "AV1" }, + { rocDecVideoCodec_NumCodecs, "Invalid" }, + }; + + if (eCodec >= 0 && eCodec <= rocDecVideoCodec_NumCodecs) { + return aCodecName[eCodec].name; + } + for (int i = rocDecVideoCodec_NumCodecs + 1; i < sizeof(aCodecName) / sizeof(aCodecName[0]); i++) { + if (eCodec == aCodecName[i].eCodec) { + return aCodecName[eCodec].name; + } + } + return "Unknown"; +} + +/** + * @brief function to return the name from codec_id + * + * @param codec_id + * @return const char* + */ +const char *RocVideoDecoder::GetCodecFmtName(rocDecVideoCodec codec_id) +{ + return GetVideoCodecString(codec_id); +} + +static const char * GetVideoChromaFormatName(rocDecVideoChromaFormat e_chroma_format) { + static struct { + rocDecVideoChromaFormat chroma_fmt; + const char *name; + } ChromaFormatName[] = { + { rocDecVideoChromaFormat_Monochrome, "YUV 400 (Monochrome)" }, + { rocDecVideoChromaFormat_420, "YUV 420" }, + { rocDecVideoChromaFormat_422, "YUV 422" }, + { rocDecVideoChromaFormat_444, "YUV 444" }, + }; + + if (e_chroma_format >= 0 && e_chroma_format <= rocDecVideoChromaFormat_444) { + return ChromaFormatName[e_chroma_format].name; + } + return "Unknown"; +} + +static float GetChromaHeightFactor(rocDecVideoSurfaceFormat surface_format) { + float factor = 0.5; + switch (surface_format) { + case rocDecVideoSurfaceFormat_NV12: + case rocDecVideoSurfaceFormat_P016: + factor = 0.5; + break; + case rocDecVideoSurfaceFormat_YUV444: + case rocDecVideoSurfaceFormat_YUV444_16Bit: + factor = 1.0; + break; + } + + return factor; +} + +static int GetChromaPlaneCount(rocDecVideoSurfaceFormat surface_format) { + int num_planes = 1; + switch (surface_format) { + case rocDecVideoSurfaceFormat_NV12: + case rocDecVideoSurfaceFormat_P016: + num_planes = 1; + break; + case rocDecVideoSurfaceFormat_YUV444: + case rocDecVideoSurfaceFormat_YUV444_16Bit: + num_planes = 2; + break; + } + + return num_planes; +} + + +/* 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 RocVideoDecoder::HandleVideoSequence(RocdecVideoFormat *pVideoFormat) { + //START_TIMER + input_video_info_str_.str(""); + input_video_info_str_.clear(); + input_video_info_str_ << "Input Video Information" << std::endl + << "\tCodec : " << GetCodecFmtName(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 : " << GetVideoChromaFormatName(pVideoFormat->chroma_format) << std::endl + << "\tBit depth : " << pVideoFormat->bit_depth_luma_minus8 + 8 + ; + input_video_info_str_ << std::endl; + + int nDecodeSurface = pVideoFormat->min_num_decode_surfaces; + + RocdecDecodeCaps decode_caps; + memset(&decode_caps, 0, sizeof(decode_caps)); + decode_caps.eCodecType = pVideoFormat->codec; + decode_caps.eChromaFormat = pVideoFormat->chroma_format; + decode_caps.nBitDepthMinus8 = pVideoFormat->bit_depth_luma_minus8; + + ROCDEC_API_CALL(rocDecGetDecoderCaps(&decode_caps)); + + if(!decode_caps.bIsSupported){ + THROW("Rocdec:: Codec not supported on this GPU: " + TOSTR(ROCDEC_NOT_SUPPORTED)); + return 0; + } + + if ((pVideoFormat->coded_width > decode_caps.nMaxWidth) || + (pVideoFormat->coded_height > decode_caps.nMaxHeight)){ + + std::ostringstream errorString; + errorString << std::endl + << "Resolution : " << pVideoFormat->coded_width << "x" << pVideoFormat->coded_height << std::endl + << "Max Supported (wxh) : " << decode_caps.nMaxWidth << "x" << decode_caps.nMaxHeight << std::endl + << "Resolution not supported on this GPU "; + + const std::string cErr = errorString.str(); + THROW(cErr+ TOSTR(ROCDEC_NOT_SUPPORTED)); + return nDecodeSurface; + } + + if (width_ && height_ && chroma_height_) { + + // rocdecCreateDecoder() has been called before, and now there's possible config change + // todo:: support reconfigure + //return ReconfigureDecoder(pVideoFormat); + } + + // eCodec has been set in the constructor (for parser). Here it's set again for potential correction + codec_id_ = pVideoFormat->codec; + video_chroma_format_ = pVideoFormat->chroma_format; + bitdepth_minus_8_ = pVideoFormat->bit_depth_luma_minus8; + byte_per_pixel_ = bitdepth_minus_8_ > 0 ? 2 : 1; + + // Set the output surface format same as chroma format + if (video_chroma_format_ == rocDecVideoChromaFormat_420 || rocDecVideoChromaFormat_Monochrome) + video_surface_format_ = pVideoFormat->bit_depth_luma_minus8 ? rocDecVideoSurfaceFormat_P016 : rocDecVideoSurfaceFormat_NV12; + else if (video_chroma_format_ == rocDecVideoChromaFormat_444) + video_surface_format_ = pVideoFormat->bit_depth_luma_minus8 ? rocDecVideoSurfaceFormat_YUV444_16Bit : rocDecVideoSurfaceFormat_YUV444; + else if (video_chroma_format_ == rocDecVideoChromaFormat_422) + video_surface_format_ = rocDecVideoSurfaceFormat_NV12; // 422 output surface is not supported:: default to NV12 + + // Check if output format supported. If not, check falback options + if (!(decode_caps.nOutputFormatMask & (1 << video_surface_format_))){ + if (decode_caps.nOutputFormatMask & (1 << rocDecVideoSurfaceFormat_NV12)) + video_surface_format_ = rocDecVideoSurfaceFormat_NV12; + else if (decode_caps.nOutputFormatMask & (1 << rocDecVideoSurfaceFormat_P016)) + video_surface_format_ = rocDecVideoSurfaceFormat_P016; + else if (decode_caps.nOutputFormatMask & (1 << rocDecVideoSurfaceFormat_YUV444)) + video_surface_format_ = rocDecVideoSurfaceFormat_YUV444; + else if (decode_caps.nOutputFormatMask & (1 << rocDecVideoSurfaceFormat_YUV444_16Bit)) + video_surface_format_ = rocDecVideoSurfaceFormat_YUV444_16Bit; + else + THROW("No supported output format found" + TOSTR(ROCDEC_NOT_SUPPORTED)); + } + video_format_ = *pVideoFormat; + + RocdecDecoderCreateInfo videoDecodeCreateInfo = { 0 }; + videoDecodeCreateInfo.CodecType = pVideoFormat->codec; + videoDecodeCreateInfo.ChromaFormat = pVideoFormat->chroma_format; + videoDecodeCreateInfo.OutputFormat = video_surface_format_; + videoDecodeCreateInfo.bitDepthMinus8 = pVideoFormat->bit_depth_luma_minus8; + videoDecodeCreateInfo.ulNumOutputSurfaces = 2; + videoDecodeCreateInfo.ulNumDecodeSurfaces = nDecodeSurface; + videoDecodeCreateInfo.ulWidth = pVideoFormat->coded_width; + videoDecodeCreateInfo.ulHeight = pVideoFormat->coded_height; + // AV1 has max width/height of sequence in sequence header + if (pVideoFormat->codec == rocDecVideoCodec_AV1 && pVideoFormat->seqhdr_data_length > 0) { + // dont overwrite if it is already set from cmdline or reconfig.txt + if (!(max_width_ > pVideoFormat->coded_width || max_height_ > pVideoFormat->coded_height)) + { + RocdecVideoFormatEx *vidFormatEx = (RocdecVideoFormatEx *)pVideoFormat; + max_width_ = vidFormatEx->max_width; + max_height_ = vidFormatEx->max_height; + } + } + if (max_width_ < (int)pVideoFormat->coded_width) + max_width_ = pVideoFormat->coded_width; + if (max_height_ < (int)pVideoFormat->coded_height) + max_height_ = pVideoFormat->coded_height; + videoDecodeCreateInfo.ulMaxWidth = max_width_; + videoDecodeCreateInfo.ulMaxHeight = max_height_; + + if (!(crop_rect_.r && crop_rect_.b) && !(resize_dim_.w && resize_dim_.h)) { + width_ = pVideoFormat->display_area.right - pVideoFormat->display_area.left; + height_ = pVideoFormat->display_area.bottom - pVideoFormat->display_area.top; + videoDecodeCreateInfo.ulTargetWidth = pVideoFormat->coded_width; + videoDecodeCreateInfo.ulTargetHeight = pVideoFormat->coded_height; + } else { + if (resize_dim_.w && resize_dim_.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; + width_ = resize_dim_.w; + height_ = resize_dim_.h; + } + + if (crop_rect_.r && crop_rect_.b) { + videoDecodeCreateInfo.display_area.left = crop_rect_.l; + videoDecodeCreateInfo.display_area.top = crop_rect_.t; + videoDecodeCreateInfo.display_area.right = crop_rect_.r; + videoDecodeCreateInfo.display_area.bottom = crop_rect_.b; + width_ = crop_rect_.r - crop_rect_.l; + height_ = crop_rect_.b - crop_rect_.t; + } + videoDecodeCreateInfo.ulTargetWidth = width_; + videoDecodeCreateInfo.ulTargetHeight = height_; + } + + chroma_height_ = (int)(ceil(height_ * GetChromaHeightFactor(video_surface_format_))); + num_chroma_planes_ = GetChromaPlaneCount(video_surface_format_); + surface_height_ = videoDecodeCreateInfo.ulTargetHeight; + surface_width_ = videoDecodeCreateInfo.ulTargetWidth; + surface_stride_ = align(surface_width_, 256) * byte_per_pixel_; // 256 alignment is enforced for internal VCN surface, keeping the same for ease of memcpy + // fill output_surface_info_ + output_surface_info_.output_width = surface_width_; + output_surface_info_.output_height = surface_height_; + output_surface_info_.output_pitch = surface_stride_; + output_surface_info_.bit_depth = bitdepth_minus_8_ + 8; + output_surface_info_.bytes_per_pixel = byte_per_pixel_; + output_surface_info_.surface_format = video_surface_format_; + output_surface_info_.num_chroma_planes = num_chroma_planes_; + + disp_rect_.b = videoDecodeCreateInfo.display_area.bottom; + disp_rect_.t = videoDecodeCreateInfo.display_area.top; + disp_rect_.l = videoDecodeCreateInfo.display_area.left; + disp_rect_.r = videoDecodeCreateInfo.display_area.right; + + input_video_info_str_ << "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 + ; + input_video_info_str_ << std::endl; + + ROCDEC_API_CALL(rocDecCreateDecoder(&roc_decoder_, &videoDecodeCreateInfo)); + return nDecodeSurface; +} + + +int RocVideoDecoder::ReconfigureDecoder(RocdecVideoFormat *pVideoFormat) { + THROW("ReconfigureDecoder is not supported in this version: " + TOSTR(ROCDEC_NOT_SUPPORTED)); + return ROCDEC_NOT_SUPPORTED; +} + +/** + * @brief + * + * @param pPicParams + * @return int 1: success 0: fail + */ +int RocVideoDecoder::HandlePictureDecode(RocdecPicParams *pPicParams) { + if (!roc_decoder_) + { + THROW("Decoder not initialized: failed with ErrCode: " + TOSTR(ROCDEC_NOT_INITIALIZED)); + return false; + } + pic_num_in_dec_order_[pPicParams->CurrPicIdx] = decode_poc_++; + ROCDEC_API_CALL(rocDecDecodeFrame(roc_decoder_, pPicParams)); + if (b_force_zero_latency_ && ((!pPicParams->field_pic_flag) || (pPicParams->second_field))) + { + RocdecParserDispInfo disp_info; + memset(&disp_info, 0, sizeof(disp_info)); + disp_info.picture_index = pPicParams->CurrPicIdx; + disp_info.progressive_frame = !pPicParams->field_pic_flag; + disp_info.top_field_first = pPicParams->bottom_field_flag ^ 1; + HandlePictureDisplay(&disp_info); + } + return 1; +} + +/** + * @brief function to handle display picture + * + * @param pDispInfo + * @return int 0:fail 1: success + */ +int RocVideoDecoder::HandlePictureDisplay(RocdecParserDispInfo *pDispInfo) { + RocdecProcParams video_proc_params = {}; + video_proc_params.progressive_frame = pDispInfo->progressive_frame; + video_proc_params.top_field_first = pDispInfo->top_field_first; + video_proc_params.output_hipstream = hip_stream_; + + if (b_extract_sei_message_) { + if (sei_message_display_q_[pDispInfo->picture_index].pSEIData) { + // Write SEI Message + uint8_t *sei_buffer = (uint8_t *)(sei_message_display_q_[pDispInfo->picture_index].pSEIData); + uint32_t sei_num_messages = sei_message_display_q_[pDispInfo->picture_index].sei_message_count; + RocdecSeiMessage *sei_message = sei_message_display_q_[pDispInfo->picture_index].pSEIMessage; + if (fp_sei_) { + for (uint32_t i = 0; i < sei_num_messages; i++) { + if (codec_id_ == rocDecVideoCodec_H264 || rocDecVideoCodec_HEVC) { + switch (sei_message[i].sei_message_type) { + case SEI_TYPE_TIME_CODE: + { + //todo:: check if we need to write timecode + } + break; + case SEI_TYPE_USER_DATA_UNREGISTERED: + { + fwrite(sei_buffer, sei_message[i].sei_message_size, 1, fp_sei_); + } + break; + } + } + if (codec_id_ == rocDecVideoCodec_AV1) { + fwrite(sei_buffer, sei_message[i].sei_message_size, 1, fp_sei_); + } + sei_buffer += sei_message[i].sei_message_size; + } + } + free(sei_message_display_q_[pDispInfo->picture_index].pSEIData); + free(sei_message_display_q_[pDispInfo->picture_index].pSEIMessage); + } + } + + void * src_dev_ptr[3] = { 0 }; + uint32_t src_pitch[3] = { 0 }; + ROCDEC_API_CALL(rocDecMapVideoFrame(roc_decoder_, pDispInfo->picture_index, src_dev_ptr, src_pitch, &video_proc_params)); + + RocdecDecodeStatus dec_status; + memset(&dec_status, 0, sizeof(dec_status)); + rocDecStatus result = rocDecGetDecodeStatus(roc_decoder_, pDispInfo->picture_index, &dec_status); + if (result == ROCDEC_SUCCESS && (dec_status.decodeStatus == rocDecodeStatus_Error || dec_status.decodeStatus == rocDecodeStatus_Error_Concealed)) { + std::cerr << "Decode Error occurred for picture: " << pic_num_in_dec_order_[pDispInfo->picture_index] << std::endl; + } + // copy the decoded surface info device or host + uint8_t *p_dec_frame = nullptr; + { + std::lock_guard lock(mtx_vp_frame_); + if ((unsigned)++decoded_frame_cnt_ > vp_frames_.size()) { + // Not enough frames in stock + num_alloced_frames_++; + DecFrameBuffer dec_frame = { 0 }; + if (b_use_device_mem_) { + // allocate based on piched or not + if (b_device_frame_pitched_) + HIP_API_CALL(hipMalloc((void **)&dec_frame.frame_ptr, GetFrameSizePitched())); + else + HIP_API_CALL(hipMalloc((void **)&dec_frame.frame_ptr, GetFrameSize())); + } + else{ + dec_frame.frame_ptr = new uint8_t[GetFrameSize()]; + } + dec_frame.pts = pDispInfo->pts; + vp_frames_.push_back(dec_frame); + } + p_dec_frame = vp_frames_[decoded_frame_cnt_ - 1].frame_ptr; + } + + // Copy luma data + int dst_pitch = b_device_frame_pitched_? surface_stride_ : width_*byte_per_pixel_; + if (b_use_device_mem_) { + if (src_pitch[0] == dst_pitch) { + int luma_size = src_pitch[0] * height_; + HIP_API_CALL(hipMemcpyDtoDAsync(p_dec_frame, src_dev_ptr[0], luma_size, hip_stream_)); + }else { + // use 2d copy to copy an ROI + HIP_API_CALL(hipMemcpy2DAsync(p_dec_frame, dst_pitch, src_dev_ptr[0], src_pitch[0], width_*byte_per_pixel_, height_, hipMemcpyDeviceToDevice, hip_stream_)); + } + } + else + HIP_API_CALL(hipMemcpy2DAsync(p_dec_frame, width_*byte_per_pixel_, src_dev_ptr[0], src_pitch[0], width_*byte_per_pixel_, height_, hipMemcpyDeviceToHost, hip_stream_)); + + // Copy chroma plane ( ) + // rocDec output gives pointer to luma and chroma pointers seperated for the decoded frame + uint8_t *p_frame_uv = p_dec_frame + dst_pitch * height_; + if (b_use_device_mem_) { + if (src_pitch[1] == dst_pitch) { + int chroma_size = chroma_height_ * dst_pitch; + HIP_API_CALL(hipMemcpyDtoDAsync(p_frame_uv, src_dev_ptr[1], chroma_size, hip_stream_)); + }else { + // use 2d copy to copy an ROI + HIP_API_CALL(hipMemcpy2DAsync(p_frame_uv, dst_pitch, src_dev_ptr[1], src_pitch[1], width_*byte_per_pixel_, chroma_height_, hipMemcpyDeviceToDevice, hip_stream_)); + } + }else + HIP_API_CALL(hipMemcpy2DAsync(p_frame_uv, dst_pitch, src_dev_ptr[1], src_pitch[1], width_*byte_per_pixel_, chroma_height_, hipMemcpyDeviceToHost, hip_stream_)); + + if (num_chroma_planes_ == 2) { + uint8_t *p_frame_uv = p_dec_frame + dst_pitch * height_*2; + if (b_use_device_mem_) { + if (src_pitch[2] == dst_pitch) { + int chroma_size = chroma_height_ * dst_pitch; + HIP_API_CALL(hipMemcpyDtoDAsync(p_frame_uv, src_dev_ptr[2], chroma_size, hip_stream_)); + }else { + // use 2d copy to copy an ROI + HIP_API_CALL(hipMemcpy2DAsync(p_frame_uv, dst_pitch, src_dev_ptr[2], src_pitch[2], width_*byte_per_pixel_, chroma_height_, hipMemcpyDeviceToDevice, hip_stream_)); + } + }else + HIP_API_CALL(hipMemcpy2DAsync(p_frame_uv, dst_pitch, src_dev_ptr[2], src_pitch[2], width_*byte_per_pixel_, chroma_height_, hipMemcpyDeviceToHost, hip_stream_)); + } + + HIP_API_CALL(hipStreamSynchronize(hip_stream_)); + ROCDEC_API_CALL(rocDecUnMapVideoFrame(roc_decoder_, src_dev_ptr[0])); + return 1; +} + +int RocVideoDecoder::GetSEIMessage(RocdecSeiMessageInfo *pSEIMessageInfo) { + uint32_t sei_num_mesages = pSEIMessageInfo->sei_message_count; + RocdecSeiMessage *p_sei_msg_info = pSEIMessageInfo->pSEIMessage; + size_t total_SEI_buff_size = 0; + if ((pSEIMessageInfo->picIdx < 0) || (pSEIMessageInfo->picIdx >= MAX_FRAME_NUM)) { + ERR("Invalid picture index for SEI message: " + TOSTR(pSEIMessageInfo->picIdx)); + return 0; + } + for (uint32_t i = 0; i < sei_num_mesages; i++) { + total_SEI_buff_size += p_sei_msg_info[i].sei_message_size; + } + if (!curr_sei_message_ptr_) { + ERR("Out of Memory, Allocation failed for m_pCurrSEIMessage"); + return 0; + } + curr_sei_message_ptr_->pSEIData = malloc(total_SEI_buff_size); + if (!curr_sei_message_ptr_->pSEIData) { + ERR("Out of Memory, Allocation failed for SEI Buffer"); + return 0; + } + memcpy(curr_sei_message_ptr_->pSEIData, pSEIMessageInfo->pSEIData, total_SEI_buff_size); + curr_sei_message_ptr_->pSEIMessage = (RocdecSeiMessage *)malloc(sizeof(RocdecSeiMessage) * sei_num_mesages); + if (!curr_sei_message_ptr_->pSEIMessage) { + free(curr_sei_message_ptr_->pSEIData); + curr_sei_message_ptr_->pSEIData = NULL; + return 0; + } + memcpy(curr_sei_message_ptr_->pSEIMessage, pSEIMessageInfo->pSEIMessage, sizeof(RocdecSeiMessage) * sei_num_mesages); + curr_sei_message_ptr_->sei_message_count = pSEIMessageInfo->sei_message_count; + sei_message_display_q_[pSEIMessageInfo->picIdx] = *curr_sei_message_ptr_; + return 1; +} + + +int RocVideoDecoder::DecodeFrame(const uint8_t *data, size_t size, int pkt_flags, int64_t pts) { + int decoded_frame_cnt_ = 0, decoded_frame_cnt_ret_ = 0; + RocdecSourceDataPacket packet = { 0 }; + packet.payload = data; + packet.payload_size = size; + packet.flags = pkt_flags | ROCDEC_PKT_TIMESTAMP; + packet.pts = pts; + if (!data || size == 0) { + packet.flags |= ROCDEC_PKT_ENDOFSTREAM; + } + ROCDEC_API_CALL(rocDecParseVideoData(rocdec_parser_, &packet)); + + return decoded_frame_cnt_; +} + +uint8_t* RocVideoDecoder::GetFrame(int64_t *pts) { + if (decoded_frame_cnt_ > 0) { + std::lock_guard lock(mtx_vp_frame_); + decoded_frame_cnt_--; + if (pts) *pts = vp_frames_[decoded_frame_cnt_ret_].pts; + return vp_frames_[decoded_frame_cnt_ret_++].frame_ptr; + } + return nullptr; +} + +#if 0 // may be needed for future + +void RocVideoDecoder::SaveImage(std::string output_file_name, void *dev_mem, OutputImageInfo *image_info, bool is_output_RGB) { + uint8_t *hst_ptr = nullptr; + uint64_t output_image_size = image_info->output_image_size_in_bytes; + if (hst_ptr == nullptr) { + hst_ptr = new uint8_t [output_image_size]; + } + hipError_t hip_status = hipSuccess; + hip_status = hipMemcpyDtoH((void *)hst_ptr, dev_mem, output_image_size); + if (hip_status != hipSuccess) { + std::cout << "ERROR: hipMemcpyDtoH failed! (" << hip_status << ")" << std::endl; + delete [] hst_ptr; + return; + } + + // no RGB dump if the surface type is YUV400 + if (image_info->chroma_format == ROCDEC_FMT_YUV400 && is_output_RGB) { + return; + } + uint8_t *tmp_hst_ptr = hst_ptr; + if (fp_out_ == nullptr) { + fp_out_ = fopen(output_file_name.c_str(), "wb"); + } + if (fp_out_) { + int img_width = image_info->output_width; + int img_height = image_info->output_height; + int output_image_stride = image_info->output_h_stride; + if (img_width * image_info->bytes_per_pixel == output_image_stride && img_height == image_info->output_v_stride) { + fwrite(hst_ptr, 1, output_image_size, fp_out_); + } else { + uint32_t width = is_output_RGB ? image_info->output_width * 3 : image_info->output_width; + if (image_info->bit_depth == 8) { + for (int i = 0; i < image_info->output_height; i++) { + fwrite(tmp_hst_ptr, 1, width, fp_out_); + tmp_hst_ptr += output_image_stride; + } + if (!is_output_RGB) { + // dump chroma + uint8_t *uv_hst_ptr = hst_ptr + output_image_stride * image_info->output_v_stride; + for (int i = 0; i < img_height >> 1; i++) { + fwrite(uv_hst_ptr, 1, width, fp_out_); + uv_hst_ptr += output_image_stride; + } + } + } else if (image_info->bit_depth > 8 && image_info->bit_depth <= 16 ) { + for (int i = 0; i < img_height; i++) { + fwrite(tmp_hst_ptr, 1, width * image_info->bytes_per_pixel, fp_out_); + tmp_hst_ptr += output_image_stride; + } + if (!is_output_RGB) { + // dump chroma + uint8_t *uv_hst_ptr = hst_ptr + output_image_stride * image_info->output_v_stride; + for (int i = 0; i < img_height >> 1; i++) { + fwrite(uv_hst_ptr, 1, width * image_info->bytes_per_pixel, fp_out_); + uv_hst_ptr += output_image_stride; + } + } + } + } + } + + if (hst_ptr != nullptr) { + delete [] hst_ptr; + hst_ptr = nullptr; + tmp_hst_ptr = nullptr; + } +} +#endif + +void RocVideoDecoder::GetDeviceinfo(std::string &device_name, std::string &gcn_arch_name, int &pci_bus_id, int &pci_domain_id, int &pci_device_id) { + device_name = hip_dev_prop_.name; + gcn_arch_name = hip_dev_prop_.gcnArchName; + pci_bus_id = hip_dev_prop_.pciBusID; + pci_domain_id = hip_dev_prop_.pciDomainID; + pci_device_id = hip_dev_prop_.pciDeviceID; +} + + +bool RocVideoDecoder::GetOutputSurfaceInfo(OutputSurfaceInfo **surface_info) { + if (!width_ || !height_) { + std::cerr << "ERROR: RocVideoDecoderr is not intialized" << std::endl; + return false; + } + *surface_info = &output_surface_info_; + return true; +} + +bool RocVideoDecoder::InitHIP(int device_id) { + HIP_API_CALL(hipGetDeviceCount(&num_devices_)); + if (num_devices_ < 1) { + std::cerr << "ERROR: didn't find any GPU!" << std::endl; + return false; + } + if (device_id >= num_devices_) { + std::cerr << "ERROR: the requested device_id is not found! " << std::endl; + return false; + } + HIP_API_CALL(hipSetDevice(device_id)); + HIP_API_CALL(hipGetDeviceProperties(&hip_dev_prop_, device_id)); + HIP_API_CALL(hipStreamCreate(&hip_stream_)); + return true; +} diff --git a/projects/rocdecode/utils/rocvideodecode/roc_video_dec.h b/projects/rocdecode/utils/rocvideodecode/roc_video_dec.h new file mode 100644 index 0000000000..ca61f8dfa0 --- /dev/null +++ b/projects/rocdecode/utils/rocvideodecode/roc_video_dec.h @@ -0,0 +1,320 @@ +/* +Copyright (c) 2023 - 2023 Advanced Micro Devices, Inc. All rights reserved. + +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include "rocdecode.h" +#include "rocparser.h" +#include "commons.h" + +#define MAX_FRAME_NUM 16 + +typedef enum{ + SEI_TYPE_TIME_CODE = 136, + SEI_TYPE_USER_DATA_UNREGISTERED = 5 +}SEI_H264_HEVC_PAYLOAD_TYPE; + + +#define ROCDEC_API_CALL( rocDecAPI ) \ + do { \ + rocDecStatus errorCode = rocDecAPI; \ + if( errorCode != ROCDEC_SUCCESS) { \ + std::ostringstream errorLog; \ + errorLog << #rocDecAPI << " returned error " << errorCode; \ + THROW(errorLog.str() + TOSTR(errorCode)); \ + } \ + } while (0) + +#define HIP_API_CALL( call ) \ + do { \ + hipError_t hip_status = call; \ + if (hip_status != hipSuccess) { \ + const char *szErrName = NULL; \ + szErrName = hipGetErrorName(hip_status); \ + std::ostringstream errorLog; \ + errorLog << "hip API error " << szErrName ; \ + THROW(errorLog.str()); \ + } \ + } \ + while (0) + + +struct Rect { + int l, t, r, b; +}; + +struct Dim { + int w, h; +}; + +static inline int align(int value, int alignment) { + return (value + alignment - 1) & ~(alignment - 1); +} + +typedef struct DecFrameBuffer_ { + uint8_t *frame_ptr; /**< device memory pointer for the decoded frame */ + int64_t pts; // timestamp +} DecFrameBuffer; + + +typedef struct OutputSurfaceInfoType { + uint32_t output_width; /**< Output width of decoded surface*/ + uint32_t output_height; /**< Output height of decoded surface*/ + uint32_t output_pitch; /**< Output pitch in bytes of luma plane, chroma pitch can be inferred based on chromaFormat*/ + uint32_t bytes_per_pixel; /**< Output BytesPerPixel of decoded image*/ + uint32_t bit_depth; /**< Output BitDepth of the image*/ + uint32_t num_chroma_planes; /**< Output Chroma number of planes*/ + uint64_t output_surface_size_in_bytes; /**< Output Image Size in Bytes; including both luma and chroma planes*/ + rocDecVideoSurfaceFormat surface_format; /**< Chroma format of the decoded image*/ +} OutputSurfaceInfo; + +class RocVideoDecoder { + public: + /** + * @brief Construct a new Roc Video Decoder object + * + * @param hip_ctx + * @param b_use_device_mem + * @param codec + * @param device_id + * @param b_low_latency + * @param device_frame_pitched + * @param p_crop_rect + * @param p_resize_dim + * @param extract_user_SEI_Message + * @param max_width + * @param max_height + * @param clk_rate + * @param force_zero_latency + */ + RocVideoDecoder(int device_id, bool b_use_device_mem, rocDecVideoCodec codec, bool b_low_latency, bool device_frame_pitched, + const Rect *p_crop_rect, const Dim *p_resize_dim, bool extract_user_SEI_Message, int max_width, int max_height, + uint32_t clk_rate, bool force_zero_latency); + ~RocVideoDecoder(); + + /** + * @brief Get the output frame width + */ + uint32_t GetWidth() { assert(width_); return width_;} + + /** + * @brief This function is used to get the actual decode width + */ + int GetDecodeWidth() { assert(width_); return width_; } + + /** + * @brief Get the output frame height + */ + uint32_t GetHeight() { assert(height_); return height_; } + + /** + * @brief This function is used to get the current chroma height. + */ + int GetChromaHeight() { assert(chroma_height_); return chroma_height_; } + + /** + * @brief This function is used to get the number of chroma planes. + */ + int GetNumChromaPlanes() { assert(num_chroma_planes_); return num_chroma_planes_; } + + /** + * @brief This function is used to get the current frame size based on pixel format. + */ + int GetFrameSize() { assert(width_); return width_ * (height_ + (chroma_height_ * num_chroma_planes_)) * byte_per_pixel_; } + + /** + * @brief This function is used to get the current frame size based on pitch + */ + int GetFrameSizePitched() { assert(surface_stride_); return surface_stride_ * (height_ + (chroma_height_ * num_chroma_planes_)); } + + /** + * @brief Get the Bit Depth and BytesPerPixel associated with the pixel format + * + * @return uint32_t + */ + uint32_t GetBitDepth() { assert(bitdepth_minus_8_); return (bitdepth_minus_8_ + 8); } + uint32_t GetBytePerPixel() { assert(byte_per_pixel_); return byte_per_pixel_; } + /** + * @brief Functions to get the output surface attributes + */ + size_t GetSurfaceSize() { assert(surface_size_); return surface_size_; } + uint32_t GetSurfaceStride() { assert(surface_stride_); return surface_stride_; } + //RocDecImageFormat GetSubsampling() { return subsampling_; } + int GetSurfaceWidth() { assert(surface_width_); return surface_width_;} + int GetSurfaceHeight() { assert(surface_height_); return surface_height_;} + /** + * @brief Get the name of the output format + * + * @param codec_id + * @return std::string + */ + const char *GetCodecFmtName(rocDecVideoCodec codec_id); + /** + * @brief Get the pointer to the Output Image Info + * + * @param surface_info ptr to output surface info + * @return true + * @return false + */ + bool GetOutputSurfaceInfo(OutputSurfaceInfo **surface_info); + /** + * @brief this function decodes a frame and returns the number of frames avalable for display + * + * @param data - pointer to the data buffer that is to be decode + * @param size - size of the data buffer in bytes + * @param pts - presentation timestamp + * @param flags - video packet flags + * @return int - num of frames to display + */ + int DecodeFrame(const uint8_t *data, size_t size, int pkt_flags, int64_t pts = 0); + /** + * @brief This function returns a decoded frame and timestamp. This should be called in a loop fetching all the available frames + * + */ + uint8_t* GetFrame(int64_t *pts); + + /** + * @brief utility function to save image to a file + * + * @param output_file_name - file to write + * @param dev_mem - dev_memory pointer of the frame + * @param image_info - output image info + * @param is_output_RGB - to write in RGB + */ + //void SaveImage(std::string output_file_name, void* dev_mem, OutputImageInfo* image_info, bool is_output_RGB = 0); + + /** + * @brief Get the Device info for the current device + * + * @param device_name + * @param gcn_arch_name + * @param pci_bus_id + * @param pci_domain_id + * @param pci_device_id + */ + void GetDeviceinfo(std::string &device_name, std::string &gcn_arch_name, int &pci_bus_id, int &pci_domain_id, int &pci_device_id); + + private: + int decoder_session_id_; // Decoder session identifier. Used to gather session level stats. + /** + * @brief Callback function to be registered for getting a callback when decoding of sequence starts + */ + static int ROCDECAPI HandleVideoSequenceProc(void *pUserData, RocdecVideoFormat *pVideoFormat) { return ((RocVideoDecoder *)pUserData)->HandleVideoSequence(pVideoFormat); } + + /** + * @brief Callback function to be registered for getting a callback when a decoded frame is ready to be decoded + */ + static int ROCDECAPI HandlePictureDecodeProc(void *pUserData, RocdecPicParams *pPicParams) { return ((RocVideoDecoder *)pUserData)->HandlePictureDecode(pPicParams); } + + /** + * @brief Callback function to be registered for getting a callback when a decoded frame is available for display + */ + static int ROCDECAPI HandlePictureDisplayProc(void *pUserData, RocdecParserDispInfo *pDispInfo) { return ((RocVideoDecoder *)pUserData)->HandlePictureDisplay(pDispInfo); } + + /** + * @brief Callback function to be registered for getting a callback when all the unregistered user SEI Messages are parsed for a frame. + */ + static int ROCDECAPI HandleSEIMessagesProc(void *pUserData, RocdecSeiMessageInfo *pSEIMessageInfo) { return ((RocVideoDecoder *)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(RocdecVideoFormat *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(RocdecPicParams *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(RocdecParserDispInfo *pDispInfo); + /** + * @brief This function gets called when all unregistered user SEI messages are parsed for a frame + */ + int GetSEIMessage(RocdecSeiMessageInfo *pSEIMessageInfo); + + /** + * @brief This function reconfigure decoder if there is a change in sequence params. + */ + int ReconfigureDecoder(RocdecVideoFormat *pVideoFormat); + + /** + * @brief Function to Initialize GPU-HIP + * + */ + bool InitHIP(int device_id); + int num_devices_; + int device_id_; + RocdecVideoParser rocdec_parser_ = nullptr; + rocDecDecoderHandle roc_decoder_ = nullptr; + bool b_use_device_mem_ = true; + bool b_extract_sei_message_ = false; + bool b_low_latency_ = true; + bool b_force_zero_latency_ = true; + bool b_device_frame_pitched_ = true; + hipDeviceProp_t hip_dev_prop_; + hipStream_t hip_stream_; + rocDecVideoCodec codec_id_ = rocDecVideoCodec_NumCodecs; + rocDecVideoChromaFormat video_chroma_format_ = rocDecVideoChromaFormat_420; + rocDecVideoSurfaceFormat video_surface_format_ = rocDecVideoSurfaceFormat_NV12; + RocdecVideoFormat video_format_ = {}; + RocdecSeiMessageInfo *curr_sei_message_ptr_ = nullptr; + RocdecSeiMessageInfo sei_message_display_q_[MAX_FRAME_NUM]; + int decoded_frame_cnt_ = 0, decoded_frame_cnt_ret_ = 0; + int decode_poc_ = 0, pic_num_in_dec_order_[MAX_FRAME_NUM]; + int num_alloced_frames_ = 0; + std::ostringstream input_video_info_str_; + int bitdepth_minus_8_ = 0; + uint32_t byte_per_pixel_ = 1; + uint32_t width_; + uint32_t height_; + int max_width_, max_height_; + uint32_t chroma_height_; + uint32_t surface_height_; + uint32_t surface_width_; + uint32_t num_chroma_planes_; + uint32_t num_components_; + uint32_t surface_stride_; + size_t surface_size_; + OutputSurfaceInfo output_surface_info_; + std::mutex mtx_vp_frame_; + std::vector vp_frames_; // vector of decoded frames + Rect disp_rect_ = {}; + Rect crop_rect_ = {}; + Dim resize_dim_ = {}; + FILE *fp_sei_ = NULL; + FILE *fp_out_ = NULL; +}; \ No newline at end of file