From 8503c64aa897cdb6d14bc5fd13f3c64ca38c4392 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Sat, 12 Oct 2024 00:28:00 +0200 Subject: [PATCH] avcodec/amfenc: add support for QueryOutput wait Enable waiting in QueryOutput() based on driver support to reduce unnecessary delays. Fix for issue #10622 Co-authored-by: Araz Iusubov --- libavcodec/amfenc.c | 21 ++++++++++++++++++--- libavcodec/amfenc.h | 3 +++ libavcodec/amfenc_av1.c | 5 +++++ libavcodec/amfenc_h264.c | 5 +++++ libavcodec/amfenc_hevc.c | 5 +++++ 5 files changed, 36 insertions(+), 3 deletions(-) diff --git a/libavcodec/amfenc.c b/libavcodec/amfenc.c index 225fb9df27..03d75031f5 100644 --- a/libavcodec/amfenc.c +++ b/libavcodec/amfenc.c @@ -426,6 +426,8 @@ static int amf_init_encoder(AVCodecContext *avctx) res = ctx->factory->pVtbl->CreateComponent(ctx->factory, ctx->context, codec_id, &ctx->encoder); AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_ENCODER_NOT_FOUND, "CreateComponent(%ls) failed with error %d\n", codec_id, res); + ctx->submitted_frame = 0; + return 0; } @@ -541,7 +543,6 @@ static int amf_copy_buffer(AVCodecContext *avctx, AVPacket *pkt, AMFBuffer *buff if ((ctx->max_b_frames > 0 || ((ctx->pa_adaptive_mini_gop == 1) ? true : false)) && ctx->dts_delay == 0) { int64_t timestamp_last = AV_NOPTS_VALUE; size_t can_read = av_fifo_can_read(ctx->timestamp_list); - AMF_RETURN_IF_FALSE(ctx, can_read > 0, AVERROR_UNKNOWN, "timestamp_list is empty while max_b_frames = %d\n", avctx->max_b_frames); av_fifo_peek(ctx->timestamp_list, ×tamp_last, 1, can_read - 1); @@ -826,6 +827,13 @@ int ff_amf_receive_packet(AVCodecContext *avctx, AVPacket *avpkt) av_frame_unref(frame); ret = av_fifo_write(ctx->timestamp_list, &pts, 1); + + if (ctx->submitted_frame == 0) + { + ctx->use_b_frame = (ctx->max_b_frames > 0 || ((ctx->pa_adaptive_mini_gop == 1) ? true : false)); + } + ctx->submitted_frame++; + if (ret < 0) return ret; } @@ -835,7 +843,7 @@ int ff_amf_receive_packet(AVCodecContext *avctx, AVPacket *avpkt) do { block_and_wait = 0; // poll data - if (!avpkt->data && !avpkt->buf) { + if (!avpkt->data && !avpkt->buf && (ctx->use_b_frame ? (ctx->submitted_frame >= 2) : true) ) { res_query = ctx->encoder->pVtbl->QueryOutput(ctx->encoder, &data); if (data) { // copy data to packet @@ -845,6 +853,7 @@ int ff_amf_receive_packet(AVCodecContext *avctx, AVPacket *avpkt) data->pVtbl->QueryInterface(data, &guid, (void**)&buffer); // query for buffer interface ret = amf_copy_buffer(avctx, avpkt, buffer); + ctx->submitted_frame++; buffer->pVtbl->Release(buffer); if (data->pVtbl->HasProperty(data, L"av_frame_ref")) { @@ -884,6 +893,7 @@ int ff_amf_receive_packet(AVCodecContext *avctx, AVPacket *avpkt) av_frame_unref(ctx->delayed_frame); AMF_RETURN_IF_FALSE(ctx, res_resubmit == AMF_OK, AVERROR_UNKNOWN, "Repeated SubmitInput() failed with error %d\n", res_resubmit); + ctx->submitted_frame++; ret = av_fifo_write(ctx->timestamp_list, &pts, 1); if (ret < 0) return ret; @@ -902,7 +912,12 @@ int ff_amf_receive_packet(AVCodecContext *avctx, AVPacket *avpkt) if (query_output_data_flag == 0) { if (res_resubmit == AMF_INPUT_FULL || ctx->delayed_drain || (ctx->eof && res_query != AMF_EOF) || (ctx->hwsurfaces_in_queue >= ctx->hwsurfaces_in_queue_max)) { block_and_wait = 1; - av_usleep(1000); + + // Only sleep if the driver doesn't support waiting in QueryOutput() + // or if we already have output data so we will skip calling it. + if (!ctx->query_timeout_supported || avpkt->data || avpkt->buf) { + av_usleep(1000); + } } } } while (block_and_wait); diff --git a/libavcodec/amfenc.h b/libavcodec/amfenc.h index 0f2abcbd82..d83ee5bf1c 100644 --- a/libavcodec/amfenc.h +++ b/libavcodec/amfenc.h @@ -68,6 +68,7 @@ typedef struct AmfContext { int hwsurfaces_in_queue; int hwsurfaces_in_queue_max; + int query_timeout_supported; // helpers to handle async calls int delayed_drain; @@ -77,6 +78,8 @@ typedef struct AmfContext { // shift dts back by max_b_frames in timing AVFifo *timestamp_list; int64_t dts_delay; + int submitted_frame; + amf_bool use_b_frame; // common encoder option options diff --git a/libavcodec/amfenc_av1.c b/libavcodec/amfenc_av1.c index fc28287a48..79b982e0d2 100644 --- a/libavcodec/amfenc_av1.c +++ b/libavcodec/amfenc_av1.c @@ -463,6 +463,11 @@ FF_ENABLE_DEPRECATION_WARNINGS } } + // Wait inside QueryOutput() if supported by the driver + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_QUERY_TIMEOUT, 1); + res = ctx->encoder->pVtbl->GetProperty(ctx->encoder, AMF_VIDEO_ENCODER_AV1_QUERY_TIMEOUT, &var); + ctx->query_timeout_supported = res == AMF_OK && var.int64Value; + // init encoder res = ctx->encoder->pVtbl->Init(ctx->encoder, ctx->format, avctx->width, avctx->height); AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_BUG, "encoder->Init() failed with error %d\n", res); diff --git a/libavcodec/amfenc_h264.c b/libavcodec/amfenc_h264.c index 959be9eab6..07aa9deb40 100644 --- a/libavcodec/amfenc_h264.c +++ b/libavcodec/amfenc_h264.c @@ -483,6 +483,11 @@ FF_ENABLE_DEPRECATION_WARNINGS AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_REF_B_PIC_DELTA_QP, ctx->ref_b_frame_delta_qp); } + // Wait inside QueryOutput() if supported by the driver + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_QUERY_TIMEOUT, 1); + res = ctx->encoder->pVtbl->GetProperty(ctx->encoder, AMF_VIDEO_ENCODER_QUERY_TIMEOUT, &var); + ctx->query_timeout_supported = res == AMF_OK && var.int64Value; + // Initialize Encoder res = ctx->encoder->pVtbl->Init(ctx->encoder, ctx->format, avctx->width, avctx->height); AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_BUG, "encoder->Init() failed with error %d\n", res); diff --git a/libavcodec/amfenc_hevc.c b/libavcodec/amfenc_hevc.c index 8ceaee0851..f633677574 100644 --- a/libavcodec/amfenc_hevc.c +++ b/libavcodec/amfenc_hevc.c @@ -425,6 +425,11 @@ FF_ENABLE_DEPRECATION_WARNINGS } } + // Wait inside QueryOutput() if supported by the driver + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_QUERY_TIMEOUT, 1); + res = ctx->encoder->pVtbl->GetProperty(ctx->encoder, AMF_VIDEO_ENCODER_HEVC_QUERY_TIMEOUT, &var); + ctx->query_timeout_supported = res == AMF_OK && var.int64Value; + // init encoder res = ctx->encoder->pVtbl->Init(ctx->encoder, ctx->format, avctx->width, avctx->height); AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_BUG, "encoder->Init() failed with error %d\n", res);