[FFmpeg-devel] avcodec/nvenc: Reconfigure resolution on-the-fly

Submitted by Oliver Collyer on March 6, 2019, 7:08 p.m.

Details

Message ID 4FD50327-D018-40C4-AC20-F64C598A9ED8@mac.com
State New
Headers show

Commit Message

Oliver Collyer March 6, 2019, 7:08 p.m.
>> diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
>> index 0ce22ec4fa..7087f82ce1 100644
>> --- a/libavcodec/avcodec.h
>> +++ b/libavcodec/avcodec.h
>> @@ -3357,6 +3357,12 @@ typedef struct AVCodecContext {
>>      * - encoding: unused
>>      */
>>     int discard_damaged_percentage;
>> +
>> +    /*
>> +     * Video encoding only. Sets the maximum picture size for encoders that
>> +     * support adjusting the picture size dynamically during encoding.
>> +     */
>> +     int max_width, max_height;
>> } AVCodecContext;
>> 
> 
> I really don't like introducing public API fields for this. Maybe a
> private nvenc option would be better at this point.
> 

So would this be suitable?

The only thing was that I wasn't sure about exposing these options to ffmpeg though, as they're useless to ffmpeg unless ffmpeg were to add features for dynamically changing the encoded resolution....I just couldn't figure out how you can add a private option that isn't exposed in this way though.

>>>>>

Comments

Oliver Collyer March 7, 2019, 8:32 p.m.
> 
>>> diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
>>> index 0ce22ec4fa..7087f82ce1 100644
>>> --- a/libavcodec/avcodec.h
>>> +++ b/libavcodec/avcodec.h
>>> @@ -3357,6 +3357,12 @@ typedef struct AVCodecContext {
>>>     * - encoding: unused
>>>     */
>>>    int discard_damaged_percentage;
>>> +
>>> +    /*
>>> +     * Video encoding only. Sets the maximum picture size for encoders that
>>> +     * support adjusting the picture size dynamically during encoding.
>>> +     */
>>> +     int max_width, max_height;
>>> } AVCodecContext;
>>> 
>> 
>> I really don't like introducing public API fields for this. Maybe a
>> private nvenc option would be better at this point.
>> 
> 
> So would this be suitable?
> 
> The only thing was that I wasn't sure about exposing these options to ffmpeg though, as they're useless to ffmpeg unless ffmpeg were to add features for dynamically changing the encoded resolution....I just couldn't figure out how you can add a private option that isn't exposed in this way though.
> 

So in the absence of any further feedback I've created a patch.
Timo Rothenpieler March 7, 2019, 11:30 p.m.
looks good at first glance, will give it a test this weekend.

Patch hide | download patch | download mbox

diff --git a/libavcodec/nvenc.c b/libavcodec/nvenc.c
index 304a684e0c..1a1331d468 100644
--- a/libavcodec/nvenc.c
+++ b/libavcodec/nvenc.c
@@ -384,6 +384,7 @@  static int nvenc_check_capabilities(AVCodecContext *avctx)
 #endif
 
     ctx->support_dyn_bitrate = nvenc_check_cap(avctx, NV_ENC_CAPS_SUPPORT_DYN_BITRATE_CHANGE);
+    ctx->support_dyn_res = nvenc_check_cap(avctx, NV_ENC_CAPS_SUPPORT_DYN_RES_CHANGE);
 
     return 0;
 }
@@ -1121,6 +1122,8 @@  static av_cold int nvenc_setup_encoder(AVCodecContext *avctx)
 
     ctx->init_encode_params.encodeHeight = avctx->height;
     ctx->init_encode_params.encodeWidth = avctx->width;
+    ctx->init_encode_params.maxEncodeHeight = ctx->max_height;
+    ctx->init_encode_params.maxEncodeWidth = ctx->max_width;
 
     ctx->init_encode_params.encodeConfig = &ctx->encode_config;
 
@@ -1276,8 +1279,8 @@  static av_cold int nvenc_alloc_surface(AVCodecContext *avctx, int idx)
         }
 
         allocSurf.version = NV_ENC_CREATE_INPUT_BUFFER_VER;
-        allocSurf.width = avctx->width;
-        allocSurf.height = avctx->height;
+        allocSurf.width = FFMAX(ctx->max_width, avctx->width);
+        allocSurf.height = FFMAX(ctx->max_height, avctx->height);
         allocSurf.bufferFmt = ctx->surfaces[idx].format;
 
         nv_status = p_nvenc->nvEncCreateInputBuffer(ctx->nvencoder, &allocSurf);
@@ -1926,7 +1929,7 @@  static void reconfig_encoder(AVCodecContext *avctx, const AVFrame *frame)
     NV_ENC_RECONFIGURE_PARAMS params = { 0 };
     int needs_reconfig = 0;
     int needs_encode_config = 0;
