nvenc: add preset selection

Some presets are not always supported on all devices and will cause an error if
used.  Specifically, the LOSSLESS presets are known to not work everywhere.
This commit is contained in:
Matthew Waters 2016-04-07 22:46:08 +10:00
parent ac8a14d1c8
commit c45fc2ef53
4 changed files with 141 additions and 4 deletions

View file

@ -50,6 +50,55 @@
#define parent_class gst_nv_base_enc_parent_class
G_DEFINE_ABSTRACT_TYPE (GstNvBaseEnc, gst_nv_base_enc, GST_TYPE_VIDEO_ENCODER);
#define GST_TYPE_NV_PRESET (gst_nv_preset_get_type())
static GType
gst_nv_preset_get_type (void)
{
static GType nv_preset_type = 0;
static const GEnumValue presets[] = {
{GST_NV_PRESET_DEFAULT, "Default", "default"},
{GST_NV_PRESET_HP, "High Performance", "hp"},
{GST_NV_PRESET_HQ, "High Quality", "hq"},
/* {GST_NV_PRESET_BD, "BD", "bd"}, */
{GST_NV_PRESET_LOW_LATENCY_DEFAULT, "Low Latency", "low-latency"},
{GST_NV_PRESET_LOW_LATENCY_HQ, "Low Latency, High Quality",
"low-latency-hq"},
{GST_NV_PRESET_LOW_LATENCY_HP, "Low Latency, High Performance",
"low-latency-hp"},
{GST_NV_PRESET_LOSSLESS_DEFAULT, "Lossless", "lossless"},
{GST_NV_PRESET_LOSSLESS_HP, "Lossless, High Performance", "lossless-hp"},
{0, NULL, NULL},
};
if (!nv_preset_type) {
nv_preset_type = g_enum_register_static ("GstNvPreset", presets);
}
return nv_preset_type;
}
static GUID
_nv_preset_to_guid (GstNvPreset preset)
{
GUID null = { 0, };
switch (preset) {
#define CASE(gst,nv) case G_PASTE(GST_NV_PRESET_,gst): return G_PASTE(G_PASTE(NV_ENC_PRESET_,nv),_GUID)
CASE (DEFAULT, DEFAULT);
CASE (HP, HP);
CASE (HQ, HQ);
/* CASE (BD, BD);*/
CASE (LOW_LATENCY_DEFAULT, LOW_LATENCY_DEFAULT);
CASE (LOW_LATENCY_HQ, LOW_LATENCY_HQ);
CASE (LOW_LATENCY_HP, LOW_LATENCY_HQ);
CASE (LOSSLESS_DEFAULT, LOSSLESS_DEFAULT);
CASE (LOSSLESS_HP, LOSSLESS_HP);
#undef CASE
default:
return null;
}
}
static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
@ -71,8 +120,11 @@ enum
{
PROP_0,
PROP_DEVICE_ID,
PROP_PRESET,
};
#define DEFAULT_PRESET GST_NV_PRESET_DEFAULT
/* 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
* some period of time. */
@ -154,6 +206,11 @@ gst_nv_base_enc_class_init (GstNvBaseEncClass * klass)
"Cuda Device ID",
"Set the GPU device to use for operations",
0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_PRESET,
g_param_spec_enum ("preset", "Encoding Preset",
"Encoding Preset",
GST_TYPE_NV_PRESET,
DEFAULT_PRESET, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
}
static gboolean
@ -517,6 +574,9 @@ gst_nv_base_enc_init (GstNvBaseEnc * nvenc)
{
GstVideoEncoder *encoder = GST_VIDEO_ENCODER (nvenc);
nvenc->preset_enum = DEFAULT_PRESET;
nvenc->selected_preset = _nv_preset_to_guid (nvenc->preset_enum);
GST_VIDEO_ENCODER_STREAM_LOCK (encoder);
GST_VIDEO_ENCODER_STREAM_UNLOCK (encoder);
}
@ -1590,6 +1650,10 @@ gst_nv_base_enc_set_property (GObject * object, guint prop_id,
case PROP_DEVICE_ID:
nvenc->cuda_device_id = g_value_get_uint (value);
break;
case PROP_PRESET:
nvenc->preset_enum = g_value_get_enum (value);
nvenc->selected_preset = _nv_preset_to_guid (nvenc->preset_enum);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -1606,6 +1670,9 @@ gst_nv_base_enc_get_property (GObject * object, guint prop_id, GValue * value,
case PROP_DEVICE_ID:
g_value_set_uint (value, nvenc->cuda_device_id);
break;
case PROP_PRESET:
g_value_set_enum (value, nvenc->preset_enum);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;

View file

@ -37,11 +37,25 @@
#define GST_IS_NV_BASE_ENC_CLASS(obj) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_NV_BASE_ENC))
typedef enum {
GST_NV_PRESET_DEFAULT,
GST_NV_PRESET_HP,
GST_NV_PRESET_HQ,
/* FIXME: problematic GST_NV_PRESET_BD, */
GST_NV_PRESET_LOW_LATENCY_DEFAULT,
GST_NV_PRESET_LOW_LATENCY_HQ,
GST_NV_PRESET_LOW_LATENCY_HP,
GST_NV_PRESET_LOSSLESS_DEFAULT,
GST_NV_PRESET_LOSSLESS_HP,
} GstNvPreset;
typedef struct {
GstVideoEncoder video_encoder;
/* properties */
guint cuda_device_id;
GstNvPreset preset_enum;
GUID selected_preset;
CUcontext cuda_ctx;
void * encoder;

View file

@ -77,6 +77,24 @@ NvEncGetInputFormats (void *encoder, GUID enc_guid,
return nvenc_api.nvEncGetInputFormats (encoder, enc_guid, array, size, num);
}
NVENCSTATUS
NvEncGetEncodePresetCount (void *encoder, GUID encodeGUID,
uint32_t * encodePresetGUIDCount)
{
g_assert (nvenc_api.nvEncGetEncodeProfileGUIDCount != NULL);
return nvenc_api.nvEncGetEncodePresetCount (encoder, encodeGUID,
encodePresetGUIDCount);
}
NVENCSTATUS
NvEncGetEncodePresetGUIDs (void *encoder, GUID encodeGUID,
GUID * presetGUIDs, uint32_t guidArraySize, uint32_t * GUIDCount)
{
g_assert (nvenc_api.nvEncGetEncodeProfileGUIDs != NULL);
return nvenc_api.nvEncGetEncodePresetGUIDs (encoder, encodeGUID,
presetGUIDs, guidArraySize, GUIDCount);
}
NVENCSTATUS
NvEncGetEncodePresetConfig (void *encoder, GUID encodeGUID,
GUID presetGUID, NV_ENC_PRESET_CONFIG * presetConfig)

View file

@ -423,6 +423,7 @@ gst_nv_h264_enc_initialize_encoder (GstNvBaseEnc * nvenc,
GstVideoInfo *info = &state->info;
GstCaps *allowed_caps, *template_caps;
GUID selected_profile = NV_ENC_CODEC_PROFILE_AUTOSELECT_GUID;
GUID selected_preset = nvenc->selected_preset;
int level_idc = NV_ENC_LEVEL_AUTOSELECT;
/* TODO: support reconfiguration */
@ -485,8 +486,45 @@ gst_nv_h264_enc_initialize_encoder (GstNvBaseEnc * nvenc,
params->encodeGUID = NV_ENC_CODEC_H264_GUID;
params->encodeWidth = GST_VIDEO_INFO_WIDTH (info);
params->encodeHeight = GST_VIDEO_INFO_HEIGHT (info);
/* FIXME: make this a property */
params->presetGUID = NV_ENC_PRESET_HP_GUID; // _DEFAULT
{
guint32 n_presets;
GUID *presets;
guint32 i;
nv_ret =
NvEncGetEncodePresetCount (GST_NV_BASE_ENC (h264enc)->encoder,
params->encodeGUID, &n_presets);
if (nv_ret != NV_ENC_SUCCESS) {
GST_ELEMENT_ERROR (h264enc, LIBRARY, SETTINGS, (NULL),
("Failed to get encoder presets"));
return FALSE;
}
presets = g_new0 (GUID, n_presets);
nv_ret =
NvEncGetEncodePresetGUIDs (GST_NV_BASE_ENC (h264enc)->encoder,
params->encodeGUID, presets, n_presets, &n_presets);
if (nv_ret != NV_ENC_SUCCESS) {
GST_ELEMENT_ERROR (h264enc, LIBRARY, SETTINGS, (NULL),
("Failed to get encoder presets"));
g_free (presets);
return FALSE;
}
for (i = 0; i < n_presets; i++) {
if (gst_nvenc_cmp_guid (presets[i], selected_preset))
break;
}
g_free (presets);
if (i >= n_presets) {
GST_ELEMENT_ERROR (h264enc, LIBRARY, SETTINGS, (NULL),
("Selected preset not supported"));
return FALSE;
}
params->presetGUID = selected_preset;
}
params->enablePTD = 1;
if (!old_state) {
/* this sets the required buffer size and the maximum allowed size on
@ -531,8 +569,8 @@ gst_nv_h264_enc_initialize_encoder (GstNvBaseEnc * nvenc,
preset_config.presetCfg.encodeCodecConfig.h264Config.chromaFormatIDC = 1;
if (GST_VIDEO_INFO_FORMAT (info) == GST_VIDEO_FORMAT_Y444) {
GST_DEBUG_OBJECT (h264enc, "have Y444 input, setting config accordingly");
preset_config.presetCfg.encodeCodecConfig.
h264Config.separateColourPlaneFlag = 1;
preset_config.presetCfg.encodeCodecConfig.h264Config.
separateColourPlaneFlag = 1;
preset_config.presetCfg.encodeCodecConfig.h264Config.chromaFormatIDC = 3;
}