From 81272eaa82ac2a808b1aee3077675232a64c3f43 Mon Sep 17 00:00:00 2001 From: Seungha Yang Date: Tue, 3 Sep 2019 21:33:15 +0900 Subject: [PATCH] nvenc: Add more rate-control options New rate-control modes are introduced (if device can support) * cbr-ld-hr: CBR low-delay high quality * cbr-hq: CBR high quality * vbr-hq: VBR high quality Also, various configurable rate-control related properties are added. --- sys/nvcodec/gstnvbaseenc.c | 264 +++++++++++++++++++++++++++++++------ sys/nvcodec/gstnvbaseenc.h | 19 +++ sys/nvcodec/gstnvenc.c | 54 ++++++++ sys/nvcodec/gstnvh264enc.c | 84 ++++++++++++ sys/nvcodec/gstnvh265enc.c | 84 ++++++++++++ 5 files changed, 467 insertions(+), 38 deletions(-) diff --git a/sys/nvcodec/gstnvbaseenc.c b/sys/nvcodec/gstnvbaseenc.c index 56b4eaffb0..4028e6b855 100644 --- a/sys/nvcodec/gstnvbaseenc.c +++ b/sys/nvcodec/gstnvbaseenc.c @@ -106,13 +106,17 @@ gst_nv_rc_mode_get_type (void) static GType nv_rc_mode_type = 0; static const GEnumValue modes[] = { - {GST_NV_RC_MODE_DEFAULT, "Default (from NVENC preset)", "default"}, + {GST_NV_RC_MODE_DEFAULT, "Default", "default"}, {GST_NV_RC_MODE_CONSTQP, "Constant Quantization", "constqp"}, {GST_NV_RC_MODE_CBR, "Constant Bit Rate", "cbr"}, {GST_NV_RC_MODE_VBR, "Variable Bit Rate", "vbr"}, {GST_NV_RC_MODE_VBR_MINQP, - "Variable Bit Rate (with minimum quantization parameter)", - "vbr-minqp"}, + "Variable Bit Rate " + "(with minimum quantization parameter, DEPRECATED)", "vbr-minqp"}, + {GST_NV_RC_MODE_CBR_LOWDELAY_HQ, + "Low-Delay CBR, High Quality", "cbr-ld-hq"}, + {GST_NV_RC_MODE_CBR_HQ, "CBR, High Quality (slower)", "cbr-hq"}, + {GST_NV_RC_MODE_VBR_HQ, "VBR, High Quality (slower)", "vbr-hq"}, {0, NULL, NULL}, }; @@ -127,15 +131,18 @@ _rc_mode_to_nv (GstNvRCMode mode) { switch (mode) { case GST_NV_RC_MODE_DEFAULT: - return -1; + return NV_ENC_PARAMS_RC_VBR; #define CASE(gst,nv) case G_PASTE(GST_NV_RC_MODE_,gst): return G_PASTE(NV_ENC_PARAMS_RC_,nv) CASE (CONSTQP, CONSTQP); CASE (CBR, CBR); CASE (VBR, VBR); CASE (VBR_MINQP, VBR_MINQP); + CASE (CBR_LOWDELAY_HQ, CBR_LOWDELAY_HQ); + CASE (CBR_HQ, CBR_HQ); + CASE (VBR_HQ, VBR_HQ); #undef CASE default: - return -1; + return NV_ENC_PARAMS_RC_VBR; } } @@ -150,6 +157,14 @@ enum PROP_QP_MAX, PROP_QP_CONST, PROP_GOP_SIZE, + PROP_MAX_BITRATE, + PROP_SPATIAL_AQ, + PROP_AQ_STRENGTH, + PROP_NON_REF_P, + PROP_ZEROLATENCY, + PROP_STRICT_GOP, + PROP_CONST_QUALITY, + PROP_I_ADAPT, }; #define DEFAULT_PRESET GST_NV_PRESET_DEFAULT @@ -159,6 +174,14 @@ enum #define DEFAULT_QP_MAX -1 #define DEFAULT_QP_CONST -1 #define DEFAULT_GOP_SIZE 75 +#define DEFAULT_MAX_BITRATE 0 +#define DEFAULT_SPATIAL_AQ FALSE +#define DEFAULT_AQ_STRENGTH 0 +#define DEFAULT_NON_REF_P FALSE +#define DEFAULT_ZEROLATENCY FALSE +#define DEFAULT_STRICT_GOP FALSE +#define DEFAULT_CONST_QUALITY 0 +#define DEFAULT_I_ADAPT FALSE /* This lock is needed to prevent the situation where multiple encoders are * initialised at the same time which appears to cause excessive CPU usage over @@ -285,6 +308,52 @@ gst_nv_base_enc_class_init (GstNvBaseEncClass * klass) DEFAULT_BITRATE, G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_MAX_BITRATE, + g_param_spec_uint ("max-bitrate", "Max Bitrate", + "Maximum Bitrate in kbit/sec (ignored for CBR mode)", 0, 2000 * 1024, + DEFAULT_MAX_BITRATE, + G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING | + G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_SPATIAL_AQ, + g_param_spec_boolean ("spatial-aq", "Spatial AQ", + "Spatial Adaptive Quantization", + DEFAULT_SPATIAL_AQ, + G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING | + G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_AQ_STRENGTH, + g_param_spec_uint ("aq-strength", "AQ Strength", + "Adaptive Quantization Strength when spatial-aq is enabled" + " from 1 (low) to 15 (aggressive), (0 = autoselect)", + 0, 15, DEFAULT_AQ_STRENGTH, + G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING | + G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_NON_REF_P, + g_param_spec_boolean ("nonref-p", "Nonref P", + "Automatic insertion of non-reference P-frames", DEFAULT_NON_REF_P, + G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING | + G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_ZEROLATENCY, + g_param_spec_boolean ("zerolatency", "Zerolatency", + "Zero latency operation (no reordering delay)", DEFAULT_ZEROLATENCY, + G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING | + G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_STRICT_GOP, + g_param_spec_boolean ("strict-gop", "Strict GOP", + "Minimize GOP-to-GOP rate fluctuations", DEFAULT_STRICT_GOP, + G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING | + G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_CONST_QUALITY, + g_param_spec_double ("const-quality", "Constant Quality", + "Target Constant Quality level for VBR mode (0 = automatic)", + 0, 51, DEFAULT_CONST_QUALITY, + G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING | + G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_I_ADAPT, + g_param_spec_boolean ("i-adapt", "I Adapt", + "Enable adaptive I-frame insert when lookahead is enabled", + DEFAULT_I_ADAPT, + G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY | + G_PARAM_STATIC_STRINGS)); } static gboolean @@ -713,6 +782,14 @@ gst_nv_base_enc_init (GstNvBaseEnc * nvenc) nvenc->qp_const = DEFAULT_QP_CONST; nvenc->bitrate = DEFAULT_BITRATE; nvenc->gop_size = DEFAULT_GOP_SIZE; + nvenc->max_bitrate = DEFAULT_MAX_BITRATE; + nvenc->spatial_aq = DEFAULT_SPATIAL_AQ; + nvenc->aq_strength = DEFAULT_AQ_STRENGTH; + nvenc->non_refp = DEFAULT_NON_REF_P; + nvenc->zerolatency = DEFAULT_ZEROLATENCY; + nvenc->strict_gop = DEFAULT_STRICT_GOP; + nvenc->const_quality = DEFAULT_CONST_QUALITY; + nvenc->i_adapt = DEFAULT_I_ADAPT; GST_VIDEO_ENCODER_STREAM_LOCK (encoder); GST_VIDEO_ENCODER_STREAM_UNLOCK (encoder); @@ -1112,6 +1189,83 @@ _get_frame_data_height (GstVideoInfo * info) return ret; } +static void +gst_nv_base_enc_setup_rate_control (GstNvBaseEnc * nvenc, + NV_ENC_RC_PARAMS * rc_params) +{ + GstNvRCMode rc_mode = nvenc->rate_control_mode; + NV_ENC_PARAMS_RC_MODE nv_rcmode; + + if (nvenc->bitrate) + rc_params->averageBitRate = nvenc->bitrate * 1024; + + if (nvenc->max_bitrate) + rc_params->maxBitRate = nvenc->max_bitrate * 1024; + + if (nvenc->vbv_buffersize) + rc_params->vbvBufferSize = nvenc->vbv_buffersize * 1024; + + /* Guess the best matching mode */ + if (rc_mode == GST_NV_RC_MODE_DEFAULT) { + if (nvenc->qp_const >= 0) { + /* constQP is used only for RC_CONSTQP mode */ + rc_mode = GST_NV_RC_MODE_CONSTQP; + } + } + + if (nvenc->qp_min >= 0) { + rc_params->enableMinQP = 1; + rc_params->minQP.qpInterB = nvenc->qp_min; + rc_params->minQP.qpInterP = nvenc->qp_min; + rc_params->minQP.qpIntra = nvenc->qp_min; + } + + if (nvenc->qp_max >= 0) { + rc_params->enableMaxQP = 1; + rc_params->maxQP.qpInterB = nvenc->qp_max; + rc_params->maxQP.qpInterP = nvenc->qp_max; + rc_params->maxQP.qpIntra = nvenc->qp_max; + } + + if (nvenc->qp_const >= 0) { + rc_params->constQP.qpInterB = nvenc->qp_const; + rc_params->constQP.qpInterP = nvenc->qp_const; + rc_params->constQP.qpIntra = nvenc->qp_const; + } + + nv_rcmode = _rc_mode_to_nv (rc_mode); + if (nv_rcmode == NV_ENC_PARAMS_RC_VBR_MINQP && nvenc->qp_min < 0) { + GST_WARNING_OBJECT (nvenc, "vbr-minqp was requested without qp-min"); + nv_rcmode = NV_ENC_PARAMS_RC_VBR; + } + + rc_params->rateControlMode = nv_rcmode; + + if (nvenc->spatial_aq) { + rc_params->enableAQ = 1; + rc_params->aqStrength = nvenc->aq_strength; + } + + rc_params->enableTemporalAQ = nvenc->temporal_aq; + + if (nvenc->rc_lookahead) { + rc_params->enableLookahead = 1; + rc_params->lookaheadDepth = nvenc->rc_lookahead; + rc_params->disableIadapt = !nvenc->i_adapt; + } + + rc_params->strictGOPTarget = nvenc->strict_gop; + rc_params->enableNonRefP = nvenc->non_refp; + rc_params->zeroReorderDelay = nvenc->zerolatency; + + if (nvenc->const_quality) { + guint scaled = (gint) (nvenc->const_quality * 256.0); + + rc_params->targetQuality = (guint8) (scaled >> 8); + rc_params->targetQualityLSB = (guint8) (scaled & 0xff); + } +} + /* GstVideoEncoder::set_format or by nvenc self if new properties were set. * * NvEncReconfigureEncoder with following conditions are not allowed @@ -1284,32 +1438,7 @@ gst_nv_base_enc_set_format (GstVideoEncoder * enc, GstVideoCodecState * state) params->darHeight = dar_d; } - if (nvenc->rate_control_mode != GST_NV_RC_MODE_DEFAULT) { - params->encodeConfig->rcParams.rateControlMode = - _rc_mode_to_nv (nvenc->rate_control_mode); - if (nvenc->bitrate > 0) { - /* FIXME: this produces larger bitrates?! */ - params->encodeConfig->rcParams.averageBitRate = nvenc->bitrate * 1024; - params->encodeConfig->rcParams.maxBitRate = nvenc->bitrate * 1024; - } - if (nvenc->qp_const > 0) { - params->encodeConfig->rcParams.constQP.qpInterB = nvenc->qp_const; - params->encodeConfig->rcParams.constQP.qpInterP = nvenc->qp_const; - params->encodeConfig->rcParams.constQP.qpIntra = nvenc->qp_const; - } - if (nvenc->qp_min >= 0) { - params->encodeConfig->rcParams.enableMinQP = 1; - params->encodeConfig->rcParams.minQP.qpInterB = nvenc->qp_min; - params->encodeConfig->rcParams.minQP.qpInterP = nvenc->qp_min; - params->encodeConfig->rcParams.minQP.qpIntra = nvenc->qp_min; - } - if (nvenc->qp_max >= 0) { - params->encodeConfig->rcParams.enableMaxQP = 1; - params->encodeConfig->rcParams.maxQP.qpInterB = nvenc->qp_max; - params->encodeConfig->rcParams.maxQP.qpInterP = nvenc->qp_max; - params->encodeConfig->rcParams.maxQP.qpIntra = nvenc->qp_max; - } - } + gst_nv_base_enc_setup_rate_control (nvenc, ¶ms->encodeConfig->rcParams); params->enableWeightedPrediction = nvenc->weighted_pred; @@ -2170,6 +2299,8 @@ gst_nv_base_enc_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (object); + GstNvBaseEncClass *klass = GST_NV_BASE_ENC_GET_CLASS (nvenc); + gboolean reconfig = TRUE; switch (prop_id) { case PROP_PRESET: @@ -2178,33 +2309,66 @@ gst_nv_base_enc_set_property (GObject * object, guint prop_id, gst_nv_base_enc_schedule_reconfig (nvenc); break; case PROP_RC_MODE: - nvenc->rate_control_mode = g_value_get_enum (value); - gst_nv_base_enc_schedule_reconfig (nvenc); + { + GstNvRCMode rc_mode = g_value_get_enum (value); + NV_ENC_PARAMS_RC_MODE nv_rc_mode = _rc_mode_to_nv (rc_mode); + + if ((klass->device_caps.rc_modes & nv_rc_mode) == nv_rc_mode) { + nvenc->rate_control_mode = rc_mode; + } else { + GST_WARNING_OBJECT (nvenc, + "device does not support requested rate control mode %d", rc_mode); + reconfig = FALSE; + } break; + } case PROP_QP_MIN: nvenc->qp_min = g_value_get_int (value); - gst_nv_base_enc_schedule_reconfig (nvenc); break; case PROP_QP_MAX: nvenc->qp_max = g_value_get_int (value); - gst_nv_base_enc_schedule_reconfig (nvenc); break; case PROP_QP_CONST: nvenc->qp_const = g_value_get_int (value); - gst_nv_base_enc_schedule_reconfig (nvenc); break; case PROP_BITRATE: nvenc->bitrate = g_value_get_uint (value); - gst_nv_base_enc_schedule_reconfig (nvenc); break; case PROP_GOP_SIZE: nvenc->gop_size = g_value_get_int (value); - gst_nv_base_enc_schedule_reconfig (nvenc); + break; + case PROP_MAX_BITRATE: + nvenc->max_bitrate = g_value_get_uint (value); + break; + case PROP_SPATIAL_AQ: + nvenc->spatial_aq = g_value_get_boolean (value); + break; + case PROP_AQ_STRENGTH: + nvenc->aq_strength = g_value_get_uint (value); + break; + case PROP_NON_REF_P: + nvenc->non_refp = g_value_get_boolean (value); + break; + case PROP_ZEROLATENCY: + nvenc->zerolatency = g_value_get_boolean (value); + break; + case PROP_STRICT_GOP: + nvenc->strict_gop = g_value_get_boolean (value); + break; + case PROP_CONST_QUALITY: + nvenc->const_quality = g_value_get_double (value); + break; + case PROP_I_ADAPT: + nvenc->i_adapt = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + reconfig = FALSE; break; } + + if (reconfig) + gst_nv_base_enc_schedule_reconfig (nvenc); } static void @@ -2239,6 +2403,30 @@ gst_nv_base_enc_get_property (GObject * object, guint prop_id, GValue * value, case PROP_GOP_SIZE: g_value_set_int (value, nvenc->gop_size); break; + case PROP_MAX_BITRATE: + g_value_set_uint (value, nvenc->max_bitrate); + break; + case PROP_SPATIAL_AQ: + g_value_set_boolean (value, nvenc->spatial_aq); + break; + case PROP_AQ_STRENGTH: + g_value_set_uint (value, nvenc->aq_strength); + break; + case PROP_NON_REF_P: + g_value_set_boolean (value, nvenc->non_refp); + break; + case PROP_ZEROLATENCY: + g_value_set_boolean (value, nvenc->zerolatency); + break; + case PROP_STRICT_GOP: + g_value_set_boolean (value, nvenc->strict_gop); + break; + case PROP_CONST_QUALITY: + g_value_set_double (value, nvenc->const_quality); + break; + case PROP_I_ADAPT: + g_value_set_boolean (value, nvenc->i_adapt); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; diff --git a/sys/nvcodec/gstnvbaseenc.h b/sys/nvcodec/gstnvbaseenc.h index bf3ed23029..4bd749f59a 100644 --- a/sys/nvcodec/gstnvbaseenc.h +++ b/sys/nvcodec/gstnvbaseenc.h @@ -56,10 +56,17 @@ typedef enum { GST_NV_RC_MODE_CBR, GST_NV_RC_MODE_VBR, GST_NV_RC_MODE_VBR_MINQP, + GST_NV_RC_MODE_CBR_LOWDELAY_HQ, + GST_NV_RC_MODE_CBR_HQ, + GST_NV_RC_MODE_VBR_HQ, } GstNvRCMode; typedef struct { gboolean weighted_prediction; + gint rc_modes; + gboolean custom_vbv_bufsize; + gboolean lookahead; + gboolean temporal_aq; } GstNvEncDeviceCaps; typedef struct { @@ -74,6 +81,15 @@ typedef struct { gint qp_const; guint bitrate; gint gop_size; + guint max_bitrate; + gboolean spatial_aq; + guint aq_strength; + gboolean non_refp; + /* zero reorder delay (consistent naming with x264) */ + gboolean zerolatency; + gboolean strict_gop; + gdouble const_quality; + gboolean i_adapt; GstCudaContext * cuda_ctx; CUstream cuda_stream; @@ -115,6 +131,9 @@ typedef struct { /*< protected >*/ /* device capability dependent properties, set by subclass */ gboolean weighted_pred; + guint vbv_buffersize; + guint rc_lookahead; + gboolean temporal_aq; } GstNvBaseEnc; typedef struct { diff --git a/sys/nvcodec/gstnvenc.c b/sys/nvcodec/gstnvenc.c index e492cba98c..f384a20ae2 100644 --- a/sys/nvcodec/gstnvenc.c +++ b/sys/nvcodec/gstnvenc.c @@ -681,15 +681,69 @@ gst_nv_enc_register (GstPlugin * plugin, GUID codec_id, const gchar * codec, max_height = 4096; } + caps_param.capsToQuery = NV_ENC_CAPS_SUPPORTED_RATECONTROL_MODES; + if (NvEncGetEncodeCaps (enc, codec_id, &caps_param, + &device_caps.rc_modes) != NV_ENC_SUCCESS) { + device_caps.rc_modes = 0; + } else { + GST_DEBUG ("[device-%d %s] rate control modes: 0x%x", + i, codec, device_caps.rc_modes); +#define IS_SUPPORTED_RC(rc_modes,mode) \ + ((((rc_modes) & (mode)) == mode) ? "supported" : "not supported") + + GST_DEBUG ("\tconst-qp: %s", + IS_SUPPORTED_RC (device_caps.rc_modes, NV_ENC_PARAMS_RC_CONSTQP)); + GST_DEBUG ("\tvbr: %s", + IS_SUPPORTED_RC (device_caps.rc_modes, NV_ENC_PARAMS_RC_VBR)); + GST_DEBUG ("\tcbr: %s", + IS_SUPPORTED_RC (device_caps.rc_modes, NV_ENC_PARAMS_RC_CBR)); + GST_DEBUG ("\tcbr-lowdelay-hq: %s", + IS_SUPPORTED_RC (device_caps.rc_modes, + NV_ENC_PARAMS_RC_CBR_LOWDELAY_HQ)); + GST_DEBUG ("\tcbr-hq: %s", + IS_SUPPORTED_RC (device_caps.rc_modes, NV_ENC_PARAMS_RC_CBR_HQ)); + GST_DEBUG ("\tvbr-hq: %s", + IS_SUPPORTED_RC (device_caps.rc_modes, NV_ENC_PARAMS_RC_VBR_HQ)); + GST_DEBUG ("\tvbr-minqp: %s (deprecated)", + IS_SUPPORTED_RC (device_caps.rc_modes, NV_ENC_PARAMS_RC_VBR_MINQP)); +#undef IS_SUPPORTED_RC + } + caps_param.capsToQuery = NV_ENC_CAPS_SUPPORT_WEIGHTED_PREDICTION; if (NvEncGetEncodeCaps (enc, codec_id, &caps_param, &device_caps.weighted_prediction) != NV_ENC_SUCCESS) { device_caps.weighted_prediction = FALSE; } + caps_param.capsToQuery = NV_ENC_CAPS_SUPPORT_CUSTOM_VBV_BUF_SIZE; + if (NvEncGetEncodeCaps (enc, codec_id, &caps_param, + &device_caps.custom_vbv_bufsize) != NV_ENC_SUCCESS) { + device_caps.custom_vbv_bufsize = FALSE; + } + + caps_param.capsToQuery = NV_ENC_CAPS_SUPPORT_LOOKAHEAD; + if (NvEncGetEncodeCaps (enc, + codec_id, &caps_param, &device_caps.lookahead) != NV_ENC_SUCCESS) { + device_caps.lookahead = FALSE; + } + + caps_param.capsToQuery = NV_ENC_CAPS_SUPPORT_TEMPORAL_AQ; + if (NvEncGetEncodeCaps (enc, codec_id, &caps_param, + &device_caps.temporal_aq) != NV_ENC_SUCCESS) { + device_caps.temporal_aq = FALSE; + } + DEBUG_DEVICE_CAPS (i, codec, "weighted prediction", device_caps.weighted_prediction); + DEBUG_DEVICE_CAPS (i, codec, "custom vbv-buffer-size", + device_caps.custom_vbv_bufsize); + + DEBUG_DEVICE_CAPS (i, codec, "rc-loockahead", device_caps.lookahead); + + DEBUG_DEVICE_CAPS (i, codec, "temporal adaptive quantization", + device_caps.temporal_aq); + interlace_modes = gst_nvenc_get_interlace_modes (enc, codec_id); sink_templ = gst_caps_new_empty_simple ("video/x-raw"); diff --git a/sys/nvcodec/gstnvh264enc.c b/sys/nvcodec/gstnvh264enc.c index 6ef3c005fb..481791180c 100644 --- a/sys/nvcodec/gstnvh264enc.c +++ b/sys/nvcodec/gstnvh264enc.c @@ -44,10 +44,16 @@ enum PROP_0, PROP_AUD, PROP_WEIGHTED_PRED, + PROP_VBV_BUFFER_SIZE, + PROP_RC_LOOKAHEAD, + PROP_TEMPORAL_AQ, }; #define DEFAULT_AUD TRUE #define DEFAULT_WEIGHTED_PRED FALSE +#define DEFAULT_VBV_BUFFER_SIZE 0 +#define DEFAULT_RC_LOOKAHEAD 0 +#define DEFAULT_TEMPORAL_AQ FALSE static gboolean gst_nv_h264_enc_open (GstVideoEncoder * enc); static gboolean gst_nv_h264_enc_close (GstVideoEncoder * enc); @@ -103,6 +109,36 @@ gst_nv_h264_enc_class_init (GstNvH264EncClass * klass, gpointer data) G_PARAM_STATIC_STRINGS)); } + if (device_caps->custom_vbv_bufsize) { + g_object_class_install_property (gobject_class, + PROP_VBV_BUFFER_SIZE, + g_param_spec_uint ("vbv-buffer-size", "VBV Buffer Size", + "VBV(HRD) Buffer Size in kbits (0 = NVENC default) " + "(Exposed only if supported by device)", 0, G_MAXUINT, + DEFAULT_VBV_BUFFER_SIZE, + G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY | + G_PARAM_STATIC_STRINGS)); + } + + if (device_caps->lookahead) { + g_object_class_install_property (gobject_class, PROP_RC_LOOKAHEAD, + g_param_spec_uint ("rc-lookahead", "Rate Control Lookahead", + "Number of frames for frame type lookahead " + "(Exposed only if supported by device)", 0, 32, + DEFAULT_RC_LOOKAHEAD, + G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY | + G_PARAM_STATIC_STRINGS)); + } + + if (device_caps->temporal_aq) { + g_object_class_install_property (gobject_class, PROP_TEMPORAL_AQ, + g_param_spec_boolean ("temporal-aq", "Temporal AQ", + "Temporal Adaptive Quantization " + "(Exposed only if supported by device)", DEFAULT_TEMPORAL_AQ, + G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING | + G_PARAM_STATIC_STRINGS)); + } + if (cdata->is_default) long_name = g_strdup ("NVENC H.264 Video Encoder"); else @@ -141,6 +177,9 @@ gst_nv_h264_enc_init (GstNvH264Enc * nvenc) /* device capability dependent properties */ baseenc->weighted_pred = DEFAULT_WEIGHTED_PRED; + baseenc->vbv_buffersize = DEFAULT_VBV_BUFFER_SIZE; + baseenc->rc_lookahead = DEFAULT_RC_LOOKAHEAD; + baseenc->temporal_aq = DEFAULT_TEMPORAL_AQ; } static void @@ -442,6 +481,30 @@ gst_nv_h264_enc_set_property (GObject * object, guint prop_id, reconfig = TRUE; } break; + case PROP_VBV_BUFFER_SIZE: + if (!device_caps->custom_vbv_bufsize) { + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } else { + nvenc->vbv_buffersize = g_value_get_uint (value); + reconfig = TRUE; + } + break; + case PROP_RC_LOOKAHEAD: + if (!device_caps->lookahead) { + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } else { + nvenc->rc_lookahead = g_value_get_uint (value); + reconfig = TRUE; + } + break; + case PROP_TEMPORAL_AQ: + if (!device_caps->temporal_aq) { + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } else { + nvenc->temporal_aq = g_value_get_boolean (value); + reconfig = TRUE; + } + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -471,6 +534,27 @@ gst_nv_h264_enc_get_property (GObject * object, guint prop_id, GValue * value, g_value_set_boolean (value, nvenc->weighted_pred); } break; + case PROP_VBV_BUFFER_SIZE: + if (!device_caps->custom_vbv_bufsize) { + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } else { + g_value_set_uint (value, nvenc->vbv_buffersize); + } + break; + case PROP_RC_LOOKAHEAD: + if (!device_caps->lookahead) { + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } else { + g_value_set_uint (value, nvenc->rc_lookahead); + } + break; + case PROP_TEMPORAL_AQ: + if (!device_caps->temporal_aq) { + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } else { + g_value_set_boolean (value, nvenc->temporal_aq); + } + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; diff --git a/sys/nvcodec/gstnvh265enc.c b/sys/nvcodec/gstnvh265enc.c index f388541a54..ee711e576a 100644 --- a/sys/nvcodec/gstnvh265enc.c +++ b/sys/nvcodec/gstnvh265enc.c @@ -46,10 +46,16 @@ enum PROP_0, PROP_AUD, PROP_WEIGHTED_PRED, + PROP_VBV_BUFFER_SIZE, + PROP_RC_LOOKAHEAD, + PROP_TEMPORAL_AQ, }; #define DEFAULT_AUD TRUE #define DEFAULT_WEIGHTED_PRED FALSE +#define DEFAULT_VBV_BUFFER_SIZE 0 +#define DEFAULT_RC_LOOKAHEAD 0 +#define DEFAULT_TEMPORAL_AQ FALSE static gboolean gst_nv_h265_enc_open (GstVideoEncoder * enc); static gboolean gst_nv_h265_enc_close (GstVideoEncoder * enc); @@ -107,6 +113,36 @@ gst_nv_h265_enc_class_init (GstNvH265EncClass * klass, gpointer data) G_PARAM_STATIC_STRINGS)); } + if (device_caps->custom_vbv_bufsize) { + g_object_class_install_property (gobject_class, + PROP_VBV_BUFFER_SIZE, + g_param_spec_uint ("vbv-buffer-size", "VBV Buffer Size", + "VBV(HRD) Buffer Size in kbits (0 = NVENC default) " + "(Exposed only if supported by device)", 0, G_MAXUINT, + DEFAULT_VBV_BUFFER_SIZE, + G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY | + G_PARAM_STATIC_STRINGS)); + } + + if (device_caps->lookahead) { + g_object_class_install_property (gobject_class, PROP_RC_LOOKAHEAD, + g_param_spec_uint ("rc-lookahead", "Rate Control Lookahead", + "Number of frames for frame type lookahead " + "(Exposed only if supported by device)", 0, 32, + DEFAULT_RC_LOOKAHEAD, + G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY | + G_PARAM_STATIC_STRINGS)); + } + + if (device_caps->temporal_aq) { + g_object_class_install_property (gobject_class, PROP_TEMPORAL_AQ, + g_param_spec_boolean ("temporal-aq", "Temporal AQ", + "Temporal Adaptive Quantization " + "(Exposed only if supported by device)", DEFAULT_TEMPORAL_AQ, + G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING | + G_PARAM_STATIC_STRINGS)); + } + if (cdata->is_default) long_name = g_strdup ("NVENC HEVC Video Encoder"); else @@ -145,6 +181,9 @@ gst_nv_h265_enc_init (GstNvH265Enc * nvenc) /* device capability dependent properties */ baseenc->weighted_pred = DEFAULT_WEIGHTED_PRED; + baseenc->vbv_buffersize = DEFAULT_VBV_BUFFER_SIZE; + baseenc->rc_lookahead = DEFAULT_RC_LOOKAHEAD; + baseenc->temporal_aq = DEFAULT_TEMPORAL_AQ; } static void @@ -597,6 +636,30 @@ gst_nv_h265_enc_set_property (GObject * object, guint prop_id, reconfig = TRUE; } break; + case PROP_VBV_BUFFER_SIZE: + if (!device_caps->custom_vbv_bufsize) { + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } else { + nvenc->vbv_buffersize = g_value_get_uint (value); + reconfig = TRUE; + } + break; + case PROP_RC_LOOKAHEAD: + if (!device_caps->lookahead) { + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } else { + nvenc->rc_lookahead = g_value_get_uint (value); + reconfig = TRUE; + } + break; + case PROP_TEMPORAL_AQ: + if (!device_caps->temporal_aq) { + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } else { + nvenc->temporal_aq = g_value_get_boolean (value); + reconfig = TRUE; + } + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -626,6 +689,27 @@ gst_nv_h265_enc_get_property (GObject * object, guint prop_id, GValue * value, g_value_set_boolean (value, nvenc->weighted_pred); } break; + case PROP_VBV_BUFFER_SIZE: + if (!device_caps->custom_vbv_bufsize) { + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } else { + g_value_set_uint (value, nvenc->vbv_buffersize); + } + break; + case PROP_RC_LOOKAHEAD: + if (!device_caps->lookahead) { + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } else { + g_value_set_uint (value, nvenc->rc_lookahead); + } + break; + case PROP_TEMPORAL_AQ: + if (!device_caps->temporal_aq) { + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } else { + g_value_set_boolean (value, nvenc->temporal_aq); + } + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break;