diff --git a/projects/rocdecode/CHANGELOG.md b/projects/rocdecode/CHANGELOG.md index ca3b11dcbb..216e518d13 100644 --- a/projects/rocdecode/CHANGELOG.md +++ b/projects/rocdecode/CHANGELOG.md @@ -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 1.0.0 for ROCm 7.0.0 +## rocdecode 1.1.0 for ROCm 7.0.0 ### Added @@ -13,7 +13,7 @@ Full documentation for rocDecode is available at [https://rocm.docs.amd.com/proj * 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 - +* The host backend in the `videoDeode` sample now uses the rocdecode-host library ### Optimized diff --git a/projects/rocdecode/CMakeLists.txt b/projects/rocdecode/CMakeLists.txt index 28b1949c81..86f743ffae 100644 --- a/projects/rocdecode/CMakeLists.txt +++ b/projects/rocdecode/CMakeLists.txt @@ -40,7 +40,7 @@ if (NOT DEFINED CMAKE_CXX_COMPILER) endif() # rocdecode Version -set(VERSION "1.0.0") +set(VERSION "1.1.0") # Set Project Version and Language project(rocdecode VERSION ${VERSION} LANGUAGES CXX) diff --git a/projects/rocdecode/samples/videoDecode/CMakeLists.txt b/projects/rocdecode/samples/videoDecode/CMakeLists.txt index b6aa182f8f..92aa389ae9 100644 --- a/projects/rocdecode/samples/videoDecode/CMakeLists.txt +++ b/projects/rocdecode/samples/videoDecode/CMakeLists.txt @@ -82,24 +82,30 @@ if(HIP_FOUND AND FFMPEG_FOUND AND rocdecode_FOUND AND Threads_FOUND AND rocprofi set(LINK_LIBRARY_LIST ${LINK_LIBRARY_LIST} Threads::Threads) # rocprofiler-register set(LINK_LIBRARY_LIST ${LINK_LIBRARY_LIST} rocprofiler-register::rocprofiler-register) - # sample app exe + # rocdecodehost + find_library(ROCDECODE_HOST_LIBRARY NAMES rocdecodehost HINTS ${ROCM_PATH}/lib) list(APPEND SOURCES ${PROJECT_SOURCE_DIR} videodecode.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../utils/rocvideodecode/roc_video_dec.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../utils/ffmpegvideodecode/ffmpeg_video_dec.cpp) + # sample app exe add_executable(${PROJECT_NAME} ${SOURCES}) - target_link_libraries(${PROJECT_NAME} ${LINK_LIBRARY_LIST}) + set(LINK_LIBRARY_LIST ${LINK_LIBRARY_LIST} rocdecode::rocdecode) + if(ROCDECODE_HOST_LIBRARY) + set(LINK_LIBRARY_LIST ${LINK_LIBRARY_LIST} ${ROCDECODE_HOST_LIBRARY}) + target_compile_definitions(${PROJECT_NAME} PUBLIC ENABLE_HOST_DECODE=1) + else() + target_compile_definitions(${PROJECT_NAME} PUBLIC ENABLE_HOST_DECODE=0) + endif() # 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() + 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() diff --git a/projects/rocdecode/samples/videoDecode/videodecode.cpp b/projects/rocdecode/samples/videoDecode/videodecode.cpp index e08638f689..c272b32e1a 100644 --- a/projects/rocdecode/samples/videoDecode/videodecode.cpp +++ b/projects/rocdecode/samples/videoDecode/videodecode.cpp @@ -42,6 +42,11 @@ THE SOFTWARE. #include "ffmpeg_video_dec.h" #include "common.h" +//hardcoding for host based decoder creation if demux is not available +#define DEFAULT_WIDTH 2912 +#define DEFAULT_HEIGHT 1888 + + void ShowHelpAndExit(const char *option = NULL) { std::cout << "Options:" << std::endl << "-i Input File Path - required" << std::endl @@ -89,7 +94,7 @@ int main(int argc, char **argv) { int seek_criteria = 0, seek_mode = 0; bool b_use_ffmpeg_demuxer = true; // true by default to use FFMPEG demuxer. set to false to use the built-in bitstream reader. - // Parse command-line arguments +// Parse command-line arguments if(argc <= 1) { ShowHelpAndExit(); } @@ -259,10 +264,16 @@ int main(int argc, char **argv) { if (!backend) // gpu backend viddec = new RocVideoDecoder(device_id, mem_type, rocdec_codec_id, b_force_zero_latency, p_crop_rect, b_extract_sei_messages, disp_delay); else { + #if ENABLE_HOST_DECODE std::cout << "info: RocDecode is using CPU backend!" << std::endl; - bool use_threading = false; + uint32_t max_width = b_use_ffmpeg_demuxer ? demuxer->GetWidth() : DEFAULT_WIDTH; + uint32_t max_height = b_use_ffmpeg_demuxer ? demuxer->GetHeight() : DEFAULT_HEIGHT; if (mem_type == OUT_SURFACE_MEM_DEV_INTERNAL) mem_type = OUT_SURFACE_MEM_DEV_COPIED; // mem_type internal is not supported in this mode - viddec = new FFMpegVideoDecoder(device_id, mem_type, rocdec_codec_id, b_force_zero_latency, p_crop_rect, b_extract_sei_messages, disp_delay, true); + viddec = new FFMpegVideoDecoder(device_id, mem_type, rocdec_codec_id, b_force_zero_latency, p_crop_rect, b_extract_sei_messages, disp_delay, max_width, max_height); + #else + std::cout << "Error: RocDecode HOST library is not found and backend is not supported!" << std::endl; + return 0; + #endif } if(!viddec->CodecSupported(device_id, rocdec_codec_id, bit_depth)) { @@ -346,7 +357,8 @@ int main(int argc, char **argv) { } n_frame_returned = viddec->DecodeFrame(pvideo, n_video_bytes, pkg_flags, pts, &decoded_pics); - if (!n_frame && !viddec->GetOutputSurfaceInfo(&surf_info)) { + // get output surface info after the first decoded frame + if (!n_frame && (n_frame_returned > 0) && !viddec->GetOutputSurfaceInfo(&surf_info)) { std::cerr << "Error: Failed to get Output Surface Info!" << std::endl; break; } diff --git a/projects/rocdecode/src/rocdecode-host/avcodec/avcodec_videodecoder.cpp b/projects/rocdecode/src/rocdecode-host/avcodec/avcodec_videodecoder.cpp index dd687cf51f..9dfe161f21 100644 --- a/projects/rocdecode/src/rocdecode-host/avcodec/avcodec_videodecoder.cpp +++ b/projects/rocdecode/src/rocdecode-host/avcodec/avcodec_videodecoder.cpp @@ -341,7 +341,8 @@ int AvcodecVideoDecoder::DecodeAvFrame(AVPacket *av_pkt, AVFrame *p_frame) { //send packet to av_codec status = avcodec_send_packet(dec_context_, av_pkt); if (status < 0) { - ERR("Error sending av packet for decoding: status: "); + if (av_pkt->data && av_pkt->size) + ERR("Error sending av packet for decoding: status: "); return status; } while (status >= 0) { diff --git a/projects/rocdecode/utils/ffmpegvideodecode/ffmpeg_video_dec.cpp b/projects/rocdecode/utils/ffmpegvideodecode/ffmpeg_video_dec.cpp index d0c1d87b76..7ca7834cde 100644 --- a/projects/rocdecode/utils/ffmpegvideodecode/ffmpeg_video_dec.cpp +++ b/projects/rocdecode/utils/ffmpegvideodecode/ffmpeg_video_dec.cpp @@ -23,27 +23,7 @@ THE SOFTWARE. #include "ffmpeg_video_dec.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; - } -} - +//helper function static inline float GetChromaWidthFactor(rocDecVideoSurfaceFormat surface_format) { float factor = 0.5; switch (surface_format) { @@ -63,66 +43,44 @@ static inline float GetChromaWidthFactor(rocDecVideoSurfaceFormat surface_format return factor; }; - -/** - * @brief helper function for inferring AVCodecID from rocDecVideoSurfaceFormat - * - * @param rocdec_codec - * @return AVCodecID - */ -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 - } -} - FFMpegVideoDecoder::FFMpegVideoDecoder(int device_id, OutputSurfaceMemoryType out_mem_type, rocDecVideoCodec codec, bool force_zero_latency, - const Rect *p_crop_rect, bool extract_user_sei_Message, uint32_t disp_delay, bool no_multithreading, int max_width, int max_height, uint32_t clk_rate) : - RocVideoDecoder(device_id, out_mem_type, codec, force_zero_latency, p_crop_rect, extract_user_sei_Message, disp_delay, max_width, max_height, clk_rate), no_multithreading_(no_multithreading) { + const Rect *p_crop_rect, bool extract_user_sei_Message, uint32_t disp_delay, int max_width, int max_height, uint32_t clk_rate) : + RocVideoDecoder(device_id, out_mem_type, codec, force_zero_latency, p_crop_rect, extract_user_sei_Message, disp_delay, max_width, max_height, clk_rate) { if ((out_mem_type_ == OUT_SURFACE_MEM_DEV_INTERNAL) || (out_mem_type_ == OUT_SURFACE_MEM_NOT_MAPPED)) { - ROCDEC_THROW("Output Memory Type is not supported", ROCDEC_INVALID_PARAMETER); - } - if (rocdec_parser_) { - rocDecDestroyVideoParser(rocdec_parser_); // need to do this here since it was already created in the base class - // create rocdec videoparser - RocdecParserParams parser_params = {}; - parser_params.codec_type = codec_id_; - parser_params.max_num_decode_surfaces = 1; // let the parser to determine the decode buffer pool size - parser_params.clock_rate = clk_rate; - parser_params.max_display_delay = disp_delay_; - parser_params.user_data = this; - parser_params.pfn_sequence_callback = FFMpegHandleVideoSequenceProc; - parser_params.pfn_decode_picture = FFMpegHandlePictureDecodeProc; - parser_params.pfn_display_picture = nullptr; // HandlePictureDisplay will be controlled by FFMpegDecoder - parser_params.pfn_get_sei_msg = b_extract_sei_message_ ? RocVideoDecoder::HandleSEIMessagesProc : NULL; - ROCDEC_API_CALL(rocDecCreateVideoParser(&rocdec_parser_, &parser_params)); - } - if (!no_multithreading_) { - // start the FFMpeg decoding thread - ffmpeg_decoder_thread_ = new std::thread(&FFMpegVideoDecoder::DecodeThread, this); - if (!ffmpeg_decoder_thread_) { - ROCDEC_THROW("FFMpegVideoDecoder create thread failed", -1); - } + ROCDEC_THROW("Unsupported output memory type", ROCDEC_INVALID_PARAMETER); } + // 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 = codec; + create_info.num_decode_threads = 0; // default + create_info.max_width = max_width; + create_info.max_height = max_height; + create_info.width = max_width; + create_info.height = max_height; + create_info.target_width = max_width; + create_info.target_height = max_height; + create_info.display_rect.left = 0; + create_info.display_rect.right = static_cast(max_width); + create_info.display_rect.top = 0; + create_info.display_rect.bottom = static_cast(max_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 = this; + create_info.pfn_sequence_callback = FFMpegHandleVideoSequenceProc; + create_info.pfn_display_picture = FFMpegHandlePictureDisplayProc; + create_info.pfn_get_sei_msg = nullptr; // tobe supported in future + ROCDEC_API_CALL(rocDecCreateDecoderHost(&roc_decoder_, &create_info)); + } FFMpegVideoDecoder::~FFMpegVideoDecoder() { - std::lock_guard lock(mtx_vp_frame_); - for (auto &p_frame : vp_frames_ffmpeg_) { + for (auto &p_frame : vp_frames_) { if (p_frame.frame_ptr) { if (out_mem_type_ == OUT_SURFACE_MEM_DEV_COPIED) { hipError_t hip_status = hipFree(p_frame.frame_ptr); @@ -132,39 +90,18 @@ FFMpegVideoDecoder::~FFMpegVideoDecoder() { } } } - - // release frame_q - while (!dec_frames_.empty()) { - av_frame_free(&dec_frames_.back()); - dec_frames_.pop_back(); - } - // free av_packet_data_ - while (!av_packet_data_.empty()) { - std::pair *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(); - } - - if (dec_context_) { - avcodec_free_context(&dec_context_); - } } /* Return value from HandleVideoSequence() are interpreted as : * 0: fail, 1: succeeded, > 1: override dpb size of parser (set by RocdecParserParams::max_num_decode_surfaces while creating parser) */ -int FFMpegVideoDecoder::HandleVideoSequence(RocdecVideoFormat *p_video_format) { - if (p_video_format == nullptr) { +int FFMpegVideoDecoder::HandleVideoSequence(RocdecVideoFormatHost *format_host) { + if (format_host == nullptr) { ROCDEC_THROW("Rocdec:: Invalid video format in HandleVideoSequence: ", ROCDEC_INVALID_PARAMETER); return 0; } auto start_time = StartTimer(); + RocdecVideoFormat *p_video_format = &format_host->video_format; input_video_info_str_.str(""); input_video_info_str_.clear(); input_video_info_str_ << "Input Video Information" << std::endl @@ -181,63 +118,6 @@ int FFMpegVideoDecoder::HandleVideoSequence(RocdecVideoFormat *p_video_format) { input_video_info_str_ << std::endl; int num_decode_surfaces = p_video_format->min_num_decode_surfaces; - - // check if the codec is supported in FFMpeg - // Initialize FFMpeg and find the decoder for codec - if (!decoder_) decoder_ = avcodec_find_decoder(RocDecVideoCodec2AVCodec(p_video_format->codec)); - if(!decoder_) { - ROCDEC_THROW("rocDecode:: Codec not supported by FFMpeg ", ROCDEC_NOT_SUPPORTED); - return 0; - } - if (!dec_context_) { - dec_context_ = avcodec_alloc_context3(decoder_); //alloc dec_context_ - if (!dec_context_) { - ROCDEC_THROW("Could not allocate video codec context", ROCDEC_RUNTIME_ERROR); - } - // set codec to automatically determine how many threads suits best for the decoding job - dec_context_->thread_count = 0; - - 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) { - ROCDEC_THROW("Could not open codec", 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; - } - // 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: revisit this to fix with the correct number from seq_header - 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; - } - if (av_packet_data_.empty()) { - for (int i = 0; i < num_decode_surfaces; i++) { - uint8_t *pkt_data = static_cast (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 < num_decode_surfaces; i++) { - AVPacket *pkt = av_packet_alloc(); - pkt->data = static_cast (av_packet_data_[i].first); - pkt->size = av_packet_data_[i].second; - av_packets_.push_back(pkt); - } - } - if (curr_video_format_ptr_ == nullptr) { curr_video_format_ptr_ = new RocdecVideoFormat(); } @@ -245,8 +125,7 @@ int FFMpegVideoDecoder::HandleVideoSequence(RocdecVideoFormat *p_video_format) { if (curr_video_format_ptr_) memcpy(curr_video_format_ptr_, p_video_format, sizeof(RocdecVideoFormat)); if (coded_width_ && coded_height_) { - end_of_stream_ = false; - b_decoder_initialized = false; // reinitialize for reconfigure + // rocdecCreateDecoder() has been called before, and now there's possible config change return ReconfigureDecoder(p_video_format); } // e_codec has been set in the constructor (for parser). Here it's set again for potential correction @@ -256,7 +135,7 @@ int FFMpegVideoDecoder::HandleVideoSequence(RocdecVideoFormat *p_video_format) { byte_per_pixel_ = bitdepth_minus_8_ > 0 ? 2 : 1; // convert AVPixelFormat to rocDecVideoChromaFormat - video_surface_format_ = AVPixelFormat2rocDecVideoSurfaceFormat(decoder_pixel_format_); + video_surface_format_ = format_host->video_surface_format; coded_width_ = p_video_format->coded_width; coded_height_ = p_video_format->coded_height; disp_rect_.top = p_video_format->display_area.top; @@ -279,7 +158,7 @@ int FFMpegVideoDecoder::HandleVideoSequence(RocdecVideoFormat *p_video_format) { max_width_ = p_video_format->coded_width; if (max_height_ < static_cast(p_video_format->coded_height)) max_height_ = p_video_format->coded_height; - + if (!(crop_rect_.right && crop_rect_.bottom)) { target_width_ = (disp_width_ + 1) & ~1; target_height_ = (disp_height_ + 1) & ~1; @@ -287,13 +166,12 @@ int FFMpegVideoDecoder::HandleVideoSequence(RocdecVideoFormat *p_video_format) { target_width_ = (crop_rect_.right - crop_rect_.left + 1) & ~1; target_height_ = (crop_rect_.bottom - crop_rect_.top + 1) & ~1; } - chroma_height_ = static_cast(ceil(target_height_ * GetChromaHeightFactor(video_surface_format_))); chroma_width_ = static_cast(ceil(target_width_ * GetChromaWidthFactor(video_surface_format_))); num_chroma_planes_ = GetChromaPlaneCount(video_surface_format_); if (video_chroma_format_ == rocDecVideoChromaFormat_Monochrome) num_chroma_planes_ = 0; surface_stride_ = target_width_ * byte_per_pixel_; - chroma_vstride_ = static_cast(ceil(surface_vstride_ * GetChromaHeightFactor(video_surface_format_))); + // fill output_surface_info_ output_surface_info_.output_width = target_width_; output_surface_info_.output_height = target_height_; @@ -318,7 +196,6 @@ int FFMpegVideoDecoder::HandleVideoSequence(RocdecVideoFormat *p_video_format) { ; input_video_info_str_ << std::endl; std::cout << input_video_info_str_.str(); - double elapsed_time = StopTimer(start_time); AddDecoderSessionOverHead(std::this_thread::get_id(), elapsed_time); return num_decode_surfaces; @@ -359,20 +236,16 @@ int FFMpegVideoDecoder::ReconfigureDecoder(RocdecVideoFormat *p_video_format) { // clear the existing output buffers of different size // note that app lose the remaining frames in the vp_frames in case application didn't set p_fn_reconfigure_flush_ callback std::lock_guard lock(mtx_vp_frame_); - while(!vp_frames_ffmpeg_.empty()) { - DecFrameBufferFFMpeg *p_frame = &vp_frames_ffmpeg_.back(); + while(!vp_frames_.empty()) { + DecFrameBuffer *p_frame = &vp_frames_.back(); // pop decoded frame - vp_frames_ffmpeg_.pop_back(); + vp_frames_.pop_back(); if (p_frame->frame_ptr) { if (out_mem_type_ == OUT_SURFACE_MEM_DEV_COPIED) { hipError_t hip_status = hipFree(p_frame->frame_ptr); if (hip_status != hipSuccess) std::cerr << "ERROR: hipFree failed! (" << hip_status << ")" << std::endl; } } - // release associated av_frame - if (p_frame->av_frame_ptr) { - av_frame_free(&p_frame->av_frame_ptr); - } } output_frame_cnt_ = 0; // reset frame_count if (is_decode_res_changed) { @@ -402,7 +275,7 @@ int FFMpegVideoDecoder::ReconfigureDecoder(RocdecVideoFormat *p_video_format) { chroma_width_ = static_cast(ceil(target_width_ * GetChromaWidthFactor(video_surface_format_))); num_chroma_planes_ = GetChromaPlaneCount(video_surface_format_); if (p_video_format->chroma_format == rocDecVideoChromaFormat_Monochrome) num_chroma_planes_ = 0; - chroma_vstride_ = static_cast(std::ceil(surface_vstride_ * GetChromaHeightFactor(video_surface_format_))); + // Fill output_surface_info_ output_surface_info_.output_width = target_width_; output_surface_info_.output_height = target_height_; @@ -436,62 +309,6 @@ int FFMpegVideoDecoder::ReconfigureDecoder(RocdecVideoFormat *p_video_format) { return 1; } -/** - * @brief - * - * @param pPicParams - * @return int 1: success 0: fail - */ -int FFMpegVideoDecoder::HandlePictureDecode(RocdecPicParams *pPicParams) { - AVPacket *av_pkt = av_packets_[av_pkt_cnt_]; - std::pair *packet_data = &av_packet_data_[av_pkt_cnt_]; - if (last_packet_.payload_size > packet_data->second) { - void *new_pkt_data = av_realloc(av_pkt->data, (last_packet_.payload_size + MAX_AV_PACKET_DATA_SIZE)); // add more to avoid frequence reallocation - if (!new_pkt_data) { - std::cerr << "ERROR: couldn't allocate packet data" << std::endl; - } - packet_data->first = static_cast(new_pkt_data); - packet_data->second = (last_packet_.payload_size + MAX_AV_PACKET_DATA_SIZE); - av_pkt->data = packet_data->first; - } - memcpy(av_pkt->data, last_packet_.payload, last_packet_.payload_size); - av_pkt->size = last_packet_.payload_size; - av_pkt->flags = 0; - av_pkt->pts = last_packet_.pts; - - if (no_multithreading_) { - DecodeAvFrame(av_pkt, dec_frames_[av_frame_cnt_]); - int num_frames_to_display = decoded_pic_cnt_; - while (num_frames_to_display) { - RocdecParserDispInfo dispInfo = {0}; // dispinfo is not used in ffmpeg decoder, so setting it to zero - HandlePictureDisplay(&dispInfo); - num_frames_to_display--; - }; - if (!last_packet_.payload_size && !end_of_stream_) { - AVPacket pkt = {0}; - DecodeAvFrame(&pkt, dec_frames_[av_frame_cnt_]); - int num_frames_to_display = decoded_pic_cnt_; - while (num_frames_to_display) { - RocdecParserDispInfo dispInfo = {0}; // don't care about this as this will be igonored - HandlePictureDisplay(&dispInfo); - num_frames_to_display--; - }; - } - } else { - //push packet into packet q for decoding - PushPacket(av_pkt); - if (!last_packet_.payload_size && !end_of_stream_) { - AVPacket pkt = {0}; - PushPacket(&pkt); - } - } - av_pkt_cnt_ = (av_pkt_cnt_ + 1) % av_packets_.size(); - if (!av_pkt->data || !av_pkt->size) { - end_of_stream_ = true; - } - - return 1; -} /** * @brief function to handle display picture @@ -532,38 +349,22 @@ int FFMpegVideoDecoder::HandlePictureDisplay(RocdecParserDispInfo *pDispInfo) { sei_message_display_q_[pDispInfo->picture_index].sei_message = NULL; // to avoid double free } } - // this will happen during PopFrame() - AVFrame *p_av_frame = nullptr; - if (no_multithreading_ ) { - if (!av_frame_q_.empty()) { - p_av_frame = av_frame_q_.front(); - av_frame_q_.pop(); - } - } else { - p_av_frame = PopFrame(); - } - - if (p_av_frame == nullptr) { - std::cerr << "Invalid avframe decode output" << std::endl; - return 0; - } - void* src_ptr[3] = {0}; - int32_t src_pitch[3] = {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]; - src_pitch[1] = p_av_frame->linesize[1]; - src_pitch[2] = p_av_frame->linesize[2]; + RocdecParserDispInfo *p_disp_info = static_cast(pDispInfo); + RocdecProcParams video_proc_params = {}; + video_proc_params.progressive_frame = p_disp_info->progressive_frame; + video_proc_params.top_field_first = p_disp_info->top_field_first; + void * src_ptr[3] = { 0 }; + uint32_t src_pitch[3] = { 0 }; + ROCDEC_API_CALL(rocDecGetVideoFrameHost(roc_decoder_, pDispInfo->picture_index, src_ptr, src_pitch, &video_proc_params)); // copy the decoded surface info device or host uint8_t *p_dec_frame = nullptr; { std::lock_guard lock(mtx_vp_frame_); // if not enough frames in stock, allocate - if (++output_frame_cnt_ > vp_frames_ffmpeg_.size()) { + if (++output_frame_cnt_ > vp_frames_.size()) { num_alloced_frames_++; - DecFrameBufferFFMpeg dec_frame = {0}; + DecFrameBuffer dec_frame = {0}; if (out_mem_type_ == OUT_SURFACE_MEM_DEV_COPIED) { // allocate device memory HIP_API_CALL(hipMalloc((void **)&dec_frame.frame_ptr, GetFrameSize())); @@ -571,12 +372,11 @@ int FFMpegVideoDecoder::HandlePictureDisplay(RocdecParserDispInfo *pDispInfo) { dec_frame.frame_ptr = new uint8_t[GetFrameSize()]; } - dec_frame.av_frame_ptr = p_av_frame; - dec_frame.pts = p_av_frame->pts; - dec_frame.picture_index = -1; //picture_index is not used here since it is handled within FFMpeg decoder - vp_frames_ffmpeg_.push_back(dec_frame); + dec_frame.pts = pDispInfo->pts; + dec_frame.picture_index = pDispInfo->picture_index; //picture_index is not used here since it is handled within FFMpeg decoder + vp_frames_.push_back(dec_frame); } - p_dec_frame = vp_frames_ffmpeg_[output_frame_cnt_ - 1].frame_ptr; + p_dec_frame = vp_frames_[output_frame_cnt_ - 1].frame_ptr; } // Copy luma data @@ -607,7 +407,7 @@ int FFMpegVideoDecoder::HandlePictureDisplay(RocdecParserDispInfo *pDispInfo) { } } } - // Copy chroma plane ( ) + // Copy chroma plane/s // rocDec output gives pointer to luma and chroma pointers seperated for the decoded frame uint8_t *p_frame_uv = p_dec_frame + dst_pitch * disp_height_; uint8_t *p_src_ptr_uv = static_cast(src_ptr[1]) + ((disp_rect_.top + crop_rect_.top) >> 1) * src_pitch[1] + ((disp_rect_.left + crop_rect_.left)>>1) * byte_per_pixel_ ; @@ -669,22 +469,15 @@ int FFMpegVideoDecoder::HandlePictureDisplay(RocdecParserDispInfo *pDispInfo) { int FFMpegVideoDecoder::DecodeFrame(const uint8_t *data, size_t size, int pkt_flags, int64_t pts, int *num_decoded_pics) { output_frame_cnt_ = 0, output_frame_cnt_ret_ = 0; decoded_pic_cnt_ = 0; - last_packet_ = {0}; - last_packet_.payload = data; - last_packet_.payload_size = size; - last_packet_.flags = pkt_flags | ROCDEC_PKT_TIMESTAMP; - last_packet_.pts = pts; + RocdecPicParamsHost pic_params = {}; + pic_params.bitstream_data_len = size; + pic_params.bitstream_data = data; if (!data || size == 0) { - last_packet_.flags |= ROCDEC_PKT_ENDOFSTREAM; + pic_params.flags = ROCDEC_PKT_ENDOFPICTURE; // mark end_of_picture flag for last frame } - ROCDEC_API_CALL(rocDecParseVideoData(rocdec_parser_, &last_packet_)); - if (last_packet_.flags & ROCDEC_PKT_ENDOFSTREAM) { - // flush last packet and let FFMpeg decode last frames - FlushDecoder(); - } - + ROCDEC_API_CALL(rocDecDecodeFrameHost(roc_decoder_, &pic_params)); if (num_decoded_pics) { - *num_decoded_pics = decoded_pic_cnt_; + *num_decoded_pics = output_frame_cnt_; } return output_frame_cnt_; } @@ -693,10 +486,10 @@ int FFMpegVideoDecoder::DecodeFrame(const uint8_t *data, size_t size, int pkt_fl uint8_t* FFMpegVideoDecoder::GetFrame(int64_t *pts) { if (output_frame_cnt_ > 0) { std::lock_guard lock(mtx_vp_frame_); - if (vp_frames_ffmpeg_.size() > 0){ + if (vp_frames_.size() > 0){ output_frame_cnt_--; - if (pts) *pts = vp_frames_ffmpeg_[output_frame_cnt_ret_].pts; - return vp_frames_ffmpeg_[output_frame_cnt_ret_++].frame_ptr; + if (pts) *pts = vp_frames_[output_frame_cnt_ret_].pts; + return vp_frames_[output_frame_cnt_ret_++].frame_ptr; } } return nullptr; @@ -704,116 +497,21 @@ uint8_t* FFMpegVideoDecoder::GetFrame(int64_t *pts) { bool FFMpegVideoDecoder::ReleaseFrame(int64_t pTimestamp, bool b_flushing) { // if not flushing the buffers are re-used, so keep them - if (out_mem_type_ == OUT_SURFACE_MEM_NOT_MAPPED || !b_flushing) - return true; // nothing to do if (!b_flushing) return true; else { // remove frames in flushing mode std::lock_guard lock(mtx_vp_frame_); - DecFrameBufferFFMpeg *fb = &vp_frames_ffmpeg_[0]; + DecFrameBuffer *fb = &vp_frames_[0]; if (pTimestamp != fb->pts) { std::cerr << "Decoded Frame is released out of order" << std::endl; return false; } - av_frame_free(&fb->av_frame_ptr); - vp_frames_ffmpeg_.erase(vp_frames_ffmpeg_.begin()); // get rid of the frames from the framestore + vp_frames_.erase(vp_frames_.begin()); // get rid of the frames from the framestore } return true; } -void FFMpegVideoDecoder::InitOutputFrameInfo(AVFrame *p_frame) { - - video_surface_format_ = AVPixelFormat2rocDecVideoSurfaceFormat((AVPixelFormat)p_frame->format); - surface_stride_ = target_width_ * byte_per_pixel_; - chroma_width_ = static_cast(ceil(target_width_ * GetChromaWidthFactor(video_surface_format_))); - chroma_height_ = static_cast(ceil(target_height_ * GetChromaHeightFactor(video_surface_format_))); - num_chroma_planes_ = GetChromaPlaneCount(video_surface_format_); - // Fill output_surface_info_ - output_surface_info_.output_width = target_width_; - output_surface_info_.output_height = target_height_; - output_surface_info_.output_pitch = surface_stride_; - output_surface_info_.output_vstride = target_height_; - output_surface_info_.bit_depth = bitdepth_minus_8_ + 8; - output_surface_info_.bytes_per_pixel = byte_per_pixel_; - output_surface_info_.surface_format = video_surface_format_; - output_surface_info_.num_chroma_planes = num_chroma_planes_; - if (out_mem_type_ == OUT_SURFACE_MEM_DEV_COPIED) { - output_surface_info_.output_surface_size_in_bytes = GetFrameSize(); - output_surface_info_.mem_type = OUT_SURFACE_MEM_DEV_COPIED; - } else if (out_mem_type_ == OUT_SURFACE_MEM_HOST_COPIED) { - output_surface_info_.output_surface_size_in_bytes = GetFrameSize(); - output_surface_info_.mem_type = OUT_SURFACE_MEM_HOST_COPIED; - } -} - -/** - * @brief Flush decoder with decoding of last frames - * - * @return int 1: success 0: fail - */ -int FFMpegVideoDecoder::FlushDecoder() { - AVPacket pkt = {0}; - if (no_multithreading_) { - DecodeAvFrame(&pkt, dec_frames_[av_frame_cnt_]); - int num_frames_to_display = decoded_pic_cnt_; - while (num_frames_to_display) { - RocdecParserDispInfo dispInfo = {0}; // don't care about this as this will be igonored - HandlePictureDisplay(&dispInfo); - num_frames_to_display--; - }; - - } else { - //push packet into packet q for decoding - PushPacket(&pkt); - } - return 0; -} - - -void FFMpegVideoDecoder::DecodeThread() -{ - AVPacket *pkt; - do { - pkt = PopPacket(); - DecodeAvFrame(pkt, dec_frames_[av_frame_cnt_]); - } while (!end_of_stream_); -} - -int FFMpegVideoDecoder::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) { - std::cout << "Error sending av packet for decoding: status: " << status << std::endl; - } - while (status >= 0) { - status = avcodec_receive_frame(dec_context_, p_frame); - if (status == AVERROR(EAGAIN) || status == AVERROR_EOF) { - end_of_stream_ = (status == AVERROR_EOF); - return 0; - } - else if (status < 0) { - std::cout << "Error during decoding" << std::endl; - return 0; - } - // for the first frame, initialize OutputsurfaceInfo - if (!b_decoder_initialized) { - InitOutputFrameInfo(p_frame); - b_decoder_initialized = true; - } - decoded_pic_cnt_++; - if (no_multithreading_) - av_frame_q_.push(p_frame); - else - PushFrame(p_frame); // add frame to the frame_q - av_frame_cnt_ = (av_frame_cnt_ + 1) % dec_frames_.size(); - p_frame = dec_frames_[av_frame_cnt_]; //advance for next frame decode - } - return 0; -} - - void FFMpegVideoDecoder::SaveFrameToFile(std::string output_file_name, void *surf_mem, OutputSurfaceInfo *surf_info, size_t rgb_image_size) { uint8_t *hst_ptr = nullptr; bool is_rgb = (rgb_image_size != 0); diff --git a/projects/rocdecode/utils/ffmpegvideodecode/ffmpeg_video_dec.h b/projects/rocdecode/utils/ffmpegvideodecode/ffmpeg_video_dec.h index b524d3fdb0..fd567c0ac8 100644 --- a/projects/rocdecode/utils/ffmpegvideodecode/ffmpeg_video_dec.h +++ b/projects/rocdecode/utils/ffmpegvideodecode/ffmpeg_video_dec.h @@ -31,28 +31,11 @@ extern "C" { #endif } #include "rocvideodecode/roc_video_dec.h" // for derived class +#include "rocdecode/rocdecode_host.h" -#include -#include -#include -#include - - -#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/device 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; - +/** + * FFMpegVideoDecoder: Derived class for FFMpeg based host decoder + */ class FFMpegVideoDecoder: public RocVideoDecoder { public: /** @@ -71,7 +54,7 @@ class FFMpegVideoDecoder: public RocVideoDecoder { * @param no_multithreading : run FFMpeg decoder in the main thread (no multithreading) */ FFMpegVideoDecoder(int num_threads, OutputSurfaceMemoryType out_mem_type, rocDecVideoCodec codec, bool force_zero_latency = false, - const Rect *p_crop_rect = nullptr, bool extract_user_SEI_Message = false, uint32_t disp_delay = 0, bool no_multithreading = false, int max_width = 0, int max_height = 0, + const Rect *p_crop_rect = nullptr, bool extract_user_SEI_Message = false, uint32_t disp_delay = 0, int max_width = 0, int max_height = 0, uint32_t clk_rate = 1000); /** * @brief destructor @@ -126,12 +109,7 @@ class FFMpegVideoDecoder: public RocVideoDecoder { /** * @brief Callback function to be registered for getting a callback when decoding of sequence starts */ - static int ROCDECAPI FFMpegHandleVideoSequenceProc(void *p_user_data, RocdecVideoFormat *p_video_format) { return ((FFMpegVideoDecoder *)p_user_data)->HandleVideoSequence(p_video_format); } - - /** - * @brief Callback function to be registered for getting a callback when a decoded frame is ready to be decoded - */ - static int ROCDECAPI FFMpegHandlePictureDecodeProc(void *p_user_data, RocdecPicParams *p_pic_params) { return ((FFMpegVideoDecoder *)p_user_data)->HandlePictureDecode(p_pic_params); } + static int ROCDECAPI FFMpegHandleVideoSequenceProc(void *p_user_data, RocdecVideoFormatHost *p_video_format) { return ((FFMpegVideoDecoder *)p_user_data)->HandleVideoSequence(p_video_format); } /** * @brief Callback function to be registered for getting a callback when a decoded frame is available for display @@ -147,13 +125,7 @@ class FFMpegVideoDecoder: public RocVideoDecoder { * @brief This function gets called when a sequence is ready to be decoded. The function also gets called when there is format change */ - int HandleVideoSequence(RocdecVideoFormat *p_video_format); - - /** - * @brief This function gets called when a picture is ready to be decoded. rocDecDecodeFrame is called from this function - * to decode the picture - */ - int HandlePictureDecode(RocdecPicParams *p_pic_params); + int HandleVideoSequence(RocdecVideoFormatHost *p_video_format); /** * @brief This function gets called after a picture is decoded and available for display. Frames are fetched and stored in @@ -171,72 +143,4 @@ class FFMpegVideoDecoder: public RocVideoDecoder { */ int ReconfigureDecoder(RocdecVideoFormat *p_video_format); - void DecodeThread(); - int DecodeAvFrame(AVPacket *av_pkt, AVFrame *p_frame); - void InitOutputFrameInfo(AVFrame *p_frame); - int FlushDecoder(); - void PushPacket(AVPacket *pkt) { - { - std::lock_guard lock(mtx_pkt_q_); - av_packet_q_.push(pkt); - } - cv_pkt_.notify_one(); - } - - AVPacket *PopPacket() { - AVPacket *pkt; - std::unique_lock lock(mtx_pkt_q_); - cv_pkt_.wait(lock, [&] { return !av_packet_q_.empty(); }); - pkt = av_packet_q_.front(); - av_packet_q_.pop(); - return pkt; - } - - void PushFrame(AVFrame *av_frame) { - { - std::lock_guard lock(mtx_frame_q_); - av_frame_q_.push(av_frame); - } - cv_frame_.notify_one(); - }; - - AVFrame *PopFrame() { - std::unique_lock lock(mtx_frame_q_); - cv_frame_.wait(lock, [&] { return !av_frame_q_.empty() || end_of_stream_; }); - if (end_of_stream_ && av_frame_q_.empty()) - return nullptr; - AVFrame *p_frame = av_frame_q_.front(); - av_frame_q_.pop(); - return p_frame; - } - - typedef enum { CMD_ABORT, CMD_DECODE } CommandType; - typedef enum { STATUS_SUCCESS = 0, STATUS_FAILURE = -1 } StatusType; - - bool no_multithreading_ = false; - bool b_decoder_initialized = false; - uint32_t av_frame_cnt_ = 0; - uint32_t av_pkt_cnt_ = 0; - RocdecSourceDataPacket last_packet_; - std::thread *ffmpeg_decoder_thread_ = nullptr; - std::queue av_packet_q_; // queue for compressed packets - std::queue av_frame_q_; - std::vector vp_frames_ffmpeg_; // vector of decoded frames - std::vector dec_frames_; // vector of AVFrame * for decoded frames - std::vector av_packets_; // store of AVPackets for decoding - std::vector> av_packet_data_; - std::mutex mtx_pkt_q_, mtx_frame_q_; //for command and status - std::condition_variable cv_pkt_, cv_frame_; //for command and status - std::atomic 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; - AVStream *video = nullptr; };