[FFmpeg-devel] [PATCH, v2] avcodec/amfenc: increase precision of Sleep() on Windows

Cameron Gutman aicommander at gmail.com
Tue Sep 10 02:48:18 EEST 2024


On Mon, Sep 9, 2024 at 11:05 AM Araz <primeadvice at gmail.com> wrote:
>>
>> > I took a look at this using AMD's AMF EncoderLatency sample and found that
>> > setting the QUERY_TIMEOUT option to 50 ms (as is default for the new AMF
>> > HQ and HQLL usage values) results in latency that is better than the
>> > current AMF code in FFmpeg *and* this patch without having to touch
>> > the process's timer precision.
>> >
>> > Here are the results from QUERY_TIMEOUT=0, amf_sleep(1), 1ms timer period:
>> > Encoder: AMFVideoEncoderVCE_AVC
>> > Total  : Frames = 500 Duration = 1157.16ms FPS = 432.09frames
>> > Latency: First,Min,Max = 7.12ms, 1.53ms, 3.73ms
>> > Latency: Average = 1.99ms
>> >
>> > and the results from QUERY_TIMEOUT=50, default timer period:
>> > Encoder: AMFVideoEncoderVCE_AVC
>> > Total  : Frames = 500 Duration = 933.03ms FPS = 535.89frames
>> > Latency: First,Min,Max = 5.80ms, 1.49ms, 2.50ms
>> > Latency: Average = 1.58ms
>> >
>> > This seems to clearly demonstrate that QUERY_TIMEOUT is a better approach
>> > than adjusting timer resolution. It avoids process-wide effects *and*
>> > it even performs better on top of that.
>> >
>> >
>> > Cameron
>
>
> Thanks everyone and Cameron,
> TIMEOUT might be a possible solution.
> Need some time for evaluating the performance impact of using it.
> The problem is that FFmpeg calls SubmitInput and QueryOutput
> from the same thread and if B-frames and/or look-ahead enabled, initially
> not enough input submitted and QueryOutput will wait till timeout value.

Ah I see, I guess we can just replace the fixed 1 ms sleep with a fixed
1 ms QUERY_TIMEOUT. We won't get the full power gains of waking exactly
once per frame when encoding is complete, but it should solve the issue
of having to wait a full 15.6ms to realize output data is available.

How about something like this?

diff --git a/libavcodec/amfenc.c b/libavcodec/amfenc.c
index 41eaef9758..555cdcde85 100644
--- a/libavcodec/amfenc.c
+++ b/libavcodec/amfenc.c
@@ -861,7 +861,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 d985d01bb1..a634c133c6 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;
diff --git a/libavcodec/amfenc_av1.c b/libavcodec/amfenc_av1.c
index 2a7a782063..f3058c5b96 100644
--- a/libavcodec/amfenc_av1.c
+++ b/libavcodec/amfenc_av1.c
@@ -467,6 +467,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 8edd39c633..6d7c5808ee 100644
--- a/libavcodec/amfenc_h264.c
+++ b/libavcodec/amfenc_h264.c
@@ -482,6 +482,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 4898824f3a..4457990247 100644
--- a/libavcodec/amfenc_hevc.c
+++ b/libavcodec/amfenc_hevc.c
@@ -429,6 +429,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);


More information about the ffmpeg-devel mailing list