nvenc: Add support for weighted prediction option

Note that this property will be exposed only if the device
supports the weighted prediction.
This commit is contained in:
Seungha Yang 2019-09-03 21:28:44 +09:00
parent d05cbdbd72
commit ea19a7c715
7 changed files with 134 additions and 13 deletions

View file

@ -1311,6 +1311,8 @@ gst_nv_base_enc_set_format (GstVideoEncoder * enc, GstVideoCodecState * state)
} }
} }
params->enableWeightedPrediction = nvenc->weighted_pred;
if (nvenc->gop_size < 0) { if (nvenc->gop_size < 0) {
params->encodeConfig->gopLength = NVENC_INFINITE_GOPLENGTH; params->encodeConfig->gopLength = NVENC_INFINITE_GOPLENGTH;
params->encodeConfig->frameIntervalP = 1; params->encodeConfig->frameIntervalP = 1;
@ -2243,22 +2245,33 @@ gst_nv_base_enc_get_property (GObject * object, guint prop_id, GValue * value,
} }
} }
typedef struct
{
guint cuda_device_id;
GstNvEncDeviceCaps device_caps;
} GstNvEncClassData;
static void static void
gst_nv_base_enc_subclass_init (gpointer g_class, gpointer data) gst_nv_base_enc_subclass_init (gpointer g_class, gpointer data)
{ {
GstNvBaseEncClass *nvbaseenc_class = GST_NV_BASE_ENC_CLASS (g_class); GstNvBaseEncClass *nvbaseenc_class = GST_NV_BASE_ENC_CLASS (g_class);
guint device_id = GPOINTER_TO_UINT (data); GstNvEncClassData *cdata = (GstNvEncClassData *) data;
nvbaseenc_class->cuda_device_id = device_id; nvbaseenc_class->cuda_device_id = cdata->cuda_device_id;
nvbaseenc_class->device_caps = cdata->device_caps;
g_free (cdata);
} }
GType GType
gst_nv_base_enc_register (const char *codec, guint device_id) gst_nv_base_enc_register (const char *codec, guint device_id,
GstNvEncDeviceCaps * device_caps)
{ {
GTypeQuery type_query; GTypeQuery type_query;
GTypeInfo type_info = { 0, }; GTypeInfo type_info = { 0, };
GType subtype; GType subtype;
gchar *type_name; gchar *type_name;
GstNvEncClassData *cdata;
type_name = g_strdup_printf ("GstNvDevice%d%sEnc", device_id, codec); type_name = g_strdup_printf ("GstNvDevice%d%sEnc", device_id, codec);
subtype = g_type_from_name (type_name); subtype = g_type_from_name (type_name);
@ -2267,12 +2280,16 @@ gst_nv_base_enc_register (const char *codec, guint device_id)
if (subtype) if (subtype)
goto done; goto done;
cdata = g_new0 (GstNvEncClassData, 1);
cdata->cuda_device_id = device_id;
cdata->device_caps = *device_caps;
g_type_query (GST_TYPE_NV_BASE_ENC, &type_query); g_type_query (GST_TYPE_NV_BASE_ENC, &type_query);
memset (&type_info, 0, sizeof (type_info)); memset (&type_info, 0, sizeof (type_info));
type_info.class_size = type_query.class_size; type_info.class_size = type_query.class_size;
type_info.instance_size = type_query.instance_size; type_info.instance_size = type_query.instance_size;
type_info.class_init = (GClassInitFunc) gst_nv_base_enc_subclass_init; type_info.class_init = (GClassInitFunc) gst_nv_base_enc_subclass_init;
type_info.class_data = GUINT_TO_POINTER (device_id); type_info.class_data = cdata;
subtype = g_type_register_static (GST_TYPE_NV_BASE_ENC, subtype = g_type_register_static (GST_TYPE_NV_BASE_ENC,
type_name, &type_info, 0); type_name, &type_info, 0);

View file

@ -58,6 +58,10 @@ typedef enum {
GST_NV_RC_MODE_VBR_MINQP, GST_NV_RC_MODE_VBR_MINQP,
} GstNvRCMode; } GstNvRCMode;
typedef struct {
gboolean weighted_prediction;
} GstNvEncDeviceCaps;
typedef struct { typedef struct {
GstVideoEncoder video_encoder; GstVideoEncoder video_encoder;
@ -107,6 +111,10 @@ typedef struct {
GstVideoInfo input_info; /* buffer configuration for buffers sent to NVENC */ GstVideoInfo input_info; /* buffer configuration for buffers sent to NVENC */
GstFlowReturn last_flow; /* ATOMIC */ GstFlowReturn last_flow; /* ATOMIC */
/*< protected >*/
/* device capability dependent properties, set by subclass */
gboolean weighted_pred;
} GstNvBaseEnc; } GstNvBaseEnc;
typedef struct { typedef struct {
@ -114,6 +122,7 @@ typedef struct {
GUID codec_id; GUID codec_id;
guint cuda_device_id; guint cuda_device_id;
GstNvEncDeviceCaps device_caps;
gboolean (*set_src_caps) (GstNvBaseEnc * nvenc, gboolean (*set_src_caps) (GstNvBaseEnc * nvenc,
GstVideoCodecState * state); GstVideoCodecState * state);
@ -129,7 +138,8 @@ G_GNUC_INTERNAL
GType gst_nv_base_enc_get_type (void); GType gst_nv_base_enc_get_type (void);
GType gst_nv_base_enc_register (const char * codec, GType gst_nv_base_enc_register (const char * codec,
guint device_id); guint device_id,
GstNvEncDeviceCaps * device_caps);
void gst_nv_base_enc_schedule_reconfig (GstNvBaseEnc * nvenc); void gst_nv_base_enc_schedule_reconfig (GstNvBaseEnc * nvenc);

View file

@ -592,6 +592,10 @@ gst_nvenc_get_supported_codec_profiles (gpointer enc, GUID codec_id)
return ret; return ret;
} }
#define DEBUG_DEVICE_CAPS(d,c,caps,s) \
GST_DEBUG ("[device-%d %s] %s: %s", \
d, c, caps, s ? "supported" : "not supported");
static void static void
gst_nv_enc_register (GstPlugin * plugin, GUID codec_id, const gchar * codec, gst_nv_enc_register (GstPlugin * plugin, GUID codec_id, const gchar * codec,
guint rank, gint device_count) guint rank, gint device_count)
@ -615,6 +619,7 @@ gst_nv_enc_register (GstPlugin * plugin, GUID codec_id, const gchar * codec,
GstCaps *src_templ = NULL; GstCaps *src_templ = NULL;
gchar *name; gchar *name;
gint j; gint j;
GstNvEncDeviceCaps device_caps = { 0, };
if (CuDeviceGet (&cuda_device, i) != CUDA_SUCCESS) if (CuDeviceGet (&cuda_device, i) != CUDA_SUCCESS)
continue; continue;
@ -676,6 +681,15 @@ gst_nv_enc_register (GstPlugin * plugin, GUID codec_id, const gchar * codec,
max_height = 4096; max_height = 4096;
} }
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;
}
DEBUG_DEVICE_CAPS (i,
codec, "weighted prediction", device_caps.weighted_prediction);
interlace_modes = gst_nvenc_get_interlace_modes (enc, codec_id); interlace_modes = gst_nvenc_get_interlace_modes (enc, codec_id);
sink_templ = gst_caps_new_empty_simple ("video/x-raw"); sink_templ = gst_caps_new_empty_simple ("video/x-raw");
@ -732,9 +746,11 @@ gst_nv_enc_register (GstPlugin * plugin, GUID codec_id, const gchar * codec,
if (sink_templ && src_templ) { if (sink_templ && src_templ) {
if (gst_nvenc_cmp_guid (codec_id, NV_ENC_CODEC_H264_GUID)) { if (gst_nvenc_cmp_guid (codec_id, NV_ENC_CODEC_H264_GUID)) {
gst_nv_h264_enc_register (plugin, i, rank, sink_templ, src_templ); gst_nv_h264_enc_register (plugin, i, rank, sink_templ, src_templ,
&device_caps);
} else if (gst_nvenc_cmp_guid (codec_id, NV_ENC_CODEC_HEVC_GUID)) { } else if (gst_nvenc_cmp_guid (codec_id, NV_ENC_CODEC_HEVC_GUID)) {
gst_nv_h265_enc_register (plugin, i, rank, sink_templ, src_templ); gst_nv_h265_enc_register (plugin, i, rank, sink_templ, src_templ,
&device_caps);
} else { } else {
g_assert_not_reached (); g_assert_not_reached ();
} }

View file

@ -43,9 +43,11 @@ enum
{ {
PROP_0, PROP_0,
PROP_AUD, PROP_AUD,
PROP_WEIGHTED_PRED,
}; };
#define DEFAULT_AUD TRUE #define DEFAULT_AUD TRUE
#define DEFAULT_WEIGHTED_PRED FALSE
static gboolean gst_nv_h264_enc_open (GstVideoEncoder * enc); static gboolean gst_nv_h264_enc_open (GstVideoEncoder * enc);
static gboolean gst_nv_h264_enc_close (GstVideoEncoder * enc); static gboolean gst_nv_h264_enc_close (GstVideoEncoder * enc);
@ -68,6 +70,7 @@ gst_nv_h264_enc_class_init (GstNvH264EncClass * klass, gpointer data)
GstElementClass *element_class = GST_ELEMENT_CLASS (klass); GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
GstVideoEncoderClass *videoenc_class = GST_VIDEO_ENCODER_CLASS (klass); GstVideoEncoderClass *videoenc_class = GST_VIDEO_ENCODER_CLASS (klass);
GstNvBaseEncClass *nvenc_class = GST_NV_BASE_ENC_CLASS (klass); GstNvBaseEncClass *nvenc_class = GST_NV_BASE_ENC_CLASS (klass);
GstNvEncDeviceCaps *device_caps = &nvenc_class->device_caps;
GstNvH264EncClassData *cdata = (GstNvH264EncClassData *) data; GstNvH264EncClassData *cdata = (GstNvH264EncClassData *) data;
gchar *long_name; gchar *long_name;
@ -91,6 +94,15 @@ gst_nv_h264_enc_class_init (GstNvH264EncClass * klass, gpointer data)
G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING | G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
G_PARAM_STATIC_STRINGS)); G_PARAM_STATIC_STRINGS));
if (device_caps->weighted_prediction) {
g_object_class_install_property (gobject_class, PROP_WEIGHTED_PRED,
g_param_spec_boolean ("weighted-pred", "Weighted Pred",
"Weighted Prediction "
"(Exposed only if supported by device)", DEFAULT_WEIGHTED_PRED,
G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
G_PARAM_STATIC_STRINGS));
}
if (cdata->is_default) if (cdata->is_default)
long_name = g_strdup ("NVENC H.264 Video Encoder"); long_name = g_strdup ("NVENC H.264 Video Encoder");
else else
@ -123,7 +135,12 @@ gst_nv_h264_enc_class_init (GstNvH264EncClass * klass, gpointer data)
static void static void
gst_nv_h264_enc_init (GstNvH264Enc * nvenc) gst_nv_h264_enc_init (GstNvH264Enc * nvenc)
{ {
GstNvBaseEnc *baseenc = GST_NV_BASE_ENC (nvenc);
nvenc->aud = DEFAULT_AUD; nvenc->aud = DEFAULT_AUD;
/* device capability dependent properties */
baseenc->weighted_pred = DEFAULT_WEIGHTED_PRED;
} }
static void static void
@ -400,6 +417,9 @@ gst_nv_h264_enc_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec) const GValue * value, GParamSpec * pspec)
{ {
GstNvH264Enc *self = (GstNvH264Enc *) object; GstNvH264Enc *self = (GstNvH264Enc *) object;
GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (object);
GstNvBaseEncClass *klass = GST_NV_BASE_ENC_GET_CLASS (object);
GstNvEncDeviceCaps *device_caps = &klass->device_caps;
gboolean reconfig = FALSE; gboolean reconfig = FALSE;
switch (prop_id) { switch (prop_id) {
@ -414,6 +434,14 @@ gst_nv_h264_enc_set_property (GObject * object, guint prop_id,
} }
break; break;
} }
case PROP_WEIGHTED_PRED:
if (!device_caps->weighted_prediction) {
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
} else {
nvenc->weighted_pred = g_value_get_boolean (value);
reconfig = TRUE;
}
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -428,11 +456,21 @@ gst_nv_h264_enc_get_property (GObject * object, guint prop_id, GValue * value,
GParamSpec * pspec) GParamSpec * pspec)
{ {
GstNvH264Enc *self = (GstNvH264Enc *) object; GstNvH264Enc *self = (GstNvH264Enc *) object;
GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (object);
GstNvBaseEncClass *klass = GST_NV_BASE_ENC_GET_CLASS (object);
GstNvEncDeviceCaps *device_caps = &klass->device_caps;
switch (prop_id) { switch (prop_id) {
case PROP_AUD: case PROP_AUD:
g_value_set_boolean (value, self->aud); g_value_set_boolean (value, self->aud);
break; break;
case PROP_WEIGHTED_PRED:
if (!device_caps->weighted_prediction) {
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
} else {
g_value_set_boolean (value, nvenc->weighted_pred);
}
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -441,7 +479,7 @@ gst_nv_h264_enc_get_property (GObject * object, guint prop_id, GValue * value,
void void
gst_nv_h264_enc_register (GstPlugin * plugin, guint device_id, guint rank, gst_nv_h264_enc_register (GstPlugin * plugin, guint device_id, guint rank,
GstCaps * sink_caps, GstCaps * src_caps) GstCaps * sink_caps, GstCaps * src_caps, GstNvEncDeviceCaps * device_caps)
{ {
GType parent_type; GType parent_type;
GType type; GType type;
@ -461,7 +499,7 @@ gst_nv_h264_enc_register (GstPlugin * plugin, guint device_id, guint rank,
(GInstanceInitFunc) gst_nv_h264_enc_init, (GInstanceInitFunc) gst_nv_h264_enc_init,
}; };
parent_type = gst_nv_base_enc_register ("H264", device_id); parent_type = gst_nv_base_enc_register ("H264", device_id, device_caps);
cdata = g_new0 (GstNvH264EncClassData, 1); cdata = g_new0 (GstNvH264EncClassData, 1);
cdata->sink_caps = gst_caps_ref (sink_caps); cdata->sink_caps = gst_caps_ref (sink_caps);

View file

@ -37,7 +37,8 @@ void gst_nv_h264_enc_register (GstPlugin * plugin,
guint device_id, guint device_id,
guint rank, guint rank,
GstCaps * sink_caps, GstCaps * sink_caps,
GstCaps * src_caps); GstCaps * src_caps,
GstNvEncDeviceCaps * device_caps);
#endif /* __GST_NV_H264_ENC_H_INCLUDED__ */ #endif /* __GST_NV_H264_ENC_H_INCLUDED__ */

View file

@ -45,9 +45,11 @@ enum
{ {
PROP_0, PROP_0,
PROP_AUD, PROP_AUD,
PROP_WEIGHTED_PRED,
}; };
#define DEFAULT_AUD TRUE #define DEFAULT_AUD TRUE
#define DEFAULT_WEIGHTED_PRED FALSE
static gboolean gst_nv_h265_enc_open (GstVideoEncoder * enc); static gboolean gst_nv_h265_enc_open (GstVideoEncoder * enc);
static gboolean gst_nv_h265_enc_close (GstVideoEncoder * enc); static gboolean gst_nv_h265_enc_close (GstVideoEncoder * enc);
@ -71,6 +73,7 @@ gst_nv_h265_enc_class_init (GstNvH265EncClass * klass, gpointer data)
GstElementClass *element_class = GST_ELEMENT_CLASS (klass); GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
GstVideoEncoderClass *videoenc_class = GST_VIDEO_ENCODER_CLASS (klass); GstVideoEncoderClass *videoenc_class = GST_VIDEO_ENCODER_CLASS (klass);
GstNvBaseEncClass *nvenc_class = GST_NV_BASE_ENC_CLASS (klass); GstNvBaseEncClass *nvenc_class = GST_NV_BASE_ENC_CLASS (klass);
GstNvEncDeviceCaps *device_caps = &nvenc_class->device_caps;
GstNvH265EncClassData *cdata = (GstNvH265EncClassData *) data; GstNvH265EncClassData *cdata = (GstNvH265EncClassData *) data;
gchar *long_name; gchar *long_name;
@ -95,6 +98,15 @@ gst_nv_h265_enc_class_init (GstNvH265EncClass * klass, gpointer data)
G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING | G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
G_PARAM_STATIC_STRINGS)); G_PARAM_STATIC_STRINGS));
if (device_caps->weighted_prediction) {
g_object_class_install_property (gobject_class, PROP_WEIGHTED_PRED,
g_param_spec_boolean ("weighted-pred", "Weighted Pred",
"Weighted Prediction "
"(Exposed only if supported by device)", DEFAULT_WEIGHTED_PRED,
G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
G_PARAM_STATIC_STRINGS));
}
if (cdata->is_default) if (cdata->is_default)
long_name = g_strdup ("NVENC HEVC Video Encoder"); long_name = g_strdup ("NVENC HEVC Video Encoder");
else else
@ -127,7 +139,12 @@ gst_nv_h265_enc_class_init (GstNvH265EncClass * klass, gpointer data)
static void static void
gst_nv_h265_enc_init (GstNvH265Enc * nvenc) gst_nv_h265_enc_init (GstNvH265Enc * nvenc)
{ {
GstNvBaseEnc *baseenc = GST_NV_BASE_ENC (nvenc);
nvenc->aud = DEFAULT_AUD; nvenc->aud = DEFAULT_AUD;
/* device capability dependent properties */
baseenc->weighted_pred = DEFAULT_WEIGHTED_PRED;
} }
static void static void
@ -555,6 +572,9 @@ gst_nv_h265_enc_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec) const GValue * value, GParamSpec * pspec)
{ {
GstNvH265Enc *self = (GstNvH265Enc *) object; GstNvH265Enc *self = (GstNvH265Enc *) object;
GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (object);
GstNvBaseEncClass *klass = GST_NV_BASE_ENC_GET_CLASS (object);
GstNvEncDeviceCaps *device_caps = &klass->device_caps;
gboolean reconfig = FALSE; gboolean reconfig = FALSE;
switch (prop_id) { switch (prop_id) {
@ -569,6 +589,14 @@ gst_nv_h265_enc_set_property (GObject * object, guint prop_id,
} }
break; break;
} }
case PROP_WEIGHTED_PRED:
if (!device_caps->weighted_prediction) {
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
} else {
nvenc->weighted_pred = g_value_get_boolean (value);
reconfig = TRUE;
}
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -583,11 +611,21 @@ gst_nv_h265_enc_get_property (GObject * object, guint prop_id, GValue * value,
GParamSpec * pspec) GParamSpec * pspec)
{ {
GstNvH265Enc *self = (GstNvH265Enc *) object; GstNvH265Enc *self = (GstNvH265Enc *) object;
GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (object);
GstNvBaseEncClass *klass = GST_NV_BASE_ENC_GET_CLASS (object);
GstNvEncDeviceCaps *device_caps = &klass->device_caps;
switch (prop_id) { switch (prop_id) {
case PROP_AUD: case PROP_AUD:
g_value_set_boolean (value, self->aud); g_value_set_boolean (value, self->aud);
break; break;
case PROP_WEIGHTED_PRED:
if (!device_caps->weighted_prediction) {
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
} else {
g_value_set_boolean (value, nvenc->weighted_pred);
}
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -596,7 +634,7 @@ gst_nv_h265_enc_get_property (GObject * object, guint prop_id, GValue * value,
void void
gst_nv_h265_enc_register (GstPlugin * plugin, guint device_id, guint rank, gst_nv_h265_enc_register (GstPlugin * plugin, guint device_id, guint rank,
GstCaps * sink_caps, GstCaps * src_caps) GstCaps * sink_caps, GstCaps * src_caps, GstNvEncDeviceCaps * device_caps)
{ {
GType parent_type; GType parent_type;
GType type; GType type;
@ -616,7 +654,7 @@ gst_nv_h265_enc_register (GstPlugin * plugin, guint device_id, guint rank,
(GInstanceInitFunc) gst_nv_h265_enc_init, (GInstanceInitFunc) gst_nv_h265_enc_init,
}; };
parent_type = gst_nv_base_enc_register ("H265", device_id); parent_type = gst_nv_base_enc_register ("H265", device_id, device_caps);
cdata = g_new0 (GstNvH265EncClassData, 1); cdata = g_new0 (GstNvH265EncClassData, 1);
cdata->sink_caps = gst_caps_ref (sink_caps); cdata->sink_caps = gst_caps_ref (sink_caps);

View file

@ -42,6 +42,7 @@ void gst_nv_h265_enc_register (GstPlugin * plugin,
guint device_id, guint device_id,
guint rank, guint rank,
GstCaps * sink_caps, GstCaps * sink_caps,
GstCaps * src_caps); GstCaps * src_caps,
GstNvEncDeviceCaps * device_caps);
#endif /* __GST_NV_HEVC_ENC_H_INCLUDED__ */ #endif /* __GST_NV_HEVC_ENC_H_INCLUDED__ */