From f831b92540ffa23e6ead56c0c9200d380d7aa68b Mon Sep 17 00:00:00 2001 From: Seungha Yang Date: Mon, 13 Feb 2023 21:59:05 +0900 Subject: [PATCH] nvdecoder: Add support for reconfiguration Instead of creating new decoder instance per new sequence, re-use configured decoder instance via cuvidReconfigureDecoder() API. It will make output surface reusable without re-allocation. Also, in order for application to be able to reserve higher resolution output surface, "init-max-width" and "init-max-height" properties are added to each decoder. Part-of: --- .../sys/nvcodec/gstcuvidloader.c | 22 +++ .../sys/nvcodec/gstcuvidloader.h | 5 + .../sys/nvcodec/gstnvav1dec.cpp | 70 +++++++++- .../sys/nvcodec/gstnvdecobject.cpp | 126 +++++++++++++++--- .../sys/nvcodec/gstnvdecobject.h | 7 + .../sys/nvcodec/gstnvdecoder.cpp | 79 +++++++++-- .../sys/nvcodec/gstnvdecoder.h | 10 +- .../sys/nvcodec/gstnvh264dec.cpp | 64 ++++++++- .../sys/nvcodec/gstnvh265dec.cpp | 71 +++++++++- .../sys/nvcodec/gstnvvp8dec.cpp | 72 +++++++++- .../sys/nvcodec/gstnvvp9dec.cpp | 72 +++++++++- 11 files changed, 565 insertions(+), 33 deletions(-) diff --git a/subprojects/gst-plugins-bad/sys/nvcodec/gstcuvidloader.c b/subprojects/gst-plugins-bad/sys/nvcodec/gstcuvidloader.c index e957e062e0..c51a428b08 100644 --- a/subprojects/gst-plugins-bad/sys/nvcodec/gstcuvidloader.c +++ b/subprojects/gst-plugins-bad/sys/nvcodec/gstcuvidloader.c @@ -56,6 +56,8 @@ typedef struct _GstnvdecCuvidVTable unsigned int reserved_flags); CUresult (CUDAAPI * CuvidCreateDecoder) (CUvideodecoder * phDecoder, CUVIDDECODECREATEINFO * pdci); + CUresult (CUDAAPI * CuvidReconfigureDecoder) (CUvideodecoder phDecoder, + CUVIDRECONFIGUREDECODERINFO * pDecReconfigParams); CUresult (CUDAAPI * CuvidDestroyDecoder) (CUvideodecoder hDecoder); CUresult (CUDAAPI * CuvidDecodePicture) (CUvideodecoder hDecoder, CUVIDPICPARAMS * pPicParams); @@ -97,6 +99,7 @@ gst_cuvid_load_library (guint api_major_ver, guint api_minor_ver) LOAD_SYMBOL (cuvidCtxLock, CuvidCtxLock, TRUE); LOAD_SYMBOL (cuvidCtxUnlock, CuvidCtxUnlock, TRUE); LOAD_SYMBOL (cuvidCreateDecoder, CuvidCreateDecoder, TRUE); + LOAD_SYMBOL (cuvidReconfigureDecoder, CuvidReconfigureDecoder, FALSE); LOAD_SYMBOL (cuvidDestroyDecoder, CuvidDestroyDecoder, TRUE); LOAD_SYMBOL (cuvidDecodePicture, CuvidDecodePicture, TRUE); LOAD_SYMBOL (cuvidCreateVideoParser, CuvidCreateVideoParser, TRUE); @@ -142,6 +145,15 @@ gst_cuvid_can_get_decoder_caps (void) return FALSE; } +gboolean +gst_cuvid_can_reconfigure (void) +{ + if (gst_cuvid_vtable.CuvidReconfigureDecoder) + return TRUE; + + return FALSE; +} + CUresult CUDAAPI CuvidCtxLockCreate (CUvideoctxlock * pLock, CUcontext ctx) { @@ -182,6 +194,16 @@ CuvidCreateDecoder (CUvideodecoder * phDecoder, CUVIDDECODECREATEINFO * pdci) return gst_cuvid_vtable.CuvidCreateDecoder (phDecoder, pdci); } +CUresult CUDAAPI +CuvidReconfigureDecoder (CUvideodecoder hDecoder, + CUVIDRECONFIGUREDECODERINFO * pDecReconfigParams) +{ + g_assert (gst_cuvid_vtable.CuvidReconfigureDecoder != NULL); + + return gst_cuvid_vtable.CuvidReconfigureDecoder (hDecoder, + pDecReconfigParams); +} + CUresult CUDAAPI CuvidDestroyDecoder (CUvideodecoder hDecoder) { diff --git a/subprojects/gst-plugins-bad/sys/nvcodec/gstcuvidloader.h b/subprojects/gst-plugins-bad/sys/nvcodec/gstcuvidloader.h index ebefe2ba01..79ee3494ee 100644 --- a/subprojects/gst-plugins-bad/sys/nvcodec/gstcuvidloader.h +++ b/subprojects/gst-plugins-bad/sys/nvcodec/gstcuvidloader.h @@ -35,6 +35,8 @@ gboolean gst_cuvid_get_api_version (guint * api_major_ver, gboolean gst_cuvid_can_get_decoder_caps (void); +gboolean gst_cuvid_can_reconfigure (void); + CUresult CUDAAPI CuvidCtxLockCreate (CUvideoctxlock * pLock, CUcontext ctx); @@ -49,6 +51,9 @@ CUresult CUDAAPI CuvidCtxUnlock (CUvideoctxlock lck, CUresult CUDAAPI CuvidCreateDecoder (CUvideodecoder * phDecoder, CUVIDDECODECREATEINFO * pdci); +CUresult CUDAAPI CuvidReconfigureDecoder (CUvideodecoder hDecoder, + CUVIDRECONFIGUREDECODERINFO * pDecReconfigParams); + CUresult CUDAAPI CuvidDestroyDecoder (CUvideodecoder hDecoder); CUresult CUDAAPI CuvidDecodePicture (CUvideodecoder hDecoder, diff --git a/subprojects/gst-plugins-bad/sys/nvcodec/gstnvav1dec.cpp b/subprojects/gst-plugins-bad/sys/nvcodec/gstnvav1dec.cpp index d09cd0e544..783a8e7eca 100644 --- a/subprojects/gst-plugins-bad/sys/nvcodec/gstnvav1dec.cpp +++ b/subprojects/gst-plugins-bad/sys/nvcodec/gstnvav1dec.cpp @@ -71,12 +71,16 @@ typedef struct _GstNvAV1Dec guint8 film_grain_params_present; guint num_output_surfaces; + guint init_max_width; + guint init_max_height; } GstNvAV1Dec; typedef struct _GstNvAV1DecClass { GstAV1DecoderClass parent_class; guint cuda_device_id; + guint max_width; + guint max_height; } GstNvAV1DecClass; enum @@ -84,6 +88,8 @@ enum PROP_0, PROP_CUDA_DEVICE_ID, PROP_NUM_OUTPUT_SURFACES, + PROP_INIT_MAX_WIDTH, + PROP_INIT_MAX_HEIGHT, }; #define DEFAULT_NUM_OUTPUT_SURFACES 0 @@ -163,6 +169,38 @@ gst_nv_av1_dec_class_init (GstNvAV1DecClass * klass, (GParamFlags) (GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + /** + * GstNvAV1Dec:init-max-width: + * + * Initial CUVIDDECODECREATEINFO.ulMaxWidth value + * + * Since: 1.24 + */ + g_object_class_install_property (object_class, PROP_INIT_MAX_WIDTH, + g_param_spec_uint ("init-max-width", "Initial Maximum Width", + "Expected maximum coded width of stream. This value is used to " + "pre-allocate higher dimension of output surfaces than " + "that of input stream, in order to help decoder reconfiguration", + 0, cdata->max_width, 0, + (GParamFlags) (GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS))); + + /** + * GstNvAV1Dec:init-max-height: + * + * Initial CUVIDDECODECREATEINFO.ulMaxHeight value + * + * Since: 1.24 + */ + g_object_class_install_property (object_class, PROP_INIT_MAX_HEIGHT, + g_param_spec_uint ("init-max-height", "Initial Maximum Height", + "Expected maximum coded height of stream. This value is used to " + "pre-allocate higher dimension of output surfaces than " + "that of input stream, in order to help decoder reconfiguration", + 0, cdata->max_height, 0, + (GParamFlags) (GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS))); + element_class->set_context = GST_DEBUG_FUNCPTR (gst_nv_av1_dec_set_context); parent_class = (GTypeClass *) g_type_class_peek_parent (klass); @@ -204,6 +242,8 @@ gst_nv_av1_dec_class_init (GstNvAV1DecClass * klass, GST_DEBUG_FUNCPTR (gst_nv_av1_dec_get_preferred_output_delay); klass->cuda_device_id = cdata->cuda_device_id; + klass->max_width = cdata->max_width; + klass->max_height = cdata->max_height; gst_caps_unref (cdata->sink_caps); gst_caps_unref (cdata->src_caps); @@ -226,6 +266,12 @@ gst_nv_av1_dec_set_property (GObject * object, guint prop_id, case PROP_NUM_OUTPUT_SURFACES: self->num_output_surfaces = g_value_get_uint (value); break; + case PROP_INIT_MAX_WIDTH: + self->init_max_width = g_value_get_uint (value); + break; + case PROP_INIT_MAX_HEIGHT: + self->init_max_height = g_value_get_uint (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -246,6 +292,12 @@ gst_nv_av1_dec_get_property (GObject * object, guint prop_id, GValue * value, case PROP_NUM_OUTPUT_SURFACES: g_value_set_uint (value, self->num_output_surfaces); break; + case PROP_INIT_MAX_WIDTH: + g_value_set_uint (value, self->init_max_width); + break; + case PROP_INIT_MAX_HEIGHT: + g_value_set_uint (value, self->init_max_height); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -421,6 +473,7 @@ gst_nv_av1_dec_new_sequence (GstAV1Decoder * decoder, const GstAV1SequenceHeaderOBU * seq_hdr, gint max_dpb_size) { GstNvAV1Dec *self = GST_NV_AV1_DEC (decoder); + GstNvAV1DecClass *klass = GST_NV_AV1_DEC_GET_CLASS (self); gboolean modified = FALSE; guint max_width, max_height; @@ -480,10 +533,15 @@ gst_nv_av1_dec_new_sequence (GstAV1Decoder * decoder, out_format, GST_ROUND_UP_2 (self->max_width), GST_ROUND_UP_2 (self->max_height)); + max_width = gst_nv_decoder_get_max_output_size (self->max_width, + self->init_max_width, klass->max_width); + max_height = gst_nv_decoder_get_max_output_size (self->max_height, + self->init_max_height, klass->max_height); + if (!gst_nv_decoder_configure (self->decoder, cudaVideoCodec_AV1, &info, self->max_width, self->max_height, self->bitdepth, max_dpb_size, self->film_grain_params_present ? TRUE : FALSE, - self->num_output_surfaces)) { + self->num_output_surfaces, max_width, max_height)) { GST_ERROR_OBJECT (self, "Failed to create decoder"); return GST_FLOW_NOT_NEGOTIATED; } @@ -985,6 +1043,8 @@ gst_nv_av1_dec_register (GstPlugin * plugin, guint device_id, guint rank, gchar *type_name; gchar *feature_name; guint index = 0; + const GValue *value; + GstStructure *s; GTypeInfo type_info = { sizeof (GstNvAV1DecClass), nullptr, @@ -1001,6 +1061,14 @@ gst_nv_av1_dec_register (GstPlugin * plugin, guint device_id, guint rank, GST_DEBUG_CATEGORY_INIT (gst_nv_av1_dec_debug, "nvav1dec", 0, "nvav1dec"); cdata = g_new0 (GstNvDecoderClassData, 1); + + s = gst_caps_get_structure (sink_caps, 0); + value = gst_structure_get_value (s, "width"); + cdata->max_width = (guint) gst_value_get_int_range_max (value); + + value = gst_structure_get_value (s, "height"); + cdata->max_height = (guint) gst_value_get_int_range_max (value); + cdata->sink_caps = gst_caps_ref (sink_caps); cdata->src_caps = gst_caps_ref (src_caps); cdata->cuda_device_id = device_id; diff --git a/subprojects/gst-plugins-bad/sys/nvcodec/gstnvdecobject.cpp b/subprojects/gst-plugins-bad/sys/nvcodec/gstnvdecobject.cpp index 22f31ecf18..56c2d9ec36 100644 --- a/subprojects/gst-plugins-bad/sys/nvcodec/gstnvdecobject.cpp +++ b/subprojects/gst-plugins-bad/sys/nvcodec/gstnvdecobject.cpp @@ -38,13 +38,14 @@ extern "C" #define GST_CAT_DEFAULT gst_nv_decoder_debug GST_DEFINE_MINI_OBJECT_TYPE (GstNvDecSurface, gst_nv_dec_surface); -static GstNvDecSurface *gst_nv_dec_surface_new (void); +static GstNvDecSurface *gst_nv_dec_surface_new (guint seq_num); /* *INDENT-OFF* */ struct GstNvDecOutput { GstNvDecObject *self = nullptr; CUdeviceptr devptr = 0; + guint seq_num = 0; }; struct GstNvDecObjectPrivate @@ -76,6 +77,8 @@ struct _GstNvDecObject guint pool_size; guint num_mapped; gboolean alloc_aux_frame; + guint plane_height; + guint seq_num; }; static void gst_nv_dec_object_finalize (GObject * object); @@ -160,9 +163,10 @@ gst_nv_dec_object_new (GstCudaContext * context, self->create_info = *create_info; self->video_info = *video_info; self->pool_size = pool_size; + self->plane_height = create_info->ulTargetHeight; for (guint i = 0; i < pool_size; i++) { - GstNvDecSurface *surf = gst_nv_dec_surface_new (); + GstNvDecSurface *surf = gst_nv_dec_surface_new (0); surf->index = i; @@ -180,6 +184,73 @@ gst_nv_dec_object_new (GstCudaContext * context, return self; } +gboolean +gst_nv_dec_object_reconfigure (GstNvDecObject * object, + CUVIDRECONFIGUREDECODERINFO * reconfigure_info, + const GstVideoInfo * video_info, gboolean alloc_aux_frame) +{ + GstNvDecObjectPrivate *priv = object->priv; + CUresult ret; + guint pool_size; + + if (!gst_cuvid_can_reconfigure ()) + return FALSE; + + pool_size = reconfigure_info->ulNumDecodeSurfaces; + if (alloc_aux_frame) + pool_size /= 2; + + std::lock_guard < std::mutex > lk (priv->lock); + if (!gst_cuda_context_push (object->context)) { + GST_ERROR_OBJECT (object, "Couldn't push context"); + return FALSE; + } + + ret = CuvidReconfigureDecoder (object->handle, reconfigure_info); + gst_cuda_context_pop (nullptr); + + if (!gst_cuda_result (ret)) { + GST_ERROR_OBJECT (object, "Couldn't reconfigure decoder"); + return FALSE; + } + + if ((guint) priv->surface_queue.size () != object->pool_size) { + GST_WARNING_OBJECT (object, "Unused surfaces %u != pool size %u", + (guint) priv->surface_queue.size (), object->pool_size); + } + + /* Release old surfaces and create new ones */ + /* *INDENT-OFF* */ + for (auto it : priv->surface_queue) + gst_nv_dec_surface_unref (it); + /* *INDENT-ON* */ + + priv->surface_queue.clear (); + + object->pool_size = pool_size; + object->video_info = *video_info; + object->seq_num++; + object->plane_height = reconfigure_info->ulTargetHeight; + + for (guint i = 0; i < pool_size; i++) { + GstNvDecSurface *surf = gst_nv_dec_surface_new (object->seq_num); + + surf->index = i; + + /* [0, pool_size - 1]: output picture + * [pool_size, pool_size * 2 - 1]: decoder output without film-grain, + * used for reference picture */ + if (alloc_aux_frame) + surf->decode_frame_index = i + pool_size; + else + surf->decode_frame_index = i; + + object->priv->surface_queue.push_back (surf); + } + + return TRUE; +} + void gst_nv_dec_object_set_flushing (GstNvDecObject * object, gboolean flushing) { @@ -381,7 +452,7 @@ gst_nv_dec_object_export_surface (GstNvDecObject * object, GST_LOG_OBJECT (object, "Exporting surface %d", surface->index); - offset = surface->pitch * object->create_info.ulTargetHeight; + offset = surface->pitch * object->plane_height; info = object->video_info; switch (GST_VIDEO_INFO_FORMAT (&info)) { @@ -431,11 +502,21 @@ gst_nv_dec_object_export_surface (GstNvDecObject * object, GST_LOG_OBJECT (object, "Waiting for output release"); priv->cond.wait (lk); } while (true); + + output = (GstNvDecOutput *) + gst_cuda_memory_get_user_data (GST_CUDA_MEMORY_CAST (mem)); + if (output->seq_num != object->seq_num) { + GST_DEBUG_OBJECT (object, + "output belongs to previous sequence, need new memory"); + gst_memory_unref (mem); + mem = nullptr; + } } if (!mem) { output = new GstNvDecOutput (); output->devptr = surface->devptr; + output->seq_num = object->seq_num; GST_LOG_OBJECT (object, "New output, allocating memory"); @@ -469,6 +550,7 @@ gst_nv_dec_surface_dispose (GstNvDecSurface * surf) { GstNvDecObject *object; GstNvDecObjectPrivate *priv; + gboolean ret = FALSE; if (!surf->object) return TRUE; @@ -476,34 +558,44 @@ gst_nv_dec_surface_dispose (GstNvDecSurface * surf) object = (GstNvDecObject *) g_steal_pointer (&surf->object); priv = object->priv; - /* Back to surface queue */ - gst_nv_dec_surface_ref (surf); - /* *INDENT-OFF* */ { std::lock_guard < std::mutex > lk (priv->lock); - /* Keep sorted order */ - priv->surface_queue.insert ( - std::upper_bound (priv->surface_queue.begin (), - priv->surface_queue.end(), surf, - [] (const GstNvDecSurface * a, const GstNvDecSurface * b) - { - return a->index < b->index; - }), surf); - priv->cond.notify_all (); + + if (surf->seq_num == object->seq_num) { + /* Back to surface queue */ + gst_nv_dec_surface_ref (surf); + + /* Keep sorted order */ + priv->surface_queue.insert ( + std::upper_bound (priv->surface_queue.begin (), + priv->surface_queue.end(), surf, + [] (const GstNvDecSurface * a, const GstNvDecSurface * b) + { + return a->index < b->index; + }), surf); + priv->cond.notify_all (); + } else { + GST_WARNING_OBJECT (object, "Releasing surface %p of previous sequence", + surf); + /* Shouldn't happen (e.g., surfaces were not flushed before reconfigure) */ + ret = TRUE; + } } /* *INDENT-ON* */ gst_object_unref (object); - return FALSE; + return ret; } static GstNvDecSurface * -gst_nv_dec_surface_new (void) +gst_nv_dec_surface_new (guint seq_num) { GstNvDecSurface *surf = g_new0 (GstNvDecSurface, 1); + surf->seq_num = seq_num; + gst_mini_object_init (GST_MINI_OBJECT_CAST (surf), 0, GST_TYPE_NV_DEC_SURFACE, nullptr, (GstMiniObjectDisposeFunction) gst_nv_dec_surface_dispose, diff --git a/subprojects/gst-plugins-bad/sys/nvcodec/gstnvdecobject.h b/subprojects/gst-plugins-bad/sys/nvcodec/gstnvdecobject.h index f33e154aa4..57b9c5c5d2 100644 --- a/subprojects/gst-plugins-bad/sys/nvcodec/gstnvdecobject.h +++ b/subprojects/gst-plugins-bad/sys/nvcodec/gstnvdecobject.h @@ -44,6 +44,8 @@ struct _GstNvDecSurface CUdeviceptr devptr; guint pitch; + + guint seq_num; }; GstNvDecObject * gst_nv_dec_object_new (GstCudaContext * context, @@ -51,6 +53,11 @@ GstNvDecObject * gst_nv_dec_object_new (GstCudaContext * context, const GstVideoInfo * video_info, gboolean alloc_aux_frame); +gboolean gst_nv_dec_object_reconfigure (GstNvDecObject * object, + CUVIDRECONFIGUREDECODERINFO * reconfigure_info, + const GstVideoInfo * video_info, + gboolean alloc_aux_frame); + void gst_nv_dec_object_set_flushing (GstNvDecObject * object, gboolean flushing); diff --git a/subprojects/gst-plugins-bad/sys/nvcodec/gstnvdecoder.cpp b/subprojects/gst-plugins-bad/sys/nvcodec/gstnvdecoder.cpp index 83d2c7b9dc..0402b79085 100644 --- a/subprojects/gst-plugins-bad/sys/nvcodec/gstnvdecoder.cpp +++ b/subprojects/gst-plugins-bad/sys/nvcodec/gstnvdecoder.cpp @@ -242,10 +242,10 @@ gboolean gst_nv_decoder_configure (GstNvDecoder * decoder, cudaVideoCodec codec, GstVideoInfo * info, gint coded_width, gint coded_height, guint coded_bitdepth, guint pool_size, gboolean alloc_aux_frame, - guint num_output_surfaces) + guint num_output_surfaces, guint init_max_width, guint init_max_height) { CUVIDDECODECREATEINFO create_info = { 0, }; - GstVideoFormat format; + GstVideoFormat format, prev_format = GST_VIDEO_FORMAT_UNKNOWN; guint alloc_size; g_return_val_if_fail (GST_IS_NV_DECODER (decoder), FALSE); @@ -256,15 +256,9 @@ gst_nv_decoder_configure (GstNvDecoder * decoder, cudaVideoCodec codec, g_return_val_if_fail (coded_bitdepth >= 8, FALSE); g_return_val_if_fail (pool_size > 0, FALSE); - g_mutex_lock (&decoder->lock); - gst_nv_decoder_reset_unlocked (decoder); - g_mutex_unlock (&decoder->lock); - - decoder->info = *info; - gst_video_info_set_format (&decoder->coded_info, GST_VIDEO_INFO_FORMAT (info), - coded_width, coded_height); - format = GST_VIDEO_INFO_FORMAT (info); + if (decoder->info.finfo) + prev_format = GST_VIDEO_INFO_FORMAT (&decoder->info); /* Additional 2 frame margin */ pool_size += 2; @@ -278,6 +272,56 @@ gst_nv_decoder_configure (GstNvDecoder * decoder, cudaVideoCodec codec, alloc_size = pool_size; } + decoder->info = *info; + gst_video_info_set_format (&decoder->coded_info, format, + coded_width, coded_height); + + g_mutex_lock (&decoder->lock); + if (decoder->object) { + GST_DEBUG_OBJECT (decoder, + "Configured max resolution %ux%u %s (bit-depth %u), " + "new resolution %ux%u %s (bit-depth %u)", + (guint) decoder->create_info.ulMaxWidth, + (guint) decoder->create_info.ulMaxHeight, + gst_video_format_to_string (prev_format), + (guint) decoder->create_info.bitDepthMinus8 + 8, + (guint) coded_width, (guint) coded_height, + gst_video_format_to_string (format), coded_bitdepth); + + if (format == prev_format && + (guint) coded_width <= decoder->create_info.ulMaxWidth && + (guint) coded_height <= decoder->create_info.ulMaxHeight && + coded_bitdepth == (guint) decoder->create_info.bitDepthMinus8 + 8) { + CUVIDRECONFIGUREDECODERINFO reconfig_info = { 0, }; + + reconfig_info.ulWidth = GST_VIDEO_INFO_WIDTH (&decoder->coded_info); + reconfig_info.ulHeight = GST_VIDEO_INFO_HEIGHT (&decoder->coded_info); + reconfig_info.ulTargetWidth = GST_VIDEO_INFO_WIDTH (info); + reconfig_info.ulTargetHeight = GST_VIDEO_INFO_HEIGHT (info); + reconfig_info.ulNumDecodeSurfaces = alloc_size; + reconfig_info.display_area.right = GST_VIDEO_INFO_WIDTH (info); + reconfig_info.display_area.bottom = GST_VIDEO_INFO_HEIGHT (info); + reconfig_info.target_rect.right = GST_VIDEO_INFO_WIDTH (info); + reconfig_info.target_rect.bottom = GST_VIDEO_INFO_HEIGHT (info); + + if (gst_nv_dec_object_reconfigure (decoder->object, + &reconfig_info, info, alloc_aux_frame)) { + GST_DEBUG_OBJECT (decoder, "Reconfigured"); + decoder->configured = TRUE; + g_mutex_unlock (&decoder->lock); + return TRUE; + } else { + GST_WARNING_OBJECT (decoder, + "Couldn't reconfigure decoder, creating new decoder instance"); + } + } else { + GST_DEBUG_OBJECT (decoder, "Need new decoder instance"); + } + } + + gst_nv_decoder_reset_unlocked (decoder); + g_mutex_unlock (&decoder->lock); + decoder->num_output_surfaces = num_output_surfaces; create_info.ulWidth = GST_VIDEO_INFO_WIDTH (&decoder->coded_info); @@ -306,6 +350,9 @@ gst_nv_decoder_configure (GstNvDecoder * decoder, cudaVideoCodec codec, create_info.target_rect.right = GST_VIDEO_INFO_WIDTH (info); create_info.target_rect.bottom = GST_VIDEO_INFO_HEIGHT (info); + create_info.ulMaxWidth = MAX (create_info.ulWidth, init_max_width); + create_info.ulMaxHeight = MAX (create_info.ulHeight, init_max_height); + decoder->create_info = create_info; decoder->configured = TRUE; @@ -1609,3 +1656,15 @@ gst_nv_decoder_reset (GstNvDecoder * decoder) gst_nv_decoder_reset_unlocked (decoder); g_mutex_unlock (&decoder->lock); } + +guint +gst_nv_decoder_get_max_output_size (guint coded_size, guint user_requested, + guint device_max) +{ + if (user_requested <= coded_size) + return coded_size; + + user_requested = GST_ROUND_UP_16 (user_requested); + + return MIN (user_requested, device_max); +} diff --git a/subprojects/gst-plugins-bad/sys/nvcodec/gstnvdecoder.h b/subprojects/gst-plugins-bad/sys/nvcodec/gstnvdecoder.h index 0ebecd6d91..b79fbf95fa 100644 --- a/subprojects/gst-plugins-bad/sys/nvcodec/gstnvdecoder.h +++ b/subprojects/gst-plugins-bad/sys/nvcodec/gstnvdecoder.h @@ -37,6 +37,8 @@ typedef struct _GstNvDecoderClassData GstCaps *sink_caps; GstCaps *src_caps; guint cuda_device_id; + guint max_width; + guint max_height; } GstNvDecoderClassData; GstNvDecoder * gst_nv_decoder_new (GstCudaContext * context); @@ -51,7 +53,9 @@ gboolean gst_nv_decoder_configure (GstNvDecoder * decoder, guint coded_bitdepth, guint pool_size, gboolean alloc_aux_frame, - guint num_output_surfaces); + guint num_output_surfaces, + guint init_max_width, + guint init_max_height); GstFlowReturn gst_nv_decoder_acquire_surface (GstNvDecoder * decoder, GstNvDecSurface ** surface); @@ -95,6 +99,10 @@ gboolean gst_nv_decoder_decide_allocation (GstNvDecoder * decoder, GstVideoDecoder * videodec, GstQuery * query); +guint gst_nv_decoder_get_max_output_size (guint coded_size, + guint user_requested, + guint device_max); + G_END_DECLS #endif /* __GST_NV_DECODER_H__ */ diff --git a/subprojects/gst-plugins-bad/sys/nvcodec/gstnvh264dec.cpp b/subprojects/gst-plugins-bad/sys/nvcodec/gstnvh264dec.cpp index 42858f35f0..0c93c455ea 100644 --- a/subprojects/gst-plugins-bad/sys/nvcodec/gstnvh264dec.cpp +++ b/subprojects/gst-plugins-bad/sys/nvcodec/gstnvh264dec.cpp @@ -127,12 +127,16 @@ typedef struct _GstNvH264Dec GArray *ref_list; guint num_output_surfaces; + guint init_max_width; + guint init_max_height; } GstNvH264Dec; typedef struct _GstNvH264DecClass { GstH264DecoderClass parent_class; guint cuda_device_id; + guint max_width; + guint max_height; } GstNvH264DecClass; enum @@ -140,6 +144,8 @@ enum PROP_0, PROP_CUDA_DEVICE_ID, PROP_NUM_OUTPUT_SURFACES, + PROP_INIT_MAX_WIDTH, + PROP_INIT_MAX_HEIGHT, }; #define DEFAULT_NUM_OUTPUT_SURFACES 0 @@ -232,6 +238,38 @@ gst_nv_h264_dec_class_init (GstNvH264DecClass * klass, (GParamFlags) (GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + /** + * GstNvH264SLDec:init-max-width: + * + * Initial CUVIDDECODECREATEINFO.ulMaxWidth value + * + * Since: 1.24 + */ + g_object_class_install_property (object_class, PROP_INIT_MAX_WIDTH, + g_param_spec_uint ("init-max-width", "Initial Maximum Width", + "Expected maximum coded width of stream. This value is used to " + "pre-allocate higher dimension of output surfaces than " + "that of input stream, in order to help decoder reconfiguration", + 0, cdata->max_width, 0, + (GParamFlags) (GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS))); + + /** + * GstNvH264SLDec:init-max-height: + * + * Initial CUVIDDECODECREATEINFO.ulMaxHeight value + * + * Since: 1.24 + */ + g_object_class_install_property (object_class, PROP_INIT_MAX_HEIGHT, + g_param_spec_uint ("init-max-height", "Initial Maximum Height", + "Expected maximum coded height of stream. This value is used to " + "pre-allocate higher dimension of output surfaces than " + "that of input stream, in order to help decoder reconfiguration", + 0, cdata->max_height, 0, + (GParamFlags) (GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS))); + element_class->set_context = GST_DEBUG_FUNCPTR (gst_nv_h264_dec_set_context); parent_class = (GTypeClass *) g_type_class_peek_parent (klass); @@ -274,6 +312,8 @@ gst_nv_h264_dec_class_init (GstNvH264DecClass * klass, GST_DEBUG_FUNCPTR (gst_nv_h264_dec_get_preferred_output_delay); klass->cuda_device_id = cdata->cuda_device_id; + klass->max_width = cdata->max_width; + klass->max_height = cdata->max_height; gst_caps_unref (cdata->sink_caps); gst_caps_unref (cdata->src_caps); @@ -311,6 +351,12 @@ gst_nv_h264_dec_set_property (GObject * object, guint prop_id, case PROP_NUM_OUTPUT_SURFACES: self->num_output_surfaces = g_value_get_uint (value); break; + case PROP_INIT_MAX_WIDTH: + self->init_max_width = g_value_get_uint (value); + break; + case PROP_INIT_MAX_HEIGHT: + self->init_max_height = g_value_get_uint (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -331,6 +377,12 @@ gst_nv_h264_dec_get_property (GObject * object, guint prop_id, GValue * value, case PROP_NUM_OUTPUT_SURFACES: g_value_set_uint (value, self->num_output_surfaces); break; + case PROP_INIT_MAX_WIDTH: + g_value_set_uint (value, self->init_max_width); + break; + case PROP_INIT_MAX_HEIGHT: + g_value_set_uint (value, self->init_max_height); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -507,9 +559,11 @@ gst_nv_h264_dec_new_sequence (GstH264Decoder * decoder, const GstH264SPS * sps, gint max_dpb_size) { GstNvH264Dec *self = GST_NV_H264_DEC (decoder); + GstNvH264DecClass *klass = GST_NV_H264_DEC_GET_CLASS (self); guint crop_width, crop_height; gboolean modified = FALSE; gboolean interlaced; + guint max_width, max_height; GST_LOG_OBJECT (self, "new sequence"); @@ -587,10 +641,16 @@ gst_nv_h264_dec_new_sequence (GstH264Decoder * decoder, const GstH264SPS * sps, GST_VIDEO_INFO_INTERLACE_MODE (&info) = GST_VIDEO_INTERLACE_MODE_MIXED; self->max_dpb_size = max_dpb_size; + max_width = gst_nv_decoder_get_max_output_size (self->coded_width, + self->init_max_width, klass->max_width); + max_height = gst_nv_decoder_get_max_output_size (self->coded_height, + self->init_max_height, klass->max_height); + /* FIXME: add support cudaVideoCodec_H264_SVC and cudaVideoCodec_H264_MVC */ if (!gst_nv_decoder_configure (self->decoder, cudaVideoCodec_H264, &info, self->coded_width, self->coded_height, - self->bitdepth, max_dpb_size, FALSE, self->num_output_surfaces)) { + self->bitdepth, max_dpb_size, FALSE, self->num_output_surfaces, + max_width, max_height)) { GST_ERROR_OBJECT (self, "Failed to configure decoder"); return GST_FLOW_NOT_NEGOTIATED; } @@ -1064,9 +1124,11 @@ gst_nv_h264_dec_register (GstPlugin * plugin, guint device_id, guint rank, s = gst_caps_get_structure (sink_caps, 0); value = gst_structure_get_value (s, "width"); + cdata->max_width = (guint) gst_value_get_int_range_max (value); gst_caps_set_value (cdata->sink_caps, "width", value); value = gst_structure_get_value (s, "height"); + cdata->max_height = (guint) gst_value_get_int_range_max (value); gst_caps_set_value (cdata->sink_caps, "height", value); GST_MINI_OBJECT_FLAG_SET (cdata->sink_caps, diff --git a/subprojects/gst-plugins-bad/sys/nvcodec/gstnvh265dec.cpp b/subprojects/gst-plugins-bad/sys/nvcodec/gstnvh265dec.cpp index b689661b0a..da1248d150 100644 --- a/subprojects/gst-plugins-bad/sys/nvcodec/gstnvh265dec.cpp +++ b/subprojects/gst-plugins-bad/sys/nvcodec/gstnvh265dec.cpp @@ -123,12 +123,16 @@ typedef struct _GstNvH265Dec guint chroma_format_idc; guint num_output_surfaces; + guint init_max_width; + guint init_max_height; } GstNvH265Dec; typedef struct _GstNvH265DecClass { GstH265DecoderClass parent_class; guint cuda_device_id; + guint max_width; + guint max_height; } GstNvH265DecClass; enum @@ -136,6 +140,8 @@ enum PROP_0, PROP_CUDA_DEVICE_ID, PROP_NUM_OUTPUT_SURFACES, + PROP_INIT_MAX_WIDTH, + PROP_INIT_MAX_HEIGHT, }; #define DEFAULT_NUM_OUTPUT_SURFACES 0 @@ -223,6 +229,38 @@ gst_nv_h265_dec_class_init (GstNvH265DecClass * klass, (GParamFlags) (GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + /** + * GstNvH265SLDec:init-max-width: + * + * Initial CUVIDDECODECREATEINFO.ulMaxWidth value + * + * Since: 1.24 + */ + g_object_class_install_property (object_class, PROP_INIT_MAX_WIDTH, + g_param_spec_uint ("init-max-width", "Initial Maximum Width", + "Expected maximum coded width of stream. This value is used to " + "pre-allocate higher dimension of output surfaces than " + "that of input stream, in order to help decoder reconfiguration", + 0, cdata->max_width, 0, + (GParamFlags) (GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS))); + + /** + * GstNvH265SLDec:init-max-height: + * + * Initial CUVIDDECODECREATEINFO.ulMaxHeight value + * + * Since: 1.24 + */ + g_object_class_install_property (object_class, PROP_INIT_MAX_HEIGHT, + g_param_spec_uint ("init-max-height", "Initial Maximum Height", + "Expected maximum coded height of stream. This value is used to " + "pre-allocate higher dimension of output surfaces than " + "that of input stream, in order to help decoder reconfiguration", + 0, cdata->max_height, 0, + (GParamFlags) (GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS))); + element_class->set_context = GST_DEBUG_FUNCPTR (gst_nv_h265_dec_set_context); parent_class = (GTypeClass *) g_type_class_peek_parent (klass); @@ -263,6 +301,8 @@ gst_nv_h265_dec_class_init (GstNvH265DecClass * klass, GST_DEBUG_FUNCPTR (gst_nv_h265_dec_get_preferred_output_delay); klass->cuda_device_id = cdata->cuda_device_id; + klass->max_width = cdata->max_width; + klass->max_height = cdata->max_height; gst_caps_unref (cdata->sink_caps); gst_caps_unref (cdata->src_caps); @@ -285,6 +325,12 @@ gst_nv_h265_dec_set_property (GObject * object, guint prop_id, case PROP_NUM_OUTPUT_SURFACES: self->num_output_surfaces = g_value_get_uint (value); break; + case PROP_INIT_MAX_WIDTH: + self->init_max_width = g_value_get_uint (value); + break; + case PROP_INIT_MAX_HEIGHT: + self->init_max_height = g_value_get_uint (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -305,6 +351,12 @@ gst_nv_h265_dec_get_property (GObject * object, guint prop_id, GValue * value, case PROP_NUM_OUTPUT_SURFACES: g_value_set_uint (value, self->num_output_surfaces); break; + case PROP_INIT_MAX_WIDTH: + g_value_set_uint (value, self->init_max_width); + break; + case PROP_INIT_MAX_HEIGHT: + g_value_set_uint (value, self->init_max_height); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -465,8 +517,10 @@ gst_nv_h265_dec_new_sequence (GstH265Decoder * decoder, const GstH265SPS * sps, gint max_dpb_size) { GstNvH265Dec *self = GST_NV_H265_DEC (decoder); + GstNvH265DecClass *klass = GST_NV_H265_DEC_GET_CLASS (self); guint crop_width, crop_height; gboolean modified = FALSE; + guint max_width, max_height; GST_LOG_OBJECT (self, "new sequence"); @@ -541,9 +595,15 @@ gst_nv_h265_dec_new_sequence (GstH265Decoder * decoder, const GstH265SPS * sps, gst_video_info_set_format (&info, out_format, GST_ROUND_UP_2 (self->width), GST_ROUND_UP_2 (self->height)); + max_width = gst_nv_decoder_get_max_output_size (self->coded_width, + self->init_max_width, klass->max_width); + max_height = gst_nv_decoder_get_max_output_size (self->coded_height, + self->init_max_height, klass->max_height); + if (!gst_nv_decoder_configure (self->decoder, cudaVideoCodec_HEVC, &info, self->coded_width, self->coded_height, - self->bitdepth, max_dpb_size, FALSE, self->num_output_surfaces)) { + self->bitdepth, max_dpb_size, FALSE, self->num_output_surfaces, + max_width, max_height)) { GST_ERROR_OBJECT (self, "Failed to configure decoder"); return GST_FLOW_NOT_NEGOTIATED; } @@ -1081,6 +1141,8 @@ gst_nv_h265_dec_register (GstPlugin * plugin, guint device_id, guint rank, gint index = 0; GValue value_list = G_VALUE_INIT; GValue value = G_VALUE_INIT; + GstStructure *s; + const GValue *res_val; GTypeInfo type_info = { sizeof (GstNvH265DecClass), nullptr, @@ -1098,6 +1160,13 @@ gst_nv_h265_dec_register (GstPlugin * plugin, guint device_id, guint rank, cdata = g_new0 (GstNvDecoderClassData, 1); cdata->sink_caps = gst_caps_copy (sink_caps); + s = gst_caps_get_structure (sink_caps, 0); + res_val = gst_structure_get_value (s, "width"); + cdata->max_width = (guint) gst_value_get_int_range_max (res_val); + + res_val = gst_structure_get_value (s, "height"); + cdata->max_height = (guint) gst_value_get_int_range_max (res_val); + /* Update stream-format since we support packetized format as well */ g_value_init (&value_list, GST_TYPE_LIST); g_value_init (&value, G_TYPE_STRING); diff --git a/subprojects/gst-plugins-bad/sys/nvcodec/gstnvvp8dec.cpp b/subprojects/gst-plugins-bad/sys/nvcodec/gstnvvp8dec.cpp index ffe90453bf..973c63b9c6 100644 --- a/subprojects/gst-plugins-bad/sys/nvcodec/gstnvvp8dec.cpp +++ b/subprojects/gst-plugins-bad/sys/nvcodec/gstnvvp8dec.cpp @@ -55,12 +55,16 @@ typedef struct _GstNvVp8Dec guint width, height; guint num_output_surfaces; + guint init_max_width; + guint init_max_height; } GstNvVp8Dec; typedef struct _GstNvVp8DecClass { GstVp8DecoderClass parent_class; guint cuda_device_id; + guint max_width; + guint max_height; } GstNvVp8DecClass; enum @@ -68,6 +72,8 @@ enum PROP_0, PROP_CUDA_DEVICE_ID, PROP_NUM_OUTPUT_SURFACES, + PROP_INIT_MAX_WIDTH, + PROP_INIT_MAX_HEIGHT, }; #define DEFAULT_NUM_OUTPUT_SURFACES 0 @@ -149,6 +155,38 @@ gst_nv_vp8_dec_class_init (GstNvVp8DecClass * klass, (GParamFlags) (GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + /** + * GstNvVp8SLDec:init-max-width: + * + * Initial CUVIDDECODECREATEINFO.ulMaxWidth value + * + * Since: 1.24 + */ + g_object_class_install_property (object_class, PROP_INIT_MAX_WIDTH, + g_param_spec_uint ("init-max-width", "Initial Maximum Width", + "Expected maximum coded width of stream. This value is used to " + "pre-allocate higher dimension of output surfaces than " + "that of input stream, in order to help decoder reconfiguration", + 0, cdata->max_width, 0, + (GParamFlags) (GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS))); + + /** + * GstNvVp8SLDec:init-max-height: + * + * Initial CUVIDDECODECREATEINFO.ulMaxHeight value + * + * Since: 1.24 + */ + g_object_class_install_property (object_class, PROP_INIT_MAX_HEIGHT, + g_param_spec_uint ("init-max-height", "Initial Maximum Height", + "Expected maximum coded height of stream. This value is used to " + "pre-allocate higher dimension of output surfaces than " + "that of input stream, in order to help decoder reconfiguration", + 0, cdata->max_height, 0, + (GParamFlags) (GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS))); + element_class->set_context = GST_DEBUG_FUNCPTR (gst_nv_vp8_dec_set_context); parent_class = (GTypeClass *) g_type_class_peek_parent (klass); @@ -185,6 +223,8 @@ gst_nv_vp8_dec_class_init (GstNvVp8DecClass * klass, GST_DEBUG_FUNCPTR (gst_nv_vp8_dec_get_preferred_output_delay); klass->cuda_device_id = cdata->cuda_device_id; + klass->max_width = cdata->max_width; + klass->max_height = cdata->max_height; gst_caps_unref (cdata->sink_caps); gst_caps_unref (cdata->src_caps); @@ -207,6 +247,12 @@ gst_nv_vp8_dec_set_property (GObject * object, guint prop_id, case PROP_NUM_OUTPUT_SURFACES: self->num_output_surfaces = g_value_get_uint (value); break; + case PROP_INIT_MAX_WIDTH: + self->init_max_width = g_value_get_uint (value); + break; + case PROP_INIT_MAX_HEIGHT: + self->init_max_height = g_value_get_uint (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -227,6 +273,12 @@ gst_nv_vp8_dec_get_property (GObject * object, guint prop_id, GValue * value, case PROP_NUM_OUTPUT_SURFACES: g_value_set_uint (value, self->num_output_surfaces); break; + case PROP_INIT_MAX_WIDTH: + g_value_set_uint (value, self->init_max_width); + break; + case PROP_INIT_MAX_HEIGHT: + g_value_set_uint (value, self->init_max_height); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -381,7 +433,9 @@ gst_nv_vp8_dec_new_sequence (GstVp8Decoder * decoder, const GstVp8FrameHdr * frame_hdr, gint max_dpb_size) { GstNvVp8Dec *self = GST_NV_VP8_DEC (decoder); + GstNvVp8DecClass *klass = GST_NV_VP8_DEC_GET_CLASS (self); gboolean modified = FALSE; + guint max_width, max_height; GST_LOG_OBJECT (self, "new sequence"); @@ -404,9 +458,15 @@ gst_nv_vp8_dec_new_sequence (GstVp8Decoder * decoder, GST_VIDEO_FORMAT_NV12, GST_ROUND_UP_2 (self->width), GST_ROUND_UP_2 (self->height)); + max_width = gst_nv_decoder_get_max_output_size (self->width, + self->init_max_width, klass->max_width); + max_height = gst_nv_decoder_get_max_output_size (self->height, + self->init_max_height, klass->max_height); + if (!gst_nv_decoder_configure (self->decoder, cudaVideoCodec_VP8, &info, self->width, self->height, 8, - max_dpb_size, FALSE, self->num_output_surfaces)) { + max_dpb_size, FALSE, self->num_output_surfaces, max_width, + max_height)) { GST_ERROR_OBJECT (self, "Failed to configure decoder"); return GST_FLOW_NOT_NEGOTIATED; } @@ -600,6 +660,8 @@ gst_nv_vp8_dec_register (GstPlugin * plugin, guint device_id, guint rank, gchar *feature_name; GstNvDecoderClassData *cdata; gint index = 0; + const GValue *value; + GstStructure *s; GTypeInfo type_info = { sizeof (GstNvVp8DecClass), nullptr, @@ -615,6 +677,14 @@ gst_nv_vp8_dec_register (GstPlugin * plugin, guint device_id, guint rank, GST_DEBUG_CATEGORY_INIT (gst_nv_vp8_dec_debug, "nvvp8dec", 0, "nvvp8dec"); cdata = g_new0 (GstNvDecoderClassData, 1); + + s = gst_caps_get_structure (sink_caps, 0); + value = gst_structure_get_value (s, "width"); + cdata->max_width = (guint) gst_value_get_int_range_max (value); + + value = gst_structure_get_value (s, "height"); + cdata->max_height = (guint) gst_value_get_int_range_max (value); + cdata->sink_caps = gst_caps_ref (sink_caps); cdata->src_caps = gst_caps_ref (src_caps); cdata->cuda_device_id = device_id; diff --git a/subprojects/gst-plugins-bad/sys/nvcodec/gstnvvp9dec.cpp b/subprojects/gst-plugins-bad/sys/nvcodec/gstnvvp9dec.cpp index 5c2dda64a1..8a621145f1 100644 --- a/subprojects/gst-plugins-bad/sys/nvcodec/gstnvvp9dec.cpp +++ b/subprojects/gst-plugins-bad/sys/nvcodec/gstnvvp9dec.cpp @@ -56,12 +56,16 @@ typedef struct _GstNvVp9Dec GstVP9Profile profile; guint num_output_surfaces; + guint init_max_width; + guint init_max_height; } GstNvVp9Dec; typedef struct _GstNvVp9DecClass { GstVp9DecoderClass parent_class; guint cuda_device_id; + guint max_width; + guint max_height; } GstNvVp9DecClass; enum @@ -69,6 +73,8 @@ enum PROP_0, PROP_CUDA_DEVICE_ID, PROP_NUM_OUTPUT_SURFACES, + PROP_INIT_MAX_WIDTH, + PROP_INIT_MAX_HEIGHT, }; #define DEFAULT_NUM_OUTPUT_SURFACES 0 @@ -152,6 +158,38 @@ gst_nv_vp9_dec_class_init (GstNvVp9DecClass * klass, (GParamFlags) (GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + /** + * GstNvVp9SLDec:init-max-width: + * + * Initial CUVIDDECODECREATEINFO.ulMaxWidth value + * + * Since: 1.24 + */ + g_object_class_install_property (object_class, PROP_INIT_MAX_WIDTH, + g_param_spec_uint ("init-max-width", "Initial Maximum Width", + "Expected maximum coded width of stream. This value is used to " + "pre-allocate higher dimension of output surfaces than " + "that of input stream, in order to help decoder reconfiguration", + 0, cdata->max_width, 0, + (GParamFlags) (GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS))); + + /** + * GstNvVp9SLDec:init-max-height: + * + * Initial CUVIDDECODECREATEINFO.ulMaxHeight value + * + * Since: 1.24 + */ + g_object_class_install_property (object_class, PROP_INIT_MAX_HEIGHT, + g_param_spec_uint ("init-max-height", "Initial Maximum Height", + "Expected maximum coded height of stream. This value is used to " + "pre-allocate higher dimension of output surfaces than " + "that of input stream, in order to help decoder reconfiguration", + 0, cdata->max_height, 0, + (GParamFlags) (GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS))); + element_class->set_context = GST_DEBUG_FUNCPTR (gst_nv_vp9_dec_set_context); parent_class = (GTypeClass *) g_type_class_peek_parent (klass); @@ -190,6 +228,8 @@ gst_nv_vp9_dec_class_init (GstNvVp9DecClass * klass, GST_DEBUG_FUNCPTR (gst_nv_vp9_dec_get_preferred_output_delay); klass->cuda_device_id = cdata->cuda_device_id; + klass->max_width = cdata->max_width; + klass->max_height = cdata->max_height; gst_caps_unref (cdata->sink_caps); gst_caps_unref (cdata->src_caps); @@ -212,6 +252,12 @@ gst_nv_vp9_dec_set_property (GObject * object, guint prop_id, case PROP_NUM_OUTPUT_SURFACES: self->num_output_surfaces = g_value_get_uint (value); break; + case PROP_INIT_MAX_WIDTH: + self->init_max_width = g_value_get_uint (value); + break; + case PROP_INIT_MAX_HEIGHT: + self->init_max_height = g_value_get_uint (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -232,6 +278,12 @@ gst_nv_vp9_dec_get_property (GObject * object, guint prop_id, GValue * value, case PROP_NUM_OUTPUT_SURFACES: g_value_set_uint (value, self->num_output_surfaces); break; + case PROP_INIT_MAX_WIDTH: + g_value_set_uint (value, self->init_max_width); + break; + case PROP_INIT_MAX_HEIGHT: + g_value_set_uint (value, self->init_max_height); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -391,8 +443,10 @@ gst_nv_vp9_dec_new_sequence (GstVp9Decoder * decoder, const GstVp9FrameHeader * frame_hdr, gint max_dpb_size) { GstNvVp9Dec *self = GST_NV_VP9_DEC (decoder); + GstNvVp9DecClass *klass = GST_NV_VP9_DEC_GET_CLASS (self); GstVideoFormat out_format = GST_VIDEO_FORMAT_UNKNOWN; GstVideoInfo info; + guint max_width, max_height; GST_LOG_OBJECT (self, "new sequence"); @@ -417,10 +471,16 @@ gst_nv_vp9_dec_new_sequence (GstVp9Decoder * decoder, gst_video_info_set_format (&info, out_format, GST_ROUND_UP_2 (self->width), GST_ROUND_UP_2 (self->height)); + + max_width = gst_nv_decoder_get_max_output_size (self->width, + self->init_max_width, klass->max_width); + max_height = gst_nv_decoder_get_max_output_size (self->height, + self->init_max_height, klass->max_height); + if (!gst_nv_decoder_configure (self->decoder, cudaVideoCodec_VP9, &info, self->width, self->height, frame_hdr->bit_depth, max_dpb_size, FALSE, - self->num_output_surfaces)) { + self->num_output_surfaces, max_width, max_height)) { GST_ERROR_OBJECT (self, "Failed to configure decoder"); return GST_FLOW_NOT_NEGOTIATED; } @@ -696,6 +756,8 @@ gst_nv_vp9_dec_register (GstPlugin * plugin, guint device_id, guint rank, gchar *feature_name; GstNvDecoderClassData *cdata; gint index = 0; + const GValue *value; + GstStructure *s; GTypeInfo type_info = { sizeof (GstNvVp9DecClass), nullptr, @@ -711,6 +773,14 @@ gst_nv_vp9_dec_register (GstPlugin * plugin, guint device_id, guint rank, GST_DEBUG_CATEGORY_INIT (gst_nv_vp9_dec_debug, "nvvp9dec", 0, "nvvp9dec"); cdata = g_new0 (GstNvDecoderClassData, 1); + + s = gst_caps_get_structure (sink_caps, 0); + value = gst_structure_get_value (s, "width"); + cdata->max_width = (guint) gst_value_get_int_range_max (value); + + value = gst_structure_get_value (s, "height"); + cdata->max_height = (guint) gst_value_get_int_range_max (value); + cdata->sink_caps = gst_caps_copy (sink_caps); gst_caps_set_simple (cdata->sink_caps, "alignment", G_TYPE_STRING, "frame", nullptr);