-    int reconfig_bitrate = 0, reconfig_dar = 0;
+    int reconfig_bitrate = 0, reconfig_dar = 0, reconfig_res = 0;
     int dw, dh;
 
     params.version = NV_ENC_RECONFIGURE_PARAMS_VER;
@@ -1986,6 +1989,24 @@  static void reconfig_encoder(AVCodecContext *avctx, const AVFrame *frame)
         }
     }
 
+    if (ctx->support_dyn_res && ctx->init_encode_params.maxEncodeWidth && ctx->init_encode_params.maxEncodeHeight) {
+        if (params.reInitEncodeParams.encodeWidth != avctx->width || params.reInitEncodeParams.encodeHeight != avctx->height) {
+            av_log(avctx, AV_LOG_VERBOSE,
+                "resolution change: %d x %d -> %d x %d\n",
+                params.reInitEncodeParams.encodeWidth,
+                params.reInitEncodeParams.encodeHeight,
+                (uint32_t)avctx->width,
+                (uint32_t)avctx->height);
+
+            params.reInitEncodeParams.encodeWidth = avctx->width;
+            params.reInitEncodeParams.encodeHeight = avctx->height;
+            params.forceIDR = 1;
+
+            needs_reconfig = 1;
+            reconfig_res = 1;
+        }
+    }
+
     if (!needs_encode_config)
         params.reInitEncodeParams.encodeConfig = NULL;
 
@@ -2005,6 +2026,10 @@  static void reconfig_encoder(AVCodecContext *avctx, const AVFrame *frame)
                 ctx->encode_config.rcParams.vbvBufferSize = params.reInitEncodeParams.encodeConfig->rcParams.vbvBufferSize;
             }
 
+            if (reconfig_res) {
+                ctx->init_encode_params.encodeWidth = params.reInitEncodeParams.encodeWidth;
+                ctx->init_encode_params.encodeHeight = params.reInitEncodeParams.encodeHeight;
+            }
         }
     }
 }
diff --git a/libavcodec/nvenc.h b/libavcodec/nvenc.h
index ddd6168409..30a3aa6f6f 100644
--- a/libavcodec/nvenc.h
+++ b/libavcodec/nvenc.h
@@ -158,6 +158,7 @@  typedef struct NvencContext
     int first_packet_output;
 
     int support_dyn_bitrate;
+    int support_dyn_res;
 
     void *nvencoder;
 
@@ -192,6 +193,8 @@  typedef struct NvencContext
     int coder;
     int b_ref_mode;
     int a53_cc;
+    int max_width;
+    int max_height;
 } NvencContext;
 
 int ff_nvenc_encode_init(AVCodecContext *avctx);
diff --git a/libavcodec/nvenc_h264.c b/libavcodec/nvenc_h264.c
index a6623f5f35..9c9a0c7b8d 100644
--- a/libavcodec/nvenc_h264.c
+++ b/libavcodec/nvenc_h264.c
@@ -138,6 +138,10 @@  static const AVOption options[] = {
     { "middle",       "",                                   0,                    AV_OPT_TYPE_CONST, { .i64 = 2 }, 0, 0,       VE, "b_ref_mode" },
 #endif
     { "a53cc",        "Use A53 Closed Captions (if available)", OFFSET(a53_cc),   AV_OPT_TYPE_BOOL,  { .i64 = 1 }, 0, 1, VE },
+    { "max_width",    "Maximum encode width for dynamic resolution encoding",
+                                                            OFFSET(max_width),    AV_OPT_TYPE_INT,   {.i64 = 0 }, 0, INT_MAX, VE },
+    { "max_height",   "Maximum encode height for dynamic resolution encoding",
+                                                            OFFSET(max_height),   AV_OPT_TYPE_INT,   {.i64 = 0 }, 0, INT_MAX, VE },
     { NULL }
 };
 
diff --git a/libavcodec/nvenc_hevc.c b/libavcodec/nvenc_hevc.c
index d567d960ba..49cb526740 100644
--- a/libavcodec/nvenc_hevc.c
+++ b/libavcodec/nvenc_hevc.c
@@ -127,6 +127,10 @@  static const AVOption options[] = {
     { "each",         "",                                   0,                    AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0,       VE, "b_ref_mode" },
     { "middle",       "",                                   0,                    AV_OPT_TYPE_CONST, { .i64 = 2 }, 0, 0,       VE, "b_ref_mode" },
 #endif
+    { "max_width",    "Maximum encode width for dynamic resolution encoding",
+                                                            OFFSET(max_width),    AV_OPT_TYPE_INT,   {.i64 = 0 }, 0, INT_MAX, VE },
+    { "max_height",   "Maximum encode height for dynamic resolution encoding",
+                                                            OFFSET(max_height),   AV_OPT_TYPE_INT,   {.i64 = 0 }, 0, INT_MAX, VE },
     { NULL }
 };