FFMpeg decoder rocdecode integration (#583)

* initial commit

* initial implementation for host based decoder

* minor change

* cmake changes and added new sample

* rocdecDecode sample implementation

* rocdecode sample changes working

* working version of avcodec decoder and sample

* Add end of stream handling for repeated decoding with reconfigure

* reorg files and added changelog

* update readme

* revert file

* remove unused class members

* addressed reviw comment for changelog

* fix sample to work on more video files

* resolved review comments

* bumped version to 0.14.0

* fixed build warnings

* addressed review comments

* addressed review comments

* addressed review comments

* fixed readme to match .cpp file options for parameters

* updated review comments, readme, and added test data for the sample

* fixed bug for saving frame for 10 bit videos

* addressed review comments

* addressed all the new review comments
Этот коммит содержится в:
Rajy Rawther
2025-06-07 10:12:57 -07:00
коммит произвёл GitHub
родитель 0f89c9c17c
Коммит 3ddb12f075
68 изменённых файлов: 2194 добавлений и 5 удалений
+3 -1
Просмотреть файл
@@ -2,7 +2,7 @@
Full documentation for rocDecode is available at [https://rocm.docs.amd.com/projects/rocDecode/en/latest/](https://rocm.docs.amd.com/projects/rocDecode/en/latest/)
## rocDecode 0.13.2 (unreleased)
## rocDecode 0.14.0 (unreleased)
### Added
@@ -12,6 +12,8 @@ Full documentation for rocDecode is available at [https://rocm.docs.amd.com/proj
* HEVC stream bit depth change handling and DPB buffer size change handling through decoder reconfiguration.
* AVC stream DPB buffer size change handling through decoder reconfiguration.
* rocDecode now uses the Cmake CMAKE_PREFIX_PATH directive.
* rocDecode - A new avcodec-based decoder built as a separate "rocdecode-host" library
### Optimized
+6 -2
Просмотреть файл
@@ -40,7 +40,7 @@ if (NOT DEFINED CMAKE_CXX_COMPILER)
endif()
# rocDecode Version
set(VERSION "0.13.2")
set(VERSION "0.14.0")
# Set Project Version and Language
project(rocdecode VERSION ${VERSION} LANGUAGES CXX)
@@ -135,6 +135,7 @@ if(HIP_FOUND AND Libva_FOUND AND Libdrm_amdgpu_FOUND)
include_directories(api src/rocdecode src/parser src/rocdecode/vaapi)
# source files
file(GLOB_RECURSE SOURCES "./src/*.cpp")
list(FILTER SOURCES EXCLUDE REGEX "./src/rocdecode-host/*.*")
# rocdecode.so
add_library(${PROJECT_NAME} SHARED ${SOURCES})
@@ -168,7 +169,7 @@ if(HIP_FOUND AND Libva_FOUND AND Libdrm_amdgpu_FOUND)
# install rocDecode include files -- {ROCM_PATH}/include/rocdecode
install(FILES api/rocdecode/rocdecode.h api/rocdecode/rocparser.h api/rocdecode/roc_bitstream_reader.h
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME} COMPONENT dev)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/rocdecode_version.h
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/rocdecode_version.h api/rocdecode/rocdecode_host.h
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME} COMPONENT dev)
# install rocDecode api trace include file -- {ROCM_PATH}/include/rocdecode/amd_detail
install(FILES api/amd_detail/rocdecode_api_trace.h
@@ -253,6 +254,9 @@ if(HIP_FOUND AND Libva_FOUND AND Libdrm_amdgpu_FOUND)
include(CTest)
add_subdirectory(test)
#make rocdecode-host for decoding on HOST
add_subdirectory(src/rocdecode-host)
# set package information
set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR})
+190
Просмотреть файл
@@ -0,0 +1,190 @@
/*
Copyright (c) 2023 - 2025 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.
*/
#ifndef ROCDECAPI
#if defined(_WIN32)
#define ROCDECAPI __stdcall // for future: only linux is supported in this version
#else
#define ROCDECAPI
#endif
#endif
#pragma once
#include "hip/hip_runtime.h"
#include "rocdecode.h"
#include "rocparser.h"
/*!
* \file
* \brief The AMD rocDecode Library.
*
* \defgroup group_amd_rocdecode rocDecode: AMD ROCm Software Decode API
* \brief The rocDecodeHost is a part of rocDecode toolkit to decode videos and images using a avcodec based video decoder on ROCm.
*/
#if defined(__cplusplus)
extern "C" {
#endif // __cplusplus
/****************************************************************/
//! \ingroup group_rocdec_struct
//! \struct RocdecVideoFormatHost
//! Video format including raw sequence header information
//! Used in rocDecCreateVideoParser API
/****************************************************************/
typedef struct {
RocdecVideoFormat video_format; /**< OUT: RocdecVideoFormat structure */
rocDecVideoSurfaceFormat video_surface_format; /**< OUT: output surface format */
uint32_t reserved[16]; // reserved for future
} RocdecVideoFormatHost;
typedef int(ROCDECAPI *PFNVIDSEQUENCECHOSTALLBACK)(void *, RocdecVideoFormatHost *);
typedef int(ROCDECAPI *PFNVIDDISPLAYHOSTCALLBACK)(void *, void *);
/******************************************************************************************/
//! \struct _RocdecPicParamsHost
//! \ingroup group_amd_rocdecode
//! Picture parameters for decoding
//! This structure is used in rocDecDecodePictureHost API
//! IN for rocDecDecodePictureHost
/******************************************************************************************/
typedef struct _RocdecPicParamsHost {
// Bitstream data
uint32_t bitstream_data_len; /**< IN: Number of bytes in bitstream data buffer */
const uint8_t *bitstream_data; /**< IN: Ptr to bitstream data for this picture (slice-layer) */
uint32_t flags; /**< IN: Combination of ROCDEC_PKT_XXX flags */
RocdecTimeStamp pts; /**< IN: Presentation time stamp (10MHz clock), only valid if ROCDEC_PKT_TIMESTAMP flag is set */
} RocdecPicParamsHost;
/**************************************************************************************************************/
//! \struct RocDecoderHostCreateInfo
//! \ingroup group_amd_rocdecode
//! This structure is used in rocDecCreateDecoderHost API
/**************************************************************************************************************/
typedef struct _RocDecoderHostCreateInfo {
uint32_t width; /**< IN: Coded sequence width in pixels */
uint32_t height; /**< IN: Coded sequence height in pixels */
uint32_t num_decode_threads; /**< IN: Maximum number of internal decode threads for multi-threading <default value 0: threading will be chosen as appropriate to get the max performace> */
rocDecVideoCodec codec_type; /**< IN: rocDecVideoCodec_XXX */
rocDecVideoChromaFormat chroma_format; /**< IN: rocDecVideoChromaFormat_XXX */
uint32_t bit_depth_minus_8; /**< IN: The value "BitDepth minus 8" */
uint32_t intra_decode_only; /**< IN: not used for avcodec based decoding (default value is 0). */
uint32_t max_width; /**< IN: Coded sequence max width in pixels used with reconfigure Decoder */
uint32_t max_height; /**< IN: Coded sequence max height in pixels used with reconfigure Decoder */
struct {
int16_t left;
int16_t top;
int16_t right;
int16_t bottom;
} display_rect; /**< IN: area of the frame that should be displayed */
rocDecVideoSurfaceFormat output_format; /**< IN: rocDecVideoSurfaceFormat_XXX */
uint32_t target_width; /**< IN: Post-processed output width (Should be aligned to 2) */
uint32_t target_height; /**< IN: Post-processed output height (Should be aligned to 2) */
uint32_t num_output_surfaces; /**< IN: Maximum number of output surfaces simultaneously mapped */
struct {
int16_t left;
int16_t top;
int16_t right;
int16_t bottom;
} target_rect; /**< IN: (for future use) target rectangle in the output frame (for aspect ratio conversion)
if a null rectangle is specified, {0,0,target_width,target_height} will be used*/
void *user_data; /**< IN: User data for callbacks */
// callback functions to enable users to consume decoded data
PFNVIDSEQUENCECHOSTALLBACK pfn_sequence_callback; /**< IN: Called before decoding frames and/or whenever there is a fmt change */
PFNVIDDISPLAYCALLBACK pfn_display_picture; /**< IN: Called whenever a picture is ready to be displayed (display order) */
PFNVIDSEIMSGCALLBACK pfn_get_sei_msg; /**< IN: Called when all SEI messages are parsed for particular frame */
uint32_t reserved[4]; /**< Reserved for future use - set to zero */
} RocDecoderHostCreateInfo;
/*****************************************************************************************************/
//! \fn rocDecStatus ROCDECAPI rocDecCreateDecoderHost(rocDecDecoderHandle *decoder_handle, RocDecoderHostCreateInfo *decoder_create_info)
//! \ingroup group_amd_rocdecode
//! Create the decoder object based on decoder_create_info. A handle to the created decoder is returned
/*****************************************************************************************************/
extern rocDecStatus ROCDECAPI rocDecCreateDecoderHost(rocDecDecoderHandle *decoder_handle, RocDecoderHostCreateInfo *decoder_create_info);
/*****************************************************************************************************/
//! \fn rocDecStatus ROCDECAPI rocDecDestroyDecoderHost(rocDecDecoderHandle decoder_handle)
//! \ingroup group_amd_rocdecode
//! Destroy the decoder object
/*****************************************************************************************************/
extern rocDecStatus ROCDECAPI rocDecDestroyDecoderHost(rocDecDecoderHandle decoder_handle);
/**********************************************************************************************************************/
//! \fn rocDecStatus ROCDECAPI rocDecGetDecoderCapsHost(RocdecDecodeCaps *decode_caps)
//! \ingroup group_amd_rocdecode
//! Queries decode capabilities of host based decoder based on codec type, chroma_format and BitDepthMinus8 parameters.
//! 1. Application fills IN parameters codec_type, chroma_format and BitDepthMinus8 of RocdecDecodeCaps structure
//! 2. For FFMpeg avcodec based decoder, this call returns success
/**********************************************************************************************************************/
extern rocDecStatus ROCDECAPI rocDecGetDecoderCapsHost(RocdecDecodeCaps *decode_caps);
/*****************************************************************************************************/
//! \fn rocDecStatus ROCDECAPI rocDecDecodeFrameHost(rocDecDecoderHandle decoder_handle, RocdecPicParamsHost *pic_params)
//! \ingroup group_amd_rocdecode
//! Decodes a single picture
//! Submits the frame for host based decoding
/*****************************************************************************************************/
extern rocDecStatus ROCDECAPI rocDecDecodeFrameHost(rocDecDecoderHandle decoder_handle, RocdecPicParamsHost *pic_params);
/************************************************************************************************************/
//! \fn rocDecStatus ROCDECAPI rocDecGetDecodeStatusHost(rocDecDecoderHandle decoder_handle, int pic_idx, RocdecDecodeStatus* decode_status);
//! \ingroup group_amd_rocdecode
//! Get the decode status for frame corresponding to nPicIdx
//! API is currently supported for HEVC, AVC/H264 and JPEG codecs.
//! API returns ROCDEC_NOT_SUPPORTED error code for unsupported codec.
/************************************************************************************************************/
extern rocDecStatus ROCDECAPI rocDecGetDecodeStatusHost(rocDecDecoderHandle decoder_handle, int pic_idx, RocdecDecodeStatus *decode_status);
/*********************************************************************************************************/
//! \fn rocDecStatus ROCDECAPI rocDecReconfigureDecoderHost(rocDecDecoderHandle decoder_handle, RocdecReconfigureDecoderInfo *reconfig_params)
//! \ingroup group_amd_rocdecode
//! Used to reuse single decoder for multiple clips. Currently supports resolution change, resize params
//! params, target area params change for same codec. Must be called during RocdecParserParams::pfn_sequence_callback
/*********************************************************************************************************/
extern rocDecStatus ROCDECAPI rocDecReconfigureDecoderHost(rocDecDecoderHandle decoder_handle, RocdecReconfigureDecoderInfo *reconfig_params);
/************************************************************************************************************************/
//! \fn extern rocDecStatus ROCDECAPI rocDecGetVideoFrameHost(rocDecDecoderHandle decoder_handle, int pic_idx,
//! uint32_t *frame_data, uint32_t *line_size,
//! RocdecProcParams *vid_postproc_params);
//! \ingroup group_amd_rocdecode
//! Post-process and map video frame corresponding to pic_idx for use in HIP. Returns HIP device pointer and associated
//! line_size of the video frame. Returns host memory pointers and pitch for each plane (Y, U and V) seperately
//! line_size is a pointer to an unsigned 32-bit integer array of size 3.
/************************************************************************************************************************/
extern rocDecStatus ROCDECAPI rocDecGetVideoFrameHost(rocDecDecoderHandle decoder_handle, int pic_idx,
void **frame_data, uint32_t *line_size,
RocdecProcParams *vid_postproc_params);
/*****************************************************************************************************/
//! \fn const char* ROCDECAPI rocDecGetErrorNameHost(rocDecStatus rocdec_status)
//! \ingroup group_amd_rocdecode
//! Return name of the specified error code in text form.
/*****************************************************************************************************/
extern const char *ROCDECAPI rocDecGetErrorNameHost(rocDecStatus rocdec_status);
#ifdef __cplusplus
}
#endif
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
+101
Просмотреть файл
@@ -0,0 +1,101 @@
################################################################################
# Copyright (c) 2023 - 2025 Advanced Micro Devices, Inc.
#
# 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.
#
################################################################################
cmake_minimum_required (VERSION 3.10)
project(rocdecdecode)
# ROCM Path
if(DEFINED ENV{ROCM_PATH})
set(ROCM_PATH $ENV{ROCM_PATH} CACHE PATH "${White}${PROJECT_NAME}: Default ROCm installation path${ColourReset}")
elseif(ROCM_PATH)
message("-- ${White}${PROJECT_NAME} :ROCM_PATH Set -- ${ROCM_PATH}${ColourReset}")
else()
set(ROCM_PATH /opt/rocm CACHE PATH "${White}${PROJECT_NAME}: Default ROCm installation path${ColourReset}")
endif()
# Set AMD Clang as default compiler
if (NOT DEFINED CMAKE_CXX_COMPILER)
set(CMAKE_C_COMPILER ${ROCM_PATH}/bin/amdclang)
set(CMAKE_CXX_COMPILER ${ROCM_PATH}/bin/amdclang++)
endif()
set(CMAKE_CXX_STANDARD 17)
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/../../cmake)
list(APPEND CMAKE_PREFIX_PATH ${ROCM_PATH}/lib/cmake)
# rocDecode sample build type
set(DEFAULT_BUILD_TYPE "Release")
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "${DEFAULT_BUILD_TYPE}" CACHE STRING "rocDecode Default Build Type" FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release")
endif()
if(CMAKE_BUILD_TYPE MATCHES Debug)
# -O0 -- Don't Optimize output file
# -gdwarf-4 -- generate debugging information, dwarf-4 for making valgrind work
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -gdwarf-4 -Wall")
else()
# -O3 -- Optimize output file
# -DNDEBUG -- turn off asserts
# -fPIC -- Generate position-independent code if possible
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -DNDEBUG -fPIC -Wall")
endif()
find_package(HIP QUIET)
find_package(FFmpeg QUIET)
find_package(ROCDECODE QUIET)
if(HIP_FOUND AND FFMPEG_FOUND AND ROCDECODE_FOUND)
# HIP
find_library(ROCDECODE_HOST_LIBRARY rocdecodehost PATHS ${ROCM_PATH}/lib)
set(LINK_LIBRARY_LIST ${LINK_LIBRARY_LIST} hip::host)
# FFMPEG
include_directories(${AVUTIL_INCLUDE_DIR} ${AVCODEC_INCLUDE_DIR}
${AVFORMAT_INCLUDE_DIR})
set(LINK_LIBRARY_LIST ${LINK_LIBRARY_LIST} ${FFMPEG_LIBRARIES})
# rocDecode and utils
include_directories (${ROCDECODE_INCLUDE_DIR} ${ROCM_PATH}/lib)
if(ROCDECODE_HOST_LIBRARY)
set(LINK_LIBRARY_LIST ${LINK_LIBRARY_LIST} ${ROCDECODE_LIBRARY} ${ROCDECODE_HOST_LIBRARY})
else()
message(FATAL_ERROR "Library ROCDECODE_HOST_LIBRARY not found!")
endif()
# rocDecode
# sample app exe
list(APPEND SOURCES ${PROJECT_SOURCE_DIR} rocdecdecode.cpp)
add_executable(${PROJECT_NAME} ${SOURCES})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++17")
target_link_libraries(${PROJECT_NAME} ${LINK_LIBRARY_LIST})
else()
message("-- ERROR!: ${PROJECT_NAME} excluded! please install all the dependencies and try again!")
if (NOT HIP_FOUND)
message(FATAL_ERROR "-- ERROR!: HIP Not Found! - please install ROCm and HIP!")
endif()
if (NOT FFMPEG_FOUND)
message(FATAL_ERROR "-- ERROR!: FFMPEG Not Found! - please install FFMPEG!")
endif()
if (NOT ROCDECODE_FOUND)
message(FATAL_ERROR "-- ERROR!: rocDecode Not Found! - please install rocDecode!")
endif()
endif()
+44
Просмотреть файл
@@ -0,0 +1,44 @@
# rocdecDecode sample
The rocdec decode sample illustrates decoding of individual frames of video elementary stream data using the rocDecoder and rocDecodeHost low level api to get the individual decoded frames in YUV format. This sample can be configured with a device ID and optionally able to dump the output to a file. This sample directly uses low-level Rocdecoder/RocDecoderHost api. This sample only works with raw elementary video frame files, and not with packetized data. Typical input to this sample is a folder containing extracted individual video frames of one or more video files. The files containing individual frames has to be numbered in ascending order of frames, otherwise the output will be corrupted since the parser assumes random order for those files which can result in corrupted reference frames.
### Note: If the input is a packetized file like ".mp4", the sample will treat it as a single video frame and output will not be correct.
## Prerequisites:
* Install [rocDecode](../../README.md#build-and-install-instructions)
* [FFMPEG](https://ffmpeg.org/about.html) for rocDecodeHost
* On `Ubuntu`
```shell
sudo apt install libavcodec-dev libavformat-dev libavutil-dev
```
* On `RHEL`/`SLES` - install ffmpeg development packages manually or use [rocDecode-setup.py](../../rocDecode-setup.py) script
## Build
```shell
mkdir rocdec_decode_sample && cd rocdec_decode_sample
cmake ../
make -j
```
## Run
```shell
./rocdecdecode -i <input video frame file or folder containing multiple frames [required]> -b <backend> -o <outfile>
-o <output path to save decoded YUV frames [optional]>
-d <GPU device ID - 0:device 0 / 1:device 1/ ... [optional - default:0]>
-b <backend for the decoder - 0:device 1:host [optional - default:0]>
-c <codec - 0 : HEVC, 1 : H264, 2: AV1, 4: VP9, 5: VP8, 6: MJPEG [optional; default: 0]>
-n <Number of iteration - specify the number of iterations for performance evaluation [optional; default: 1]>
-m <output_surface_memory_type - decoded surface memory [optional - default: 0][0 : OUT_SURFACE_MEM_DEV_INTERNAL/ 1 : OUT_SURFACE_MEM_DEV_COPIED/ 2 : OUT_SURFACE_MEM_HOST_COPIED/3 : OUT_SURFACE_MEM_NOT_MAPPED]>
```
```shell
"./rocdecdecode -i ROCDECODE_DATA_FOLDER/frames -o <output.yuv> -b 0".
```
+752
Просмотреть файл
@@ -0,0 +1,752 @@
/*
Copyright (c) 2023 - 2025 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 <chrono>
#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <filesystem>
#include <hip/hip_runtime.h>
#include <rocdecode/rocdecode.h>
#include <rocdecode/rocdecode_host.h>
namespace fs = std::filesystem;
static inline int align(int value, int alignment) {
return (value + alignment - 1) & ~(alignment - 1);
}
__attribute__((visibility("hidden"))) inline bool is_error(rocDecStatus status)
{
return status != ROCDEC_SUCCESS;
}
__attribute__((visibility("hidden"))) inline const char* error_string(rocDecStatus status)
{
return rocDecGetErrorName(status);
}
struct Rect {
int left;
int top;
int right;
int bottom;
};
template <typename Status, typename... Args>
__attribute__((visibility("hidden"))) inline void report_error(
Status status, const char* function_name, const char* file_name, int line, Args&&... args)
{
((std::cerr << "ERROR: " << error_string(status) << "; " << function_name << "; "
<< file_name << ":" << line)
<< ... << std::forward<Args>(args))
<< std::endl;
std::abort();
}
//hardcoding for this sample
#define DEFAULT_WIDTH 2912
#define DEFAULT_HEIGHT 1888
// helper functions for saving output to file
static inline float GetChromaHeightFactor(rocDecVideoSurfaceFormat surface_format) {
float factor = 0.5;
switch (surface_format) {
case rocDecVideoSurfaceFormat_NV12:
case rocDecVideoSurfaceFormat_P016:
case rocDecVideoSurfaceFormat_YUV420:
case rocDecVideoSurfaceFormat_YUV420_16Bit:
factor = 0.5;
break;
case rocDecVideoSurfaceFormat_YUV422:
case rocDecVideoSurfaceFormat_YUV422_16Bit:
case rocDecVideoSurfaceFormat_YUV444:
case rocDecVideoSurfaceFormat_YUV444_16Bit:
factor = 1.0;
break;
}
return factor;
};
static inline rocDecVideoCodec CodecTypeToRocDecVideoCodec(int codec_type) {
switch (codec_type) {
case 0: return rocDecVideoCodec_HEVC;
case 1: return rocDecVideoCodec_AVC;
case 2: return rocDecVideoCodec_AV1;
case 3: return rocDecVideoCodec_VP9;
case 4: return rocDecVideoCodec_VP8;
case 5: return rocDecVideoCodec_JPEG;
default: return rocDecVideoCodec_NumCodecs;
}
}
static inline float GetChromaWidthFactor(rocDecVideoSurfaceFormat surface_format) {
float factor = 0.5;
switch (surface_format) {
case rocDecVideoSurfaceFormat_NV12:
case rocDecVideoSurfaceFormat_P016:
case rocDecVideoSurfaceFormat_YUV444:
case rocDecVideoSurfaceFormat_YUV444_16Bit:
factor = 1.0;
break;
case rocDecVideoSurfaceFormat_YUV420:
case rocDecVideoSurfaceFormat_YUV420_16Bit:
case rocDecVideoSurfaceFormat_YUV422:
case rocDecVideoSurfaceFormat_YUV422_16Bit:
factor = 0.5;
break;
}
return factor;
};
// only 2 types of memory mode is supported in this sample for simplicity.
typedef enum OutputSurfaceMemoryType_enum {
OUT_SURFACE_MEM_DEV_INTERNAL = 0, /**< Internal interopped decoded surface memory(original mapped decoded surface) */
OUT_SURFACE_MEM_HOST = 2, /**< decoded output will be in host memory (true for host based decoding) **/
} OutputSurfaceMemoryType;
// Enum for decoder backend
typedef enum DecoderBackend_enum {
DECODER_BACKEND_DEVICE = 0, /**< Decoding using VCN hardware in the device specified by user */
DECODER_BACKEND_HOST = 1, /**< decoded using host and ffmpeg avcodec **/
} DecoderBackend;
#define CHECK(callable, ...) \
do \
{ \
auto status__ = callable; /* invoke the callable and assign the return status */ \
if (is_error(status__)) \
{ \
report_error(status__, __FUNCTION__, __FILE__, __LINE__, ##__VA_ARGS__); \
} \
} while (false)
/**
* @brief Struct containing all the information for decoding and displaying output
*
*/
struct DecoderInfo {
int dec_device_id;
DecoderBackend backend; //0: device, 1: host
rocDecDecoderHandle decoder;
RocdecVideoParser parser;
std::uint32_t bit_depth;
rocDecVideoCodec rocdec_codec_id;
int dump_decoded_frames;
std::string output_file_path;
OutputSurfaceMemoryType mem_type;
rocDecVideoSurfaceFormat surf_format;
rocDecVideoSurfaceFormat video_chroma_format;
uint32_t coded_width, coded_height;
uint32_t bytes_per_pixel;
bool is_decoder_reconfigured;
Rect disp_rect;
FILE *fp_out;
DecoderInfo() : dec_device_id(0), backend(DECODER_BACKEND_DEVICE), decoder(nullptr), bit_depth(8), dump_decoded_frames(0), mem_type{OUT_SURFACE_MEM_DEV_INTERNAL},
surf_format{rocDecVideoSurfaceFormat_NV12}, video_chroma_format{rocDecVideoSurfaceFormat_NV12},
is_decoder_reconfigured{false}, fp_out{nullptr} {}
};
/**
* @brief Funtion to save internal frame buffer to file for device buffer : chroma format is assumed to be NV12 for internal device memory
*
* @param p_dec_info
* @param surf_mem device mem pointers of luma and chroma planes
* @param pitch stride in bytes of luma and chroma planes
*/
void save_frame_to_file(DecoderInfo *p_dec_info, void *surf_mem[], uint32_t *pitch) {
uint8_t *hst_ptr = nullptr;
uint64_t output_image_size_luma = pitch[0] * p_dec_info->coded_height;
uint64_t output_image_size_chroma = pitch[1] * ((p_dec_info->coded_height * GetChromaHeightFactor(p_dec_info->surf_format)));
if (p_dec_info->mem_type == OUT_SURFACE_MEM_DEV_INTERNAL) {
if (hst_ptr == nullptr) {
hst_ptr = new uint8_t [output_image_size_luma + output_image_size_chroma];
}
hipError_t hip_status = hipSuccess;
// copy luma
hip_status = hipMemcpyDtoH((void *)hst_ptr, surf_mem[0], output_image_size_luma);
if (hip_status != hipSuccess) {
std::cerr << "ERROR: hipMemcpyDtoH failed for luma! (" << hipGetErrorName(hip_status) << ")" << std::endl;
delete [] hst_ptr;
return;
}
hip_status = hipMemcpyDtoH((void *)(hst_ptr + output_image_size_luma), surf_mem[1], output_image_size_chroma);
if (hip_status != hipSuccess) {
std::cerr << "ERROR: hipMemcpyDtoH failed for chroma! (" << hipGetErrorName(hip_status) << ")" << std::endl;
delete [] hst_ptr;
return;
}
} else
hst_ptr = static_cast<uint8_t *> (surf_mem[0]);
if (p_dec_info->is_decoder_reconfigured) {
if (p_dec_info->fp_out) {
fclose(p_dec_info->fp_out);
p_dec_info->fp_out = nullptr;
}
p_dec_info->is_decoder_reconfigured = false;
}
if (p_dec_info->fp_out == nullptr && !p_dec_info->output_file_path.empty()) {
p_dec_info->fp_out = fopen(p_dec_info->output_file_path.c_str(), "wb");
}
if (p_dec_info->fp_out) {
uint8_t *tmp_hst_ptr = hst_ptr;
if (p_dec_info->mem_type == OUT_SURFACE_MEM_DEV_INTERNAL) {
tmp_hst_ptr += (p_dec_info->disp_rect.top * pitch[0]) + (p_dec_info->disp_rect.left * p_dec_info->bytes_per_pixel);
}
int img_width = p_dec_info->disp_rect.right - p_dec_info->disp_rect.left;
int img_height = p_dec_info->disp_rect.bottom - p_dec_info->disp_rect.top;
uint32_t output_stride = pitch[0];
if ((img_width * p_dec_info->bytes_per_pixel) == output_stride) {
fwrite(tmp_hst_ptr, 1, output_image_size_luma, p_dec_info->fp_out);
tmp_hst_ptr += output_image_size_luma;
fwrite(tmp_hst_ptr, 1, output_image_size_chroma, p_dec_info->fp_out);
} else {
uint32_t width = img_width * p_dec_info->bytes_per_pixel;
if (p_dec_info->bit_depth <= 16) {
for (int i = 0; i < img_height; i++) {
fwrite(tmp_hst_ptr, 1, width, p_dec_info->fp_out);
tmp_hst_ptr += output_stride;
}
// dump chroma
uint8_t *uv_hst_ptr = hst_ptr + output_image_size_luma;
uint32_t chroma_height = static_cast<int>(GetChromaHeightFactor(p_dec_info->surf_format) * img_height);
if (p_dec_info->mem_type == OUT_SURFACE_MEM_DEV_INTERNAL) {
uv_hst_ptr += ((p_dec_info->disp_rect.top >> 1) * output_stride) + (p_dec_info->disp_rect.left * p_dec_info->bytes_per_pixel);
}
for (uint32_t i = 0; i < chroma_height; i++) {
fwrite(uv_hst_ptr, 1, width, p_dec_info->fp_out);
uv_hst_ptr += pitch[1];
}
}
}
}
if (hst_ptr != nullptr) {
delete [] hst_ptr;
}
}
/**
* @brief Funtion to save internal frame buffer to file for host buffer
*
* @param p_dec_info
* @param frame_mem
* @param pitch
*/
void save_frame_to_file_host(DecoderInfo *p_dec_info, void *frame_mem[], uint32_t *pitch) {
if (p_dec_info->is_decoder_reconfigured) {
if (p_dec_info->fp_out) {
fclose(p_dec_info->fp_out);
p_dec_info->fp_out = nullptr;
}
p_dec_info->is_decoder_reconfigured = false;
}
if (p_dec_info->fp_out == nullptr && !p_dec_info->output_file_path.empty()) {
p_dec_info->fp_out = fopen(p_dec_info->output_file_path.c_str(), "wb");
}
if (p_dec_info->fp_out) {
uint8_t *p_src_ptr_y = static_cast<uint8_t *>(frame_mem[0]) + (p_dec_info->disp_rect.top * pitch[0] + p_dec_info->disp_rect.left * p_dec_info->bytes_per_pixel);
if (!p_src_ptr_y) {
std::cerr << "save_frame_to_file_host: Invalid Memory address for src/dst" << std::endl;
return;
}
int img_width = p_dec_info->disp_rect.right - p_dec_info->disp_rect.left;
int img_height = p_dec_info->disp_rect.bottom - p_dec_info->disp_rect.top;
int output_stride = pitch[0];
uint32_t width = img_width * p_dec_info->bytes_per_pixel;
if (p_dec_info->bit_depth <= 16) {
for (int i = 0; i < img_height; i++) {
fwrite(p_src_ptr_y, 1, width, p_dec_info->fp_out);
p_src_ptr_y += output_stride;
}
// dump chroma
uint8_t *p_src_ptr_uv = static_cast<uint8_t *>(frame_mem[1]) + ((p_dec_info->disp_rect.top >> 1) * pitch[1] + (p_dec_info->disp_rect.left >> 1) * p_dec_info->bytes_per_pixel);
int32_t chroma_height = static_cast<int>(GetChromaHeightFactor(p_dec_info->surf_format) * img_height);
int32_t chroma_width = static_cast<int>(GetChromaWidthFactor(p_dec_info->surf_format) * img_width) * p_dec_info->bytes_per_pixel;
for (int32_t i = 0; i < chroma_height; i++) {
fwrite(p_src_ptr_uv, 1, chroma_width, p_dec_info->fp_out);
p_src_ptr_uv += pitch[1];
}
if (frame_mem[2] != nullptr) {
uint8_t *p_src_ptr_v = static_cast<uint8_t *>(frame_mem[2]) + p_dec_info->disp_rect.top * pitch[2] + (p_dec_info->disp_rect.left >> 1) * p_dec_info->bytes_per_pixel;
for (int32_t i = 0; i < chroma_height; i++) {
fwrite(p_src_ptr_v, 1, chroma_width, p_dec_info->fp_out);
p_src_ptr_v += pitch[2];
}
}
}
}
}
std::vector<std::vector<uint8_t>> read_frames(std::vector<std::string>& names) {
std::vector<std::vector<uint8_t>> frames;
// sort the frames file so it is consecutive
for (std::string name : names) {
std::ifstream inputFile(name.c_str(), std::ios::binary);
if (!inputFile) {
std::cerr << "Error opening " << name << " for reading." << std::endl;
std::abort();
}
std::cout << "Reading " << name << " for reading." << std::endl;
// Determine the file size
inputFile.seekg(0, std::ios::end);
std::streamsize fileSize = inputFile.tellg();
inputFile.seekg(0, std::ios::beg);
// Read the file contents into a byte array
std::vector<uint8_t> frame(fileSize);
if (!inputFile.read(reinterpret_cast<char*>(frame.data()), fileSize)) {
std::cerr << "Error reading " << name << "." << std::endl;
std::abort();
}
// Close the file
inputFile.close();
frames.push_back(std::move(frame));
}
return frames;
}
void init() {}
void create_decoder(DecoderInfo& dec_info) {
RocDecoderCreateInfo create_info = {};
create_info.codec_type = dec_info.rocdec_codec_id; // user specified codec_type for raw files
create_info.max_width = DEFAULT_WIDTH;
create_info.max_height = DEFAULT_HEIGHT;
create_info.width = DEFAULT_WIDTH;
create_info.height = DEFAULT_HEIGHT;
create_info.num_decode_surfaces = 6;
create_info.target_width = DEFAULT_WIDTH;
create_info.target_height = DEFAULT_HEIGHT;
create_info.display_rect.left = 0;
create_info.display_rect.right = static_cast<short>(DEFAULT_WIDTH);
create_info.display_rect.top = 0;
create_info.display_rect.bottom = static_cast<short>(DEFAULT_HEIGHT);
// for decode creation: assuming chroma_format is 4:2:0 and output_format is NV12.
// video dimensions ( width, height, max_width, max_height), num_decode_surfaces, and bit_depth_minus_8 are hardcoded here
// this will get changed in reconfigure when the sequence header is parsed from the stream to detect the actual video parameters
create_info.chroma_format = rocDecVideoChromaFormat_420;
create_info.output_format = rocDecVideoSurfaceFormat_NV12;
create_info.bit_depth_minus_8 = 2;
create_info.num_output_surfaces = 1;
CHECK(rocDecCreateDecoder(&dec_info.decoder, &create_info));
}
int ROCDECAPI handle_video_sequence_host(void* user_data, RocdecVideoFormatHost* format_host) {
DecoderInfo *p_dec_info = static_cast<DecoderInfo *>(user_data);
RocdecVideoFormat *format = &format_host->video_format;
RocdecReconfigureDecoderInfo reconfig_params = {};
reconfig_params.width = format->coded_width;
reconfig_params.height = format->coded_height;
reconfig_params.num_decode_surfaces = 6;
reconfig_params.target_width = format->coded_width;
reconfig_params.target_height = format->coded_height;
reconfig_params.display_rect.left = 0;
reconfig_params.display_rect.right = static_cast<short>(format->coded_width);
reconfig_params.display_rect.top = 0;
reconfig_params.display_rect.bottom = static_cast<short>(format->coded_height);
p_dec_info->surf_format = format_host->video_surface_format;
p_dec_info->disp_rect.top = format->display_area.top;
p_dec_info->disp_rect.bottom = format->display_area.bottom;
p_dec_info->disp_rect.left = format->display_area.left;
p_dec_info->disp_rect.right = format->display_area.right;
CHECK(rocDecReconfigureDecoderHost(p_dec_info->decoder, &reconfig_params));
p_dec_info->is_decoder_reconfigured = true;
int bitdepth_minus_8 = format->bit_depth_luma_minus8;
p_dec_info->coded_width = format->coded_width;
p_dec_info->coded_height = format->coded_height;
p_dec_info->bytes_per_pixel = bitdepth_minus_8 > 0 ? 2 : 1;
std::ostringstream input_video_info_str;
input_video_info_str.str("");
input_video_info_str.clear();
input_video_info_str << "Input Video Information" << std::endl
<< "\tCodec : " << format->codec << std::endl;
if (format->frame_rate.numerator && format->frame_rate.denominator) {
input_video_info_str << "\tFrame rate : " << format->frame_rate.numerator << "/" << format->frame_rate.denominator << " = " << 1.0 * format->frame_rate.numerator / format->frame_rate.denominator << " fps" << std::endl;
}
input_video_info_str << "\tSequence : " << (format->progressive_sequence ? "Progressive" : "Interlaced") << std::endl
<< "\tCoded size : [" << format->coded_width << ", " << format->coded_height << "]" << std::endl
<< "\tDisplay area : [" << format->display_area.left << ", " << format->display_area.top << ", "
<< format->display_area.right << ", " << format->display_area.bottom << "]" << std::endl
<< "\tBit depth : " << format->bit_depth_luma_minus8 + 8
;
input_video_info_str << std::endl;
std::cout << input_video_info_str.str();
return 1;
}
int ROCDECAPI handle_picture_display_host(void* user_data, RocdecParserDispInfo* disp_info) {
DecoderInfo *p_dec_info = static_cast<DecoderInfo *>(user_data);
RocdecParserDispInfo *p_disp_info = static_cast<RocdecParserDispInfo *>(disp_info);
RocdecProcParams params = {};
params.progressive_frame = p_disp_info->progressive_frame;
params.top_field_first = p_disp_info->top_field_first;
void* frame_mem_ptr[3] = {nullptr};
uint32_t pitch[3] = {0};
CHECK(rocDecGetVideoFrameHost(p_dec_info->decoder, p_disp_info->picture_index, frame_mem_ptr, pitch, &params));
p_dec_info->mem_type = OUT_SURFACE_MEM_HOST;
if (p_dec_info->dump_decoded_frames) {
save_frame_to_file_host(p_dec_info, frame_mem_ptr, pitch);
}
return 1;
}
void create_decoder_host(DecoderInfo& dec_info) {
// many of the decoder parameters are hardcoded below for just creating the decoder.
// In the handlevideosequence callback, the decoder will get reconfigured to the actual parameters in the sequence header
RocDecoderHostCreateInfo create_info = {};
create_info.codec_type = dec_info.rocdec_codec_id;
create_info.num_decode_threads = 0; // default
create_info.max_width = DEFAULT_WIDTH;
create_info.max_height = DEFAULT_HEIGHT;
create_info.width = DEFAULT_WIDTH;
create_info.height = DEFAULT_HEIGHT;
create_info.target_width = DEFAULT_WIDTH;
create_info.target_height = DEFAULT_HEIGHT;
create_info.display_rect.left = 0;
create_info.display_rect.right = static_cast<short>(DEFAULT_WIDTH);
create_info.display_rect.top = 0;
create_info.display_rect.bottom = static_cast<short>(DEFAULT_HEIGHT);
create_info.chroma_format = rocDecVideoChromaFormat_420;
create_info.output_format = rocDecVideoSurfaceFormat_P016;
create_info.bit_depth_minus_8 = 2;
create_info.num_output_surfaces = 1;
create_info.user_data = &dec_info;
create_info.pfn_sequence_callback = handle_video_sequence_host;
create_info.pfn_display_picture = handle_picture_display_host;
CHECK(rocDecCreateDecoderHost(&dec_info.decoder, &create_info));
dec_info.backend = DECODER_BACKEND_HOST;
}
int ROCDECAPI handle_video_sequence(void* user_data, RocdecVideoFormat* format) {
DecoderInfo *p_dec_info = static_cast<DecoderInfo *>(user_data);
RocdecReconfigureDecoderInfo reconfig_params = {};
int bitdepth_minus_8 = format->bit_depth_luma_minus8;
uint32_t target_width = (format->display_area.right - format->display_area.left + 1) & ~1;
uint32_t target_height = (format->display_area.bottom - format->display_area.top + 1) & ~1;
reconfig_params.width = format->coded_width;
reconfig_params.height = format->coded_height;
reconfig_params.bit_depth_minus_8 = bitdepth_minus_8;
reconfig_params.num_decode_surfaces = format->min_num_decode_surfaces;
reconfig_params.target_width = target_width;
reconfig_params.target_height = target_height;
reconfig_params.display_rect.left = format->display_area.left;
reconfig_params.display_rect.right = format->display_area.right;
reconfig_params.display_rect.top = format->display_area.top;
reconfig_params.display_rect.bottom = format->display_area.bottom;
CHECK(rocDecReconfigureDecoder(p_dec_info->decoder, &reconfig_params));
p_dec_info->is_decoder_reconfigured = true;
p_dec_info->disp_rect.top = format->display_area.top;
p_dec_info->disp_rect.bottom = format->display_area.bottom;
p_dec_info->disp_rect.left = format->display_area.left;
p_dec_info->disp_rect.right = format->display_area.right;
rocDecVideoChromaFormat video_chroma_format = format->chroma_format;
if (video_chroma_format == rocDecVideoChromaFormat_420 || rocDecVideoChromaFormat_Monochrome)
p_dec_info->surf_format = bitdepth_minus_8 ? rocDecVideoSurfaceFormat_P016 : rocDecVideoSurfaceFormat_NV12;
else if (video_chroma_format == rocDecVideoChromaFormat_444)
p_dec_info->surf_format = bitdepth_minus_8 ? rocDecVideoSurfaceFormat_YUV444_16Bit : rocDecVideoSurfaceFormat_YUV444;
else if (video_chroma_format == rocDecVideoChromaFormat_422)
p_dec_info->surf_format = bitdepth_minus_8 ? rocDecVideoSurfaceFormat_YUV422_16Bit : rocDecVideoSurfaceFormat_YUV422;
p_dec_info->coded_width = format->coded_width;
p_dec_info->coded_height = format->coded_height;
p_dec_info->bytes_per_pixel = bitdepth_minus_8 > 0 ? 2 : 1;
std::ostringstream input_video_info_str;
input_video_info_str.str("");
input_video_info_str.clear();
input_video_info_str << "Input Video Information" << std::endl
<< "\tCodec : " << format->codec << std::endl;
if (format->frame_rate.numerator && format->frame_rate.denominator) {
input_video_info_str << "\tFrame rate : " << format->frame_rate.numerator << "/" << format->frame_rate.denominator << " = " << 1.0 * format->frame_rate.numerator / format->frame_rate.denominator << " fps" << std::endl;
}
input_video_info_str << "\tSequence : " << (format->progressive_sequence ? "Progressive" : "Interlaced") << std::endl
<< "\tCoded size : [" << format->coded_width << ", " << format->coded_height << "]" << std::endl
<< "\tDisplay area : [" << format->display_area.left << ", " << format->display_area.top << ", "
<< format->display_area.right << ", " << format->display_area.bottom << "]" << std::endl
<< "\tBit depth : " << format->bit_depth_luma_minus8 + 8
;
input_video_info_str << std::endl;
std::cout << input_video_info_str.str();
return 1;
}
int ROCDECAPI handle_picture_decode(void* user_data, RocdecPicParams* params) {
DecoderInfo *p_dec_info = static_cast<DecoderInfo *>(user_data);
CHECK(rocDecDecodeFrame(p_dec_info->decoder, params));
return 1;
}
int ROCDECAPI handle_picture_display(void* user_data, RocdecParserDispInfo* disp_info) {
DecoderInfo *p_dec_info = static_cast<DecoderInfo *>(user_data);
RocdecProcParams params = {};
params.progressive_frame = disp_info->progressive_frame;
params.top_field_first = disp_info->top_field_first;
// check if decoding is complete
RocdecDecodeStatus dec_status;
memset(&dec_status, 0, sizeof(dec_status));
CHECK(rocDecGetDecodeStatus(p_dec_info->decoder, disp_info->picture_index, &dec_status));
if ((dec_status.decode_status == rocDecodeStatus_Error || dec_status.decode_status == rocDecodeStatus_Error_Concealed)) {
std::cerr << "Decode Error occurred for picture: " << disp_info->picture_index << std::endl;
return 0;
}
// get device memory pointer for decoded output surface
void* dev_mem_ptr[3] = { 0 };
uint32_t pitch[3] = { 0 };
CHECK(rocDecGetVideoFrame(p_dec_info->decoder, disp_info->picture_index, dev_mem_ptr, pitch, &params));
if (p_dec_info->dump_decoded_frames) {
save_frame_to_file(p_dec_info, dev_mem_ptr, pitch);
}
return 1;
}
void create_parser(DecoderInfo& dec_info) {
RocdecParserParams params = {};
params.codec_type = dec_info.rocdec_codec_id;
params.max_num_decode_surfaces = 6;
params.max_display_delay = 1; // min display delay of 1 is recommented to get optimal performance from hardware decoder
params.user_data = &dec_info;
params.pfn_sequence_callback = handle_video_sequence;
params.pfn_decode_picture = handle_picture_decode;
params.pfn_display_picture = handle_picture_display;
CHECK(rocDecCreateVideoParser(&dec_info.parser, &params));
}
void decode_frames(DecoderInfo& dec_info, const std::vector<std::vector<uint8_t>>& frames) {
// gpu backend using VCN
if (dec_info.backend == DECODER_BACKEND_DEVICE) {
for (int i=0; i < static_cast<int>(frames.size()); ++i) {
RocdecSourceDataPacket packet = {};
packet.payload_size = frames[i].size();
packet.payload = frames[i].data();
if (i == static_cast<int>(frames.size() - 1)) {
packet.flags = ROCDEC_PKT_ENDOFPICTURE; // mark end_of_picture flag for last frame
}
CHECK(rocDecParseVideoData(dec_info.parser, &packet));
}
} else if (dec_info.backend == DECODER_BACKEND_HOST) {
for (int i=0; i < static_cast<int>(frames.size()); ++i) {
RocdecPicParamsHost pic_params = {};
pic_params.bitstream_data_len = frames[i].size();
pic_params.bitstream_data = frames[i].data();
if (i == static_cast<int>(frames.size() - 1)) {
pic_params.flags = ROCDEC_PKT_ENDOFPICTURE; // mark end_of_picture flag for last frame
}
CHECK(rocDecDecodeFrameHost(dec_info.decoder, &pic_params));
}
}
}
void destroy_decoder(DecoderInfo& dec_info) {
if (dec_info.backend == DECODER_BACKEND_DEVICE)
CHECK(rocDecDestroyDecoder(dec_info.decoder));
else if (dec_info.backend == DECODER_BACKEND_HOST) {
CHECK(rocDecDestroyDecoderHost(dec_info.decoder));
}
}
void destroy_parser(DecoderInfo& dec_info) {
if (dec_info.backend == DECODER_BACKEND_DEVICE)
CHECK(rocDecDestroyVideoParser(dec_info.parser));
}
void ShowHelpAndExit(const char *option = NULL) {
std::cout << "Options:" << std::endl
<< "-i Input File Path - required" << std::endl
<< "-o Output File Path - dumps output if requested; optional" << std::endl
<< "-d GPU device ID (0 for the first device, 1 for the second, etc.); optional; default: 0" << std::endl
<< "-b backend (0 for GPU, 1 CPU-FFMpeg); optional; default: 0" << std::endl
<< "-c codec (0 : HEVC, 1 : H264, 2: AV1, 4: VP9, 5: VP8 ); optional; default: 0" << std::endl
<< "-n Number of iteration - specify the number of iterations for performance evaluation; optional; default: 1" << std::endl
<< "-m output_surface_memory_type - decoded surface memory; optional; default - 0"
<< " [0 : OUT_SURFACE_MEM_DEV_INTERNAL/ 1 : OUT_SURFACE_MEM_DEV_COPIED/ 2 : OUT_SURFACE_MEM_HOST_COPIED/ 3 : OUT_SURFACE_MEM_NOT_MAPPED]" << std::endl;
exit(0);
}
// helper function for sort
std::string getLastPart(const std::string& str, char delimiter) {
size_t pos = str.find_last_of(delimiter);
if (pos == std::string::npos) {
return str; // Delimiter not found, return the whole string
}
return str.substr(pos + 1);
}
// helper function for sort
int extractNumber(const std::string& filename) {
std::string numStr;
for (char c : filename) {
if (std::isdigit(c)) {
numStr += c;
} else if (!numStr.empty()) {
break; // Stop at first non-digit after a digit sequence
}
}
return numStr.empty() ? 0 : std::stoi(numStr);
}
// helper function for sort
// Sort entries based on the numerical part of their filenames
bool compareFilenames(const std::string& a, const std::string& b) {
int num_a = extractNumber(a);
int num_b = extractNumber(b);
if (num_a != num_b) {
return num_a < num_b;
}
return a < b; // Fallback to lexicographical comparison
};
int main(int argc, char** argv) {
std::string input_file_path, output_file_path;
int dump_output_frames = 0;
int device_id = 0;
DecoderBackend backend = DECODER_BACKEND_DEVICE;
int num_iterations = 1;
std::vector<std::string> input_file_names;
int codec_type = 0; // default for HEVC
DecoderInfo dec_info;
// Parse command-line arguments
if(argc <= 1) {
ShowHelpAndExit();
}
for (int i = 1; i < argc; i++) {
if (!strcmp(argv[i], "-h")) {
ShowHelpAndExit();
}
if (!strcmp(argv[i], "-i")) {
if (++i == argc) {
ShowHelpAndExit("-i");
}
input_file_path = argv[i];
bool b_sort_filenames = false;
if (std::filesystem::is_directory(input_file_path)) {
for (const auto& entry : std::filesystem::directory_iterator(input_file_path)) {
if (entry.is_directory()) {
std::vector<std::string> file_names_sub_folder;
for (const auto& sub_entry : std::filesystem::directory_iterator(entry)) {
file_names_sub_folder.push_back(sub_entry.path());
}
std::sort(file_names_sub_folder.begin(), file_names_sub_folder.end(), compareFilenames);
input_file_names.insert(input_file_names.end(), file_names_sub_folder.begin(), file_names_sub_folder.end());
file_names_sub_folder.clear();
} else if(entry.is_regular_file()) {
b_sort_filenames = true;
input_file_names.push_back(entry.path());
}
else {
std::cout << "unknown file type in input folder: " << entry.path().string() << '\n';
continue;
}
}
if (b_sort_filenames) {
std::sort(input_file_names.begin(), input_file_names.end(), compareFilenames);
}
} else {
input_file_names.push_back(input_file_path);
}
std::cout << "Read " << input_file_names.size() << " frames from disk." << std::endl;
continue;
}
if (!strcmp(argv[i], "-o")) {
if (++i == argc) {
ShowHelpAndExit("-o");
}
output_file_path = argv[i];
dec_info.output_file_path = output_file_path;
dump_output_frames = true;
continue;
}
if (!strcmp(argv[i], "-b")) {
if (++i == argc) {
ShowHelpAndExit("-b");
}
backend = static_cast<DecoderBackend>(atoi(argv[i]));
continue;
}
if (!strcmp(argv[i], "-d")) {
if (++i == argc) {
ShowHelpAndExit("-d");
}
device_id = atoi(argv[i]);
continue;
}
if (!strcmp(argv[i], "-c")) {
if (++i == argc) {
ShowHelpAndExit("-c");
}
codec_type = atoi(argv[i]);
continue;
}
if (!strcmp(argv[i], "-n")) {
if (++i == argc) {
ShowHelpAndExit("-n");
}
num_iterations = atoi(argv[i]);
continue;
}
ShowHelpAndExit(argv[i]);
}
dec_info.rocdec_codec_id = CodecTypeToRocDecVideoCodec(codec_type);
dec_info.dec_device_id = device_id;
dec_info.mem_type = (!backend) ? OUT_SURFACE_MEM_DEV_INTERNAL : OUT_SURFACE_MEM_HOST;
init();
if (backend == DECODER_BACKEND_DEVICE) {
create_parser(dec_info);
create_decoder(dec_info);
} else {
create_decoder_host(dec_info);
}
dec_info.dump_decoded_frames = dump_output_frames;
auto input_frames = read_frames(input_file_names);
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < num_iterations; i++) {
decode_frames(dec_info, input_frames);
}
auto end = std::chrono::high_resolution_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
std::cout << "Decoding time: " << elapsed << " microseconds" << std::endl;
destroy_decoder(dec_info);
destroy_parser(dec_info);
std::cout << "Success." << std::endl << std::endl << std::endl;
return 0;
}
+114
Просмотреть файл
@@ -0,0 +1,114 @@
# ##############################################################################
# Copyright (c) 2023 - 2025 Advanced Micro Devices, Inc.
#
# 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.
#
# ##############################################################################
cmake_minimum_required(VERSION 3.10)
# ROCM Path
if(DEFINED ENV{ROCM_PATH})
set(ROCM_PATH $ENV{ROCM_PATH} CACHE PATH "Default ROCm installation path")
elseif(ROCM_PATH)
message("-- INFO:ROCM_PATH Set -- ${ROCM_PATH}")
else()
set(ROCM_PATH /opt/rocm CACHE PATH "Default ROCm installation path")
endif()
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED On)
# Set AMD Clang as default compiler
if (NOT DEFINED CMAKE_CXX_COMPILER)
set(CMAKE_C_COMPILER ${ROCM_PATH}/bin/amdclang)
set(CMAKE_CXX_COMPILER ${ROCM_PATH}/bin/amdclang++)
endif()
# Set Project Version and Language
project(rocdecodehost LANGUAGES CXX)
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/../../cmake)
list(APPEND CMAKE_PREFIX_PATH ${ROCM_PATH}/lib/cmake)
set(DEFAULT_BUILD_TYPE "Release")
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "${DEFAULT_BUILD_TYPE}" CACHE STRING "rocdecode-host Default Build Type" FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release")
endif()
if(CMAKE_BUILD_TYPE MATCHES Debug)
# -O0 -- Don't Optimize output file
# -gdwarf-4 -- generate debugging information, dwarf-4 for making valgrind work
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -gdwarf-4 -Wall")
else()
# -O3 -- Optimize output file
# -DNDEBUG -- turn off asserts
# -fPIC -- Generate position-independent code if possible
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -DNDEBUG -fPIC -Wall")
endif()
message("-- ${BoldBlue}rocdecode-host Build Type -- ${CMAKE_BUILD_TYPE}${ColourReset}")
find_package(rocprofiler-register QUIET)
#find_package(FFmpeg QUIET)
find_package(FFmpeg)
if(HIP_FOUND AND FFMPEG_FOUND AND Threads_FOUND AND rocprofiler-register_FOUND)
# HIP
set(LINK_LIBRARY_LIST ${LINK_LIBRARY_LIST} hip::host)
# FFMPEG
include_directories(${AVUTIL_INCLUDE_DIR} ${AVCODEC_INCLUDE_DIR} ${AVFORMAT_INCLUDE_DIR})
set(LINK_LIBRARY_LIST ${LINK_LIBRARY_LIST} ${FFMPEG_LIBRARIES})
# rocprofiler
if (rocprofiler-register_FOUND)
set(LINK_LIBRARY_LIST ${LINK_LIBRARY_LIST} rocprofiler-register::rocprofiler-register)
endif()
# local include files
include_directories(api rocdecode-host ../src/rocdecode ../src/parser)
# source files
file(GLOB_RECURSE SOURCES "./*.cpp")
# rocdecode.so
add_library(${PROJECT_NAME} SHARED ${SOURCES})
target_link_libraries(${PROJECT_NAME} ${LINK_LIBRARY_LIST})
# FFMPEG multi-version support
if(_FFMPEG_AVCODEC_VERSION VERSION_LESS_EQUAL 58.134.100)
target_compile_definitions(${PROJECT_NAME} PUBLIC USE_AVCODEC_GREATER_THAN_58_134=0)
else()
target_compile_definitions(${PROJECT_NAME} PUBLIC USE_AVCODEC_GREATER_THAN_58_134=1)
endif()
set_target_properties(${PROJECT_NAME} PROPERTIES POSITION_INDEPENDENT_CODE ON)
else()
message("-- ERROR!: ${PROJECT_NAME} excluded! please install all the dependencies and try again!")
if (NOT HIP_FOUND)
message(FATAL_ERROR "-- ERROR!: HIP Not Found! - please install ROCm and HIP!")
endif()
if (NOT FFMPEG_FOUND)
message(FATAL_ERROR "-- ERROR!: FFMPEG Not Found! - please install FFMPEG!")
endif()
if (NOT Threads_FOUND)
message(FATAL_ERROR "-- ERROR!: Threads Not Found! - please insatll Threads!")
endif()
if (NOT rocprofiler-register_FOUND)
message(FATAL_ERROR "-- ERROR!: rocprofiler-register Not Found! - please install rocprofiler-register!")
endif()
endif()
# install rocDecode libs -- {ROCM_PATH}/lib
install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT dev)
+435
Просмотреть файл
@@ -0,0 +1,435 @@
/*
Copyright (c) 2023 - 2025 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 "avcodec_videodecoder.h"
/**
* @brief helper function for inferring AVCodecID from rocDecVideoCodec
*
* @param rocdec_codec
* @return AVCodecID
*/
static inline AVCodecID RocDecVideoCodec2AVCodec(rocDecVideoCodec rocdec_codec) {
switch (rocdec_codec) {
case rocDecVideoCodec_MPEG1 : return AV_CODEC_ID_MPEG1VIDEO;
case rocDecVideoCodec_MPEG2 : return AV_CODEC_ID_MPEG2VIDEO;
case rocDecVideoCodec_MPEG4 : return AV_CODEC_ID_MPEG4;
case rocDecVideoCodec_AVC : return AV_CODEC_ID_H264;
case rocDecVideoCodec_HEVC : return AV_CODEC_ID_HEVC;
case rocDecVideoCodec_VP8 : return AV_CODEC_ID_VP8;
case rocDecVideoCodec_VP9 : return AV_CODEC_ID_VP9;
case rocDecVideoCodec_JPEG : return AV_CODEC_ID_MJPEG;
case rocDecVideoCodec_AV1 : return AV_CODEC_ID_AV1;
default : return AV_CODEC_ID_NONE;
}
}
/**
* @brief helper function for inferring AVCodecID from rocDecVideoSurfaceFormat
*
* @param AVPixelFormat
* @return rocDecVideoChromaFormat
*/
static inline rocDecVideoChromaFormat AVPixelFormat2rocDecVideoChromaFormat(AVPixelFormat av_pixel_format) {
switch (av_pixel_format) {
case AV_PIX_FMT_YUV420P :
case AV_PIX_FMT_YUVJ420P :
case AV_PIX_FMT_YUV420P10LE :
case AV_PIX_FMT_YUV420P12LE :
return rocDecVideoChromaFormat_420;
case AV_PIX_FMT_YUV422P :
case AV_PIX_FMT_YUVJ422P :
return rocDecVideoChromaFormat_422;
case AV_PIX_FMT_YUV444P :
case AV_PIX_FMT_YUVJ444P :
return rocDecVideoChromaFormat_444;
default :
std::cerr << "ERROR: " << av_get_pix_fmt_name(av_pixel_format) << " pixel_format is not supported!" << std::endl;
return rocDecVideoChromaFormat_420; // for sanity
}
}
/**
* @brief helper function for inferring AVCodecID from rocDecVideoSurfaceFormat
*
* @param AVPixelFormat
* @return AVCodecID
*/
static inline int BitDepthFromPixelFormat(AVPixelFormat av_pixel_format) {
switch (av_pixel_format) {
case AV_PIX_FMT_YUV420P :
case AV_PIX_FMT_YUVJ420P :
return 8;
case AV_PIX_FMT_YUV420P10LE :
case AV_PIX_FMT_YUV420P12LE :
return 16;
case AV_PIX_FMT_YUV422P :
case AV_PIX_FMT_YUVJ422P :
case AV_PIX_FMT_YUV444P :
case AV_PIX_FMT_YUVJ444P :
return 16;
default :
std::cerr << "ERROR: " << av_get_pix_fmt_name(av_pixel_format) << " pixel_format is not supported!" << std::endl;
return AV_PIX_FMT_YUV420P; // for sanity
}
}
/**
* @brief helper function for inferring AVCodecID from rocDecVideoSurfaceFormat
*
* @param AVPixelFormat
* @return rocDecVideoSurfaceFormat
*/
static inline rocDecVideoSurfaceFormat AVPixelFormat2rocDecVideoSurfaceFormat(AVPixelFormat av_pixel_format) {
switch (av_pixel_format) {
case AV_PIX_FMT_YUV420P :
case AV_PIX_FMT_YUVJ420P :
return rocDecVideoSurfaceFormat_YUV420;
case AV_PIX_FMT_YUV444P :
case AV_PIX_FMT_YUVJ444P :
return rocDecVideoSurfaceFormat_YUV444;
case AV_PIX_FMT_YUV420P10LE :
case AV_PIX_FMT_YUV420P12LE :
return rocDecVideoSurfaceFormat_YUV420_16Bit;
default :
std::cerr << "ERROR: " << av_get_pix_fmt_name(av_pixel_format) << " pixel_format is not supported!" << std::endl;
return rocDecVideoSurfaceFormat_NV12; // for sanity
}
}
/**
* @brief Constructor
*/
AvcodecVideoDecoder::AvcodecVideoDecoder(RocDecoderHostCreateInfo &decoder_create_info) : decoder_create_info_{decoder_create_info} {
b_multithreading_ = false; // todo:: remove
pfn_sequece_cb_ = decoder_create_info_.pfn_sequence_callback;
pfn_display_picture_cb_ = decoder_create_info_.pfn_display_picture;
pfn_get_sei_message_cb_ = decoder_create_info_.pfn_get_sei_msg;
// start the avcodec decoding thread for multi-threading
if (b_multithreading_) {
ffmpeg_decoder_thread_ = new std::thread(&AvcodecVideoDecoder::DecodeThread, this);
if (!ffmpeg_decoder_thread_) {
THROW("FFMpegVideoDecoder create thread failed");
}
}
};
AvcodecVideoDecoder::~AvcodecVideoDecoder() {
// free av_packet_data_
while (!av_packet_data_.empty()) {
std::pair<uint8_t *, int> *packet_data = &av_packet_data_.back();
av_freep(&packet_data->first);
av_packet_data_.pop_back();
}
//release av_packets
while (!av_packets_.empty()) {
av_packet_free(&av_packets_.back());
av_packets_.pop_back();
}
// free decoder context
if (dec_context_) {
avcodec_free_context(&dec_context_);
dec_context_ = nullptr;
}
}
/**
* @brief InitializeDecoder()
*
* @return rocDecStatus
*/
rocDecStatus AvcodecVideoDecoder::InitializeDecoder() {
if (!decoder_) decoder_ = avcodec_find_decoder(RocDecVideoCodec2AVCodec(decoder_create_info_.codec_type));
if(!decoder_) {
ERR("rocDecode<FFMpeg>:: Codec not supported by FFMpeg ");
return ROCDEC_NOT_SUPPORTED;
}
if (!dec_context_) {
dec_context_ = avcodec_alloc_context3(decoder_); //alloc dec_context_
if (!dec_context_) {
ERR("Could not allocate video codec context");
return ROCDEC_RUNTIME_ERROR;
}
// set codec to automatically determine how many threads suits best for the decoding job
dec_context_->thread_count = decoder_create_info_.num_decode_threads;
if (decoder_->capabilities & AV_CODEC_CAP_FRAME_THREADS)
dec_context_->thread_type = FF_THREAD_FRAME;
else if (decoder_->capabilities & AV_CODEC_CAP_SLICE_THREADS)
dec_context_->thread_type = FF_THREAD_SLICE;
else
dec_context_->thread_count = 1;
// open the codec
if (avcodec_open2(dec_context_, decoder_, NULL) < 0) {
ERR("Could not open codec");
return ROCDEC_RUNTIME_ERROR;
}
// get the output pixel format from dec_context_
decoder_pixel_format_ = (dec_context_->pix_fmt == AV_PIX_FMT_NONE) ? AV_PIX_FMT_YUV420P : dec_context_->pix_fmt;
}
// set log level to
av_log_set_level(AV_LOG_QUIET);
// allocate av_frame buffer pool for number of surfaces to be in the decoder pool
// Note: with multi-threading, av_codec needs (dec_context_->delay + max_num_B_frames) number of av_frames.
// max_num_B_frames is assumed to be 4 here
if (dec_frames_.empty()) {
for (int i = 0; i < (dec_context_->delay + 4); i++) {
AVFrame *p_frame = av_frame_alloc();
dec_frames_.push_back(p_frame);
}
av_frame_cnt_ = 0;
}
// allocate max. packet_q of 4
if (av_packet_data_.empty()) {
for (int i = 0; i < 4; i++) {
uint8_t *pkt_data = static_cast<uint8_t *> (av_malloc(MAX_AV_PACKET_DATA_SIZE));
av_packet_data_.push_back(std::make_pair(pkt_data, MAX_AV_PACKET_DATA_SIZE));
}
}
// allocate av_packets_ for decoding
if (av_packets_.empty()) {
for (int i = 0; i < 4; i++) {
AVPacket *pkt = av_packet_alloc();
pkt->data = static_cast<uint8_t *> (av_packet_data_[i].first);
pkt->size = av_packet_data_[i].second;
av_packets_.push_back(pkt);
}
}
disp_rect_.left = decoder_create_info_.display_rect.left;
disp_rect_.top = decoder_create_info_.display_rect.top;
disp_rect_.right = decoder_create_info_.display_rect.right;
disp_rect_.bottom = decoder_create_info_.display_rect.bottom;
return ROCDEC_SUCCESS;
}
rocDecStatus AvcodecVideoDecoder::SubmitDecode(RocdecPicParamsHost *pPicParams) {
decoded_pic_cnt_ = 0;
if (end_of_stream_) {
avcodec_flush_buffers(dec_context_);
av_pkt_cnt_ = 0;
end_of_stream_ = false;
}
AVPacket *av_pkt = av_packets_[av_pkt_cnt_];
std::pair<uint8_t *, int> *packet_data = &av_packet_data_[av_pkt_cnt_];
if (pPicParams->bitstream_data_len > packet_data->second) {
void *new_pkt_data = av_realloc(av_pkt->data, (pPicParams->bitstream_data_len + MAX_AV_PACKET_DATA_SIZE)); // add more to avoid frequence reallocation
if (!new_pkt_data) {
ERR("ERROR: couldn't allocate packet data");
return ROCDEC_OUTOF_MEMORY;
}
packet_data->first = static_cast<uint8_t *>(new_pkt_data);
packet_data->second = (pPicParams->bitstream_data_len + MAX_AV_PACKET_DATA_SIZE);
av_pkt->data = packet_data->first;
}
memcpy(av_pkt->data, pPicParams->bitstream_data, pPicParams->bitstream_data_len);
av_pkt->size = pPicParams->bitstream_data_len;
av_pkt->flags = 0;
av_pkt->pts = pPicParams->pts;
if (!b_multithreading_) {
// flush and reconfigure the decoder when we reached eos
DecodeAvFrame(av_pkt, dec_frames_[av_frame_cnt_]);
NotifyPictureDisplay();
if ((!pPicParams->bitstream_data_len || pPicParams->flags == ROCDEC_PKT_ENDOFPICTURE) && !end_of_stream_) {
AVPacket pkt = {0};
DecodeAvFrame(&pkt, dec_frames_[av_frame_cnt_]);
NotifyPictureDisplay();
}
} else {
//push packet into packet q for decoding
PushPacket(av_pkt);
if ((!pPicParams->bitstream_data_len || pPicParams->flags == ROCDEC_PKT_ENDOFPICTURE) && !end_of_stream_) {
AVPacket pkt = {0};
PushPacket(&pkt);
}
// display frames
if (!disp_frames_q_.empty()) {
NotifyPictureDisplay();
}
}
av_pkt_cnt_ = (av_pkt_cnt_ + 1) % av_packets_.size();
if (!av_pkt->data || !av_pkt->size) {
end_of_stream_ = true;
}
if (pPicParams->flags & ROCDEC_PKT_ENDOFSTREAM) {
// flush last packet and let FFMpeg decode last frames
NotifyPictureDisplay();
}
return ROCDEC_SUCCESS;
}
rocDecStatus AvcodecVideoDecoder::GetDecodeStatus(int pic_idx, RocdecDecodeStatus *decode_status) {
if (p_disp_frame_ && p_disp_frame_->picture_index == pic_idx)
return ROCDEC_SUCCESS;
else
return ROCDEC_RUNTIME_ERROR;
}
rocDecStatus AvcodecVideoDecoder::GetVideoFrame(int pic_idx, void **frame_ptr, uint32_t *line_size, RocdecProcParams *vid_postproc_params){
if (p_disp_frame_ == nullptr) {
ERR("GetVideoFrame: No frame available to display");
return ROCDEC_RUNTIME_ERROR;
}
if (p_disp_frame_->picture_index != pic_idx) {
ERR("GetVideoFrame: pic_index is invalid" );
return ROCDEC_INVALID_PARAMETER;
}
auto p_av_frame = p_disp_frame_->av_frame_ptr;
frame_ptr[0] = p_av_frame->data[0];
frame_ptr[1] = p_av_frame->data[1];
frame_ptr[2] = p_av_frame->data[2];
line_size[0] = p_av_frame->linesize[0];
line_size[1] = p_av_frame->linesize[1];
line_size[2] = p_av_frame->linesize[2];
return ROCDEC_SUCCESS;
}
rocDecStatus AvcodecVideoDecoder::ReconfigureDecoder(RocdecReconfigureDecoderInfo *preconfig_params) {
rocDecStatus rocdec_status = ROCDEC_SUCCESS;
if (preconfig_params == nullptr) {
return ROCDEC_INVALID_PARAMETER;
}
//avcoded can handle reolution changes. So we just need to flush all remaining frames here.
bool is_decode_res_changed = !(preconfig_params->width == coded_width_ && preconfig_params->height == coded_height_);
if (is_decode_res_changed) {
AVPacket pkt = {0};
PushPacket(&pkt);
NotifyPictureDisplay();
}
return rocdec_status;
}
void AvcodecVideoDecoder::DecodeThread()
{
AVPacket *pkt;
do {
pkt = PopPacket();
DecodeAvFrame(pkt, dec_frames_[av_frame_cnt_]);
} while (!end_of_stream_);
}
int AvcodecVideoDecoder::DecodeAvFrame(AVPacket *av_pkt, AVFrame *p_frame) {
int status;
//send packet to av_codec
status = avcodec_send_packet(dec_context_, av_pkt);
if (status < 0) {
ERR("Error sending av packet for decoding: status: ");
return status;
}
while (status >= 0) {
status = avcodec_receive_frame(dec_context_, p_frame);
if (status == AVERROR(EAGAIN) || status == AVERROR_EOF) {
//if (status == AVERROR_EOF) std::cout << "got end of stream from avcodec_receive_frame" << std::endl;
end_of_stream_ = (status == AVERROR_EOF);
return 0;
}
else if (status < 0) {
ERR("Error during decoding");
return 0;
}
// for the first frame, initialize OutputsurfaceInfo
if (p_frame->width != coded_width_ || p_frame->height != coded_height_ || p_frame->format != av_sample_format) {
coded_width_ = p_frame->width;
coded_height_ = p_frame->height;
av_sample_format = p_frame->format;
NotifyNewSequence(p_frame);
}
// push frame into q
DecFrameBufferFFMpeg dec_frame = { 0 };
dec_frame.av_frame_ptr = p_frame;
dec_frame.pts = p_frame->pts;
dec_frame.picture_index = av_frame_cnt_; //picture_index is not used here since it is handled within FFMpeg decoder
PushDisplayFrame(dec_frame);
decoded_pic_cnt_++;
av_frame_cnt_ = (av_frame_cnt_ + 1) % dec_frames_.size();
p_frame = dec_frames_[av_frame_cnt_]; //advance for next frame decode
}
return 0;
}
rocDecStatus AvcodecVideoDecoder::NotifyNewSequence(AVFrame *p_frame) {
if (!p_frame)
return ROCDEC_INVALID_PARAMETER;
video_format_host_.video_surface_format = AVPixelFormat2rocDecVideoSurfaceFormat((AVPixelFormat)p_frame->format);
RocdecVideoFormat *p_video_format = &video_format_host_.video_format;
p_video_format->codec = decoder_create_info_.codec_type;
p_video_format->frame_rate.numerator = dec_context_->framerate.num;
p_video_format->frame_rate.denominator = dec_context_->framerate.den;
p_video_format->bit_depth_luma_minus8 = BitDepthFromPixelFormat(dec_context_->pix_fmt) - 8;
p_video_format->bit_depth_chroma_minus8 = p_video_format->bit_depth_luma_minus8;
p_video_format->progressive_sequence = !p_frame->interlaced_frame;
p_video_format->min_num_decode_surfaces = dec_context_->delay + dec_context_->max_b_frames;
p_video_format->coded_width = p_frame->linesize[0];
p_video_format->coded_height = p_frame->height;
p_video_format->chroma_format = AVPixelFormat2rocDecVideoChromaFormat(dec_context_->pix_fmt);
p_video_format->display_area = { 0, 0, p_frame->width, p_frame->height };
p_video_format->bitrate = 0;
p_video_format->display_aspect_ratio.x = p_frame->sample_aspect_ratio.num;
p_video_format->display_aspect_ratio.y = p_frame->sample_aspect_ratio.den;
if (pfn_sequece_cb_ && decoder_create_info_.user_data &&
pfn_sequece_cb_(decoder_create_info_.user_data, &video_format_host_) == 0) {
ERR("Sequence callback function failed.");
return ROCDEC_RUNTIME_ERROR;
} else {
return ROCDEC_SUCCESS;
}
}
rocDecStatus AvcodecVideoDecoder::SendSeiMsgPayload(AVFrame *p_frame) {
#if 0 //todo
sei_message_info_params_.sei_message_count = sei_message_count_;
sei_message_info_params_.sei_message = sei_message_list_.data();
sei_message_info_params_.sei_data = (void*)sei_payload_buf_;
sei_message_info_params_.picIdx = curr_pic_info_.dec_buf_idx;
// callback function with RocdecSeiMessageInfo params filled out
if (pfn_get_sei_message_cb_) pfn_get_sei_message_cb_(parser_params_.user_data, &sei_message_info_params_);
#endif
return ROCDEC_NOT_IMPLEMENTED;
}
rocDecStatus AvcodecVideoDecoder::NotifyPictureDisplay() {
int num_frames_to_display = decoded_pic_cnt_;
while (num_frames_to_display) {
p_disp_frame_ = GetDisplayFrame();
if (p_disp_frame_) {
RocdecParserDispInfo dispInfo = {0}; // dispinfo is not used in ffmpeg decoder, so setting it to zero
dispInfo.picture_index = p_disp_frame_->picture_index;
if (pfn_display_picture_cb_ && decoder_create_info_.user_data) {
pfn_display_picture_cb_(decoder_create_info_.user_data, &dispInfo);
}
}
num_frames_to_display--;
};
return ROCDEC_SUCCESS;
}
+166
Просмотреть файл
@@ -0,0 +1,166 @@
/*
Copyright (c) 2023 - 2025 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
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/pixdesc.h>
#if USE_AVCODEC_GREATER_THAN_58_134
#include <libavcodec/bsf.h>
#endif
}
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <cstring>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <atomic>
#include "../src/commons.h"
#include "../api/rocdecode/rocdecode.h"
#include "../api/rocdecode/rocdecode_host.h"
#define MAX_AV_PACKET_DATA_SIZE 4096
typedef struct DecFrameBufferFFMpeg_ {
AVFrame *av_frame_ptr; /**< av_frame pointer for the decoded frame */
uint8_t *frame_ptr; /**< host memory pointer for the decoded frame depending on mem_type*/
int64_t pts; /**< timestamp for the decoded frame */
int picture_index; /**< surface index for the decoded frame */
} DecFrameBufferFFMpeg;
typedef struct DecPacketBuffer_
{
AVPacket *av_pckt;
int av_frame_index;
} DecPacketBuffer;
typedef struct Rect_{
int16_t left;
int16_t top;
int16_t right;
int16_t bottom;
} Rect;
/**
* Class definition for AVcodec based video decoder class. This uses FFMpeg avcodec decoder to decode video frames
*/
class AvcodecVideoDecoder {
public:
AvcodecVideoDecoder(RocDecoderHostCreateInfo &decoder_create_info);
~AvcodecVideoDecoder();
rocDecStatus InitializeDecoder();
rocDecStatus SubmitDecode(RocdecPicParamsHost *pPicParams);
rocDecStatus GetDecodeStatus(int pic_idx, RocdecDecodeStatus* decode_status);
rocDecStatus ReconfigureDecoder(RocdecReconfigureDecoderInfo *reconfig_params);
rocDecStatus GetVideoFrame(int pic_idx, void **frame_ptr, uint32_t *line_size, RocdecProcParams *vid_postproc_params);
protected:
RocDecoderHostCreateInfo decoder_create_info_;
RocdecVideoFormatHost video_format_host_;
/*! \brief callback function pointers for the parser
*/
PFNVIDSEQUENCECHOSTALLBACK pfn_sequece_cb_ = nullptr; /**< Called before decoding frames and/or whenever there is a fmt change */
PFNVIDDISPLAYCALLBACK pfn_display_picture_cb_ = nullptr; /**< Called whenever a picture is ready to be displayed (display order) */
PFNVIDSEIMSGCALLBACK pfn_get_sei_message_cb_ = nullptr; /**< Called when all SEI messages are parsed for particular frame */
private:
void DecodeThread();
int DecodeAvFrame(AVPacket *av_pkt, AVFrame *p_frame);
int FlushDecoder();
rocDecStatus NotifyNewSequence(AVFrame *p_frame);
rocDecStatus NotifySeiMesage(AVFrame *p_frame);
rocDecStatus NotifyPictureDisplay();
rocDecStatus SendSeiMsgPayload(AVFrame *p_frame);
void PushPacket(AVPacket *pkt) {
{
std::lock_guard<std::mutex> lock(mtx_pkt_q_);
av_packet_q_.push(pkt);
}
cv_pkt_.notify_one();
}
AVPacket *PopPacket() {
AVPacket *pkt;
std::unique_lock<std::mutex> lock(mtx_pkt_q_);
cv_pkt_.wait(lock, [&] { return !av_packet_q_.empty(); });
pkt = av_packet_q_.front();
av_packet_q_.pop();
return pkt;
}
DecFrameBufferFFMpeg *GetDisplayFrame() {
std::unique_lock<std::mutex> lock(mtx_frame_q_);
cv_frame_.wait(lock, [&] { return !disp_frames_q_.empty() || end_of_stream_; });
if (end_of_stream_ && disp_frames_q_.empty())
return nullptr;
DecFrameBufferFFMpeg *p_disp_frame = &disp_frames_q_.front();
disp_frames_q_.pop();
return p_disp_frame;
}
void PushDisplayFrame(DecFrameBufferFFMpeg& frame) {
{
std::lock_guard<std::mutex> lock(mtx_frame_q_);
disp_frames_q_.push(frame);
}
cv_frame_.notify_one();
};
int decoded_pic_cnt_ = 0;
int coded_width_ = 0, coded_height_ = 0; // need to detect resolution changes for sps callback function
Rect disp_rect_ = {}; // displayable area specified in the bitstream
int av_sample_format = -1;
bool b_multithreading_ = true;
uint32_t av_frame_cnt_ = 0;
uint32_t av_pkt_cnt_ = 0;
RocdecSourceDataPacket last_packet_;
std::thread *ffmpeg_decoder_thread_ = nullptr;
std::queue<AVPacket *> av_packet_q_; // queue for compressed packets
std::queue<DecFrameBufferFFMpeg> disp_frames_q_; // vector of decoded frames
std::vector<AVFrame *> dec_frames_; // vector of AVFrame * for decoded frames
std::vector<AVPacket *> av_packets_; // store of AVPackets for decoding
std::vector<std::pair<uint8_t *, int>> av_packet_data_;
std::mutex mtx_pkt_q_, mtx_frame_q_; //for packet and frames
std::condition_variable cv_pkt_, cv_frame_;
DecFrameBufferFFMpeg *p_disp_frame_ = nullptr; // frame to display
std::atomic<bool> end_of_stream_ = false;
// Variables for FFMpeg decoding
AVCodecContext * dec_context_ = nullptr;
AVPixelFormat decoder_pixel_format_;
#if USE_AVCODEC_GREATER_THAN_58_134
const AVCodec *decoder_ = nullptr;
#else
AVCodec *decoder_ = nullptr;
#endif
AVFormatContext * formatContext = nullptr;
AVInputFormat * inputFormat = nullptr;
};
+45
Просмотреть файл
@@ -0,0 +1,45 @@
/*
Copyright (c) 2023 - 2025 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 <memory>
#include <string>
#include "roc_decoder_host.h"
/**
* @brief DecHandle structure
*
*/
struct DecHandleHost {
explicit DecHandleHost(RocDecoderHostCreateInfo& decoder_create_info) : roc_decoder_host_(std::make_shared<RocDecoderHost>(decoder_create_info)) {}; //constructor
~DecHandleHost() { ClearErrors(); }
std::shared_ptr<RocDecoderHost> roc_decoder_host_;
bool NoError() { return error_.empty(); }
const char* ErrorMsg() { return error_.c_str(); }
void CaptureError(const std::string& err_msg) { error_ = err_msg; }
private:
void ClearErrors() { error_ = "";}
std::string error_;
};
+85
Просмотреть файл
@@ -0,0 +1,85 @@
/*
Copyright (c) 2023 - 2025 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 "../commons.h"
#include "roc_decoder_host.h"
RocDecoderHost::RocDecoderHost(RocDecoderHostCreateInfo& decoder_create_info): avcodec_video_decoder_{decoder_create_info}, decoder_create_info_{decoder_create_info} {}
RocDecoderHost::~RocDecoderHost() {}
rocDecStatus RocDecoderHost::InitializeDecoder() {
rocDecStatus rocdec_status = ROCDEC_SUCCESS;
if (!decoder_create_info_.user_data) {
ERR("Invalid function callback pointer passed");
return ROCDEC_NOT_INITIALIZED;
}
rocdec_status = avcodec_video_decoder_.InitializeDecoder();
if (rocdec_status != ROCDEC_SUCCESS) {
ERR("Failed to initialize the FFMpeg Video decoder.");
return rocdec_status;
}
return rocdec_status;
}
rocDecStatus RocDecoderHost::DecodeFrame(RocdecPicParamsHost *pic_params) {
rocDecStatus rocdec_status = ROCDEC_SUCCESS;
rocdec_status = avcodec_video_decoder_.SubmitDecode(pic_params);
if (rocdec_status != ROCDEC_SUCCESS) {
ERR("Decode submission is not successful.");
}
return rocdec_status;
}
rocDecStatus RocDecoderHost::GetDecodeStatus(int pic_idx, RocdecDecodeStatus* decode_status) {
rocDecStatus rocdec_status = ROCDEC_SUCCESS;
rocdec_status = avcodec_video_decoder_.GetDecodeStatus(pic_idx, decode_status);
if (rocdec_status != ROCDEC_SUCCESS) {
ERR("Failed to query the decode status.");
}
return rocdec_status;
}
rocDecStatus RocDecoderHost::ReconfigureDecoder(RocdecReconfigureDecoderInfo *reconfig_params) {
if (reconfig_params == nullptr) {
return ROCDEC_INVALID_PARAMETER;
}
rocDecStatus rocdec_status = avcodec_video_decoder_.ReconfigureDecoder(reconfig_params);
if (rocdec_status != ROCDEC_SUCCESS) {
ERR("Reconfiguration of the decoder failed.");
return rocdec_status;
}
return rocdec_status;
}
rocDecStatus RocDecoderHost::GetVideoFrame(int pic_idx, void **frame_ptr, uint32_t *line_size, RocdecProcParams *vid_postproc_params) {
if (vid_postproc_params == nullptr || frame_ptr == nullptr) {
return ROCDEC_INVALID_PARAMETER;
}
rocDecStatus rocdec_status = avcodec_video_decoder_.GetVideoFrame(pic_idx, frame_ptr, line_size, vid_postproc_params);
if (rocdec_status != ROCDEC_SUCCESS) {
ERR("GetVideoFrame failed.");
return rocdec_status;
}
return rocdec_status;
}
+51
Просмотреть файл
@@ -0,0 +1,51 @@
/*
Copyright (c) 2023 - 2025 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 <vector>
#include <string>
#include <iostream>
#include <sstream>
#include <string.h>
#include <map>
#include "avcodec/avcodec_videodecoder.h"
/**
* RocDecoderHost class: Wrapper class for rocDecoderHost API implementation
*/
class RocDecoderHost {
public:
RocDecoderHost(RocDecoderHostCreateInfo &decoder_create_info);
~RocDecoderHost();
rocDecStatus InitializeDecoder();
rocDecStatus DecodeFrame(RocdecPicParamsHost *pic_params);
rocDecStatus GetDecodeStatus(int pic_idx, RocdecDecodeStatus* decode_status);
rocDecStatus ReconfigureDecoder(RocdecReconfigureDecoderInfo *reconfig_params);
rocDecStatus GetVideoFrame(int pic_idx, void *frame_ptr[3], uint32_t line_size[3], RocdecProcParams *vid_postproc_params);
private:
AvcodecVideoDecoder avcodec_video_decoder_;
RocDecoderHostCreateInfo decoder_create_info_;
};
+200
Просмотреть файл
@@ -0,0 +1,200 @@
/*
Copyright (c) 2023 - 2025 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 "dec_handle_host.h"
#include "../api/rocdecode/rocdecode.h"
#include "../src/commons.h"
/*****************************************************************************************************/
//! \fn rocDecStatus ROCDECAPI rocDecCreateDecoderHost(rocDecDecoderHandle *decoder_handle, RocDecoderCreateInfoHost *decoder_create_info)
//! Create the decoder object based on decoder_create_info. A handle to the created decoder is returned
/*****************************************************************************************************/
rocDecStatus ROCDECAPI
rocDecCreateDecoderHost(rocDecDecoderHandle *decoder_handle, RocDecoderHostCreateInfo *decoder_create_info) {
if (decoder_handle == nullptr || decoder_create_info == nullptr) {
return ROCDEC_INVALID_PARAMETER;
}
rocDecDecoderHandle handle = nullptr;
try {
handle = new DecHandleHost(*decoder_create_info);
}
catch(const std::exception& e) {
ERR( STR("Failed to init the rocDecode handle, ") + STR(e.what()))
return ROCDEC_NOT_INITIALIZED;
}
*decoder_handle = handle;
return static_cast<DecHandleHost *>(handle)->roc_decoder_host_->InitializeDecoder();
}
/*****************************************************************************************************/
//! \fn rocDecStatus ROCDECAPI rocDecDestroyDecoderHost(rocDecDecoderHandle decoder_handle)
//! Destroy the decoder object
/*****************************************************************************************************/
rocDecStatus ROCDECAPI
rocDecDestroyDecoderHost(rocDecDecoderHandle decoder_handle) {
if (decoder_handle == nullptr) {
return ROCDEC_INVALID_PARAMETER;
}
auto handle = static_cast<DecHandleHost *>(decoder_handle);
delete handle;
return ROCDEC_SUCCESS;
}
/**********************************************************************************************************************/
//! \fn rocDecStatus ROCDECAPI rocDecGetDecoderCapsHost(rocDecDecoderHandle decoder_handle, RocdecDecodeCaps *pdc)
//! Queries decode capabilities of host based decoder based on codec type, chroma_format and BitDepthMinus8 parameters.
//! 1. Application fills IN parameters codec_type, chroma_format and BitDepthMinus8 of RocdecDecodeCaps structure
//! 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.
//! Currently this is not supported in avcodec based decoder
/**********************************************************************************************************************/
rocDecStatus ROCDECAPI
rocDecGetDecoderCapsHost(RocdecDecodeCaps *pdc) {
if (pdc == nullptr) {
return ROCDEC_INVALID_PARAMETER;
}
//todo:
return ROCDEC_NOT_IMPLEMENTED;
}
/*****************************************************************************************************/
//! \fn rocDecStatus ROCDECAPI rocDecDecodeFrameHost(rocDecDecoderHandle decoder_handle, RocdecPicParams *pic_params)
//! Decodes a single picture
//! Submits the frame for HW decoding
/*****************************************************************************************************/
rocDecStatus ROCDECAPI
rocDecDecodeFrameHost(rocDecDecoderHandle decoder_handle, _RocdecPicParamsHost *pic_params) {
if (decoder_handle == nullptr || pic_params == nullptr) {
return ROCDEC_INVALID_PARAMETER;
}
auto handle = static_cast<DecHandleHost *>(decoder_handle);
rocDecStatus ret;
try {
ret = handle->roc_decoder_host_->DecodeFrame(pic_params);
}
catch(const std::exception& e) {
handle->CaptureError(e.what());
ERR(e.what())
return ROCDEC_RUNTIME_ERROR;
}
return ret;
}
/************************************************************************************************************/
//! \fn rocDecStatus ROCDECAPI rocDecGetDecodeStatusHost(rocDecDecoderHandle decoder_handle, int pic_idx, RocdecDecodeStatus* decode_status);
//! Get the decode status for frame corresponding to pic_idx
//! API is currently supported for HEVC codec.
//! API returns ROCDEC_NOT_SUPPORTED error code for unsupported GPU or codec.
/************************************************************************************************************/
rocDecStatus ROCDECAPI
rocDecGetDecodeStatusHost(rocDecDecoderHandle decoder_handle, int pic_idx, RocdecDecodeStatus* decode_status) {
if (decoder_handle == nullptr || decode_status == nullptr) {
return ROCDEC_INVALID_PARAMETER;
}
auto handle = static_cast<DecHandleHost *>(decoder_handle);
rocDecStatus ret;
try {
ret = handle->roc_decoder_host_->GetDecodeStatus(pic_idx, decode_status);
}
catch(const std::exception& e) {
handle->CaptureError(e.what());
ERR(e.what())
return ROCDEC_RUNTIME_ERROR;
}
return ret;
}
/*********************************************************************************************************/
//! \fn rocDecStatus ROCDECAPI rocDecReconfigureDecoderHost(rocDecDecoderHandle decoder_handle, RocdecReconfigureDecoderInfo *reconfig_params)
//! Used to reuse single decoder for multiple clips. Currently supports resolution change, resize params
//! params, target area params change for same codec. Must be called during RocdecParserParams::pfn_sequence_callback
/*********************************************************************************************************/
rocDecStatus ROCDECAPI
rocDecReconfigureDecoderHost(rocDecDecoderHandle decoder_handle, RocdecReconfigureDecoderInfo *reconfig_params) {
if (decoder_handle == nullptr || reconfig_params == nullptr) {
return ROCDEC_INVALID_PARAMETER;
}
auto handle = static_cast<DecHandleHost *>(decoder_handle);
rocDecStatus ret;
try {
ret = handle->roc_decoder_host_->ReconfigureDecoder(reconfig_params);
}
catch(const std::exception& e) {
handle->CaptureError(e.what());
ERR(e.what())
return ROCDEC_RUNTIME_ERROR;
}
return ret;
}
/************************************************************************************************************************/
//! \fn rocDecStatus ROCDECAPI rocDecGetVideoFrameHost(rocDecDecoderHandle decoder_handle, int pic_idx, unsigned int *dev_mem_ptr,
//! unsigned int *horizontal_pitch, RocdecProcParams *vid_postproc_params);
//! Post-process and map video frame corresponding to pic_idx 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) separately
/************************************************************************************************************************/
rocDecStatus ROCDECAPI
rocDecGetVideoFrameHost(rocDecDecoderHandle decoder_handle, int pic_idx,
void **frame_data, uint32_t *line_size,
RocdecProcParams *vid_postproc_params) {
if (decoder_handle == nullptr || frame_data == nullptr || line_size == nullptr || vid_postproc_params == nullptr) {
return ROCDEC_INVALID_PARAMETER;
}
auto handle = static_cast<DecHandleHost *>(decoder_handle);
rocDecStatus ret;
try {
ret = handle->roc_decoder_host_->GetVideoFrame(pic_idx, frame_data, line_size, vid_postproc_params);
}
catch(const std::exception& e) {
handle->CaptureError(e.what());
ERR(e.what())
return ROCDEC_RUNTIME_ERROR;
}
return ret;
}
/*****************************************************************************************************/
//! \fn const char* ROCDECAPI rocDecGetErrorNameHost(rocDecStatus rocdec_status)
//! \ingroup group_amd_rocdecode
//! Return name of the specified error code in text form.
/*****************************************************************************************************/
const char* ROCDECAPI rocDecGetErrorNameHost(rocDecStatus rocdec_status) {
switch (rocdec_status) {
case ROCDEC_DEVICE_INVALID:
return "ROCDEC_DEVICE_INVALID";
case ROCDEC_CONTEXT_INVALID:
return "ROCDEC_CONTEXT_INVALID";
case ROCDEC_RUNTIME_ERROR:
return "ROCDEC_RUNTIME_ERROR";
case ROCDEC_OUTOF_MEMORY:
return "ROCDEC_OUTOF_MEMORY";
case ROCDEC_INVALID_PARAMETER:
return "ROCDEC_INVALID_PARAMETER";
case ROCDEC_NOT_IMPLEMENTED:
return "ROCDEC_NOT_IMPLEMENTED";
case ROCDEC_NOT_INITIALIZED:
return "ROCDEC_NOT_INITIALIZED";
case ROCDEC_NOT_SUPPORTED:
return "ROCDEC_NOT_SUPPORTED";
default:
return "UNKNOWN_ERROR";
}
}
+1 -1
Просмотреть файл
@@ -109,7 +109,7 @@ rocDecDecodeFrame(rocDecDecoderHandle decoder_handle, RocdecPicParams *pic_param
//! \fn rocDecStatus ROCDECAPI RocdecGetDecodeStatus(rocDecDecoderHandle decoder_handle, int pic_idx, RocdecDecodeStatus* decode_status);
//! Get the decode status for frame corresponding to pic_idx
//! API is currently supported for HEVC codec.
//! API returns CUDA_ERROR_NOT_SUPPORTED error code for unsupported GPU or codec.
//! API returns ROCDEC_NOT_SUPPORTED error code for unsupported GPU or codec.
/************************************************************************************************************/
rocDecStatus ROCDECAPI
rocDecGetDecodeStatus(rocDecDecoderHandle decoder_handle, int pic_idx, RocdecDecodeStatus* decode_status) {
+1 -1
Просмотреть файл
@@ -549,7 +549,7 @@ int FFMpegVideoDecoder::HandlePictureDisplay(RocdecParserDispInfo *pDispInfo) {
}
void* src_ptr[3] = {0};
int32_t src_pitch[3] = {0};
src_ptr[0] = p_av_frame->data[0];
src_ptr[0] = p_av_frame->data[0];
src_ptr[1] = p_av_frame->data[1];
src_ptr[2] = p_av_frame->data[2];
src_pitch[0] = p_av_frame->linesize[0];