diff --git a/subprojects/gst-plugins-bad/sys/nvcodec/gstnvav1encoder.cpp b/subprojects/gst-plugins-bad/sys/nvcodec/gstnvav1encoder.cpp new file mode 100644 index 0000000000..4f639a234b --- /dev/null +++ b/subprojects/gst-plugins-bad/sys/nvcodec/gstnvav1encoder.cpp @@ -0,0 +1,1819 @@ +/* GStreamer + * Copyright (C) 2024 Seungha Yang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstnvav1encoder.h" +#include +#include +#include +#include + +GST_DEBUG_CATEGORY_STATIC (gst_nv_av1_encoder_debug); +#define GST_CAT_DEFAULT gst_nv_av1_encoder_debug + +static GTypeClass *parent_class = nullptr; + +enum +{ + PROP_0, + PROP_ADAPTER_LUID, + PROP_CUDA_DEVICE_ID, + + /* init params */ + PROP_PRESET, + PROP_TUNE, + PROP_MULTI_PASS, + PROP_WEIGHTED_PRED, + + /* encoding config */ + PROP_GOP_SIZE, + PROP_B_FRAMES, + + /* rate-control params */ + PROP_RATE_CONTROL, + + PROP_QP_CONST_I, + PROP_QP_CONST_P, + PROP_QP_CONST_B, + + PROP_BITRATE, + PROP_MAX_BITRATE, + PROP_VBV_BUFFER_SIZE, + + PROP_RC_LOOKAHEAD, + PROP_I_ADAPT, + PROP_B_ADAPT, + PROP_SPATIAL_AQ, + PROP_TEMPORAL_AQ, + PROP_ZEROLATENCY, + PROP_NON_REF_P, + PROP_STRICT_GOP, + PROP_AQ_STRENGTH, + + PROP_QP_MIN_I, + PROP_QP_MIN_P, + PROP_QP_MIN_B, + + PROP_QP_MAX_I, + PROP_QP_MAX_P, + PROP_QP_MAX_B, + + PROP_CONST_QUALITY, +}; + +#define DEFAULT_PRESET GST_NV_ENCODER_PRESET_DEFAULT +#define DEFAULT_TUNE GST_NV_ENCODER_TUNE_DEFAULT +#define DEFAULT_MULTI_PASS GST_NV_ENCODER_MULTI_PASS_DEFAULT +#define DEFAULT_WEIGHTED_PRED FALSE +#define DEFAULT_GOP_SIZE 75 +#define DEFAULT_B_FRAMES 0 +#define DEFAULT_RATE_CONTROL GST_NV_ENCODER_RC_MODE_DEFAULT +#define DEFAULT_QP -1 +#define DEFAULT_BITRATE 0 +#define DEFAULT_MAX_BITRATE 0 +#define DEFAULT_VBV_BUFFER_SIZE 0 +#define DEFAULT_RC_LOOKAHEAD 0 +#define DEFAULT_I_ADAPT FALSE +#define DEFAULT_B_ADAPT FALSE +#define DEFAULT_SPATIAL_AQ FALSE +#define DEFAULT_TEMPORAL_AQ FALSE +#define DEFAULT_ZEROLATENCY FALSE +#define DEFAULT_NON_REF_P FALSE +#define DEFAULT_STRICT_GOP FALSE +#define DEFAULT_AQ_STRENGTH FALSE +#define DEFAULT_CONST_QUALITY 0 + +typedef struct _GstNvAv1Encoder +{ + GstNvEncoder parent; + GMutex prop_lock; + + gboolean init_param_updated; + gboolean rc_param_updated; + gboolean bitrate_updated; + + GstNvEncoderDeviceMode selected_device_mode; + + /* Properties */ + guint cuda_device_id; + gint64 adapter_luid; + + GstNvEncoderPreset preset; + GstNvEncoderMultiPass multipass; + GstNvEncoderTune tune; + gboolean weighted_pred; + + gint gop_size; + guint bframes; + + GstNvEncoderRCMode rc_mode; + gint qp_const_i; + gint qp_const_p; + gint qp_const_b; + guint bitrate; + guint max_bitrate; + guint vbv_buffer_size; + guint rc_lookahead; + gboolean i_adapt; + gboolean b_adapt; + gboolean spatial_aq; + gboolean temporal_aq; + gboolean zero_reorder_delay; + gboolean non_ref_p; + gboolean strict_gop; + guint aq_strength; + gint qp_min_i; + gint qp_min_p; + gint qp_min_b; + gint qp_max_i; + gint qp_max_p; + gint qp_max_b; + gdouble const_quality; +} GstNvAv1Encoder; + +typedef struct _GstNvAv1EncoderClass +{ + GstNvEncoderClass parent_class; + + guint cuda_device_id; + gint64 adapter_luid; + + GstNvEncoderDeviceMode device_mode; + + /* representative device caps */ + GstNvEncoderDeviceCaps device_caps; + + /* auto gpu select mode */ + guint cuda_device_id_list[8]; + guint cuda_device_id_size; + + gint64 adapter_luid_list[8]; + guint adapter_luid_size; +} GstNvAv1EncoderClass; + +#define GST_NV_AV1_ENCODER(object) ((GstNvAv1Encoder *) (object)) +#define GST_NV_AV1_ENCODER_GET_CLASS(object) \ + (G_TYPE_INSTANCE_GET_CLASS ((object),G_TYPE_FROM_INSTANCE (object),GstNvAv1EncoderClass)) + +static void gst_nv_av1_encoder_finalize (GObject * object); +static void gst_nv_av1_encoder_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_nv_av1_encoder_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static gboolean gst_nv_av1_encoder_set_format (GstNvEncoder * encoder, + GstVideoCodecState * state, gpointer session, + NV_ENC_INITIALIZE_PARAMS * init_params, NV_ENC_CONFIG * config); +static gboolean gst_nv_av1_encoder_set_output_state (GstNvEncoder * encoder, + GstVideoCodecState * state, gpointer session); +static GstNvEncoderReconfigure +gst_nv_av1_encoder_check_reconfigure (GstNvEncoder * encoder, + NV_ENC_CONFIG * config); +static gboolean gst_nv_av1_encoder_select_device (GstNvEncoder * encoder, + const GstVideoInfo * info, GstBuffer * buffer, + GstNvEncoderDeviceData * data); +static guint gst_nv_av1_encoder_calculate_min_buffers (GstNvEncoder * encoder); + +static void +gst_nv_av1_encoder_class_init (GstNvAv1EncoderClass * klass, gpointer data) +{ + auto object_class = G_OBJECT_CLASS (klass); + auto element_class = GST_ELEMENT_CLASS (klass); + auto nvenc_class = GST_NV_ENCODER_CLASS (klass); + auto cdata = (GstNvEncoderClassData *) data; + auto dev_caps = &cdata->device_caps; + GParamFlags param_flags = (GParamFlags) (G_PARAM_READWRITE | + GST_PARAM_MUTABLE_PLAYING | G_PARAM_STATIC_STRINGS); + GParamFlags conditional_param_flags = (GParamFlags) (G_PARAM_READWRITE | + GST_PARAM_CONDITIONALLY_AVAILABLE | GST_PARAM_MUTABLE_PLAYING | + G_PARAM_STATIC_STRINGS); + + parent_class = (GTypeClass *) g_type_class_peek_parent (klass); + + object_class->finalize = gst_nv_av1_encoder_finalize; + object_class->set_property = gst_nv_av1_encoder_set_property; + object_class->get_property = gst_nv_av1_encoder_get_property; + + switch (cdata->device_mode) { + case GST_NV_ENCODER_DEVICE_CUDA: + g_object_class_install_property (object_class, PROP_CUDA_DEVICE_ID, + g_param_spec_uint ("cuda-device-id", "CUDA Device ID", + "CUDA device ID of associated GPU", + 0, G_MAXINT, 0, + (GParamFlags) (GST_PARAM_DOC_SHOW_DEFAULT | + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS))); + break; + case GST_NV_ENCODER_DEVICE_D3D11: + g_object_class_install_property (object_class, PROP_ADAPTER_LUID, + g_param_spec_int64 ("adapter-luid", "Adapter LUID", + "DXGI Adapter LUID (Locally Unique Identifier) of associated GPU", + G_MININT64, G_MAXINT64, 0, + (GParamFlags) (GST_PARAM_DOC_SHOW_DEFAULT | + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS))); + break; + case GST_NV_ENCODER_DEVICE_AUTO_SELECT: + if (cdata->cuda_device_id_size > 0) { + g_object_class_install_property (object_class, PROP_CUDA_DEVICE_ID, + g_param_spec_uint ("cuda-device-id", "CUDA Device ID", + "CUDA device ID to use", + 0, G_MAXINT, 0, + (GParamFlags) (conditional_param_flags | + GST_PARAM_DOC_SHOW_DEFAULT))); + } + if (cdata->adapter_luid_size > 0) { + g_object_class_install_property (object_class, PROP_ADAPTER_LUID, + g_param_spec_int64 ("adapter-luid", "Adapter LUID", + "DXGI Adapter LUID (Locally Unique Identifier) to use", + G_MININT64, G_MAXINT64, 0, + (GParamFlags) (conditional_param_flags | + GST_PARAM_DOC_SHOW_DEFAULT))); + } + break; + default: + g_assert_not_reached (); + break; + } + + g_object_class_install_property (object_class, PROP_PRESET, + g_param_spec_enum ("preset", "Encoding Preset", + "Encoding Preset", GST_TYPE_NV_ENCODER_PRESET, + DEFAULT_PRESET, param_flags)); + + g_object_class_install_property (object_class, PROP_TUNE, + g_param_spec_enum ("tune", "Tune", + "Encoding tune", GST_TYPE_NV_ENCODER_TUNE, + DEFAULT_TUNE, param_flags)); + + g_object_class_install_property (object_class, PROP_MULTI_PASS, + g_param_spec_enum ("multi-pass", "Multi Pass", + "Multi pass encoding", GST_TYPE_NV_ENCODER_MULTI_PASS, + DEFAULT_MULTI_PASS, param_flags)); + if (dev_caps->weighted_prediction) { + g_object_class_install_property (object_class, PROP_WEIGHTED_PRED, + g_param_spec_boolean ("weighted-pred", "Weighted Pred", + "Enables Weighted Prediction", DEFAULT_WEIGHTED_PRED, + conditional_param_flags)); + } + g_object_class_install_property (object_class, PROP_GOP_SIZE, + g_param_spec_int ("gop-size", "GOP size", + "Number of frames between intra frames (-1 = infinite)", + -1, G_MAXINT, DEFAULT_GOP_SIZE, param_flags)); + if (dev_caps->max_bframes > 0) { + g_object_class_install_property (object_class, PROP_B_FRAMES, + g_param_spec_uint ("bframes", "B Frames", + "Number of B-frames between I and P", 0, dev_caps->max_bframes, + DEFAULT_B_FRAMES, conditional_param_flags)); + } + g_object_class_install_property (object_class, PROP_RATE_CONTROL, + g_param_spec_enum ("rc-mode", "RC Mode", "Rate Control Mode", + GST_TYPE_NV_ENCODER_RC_MODE, DEFAULT_RATE_CONTROL, param_flags)); + g_object_class_install_property (object_class, PROP_QP_CONST_I, + g_param_spec_int ("qp-const-i", "QP I", + "Constant QP value for I frame (-1 = default)", -1, 255, + DEFAULT_QP, param_flags)); + g_object_class_install_property (object_class, PROP_QP_CONST_P, + g_param_spec_int ("qp-const-p", "QP P", + "Constant QP value for P frame (-1 = default)", -1, 255, + DEFAULT_QP, param_flags)); + g_object_class_install_property (object_class, PROP_QP_CONST_B, + g_param_spec_int ("qp-const-b", "QP B", + "Constant QP value for B frame (-1 = default)", -1, 255, + DEFAULT_QP, param_flags)); + g_object_class_install_property (object_class, PROP_BITRATE, + g_param_spec_uint ("bitrate", "Bitrate", + "Bitrate in kbit/sec (0 = automatic)", 0, 2000 * 1024, + DEFAULT_BITRATE, param_flags)); + g_object_class_install_property (object_class, PROP_MAX_BITRATE, + g_param_spec_uint ("max-bitrate", "Max Bitrate", + "Maximum Bitrate in kbit/sec (ignored in CBR mode)", 0, 2000 * 1024, + DEFAULT_MAX_BITRATE, param_flags)); + if (dev_caps->custom_vbv_buf_size) { + g_object_class_install_property (object_class, + PROP_VBV_BUFFER_SIZE, + g_param_spec_uint ("vbv-buffer-size", "VBV Buffer Size", + "VBV(HRD) Buffer Size in kbits (0 = NVENC default)", + 0, G_MAXUINT, DEFAULT_VBV_BUFFER_SIZE, conditional_param_flags)); + } + if (dev_caps->lookahead) { + g_object_class_install_property (object_class, PROP_RC_LOOKAHEAD, + g_param_spec_uint ("rc-lookahead", "Rate Control Lookahead", + "Number of frames for frame type lookahead", + 0, 32, DEFAULT_RC_LOOKAHEAD, conditional_param_flags)); + g_object_class_install_property (object_class, PROP_I_ADAPT, + g_param_spec_boolean ("i-adapt", "I Adapt", + "Enable adaptive I-frame insert when lookahead is enabled", + DEFAULT_I_ADAPT, conditional_param_flags)); + if (dev_caps->max_bframes > 0) { + g_object_class_install_property (object_class, PROP_B_ADAPT, + g_param_spec_boolean ("b-adapt", "B Adapt", + "Enable adaptive B-frame insert when lookahead is enabled", + DEFAULT_B_ADAPT, conditional_param_flags)); + } + } + g_object_class_install_property (object_class, PROP_SPATIAL_AQ, + g_param_spec_boolean ("spatial-aq", "Spatial AQ", + "Spatial Adaptive Quantization", DEFAULT_SPATIAL_AQ, param_flags)); + if (dev_caps->temporal_aq) { + g_object_class_install_property (object_class, PROP_TEMPORAL_AQ, + g_param_spec_boolean ("temporal-aq", "Temporal AQ", + "Temporal Adaptive Quantization", DEFAULT_TEMPORAL_AQ, + conditional_param_flags)); + } + g_object_class_install_property (object_class, PROP_ZEROLATENCY, + g_param_spec_boolean ("zerolatency", "Zerolatency", + "Zero latency operation (no reordering delay)", + DEFAULT_ZEROLATENCY, param_flags)); + g_object_class_install_property (object_class, PROP_NON_REF_P, + g_param_spec_boolean ("nonref-p", "Nonref P", + "Automatic insertion of non-reference P-frames", DEFAULT_NON_REF_P, + param_flags)); + g_object_class_install_property (object_class, PROP_STRICT_GOP, + g_param_spec_boolean ("strict-gop", "Strict GOP", + "Minimize GOP-to-GOP rate fluctuations", DEFAULT_STRICT_GOP, + param_flags)); + g_object_class_install_property (object_class, PROP_AQ_STRENGTH, + g_param_spec_uint ("aq-strength", "AQ Strength", + "Adaptive Quantization Strength when spatial-aq is enabled" + " from 1 (low) to 15 (aggressive), (0 = autoselect)", + 0, 15, DEFAULT_AQ_STRENGTH, param_flags)); + g_object_class_install_property (object_class, PROP_QP_MIN_I, + g_param_spec_int ("qp-min-i", "QP Min I", + "Minimum QP value for I frame, (-1 = automatic)", -1, 255, + DEFAULT_QP, param_flags)); + g_object_class_install_property (object_class, PROP_QP_MIN_P, + g_param_spec_int ("qp-min-p", "QP Min P", + "Minimum QP value for P frame, (-1 = automatic)", -1, 255, + DEFAULT_QP, param_flags)); + g_object_class_install_property (object_class, PROP_QP_MIN_B, + g_param_spec_int ("qp-min-b", "QP Min B", + "Minimum QP value for B frame, (-1 = automatic)", -1, 255, + DEFAULT_QP, param_flags)); + g_object_class_install_property (object_class, PROP_QP_MAX_I, + g_param_spec_int ("qp-max-i", "QP Max I", + "Maximum QP value for I frame, (-1 = automatic)", -1, 255, + DEFAULT_QP, param_flags)); + g_object_class_install_property (object_class, PROP_QP_MAX_P, + g_param_spec_int ("qp-max-p", "QP Max P", + "Maximum QP value for P frame, (-1 = automatic)", -1, 255, + DEFAULT_QP, param_flags)); + g_object_class_install_property (object_class, PROP_QP_MAX_B, + g_param_spec_int ("qp-max-b", "Max QP B", + "Maximum QP value for B frame, (-1 = automatic)", -1, 255, + DEFAULT_QP, param_flags)); + g_object_class_install_property (object_class, PROP_CONST_QUALITY, + g_param_spec_double ("const-quality", "Constant Quality", + "Target Constant Quality level for VBR mode (0 = automatic)", + 0, 51, DEFAULT_CONST_QUALITY, param_flags)); + + switch (cdata->device_mode) { + case GST_NV_ENCODER_DEVICE_CUDA: + gst_element_class_set_static_metadata (element_class, + "NVENC AV1 Video Encoder CUDA Mode", + "Codec/Encoder/Video/Hardware", + "Encode AV1 video streams using NVCODEC API CUDA Mode", + "Seungha Yang "); + break; + case GST_NV_ENCODER_DEVICE_D3D11: + gst_element_class_set_static_metadata (element_class, + "NVENC AV1 Video Encoder Direct3D11 Mode", + "Codec/Encoder/Video/Hardware", + "Encode AV1 video streams using NVCODEC API Direct3D11 Mode", + "Seungha Yang "); + break; + case GST_NV_ENCODER_DEVICE_AUTO_SELECT: + gst_element_class_set_static_metadata (element_class, + "NVENC AV1 Video Encoder Auto GPU select Mode", + "Codec/Encoder/Video/Hardware", + "Encode AV1 video streams using NVCODEC API auto GPU select Mode", + "Seungha Yang "); + break; + default: + g_assert_not_reached (); + break; + } + + gst_element_class_add_pad_template (element_class, + gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, + cdata->sink_caps)); + gst_element_class_add_pad_template (element_class, + gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, + cdata->src_caps)); + + nvenc_class->set_format = GST_DEBUG_FUNCPTR (gst_nv_av1_encoder_set_format); + nvenc_class->set_output_state = + GST_DEBUG_FUNCPTR (gst_nv_av1_encoder_set_output_state); + nvenc_class->check_reconfigure = + GST_DEBUG_FUNCPTR (gst_nv_av1_encoder_check_reconfigure); + nvenc_class->select_device = + GST_DEBUG_FUNCPTR (gst_nv_av1_encoder_select_device); + nvenc_class->calculate_min_buffers = + GST_DEBUG_FUNCPTR (gst_nv_av1_encoder_calculate_min_buffers); + + klass->device_caps = cdata->device_caps; + klass->cuda_device_id = cdata->cuda_device_id; + klass->adapter_luid = cdata->adapter_luid; + klass->device_mode = cdata->device_mode; + klass->cuda_device_id_size = cdata->cuda_device_id_size; + klass->adapter_luid_size = cdata->adapter_luid_size; + memcpy (klass->cuda_device_id_list, cdata->cuda_device_id_list, + sizeof (klass->cuda_device_id_list)); + memcpy (klass->adapter_luid_list, cdata->adapter_luid_list, + sizeof (klass->adapter_luid_list)); + + gst_nv_encoder_class_data_unref (cdata); +} + +static void +gst_nv_av1_encoder_init (GstNvAv1Encoder * self) +{ + auto klass = GST_NV_AV1_ENCODER_GET_CLASS (self); + + g_mutex_init (&self->prop_lock); + + self->selected_device_mode = klass->device_mode; + self->cuda_device_id = klass->cuda_device_id; + self->adapter_luid = klass->adapter_luid; + self->preset = DEFAULT_PRESET; + self->tune = DEFAULT_TUNE; + self->multipass = DEFAULT_MULTI_PASS; + self->weighted_pred = DEFAULT_WEIGHTED_PRED; + self->gop_size = DEFAULT_GOP_SIZE; + self->bframes = DEFAULT_B_FRAMES; + self->rc_mode = DEFAULT_RATE_CONTROL; + self->qp_const_i = DEFAULT_QP; + self->qp_const_p = DEFAULT_QP; + self->qp_const_b = DEFAULT_QP; + self->bitrate = DEFAULT_BITRATE; + self->max_bitrate = DEFAULT_MAX_BITRATE; + self->vbv_buffer_size = DEFAULT_VBV_BUFFER_SIZE; + self->rc_lookahead = DEFAULT_RC_LOOKAHEAD; + self->i_adapt = DEFAULT_I_ADAPT; + self->b_adapt = DEFAULT_B_ADAPT; + self->spatial_aq = DEFAULT_SPATIAL_AQ; + self->temporal_aq = DEFAULT_TEMPORAL_AQ; + self->zero_reorder_delay = DEFAULT_ZEROLATENCY; + self->non_ref_p = DEFAULT_NON_REF_P; + self->strict_gop = DEFAULT_STRICT_GOP; + self->aq_strength = DEFAULT_AQ_STRENGTH; + self->qp_min_i = DEFAULT_QP; + self->qp_min_p = DEFAULT_QP; + self->qp_min_b = DEFAULT_QP; + self->qp_max_i = DEFAULT_QP; + self->qp_max_p = DEFAULT_QP; + self->qp_max_b = DEFAULT_QP; + self->const_quality = DEFAULT_CONST_QUALITY; + + gst_nv_encoder_set_device_mode (GST_NV_ENCODER (self), klass->device_mode, + klass->cuda_device_id, klass->adapter_luid); +} + +static void +gst_nv_av1_encoder_finalize (GObject * object) +{ + auto self = GST_NV_AV1_ENCODER (object); + + g_mutex_clear (&self->prop_lock); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +typedef enum +{ + UPDATE_INIT_PARAM, + UPDATE_RC_PARAM, + UPDATE_BITRATE, +} PropUpdateLevel; + +static void +update_boolean (GstNvAv1Encoder * self, gboolean * old_val, + const GValue * new_val, PropUpdateLevel level) +{ + gboolean val = g_value_get_boolean (new_val); + + if (*old_val == val) + return; + + *old_val = val; + switch (level) { + case UPDATE_INIT_PARAM: + self->init_param_updated = TRUE; + break; + case UPDATE_RC_PARAM: + self->rc_param_updated = TRUE; + break; + case UPDATE_BITRATE: + self->bitrate_updated = TRUE; + break; + } +} + +static void +update_int (GstNvAv1Encoder * self, gint * old_val, + const GValue * new_val, PropUpdateLevel level) +{ + gint val = g_value_get_int (new_val); + + if (*old_val == val) + return; + + *old_val = val; + switch (level) { + case UPDATE_INIT_PARAM: + self->init_param_updated = TRUE; + break; + case UPDATE_RC_PARAM: + self->rc_param_updated = TRUE; + break; + case UPDATE_BITRATE: + self->bitrate_updated = TRUE; + break; + } +} + +static void +update_uint (GstNvAv1Encoder * self, guint * old_val, + const GValue * new_val, PropUpdateLevel level) +{ + guint val = g_value_get_uint (new_val); + + if (*old_val == val) + return; + + *old_val = val; + switch (level) { + case UPDATE_INIT_PARAM: + self->init_param_updated = TRUE; + break; + case UPDATE_RC_PARAM: + self->rc_param_updated = TRUE; + break; + case UPDATE_BITRATE: + self->bitrate_updated = TRUE; + break; + } +} + +static void +update_double (GstNvAv1Encoder * self, gdouble * old_val, + const GValue * new_val, PropUpdateLevel level) +{ + gdouble val = g_value_get_double (new_val); + + if (*old_val == val) + return; + + *old_val = val; + switch (level) { + case UPDATE_INIT_PARAM: + self->init_param_updated = TRUE; + break; + case UPDATE_RC_PARAM: + self->rc_param_updated = TRUE; + break; + case UPDATE_BITRATE: + self->bitrate_updated = TRUE; + break; + } +} + +static void +gst_nv_av1_encoder_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + auto self = GST_NV_AV1_ENCODER (object); + auto klass = GST_NV_AV1_ENCODER_GET_CLASS (self); + + g_mutex_lock (&self->prop_lock); + switch (prop_id) { + case PROP_ADAPTER_LUID:{ + gint64 adapter_luid = g_value_get_int64 (value); + gboolean is_valid = FALSE; + + for (guint i = 0; i < klass->adapter_luid_size; i++) { + if (klass->adapter_luid_list[i] == adapter_luid) { + self->adapter_luid = adapter_luid; + is_valid = TRUE; + break; + } + } + + if (!is_valid) + g_warning ("%" G_GINT64_FORMAT " is not a valid adapter luid", + adapter_luid); + break; + } + case PROP_CUDA_DEVICE_ID:{ + guint cuda_device_id = g_value_get_uint (value); + gboolean is_valid = FALSE; + + for (guint i = 0; i < klass->cuda_device_id_size; i++) { + if (klass->cuda_device_id_list[i] == cuda_device_id) { + self->cuda_device_id = cuda_device_id; + is_valid = TRUE; + break; + } + } + + if (!is_valid) + g_warning ("%d is not a valid cuda device id", cuda_device_id); + break; + } + case PROP_PRESET:{ + GstNvEncoderPreset preset = (GstNvEncoderPreset) g_value_get_enum (value); + if (preset != self->preset) { + self->preset = preset; + self->init_param_updated = TRUE; + } + break; + } + case PROP_TUNE:{ + GstNvEncoderTune tune = (GstNvEncoderTune) g_value_get_enum (value); + if (tune != self->tune) { + self->tune = tune; + self->init_param_updated = TRUE; + } + break; + } + case PROP_MULTI_PASS:{ + GstNvEncoderMultiPass multipass = + (GstNvEncoderMultiPass) g_value_get_enum (value); + if (multipass != self->multipass) { + self->multipass = multipass; + self->init_param_updated = TRUE; + } + break; + } + case PROP_WEIGHTED_PRED: + update_boolean (self, &self->weighted_pred, value, UPDATE_INIT_PARAM); + break; + case PROP_GOP_SIZE: + update_int (self, &self->gop_size, value, UPDATE_INIT_PARAM); + break; + case PROP_B_FRAMES: + update_uint (self, &self->bframes, value, UPDATE_INIT_PARAM); + break; + case PROP_RATE_CONTROL:{ + GstNvEncoderRCMode mode = (GstNvEncoderRCMode) g_value_get_enum (value); + if (mode != self->rc_mode) { + self->rc_mode = mode; + self->rc_param_updated = TRUE; + } + break; + } + case PROP_QP_CONST_I: + update_int (self, &self->qp_const_i, value, UPDATE_RC_PARAM); + break; + case PROP_QP_CONST_P: + update_int (self, &self->qp_const_p, value, UPDATE_RC_PARAM); + break; + case PROP_QP_CONST_B: + update_int (self, &self->qp_const_b, value, UPDATE_RC_PARAM); + break; + case PROP_BITRATE: + update_uint (self, &self->bitrate, value, UPDATE_BITRATE); + break; + case PROP_MAX_BITRATE: + update_uint (self, &self->max_bitrate, value, UPDATE_BITRATE); + break; + case PROP_VBV_BUFFER_SIZE: + update_uint (self, &self->vbv_buffer_size, value, UPDATE_RC_PARAM); + break; + case PROP_RC_LOOKAHEAD: + /* rc-lookahead update requires pool size change */ + update_uint (self, &self->rc_lookahead, value, UPDATE_INIT_PARAM); + break; + case PROP_I_ADAPT: + update_boolean (self, &self->i_adapt, value, UPDATE_RC_PARAM); + break; + case PROP_B_ADAPT: + update_boolean (self, &self->b_adapt, value, UPDATE_RC_PARAM); + break; + case PROP_SPATIAL_AQ: + update_boolean (self, &self->spatial_aq, value, UPDATE_RC_PARAM); + break; + case PROP_TEMPORAL_AQ: + update_boolean (self, &self->temporal_aq, value, UPDATE_RC_PARAM); + break; + case PROP_ZEROLATENCY: + update_boolean (self, &self->zero_reorder_delay, value, UPDATE_RC_PARAM); + break; + case PROP_NON_REF_P: + update_boolean (self, &self->non_ref_p, value, UPDATE_RC_PARAM); + break; + case PROP_STRICT_GOP: + update_boolean (self, &self->strict_gop, value, UPDATE_RC_PARAM); + break; + case PROP_AQ_STRENGTH: + update_uint (self, &self->aq_strength, value, UPDATE_RC_PARAM); + break; + case PROP_QP_MIN_I: + update_int (self, &self->qp_min_i, value, UPDATE_RC_PARAM); + break; + case PROP_QP_MIN_P: + update_int (self, &self->qp_min_p, value, UPDATE_RC_PARAM); + break; + case PROP_QP_MIN_B: + update_int (self, &self->qp_min_b, value, UPDATE_RC_PARAM); + break; + case PROP_QP_MAX_I: + update_int (self, &self->qp_max_i, value, UPDATE_RC_PARAM); + break; + case PROP_QP_MAX_P: + update_int (self, &self->qp_max_p, value, UPDATE_RC_PARAM); + break; + case PROP_QP_MAX_B: + update_int (self, &self->qp_max_b, value, UPDATE_RC_PARAM); + break; + case PROP_CONST_QUALITY: + update_double (self, &self->const_quality, value, UPDATE_RC_PARAM); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } + + g_mutex_unlock (&self->prop_lock); +} + +static void +gst_nv_av1_encoder_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + auto self = GST_NV_AV1_ENCODER (object); + + switch (prop_id) { + case PROP_ADAPTER_LUID: + g_value_set_int64 (value, self->adapter_luid); + break; + case PROP_CUDA_DEVICE_ID: + g_value_set_uint (value, self->cuda_device_id); + break; + case PROP_PRESET: + g_value_set_enum (value, self->preset); + break; + case PROP_TUNE: + g_value_set_enum (value, self->tune); + break; + case PROP_MULTI_PASS: + g_value_set_enum (value, self->multipass); + break; + case PROP_WEIGHTED_PRED: + g_value_set_boolean (value, self->weighted_pred); + break; + case PROP_GOP_SIZE: + g_value_set_int (value, self->gop_size); + break; + case PROP_B_FRAMES: + g_value_set_uint (value, self->bframes); + break; + case PROP_RATE_CONTROL: + g_value_set_enum (value, self->rc_mode); + break; + case PROP_QP_CONST_I: + g_value_set_int (value, self->qp_const_i); + break; + case PROP_QP_CONST_P: + g_value_set_int (value, self->qp_const_p); + break; + case PROP_QP_CONST_B: + g_value_set_int (value, self->qp_const_b); + break; + case PROP_BITRATE: + g_value_set_uint (value, self->bitrate); + break; + case PROP_MAX_BITRATE: + g_value_set_uint (value, self->max_bitrate); + break; + case PROP_VBV_BUFFER_SIZE: + g_value_set_uint (value, self->vbv_buffer_size); + break; + case PROP_RC_LOOKAHEAD: + g_value_set_uint (value, self->rc_lookahead); + break; + case PROP_I_ADAPT: + g_value_set_boolean (value, self->i_adapt); + break; + case PROP_B_ADAPT: + g_value_set_boolean (value, self->b_adapt); + break; + case PROP_SPATIAL_AQ: + g_value_set_boolean (value, self->spatial_aq); + break; + case PROP_TEMPORAL_AQ: + g_value_set_boolean (value, self->temporal_aq); + break; + case PROP_ZEROLATENCY: + g_value_set_boolean (value, self->zero_reorder_delay); + break; + case PROP_NON_REF_P: + g_value_set_boolean (value, self->non_ref_p); + break; + case PROP_STRICT_GOP: + g_value_set_boolean (value, self->strict_gop); + break; + case PROP_AQ_STRENGTH: + g_value_set_uint (value, self->aq_strength); + break; + case PROP_QP_MIN_I: + g_value_set_int (value, self->qp_min_i); + break; + case PROP_QP_MIN_P: + g_value_set_int (value, self->qp_min_p); + break; + case PROP_QP_MIN_B: + g_value_set_int (value, self->qp_min_b); + break; + case PROP_QP_MAX_I: + g_value_set_int (value, self->qp_max_i); + break; + case PROP_QP_MAX_P: + g_value_set_int (value, self->qp_max_p); + break; + case PROP_QP_MAX_B: + g_value_set_int (value, self->qp_max_b); + break; + case PROP_CONST_QUALITY: + g_value_set_double (value, self->const_quality); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +gst_nv_av1_encoder_set_format (GstNvEncoder * encoder, + GstVideoCodecState * state, gpointer session, + NV_ENC_INITIALIZE_PARAMS * init_params, NV_ENC_CONFIG * config) +{ + auto self = GST_NV_AV1_ENCODER (encoder); + auto klass = GST_NV_AV1_ENCODER_GET_CLASS (self); + auto dev_caps = &klass->device_caps; + NV_ENC_RC_PARAMS *rc_params; + GstVideoInfo *info = &state->info; + NVENCSTATUS status; + NV_ENC_PRESET_CONFIG preset_config = { 0, }; + gboolean bframe_aborted = FALSE; + gboolean weight_pred_aborted = FALSE; + gboolean vbv_buffer_size_aborted = FALSE; + gboolean lookahead_aborted = FALSE; + gboolean temporal_aq_aborted = FALSE; + guint bitdepth_minus8 = GST_VIDEO_INFO_COMP_DEPTH (info, 0) - 8; + + g_mutex_lock (&self->prop_lock); + + if (klass->device_mode == GST_NV_ENCODER_DEVICE_AUTO_SELECT) { + GstNvEncoderDeviceCaps dcaps; + + gst_nv_encoder_get_encoder_caps (session, &NV_ENC_CODEC_AV1_GUID, &dcaps); + if (self->bframes > 0 && !dcaps.max_bframes) { + self->bframes = 0; + bframe_aborted = TRUE; + + GST_INFO_OBJECT (self, "B-frame was enabled but not support by device"); + } + + if (self->weighted_pred && !dcaps.weighted_prediction) { + self->weighted_pred = FALSE; + weight_pred_aborted = TRUE; + + GST_INFO_OBJECT (self, + "Weighted prediction was enabled but not support by device"); + } + + if (self->vbv_buffer_size && !dcaps.custom_vbv_buf_size) { + self->vbv_buffer_size = 0; + vbv_buffer_size_aborted = TRUE; + + GST_INFO_OBJECT (self, + "VBV buffer size was specified but not supported by device"); + } + + if (self->rc_lookahead && !dcaps.lookahead) { + self->rc_lookahead = 0; + lookahead_aborted = TRUE; + + GST_INFO_OBJECT (self, + "VBV buffer size was specified but not supported by device"); + } + + if (self->temporal_aq && !dcaps.temporal_aq) { + self->temporal_aq = FALSE; + temporal_aq_aborted = TRUE; + + GST_INFO_OBJECT (self, + "temporal-aq was enabled but not supported by device"); + } + } + + init_params->version = gst_nvenc_get_initialize_params_version (); + init_params->encodeGUID = NV_ENC_CODEC_AV1_GUID; + + init_params->encodeWidth = GST_VIDEO_INFO_WIDTH (info); + init_params->maxEncodeWidth = GST_VIDEO_INFO_WIDTH (info); + init_params->encodeHeight = GST_VIDEO_INFO_HEIGHT (info); + init_params->maxEncodeHeight = GST_VIDEO_INFO_HEIGHT (info); + init_params->enablePTD = TRUE; + if (dev_caps->async_encoding_support) + init_params->enableEncodeAsync = 1; + if (info->fps_d > 0 && info->fps_n > 0) { + init_params->frameRateNum = info->fps_n; + init_params->frameRateDen = info->fps_d; + } else { + init_params->frameRateNum = 0; + init_params->frameRateDen = 1; + } + + init_params->enableWeightedPrediction = self->weighted_pred; + + init_params->darWidth = GST_VIDEO_INFO_WIDTH (info); + init_params->darHeight = GST_VIDEO_INFO_HEIGHT (info); + + GstNvEncoderPresetOptions in_opt = { }; + GstNvEncoderPresetOptionsNative out_opt = { }; + in_opt.preset = self->preset; + in_opt.tune = self->tune; + in_opt.rc_mode = self->rc_mode; + in_opt.multi_pass = self->multipass; + GstNvEncoderPresetResolution resolution = GST_NV_ENCODER_PRESET_720; + auto frame_size = info->width * info->height; + if (frame_size >= 3840 * 2160) + resolution = GST_NV_ENCODER_PRESET_2160; + else if (frame_size >= 1920 * 1080) + resolution = GST_NV_ENCODER_PRESET_1080; + + gst_nv_encoder_preset_to_native (resolution, &in_opt, &out_opt); + init_params->presetGUID = out_opt.preset; + init_params->tuningInfo = out_opt.tune; + + preset_config.version = gst_nvenc_get_preset_config_version (); + preset_config.presetCfg.version = gst_nvenc_get_config_version (); + + status = NvEncGetEncodePresetConfigEx (session, NV_ENC_CODEC_AV1_GUID, + init_params->presetGUID, init_params->tuningInfo, &preset_config); + if (!gst_nv_enc_result (status, self)) { + GST_ERROR_OBJECT (self, "Failed to get preset config"); + g_mutex_unlock (&self->prop_lock); + return FALSE; + } + + *config = preset_config.presetCfg; + if (self->gop_size < 0) { + config->gopLength = NVENC_INFINITE_GOPLENGTH; + config->frameIntervalP = 1; + } else if (self->gop_size > 0) { + config->gopLength = self->gop_size; + /* frameIntervalP + * 0: All Intra frames + * 1: I/P only + * 2: IBP + * 3: IBBP + */ + config->frameIntervalP = self->bframes + 1; + } else { + /* gop size == 0 means all intra frames */ + config->gopLength = 1; + config->frameIntervalP = 0; + } + + rc_params = &config->rcParams; + + rc_params->rateControlMode = out_opt.rc_mode; + rc_params->multiPass = out_opt.multi_pass; + + if (self->bitrate) + rc_params->averageBitRate = self->bitrate * 1024; + if (self->max_bitrate) + rc_params->maxBitRate = self->max_bitrate * 1024; + if (self->vbv_buffer_size) + rc_params->vbvBufferSize = self->vbv_buffer_size * 1024; + + if (self->qp_min_i >= 0) { + rc_params->enableMinQP = TRUE; + rc_params->minQP.qpIntra = self->qp_min_i; + if (self->qp_min_p >= 0) { + rc_params->minQP.qpInterP = self->qp_min_p; + } else { + rc_params->minQP.qpInterP = rc_params->minQP.qpIntra; + } + if (self->qp_min_b >= 0) { + rc_params->minQP.qpInterB = self->qp_min_b; + } else { + rc_params->minQP.qpInterB = rc_params->minQP.qpInterP; + } + } + + if (self->qp_max_i >= 0) { + rc_params->enableMaxQP = TRUE; + rc_params->maxQP.qpIntra = self->qp_max_i; + if (self->qp_max_p >= 0) { + rc_params->maxQP.qpInterP = self->qp_max_p; + } else { + rc_params->maxQP.qpInterP = rc_params->maxQP.qpIntra; + } + if (self->qp_max_b >= 0) { + rc_params->maxQP.qpInterB = self->qp_max_b; + } else { + rc_params->maxQP.qpInterB = rc_params->maxQP.qpInterP; + } + } + + if (rc_params->rateControlMode == NV_ENC_PARAMS_RC_CONSTQP) { + if (self->qp_const_i >= 0) + rc_params->constQP.qpIntra = self->qp_const_i; + if (self->qp_const_p >= 0) + rc_params->constQP.qpInterP = self->qp_const_p; + if (self->qp_const_b >= 0) + rc_params->constQP.qpInterB = self->qp_const_b; + } + + if (self->spatial_aq) { + rc_params->enableAQ = TRUE; + rc_params->aqStrength = self->aq_strength; + } + + rc_params->enableTemporalAQ = self->temporal_aq; + + if (self->rc_lookahead) { + rc_params->enableLookahead = 1; + rc_params->lookaheadDepth = self->rc_lookahead; + rc_params->disableIadapt = !self->i_adapt; + rc_params->disableBadapt = !self->b_adapt; + } + + rc_params->strictGOPTarget = self->strict_gop; + rc_params->enableNonRefP = self->non_ref_p; + rc_params->zeroReorderDelay = self->zero_reorder_delay; + + if (self->const_quality) { + guint scaled = (gint) (self->const_quality * 256.0); + + rc_params->targetQuality = (guint8) (scaled >> 8); + rc_params->targetQualityLSB = (guint8) (scaled & 0xff); + } + self->init_param_updated = FALSE; + self->bitrate_updated = FALSE; + self->rc_param_updated = FALSE; + + config->version = gst_nvenc_get_config_version (); + config->profileGUID = NV_ENC_AV1_PROFILE_MAIN_GUID; + + NV_ENC_CONFIG_AV1 *av1_config = &config->encodeCodecConfig.av1Config; + av1_config->level = NV_ENC_LEVEL_AV1_AUTOSELECT; + av1_config->tier = NV_ENC_TIER_AV1_0; + /* TODO: property */ + av1_config->minPartSize = NV_ENC_AV1_PART_SIZE_AUTOSELECT; + av1_config->maxPartSize = NV_ENC_AV1_PART_SIZE_AUTOSELECT; + av1_config->outputAnnexBFormat = FALSE; + av1_config->enableTimingInfo = FALSE; + av1_config->enableDecoderModelInfo = FALSE; + /* TODO: Maybe useful for debugging, but not required for decoding */ + av1_config->enableFrameIdNumbers = FALSE; + av1_config->disableSeqHdr = FALSE; + av1_config->repeatSeqHdr = TRUE; + /* TODO: property */ + av1_config->enableIntraRefresh = FALSE; + /* TODO: main profile only for now */ + av1_config->chromaFormatIDC = 1; + av1_config->enableBitstreamPadding = FALSE; + /* TODO: property and configure tile info accordingly */ + av1_config->enableCustomTileConfig = FALSE; + /* TODO: property, support user specified film grain params */ + av1_config->enableFilmGrainParams = FALSE; + av1_config->inputPixelBitDepthMinus8 = bitdepth_minus8; + av1_config->pixelBitDepthMinus8 = bitdepth_minus8; + av1_config->idrPeriod = config->gopLength; + + /* TODO: support intra refresh */ + av1_config->intraRefreshPeriod = 0; + av1_config->intraRefreshCnt = 0; + + av1_config->maxNumRefFramesInDPB = 0; + av1_config->numFwdRefs = NV_ENC_NUM_REF_FRAMES_AUTOSELECT; + av1_config->numBwdRefs = NV_ENC_NUM_REF_FRAMES_AUTOSELECT; + + GstVideoColorimetry cinfo; + if (GST_VIDEO_INFO_IS_YUV (info)) { + cinfo = info->colorimetry; + } else { + /* Other formats will be converted 4:2:0 YUV by runtime */ + gst_video_colorimetry_from_string (&cinfo, GST_VIDEO_COLORIMETRY_BT709); + } + + av1_config->colorPrimaries = (NV_ENC_VUI_COLOR_PRIMARIES) + gst_video_color_primaries_to_iso (cinfo.primaries); + av1_config->transferCharacteristics = (NV_ENC_VUI_TRANSFER_CHARACTERISTIC) + gst_video_transfer_function_to_iso (cinfo.transfer); + av1_config->matrixCoefficients = (NV_ENC_VUI_MATRIX_COEFFS) + gst_video_color_matrix_to_iso (cinfo.matrix); + if (cinfo.range == GST_VIDEO_COLOR_RANGE_0_255) { + av1_config->colorRange = 1; + } else { + av1_config->colorRange = 0; + } + + g_mutex_unlock (&self->prop_lock); + + if (bframe_aborted) + g_object_notify (G_OBJECT (self), "b-frames"); + if (weight_pred_aborted) + g_object_notify (G_OBJECT (self), "weighted-pred"); + if (vbv_buffer_size_aborted) + g_object_notify (G_OBJECT (self), "vbv-buffer-size"); + if (lookahead_aborted) + g_object_notify (G_OBJECT (self), "rc-lookahead"); + if (temporal_aq_aborted) + g_object_notify (G_OBJECT (self), "temporal-aq"); + + return TRUE; +} + +static gboolean +gst_nv_av1_encoder_set_output_state (GstNvEncoder * encoder, + GstVideoCodecState * state, gpointer session) +{ + auto self = GST_NV_AV1_ENCODER (encoder); + auto venc = GST_VIDEO_ENCODER (encoder); + + auto caps = gst_caps_new_simple ("video/x-av1", "stream-format", + G_TYPE_STRING, "obu-stream", "alignment", G_TYPE_STRING, "tu", + "profile", G_TYPE_STRING, "main", nullptr); + + auto output_state = gst_video_encoder_set_output_state (venc, caps, state); + if (GST_VIDEO_INFO_IS_RGB (&state->info)) { + /* Format converted by runtime */ + gst_video_colorimetry_from_string (&output_state->info.colorimetry, + GST_VIDEO_COLORIMETRY_BT709); + output_state->info.chroma_site = GST_VIDEO_CHROMA_SITE_H_COSITED; + } + + GST_INFO_OBJECT (self, "Output caps: %" GST_PTR_FORMAT, output_state->caps); + gst_video_codec_state_unref (output_state); + + auto tags = gst_tag_list_new_empty (); + gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_ENCODER, + "nvav1enc", nullptr); + + gst_video_encoder_merge_tags (GST_VIDEO_ENCODER (encoder), + tags, GST_TAG_MERGE_REPLACE); + gst_tag_list_unref (tags); + + return TRUE; +} + +static GstNvEncoderReconfigure +gst_nv_av1_encoder_check_reconfigure (GstNvEncoder * encoder, + NV_ENC_CONFIG * config) +{ + auto self = GST_NV_AV1_ENCODER (encoder); + GstNvEncoderReconfigure reconfig = GST_NV_ENCODER_RECONFIGURE_NONE; + + /* Dynamic RC param update is not tested, do soft-reconfigure only for + * bitrate update */ + g_mutex_lock (&self->prop_lock); + if (self->init_param_updated || self->rc_param_updated) { + reconfig = GST_NV_ENCODER_RECONFIGURE_FULL; + goto done; + } + + if (self->bitrate_updated) { + auto klass = GST_NV_AV1_ENCODER_GET_CLASS (self); + if (klass->device_caps.dyn_bitrate_change > 0) { + config->rcParams.averageBitRate = self->bitrate * 1024; + config->rcParams.maxBitRate = self->max_bitrate * 1024; + reconfig = GST_NV_ENCODER_RECONFIGURE_BITRATE; + } else { + reconfig = GST_NV_ENCODER_RECONFIGURE_FULL; + } + } + +done: + self->init_param_updated = FALSE; + self->rc_param_updated = FALSE; + self->bitrate_updated = FALSE; + g_mutex_unlock (&self->prop_lock); + + return reconfig; +} + +static gboolean +gst_nv_av1_encoder_select_device (GstNvEncoder * encoder, + const GstVideoInfo * info, GstBuffer * buffer, + GstNvEncoderDeviceData * data) +{ + auto self = GST_NV_AV1_ENCODER (encoder); + auto klass = GST_NV_AV1_ENCODER_GET_CLASS (self); + GstMemory *mem; + + memset (data, 0, sizeof (GstNvEncoderDeviceData)); + + g_assert (klass->device_mode == GST_NV_ENCODER_DEVICE_AUTO_SELECT); + + mem = gst_buffer_peek_memory (buffer, 0); + if (klass->cuda_device_id_size > 0 && gst_is_cuda_memory (mem)) { + GstCudaMemory *cmem = GST_CUDA_MEMORY_CAST (mem); + GstCudaContext *context = cmem->context; + guint device_id; + gboolean found = FALSE; + + g_object_get (context, "cuda-device-id", &device_id, nullptr); + + data->device_mode = GST_NV_ENCODER_DEVICE_CUDA; + self->selected_device_mode = GST_NV_ENCODER_DEVICE_CUDA; + + for (guint i = 0; i < klass->cuda_device_id_size; i++) { + if (klass->cuda_device_id_list[i] == device_id) { + data->cuda_device_id = device_id; + found = TRUE; + break; + } + } + + if (!found) { + GST_INFO_OBJECT (self, + "Upstream CUDA device is not in supported device list"); + data->cuda_device_id = self->cuda_device_id; + } else { + data->device = (GstObject *) gst_object_ref (context); + } + + if (data->cuda_device_id != self->cuda_device_id) { + self->cuda_device_id = data->cuda_device_id; + g_object_notify (G_OBJECT (self), "cuda-device-id"); + } + + return TRUE; + } +#ifdef G_OS_WIN32 + if (klass->adapter_luid_size > 0 && gst_is_d3d11_memory (mem)) { + GstD3D11Memory *dmem = GST_D3D11_MEMORY_CAST (mem); + GstD3D11Device *device = dmem->device; + gint64 adapter_luid; + gboolean found = FALSE; + + g_object_get (device, "adapter-luid", &adapter_luid, nullptr); + + data->device_mode = GST_NV_ENCODER_DEVICE_D3D11; + self->selected_device_mode = GST_NV_ENCODER_DEVICE_D3D11; + + for (guint i = 0; i < klass->cuda_device_id_size; i++) { + if (klass->adapter_luid_list[i] == adapter_luid) { + data->adapter_luid = adapter_luid; + found = TRUE; + break; + } + } + + if (!found) { + GST_INFO_OBJECT (self, + "Upstream D3D11 device is not in supported device list"); + data->adapter_luid = self->adapter_luid; + } else { + data->device = (GstObject *) gst_object_ref (device); + } + + if (data->adapter_luid != self->adapter_luid) { + self->adapter_luid = data->adapter_luid; + g_object_notify (G_OBJECT (self), "adapter-luid"); + } + + return TRUE; + } +#endif + + if (klass->cuda_device_id_size > 0 && + (self->selected_device_mode != GST_NV_ENCODER_DEVICE_D3D11)) { + GST_INFO_OBJECT (self, "Upstream is system memory, use CUDA mode"); + data->device_mode = GST_NV_ENCODER_DEVICE_CUDA; + data->cuda_device_id = self->cuda_device_id; + } else { + GST_INFO_OBJECT (self, "Upstream is system memory, use CUDA mode"); + data->device_mode = GST_NV_ENCODER_DEVICE_D3D11; + data->adapter_luid = self->adapter_luid; + } + + self->selected_device_mode = data->device_mode; + + return TRUE; +} + +static guint +gst_nv_av1_encoder_calculate_min_buffers (GstNvEncoder * encoder) +{ + auto self = GST_NV_AV1_ENCODER (encoder); + guint num_buffers; + + /* At least 4 surfaces are required as documented by Nvidia Encoder guide */ + num_buffers = 4; + + /* lookahead depth */ + num_buffers += self->rc_lookahead; + + /* B frames + 1 */ + num_buffers += self->bframes + 1; + + return num_buffers; +} + +static GstNvEncoderClassData * +gst_nv_av1_encoder_create_class_data (GstObject * device, gpointer session, + GstNvEncoderDeviceMode device_mode) +{ + NVENCSTATUS status; + GstNvEncoderDeviceCaps dev_caps = { 0, }; + GUID profile_guids[16]; + NV_ENC_BUFFER_FORMAT input_formats[16]; + guint32 profile_guid_count = 0; + guint32 input_format_count = 0; + std::string sink_caps_str; + std::string src_caps_str; + std::string format_str; + std::set < std::string > formats; + std::string profile_str; + std::string resolution_str; + GstNvEncoderClassData *cdata; + GstCaps *sink_caps; + GstCaps *system_caps; + NV_ENC_PRESET_CONFIG preset_config = { 0, }; + + preset_config.version = gst_nvenc_get_preset_config_version (); + preset_config.presetCfg.version = gst_nvenc_get_config_version (); + + status = NvEncGetEncodePresetConfigEx (session, NV_ENC_CODEC_AV1_GUID, + NV_ENC_PRESET_P4_GUID, NV_ENC_TUNING_INFO_HIGH_QUALITY, &preset_config); + if (status != NV_ENC_SUCCESS) { + GST_WARNING_OBJECT (device, "New preset is not supported"); + return nullptr; + } + + status = NvEncGetEncodeProfileGUIDs (session, NV_ENC_CODEC_AV1_GUID, + profile_guids, G_N_ELEMENTS (profile_guids), &profile_guid_count); + if (status != NV_ENC_SUCCESS || profile_guid_count == 0) { + GST_WARNING_OBJECT (device, "Unable to get supported profiles"); + return nullptr; + } + + status = NvEncGetInputFormats (session, NV_ENC_CODEC_AV1_GUID, input_formats, + G_N_ELEMENTS (input_formats), &input_format_count); + if (status != NV_ENC_SUCCESS || input_format_count == 0) { + GST_WARNING_OBJECT (device, "Unable to get supported input formats"); + return nullptr; + } + + gst_nv_encoder_get_encoder_caps (session, &NV_ENC_CODEC_AV1_GUID, &dev_caps); + + for (guint32 i = 0; i < input_format_count; i++) { + switch (input_formats[i]) { + case NV_ENC_BUFFER_FORMAT_NV12: + formats.insert ("NV12"); + break; + case NV_ENC_BUFFER_FORMAT_YUV420_10BIT: + if (dev_caps.supports_10bit_encode) + formats.insert ("P010_10LE"); + break; + case NV_ENC_BUFFER_FORMAT_AYUV: + formats.insert ("VUYA"); + break; + case NV_ENC_BUFFER_FORMAT_ABGR: + formats.insert ("RGBA"); + formats.insert ("RGBx"); + break; + case NV_ENC_BUFFER_FORMAT_ARGB: + formats.insert ("BGRA"); + formats.insert ("BGRx"); + break; + case NV_ENC_BUFFER_FORMAT_ABGR10: + if (dev_caps.supports_10bit_encode) + formats.insert ("RGB10A2_LE"); + break; + default: + break; + } + } + + if (formats.empty ()) { + GST_WARNING_OBJECT (device, "Empty supported input format"); + return nullptr; + } +#define APPEND_STRING(dst,set,str) G_STMT_START { \ + if (set.find(str) != set.end()) { \ + if (!first) \ + dst += ", "; \ + dst += str; \ + first = false; \ + } \ +} G_STMT_END + + if (formats.size () == 1) { + format_str = "format = (string) " + *(formats.begin ()); + } else { + bool first = true; + + format_str = "format = (string) { "; + APPEND_STRING (format_str, formats, "NV12"); + APPEND_STRING (format_str, formats, "P010_10LE"); + APPEND_STRING (format_str, formats, "VUYA"); + APPEND_STRING (format_str, formats, "RGBA"); + APPEND_STRING (format_str, formats, "RGBx"); + APPEND_STRING (format_str, formats, "BGRA"); + APPEND_STRING (format_str, formats, "BGRx"); + APPEND_STRING (format_str, formats, "RGB10A2_LE"); + format_str += " }"; + } +#undef APPEND_STRING + + resolution_str = "width = (int) [ " + + std::to_string (GST_ROUND_UP_16 (dev_caps.width_min)) + + ", " + std::to_string (dev_caps.width_max) + " ]"; + resolution_str += ", height = (int) [ " + + std::to_string (GST_ROUND_UP_16 (dev_caps.height_min)) + + ", " + std::to_string (dev_caps.height_max) + " ]"; + + sink_caps_str = "video/x-raw, " + format_str + ", " + resolution_str; + + src_caps_str = "video/x-av1, " + resolution_str + ", profile = (string) main" + + ", stream-format = (string) obu-stream, alignment = (string) tu"; + + system_caps = gst_caps_from_string (sink_caps_str.c_str ()); + sink_caps = gst_caps_copy (system_caps); +#ifdef G_OS_WIN32 + if (device_mode == GST_NV_ENCODER_DEVICE_D3D11) { + gst_caps_set_features (sink_caps, 0, + gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY, nullptr)); + } +#endif + + if (device_mode == GST_NV_ENCODER_DEVICE_CUDA) { + gst_caps_set_features (sink_caps, 0, + gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_CUDA_MEMORY, nullptr)); +#ifdef HAVE_CUDA_GST_GL + GstCaps *gl_caps = gst_caps_copy (system_caps); + gst_caps_set_features (gl_caps, 0, + gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_GL_MEMORY, nullptr)); + gst_caps_append (sink_caps, gl_caps); +#endif + } + + gst_caps_append (sink_caps, system_caps); + + cdata = gst_nv_encoder_class_data_new (); + cdata->sink_caps = sink_caps; + cdata->src_caps = gst_caps_from_string (src_caps_str.c_str ()); + cdata->device_caps = dev_caps; + cdata->device_mode = device_mode; + + /* *INDENT-OFF* */ + for (const auto &iter: formats) + cdata->formats = g_list_append (cdata->formats, g_strdup (iter.c_str())); + /* *INDENT-ON* */ + + if (device_mode == GST_NV_ENCODER_DEVICE_D3D11) + g_object_get (device, "adapter-luid", &cdata->adapter_luid, nullptr); + + if (device_mode == GST_NV_ENCODER_DEVICE_CUDA) + g_object_get (device, "cuda-device-id", &cdata->cuda_device_id, nullptr); + + /* class data will be leaked if the element never gets instantiated */ + GST_MINI_OBJECT_FLAG_SET (cdata->sink_caps, + GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED); + GST_MINI_OBJECT_FLAG_SET (cdata->src_caps, + GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED); + + return cdata; +} + +GstNvEncoderClassData * +gst_nv_av1_encoder_register_cuda (GstPlugin * plugin, GstCudaContext * context, + guint rank) +{ + NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS session_params = { 0, }; + gpointer session; + NVENCSTATUS status; + GstNvEncoderClassData *cdata; + + GST_DEBUG_CATEGORY_INIT (gst_nv_av1_encoder_debug, "nvav1encoder", 0, + "nvav1encoder"); + + session_params.version = + gst_nvenc_get_open_encode_session_ex_params_version (); + session_params.deviceType = NV_ENC_DEVICE_TYPE_CUDA; + session_params.device = gst_cuda_context_get_handle (context); + session_params.apiVersion = gst_nvenc_get_api_version (); + + status = NvEncOpenEncodeSessionEx (&session_params, &session); + if (status != NV_ENC_SUCCESS) { + GST_WARNING_OBJECT (context, "Failed to open session"); + return nullptr; + } + + cdata = gst_nv_av1_encoder_create_class_data (GST_OBJECT (context), session, + GST_NV_ENCODER_DEVICE_CUDA); + NvEncDestroyEncoder (session); + + if (!cdata) + return nullptr; + + gst_nv_encoder_class_data_ref (cdata); + + GType type; + gchar *type_name; + gchar *feature_name; + GTypeInfo type_info = { + sizeof (GstNvAv1EncoderClass), + nullptr, + nullptr, + (GClassInitFunc) gst_nv_av1_encoder_class_init, + nullptr, + cdata, + sizeof (GstNvAv1Encoder), + 0, + (GInstanceInitFunc) gst_nv_av1_encoder_init, + }; + + type_name = g_strdup ("GstNvAv1Enc"); + feature_name = g_strdup ("nvav1enc"); + + gint index = 0; + while (g_type_from_name (type_name)) { + index++; + g_free (type_name); + g_free (feature_name); + type_name = g_strdup_printf ("GstNvAv1Device%dEnc", index); + feature_name = g_strdup_printf ("nvav1device%denc", index); + } + + type = g_type_register_static (GST_TYPE_NV_ENCODER, type_name, + &type_info, (GTypeFlags) 0); + + if (rank > 0 && index != 0) + rank--; + + if (index != 0) + gst_element_type_set_skip_documentation (type); + + if (!gst_element_register (plugin, feature_name, rank, type)) + GST_WARNING ("Failed to register plugin '%s'", type_name); + + g_free (type_name); + g_free (feature_name); + + return cdata; +} + +#ifdef G_OS_WIN32 +GstNvEncoderClassData * +gst_nv_av1_encoder_register_d3d11 (GstPlugin * plugin, GstD3D11Device * device, + guint rank) +{ + NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS session_params = { 0, }; + gpointer session; + NVENCSTATUS status; + GstNvEncoderClassData *cdata; + + GST_DEBUG_CATEGORY_INIT (gst_nv_av1_encoder_debug, "nvav1encoder", 0, + "nvav1encoder"); + + session_params.version = + gst_nvenc_get_open_encode_session_ex_params_version (); + session_params.deviceType = NV_ENC_DEVICE_TYPE_DIRECTX; + session_params.device = gst_d3d11_device_get_device_handle (device); + session_params.apiVersion = gst_nvenc_get_api_version (); + + status = NvEncOpenEncodeSessionEx (&session_params, &session); + if (status != NV_ENC_SUCCESS) { + GST_WARNING_OBJECT (device, "Failed to open session"); + return nullptr; + } + + cdata = gst_nv_av1_encoder_create_class_data (GST_OBJECT (device), session, + GST_NV_ENCODER_DEVICE_D3D11); + NvEncDestroyEncoder (session); + + if (!cdata) + return nullptr; + + gst_nv_encoder_class_data_ref (cdata); + + GType type; + gchar *type_name; + gchar *feature_name; + GTypeInfo type_info = { + sizeof (GstNvAv1EncoderClass), + nullptr, + nullptr, + (GClassInitFunc) gst_nv_av1_encoder_class_init, + nullptr, + cdata, + sizeof (GstNvAv1Encoder), + 0, + (GInstanceInitFunc) gst_nv_av1_encoder_init, + }; + + type_name = g_strdup ("GstNvD3D11Av1Enc"); + feature_name = g_strdup ("nvd3d11av1enc"); + + gint index = 0; + while (g_type_from_name (type_name)) { + index++; + g_free (type_name); + g_free (feature_name); + type_name = g_strdup_printf ("GstNvD3D11Av1Device%dEnc", index); + feature_name = g_strdup_printf ("nvd3d11av1device%denc", index); + } + + type = g_type_register_static (GST_TYPE_NV_ENCODER, type_name, + &type_info, (GTypeFlags) 0); + + if (rank > 0 && index != 0) + rank--; + + if (index != 0) + gst_element_type_set_skip_documentation (type); + + if (!gst_element_register (plugin, feature_name, rank, type)) + GST_WARNING ("Failed to register plugin '%s'", type_name); + + g_free (type_name); + g_free (feature_name); + + return cdata; +} +#endif + +void +gst_nv_av1_encoder_register_auto_select (GstPlugin * plugin, + GList * device_caps_list, guint rank) +{ + std::set < std::string > formats; + std::string sink_caps_str; + std::string src_caps_str; + std::string format_str; + std::string resolution_str; + GList *iter; + guint adapter_luid_size = 0; + gint64 adapter_luid_list[8] = { 0, }; + guint cuda_device_id_size = 0; + guint cuda_device_id_list[8] = { 0, }; + GstNvEncoderDeviceCaps dev_caps; + GstNvEncoderClassData *cdata; + GstCaps *sink_caps = nullptr; + GstCaps *system_caps; + + GST_DEBUG_CATEGORY_INIT (gst_nv_av1_encoder_debug, "nvav1encoder", 0, + "nvav1encoder"); + + for (iter = device_caps_list; iter; iter = g_list_next (iter)) { + GstNvEncoderClassData *cdata = (GstNvEncoderClassData *) iter->data; + GList *walk; + + for (walk = cdata->formats; walk; walk = g_list_next (walk)) + formats.insert ((gchar *) walk->data); + + if (cdata->device_mode == GST_NV_ENCODER_DEVICE_D3D11 && + adapter_luid_size <= G_N_ELEMENTS (adapter_luid_list) - 1) { + adapter_luid_list[adapter_luid_size] = cdata->adapter_luid; + adapter_luid_size++; + } + + if (cdata->device_mode == GST_NV_ENCODER_DEVICE_CUDA && + cuda_device_id_size <= G_N_ELEMENTS (cuda_device_id_list) - 1) { + cuda_device_id_list[cuda_device_id_size] = cdata->cuda_device_id; + cuda_device_id_size++; + } + + if (iter == device_caps_list) { + dev_caps = cdata->device_caps; + } else { + gst_nv_encoder_merge_device_caps (&dev_caps, &cdata->device_caps, + &dev_caps); + } + } + + g_list_free_full (device_caps_list, + (GDestroyNotify) gst_nv_encoder_class_data_unref); + if (formats.empty ()) + return; + +#define APPEND_STRING(dst,set,str) G_STMT_START { \ + if (set.find(str) != set.end()) { \ + if (!first) \ + dst += ", "; \ + dst += str; \ + first = false; \ + } \ +} G_STMT_END + + if (formats.size () == 1) { + format_str = "format = (string) " + *(formats.begin ()); + } else { + bool first = true; + + format_str = "format = (string) { "; + APPEND_STRING (format_str, formats, "NV12"); + APPEND_STRING (format_str, formats, "P010_10LE"); + APPEND_STRING (format_str, formats, "VUYA"); + APPEND_STRING (format_str, formats, "RGBA"); + APPEND_STRING (format_str, formats, "RGBx"); + APPEND_STRING (format_str, formats, "BGRA"); + APPEND_STRING (format_str, formats, "BGRx"); + APPEND_STRING (format_str, formats, "RGB10A2_LE"); + format_str += " }"; + } +#undef APPEND_STRING + + resolution_str = "width = (int) [ " + + std::to_string (GST_ROUND_UP_16 (dev_caps.width_min)) + + ", " + std::to_string (dev_caps.width_max) + " ]"; + resolution_str += ", height = (int) [ " + + std::to_string (GST_ROUND_UP_16 (dev_caps.height_min)) + + ", " + std::to_string (dev_caps.height_max) + " ]"; + + + sink_caps_str = "video/x-raw, " + format_str + ", " + resolution_str; + src_caps_str = "video/x-av1, " + resolution_str + ", profile = (string) main" + + ", stream-format = (string) obu-stream, alignment = (string) tu"; + + system_caps = gst_caps_from_string (sink_caps_str.c_str ()); + sink_caps = gst_caps_new_empty (); + + if (cuda_device_id_size > 0) { + GstCaps *cuda_caps = gst_caps_copy (system_caps); + gst_caps_set_features (cuda_caps, 0, + gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_CUDA_MEMORY, nullptr)); + gst_caps_append (sink_caps, cuda_caps); + } +#ifdef G_OS_WIN32 + if (adapter_luid_size > 0) { + GstCaps *d3d11_caps = gst_caps_copy (system_caps); + gst_caps_set_features (d3d11_caps, 0, + gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY, nullptr)); + gst_caps_append (sink_caps, d3d11_caps); + } +#endif + +#ifdef HAVE_CUDA_GST_GL + GstCaps *gl_caps = gst_caps_copy (system_caps); + gst_caps_set_features (gl_caps, 0, + gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_GL_MEMORY, nullptr)); + gst_caps_append (sink_caps, gl_caps); +#endif + + gst_caps_append (sink_caps, system_caps); + + cdata = gst_nv_encoder_class_data_new (); + cdata->sink_caps = sink_caps; + cdata->src_caps = gst_caps_from_string (src_caps_str.c_str ()); + cdata->device_caps = dev_caps; + cdata->device_mode = GST_NV_ENCODER_DEVICE_AUTO_SELECT; + cdata->adapter_luid = adapter_luid_list[0]; + cdata->adapter_luid_size = adapter_luid_size; + memcpy (&cdata->adapter_luid_list, + adapter_luid_list, sizeof (adapter_luid_list)); + cdata->cuda_device_id = cuda_device_id_list[0]; + cdata->cuda_device_id_size = cuda_device_id_size; + memcpy (&cdata->cuda_device_id_list, + cuda_device_id_list, sizeof (cuda_device_id_list)); + + /* class data will be leaked if the element never gets instantiated */ + GST_MINI_OBJECT_FLAG_SET (cdata->sink_caps, + GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED); + GST_MINI_OBJECT_FLAG_SET (cdata->src_caps, + GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED); + + GType type; + GTypeInfo type_info = { + sizeof (GstNvAv1EncoderClass), + nullptr, + nullptr, + (GClassInitFunc) gst_nv_av1_encoder_class_init, + nullptr, + cdata, + sizeof (GstNvAv1Encoder), + 0, + (GInstanceInitFunc) gst_nv_av1_encoder_init, + }; + + type = g_type_register_static (GST_TYPE_NV_ENCODER, "GstNvAutoGpuAv1Enc", + &type_info, (GTypeFlags) 0); + + if (!gst_element_register (plugin, "nvautogpuav1enc", rank, type)) + GST_WARNING ("Failed to register plugin 'GstNvAutoGpuAv1Enc'"); +} diff --git a/subprojects/gst-plugins-bad/sys/nvcodec/gstnvav1encoder.h b/subprojects/gst-plugins-bad/sys/nvcodec/gstnvav1encoder.h new file mode 100644 index 0000000000..9a375747ba --- /dev/null +++ b/subprojects/gst-plugins-bad/sys/nvcodec/gstnvav1encoder.h @@ -0,0 +1,41 @@ +/* GStreamer + * Copyright (C) 2024 Seungha Yang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include "gstnvencoder.h" + +G_BEGIN_DECLS + +GstNvEncoderClassData * gst_nv_av1_encoder_register_cuda (GstPlugin * plugin, + GstCudaContext * context, + guint rank); + +#ifdef G_OS_WIN32 +GstNvEncoderClassData * gst_nv_av1_encoder_register_d3d11 (GstPlugin * plugin, + GstD3D11Device * device, + guint rank); +#endif + +void gst_nv_av1_encoder_register_auto_select (GstPlugin * plugin, + GList * device_caps_list, + guint rank); + + +G_END_DECLS diff --git a/subprojects/gst-plugins-bad/sys/nvcodec/gstnvencobject.cpp b/subprojects/gst-plugins-bad/sys/nvcodec/gstnvencobject.cpp index 63f6686d2e..8e933bb6f0 100644 --- a/subprojects/gst-plugins-bad/sys/nvcodec/gstnvencobject.cpp +++ b/subprojects/gst-plugins-bad/sys/nvcodec/gstnvencobject.cpp @@ -145,11 +145,14 @@ GstNvEncObject::IsSuccess (NVENCSTATUS status, GstNvEncObject * self, #ifndef GST_DISABLE_GST_DEBUG const gchar *status_str = nvenc_status_to_string (status); + const gchar *error_detail = nullptr; + if (self && self->session_) + error_detail = NvEncGetLastErrorString (self->session_); if (self) { gst_debug_log_id (GST_CAT_DEFAULT, GST_LEVEL_ERROR, file, function, - line, self->id_.c_str (), "NvEnc API call failed: 0x%x, %s", - (guint) status, status_str); + line, self->id_.c_str (), "NvEnc API call failed: 0x%x, %s (%s)", + (guint) status, status_str, GST_STR_NULL (error_detail)); } else { gst_debug_log (GST_CAT_DEFAULT, GST_LEVEL_ERROR, file, function, line, nullptr, "NvEnc API call failed: 0x%x, %s", @@ -293,8 +296,11 @@ GstNvEncObject::InitSession (NV_ENC_INITIALIZE_PARAMS * params, if (memcmp (¶ms->encodeGUID, &NV_ENC_CODEC_H264_GUID, sizeof (GUID)) == 0) { codec_ = GST_NV_ENC_CODEC_H264; - } else { + } else if (memcmp (¶ms->encodeGUID, + &NV_ENC_CODEC_HEVC_GUID, sizeof (GUID)) == 0) { codec_ = GST_NV_ENC_CODEC_H265; + } else { + codec_ = GST_NV_ENC_CODEC_AV1; } info_ = *info; diff --git a/subprojects/gst-plugins-bad/sys/nvcodec/gstnvencobject.h b/subprojects/gst-plugins-bad/sys/nvcodec/gstnvencobject.h index 0902450950..248c689869 100644 --- a/subprojects/gst-plugins-bad/sys/nvcodec/gstnvencobject.h +++ b/subprojects/gst-plugins-bad/sys/nvcodec/gstnvencobject.h @@ -138,6 +138,7 @@ enum GstNvEncCodec { GST_NV_ENC_CODEC_H264, GST_NV_ENC_CODEC_H265, + GST_NV_ENC_CODEC_AV1, }; class GstNvEncObject : public std::enable_shared_from_this diff --git a/subprojects/gst-plugins-bad/sys/nvcodec/meson.build b/subprojects/gst-plugins-bad/sys/nvcodec/meson.build index 231e2cea05..890049b944 100644 --- a/subprojects/gst-plugins-bad/sys/nvcodec/meson.build +++ b/subprojects/gst-plugins-bad/sys/nvcodec/meson.build @@ -10,6 +10,7 @@ nvcodec_sources = [ 'gstcudamemorycopy.c', 'gstcuvidloader.c', 'gstnvav1dec.cpp', + 'gstnvav1encoder.cpp', 'gstnvdec.c', 'gstnvdecobject.cpp', 'gstnvdecoder.cpp', diff --git a/subprojects/gst-plugins-bad/sys/nvcodec/plugin.c b/subprojects/gst-plugins-bad/sys/nvcodec/plugin.c index f9818062a0..d784bdb6fe 100644 --- a/subprojects/gst-plugins-bad/sys/nvcodec/plugin.c +++ b/subprojects/gst-plugins-bad/sys/nvcodec/plugin.c @@ -49,6 +49,7 @@ #endif #include "gstnvh264encoder.h" #include "gstnvh265encoder.h" +#include "gstnvav1encoder.h" #include "gstcudaipcsink.h" #include "gstcudaipcsrc.h" #include "gstnvcodecutils.h" @@ -126,6 +127,7 @@ plugin_init (GstPlugin * plugin) guint api_minor_ver = 1; GList *h264_enc_cdata = NULL; GList *h265_enc_cdata = NULL; + GList *av1_enc_cdata = NULL; gboolean have_nvrtc = FALSE; GST_DEBUG_CATEGORY_INIT (gst_nvcodec_debug, "nvcodec", 0, "nvcodec"); @@ -291,6 +293,11 @@ plugin_init (GstPlugin * plugin) if (cdata) h265_enc_cdata = g_list_append (h265_enc_cdata, cdata); + cdata = gst_nv_av1_encoder_register_d3d11 (plugin, + d3d11_device, GST_RANK_NONE); + if (cdata) + av1_enc_cdata = g_list_append (av1_enc_cdata, cdata); + gst_object_unref (d3d11_device); } } @@ -304,6 +311,11 @@ plugin_init (GstPlugin * plugin) context, GST_RANK_PRIMARY + 1); if (cdata) h265_enc_cdata = g_list_append (h265_enc_cdata, cdata); + + cdata = gst_nv_av1_encoder_register_cuda (plugin, + context, GST_RANK_PRIMARY + 1); + if (cdata) + av1_enc_cdata = g_list_append (av1_enc_cdata, cdata); } gst_nv_jpeg_enc_register (plugin, context, GST_RANK_NONE, have_nvrtc); @@ -315,11 +327,17 @@ plugin_init (GstPlugin * plugin) gst_nv_h264_encoder_register_auto_select (plugin, h264_enc_cdata, GST_RANK_NONE); } + if (h265_enc_cdata) { gst_nv_h265_encoder_register_auto_select (plugin, h265_enc_cdata, GST_RANK_NONE); } + if (av1_enc_cdata) { + gst_nv_av1_encoder_register_auto_select (plugin, av1_enc_cdata, + GST_RANK_NONE); + } + gst_cuda_memory_copy_register (plugin, GST_RANK_NONE); if (have_nvrtc) {