Rr/roc video decode class (#20)

* WIP: class implementation

* add more definitions

* rocvideodecode implementation

* formatting fixes

* address review comments

[ROCm/rocdecode commit: d0e6743872]
Este commit está contenido en:
Rajy Rawther
2023-10-23 09:16:45 -07:00
cometido por GitHub
padre 9b158f2b07
commit db6dbf7076
Se han modificado 18 ficheros con 1144 adiciones y 44 borrados
+1 -1
Ver fichero
@@ -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}")
+9 -9
Ver fichero
@@ -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);
/*****************************************************************************************************/
+17 -2
Ver fichero
@@ -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;
+1
Ver fichero
@@ -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
@@ -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!")
+11 -1
Ver fichero
@@ -49,4 +49,14 @@ rocDecStatus H264VideoParser::Initialize(RocdecParserParams *pParams) {
*/
rocDecStatus H264VideoParser::ParseVideoData(RocdecSourceDataPacket *pData) {
return ROCDEC_NOT_IMPLEMENTED;
}
}
/**
* @brief function to uninitialize h264 parser
*
* @return rocDecStatus
*/
rocDecStatus H264VideoParser::UnInitialize() {
//todo::
return ROCDEC_NOT_IMPLEMENTED;
}
@@ -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:
@@ -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) {
+10 -2
Ver fichero
@@ -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
@@ -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<RocVideoParser> 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;
};
@@ -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;
@@ -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<RocParserHandle *> (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;
}
@@ -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
+1 -2
Ver fichero
@@ -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:
@@ -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<DecHandle *> (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<DecHandle *> (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<DecHandle *> (hDecoder);
rocDecStatus ret;
try {
@@ -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 <stdexcept>
#include <exception>
#include <string>
#include <iostream>
#define TOSTR(X) std::to_string(static_cast<int>(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);
@@ -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<std::mutex> 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<std::mutex> 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;
}
@@ -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 <assert.h>
#include <stdint.h>
#include <mutex>
#include <vector>
#include <string>
#include <iostream>
#include <sstream>
#include <string.h>
#include <hip/hip_runtime.h>
#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<DecFrameBuffer> vp_frames_; // vector of decoded frames
Rect disp_rect_ = {};
Rect crop_rect_ = {};
Dim resize_dim_ = {};
FILE *fp_sei_ = NULL;
FILE *fp_out_ = NULL;
};