mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-02-17 03:35:21 +00:00
nvenc: Add support runtime resolution change freely
Do not restrict allowed maximum resolution depending on the initial resolution. If new resolution is larger than previous one, just re-init encode session.
This commit is contained in:
parent
5f63f59837
commit
09fd34dbb0
2 changed files with 102 additions and 70 deletions
|
@ -213,6 +213,7 @@ static GstCaps *gst_nv_base_enc_getcaps (GstVideoEncoder * enc,
|
|||
GstCaps * filter);
|
||||
static gboolean gst_nv_base_enc_stop_bitstream_thread (GstNvBaseEnc * nvenc,
|
||||
gboolean force);
|
||||
static gboolean gst_nv_base_enc_drain_encoder (GstNvBaseEnc * nvenc);
|
||||
|
||||
static void
|
||||
gst_nv_base_enc_class_init (GstNvBaseEncClass * klass)
|
||||
|
@ -286,6 +287,21 @@ gst_nv_base_enc_class_init (GstNvBaseEncClass * klass)
|
|||
G_PARAM_STATIC_STRINGS));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_nv_base_enc_open_encode_session (GstNvBaseEnc * nvenc)
|
||||
{
|
||||
NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS params = { 0, };
|
||||
NVENCSTATUS nv_ret;
|
||||
|
||||
params.version = gst_nvenc_get_open_encode_session_ex_params_version ();
|
||||
params.apiVersion = gst_nvenc_get_api_version ();
|
||||
params.device = gst_cuda_context_get_handle (nvenc->cuda_ctx);
|
||||
params.deviceType = NV_ENC_DEVICE_TYPE_CUDA;
|
||||
nv_ret = NvEncOpenEncodeSessionEx (¶ms, &nvenc->encoder);
|
||||
|
||||
return nv_ret == NV_ENC_SUCCESS;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_nv_base_enc_open (GstVideoEncoder * enc)
|
||||
{
|
||||
|
@ -310,23 +326,14 @@ gst_nv_base_enc_open (GstVideoEncoder * enc)
|
|||
gst_cuda_context_pop (NULL);
|
||||
}
|
||||
|
||||
{
|
||||
NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS params = { 0, };
|
||||
NVENCSTATUS nv_ret;
|
||||
|
||||
params.version = gst_nvenc_get_open_encode_session_ex_params_version ();
|
||||
params.apiVersion = gst_nvenc_get_api_version ();
|
||||
params.device = gst_cuda_context_get_handle (nvenc->cuda_ctx);
|
||||
params.deviceType = NV_ENC_DEVICE_TYPE_CUDA;
|
||||
nv_ret = NvEncOpenEncodeSessionEx (¶ms, &nvenc->encoder);
|
||||
if (nv_ret != NV_ENC_SUCCESS) {
|
||||
GST_ERROR ("Failed to create NVENC encoder session, ret=%d", nv_ret);
|
||||
gst_clear_object (&nvenc->cuda_ctx);
|
||||
return FALSE;
|
||||
}
|
||||
GST_INFO ("created NVENC encoder %p", nvenc->encoder);
|
||||
if (!gst_nv_base_enc_open_encode_session (nvenc)) {
|
||||
GST_ERROR ("Failed to create NVENC encoder session");
|
||||
gst_clear_object (&nvenc->cuda_ctx);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
GST_INFO ("created NVENC encoder %p", nvenc->encoder);
|
||||
|
||||
/* query supported input formats */
|
||||
if (!gst_nvenc_get_supported_input_formats (nvenc->encoder, klass->codec_id,
|
||||
&formats)) {
|
||||
|
@ -408,6 +415,8 @@ gst_nv_base_enc_start (GstVideoEncoder * enc)
|
|||
nvenc->in_bufs_pool = g_async_queue_new ();
|
||||
|
||||
nvenc->last_flow = GST_FLOW_OK;
|
||||
memset (&nvenc->init_params, 0, sizeof (NV_ENC_INITIALIZE_PARAMS));
|
||||
memset (&nvenc->config, 0, sizeof (NV_ENC_CONFIG));
|
||||
|
||||
#if HAVE_NVCODEC_GST_GL
|
||||
{
|
||||
|
@ -1103,22 +1112,22 @@ _get_frame_data_height (GstVideoInfo * info)
|
|||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
gst_nv_base_enc_set_max_encode_size (GstNvBaseEnc * nvenc, guint max_width,
|
||||
guint max_height)
|
||||
{
|
||||
nvenc->max_encode_width = max_width;
|
||||
nvenc->max_encode_height = max_height;
|
||||
}
|
||||
|
||||
void
|
||||
gst_nv_base_enc_get_max_encode_size (GstNvBaseEnc * nvenc, guint * max_width,
|
||||
guint * max_height)
|
||||
{
|
||||
*max_width = nvenc->max_encode_width;
|
||||
*max_height = nvenc->max_encode_height;
|
||||
}
|
||||
|
||||
/* GstVideoEncoder::set_format or by nvenc self if new properties were set.
|
||||
*
|
||||
* NvEncReconfigureEncoder with following conditions are not allowed
|
||||
* 1) GOP structure change
|
||||
* 2) sync-Async mode change (Async mode is Windows only and we didn't support it)
|
||||
* 3) MaxWidth, MaxHeight
|
||||
* 4) PTDmode (Picture Type Decision mode)
|
||||
*
|
||||
* So we will force to re-init the encode session if
|
||||
* 1) New resolution is larger than previous config
|
||||
* 2) GOP size changed
|
||||
* 3) Input pixel format change
|
||||
* pre-allocated CUDA memory could not ensure stride, width and height
|
||||
*
|
||||
* TODO: bframe also considered as force re-init case
|
||||
*/
|
||||
static gboolean
|
||||
gst_nv_base_enc_set_format (GstVideoEncoder * enc, GstVideoCodecState * state)
|
||||
{
|
||||
|
@ -1127,19 +1136,60 @@ gst_nv_base_enc_set_format (GstVideoEncoder * enc, GstVideoCodecState * state)
|
|||
GstVideoInfo *info = &state->info;
|
||||
GstVideoCodecState *old_state = nvenc->input_state;
|
||||
NV_ENC_RECONFIGURE_PARAMS reconfigure_params = { 0, };
|
||||
NV_ENC_INITIALIZE_PARAMS init_params = { 0, };
|
||||
NV_ENC_INITIALIZE_PARAMS *params;
|
||||
NV_ENC_INITIALIZE_PARAMS *params = &nvenc->init_params;
|
||||
NV_ENC_PRESET_CONFIG preset_config = { 0, };
|
||||
NVENCSTATUS nv_ret;
|
||||
gint dar_n, dar_d;
|
||||
gboolean reconfigure = FALSE;
|
||||
|
||||
g_atomic_int_set (&nvenc->reconfig, FALSE);
|
||||
|
||||
if (old_state) {
|
||||
reconfigure_params.version = gst_nvenc_get_reconfigure_params_version ();
|
||||
params = &reconfigure_params.reInitEncodeParams;
|
||||
} else {
|
||||
params = &init_params;
|
||||
gboolean larger_resolution;
|
||||
gboolean format_changed;
|
||||
gboolean gop_size_changed;
|
||||
|
||||
larger_resolution =
|
||||
(GST_VIDEO_INFO_WIDTH (info) > nvenc->init_params.maxEncodeWidth ||
|
||||
GST_VIDEO_INFO_HEIGHT (info) > nvenc->init_params.maxEncodeHeight);
|
||||
format_changed =
|
||||
GST_VIDEO_INFO_FORMAT (info) !=
|
||||
GST_VIDEO_INFO_FORMAT (&old_state->info);
|
||||
|
||||
if (nvenc->config.gopLength == NVENC_INFINITE_GOPLENGTH
|
||||
&& nvenc->gop_size == -1) {
|
||||
gop_size_changed = FALSE;
|
||||
} else if (nvenc->config.gopLength != nvenc->gop_size) {
|
||||
gop_size_changed = TRUE;
|
||||
} else {
|
||||
gop_size_changed = FALSE;
|
||||
}
|
||||
|
||||
if (larger_resolution || format_changed || gop_size_changed) {
|
||||
GST_DEBUG_OBJECT (nvenc,
|
||||
"resolution %dx%d -> %dx%d, format %s -> %s, re-init",
|
||||
nvenc->init_params.maxEncodeWidth, nvenc->init_params.maxEncodeHeight,
|
||||
GST_VIDEO_INFO_WIDTH (info), GST_VIDEO_INFO_HEIGHT (info),
|
||||
gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (&old_state->info)),
|
||||
gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (info)));
|
||||
|
||||
gst_nv_base_enc_drain_encoder (nvenc);
|
||||
gst_nv_base_enc_stop_bitstream_thread (nvenc, FALSE);
|
||||
gst_nv_base_enc_free_buffers (nvenc);
|
||||
NvEncDestroyEncoder (nvenc->encoder);
|
||||
nvenc->encoder = NULL;
|
||||
|
||||
if (!gst_nv_base_enc_open_encode_session (nvenc)) {
|
||||
GST_ERROR_OBJECT (nvenc, "Failed to open encode session");
|
||||
return FALSE;
|
||||
}
|
||||
} else {
|
||||
reconfigure_params.version = gst_nvenc_get_reconfigure_params_version ();
|
||||
/* reset rate control state and start from IDR */
|
||||
reconfigure_params.resetEncoder = TRUE;
|
||||
reconfigure_params.forceIDR = TRUE;
|
||||
reconfigure = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
params->version = gst_nvenc_get_initialize_params_version ();
|
||||
|
@ -1187,25 +1237,11 @@ gst_nv_base_enc_set_format (GstVideoEncoder * enc, GstVideoCodecState * state)
|
|||
}
|
||||
|
||||
params->enablePTD = 1;
|
||||
if (!old_state) {
|
||||
if (!reconfigure) {
|
||||
/* this sets the required buffer size and the maximum allowed size on
|
||||
* subsequent reconfigures */
|
||||
/* FIXME: propertise this */
|
||||
params->maxEncodeWidth = GST_VIDEO_INFO_WIDTH (info);
|
||||
params->maxEncodeHeight = GST_VIDEO_INFO_HEIGHT (info);
|
||||
gst_nv_base_enc_set_max_encode_size (nvenc, params->maxEncodeWidth,
|
||||
params->maxEncodeHeight);
|
||||
} else {
|
||||
guint max_width, max_height;
|
||||
|
||||
gst_nv_base_enc_get_max_encode_size (nvenc, &max_width, &max_height);
|
||||
|
||||
if (GST_VIDEO_INFO_WIDTH (info) > max_width
|
||||
|| GST_VIDEO_INFO_HEIGHT (info) > max_height) {
|
||||
GST_ELEMENT_ERROR (nvenc, STREAM, FORMAT, ("%s", "Requested stream "
|
||||
"size is larger than the maximum configured size"), (NULL));
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
preset_config.version = gst_nvenc_get_preset_config_version ();
|
||||
|
@ -1288,8 +1324,12 @@ gst_nv_base_enc_set_format (GstVideoEncoder * enc, GstVideoCodecState * state)
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
/* store the last config to reconfig/re-init decision in the next time */
|
||||
nvenc->config = *params->encodeConfig;
|
||||
|
||||
G_LOCK (initialization_lock);
|
||||
if (old_state) {
|
||||
if (reconfigure) {
|
||||
reconfigure_params.reInitEncodeParams = nvenc->init_params;
|
||||
nv_ret = NvEncReconfigureEncoder (nvenc->encoder, &reconfigure_params);
|
||||
} else {
|
||||
nv_ret = NvEncInitializeEncoder (nvenc->encoder, params);
|
||||
|
@ -1298,12 +1338,11 @@ gst_nv_base_enc_set_format (GstVideoEncoder * enc, GstVideoCodecState * state)
|
|||
|
||||
if (nv_ret != NV_ENC_SUCCESS) {
|
||||
GST_ELEMENT_ERROR (nvenc, LIBRARY, SETTINGS, (NULL),
|
||||
("Failed to %sinit encoder: %d", old_state ? "re" : "", nv_ret));
|
||||
("Failed to %sinit encoder: %d", reconfigure ? "re" : "", nv_ret));
|
||||
return FALSE;
|
||||
}
|
||||
GST_INFO_OBJECT (nvenc, "configured encoder");
|
||||
|
||||
if (!old_state) {
|
||||
if (!reconfigure) {
|
||||
nvenc->input_info = *info;
|
||||
nvenc->gl_input = FALSE;
|
||||
}
|
||||
|
@ -1311,10 +1350,10 @@ gst_nv_base_enc_set_format (GstVideoEncoder * enc, GstVideoCodecState * state)
|
|||
if (nvenc->input_state)
|
||||
gst_video_codec_state_unref (nvenc->input_state);
|
||||
nvenc->input_state = gst_video_codec_state_ref (state);
|
||||
GST_INFO_OBJECT (nvenc, "configured encoder");
|
||||
GST_INFO_OBJECT (nvenc, "%sconfigured encoder", reconfigure ? "re" : "");
|
||||
|
||||
/* now allocate some buffers only on first configuration */
|
||||
if (!old_state) {
|
||||
if (!reconfigure) {
|
||||
#if HAVE_NVCODEC_GST_GL
|
||||
GstCapsFeatures *features;
|
||||
#endif
|
||||
|
@ -1816,6 +1855,9 @@ gst_nv_base_enc_handle_frame (GstVideoEncoder * enc, GstVideoCodecFrame * frame)
|
|||
if (g_atomic_int_compare_and_exchange (&nvenc->reconfig, TRUE, FALSE)) {
|
||||
if (!gst_nv_base_enc_set_format (enc, nvenc->input_state))
|
||||
return GST_FLOW_ERROR;
|
||||
|
||||
/* reconfigured encode session should start from keyframe */
|
||||
GST_VIDEO_CODEC_FRAME_SET_FORCE_KEYFRAME (frame);
|
||||
}
|
||||
#if HAVE_NVCODEC_GST_GL
|
||||
if (nvenc->gl_input)
|
||||
|
|
|
@ -74,6 +74,8 @@ typedef struct {
|
|||
GstCudaContext * cuda_ctx;
|
||||
CUstream cuda_stream;
|
||||
void * encoder;
|
||||
NV_ENC_INITIALIZE_PARAMS init_params;
|
||||
NV_ENC_CONFIG config;
|
||||
|
||||
/* the supported input formats */
|
||||
GValue * input_formats; /* OBJECT LOCK */
|
||||
|
@ -102,10 +104,6 @@ typedef struct {
|
|||
void *display; /* GstGLDisplay */
|
||||
void *other_context; /* GstGLContext */
|
||||
|
||||
/* the maximum buffer size the encoder is configured for */
|
||||
guint max_encode_width;
|
||||
guint max_encode_height;
|
||||
|
||||
GstVideoInfo input_info; /* buffer configuration for buffers sent to NVENC */
|
||||
|
||||
GstFlowReturn last_flow; /* ATOMIC */
|
||||
|
@ -130,14 +128,6 @@ typedef struct {
|
|||
G_GNUC_INTERNAL
|
||||
GType gst_nv_base_enc_get_type (void);
|
||||
|
||||
|
||||
void gst_nv_base_enc_get_max_encode_size (GstNvBaseEnc * nvenc,
|
||||
guint * max_width,
|
||||
guint * max_height);
|
||||
void gst_nv_base_enc_set_max_encode_size (GstNvBaseEnc * nvenc,
|
||||
guint max_width,
|
||||
guint max_height);
|
||||
|
||||
void gst_nv_base_enc_register (GstPlugin * plugin,
|
||||
GType type,
|
||||
const char * codec,
|
||||
|
|
Loading…
Reference in a new issue