diff --git a/projects/rocdecode/samples/videoDecode/README.md b/projects/rocdecode/samples/videoDecode/README.md index b186baafa3..afa58ce907 100644 --- a/projects/rocdecode/samples/videoDecode/README.md +++ b/projects/rocdecode/samples/videoDecode/README.md @@ -32,6 +32,7 @@ make -j -d -f -z + -disp_delay -sei -md5 -md5_check MD5_File_Path diff --git a/projects/rocdecode/samples/videoDecode/videodecode.cpp b/projects/rocdecode/samples/videoDecode/videodecode.cpp index 457d872138..8ea2646d26 100644 --- a/projects/rocdecode/samples/videoDecode/videodecode.cpp +++ b/projects/rocdecode/samples/videoDecode/videodecode.cpp @@ -47,6 +47,7 @@ void ShowHelpAndExit(const char *option = NULL) { << "-d GPU device ID (0 for the first device, 1 for the second, etc.); optional; default: 0" << std::endl << "-f Number of decoded frames - specify the number of pictures to be decoded; optional" << std::endl << "-z force_zero_latency (force_zero_latency, Decoded frames will be flushed out for display immediately); optional;" << std::endl + << "-disp_delay -specify the number of frames to be delayed for display; optional;" << std::endl << "-sei extract SEI messages; optional;" << std::endl << "-md5 generate MD5 message digest on the decoded YUV image sequence; optional;" << std::endl << "-md5_check MD5 File Path - generate MD5 message digest on the decoded YUV image sequence and compare to the reference MD5 string in a file; optional;" << std::endl @@ -66,6 +67,7 @@ int main(int argc, char **argv) { std::fstream ref_md5_file; int dump_output_frames = 0; int device_id = 0; + int disp_delay = 0; bool b_force_zero_latency = false; // false by default: enabling this option might affect decoding performance bool b_extract_sei_messages = false; bool b_generate_md5 = false; @@ -111,6 +113,13 @@ int main(int argc, char **argv) { device_id = atoi(argv[i]); continue; } + if (!strcmp(argv[i], "-disp_delay")) { + if (++i == argc) { + ShowHelpAndExit("-disp_delay"); + } + disp_delay = atoi(argv[i]); + continue; + } if (!strcmp(argv[i], "-f")) { if (++i == argc) { ShowHelpAndExit("-d"); @@ -197,7 +206,7 @@ int main(int argc, char **argv) { VideoDemuxer demuxer(input_file_path.c_str()); VideoSeekContext video_seek_ctx; rocDecVideoCodec rocdec_codec_id = AVCodec2RocDecVideoCodec(demuxer.GetCodecID()); - RocVideoDecoder viddec(device_id, mem_type, rocdec_codec_id, b_force_zero_latency, p_crop_rect, b_extract_sei_messages); + RocVideoDecoder viddec(device_id, mem_type, rocdec_codec_id, b_force_zero_latency, p_crop_rect, b_extract_sei_messages, disp_delay); std::string device_name, gcn_arch_name; int pci_bus_id, pci_domain_id, pci_device_id; diff --git a/projects/rocdecode/src/parser/avc_defines.h b/projects/rocdecode/src/parser/avc_defines.h index c33546f0ac..72000584d0 100644 --- a/projects/rocdecode/src/parser/avc_defines.h +++ b/projects/rocdecode/src/parser/avc_defines.h @@ -33,7 +33,7 @@ THE SOFTWARE. #define AVC_MAX_REF_FRAME_NUM 16 #define AVC_MAX_REF_PICTURE_NUM 32 -#define AVC_MAX_DPB_FRAMES 18 +#define AVC_MAX_DPB_FRAMES 16 #define AVC_MAX_DPB_FIELDS AVC_MAX_DPB_FRAMES * 2 #define AVC_MACRO_BLOCK_SIZE 16 diff --git a/projects/rocdecode/src/parser/avc_parser.cpp b/projects/rocdecode/src/parser/avc_parser.cpp index 25b2fb9b0e..b9807de2d1 100644 --- a/projects/rocdecode/src/parser/avc_parser.cpp +++ b/projects/rocdecode/src/parser/avc_parser.cpp @@ -24,6 +24,9 @@ THE SOFTWARE. #include "avc_parser.h" AvcVideoParser::AvcVideoParser() { + active_sps_id_ = -1; + active_pps_id_ = -1; + prev_pic_order_cnt_msb_ = 0; prev_pic_order_cnt_lsb_ = 0; prev_top_field_order_cnt_ = 0; @@ -42,6 +45,7 @@ AvcVideoParser::AvcVideoParser() { field_pic_count_ = 0; second_field_ = 0; first_field_pic_idx_ = 0; + first_field_dec_buf_idx_ = 0; InitDpb(); } @@ -59,9 +63,6 @@ rocDecStatus AvcVideoParser::UnInitialize() { rocDecStatus AvcVideoParser::ParseVideoData(RocdecSourceDataPacket *p_data) { if (p_data->payload && p_data->payload_size) { - // Clear DPB output/display buffer number - dpb_buffer_.num_output_pics = 0; - if (ParsePictureData(p_data->payload, p_data->payload_size) != PARSER_OK) { ERR(STR("Parser failed!")); return ROCDEC_RUNTIME_ERROR; @@ -86,8 +87,8 @@ rocDecStatus AvcVideoParser::ParseVideoData(RocdecSourceDataPacket *p_data) { } // Output decoded pictures from DPB if any are ready in case of frame_num gaps. - if (pfn_display_picture_cb_ && dpb_buffer_.num_output_pics > 0) { - if (OutputDecodedPictures() != PARSER_OK) { + if (pfn_display_picture_cb_ && num_output_pics_ > 0) { + if (OutputDecodedPictures(false) != PARSER_OK) { return ROCDEC_RUNTIME_ERROR; } } @@ -237,9 +238,9 @@ ParserResult AvcVideoParser::ParsePictureData(const uint8_t *p_stream, uint32_t } if (num_slices_ == 0) { - // Find a free buffer in DPB for the current picture. Due to the current 1-1 mapping of DPB and - // decoded buffer pool at VAAP level, we need to get a surface from DPB for the current picture to be - // decoded into. + if ((ret2 = FindFreeInDecBufPool()) != PARSER_OK) { + return ret2; + } if ((ret2 = FindFreeBufInDpb()) != PARSER_OK) { return ret2; } @@ -299,7 +300,7 @@ ParserResult AvcVideoParser::NotifyNewSps(AvcSeqParameterSet *p_sps) { video_format_params_.bit_depth_luma_minus8 = p_sps->bit_depth_luma_minus8; video_format_params_.bit_depth_chroma_minus8 = p_sps->bit_depth_chroma_minus8; video_format_params_.progressive_sequence = p_sps->frame_mbs_only_flag ? 1 : 0; - video_format_params_.min_num_decode_surfaces = dpb_buffer_.dpb_size; + video_format_params_.min_num_decode_surfaces = dec_buf_pool_size_; video_format_params_.coded_width = pic_width_; video_format_params_.coded_height = pic_height_; video_format_params_.chroma_format = static_cast(p_sps->chroma_format_idc); @@ -408,7 +409,7 @@ void AvcVideoParser::SendSeiMsgPayload() { 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_.pic_idx; + sei_message_info_params_.picIdx = curr_pic_.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_); @@ -441,7 +442,7 @@ ParserResult AvcVideoParser::SendPicForDecode() { dec_pic_params_.pic_width = pic_width_; dec_pic_params_.pic_height = pic_height_; - dec_pic_params_.curr_pic_idx = curr_pic_.pic_idx; + dec_pic_params_.curr_pic_idx = curr_pic_.dec_buf_idx; dec_pic_params_.field_pic_flag = p_slice_header->field_pic_flag; dec_pic_params_.bottom_field_flag = p_slice_header->bottom_field_flag; dec_pic_params_.second_field = second_field_; @@ -457,7 +458,7 @@ ParserResult AvcVideoParser::SendPicForDecode() { RocdecAvcPicParams *p_pic_param = &dec_pic_params_.pic_params.avc; // Current picture - p_pic_param->curr_pic.pic_idx = curr_pic_.pic_idx; + p_pic_param->curr_pic.pic_idx = curr_pic_.dec_buf_idx; if (curr_pic_.is_reference == kUsedForLongTerm) { p_pic_param->curr_pic.frame_idx = curr_pic_.long_term_pic_num; } else { @@ -479,7 +480,7 @@ ParserResult AvcVideoParser::SendPicForDecode() { if (p_slice_header->field_pic_flag == 0) { // frame picture AvcPicture *p_ref_pic = &dpb_buffer_.frame_buffer_list[i]; if (p_ref_pic->is_reference != kUnusedForReference) { - p_pic_param->ref_frames[buf_index].pic_idx = p_ref_pic->pic_idx; + p_pic_param->ref_frames[buf_index].pic_idx = p_ref_pic->dec_buf_idx; if ( p_ref_pic->is_reference == kUsedForLongTerm) { p_pic_param->ref_frames[buf_index].frame_idx = p_ref_pic->long_term_pic_num; } else { @@ -500,7 +501,7 @@ ParserResult AvcVideoParser::SendPicForDecode() { for (int j = 0; j < 2; j++) { AvcPicture *p_field_pic = &dpb_buffer_.field_pic_list[i * 2 + j]; if (p_field_pic->is_reference != kUnusedForReference) { - p_ref_frame->pic_idx = p_field_pic->pic_idx; + p_ref_frame->pic_idx = p_field_pic->dec_buf_idx; if (p_field_pic->is_reference == kUsedForLongTerm) { p_ref_frame->frame_idx = p_field_pic->long_term_frame_idx; } else { @@ -611,7 +612,7 @@ ParserResult AvcVideoParser::SendPicForDecode() { for (i = 0; i <= p_slice_info->slice_header.num_ref_idx_l0_active_minus1; i++) { AvcPicture *p_ref_pic = &p_slice_info->ref_list_0_[i]; if (p_ref_pic->is_reference != kUnusedForReference) { - p_slice_param->ref_pic_list_0[i].pic_idx = p_ref_pic->pic_idx; + p_slice_param->ref_pic_list_0[i].pic_idx = p_ref_pic->dec_buf_idx; if ( p_ref_pic->is_reference == kUsedForLongTerm) { p_slice_param->ref_pic_list_0[i].frame_idx = p_ref_pic->long_term_pic_num; } else { @@ -632,7 +633,7 @@ ParserResult AvcVideoParser::SendPicForDecode() { for (i = 0; i <= p_slice_info->slice_header.num_ref_idx_l1_active_minus1; i++) { AvcPicture *p_ref_pic = &p_slice_info->ref_list_1_[i]; if (p_ref_pic->is_reference != kUnusedForReference) { - p_slice_param->ref_pic_list_1[i].pic_idx = p_ref_pic->pic_idx; + p_slice_param->ref_pic_list_1[i].pic_idx = p_ref_pic->dec_buf_idx; if ( p_ref_pic->is_reference == kUsedForLongTerm) { p_slice_param->ref_pic_list_1[i].frame_idx = p_ref_pic->long_term_pic_num; } else { @@ -709,20 +710,6 @@ ParserResult AvcVideoParser::SendPicForDecode() { } } -ParserResult AvcVideoParser::OutputDecodedPictures() { - RocdecParserDispInfo disp_info = {0}; - disp_info.progressive_frame = sps_list_[active_sps_id_].frame_mbs_only_flag; - disp_info.top_field_first = slice_info_list_[0].slice_header.bottom_field_flag && (pic_count_ & 1); - - for (int i = 0; i < dpb_buffer_.num_output_pics; i++) { - disp_info.picture_index = dpb_buffer_.frame_buffer_list[dpb_buffer_.output_pic_list[i]].pic_idx; - pfn_display_picture_cb_(parser_params_.user_data, &disp_info); - } - - dpb_buffer_.num_output_pics = 0; - return PARSER_OK; -} - AvcNalUnitHeader AvcVideoParser::ParseNalUnitHeader(uint8_t header_byte) { size_t bit_offset = 0; AvcNalUnitHeader nal_header; @@ -1192,7 +1179,7 @@ ParserResult AvcVideoParser::ParseSliceHeader(uint8_t *p_stream, size_t stream_s return PARSER_WRONG_STATE; } // Re-set DPB size. - dpb_buffer_.dpb_size = p_sps->max_num_ref_frames + 3; + dpb_buffer_.dpb_size = p_sps->max_num_ref_frames + 1; dpb_buffer_.dpb_size = dpb_buffer_.dpb_size > AVC_MAX_DPB_FRAMES ? AVC_MAX_DPB_FRAMES : dpb_buffer_.dpb_size; new_sps_activated_ = true; // Note: clear this flag after the actions are taken. } @@ -1206,11 +1193,16 @@ ParserResult AvcVideoParser::ParseSliceHeader(uint8_t *p_stream, size_t stream_s pic_height_ = curr_pic_height; // Take care of the case where a new SPS replaces the old SPS with the same id but with different dimensions // Re-set DPB size. - dpb_buffer_.dpb_size = p_sps->max_num_ref_frames + 3; + dpb_buffer_.dpb_size = p_sps->max_num_ref_frames + 1; dpb_buffer_.dpb_size = dpb_buffer_.dpb_size > AVC_MAX_DPB_FRAMES ? AVC_MAX_DPB_FRAMES : dpb_buffer_.dpb_size; new_sps_activated_ = true; // Note: clear this flag after the actions are taken. } + // Check and adjust decode buffer pool size if needed + if (new_sps_activated_) { + CheckAndAdjustDecBufPoolSize(dpb_buffer_.dpb_size); + } + // Set frame rate if available if (new_sps_activated_) { if (p_sps->vui_seq_parameters.timing_info_present_flag) { @@ -1626,14 +1618,14 @@ void AvcVideoParser::InitDpb() { for (int i = 0; i < AVC_MAX_DPB_FRAMES; i++) { dpb_buffer_.frame_buffer_list[i].pic_idx = i; dpb_buffer_.frame_buffer_list[i].is_reference = kUnusedForReference; - dpb_buffer_.frame_buffer_list[i].use_status = 0; + dpb_buffer_.frame_buffer_list[i].use_status = kNotUsed; dpb_buffer_.frame_buffer_list[i].pic_output_flag = 0; dpb_buffer_.field_pic_list[i * 2].pic_idx = i; dpb_buffer_.field_pic_list[i * 2 + 1].pic_idx = i; dpb_buffer_.field_pic_list[i * 2].is_reference = kUnusedForReference; dpb_buffer_.field_pic_list[i * 2 + 1].is_reference = kUnusedForReference; - dpb_buffer_.field_pic_list[i * 2].use_status = 0; - dpb_buffer_.field_pic_list[i * 2 + 1].use_status = 0; + dpb_buffer_.field_pic_list[i * 2].use_status = kNotUsed; + dpb_buffer_.field_pic_list[i * 2 + 1].use_status = kNotUsed; dpb_buffer_.field_pic_list[i * 2].pic_output_flag = 0; dpb_buffer_.field_pic_list[i * 2 + 1].pic_output_flag = 0; } @@ -1644,7 +1636,6 @@ void AvcVideoParser::InitDpb() { dpb_buffer_.num_short_term_ref_fields = 0; dpb_buffer_.num_long_term_ref_fields = 0; dpb_buffer_.num_pics_needed_for_output = 0; - dpb_buffer_.num_output_pics = 0; } // 8.2.1 Decoding process for picture order count @@ -1962,13 +1953,13 @@ ParserResult AvcVideoParser::DecodeFrameNumGaps() { } for (i = 0; i < dpb_buffer_.dpb_size; i++) { - if (dpb_buffer_.frame_buffer_list[i].use_status == 0) { + if (dpb_buffer_.frame_buffer_list[i].use_status == kNotUsed) { break; } } if (i < dpb_buffer_.dpb_size) { non_existing_pic.pic_idx = dpb_buffer_.frame_buffer_list[i].pic_idx; - non_existing_pic.use_status = 3; + non_existing_pic.use_status = kFrameUsedForDecode; dpb_buffer_.frame_buffer_list[i] = non_existing_pic; dpb_buffer_.dpb_fullness++; dpb_buffer_.num_short_term++; @@ -2566,14 +2557,40 @@ ParserResult AvcVideoParser::CheckDpbAndOutput() { } } // Output decoded pictures from DPB if any are ready - if (pfn_display_picture_cb_ && dpb_buffer_.num_output_pics > 0) { - if (OutputDecodedPictures() != PARSER_OK) { + if (pfn_display_picture_cb_ && num_output_pics_ > 0) { + if (OutputDecodedPictures(false) != PARSER_OK) { return PARSER_FAIL; } } return PARSER_OK; } +ParserResult AvcVideoParser::FindFreeInDecBufPool() { + int dec_buf_index; + + if (curr_pic_.pic_structure == kFrame || !second_field_) { + // Find a free buffer in decode buffer pool + for (dec_buf_index = 0; dec_buf_index < dec_buf_pool_size_; dec_buf_index++) { + if (decode_buffer_pool_[dec_buf_index].use_status == kNotUsed) { + break; + } + } + if (dec_buf_index == dec_buf_pool_size_) { + ERR("Could not find a free buffer in decode buffer pool."); + return PARSER_NOT_FOUND; + } + + curr_pic_.dec_buf_idx = dec_buf_index; + if ( curr_pic_.pic_structure != kFrame) { + first_field_dec_buf_idx_ = dec_buf_index; + } + } else { + curr_pic_.dec_buf_idx = first_field_dec_buf_idx_; + } + + return PARSER_OK; +} + ParserResult AvcVideoParser::FindFreeBufInDpb() { int i; if (curr_pic_.pic_structure == kFrame || !second_field_) { @@ -2584,18 +2601,18 @@ ParserResult AvcVideoParser::FindFreeBufInDpb() { } for (i = 0; i < dpb_buffer_.dpb_size; i++) { - if (dpb_buffer_.frame_buffer_list[i].use_status == 0) { + if (dpb_buffer_.frame_buffer_list[i].use_status == kNotUsed) { break; } } if (i < dpb_buffer_.dpb_size) { curr_pic_.pic_idx = dpb_buffer_.frame_buffer_list[i].pic_idx; if (curr_pic_.pic_structure == kFrame) { - curr_pic_.use_status = 3; + curr_pic_.use_status = kFrameUsedForDecode; } else if (curr_pic_.pic_structure == kTopField) { - curr_pic_.use_status = 1; + curr_pic_.use_status = kTopFieldUsedForDecode; } else { - curr_pic_.use_status = 2; + curr_pic_.use_status = kBottomFieldUsedForDecode; } } else { ERR("Could not find any free frame buffer in DPB."); @@ -2608,9 +2625,9 @@ ParserResult AvcVideoParser::FindFreeBufInDpb() { } else { curr_pic_.pic_idx = first_field_pic_idx_; if (curr_pic_.pic_structure == kTopField) { - curr_pic_.use_status = 1; + curr_pic_.use_status = kTopFieldUsedForDecode; } else { - curr_pic_.use_status = 2; + curr_pic_.use_status = kBottomFieldUsedForDecode; } } @@ -2982,12 +2999,14 @@ ParserResult AvcVideoParser::BumpPicFromDpb() { if (dpb_buffer_.num_pics_needed_for_output > 0) { dpb_buffer_.num_pics_needed_for_output--; // Insert into output/display picture list - if (dpb_buffer_.num_output_pics >= AVC_MAX_DPB_FRAMES) { - ERR("Error! DPB output buffer list overflow!"); - return PARSER_OUT_OF_RANGE; - } else { - dpb_buffer_.output_pic_list[dpb_buffer_.num_output_pics] = min_poc_pic_idx_ref; - dpb_buffer_.num_output_pics++; + if (pfn_display_picture_cb_) { + if (num_output_pics_ >= dec_buf_pool_size_) { + ERR("Error! Decode buffer pool overflow!"); + return PARSER_OUT_OF_RANGE; + } else { + output_pic_list_[num_output_pics_] = dpb_buffer_.frame_buffer_list[min_poc_pic_idx_ref].dec_buf_idx; + num_output_pics_++; + } } } min_poc_ref = 0x7FFFFFFF; @@ -3008,16 +3027,19 @@ ParserResult AvcVideoParser::BumpPicFromDpb() { } // Insert into output/display picture list - if (dpb_buffer_.num_output_pics >= AVC_MAX_DPB_FRAMES) { - ERR("Error! DPB output buffer list overflow!"); - return PARSER_OUT_OF_RANGE; - } else { - dpb_buffer_.output_pic_list[dpb_buffer_.num_output_pics] = min_poc_pic_idx_no_ref; - dpb_buffer_.num_output_pics++; + if (pfn_display_picture_cb_) { + if (num_output_pics_ >= dec_buf_pool_size_) { + ERR("Error! Decode buffer pool overflow!"); + return PARSER_OUT_OF_RANGE; + } else { + output_pic_list_[num_output_pics_] = dpb_buffer_.frame_buffer_list[min_poc_pic_idx_no_ref].dec_buf_idx; + num_output_pics_++; + } } } - // Remove it from DPB. - dpb_buffer_.frame_buffer_list[min_poc_pic_idx_no_ref].use_status = 0; + // Remove it from DPB and mark unused for decode in decode buffer pool + dpb_buffer_.frame_buffer_list[min_poc_pic_idx_no_ref].use_status = kNotUsed; + decode_buffer_pool_[dpb_buffer_.frame_buffer_list[min_poc_pic_idx_no_ref].dec_buf_idx].use_status &= ~kFrameUsedForDecode; if (dpb_buffer_.dpb_fullness > 0 ) { dpb_buffer_.dpb_fullness--; } @@ -3069,7 +3091,7 @@ ParserResult AvcVideoParser::InsertCurrPicIntoDpb() { } dpb_buffer_.frame_buffer_list[i].pic_order_cnt = dpb_buffer_.frame_buffer_list[i].top_field_order_cnt <= dpb_buffer_.frame_buffer_list[i].bottom_field_order_cnt ? dpb_buffer_.frame_buffer_list[i].top_field_order_cnt : dpb_buffer_.frame_buffer_list[i].bottom_field_order_cnt; dpb_buffer_.frame_buffer_list[i].pic_output_flag = curr_pic_.pic_output_flag; - dpb_buffer_.frame_buffer_list[i].use_status = 3; + dpb_buffer_.frame_buffer_list[i].use_status = kFrameUsedForDecode; if (dpb_buffer_.frame_buffer_list[i].pic_output_flag) { dpb_buffer_.num_pics_needed_for_output++; } @@ -3081,6 +3103,13 @@ ParserResult AvcVideoParser::InsertCurrPicIntoDpb() { } } } + + // Mark as used in decode buffer pool + decode_buffer_pool_[curr_pic_.dec_buf_idx].use_status |= kFrameUsedForDecode; + if (pfn_display_picture_cb_ && curr_pic_.pic_output_flag) { + decode_buffer_pool_[curr_pic_.dec_buf_idx].use_status |= kFrameUsedForDisplay; + } + decode_buffer_pool_[curr_pic_.dec_buf_idx].pic_order_cnt = curr_pic_.pic_order_cnt; } else { ERR("Could not find the reserved frame buffer for the current picture in DPB."); return PARSER_FAIL; @@ -3097,6 +3126,8 @@ ParserResult AvcVideoParser::FlushDpb() { // Mark all reference pictures as "unused for reference for (int i = 0; i < AVC_MAX_DPB_FRAMES; i++) { dpb_buffer_.frame_buffer_list[i].is_reference = kUnusedForReference; + dpb_buffer_.field_pic_list[i * 2].is_reference = kUnusedForReference; + dpb_buffer_.field_pic_list[i * 2 + 1].is_reference = kUnusedForReference; } // Bump the remaining pictures while (dpb_buffer_.num_pics_needed_for_output) { @@ -3104,12 +3135,20 @@ ParserResult AvcVideoParser::FlushDpb() { return PARSER_FAIL; } } - if (pfn_display_picture_cb_ && dpb_buffer_.num_output_pics > 0) { - if (OutputDecodedPictures() != PARSER_OK) { - return PARSER_FAIL; - } + } + if (pfn_display_picture_cb_ && num_output_pics_ > 0) { + if (OutputDecodedPictures(true) != PARSER_OK) { + return PARSER_FAIL; } } + + // Empty DPB + for (int i = 0; i < AVC_MAX_DPB_FRAMES; i++) { + dpb_buffer_.frame_buffer_list[i].use_status = kNotUsed; + dpb_buffer_.field_pic_list[i * 2].use_status = kNotUsed; + dpb_buffer_.field_pic_list[i * 2 + 1].use_status = kNotUsed; + decode_buffer_pool_[dpb_buffer_.frame_buffer_list[i].dec_buf_idx].use_status = kNotUsed; + } return PARSER_OK; } @@ -3340,11 +3379,10 @@ void AvcVideoParser::PrintDpb() { MSG("second_field_ = " << second_field_); MSG("num_pics_needed_for_output = " << dpb_buffer_.num_pics_needed_for_output); MSG("dpb_fullness = " << dpb_buffer_.dpb_fullness); - MSG("num_output_pics = " << dpb_buffer_.num_output_pics); MSG("Frame buffer store:"); for (i = 0; i < AVC_MAX_DPB_FRAMES; i++) { AvcPicture *p_buf = &dpb_buffer_.frame_buffer_list[i]; - MSG("Frame buffer " << i << ": pic_idx = " << p_buf->pic_idx << ", pic_structure = " << p_buf->pic_structure << ", pic_order_cnt = " << p_buf->pic_order_cnt << ", top_field_order_cnt = " << p_buf->top_field_order_cnt << ", bottom_field_order_cnt = " << p_buf->bottom_field_order_cnt << ", frame_num = " << p_buf->frame_num << ", frame_num_wrap = " << p_buf->frame_num_wrap << ", pic_num = " << p_buf->pic_num << ", long_term_pic_num = " << p_buf->long_term_pic_num << ", long_term_frame_idx = " << p_buf->long_term_frame_idx << ", is_reference = " << p_buf->is_reference << ", use_status = " << p_buf->use_status << ", pic_output_flag = " << p_buf->pic_output_flag); + MSG("Frame buffer " << i << ": pic_idx = " << p_buf->pic_idx << ", dec_buf_idx = " << p_buf->dec_buf_idx << ", pic_structure = " << p_buf->pic_structure << ", pic_order_cnt = " << p_buf->pic_order_cnt << ", top_field_order_cnt = " << p_buf->top_field_order_cnt << ", bottom_field_order_cnt = " << p_buf->bottom_field_order_cnt << ", frame_num = " << p_buf->frame_num << ", frame_num_wrap = " << p_buf->frame_num_wrap << ", pic_num = " << p_buf->pic_num << ", long_term_pic_num = " << p_buf->long_term_pic_num << ", long_term_frame_idx = " << p_buf->long_term_frame_idx << ", is_reference = " << p_buf->is_reference << ", use_status = " << p_buf->use_status << ", pic_output_flag = " << p_buf->pic_output_flag); } MSG(""); MSG("Field picture store:"); @@ -3353,12 +3391,20 @@ void AvcVideoParser::PrintDpb() { MSG("Field picture " << i << ": pic_idx = " << p_buf->pic_idx << ", pic_structure = " << p_buf->pic_structure << ", pic_order_cnt = " << p_buf->pic_order_cnt << ", top_field_order_cnt = " << p_buf->top_field_order_cnt << ", bottom_field_order_cnt = " << p_buf->bottom_field_order_cnt << ", frame_num = " << p_buf->frame_num << ", frame_num_wrap = " << p_buf->frame_num_wrap << ", pic_num = " << p_buf->pic_num << ", long_term_pic_num = " << p_buf->long_term_pic_num << ", long_term_frame_idx = " << p_buf->long_term_frame_idx << ", is_reference = " << p_buf->is_reference << ", use_status = " << p_buf->use_status << ", pic_output_flag = " << p_buf->pic_output_flag); } MSG(""); - if (dpb_buffer_.num_output_pics) { + + + MSG("Decode buffer pool:"); + for(i = 0; i < dec_buf_pool_size_; i++) { + DecodeFrameBuffer *p_dec_buf = &decode_buffer_pool_[i]; + MSG("Decode buffer " << i << ": use_status = " << p_dec_buf->use_status << ", pic_order_cnt = " << p_dec_buf->pic_order_cnt); + } + MSG("num_output_pics_ = " << num_output_pics_); + if (num_output_pics_) { MSG("output_pic_list:"); - for (i = 0; i < dpb_buffer_.num_output_pics; i++) { - MSG_NO_NEWLINE(dpb_buffer_.output_pic_list[i] << ", "); + for (i = 0; i < num_output_pics_; i++) { + MSG_NO_NEWLINE(output_pic_list_[i] << ", "); } - MSG(""); + MSG(""); } } diff --git a/projects/rocdecode/src/parser/avc_parser.h b/projects/rocdecode/src/parser/avc_parser.h index 40adc8bcef..d2861c3b26 100644 --- a/projects/rocdecode/src/parser/avc_parser.h +++ b/projects/rocdecode/src/parser/avc_parser.h @@ -60,6 +60,7 @@ public: typedef struct { int pic_idx; // picture index or id + int dec_buf_idx; // frame index in decode buffer pool PictureStructure pic_structure; int32_t pic_order_cnt; int32_t top_field_order_cnt; @@ -70,7 +71,7 @@ public: int32_t long_term_pic_num; // LongTermPicNum uint32_t long_term_frame_idx; // LongTermFrameIdx: long term reference frame/field identifier uint32_t is_reference; - uint32_t use_status; // 0 = empty; 1 = top used; 2 = bottom used; 3 = both fields or frame used + uint32_t use_status; // refer to FrameBufUseStatus uint32_t pic_output_flag; // OutputFlag } AvcPicture; @@ -134,6 +135,7 @@ protected: uint32_t field_pic_count_; int second_field_; int first_field_pic_idx_; + int first_field_dec_buf_idx_; // DPB AvcPicture curr_pic_; @@ -150,11 +152,6 @@ protected: */ ParserResult SendPicForDecode(); - /*! \brief Function to output decoded pictures from DPB for post-processing. - * \return Return code in ParserResult form - */ - ParserResult OutputDecodedPictures(); - /*! \brief Callback function to send parsed SEI playload to decoder. */ void SendSeiMsgPayload(); @@ -263,6 +260,11 @@ protected: */ ParserResult CheckDpbAndOutput(); + /*! \brief Function to find a free buffer in the decode buffer pool + * \return ParserResult + */ + ParserResult FindFreeInDecBufPool(); + /*! \brief Function to find a free buffer in DPB for the current picture * \return ParserResult */ diff --git a/projects/rocdecode/src/parser/hevc_parser.cpp b/projects/rocdecode/src/parser/hevc_parser.cpp index 21b8b5d6d2..b85e5124ae 100644 --- a/projects/rocdecode/src/parser/hevc_parser.cpp +++ b/projects/rocdecode/src/parser/hevc_parser.cpp @@ -78,9 +78,6 @@ rocDecStatus HevcVideoParser::UnInitialize() { rocDecStatus HevcVideoParser::ParseVideoData(RocdecSourceDataPacket *p_data) { if (p_data->payload && p_data->payload_size) { - // Clear DPB output/display buffer number - dpb_buffer_.num_output_pics = 0; - if (ParsePictureData(p_data->payload, p_data->payload_size) != PARSER_OK) { ERR(STR("Parser failed!")); return ROCDEC_RUNTIME_ERROR; @@ -111,8 +108,8 @@ rocDecStatus HevcVideoParser::ParseVideoData(RocdecSourceDataPacket *p_data) { } // Output decoded pictures from DPB if any are ready - if (pfn_display_picture_cb_ && dpb_buffer_.num_output_pics > 0) { - if (OutputDecodedPictures() != PARSER_OK) { + if (pfn_display_picture_cb_ && num_output_pics_ > 0) { + if (OutputDecodedPictures(false) != PARSER_OK) { return ROCDEC_RUNTIME_ERROR; } } @@ -144,7 +141,7 @@ int HevcVideoParser::FillSeqCallbackFn(HevcSeqParamSet* sps_data) { video_format_params_.progressive_sequence = 0; else // default value video_format_params_.progressive_sequence = 1; - video_format_params_.min_num_decode_surfaces = dpb_buffer_.dpb_size; + video_format_params_.min_num_decode_surfaces = dec_buf_pool_size_; video_format_params_.coded_width = sps_data->pic_width_in_luma_samples; video_format_params_.coded_height = sps_data->pic_height_in_luma_samples; video_format_params_.chroma_format = static_cast(sps_data->chroma_format_idc); @@ -237,7 +234,7 @@ void HevcVideoParser::SendSeiMsgPayload() { 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_.pic_idx; + 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_); @@ -251,7 +248,7 @@ int HevcVideoParser::SendPicForDecode() { dec_pic_params_.pic_width = sps_ptr->pic_width_in_luma_samples; dec_pic_params_.pic_height = sps_ptr->pic_height_in_luma_samples; - dec_pic_params_.curr_pic_idx = curr_pic_info_.pic_idx; + dec_pic_params_.curr_pic_idx = curr_pic_info_.dec_buf_idx; dec_pic_params_.field_pic_flag = sps_ptr->profile_tier_level.general_interlaced_source_flag; dec_pic_params_.bottom_field_flag = 0; // For now. Need to parse VUI/SEI pic_timing() dec_pic_params_.second_field = 0; // For now. Need to parse VUI/SEI pic_timing() @@ -270,14 +267,14 @@ int HevcVideoParser::SendPicForDecode() { RocdecHevcPicParams *pic_param_ptr = &dec_pic_params_.pic_params.hevc; // Current picture - pic_param_ptr->curr_pic.pic_idx = curr_pic_info_.pic_idx; + pic_param_ptr->curr_pic.pic_idx = curr_pic_info_.dec_buf_idx; pic_param_ptr->curr_pic.poc = curr_pic_info_.pic_order_cnt; // Reference pictures ref_idx = 0; for (i = 0; i < num_poc_st_curr_before_; i++) { buf_idx = ref_pic_set_st_curr_before_[i]; // buffer index in DPB - pic_param_ptr->ref_frames[ref_idx].pic_idx = dpb_buffer_.frame_buffer_list[buf_idx].pic_idx; + pic_param_ptr->ref_frames[ref_idx].pic_idx = dpb_buffer_.frame_buffer_list[buf_idx].dec_buf_idx; pic_param_ptr->ref_frames[ref_idx].poc = dpb_buffer_.frame_buffer_list[buf_idx].pic_order_cnt; pic_param_ptr->ref_frames[ref_idx].flags = 0; // assume frame picture for now pic_param_ptr->ref_frames[ref_idx].flags |= RocdecHevcPicture_RPS_ST_CURR_BEFORE; @@ -286,7 +283,7 @@ int HevcVideoParser::SendPicForDecode() { for (i = 0; i < num_poc_st_curr_after_; i++) { buf_idx = ref_pic_set_st_curr_after_[i]; // buffer index in DPB - pic_param_ptr->ref_frames[ref_idx].pic_idx = dpb_buffer_.frame_buffer_list[buf_idx].pic_idx; + pic_param_ptr->ref_frames[ref_idx].pic_idx = dpb_buffer_.frame_buffer_list[buf_idx].dec_buf_idx; pic_param_ptr->ref_frames[ref_idx].poc = dpb_buffer_.frame_buffer_list[buf_idx].pic_order_cnt; pic_param_ptr->ref_frames[ref_idx].flags = 0; // assume frame picture for now pic_param_ptr->ref_frames[ref_idx].flags |= RocdecHevcPicture_RPS_ST_CURR_AFTER; @@ -295,7 +292,7 @@ int HevcVideoParser::SendPicForDecode() { for (i = 0; i < num_poc_lt_curr_; i++) { buf_idx = ref_pic_set_lt_curr_[i]; // buffer index in DPB - pic_param_ptr->ref_frames[ref_idx].pic_idx = dpb_buffer_.frame_buffer_list[buf_idx].pic_idx; + pic_param_ptr->ref_frames[ref_idx].pic_idx = dpb_buffer_.frame_buffer_list[buf_idx].dec_buf_idx; pic_param_ptr->ref_frames[ref_idx].poc = dpb_buffer_.frame_buffer_list[buf_idx].pic_order_cnt; pic_param_ptr->ref_frames[ref_idx].flags = 0; // assume frame picture for now pic_param_ptr->ref_frames[ref_idx].flags |= RocdecHevcPicture_LONG_TERM_REFERENCE | RocdecHevcPicture_RPS_LT_CURR; @@ -304,7 +301,7 @@ int HevcVideoParser::SendPicForDecode() { for (i = 0; i < num_poc_st_foll_; i++) { buf_idx = ref_pic_set_st_foll_[i]; // buffer index in DPB - pic_param_ptr->ref_frames[ref_idx].pic_idx = dpb_buffer_.frame_buffer_list[buf_idx].pic_idx; + pic_param_ptr->ref_frames[ref_idx].pic_idx = dpb_buffer_.frame_buffer_list[buf_idx].dec_buf_idx; pic_param_ptr->ref_frames[ref_idx].poc = dpb_buffer_.frame_buffer_list[buf_idx].pic_order_cnt; pic_param_ptr->ref_frames[ref_idx].flags = 0; // assume frame picture for now ref_idx++; @@ -312,7 +309,7 @@ int HevcVideoParser::SendPicForDecode() { for (i = 0; i < num_poc_lt_foll_; i++) { buf_idx = ref_pic_set_lt_foll_[i]; // buffer index in DPB - pic_param_ptr->ref_frames[ref_idx].pic_idx = dpb_buffer_.frame_buffer_list[buf_idx].pic_idx; + pic_param_ptr->ref_frames[ref_idx].pic_idx = dpb_buffer_.frame_buffer_list[buf_idx].dec_buf_idx; pic_param_ptr->ref_frames[ref_idx].poc = dpb_buffer_.frame_buffer_list[buf_idx].pic_order_cnt; pic_param_ptr->ref_frames[ref_idx].flags = 0; // assume frame picture for now ref_idx++; @@ -424,8 +421,9 @@ int HevcVideoParser::SendPicForDecode() { if (p_slice_header->slice_type != HEVC_SLICE_TYPE_I) { for (i = 0; i <= p_slice_header->num_ref_idx_l0_active_minus1; i++) { int idx = p_slice_info->ref_pic_list_0_[i]; // pic_idx of the ref pic + int dec_buf_idx = dpb_buffer_.frame_buffer_list[idx].dec_buf_idx; for (j = 0; j < 15; j++) { - if (pic_param_ptr->ref_frames[j].pic_idx == idx) { + if (pic_param_ptr->ref_frames[j].pic_idx == dec_buf_idx) { break; } } @@ -440,8 +438,9 @@ int HevcVideoParser::SendPicForDecode() { if (p_slice_header->slice_type == HEVC_SLICE_TYPE_B) { for (i = 0; i <= p_slice_header->num_ref_idx_l1_active_minus1; i++) { int idx = p_slice_info->ref_pic_list_1_[i]; // pic_idx of the ref pic + int dec_buf_idx = dpb_buffer_.frame_buffer_list[idx].dec_buf_idx; for (j = 0; j < 15; j++) { - if (pic_param_ptr->ref_frames[j].pic_idx == idx) { + if (pic_param_ptr->ref_frames[j].pic_idx == dec_buf_idx) { break; } } @@ -532,6 +531,10 @@ int HevcVideoParser::SendPicForDecode() { } } +#if DBGINFO + PrintVappiBufInfo(); +#endif // DBGINFO + if (pfn_decode_picture_cb_(parser_params_.user_data, &dec_pic_params_) == 0) { ERR("Decode error occurred."); return PARSER_FAIL; @@ -540,20 +543,6 @@ int HevcVideoParser::SendPicForDecode() { } } -int HevcVideoParser::OutputDecodedPictures() { - RocdecParserDispInfo disp_info = {0}; - disp_info.progressive_frame = m_sps_[m_active_sps_id_].profile_tier_level.general_progressive_source_flag; - disp_info.top_field_first = 1; - - for (int i = 0; i < dpb_buffer_.num_output_pics; i++) { - disp_info.picture_index = dpb_buffer_.frame_buffer_list[dpb_buffer_.output_pic_list[i]].pic_idx; - pfn_display_picture_cb_(parser_params_.user_data, &disp_info); - } - - dpb_buffer_.num_output_pics = 0; - return PARSER_OK; -} - ParserResult HevcVideoParser::ParsePictureData(const uint8_t* p_stream, uint32_t pic_data_size) { ParserResult ret = PARSER_OK; ParserResult ret2; @@ -667,6 +656,11 @@ ParserResult HevcVideoParser::ParsePictureData(const uint8_t* p_stream, uint32_t // Get POC. 8.3.1. CalculateCurrPoc(); + // Locate a free buffer for the current picutre in decode buffer pool before output picture marking (C.5.2.2) + if (FindFreeInDecBufPool() != PARSER_OK) { + return PARSER_FAIL; + } + // Decode RPS. 8.3.2. DecodeRps(); } @@ -683,9 +677,13 @@ ParserResult HevcVideoParser::ParsePictureData(const uint8_t* p_stream, uint32_t } // C.5.2.3. Find a free buffer in DPB and mark as used. (After 8.3.2.) - if (FindFreeBufAndMark() != PARSER_OK) { + if (FindFreeInDpbAndMark() != PARSER_OK) { return PARSER_FAIL; } + +#if DBGINFO + PrintDpb(); +#endif // DBGINFO } num_slices_++; break; @@ -1552,8 +1550,8 @@ ParserResult HevcVideoParser::ParseSliceHeader(uint8_t *nalu, size_t size, HevcS if (m_active_sps_id_ != pps_ptr->pps_seq_parameter_set_id) { m_active_sps_id_ = pps_ptr->pps_seq_parameter_set_id; sps_ptr = &m_sps_[m_active_sps_id_]; - // Re-set DPB size. We add 2 addition buffers to avoid overwritting buffer needed for output in certain cases. - dpb_buffer_.dpb_size = sps_ptr->sps_max_dec_pic_buffering_minus1[sps_ptr->sps_max_sub_layers_minus1] + 3; + // Re-set DPB size. + dpb_buffer_.dpb_size = sps_ptr->sps_max_dec_pic_buffering_minus1[sps_ptr->sps_max_sub_layers_minus1] + 1; dpb_buffer_.dpb_size = dpb_buffer_.dpb_size > HEVC_MAX_DPB_FRAMES ? HEVC_MAX_DPB_FRAMES : dpb_buffer_.dpb_size; new_sps_activated_ = true; // Note: clear this flag after the actions are taken. } @@ -1573,12 +1571,17 @@ ParserResult HevcVideoParser::ParseSliceHeader(uint8_t *nalu, size_t size, HevcS pic_width_ = sps_ptr->pic_width_in_luma_samples; pic_height_ = sps_ptr->pic_height_in_luma_samples; // Take care of the case where a new SPS replaces the old SPS with the same id but with different dimensions - // Re-set DPB size. We add 2 addition buffers to avoid overwritting buffer needed for output in certain cases. - dpb_buffer_.dpb_size = sps_ptr->sps_max_dec_pic_buffering_minus1[sps_ptr->sps_max_sub_layers_minus1] + 3; + // Re-set DPB size. + dpb_buffer_.dpb_size = sps_ptr->sps_max_dec_pic_buffering_minus1[sps_ptr->sps_max_sub_layers_minus1] + 1; dpb_buffer_.dpb_size = dpb_buffer_.dpb_size > HEVC_MAX_DPB_FRAMES ? HEVC_MAX_DPB_FRAMES : dpb_buffer_.dpb_size; new_sps_activated_ = true; // Note: clear this flag after the actions are taken. } + // Check and adjust decode buffer pool size if needed + if (new_sps_activated_) { + CheckAndAdjustDecBufPoolSize(dpb_buffer_.dpb_size); + } + // Set frame rate if available if (new_sps_activated_) { if (m_vps_[m_active_vps_id_].vps_timing_info_present_flag) { @@ -2130,25 +2133,23 @@ void HevcVideoParser::InitDpb() { dpb_buffer_.frame_buffer_list[i].pic_idx = i; dpb_buffer_.frame_buffer_list[i].is_reference = kUnusedForReference; dpb_buffer_.frame_buffer_list[i].pic_output_flag = 0; - dpb_buffer_.frame_buffer_list[i].use_status = 0; - dpb_buffer_.output_pic_list[i] = 0xFF; + dpb_buffer_.frame_buffer_list[i].use_status = kNotUsed; } dpb_buffer_.dpb_size = 0; dpb_buffer_.dpb_fullness = 0; dpb_buffer_.num_pics_needed_for_output = 0; - dpb_buffer_.num_output_pics = 0; } void HevcVideoParser::EmptyDpb() { for (int i = 0; i < HEVC_MAX_DPB_FRAMES; i++) { dpb_buffer_.frame_buffer_list[i].is_reference = kUnusedForReference; dpb_buffer_.frame_buffer_list[i].pic_output_flag = 0; - dpb_buffer_.frame_buffer_list[i].use_status = 0; - dpb_buffer_.output_pic_list[i] = 0xFF; + dpb_buffer_.frame_buffer_list[i].use_status = kNotUsed; + decode_buffer_pool_[dpb_buffer_.frame_buffer_list[i].dec_buf_idx].use_status = kNotUsed; } dpb_buffer_.dpb_fullness = 0; dpb_buffer_.num_pics_needed_for_output = 0; - dpb_buffer_.num_output_pics = 0; + num_output_pics_ = 0; } int HevcVideoParser::FlushDpb() { @@ -2159,12 +2160,13 @@ int HevcVideoParser::FlushDpb() { return PARSER_FAIL; } } - if (pfn_display_picture_cb_ && dpb_buffer_.num_output_pics > 0) { - if (OutputDecodedPictures() != PARSER_OK) { - return PARSER_FAIL; - } + } + if (pfn_display_picture_cb_ && num_output_pics_ > 0) { + if (OutputDecodedPictures(true) != PARSER_OK) { + return PARSER_FAIL; } } + return PARSER_OK; } @@ -2185,11 +2187,16 @@ int HevcVideoParser::MarkOutputPictures() { } } + // We can still have remaining undisplayed frames due to display delay feature + if (num_output_pics_) { + OutputDecodedPictures(true); + } EmptyDpb(); } else { for (i = 0; i < HEVC_MAX_DPB_FRAMES; i++) { if (dpb_buffer_.frame_buffer_list[i].is_reference == kUnusedForReference && dpb_buffer_.frame_buffer_list[i].pic_output_flag == 0 && dpb_buffer_.frame_buffer_list[i].use_status) { - dpb_buffer_.frame_buffer_list[i].use_status = 0; + dpb_buffer_.frame_buffer_list[i].use_status = kNotUsed; + decode_buffer_pool_[dpb_buffer_.frame_buffer_list[i].dec_buf_idx].use_status &= ~kFrameUsedForDecode; if (dpb_buffer_.dpb_fullness > 0) { dpb_buffer_.dpb_fullness--; } else { @@ -2222,27 +2229,34 @@ int HevcVideoParser::MarkOutputPictures() { return PARSER_OK; } -int HevcVideoParser::FindFreeBufAndMark() { +ParserResult HevcVideoParser::FindFreeInDecBufPool() { + int dec_buf_index; + + // Find a free buffer in decode buffer pool + for (dec_buf_index = 0; dec_buf_index < dec_buf_pool_size_; dec_buf_index++) { + if (decode_buffer_pool_[dec_buf_index].use_status == kNotUsed) { + break; + } + } + if (dec_buf_index == dec_buf_pool_size_) { + ERR("Could not find a free buffer in decode buffer pool."); + return PARSER_NOT_FOUND; + } + curr_pic_info_.dec_buf_idx = dec_buf_index; + return PARSER_OK; +} + +ParserResult HevcVideoParser::FindFreeInDpbAndMark() { int i, j; - // Look for an empty buffer with longest decode history (lowest decode count) + // Look for an empty buffer in DPB with longest decode history (lowest decode count) uint32_t min_decode_order_count = 0xFFFFFFFF; int index = dpb_buffer_.dpb_size; for (i = 0; i < dpb_buffer_.dpb_size; i++) { - if (dpb_buffer_.frame_buffer_list[i].use_status == 0) { + if (dpb_buffer_.frame_buffer_list[i].use_status == kNotUsed) { if (dpb_buffer_.frame_buffer_list[i].decode_order_count < min_decode_order_count) { - // Check if this picture has been bumped to the output/display list. If yes, skip it because we do not want to - // decode the current picture into any buffers in the output list - bool is_in_output_list = false; - for (j = 0; j < dpb_buffer_.num_output_pics; j++) { - if (dpb_buffer_.output_pic_list[j] == i) { - is_in_output_list = true; - } - } - if (!is_in_output_list) { - min_decode_order_count = dpb_buffer_.frame_buffer_list[i].decode_order_count; - index = i; - } + min_decode_order_count = dpb_buffer_.frame_buffer_list[i].decode_order_count; + index = i; } } } @@ -2254,6 +2268,7 @@ int HevcVideoParser::FindFreeBufAndMark() { curr_pic_info_.pic_idx = index; curr_pic_info_.is_reference = kUsedForShortTerm; // dpb_buffer_.frame_buffer_list[i].pic_idx is already set in InitDpb(). + dpb_buffer_.frame_buffer_list[index].dec_buf_idx = curr_pic_info_.dec_buf_idx; dpb_buffer_.frame_buffer_list[index].pic_order_cnt = curr_pic_info_.pic_order_cnt; dpb_buffer_.frame_buffer_list[index].prev_poc_lsb = curr_pic_info_.prev_poc_lsb; dpb_buffer_.frame_buffer_list[index].prev_poc_msb = curr_pic_info_.prev_poc_msb; @@ -2261,19 +2276,25 @@ int HevcVideoParser::FindFreeBufAndMark() { dpb_buffer_.frame_buffer_list[index].decode_order_count = curr_pic_info_.decode_order_count; dpb_buffer_.frame_buffer_list[index].pic_output_flag = curr_pic_info_.pic_output_flag; dpb_buffer_.frame_buffer_list[index].is_reference = kUsedForShortTerm; - dpb_buffer_.frame_buffer_list[index].use_status = 3; + dpb_buffer_.frame_buffer_list[index].use_status = kFrameUsedForDecode; if (dpb_buffer_.frame_buffer_list[index].pic_output_flag) { dpb_buffer_.num_pics_needed_for_output++; } dpb_buffer_.dpb_fullness++; + // Mark as used in decode buffer pool + decode_buffer_pool_[curr_pic_info_.dec_buf_idx].use_status |= kFrameUsedForDecode; + if (pfn_display_picture_cb_ && curr_pic_info_.pic_output_flag) { + decode_buffer_pool_[curr_pic_info_.dec_buf_idx].use_status |= kFrameUsedForDisplay; + } + decode_buffer_pool_[curr_pic_info_.dec_buf_idx].pic_order_cnt = curr_pic_info_.pic_order_cnt; + HevcSeqParamSet *sps_ptr = &m_sps_[m_active_sps_id_]; uint32_t highest_tid = sps_ptr->sps_max_sub_layers_minus1; // HighestTid uint32_t max_num_reorder_pics = sps_ptr->sps_max_num_reorder_pics[highest_tid]; - // Conditional bumping (when max_num_reorder_pics > 0) to avoid synchronous job submission while keeping in conformance with the spec. - while (max_num_reorder_pics > 0 && dpb_buffer_.num_pics_needed_for_output > max_num_reorder_pics) { + while ( dpb_buffer_.num_pics_needed_for_output > max_num_reorder_pics) { if (BumpPicFromDpb() != PARSER_OK) { return PARSER_FAIL; } @@ -2310,19 +2331,22 @@ int HevcVideoParser::BumpPicFromDpb() { // If it is not used for reference, empty it. if (dpb_buffer_.frame_buffer_list[min_poc_pic_idx].is_reference == kUnusedForReference) { - dpb_buffer_.frame_buffer_list[min_poc_pic_idx].use_status = 0; + dpb_buffer_.frame_buffer_list[min_poc_pic_idx].use_status = kNotUsed; + decode_buffer_pool_[dpb_buffer_.frame_buffer_list[min_poc_pic_idx].dec_buf_idx].use_status &= ~kFrameUsedForDecode; if (dpb_buffer_.dpb_fullness > 0 ) { dpb_buffer_.dpb_fullness--; } } // Insert into output/display picture list - if (dpb_buffer_.num_output_pics >= HEVC_MAX_DPB_FRAMES) { - ERR("Error! DPB output buffer list overflow!"); - return PARSER_OUT_OF_RANGE; - } else { - dpb_buffer_.output_pic_list[dpb_buffer_.num_output_pics] = min_poc_pic_idx; - dpb_buffer_.num_output_pics++; + if (pfn_display_picture_cb_) { + if (num_output_pics_ >= dec_buf_pool_size_) { + ERR("Error! Decode buffer pool overflow!"); + return PARSER_OUT_OF_RANGE; + } else { + output_pic_list_[num_output_pics_] = dpb_buffer_.frame_buffer_list[min_poc_pic_idx].dec_buf_idx; + num_output_pics_++; + } } return PARSER_OK; @@ -2630,7 +2654,7 @@ void HevcVideoParser::PrintSliceSegHeader(HevcSliceSegHeader *slice_header_ptr) MSG("slice_qp_delta = " << slice_header_ptr->slice_qp_delta); MSG("slice_cb_qp_offset = " << slice_header_ptr->slice_cb_qp_offset); MSG("slice_cr_qp_offset = " << slice_header_ptr->slice_cr_qp_offset); - MSG("cu_chroma_qp_offset_enabled_flag = " << slice_header_ptr->cu_chroma_qp_offset_enabled_flag); + MSG("cu_chroma_qp_offset_enabled_flag = " << static_cast(slice_header_ptr->cu_chroma_qp_offset_enabled_flag)); MSG("deblocking_filter_override_flag = " << slice_header_ptr->deblocking_filter_override_flag); MSG("slice_deblocking_filter_disabled_flag = " << slice_header_ptr->slice_deblocking_filter_disabled_flag); MSG("slice_beta_offset_div2 = " << slice_header_ptr->slice_beta_offset_div2); @@ -2644,18 +2668,18 @@ void HevcVideoParser::PrintSliceSegHeader(HevcSliceSegHeader *slice_header_ptr) void HevcVideoParser::PrintStRps(HevcShortTermRps *rps_ptr) { MSG("==== Short-term reference picture set =====") - MSG("inter_ref_pic_set_prediction_flag = " << rps_ptr->inter_ref_pic_set_prediction_flag); + MSG("inter_ref_pic_set_prediction_flag = " << static_cast(rps_ptr->inter_ref_pic_set_prediction_flag)); MSG("delta_idx_minus1 = " << rps_ptr->delta_idx_minus1); - MSG("delta_rps_sign = " << rps_ptr->delta_rps_sign); + MSG("delta_rps_sign = " << static_cast(rps_ptr->delta_rps_sign)); MSG("abs_delta_rps_minus1 = " << rps_ptr->abs_delta_rps_minus1); MSG_NO_NEWLINE("rps->used_by_curr_pic_flag[]:"); for(int j = 0; j < 16; j++) { - MSG_NO_NEWLINE(" " << rps_ptr->used_by_curr_pic_flag[j]); + MSG_NO_NEWLINE(" " << static_cast(rps_ptr->used_by_curr_pic_flag[j])); } MSG(""); MSG_NO_NEWLINE("use_delta_flag[]:"); for(int j = 0; j < 16; j++) { - MSG_NO_NEWLINE(" " << rps_ptr->use_delta_flag[j]); + MSG_NO_NEWLINE(" " << static_cast(rps_ptr->use_delta_flag[j])); } MSG(""); MSG("num_negative_pics = " << rps_ptr->num_negative_pics); @@ -2669,7 +2693,7 @@ void HevcVideoParser::PrintStRps(HevcShortTermRps *rps_ptr) { MSG(""); MSG_NO_NEWLINE("used_by_curr_pic_s0_flag[]:"); for(int j = 0; j < 16; j++) { - MSG_NO_NEWLINE(" " << rps_ptr->used_by_curr_pic_s0_flag[j]); + MSG_NO_NEWLINE(" " << static_cast(rps_ptr->used_by_curr_pic_s0_flag[j])); } MSG(""); MSG_NO_NEWLINE("delta_poc_s1_minus1[]:"); @@ -2679,7 +2703,7 @@ void HevcVideoParser::PrintStRps(HevcShortTermRps *rps_ptr) { MSG(""); MSG_NO_NEWLINE("used_by_curr_pic_s1_flag[]:"); for(int j = 0; j < 16; j++) { - MSG_NO_NEWLINE(" " << rps_ptr->used_by_curr_pic_s1_flag[j]); + MSG_NO_NEWLINE(" " << static_cast(rps_ptr->used_by_curr_pic_s1_flag[j])); } MSG(""); @@ -2695,12 +2719,12 @@ void HevcVideoParser::PrintStRps(HevcShortTermRps *rps_ptr) { MSG(""); MSG_NO_NEWLINE("used_by_curr_pic_s0[16]:"); for(int j = 0; j < 16; j++) { - MSG_NO_NEWLINE(" " << rps_ptr->used_by_curr_pic_s0[j]); + MSG_NO_NEWLINE(" " << static_cast(rps_ptr->used_by_curr_pic_s0[j])); } MSG(""); MSG_NO_NEWLINE("used_by_curr_pic_s1[16]:"); for(int j = 0; j < 16; j++) { - MSG_NO_NEWLINE(" " << rps_ptr->used_by_curr_pic_s1[j]); + MSG_NO_NEWLINE(" " << static_cast(rps_ptr->used_by_curr_pic_s1[j])); } MSG(""); } @@ -2719,4 +2743,71 @@ void HevcVideoParser::PrintLtRefInfo(HevcLongTermRps *lt_info_ptr) { } MSG(""); } + +void HevcVideoParser::PrintDpb() { + uint32_t i; + + MSG("======================="); + MSG("DPB buffer content: "); + MSG("======================="); + MSG("dpb_size = " << dpb_buffer_.dpb_size); + MSG("num_pics_needed_for_output = " << dpb_buffer_.num_pics_needed_for_output); + MSG("dpb_fullness = " << dpb_buffer_.dpb_fullness); + MSG("Frame buffer store:"); + for (i = 0; i < HEVC_MAX_DPB_FRAMES; i++) { + HevcPicInfo *p_buf = &dpb_buffer_.frame_buffer_list[i]; + MSG("Frame buffer " << i << ": pic_idx = " << p_buf->pic_idx << ", dec_buf_idx = " << p_buf->dec_buf_idx << ", pic_order_cnt = " << p_buf->pic_order_cnt << ", slice_pic_order_cnt_lsb = " << p_buf->slice_pic_order_cnt_lsb << ", decode_order_count = " << p_buf->decode_order_count << ", is_reference = " << p_buf->is_reference << ", use_status = " << p_buf->use_status << ", pic_output_flag = " << p_buf->pic_output_flag); + } + MSG(""); + + MSG("Decode buffer pool:"); + for(i = 0; i < dec_buf_pool_size_; i++) { + DecodeFrameBuffer *p_dec_buf = &decode_buffer_pool_[i]; + MSG("Decode buffer " << i << ": use_status = " << p_dec_buf->use_status << ", pic_order_cnt = " << p_dec_buf->pic_order_cnt); + } + MSG("num_output_pics_ = " << num_output_pics_); + if (num_output_pics_) { + MSG("output_pic_list:"); + for (i = 0; i < num_output_pics_; i++) { + MSG_NO_NEWLINE(output_pic_list_[i] << ", "); + } + MSG(""); + } +} + +void HevcVideoParser::PrintVappiBufInfo() { + RocdecHevcPicParams *p_pic_param = &dec_pic_params_.pic_params.hevc; + MSG("======================="); + MSG("VAAPI Buffer Info: "); + MSG("======================="); + MSG("Current buffer:"); + MSG_NO_NEWLINE("pic_idx = " << p_pic_param->curr_pic.pic_idx << ", poc = " << p_pic_param->curr_pic.poc); + MSG(", flags = 0x" << std::hex << p_pic_param->curr_pic.flags); + MSG(std::dec); + + MSG("Reference pictures:"); + for (int i = 0; i < 15; i++) { + RocdecHevcPicture *p_ref_pic = &p_pic_param->ref_frames[i]; + MSG_NO_NEWLINE("Ref pic " << i << ": " << "pic_idx = " << p_ref_pic->pic_idx << ", poc = " << p_ref_pic->poc); + MSG(", flags = 0x" << std::hex << p_ref_pic->flags); + MSG_NO_NEWLINE(std::dec); + } + + MSG("Slice ref lists:") + for (int slice_index = 0; slice_index < num_slices_; slice_index++) { + RocdecHevcSliceParams *p_slice_param = &slice_param_list_[slice_index]; + HevcSliceInfo *p_slice_info = &slice_info_list_[slice_index]; + MSG("Slice " << slice_index << " ref list 0:"); + for (int i = 0; i <= p_slice_info->slice_header.num_ref_idx_l0_active_minus1; i++) { + MSG("Index " << i << ": " << static_cast(p_slice_param->ref_pic_list[0][i])); + } + if (p_slice_info->slice_header.slice_type == HEVC_SLICE_TYPE_B) { + MSG("Slice " << slice_index << " ref list 1: "); + for (int i = 0; i <= p_slice_info->slice_header.num_ref_idx_l1_active_minus1; i++) { + MSG("Index " << i << ": " << static_cast(p_slice_param->ref_pic_list[1][i])); + } + } + MSG(""); + } +} #endif // DBGINFO \ No newline at end of file diff --git a/projects/rocdecode/src/parser/hevc_parser.h b/projects/rocdecode/src/parser/hevc_parser.h index 982dfe4dc0..f704733ef4 100644 --- a/projects/rocdecode/src/parser/hevc_parser.h +++ b/projects/rocdecode/src/parser/hevc_parser.h @@ -98,6 +98,7 @@ protected: */ typedef struct { int pic_idx; // picture index or id + int dec_buf_idx; // frame index in decode buffer pool // POC info int32_t pic_order_cnt; // PicOrderCnt int32_t prev_poc_lsb; // prevPicOrderCntLsb @@ -107,7 +108,7 @@ protected: uint32_t pic_output_flag; // PicOutputFlag uint32_t is_reference; - uint32_t use_status; // 0 = empty; 1 = top used; 2 = bottom used; 3 = both fields or frame used + uint32_t use_status; // refer to FrameBufUseStatus } HevcPicInfo; /*! \brief Decoded picture buffer @@ -118,9 +119,6 @@ protected: uint32_t num_pics_needed_for_output; // number of pictures in DPB that need to be output uint32_t dpb_fullness; // number of pictures in DPB HevcPicInfo frame_buffer_list[HEVC_MAX_DPB_FRAMES]; - - uint32_t num_output_pics; // number of pictures that are output after the decode call - uint32_t output_pic_list[HEVC_MAX_DPB_FRAMES]; // sorted output picuture index to frame_buffer_list[] } DecodedPictureBuffer; // Data members of HEVC class @@ -308,11 +306,16 @@ protected: */ int MarkOutputPictures(); + /*! \brief Function to find a free buffer in the decode buffer pool + * \return ParserResult + */ + ParserResult FindFreeInDecBufPool(); + /*! \brief Function to find a free buffer in DPB for the current picture and mark it. Additional picture * bumping is done if needed. C.5.2.3. - * \return Code in ParserResult form. + * \return ParserResult */ - int FindFreeBufAndMark(); + ParserResult FindFreeInDpbAndMark(); /*! \brief Function to bump one picture out of DPB. C.5.2.4. * \return Code in ParserResult form. @@ -333,6 +336,8 @@ protected: void PrintSliceSegHeader(HevcSliceSegHeader *slice_header_ptr); void PrintStRps(HevcShortTermRps *rps_ptr); void PrintLtRefInfo(HevcLongTermRps *lt_info_ptr); + void PrintDpb(); + void PrintVappiBufInfo(); #endif // DBGINFO private: @@ -349,11 +354,6 @@ private: */ int SendPicForDecode(); - /*! \brief Callback function to output decoded pictures from DPB for post-processing. - * \return Return code in ParserResult form - */ - int OutputDecodedPictures(); - bool IsIdrPic(HevcNalUnitHeader *nal_header_ptr); bool IsCraPic(HevcNalUnitHeader *nal_header_ptr); bool IsBlaPic(HevcNalUnitHeader *nal_header_ptr); diff --git a/projects/rocdecode/src/parser/roc_video_parser.cpp b/projects/rocdecode/src/parser/roc_video_parser.cpp index 41c8ca4427..d568a924c2 100644 --- a/projects/rocdecode/src/parser/roc_video_parser.cpp +++ b/projects/rocdecode/src/parser/roc_video_parser.cpp @@ -57,16 +57,65 @@ rocDecStatus RocVideoParser::Initialize(RocdecParserParams *pParams) { return ROCDEC_NOT_INITIALIZED; } // Initialize callback function pointers - pfn_sequece_cb_ = pParams->pfn_sequence_callback; /**< Called before decoding frames and/or whenever there is a fmt change */ + pfn_sequece_cb_ = pParams->pfn_sequence_callback; /**< Called before decoding frames and/or whenever there is a fmt change */ pfn_decode_picture_cb_ = pParams->pfn_decode_picture; /**< Called when a picture is ready to be decoded (decode order) */ - pfn_display_picture_cb_ = pParams->pfn_display_picture; /**< Called whenever a picture is ready to be displayed (display order) */ - pfn_get_sei_message_cb_ = pParams->pfn_get_sei_msg; /**< Called when all SEI messages are parsed for particular frame */ + pfn_display_picture_cb_ = pParams->pfn_display_picture; /**< Called whenever a picture is ready to be displayed (display order) */ + pfn_get_sei_message_cb_ = pParams->pfn_get_sei_msg; /**< Called when all SEI messages are parsed for particular frame */ parser_params_ = *pParams; + dec_buf_pool_size_ = parser_params_.max_num_decode_surfaces; + decode_buffer_pool_.resize(dec_buf_pool_size_, {0}); + output_pic_list_.resize(dec_buf_pool_size_, 0xFF); + InitDecBufPool(); + return ROCDEC_SUCCESS; } +void RocVideoParser::InitDecBufPool() { + for (int i = 0; i < dec_buf_pool_size_; i++) { + decode_buffer_pool_[i].use_status = kNotUsed; + decode_buffer_pool_[i].pic_order_cnt = 0; + output_pic_list_[i] = 0xFF; + } + num_output_pics_ = 0; +} + +void RocVideoParser::CheckAndAdjustDecBufPoolSize(int dpb_size) { + int min_dec_buf_pool_size = dpb_size + (parser_params_.max_display_delay > DECODE_BUF_POOL_EXTENSION ? parser_params_.max_display_delay : DECODE_BUF_POOL_EXTENSION); + if ( dec_buf_pool_size_ < min_dec_buf_pool_size) { + dec_buf_pool_size_ = min_dec_buf_pool_size; + decode_buffer_pool_.resize(dec_buf_pool_size_, {0}); + output_pic_list_.resize(dec_buf_pool_size_, 0xFF); + InitDecBufPool(); + } +} + +ParserResult RocVideoParser::OutputDecodedPictures(bool no_delay) { + RocdecParserDispInfo disp_info = {0}; + disp_info.progressive_frame = 1; // not used + disp_info.top_field_first = 1; // not used + + int disp_delay = no_delay ? 0 : parser_params_.max_display_delay; + if (num_output_pics_ > disp_delay) { + int num_disp = num_output_pics_ - disp_delay; + for (int i = 0; i < num_disp; i++) { + disp_info.picture_index = output_pic_list_[i]; + pfn_display_picture_cb_(parser_params_.user_data, &disp_info); + decode_buffer_pool_[output_pic_list_[i]].use_status &= ~kFrameUsedForDisplay; + } + + num_output_pics_ = disp_delay; + // Shift the remaining frames to the top + if (num_output_pics_) { + for (int i = 0; i < num_output_pics_; i++) { + output_pic_list_[i] = output_pic_list_[i + num_disp]; + } + } + } + return PARSER_OK; +} + ParserResult RocVideoParser::GetNalUnit() { bool start_code_found = false; diff --git a/projects/rocdecode/src/parser/roc_video_parser.h b/projects/rocdecode/src/parser/roc_video_parser.h index 62967d5bda..1ebd3130c2 100644 --- a/projects/rocdecode/src/parser/roc_video_parser.h +++ b/projects/rocdecode/src/parser/roc_video_parser.h @@ -78,6 +78,15 @@ typedef struct { #define INIT_SLICE_LIST_NUM 16 // initial slice information/parameter struct list size #define INIT_SEI_MESSAGE_COUNT 16 // initial SEI message count #define INIT_SEI_PAYLOAD_BUF_SIZE 1024 * 1024 // initial SEI payload buffer size, 1 MB +#define DECODE_BUF_POOL_EXTENSION 2 + +enum { + kNotUsed = 0, + kTopFieldUsedForDecode = 1, + kBottomFieldUsedForDecode = 1 << 1, + kFrameUsedForDecode = kTopFieldUsedForDecode | kBottomFieldUsedForDecode, + kFrameUsedForDisplay = 1 << 2 +} FrameBufUseStatus; /** * @brief Base class for video parsing @@ -109,6 +118,21 @@ protected: uint32_t pic_height_; bool new_sps_activated_; + // Decoded buffer pool + typedef struct { + uint32_t use_status; // refer to FrameBufUseStatus + uint32_t pic_order_cnt; + } DecodeFrameBuffer; + uint32_t dec_buf_pool_size_; /* Number of decoded frame surfaces in the pool which are recycled. The size should be greater + than or equal to DPB size (normally greater to guarantee smooth operations). The value is + set to max_num_decode_surfaces from the decoder but parser checks and increases if needed. */ + /* This array maps to VA surface array allocated at VA-API layer. A frame in the pool is identified by its index in the array, which + * is used to retrieve the VA surface Id. + */ + std::vector decode_buffer_pool_; + uint32_t num_output_pics_; // number of pictures that are ready to be ouput + std::vector output_pic_list_; // sorted output frame index to decode_buffer_pool_ + Rational frame_rate_; RocdecVideoFormat video_format_params_; @@ -141,6 +165,17 @@ protected: uint32_t sei_payload_buf_size_; uint32_t sei_payload_size_; // total SEI payload size of the current frame + /*! \brief Function to check the initially set (by decoder) decode buffer pool size and adjust if needed + * \param dpb_size The DPB buffer size of the current sequence + */ + void CheckAndAdjustDecBufPoolSize(int dpb_size); + + /*! \brief Callback function to output decoded pictures from DPB for post-processing. + * \param [in] no_delay Indicator to override the display delay parameter wth no delay + * \return ParserResult + */ + ParserResult OutputDecodedPictures(bool no_delay); + /*! \brief Function to get the NAL Unit data * \return Returns OK if successful, else error code */ @@ -161,6 +196,10 @@ protected: * \return No return value */ void ParseSeiMessage(uint8_t *nalu, size_t size); + + /*! \brief Function to initialize the decoded buffer pool + */ + void InitDecBufPool(); }; // helpers diff --git a/projects/rocdecode/utils/rocvideodecode/roc_video_dec.cpp b/projects/rocdecode/utils/rocvideodecode/roc_video_dec.cpp index e3dadd7121..882236df4b 100644 --- a/projects/rocdecode/utils/rocvideodecode/roc_video_dec.cpp +++ b/projects/rocdecode/utils/rocvideodecode/roc_video_dec.cpp @@ -23,9 +23,9 @@ THE SOFTWARE. #include "roc_video_dec.h" RocVideoDecoder::RocVideoDecoder(int device_id, OutputSurfaceMemoryType out_mem_type, rocDecVideoCodec codec, bool force_zero_latency, - const Rect *p_crop_rect, bool extract_user_sei_Message, int max_width, int max_height, uint32_t clk_rate) : + const Rect *p_crop_rect, bool extract_user_sei_Message, uint32_t disp_delay, int max_width, int max_height, uint32_t clk_rate) : device_id_{device_id}, out_mem_type_(out_mem_type), codec_id_(codec), b_force_zero_latency_(force_zero_latency), - b_extract_sei_message_(extract_user_sei_Message), max_width_ (max_width), max_height_(max_height) { + b_extract_sei_message_(extract_user_sei_Message), disp_delay_(disp_delay), max_width_ (max_width), max_height_(max_height) { if (!InitHIP(device_id_)) { THROW("Failed to initilize the HIP"); @@ -39,9 +39,9 @@ RocVideoDecoder::RocVideoDecoder(int device_id, OutputSurfaceMemoryType out_mem_ // create rocdec videoparser RocdecParserParams parser_params = {}; parser_params.codec_type = codec_id_; - parser_params.max_num_decode_surfaces = 1; + 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 = 0; + parser_params.max_display_delay = disp_delay_; parser_params.user_data = this; parser_params.pfn_sequence_callback = HandleVideoSequenceProc; parser_params.pfn_decode_picture = HandlePictureDecodeProc; diff --git a/projects/rocdecode/utils/rocvideodecode/roc_video_dec.h b/projects/rocdecode/utils/rocvideodecode/roc_video_dec.h index 40fefb6dcd..8069cc9ec2 100644 --- a/projects/rocdecode/utils/rocvideodecode/roc_video_dec.h +++ b/projects/rocdecode/utils/rocvideodecode/roc_video_dec.h @@ -50,7 +50,8 @@ extern "C" { * \brief AMD The rocDecode video decoder for AMD’s GPUs. */ -#define MAX_FRAME_NUM 16 +#define MAX_FRAME_NUM 16 + typedef int (ROCDECAPI *PFNRECONFIGUEFLUSHCALLBACK)(void *, uint32_t, void *); typedef enum SeiAvcHevcPayloadType_enum { @@ -177,7 +178,7 @@ class RocVideoDecoder { * @param force_zero_latency */ RocVideoDecoder(int device_id, OutputSurfaceMemoryType out_mem_type, rocDecVideoCodec codec, bool force_zero_latency = false, - const Rect *p_crop_rect = nullptr, bool extract_user_SEI_Message = 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); ~RocVideoDecoder(); @@ -426,6 +427,7 @@ class RocVideoDecoder { OutputSurfaceMemoryType out_mem_type_ = OUT_SURFACE_MEM_DEV_INTERNAL; bool b_extract_sei_message_ = false; bool b_force_zero_latency_ = false; + uint32_t disp_delay_; ReconfigParams *p_reconfig_params_ = nullptr; int32_t num_frames_flushed_during_reconfig_ = 0; hipDeviceProp_t hip_dev_prop_;