From c45fc2ef53813d67115872c519263da325a4006b Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Thu, 7 Apr 2016 22:46:08 +1000 Subject: [PATCH] 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. --- sys/nvenc/gstnvbaseenc.c | 67 ++++++++++++++++++++++++++++++++++++++++ sys/nvenc/gstnvbaseenc.h | 14 +++++++++ sys/nvenc/gstnvenc.c | 18 +++++++++++ sys/nvenc/gstnvh264enc.c | 46 ++++++++++++++++++++++++--- 4 files changed, 141 insertions(+), 4 deletions(-) diff --git a/sys/nvenc/gstnvbaseenc.c b/sys/nvenc/gstnvbaseenc.c index 4093759b31..53761de482 100644 --- a/sys/nvenc/gstnvbaseenc.c +++ b/sys/nvenc/gstnvbaseenc.c @@ -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; diff --git a/sys/nvenc/gstnvbaseenc.h b/sys/nvenc/gstnvbaseenc.h index 0a843e9eff..6debe8eae7 100644 --- a/sys/nvenc/gstnvbaseenc.h +++ b/sys/nvenc/gstnvbaseenc.h @@ -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; diff --git a/sys/nvenc/gstnvenc.c b/sys/nvenc/gstnvenc.c index 6b0d5685c1..30b5b13f1a 100644 --- a/sys/nvenc/gstnvenc.c +++ b/sys/nvenc/gstnvenc.c @@ -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) diff --git a/sys/nvenc/gstnvh264enc.c b/sys/nvenc/gstnvh264enc.c index 98274a2566..4c7a9846d2 100644 --- a/sys/nvenc/gstnvh264enc.c +++ b/sys/nvenc/gstnvh264enc.c @@ -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; }