diff --git a/sys/nvcodec/gstnvbaseenc.c b/sys/nvcodec/gstnvbaseenc.c index 0789172f76..68d3a106ad 100644 --- a/sys/nvcodec/gstnvbaseenc.c +++ b/sys/nvcodec/gstnvbaseenc.c @@ -242,8 +242,8 @@ gst_nv_base_enc_class_init (GstNvBaseEncClass * klass) g_object_class_install_property (gobject_class, PROP_DEVICE_ID, g_param_spec_uint ("cuda-device-id", "Cuda Device ID", - "Set the GPU device to use for operations", - 0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + "Get the GPU device to use for operations", + 0, G_MAXUINT, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_PRESET, g_param_spec_enum ("preset", "Encoding Preset", "Encoding Preset", @@ -285,119 +285,14 @@ gst_nv_base_enc_class_init (GstNvBaseEncClass * klass) G_PARAM_STATIC_STRINGS)); } -static gboolean -_get_supported_input_formats (GstNvBaseEnc * nvenc) -{ - GstNvBaseEncClass *nvenc_class = GST_NV_BASE_ENC_GET_CLASS (nvenc); - guint64 format_mask = 0; - uint32_t i, num = 0; - NV_ENC_BUFFER_FORMAT formats[64]; - GValue val = G_VALUE_INIT; - - if (nvenc->input_formats) - return TRUE; - - NvEncGetInputFormats (nvenc->encoder, nvenc_class->codec_id, formats, - G_N_ELEMENTS (formats), &num); - - for (i = 0; i < num; ++i) { - GST_INFO_OBJECT (nvenc, "input format: 0x%08x", formats[i]); - /* Apparently we can just ignore the tiled formats and can feed - * it the respective untiled planar format instead ?! */ - switch (formats[i]) { - case NV_ENC_BUFFER_FORMAT_NV12_PL: -#if defined (NV_ENC_BUFFER_FORMAT_NV12_TILED16x16) - case NV_ENC_BUFFER_FORMAT_NV12_TILED16x16: -#endif -#if defined (NV_ENC_BUFFER_FORMAT_NV12_TILED64x16) - case NV_ENC_BUFFER_FORMAT_NV12_TILED64x16: -#endif - format_mask |= (1 << GST_VIDEO_FORMAT_NV12); - break; - case NV_ENC_BUFFER_FORMAT_YV12_PL: -#if defined(NV_ENC_BUFFER_FORMAT_YV12_TILED16x16) - case NV_ENC_BUFFER_FORMAT_YV12_TILED16x16: -#endif -#if defined (NV_ENC_BUFFER_FORMAT_YV12_TILED64x16) - case NV_ENC_BUFFER_FORMAT_YV12_TILED64x16: -#endif - format_mask |= (1 << GST_VIDEO_FORMAT_YV12); - break; - case NV_ENC_BUFFER_FORMAT_IYUV_PL: -#if defined (NV_ENC_BUFFER_FORMAT_IYUV_TILED16x16) - case NV_ENC_BUFFER_FORMAT_IYUV_TILED16x16: -#endif -#if defined (NV_ENC_BUFFER_FORMAT_IYUV_TILED64x16) - case NV_ENC_BUFFER_FORMAT_IYUV_TILED64x16: -#endif - format_mask |= (1 << GST_VIDEO_FORMAT_I420); - break; - case NV_ENC_BUFFER_FORMAT_YUV444_PL: -#if defined (NV_ENC_BUFFER_FORMAT_YUV444_TILED16x16) - case NV_ENC_BUFFER_FORMAT_YUV444_TILED16x16: -#endif -#if defined (NV_ENC_BUFFER_FORMAT_YUV444_TILED64x16) - case NV_ENC_BUFFER_FORMAT_YUV444_TILED64x16: -#endif - { - NV_ENC_CAPS_PARAM caps_param = { 0, }; - int yuv444_supported = 0; - - caps_param.version = NV_ENC_CAPS_PARAM_VER; - caps_param.capsToQuery = NV_ENC_CAPS_SUPPORT_YUV444_ENCODE; - - if (NvEncGetEncodeCaps (nvenc->encoder, nvenc_class->codec_id, - &caps_param, &yuv444_supported) != NV_ENC_SUCCESS) - yuv444_supported = 0; - - if (yuv444_supported) - format_mask |= (1 << GST_VIDEO_FORMAT_Y444); - break; - } - default: - GST_FIXME ("unmapped input format: 0x%08x", formats[i]); - break; - } - } - - if (format_mask == 0) - return FALSE; - - GST_OBJECT_LOCK (nvenc); - nvenc->input_formats = g_new0 (GValue, 1); - - /* process a second time so we can add formats in the order we want */ - g_value_init (nvenc->input_formats, GST_TYPE_LIST); - g_value_init (&val, G_TYPE_STRING); - if ((format_mask & (1 << GST_VIDEO_FORMAT_NV12))) { - g_value_set_static_string (&val, "NV12"); - gst_value_list_append_value (nvenc->input_formats, &val); - } - if ((format_mask & (1 << GST_VIDEO_FORMAT_YV12))) { - g_value_set_static_string (&val, "YV12"); - gst_value_list_append_value (nvenc->input_formats, &val); - } - if ((format_mask & (1 << GST_VIDEO_FORMAT_I420))) { - g_value_set_static_string (&val, "I420"); - gst_value_list_append_value (nvenc->input_formats, &val); - } - if ((format_mask & (1 << GST_VIDEO_FORMAT_Y444))) { - g_value_set_static_string (&val, "Y444"); - gst_value_list_append_value (nvenc->input_formats, &val); - } - g_value_unset (&val); - - GST_OBJECT_UNLOCK (nvenc); - - return TRUE; -} - static gboolean gst_nv_base_enc_open (GstVideoEncoder * enc) { GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (enc); + GstNvBaseEncClass *klass = GST_NV_BASE_ENC_GET_CLASS (enc); + GValue *formats = NULL; - nvenc->cuda_ctx = gst_nvenc_create_cuda_context (nvenc->cuda_device_id); + nvenc->cuda_ctx = gst_nvenc_create_cuda_context (klass->cuda_device_id); if (nvenc->cuda_ctx == NULL) { GST_ELEMENT_ERROR (enc, LIBRARY, INIT, (NULL), ("Failed to create CUDA context, perhaps CUDA is not supported.")); @@ -423,12 +318,15 @@ gst_nv_base_enc_open (GstVideoEncoder * enc) } /* query supported input formats */ - if (!_get_supported_input_formats (nvenc)) { + if (!gst_nv_enc_get_supported_input_formats (nvenc->encoder, klass->codec_id, + &formats)) { GST_WARNING_OBJECT (nvenc, "No supported input formats"); gst_nv_base_enc_close (enc); return FALSE; } + nvenc->input_formats = formats; + return TRUE; } @@ -542,42 +440,11 @@ gst_nv_base_enc_stop (GstVideoEncoder * enc) return TRUE; } -static GValue * -_get_interlace_modes (GstNvBaseEnc * nvenc) -{ - GstNvBaseEncClass *nvenc_class = GST_NV_BASE_ENC_GET_CLASS (nvenc); - NV_ENC_CAPS_PARAM caps_param = { 0, }; - GValue *list = g_new0 (GValue, 1); - GValue val = G_VALUE_INIT; - - g_value_init (list, GST_TYPE_LIST); - g_value_init (&val, G_TYPE_STRING); - - g_value_set_static_string (&val, "progressive"); - gst_value_list_append_value (list, &val); - - caps_param.version = NV_ENC_CAPS_PARAM_VER; - caps_param.capsToQuery = NV_ENC_CAPS_SUPPORT_FIELD_ENCODING; - - if (NvEncGetEncodeCaps (nvenc->encoder, nvenc_class->codec_id, - &caps_param, &nvenc->interlace_modes) != NV_ENC_SUCCESS) - nvenc->interlace_modes = 0; - - if (nvenc->interlace_modes >= 1) { - g_value_set_static_string (&val, "interleaved"); - gst_value_list_append_value (list, &val); - g_value_set_static_string (&val, "mixed"); - gst_value_list_append_and_take_value (list, &val); - } - /* TODO: figure out what nvenc frame based interlacing means in gst terms */ - - return list; -} - static GstCaps * gst_nv_base_enc_getcaps (GstVideoEncoder * enc, GstCaps * filter) { GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (enc); + GstNvBaseEncClass *klass = GST_NV_BASE_ENC_GET_CLASS (enc); GstCaps *supported_incaps = NULL; GstCaps *template_caps, *caps; @@ -590,7 +457,7 @@ gst_nv_base_enc_getcaps (GstVideoEncoder * enc, GstCaps * filter) supported_incaps = gst_caps_copy (template_caps); gst_caps_set_value (supported_incaps, "format", nvenc->input_formats); - val = _get_interlace_modes (nvenc); + val = gst_nv_enc_get_interlace_modes (nvenc->encoder, klass->codec_id); gst_caps_set_value (supported_incaps, "interlace-mode", val); g_value_unset (val); g_free (val); @@ -1037,7 +904,8 @@ _get_plane_width (GstVideoInfo * info, guint plane) /* For now component width and plane width are the same and the * plane-component mapping matches */ - return GST_VIDEO_INFO_COMP_WIDTH (info, plane); + return GST_VIDEO_INFO_COMP_WIDTH (info, plane) + * GST_VIDEO_INFO_COMP_PSTRIDE (info, plane); else /* RGB, GRAY */ return GST_VIDEO_INFO_WIDTH (info); } @@ -1319,7 +1187,7 @@ gst_nv_base_enc_set_format (GstVideoEncoder * enc, GstVideoCodecState * state) /* scratch buffer for non-contigious planer into a contigious buffer */ cu_ret = CuMemAllocPitch ((CUdeviceptr *) & in_gl_resource->cuda_pointer, - &in_gl_resource->cuda_stride, input_width, + &in_gl_resource->cuda_stride, _get_plane_width (info, 0), _get_frame_data_height (info), 16); if (cu_ret != CUDA_SUCCESS) { const gchar *err; @@ -1449,47 +1317,25 @@ gst_nv_base_enc_set_format (GstVideoEncoder * enc, GstVideoCodecState * state) return TRUE; } -static inline guint -_plane_get_n_components (GstVideoInfo * info, guint plane) +#if HAVE_NVCODEC_GST_GL +static guint +_get_cuda_device_stride (GstVideoInfo * info, guint plane, gsize cuda_stride) { switch (GST_VIDEO_INFO_FORMAT (info)) { - case GST_VIDEO_FORMAT_RGBx: - case GST_VIDEO_FORMAT_BGRx: - case GST_VIDEO_FORMAT_xRGB: - case GST_VIDEO_FORMAT_xBGR: - case GST_VIDEO_FORMAT_RGBA: - case GST_VIDEO_FORMAT_BGRA: - case GST_VIDEO_FORMAT_ARGB: - case GST_VIDEO_FORMAT_ABGR: - case GST_VIDEO_FORMAT_AYUV: - return 4; - case GST_VIDEO_FORMAT_RGB: - case GST_VIDEO_FORMAT_BGR: - case GST_VIDEO_FORMAT_RGB16: - case GST_VIDEO_FORMAT_BGR16: - return 3; - case GST_VIDEO_FORMAT_GRAY16_BE: - case GST_VIDEO_FORMAT_GRAY16_LE: - case GST_VIDEO_FORMAT_YUY2: - case GST_VIDEO_FORMAT_UYVY: - return 2; case GST_VIDEO_FORMAT_NV12: case GST_VIDEO_FORMAT_NV21: - return plane == 0 ? 1 : 2; - case GST_VIDEO_FORMAT_GRAY8: + case GST_VIDEO_FORMAT_P010_10LE: + case GST_VIDEO_FORMAT_P010_10BE: case GST_VIDEO_FORMAT_Y444: - case GST_VIDEO_FORMAT_Y42B: - case GST_VIDEO_FORMAT_Y41B: + return cuda_stride; case GST_VIDEO_FORMAT_I420: - case GST_VIDEO_FORMAT_YV12: - return 1; + return plane == 0 ? cuda_stride : (GST_ROUND_UP_2 (cuda_stride) / 2); default: g_assert_not_reached (); - return 1; + return cuda_stride; } } -#if HAVE_NVCODEC_GST_GL struct map_gl_input { GstNvBaseEnc *nvenc; @@ -1509,7 +1355,6 @@ _map_gl_input_buffer (GstGLContext * context, struct map_gl_input *data) CuCtxPushCurrent (data->nvenc->cuda_ctx); data_pointer = data->in_gl_resource->cuda_pointer; for (i = 0; i < GST_VIDEO_INFO_N_PLANES (data->info); i++) { - guint plane_n_components; GstGLBuffer *gl_buf_obj; GstGLMemoryPBO *gl_mem; guint src_stride, dest_stride; @@ -1519,7 +1364,6 @@ _map_gl_input_buffer (GstGLContext * context, struct map_gl_input *data) i); g_return_if_fail (gst_is_gl_memory_pbo ((GstMemory *) gl_mem)); data->in_gl_resource->gl_mem[i] = GST_GL_MEMORY_CAST (gl_mem); - plane_n_components = _plane_get_n_components (data->info, i); gl_buf_obj = (GstGLBuffer *) gl_mem->pbo; g_return_if_fail (gl_buf_obj != NULL); @@ -1559,7 +1403,9 @@ _map_gl_input_buffer (GstGLContext * context, struct map_gl_input *data) } src_stride = GST_VIDEO_INFO_PLANE_STRIDE (data->info, i); - dest_stride = data->in_gl_resource->cuda_stride; + dest_stride = + _get_cuda_device_stride (data->info, i, + data->in_gl_resource->cuda_stride); /* copy into scratch buffer */ param.srcXInBytes = 0; @@ -1573,7 +1419,7 @@ _map_gl_input_buffer (GstGLContext * context, struct map_gl_input *data) param.dstMemoryType = CU_MEMORYTYPE_DEVICE; param.dstDevice = (CUdeviceptr) data_pointer; param.dstPitch = dest_stride; - param.WidthInBytes = _get_plane_width (data->info, i) * plane_n_components; + param.WidthInBytes = _get_plane_width (data->info, i); param.Height = _get_plane_height (data->info, i); cuda_ret = CuMemcpy2D (¶m); @@ -1599,10 +1445,8 @@ _map_gl_input_buffer (GstGLContext * context, struct map_gl_input *data) g_assert_not_reached (); } - data_pointer = - data_pointer + - data->in_gl_resource->cuda_stride * - _get_plane_height (&data->nvenc->input_info, i); + data_pointer = data_pointer + + dest_stride * _get_plane_height (&data->nvenc->input_info, i); } CuCtxPopCurrent (NULL); } @@ -1812,7 +1656,8 @@ gst_nv_base_enc_handle_frame (GstVideoEncoder * enc, GstVideoCodecFrame * frame) } GST_LOG_OBJECT (nvenc, "Locked input buffer %p", in_buf); - width = GST_VIDEO_FRAME_WIDTH (&vframe); + width = GST_VIDEO_FRAME_COMP_WIDTH (&vframe, 0) * + GST_VIDEO_FRAME_COMP_PSTRIDE (&vframe, 0); height = GST_VIDEO_FRAME_HEIGHT (&vframe); /* copy Y plane */ @@ -1826,7 +1671,9 @@ gst_nv_base_enc_handle_frame (GstVideoEncoder * enc, GstVideoCodecFrame * frame) src += src_stride; } - if (GST_VIDEO_FRAME_FORMAT (&vframe) == GST_VIDEO_FORMAT_NV12) { + if (GST_VIDEO_FRAME_FORMAT (&vframe) == GST_VIDEO_FORMAT_NV12 || + GST_VIDEO_FRAME_FORMAT (&vframe) == GST_VIDEO_FORMAT_P010_10LE || + GST_VIDEO_FRAME_FORMAT (&vframe) == GST_VIDEO_FORMAT_P010_10BE) { /* copy UV plane */ src = GST_VIDEO_FRAME_PLANE_DATA (&vframe, 1); src_stride = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 1); @@ -2003,9 +1850,6 @@ gst_nv_base_enc_set_property (GObject * object, guint prop_id, GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (object); switch (prop_id) { - case PROP_DEVICE_ID: - nvenc->cuda_device_id = g_value_get_uint (value); - break; case PROP_PRESET: nvenc->preset_enum = g_value_get_enum (value); nvenc->selected_preset = _nv_preset_to_guid (nvenc->preset_enum); @@ -2046,10 +1890,11 @@ gst_nv_base_enc_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (object); + GstNvBaseEncClass *nvenc_class = GST_NV_BASE_ENC_GET_CLASS (object); switch (prop_id) { case PROP_DEVICE_ID: - g_value_set_uint (value, nvenc->cuda_device_id); + g_value_set_uint (value, nvenc_class->cuda_device_id); break; case PROP_PRESET: g_value_set_enum (value, nvenc->preset_enum); @@ -2077,3 +1922,88 @@ gst_nv_base_enc_get_property (GObject * object, guint prop_id, GValue * value, break; } } + +typedef struct +{ + GstCaps *sink_caps; + GstCaps *src_caps; + guint cuda_device_id; + gboolean is_default; +} GstNvEncClassData; + +static void +gst_nv_base_enc_subclass_init (gpointer g_class, gpointer data) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + GstNvBaseEncClass *nvbaseenc_class = GST_NV_BASE_ENC_CLASS (g_class); + GstNvEncClassData *cdata = data; + + if (!cdata->is_default) { + const gchar *long_name; + gchar *new_long_name; + + long_name = gst_element_class_get_metadata (element_class, + GST_ELEMENT_METADATA_LONGNAME); + + new_long_name = g_strdup_printf ("%s with devide-id %d", long_name, + cdata->cuda_device_id); + + gst_element_class_add_metadata (element_class, + GST_ELEMENT_METADATA_LONGNAME, new_long_name); + g_free (new_long_name); + } + + + 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)); + + nvbaseenc_class->cuda_device_id = cdata->cuda_device_id; + + gst_caps_unref (cdata->sink_caps); + gst_caps_unref (cdata->src_caps); + g_free (cdata); +} + +void +gst_nv_base_enc_register (GstPlugin * plugin, GType type, const char *codec, + guint device_id, guint rank, GstCaps * sink_caps, GstCaps * src_caps) +{ + GTypeQuery type_query; + GTypeInfo type_info = { 0, }; + GType subtype; + gchar *type_name; + GstNvEncClassData *cdata; + + cdata = g_new0 (GstNvEncClassData, 1); + cdata->sink_caps = gst_caps_ref (sink_caps); + cdata->src_caps = gst_caps_ref (src_caps); + cdata->cuda_device_id = device_id; + cdata->is_default = TRUE; + + g_type_query (type, &type_query); + memset (&type_info, 0, sizeof (type_info)); + type_info.class_size = type_query.class_size; + type_info.instance_size = type_query.instance_size; + type_info.class_init = gst_nv_base_enc_subclass_init; + type_info.class_data = cdata; + + type_name = g_strdup_printf ("nv%senc", codec); + + if (g_type_from_name (type_name) != 0) { + g_free (type_name); + type_name = g_strdup_printf ("nv%sdevice%denc", codec, device_id); + cdata->is_default = FALSE; + } + + subtype = g_type_register_static (type, type_name, &type_info, 0); + + /* make lower rank than default device */ + if (!gst_element_register (plugin, type_name, rank - 1, subtype)) + GST_WARNING ("Failed to register plugin '%s'", type_name); + + g_free (type_name); +} diff --git a/sys/nvcodec/gstnvbaseenc.h b/sys/nvcodec/gstnvbaseenc.h index 62ee7dfde3..6ef0fcdf50 100644 --- a/sys/nvcodec/gstnvbaseenc.h +++ b/sys/nvcodec/gstnvbaseenc.h @@ -61,7 +61,6 @@ typedef struct { GstVideoEncoder video_encoder; /* properties */ - guint cuda_device_id; GstNvPreset preset_enum; GUID selected_preset; GstNvRCMode rate_control_mode; @@ -118,6 +117,7 @@ typedef struct { GstVideoEncoderClass video_encoder_class; GUID codec_id; + guint cuda_device_id; gboolean (*set_src_caps) (GstNvBaseEnc * nvenc, GstVideoCodecState * state); @@ -140,4 +140,13 @@ void gst_nv_base_enc_set_max_encode_size (GstNvBaseEnc * nvenc, guint max_width, guint max_height); +void gst_nv_base_enc_register (GstPlugin * plugin, + GType type, + const char * codec, + guint device_id, + guint rank, + GstCaps * sink_caps, + GstCaps * src_caps); + + #endif /* __GST_NV_BASE_ENC_H_INCLUDED__ */ diff --git a/sys/nvcodec/gstnvenc.c b/sys/nvcodec/gstnvenc.c index 6a4f38d528..17b9a0397c 100644 --- a/sys/nvcodec/gstnvenc.c +++ b/sys/nvcodec/gstnvenc.c @@ -26,6 +26,10 @@ #include "gstnvh265enc.h" #include +#if HAVE_NVCODEC_GST_GL +#include +#endif + #ifdef _WIN32 #ifdef _WIN64 #define NVENC_LIBRARY_NAME "nvEncodeAPI64.dll" @@ -264,6 +268,9 @@ gst_nvenc_get_nv_buffer_format (GstVideoFormat fmt) return NV_ENC_BUFFER_FORMAT_IYUV_PL; case GST_VIDEO_FORMAT_Y444: return NV_ENC_BUFFER_FORMAT_YUV444_PL; + case GST_VIDEO_FORMAT_P010_10LE: + case GST_VIDEO_FORMAT_P010_10BE: + return NV_ENC_BUFFER_FORMAT_YUV420_10BIT; default: break; } @@ -362,6 +369,421 @@ load_nvenc_library (void) return TRUE; } +typedef struct +{ + GstVideoFormat gst_format; + NV_ENC_BUFFER_FORMAT nv_format; + gboolean is_10bit; + + gboolean supported; +} GstNvEncFormat; + +gboolean +gst_nv_enc_get_supported_input_formats (gpointer encoder, GUID codec_id, + GValue ** formats) +{ + guint32 i, count = 0; + NV_ENC_BUFFER_FORMAT format_list[64]; + GValue val = G_VALUE_INIT; + GValue *ret = NULL; + NV_ENC_CAPS_PARAM param = { 0, }; + gint support_yuv444 = 0; + gint support_10bit = 0; + guint num_format = 0; + GstNvEncFormat format_map[] = { + {GST_VIDEO_FORMAT_NV12, NV_ENC_BUFFER_FORMAT_NV12, FALSE, FALSE}, + {GST_VIDEO_FORMAT_YV12, NV_ENC_BUFFER_FORMAT_YV12, FALSE, FALSE}, + {GST_VIDEO_FORMAT_I420, NV_ENC_BUFFER_FORMAT_IYUV, FALSE, FALSE}, + {GST_VIDEO_FORMAT_Y444, NV_ENC_BUFFER_FORMAT_YUV444, FALSE, FALSE}, +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + {GST_VIDEO_FORMAT_P010_10LE, NV_ENC_BUFFER_FORMAT_YUV420_10BIT, TRUE, + FALSE}, +#else + {GST_VIDEO_FORMAT_P010_10BE, NV_ENC_BUFFER_FORMAT_YUV420_10BIT, TRUE, + FALSE}, +#endif + }; + + param.version = NV_ENC_CAPS_PARAM_VER; + param.capsToQuery = NV_ENC_CAPS_SUPPORT_YUV444_ENCODE; + if (NvEncGetEncodeCaps (encoder, + codec_id, ¶m, &support_yuv444) != NV_ENC_SUCCESS) { + support_yuv444 = 0; + } + + param.capsToQuery = NV_ENC_CAPS_SUPPORT_10BIT_ENCODE; + if (NvEncGetEncodeCaps (encoder, + codec_id, ¶m, &support_10bit) != NV_ENC_SUCCESS) { + support_10bit = 0; + } + + if (NvEncGetInputFormats (encoder, + codec_id, format_list, G_N_ELEMENTS (format_list), + &count) != NV_ENC_SUCCESS || count == 0) { + return FALSE; + } + + for (i = 0; i < count; i++) { + GST_INFO ("input format: 0x%08x", format_list[i]); + switch (format_list[i]) { + case NV_ENC_BUFFER_FORMAT_NV12: + if (!format_map[0].supported) { + format_map[0].supported = TRUE; + num_format++; + } + break; + case NV_ENC_BUFFER_FORMAT_YV12: + if (!format_map[1].supported) { + format_map[0].supported = TRUE; + num_format++; + } + break; + case NV_ENC_BUFFER_FORMAT_IYUV: + if (!format_map[2].supported) { + format_map[2].supported = TRUE; + num_format++; + } + break; + case NV_ENC_BUFFER_FORMAT_YUV444: + if (support_yuv444 && !format_map[3].supported) { + format_map[3].supported = TRUE; + num_format++; + } + break; + case NV_ENC_BUFFER_FORMAT_YUV420_10BIT: + if (support_yuv444 && support_10bit && !format_map[4].supported) { + format_map[4].supported = TRUE; + num_format++; + } + break; + default: + GST_FIXME ("unmapped input format: 0x%08x", format_list[i]); + break; + } + } + + if (num_format == 0) + return FALSE; + + /* process a second time so we can add formats in the order we want */ + g_value_init (&val, G_TYPE_STRING); + ret = g_new0 (GValue, 1); + g_value_init (ret, GST_TYPE_LIST); + + for (i = 0; i < G_N_ELEMENTS (format_map); i++) { + if (!format_map[i].supported) + continue; + + g_value_set_static_string (&val, + gst_video_format_to_string (format_map[i].gst_format)); + + gst_value_list_append_value (ret, &val); + } + + g_value_unset (&val); + + *formats = ret; + + return TRUE; +} + +GValue * +gst_nv_enc_get_interlace_modes (gpointer enc, GUID codec_id) +{ + NV_ENC_CAPS_PARAM caps_param = { 0, }; + GValue *list; + GValue val = G_VALUE_INIT; + gint interlace_modes = 0; + + caps_param.version = NV_ENC_CAPS_PARAM_VER; + caps_param.capsToQuery = NV_ENC_CAPS_SUPPORT_FIELD_ENCODING; + + if (NvEncGetEncodeCaps (enc, codec_id, &caps_param, + &interlace_modes) != NV_ENC_SUCCESS) + interlace_modes = 0; + + list = g_new0 (GValue, 1); + + g_value_init (list, GST_TYPE_LIST); + g_value_init (&val, G_TYPE_STRING); + + g_value_set_static_string (&val, "progressive"); + gst_value_list_append_value (list, &val); + + if (interlace_modes == 0) + return list; + + if (interlace_modes >= 1) { + g_value_set_static_string (&val, "interleaved"); + gst_value_list_append_value (list, &val); + g_value_set_static_string (&val, "mixed"); + gst_value_list_append_value (list, &val); + g_value_unset (&val); + } + /* TODO: figure out what nvenc frame based interlacing means in gst terms */ + + return list; +} + +typedef struct +{ + const gchar *gst_profile; + const GUID nv_profile; + const GUID codec_id; + const gboolean need_yuv444; + const gboolean need_10bit; + + gboolean supported; +} GstNvEncCodecProfile; + +GValue * +gst_nv_enc_get_supported_codec_profiles (gpointer enc, GUID codec_id) +{ + NVENCSTATUS nv_ret; + GUID profile_guids[64]; + GValue *ret; + GValue val = G_VALUE_INIT; + guint i, j, n, n_profiles; + NV_ENC_CAPS_PARAM param = { 0, }; + gint support_yuv444 = 0; + gint support_10bit = 0; + GstNvEncCodecProfile profiles[] = { + /* avc profiles */ + {"baseline", NV_ENC_H264_PROFILE_BASELINE_GUID, NV_ENC_CODEC_H264_GUID, + FALSE, FALSE, FALSE}, + {"main", NV_ENC_H264_PROFILE_MAIN_GUID, NV_ENC_CODEC_H264_GUID, FALSE, + FALSE, FALSE}, + {"high", NV_ENC_H264_PROFILE_HIGH_GUID, NV_ENC_CODEC_H264_GUID, FALSE, + FALSE, FALSE}, + {"high-4:4:4", NV_ENC_H264_PROFILE_HIGH_444_GUID, NV_ENC_CODEC_H264_GUID, + TRUE, FALSE, FALSE}, + /* hevc profiles */ + {"main", NV_ENC_HEVC_PROFILE_MAIN_GUID, NV_ENC_CODEC_HEVC_GUID, FALSE, + FALSE, FALSE}, + {"main-10", NV_ENC_HEVC_PROFILE_MAIN10_GUID, NV_ENC_CODEC_HEVC_GUID, FALSE, + TRUE, FALSE}, + {"main-444", NV_ENC_HEVC_PROFILE_FREXT_GUID, NV_ENC_CODEC_HEVC_GUID, TRUE, + FALSE, FALSE}, +#if 0 + /* FIXME: seems to unsupported format */ + {"main-444-10", NV_ENC_HEVC_PROFILE_FREXT_GUID, FALSE} +#endif + }; + + param.version = NV_ENC_CAPS_PARAM_VER; + param.capsToQuery = NV_ENC_CAPS_SUPPORT_YUV444_ENCODE; + if (NvEncGetEncodeCaps (enc, + codec_id, ¶m, &support_yuv444) != NV_ENC_SUCCESS) { + support_yuv444 = 0; + } + + param.capsToQuery = NV_ENC_CAPS_SUPPORT_10BIT_ENCODE; + if (NvEncGetEncodeCaps (enc, + codec_id, ¶m, &support_10bit) != NV_ENC_SUCCESS) { + support_10bit = 0; + } + + nv_ret = NvEncGetEncodeProfileGUIDCount (enc, codec_id, &n); + + if (nv_ret != NV_ENC_SUCCESS) + return NULL; + + nv_ret = NvEncGetEncodeProfileGUIDs (enc, + codec_id, profile_guids, G_N_ELEMENTS (profile_guids), &n); + + if (nv_ret != NV_ENC_SUCCESS) + return NULL; + + n_profiles = 0; + + for (i = 0; i < n; i++) { + for (j = 0; j < G_N_ELEMENTS (profiles); j++) { + if (profiles[j].supported == FALSE && + gst_nvenc_cmp_guid (profile_guids[i], profiles[j].nv_profile) && + gst_nvenc_cmp_guid (codec_id, profiles[j].codec_id)) { + if (profiles[j].need_yuv444 && !support_yuv444) + continue; + + if (profiles[j].need_10bit && !support_10bit) + continue; + + profiles[j].supported = TRUE; + n_profiles++; + } + } + } + + if (n_profiles == 0) + return NULL; + + ret = g_new0 (GValue, 1); + + g_value_init (ret, GST_TYPE_LIST); + g_value_init (&val, G_TYPE_STRING); + + for (i = 0; i < G_N_ELEMENTS (profiles); i++) { + if (!profiles[i].supported) + continue; + + g_value_set_static_string (&val, profiles[i].gst_profile); + gst_value_list_append_value (ret, &val); + } + + g_value_unset (&val); + + return ret; +} + +static gboolean +gst_nv_enc_register (GstPlugin * plugin, GType type, GUID codec_id, + const gchar * codec, guint rank, gint device_count) +{ + gint i; + + for (i = 0; i < device_count; i++) { + CUdevice cuda_device; + CUcontext cuda_ctx, dummy; + GValue *formats = NULL; + GValue *profiles; + GValue *interlace_modes; + gpointer enc; + NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS params = { 0, }; + NV_ENC_CAPS_PARAM caps_param = { 0, }; + GUID guids[16]; + guint32 count; + gint max_width = 0; + gint max_height = 0; + GstCaps *sink_templ = NULL; + GstCaps *src_templ = NULL; + gchar *name; + gint j; + + if (CuDeviceGet (&cuda_device, i) != CUDA_SUCCESS) + continue; + + if (CuCtxCreate (&cuda_ctx, 0, cuda_device) != CUDA_SUCCESS) + continue; + + if (CuCtxPopCurrent (&dummy) != CUDA_SUCCESS) { + goto cuda_free; + } + + params.version = NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER; + params.apiVersion = NVENCAPI_VERSION; + params.device = cuda_ctx; + params.deviceType = NV_ENC_DEVICE_TYPE_CUDA; + + if (NvEncOpenEncodeSessionEx (¶ms, &enc) != NV_ENC_SUCCESS) { + goto cuda_free; + } + + if (NvEncGetEncodeGUIDs (enc, guids, G_N_ELEMENTS (guids), + &count) != NV_ENC_SUCCESS) { + goto enc_free; + } + + for (j = 0; j < count; j++) { + if (gst_nvenc_cmp_guid (guids[j], codec_id)) + break; + } + + if (j == count) + goto enc_free; + + if (!gst_nv_enc_get_supported_input_formats (enc, codec_id, &formats)) + goto enc_free; + + profiles = gst_nv_enc_get_supported_codec_profiles (enc, codec_id); + if (!profiles) + goto free_format; + + caps_param.version = NV_ENC_CAPS_PARAM_VER; + caps_param.capsToQuery = NV_ENC_CAPS_WIDTH_MAX; + if (NvEncGetEncodeCaps (enc, + codec_id, &caps_param, &max_width) != NV_ENC_SUCCESS) { + GST_WARNING ("could not query max width"); + max_width = 4096; + } else if (max_width < 4096) { + GST_WARNING ("max width %d is less than expected value", max_width); + max_width = 4096; + } + + caps_param.capsToQuery = NV_ENC_CAPS_HEIGHT_MAX; + if (NvEncGetEncodeCaps (enc, + codec_id, &caps_param, &max_height) != NV_ENC_SUCCESS) { + GST_WARNING ("could not query max height"); + max_height = 4096; + } else if (max_height < 4096) { + GST_WARNING ("max height %d is less than expected value", max_height); + max_height = 4096; + } + + interlace_modes = gst_nv_enc_get_interlace_modes (enc, codec_id); + + sink_templ = gst_caps_new_empty_simple ("video/x-raw"); + gst_caps_set_value (sink_templ, "format", formats); + + gst_caps_set_simple (sink_templ, + "width", GST_TYPE_INT_RANGE, 16, max_width, + "height", GST_TYPE_INT_RANGE, 16, max_height, + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL); + + if (interlace_modes) { + gst_caps_set_value (sink_templ, "interlace-mode", interlace_modes); + g_value_unset (interlace_modes); + g_free (interlace_modes); + } +#if HAVE_NVCODEC_GST_GL + { + GstCaps *gl_caps = gst_caps_copy (sink_templ); + gst_caps_set_features_simple (gl_caps, + gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_GL_MEMORY)); + gst_caps_append (sink_templ, gl_caps); + } +#endif + + name = g_strdup_printf ("video/x-%s", codec); + src_templ = gst_caps_new_simple (name, + "width", GST_TYPE_INT_RANGE, 16, max_width, + "height", GST_TYPE_INT_RANGE, 16, max_height, + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, + "stream-format", G_TYPE_STRING, "byte-stream", + "alignment", G_TYPE_STRING, "au", NULL); + gst_caps_set_value (src_templ, "profile", profiles); + g_free (name); + + GST_DEBUG ("sink template caps %" GST_PTR_FORMAT, sink_templ); + GST_DEBUG ("src template caps %" GST_PTR_FORMAT, src_templ); + + g_value_unset (profiles); + g_free (profiles); + + free_format: + if (formats) { + g_value_unset (formats); + g_free (formats); + } + /* fall-through */ + + enc_free: + NvEncDestroyEncoder (enc); + /* fall-through */ + + cuda_free: + CuCtxDestroy (cuda_ctx); + + if (sink_templ && src_templ) + gst_nv_base_enc_register (plugin, type, codec, i, rank, sink_templ, + src_templ); + + gst_clear_caps (&sink_templ); + gst_clear_caps (&src_templ); + } + + return TRUE; +} + + gboolean gst_nvenc_plugin_init (GstPlugin * plugin) { @@ -370,18 +792,38 @@ gst_nvenc_plugin_init (GstPlugin * plugin) GST_DEBUG_CATEGORY_INIT (gst_nvenc_debug, "nvenc", 0, "Nvidia NVENC encoder"); nvenc_api.version = NV_ENCODE_API_FUNCTION_LIST_VER; - if (!load_nvenc_library ()) + if (!load_nvenc_library ()) { + GST_INFO ("Failed to load nvenc library"); return TRUE; + } if (nvEncodeAPICreateInstance (&nvenc_api) != NV_ENC_SUCCESS) { GST_ERROR ("Failed to get NVEncodeAPI function table!"); } else { + CUresult cuda_ret; + gint dev_count = 0; + GST_INFO ("Created NVEncodeAPI instance, got function table"); - ret &= gst_element_register (plugin, "nvh264enc", GST_RANK_PRIMARY * 2, - gst_nv_h264_enc_get_type ()); - ret &= gst_element_register (plugin, "nvh265enc", GST_RANK_PRIMARY * 2, - gst_nv_h265_enc_get_type ()); + cuda_ret = CuInit (0); + if (cuda_ret != CUDA_SUCCESS) { + GST_ERROR ("Failed to initialize CUDA API"); + return TRUE; + } + + cuda_ret = CuDeviceGetCount (&dev_count); + if (cuda_ret != CUDA_SUCCESS || dev_count == 0) { + GST_ERROR ("No CUDA devices detected"); + return TRUE; + } + + ret &= + gst_nv_enc_register (plugin, GST_TYPE_NV_H264_ENC, + NV_ENC_CODEC_H264_GUID, "h264", GST_RANK_PRIMARY * 2, dev_count); + ret &= + gst_nv_enc_register (plugin, GST_TYPE_NV_H265_ENC, + NV_ENC_CODEC_HEVC_GUID, "h265", GST_RANK_PRIMARY * 2, dev_count); + } return ret; diff --git a/sys/nvcodec/gstnvenc.h b/sys/nvcodec/gstnvenc.h index 71b4384ce4..07e0920b75 100644 --- a/sys/nvcodec/gstnvenc.h +++ b/sys/nvcodec/gstnvenc.h @@ -36,6 +36,18 @@ gboolean gst_nvenc_cmp_guid (GUID g1, GUID g2); NV_ENC_BUFFER_FORMAT gst_nvenc_get_nv_buffer_format (GstVideoFormat fmt); +gboolean gst_nv_enc_get_supported_input_formats (gpointer encoder, + GUID codec_id, + GValue ** formats); + + +GValue * gst_nv_enc_get_interlace_modes (gpointer enc, + GUID codec_id); + +GValue * gst_nv_enc_get_supported_codec_profiles (gpointer enc, + GUID codec_id); + + gboolean gst_nvenc_plugin_init (GstPlugin * plugin); diff --git a/sys/nvcodec/gstnvh264enc.c b/sys/nvcodec/gstnvh264enc.c index 4badf9931c..d21f9b22d0 100644 --- a/sys/nvcodec/gstnvh264enc.c +++ b/sys/nvcodec/gstnvh264enc.c @@ -33,45 +33,8 @@ GST_DEBUG_CATEGORY_STATIC (gst_nv_h264_enc_debug); #define parent_class gst_nv_h264_enc_parent_class G_DEFINE_TYPE (GstNvH264Enc, gst_nv_h264_enc, GST_TYPE_NV_BASE_ENC); -#if HAVE_NVCODEC_GST_GL -#define GL_CAPS_STR \ - ";" \ - "video/x-raw(memory:GLMemory), " \ - "format = (string) { NV12, Y444 }, " \ - "width = (int) [ 16, 4096 ], height = (int) [ 16, 4096 ], " \ - "framerate = (fraction) [0, MAX]," \ - "interlace-mode = { progressive, mixed, interleaved } " -#else -#define GL_CAPS_STR "" -#endif - -/* *INDENT-OFF* */ -static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("video/x-raw, " "format = (string) { NV12, I420, Y444 }, " - "width = (int) [ 16, 4096 ], height = (int) [ 16, 4096 ], " - "framerate = (fraction) [0, MAX]," - "interlace-mode = { progressive, mixed, interleaved } " - GL_CAPS_STR - )); - -static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("video/x-h264, " - "width = (int) [ 1, 4096 ], height = (int) [ 1, 4096 ], " - "framerate = (fraction) [0/1, MAX], " - "stream-format = (string) byte-stream, " // TODO: avc support - "alignment = (string) au, " - "profile = (string) { high, main, baseline }") // TODO: a couple of others - ); -/* *INDENT-ON* */ - static gboolean gst_nv_h264_enc_open (GstVideoEncoder * enc); static gboolean gst_nv_h264_enc_close (GstVideoEncoder * enc); -static GstCaps *gst_nv_h264_enc_getcaps (GstVideoEncoder * enc, - GstCaps * filter); static gboolean gst_nv_h264_enc_set_src_caps (GstNvBaseEnc * nvenc, GstVideoCodecState * state); static gboolean gst_nv_h264_enc_set_encoder_config (GstNvBaseEnc * nvenc, @@ -99,22 +62,18 @@ gst_nv_h264_enc_class_init (GstNvH264EncClass * klass) videoenc_class->open = GST_DEBUG_FUNCPTR (gst_nv_h264_enc_open); videoenc_class->close = GST_DEBUG_FUNCPTR (gst_nv_h264_enc_close); - videoenc_class->getcaps = GST_DEBUG_FUNCPTR (gst_nv_h264_enc_getcaps); - nvenc_class->codec_id = NV_ENC_CODEC_H264_GUID; nvenc_class->set_encoder_config = gst_nv_h264_enc_set_encoder_config; nvenc_class->set_src_caps = gst_nv_h264_enc_set_src_caps; nvenc_class->set_pic_params = gst_nv_h264_enc_set_pic_params; - gst_element_class_add_static_pad_template (element_class, &sink_factory); - gst_element_class_add_static_pad_template (element_class, &src_factory); - gst_element_class_set_static_metadata (element_class, "NVENC H.264 Video Encoder", "Codec/Encoder/Video/Hardware", "Encode H.264 video streams using NVIDIA's hardware-accelerated NVENC encoder API", - "Tim-Philipp Müller \n" - "Matthew Waters "); + "Tim-Philipp Müller , " + "Matthew Waters , " + "Seungha Yang "); GST_DEBUG_CATEGORY_INIT (gst_nv_h264_enc_debug, "nvh264enc", 0, "Nvidia H.264 encoder"); @@ -131,71 +90,11 @@ gst_nv_h264_enc_finalize (GObject * obj) G_OBJECT_CLASS (gst_nv_h264_enc_parent_class)->finalize (obj); } -static gboolean -_get_supported_profiles (GstNvH264Enc * nvenc) -{ - NVENCSTATUS nv_ret; - GUID profile_guids[64]; - GValue list = G_VALUE_INIT; - GValue val = G_VALUE_INIT; - guint i, n, n_profiles; - - if (nvenc->supported_profiles) - return TRUE; - - nv_ret = - NvEncGetEncodeProfileGUIDCount (GST_NV_BASE_ENC (nvenc)->encoder, - NV_ENC_CODEC_H264_GUID, &n); - if (nv_ret != NV_ENC_SUCCESS) - return FALSE; - - nv_ret = - NvEncGetEncodeProfileGUIDs (GST_NV_BASE_ENC (nvenc)->encoder, - NV_ENC_CODEC_H264_GUID, profile_guids, G_N_ELEMENTS (profile_guids), &n); - if (nv_ret != NV_ENC_SUCCESS) - return FALSE; - - n_profiles = 0; - g_value_init (&list, GST_TYPE_LIST); - for (i = 0; i < n; i++) { - g_value_init (&val, G_TYPE_STRING); - - if (gst_nvenc_cmp_guid (profile_guids[i], - NV_ENC_H264_PROFILE_BASELINE_GUID)) { - g_value_set_static_string (&val, "baseline"); - gst_value_list_append_value (&list, &val); - n_profiles++; - } else if (gst_nvenc_cmp_guid (profile_guids[i], - NV_ENC_H264_PROFILE_MAIN_GUID)) { - g_value_set_static_string (&val, "main"); - gst_value_list_append_value (&list, &val); - n_profiles++; - } else if (gst_nvenc_cmp_guid (profile_guids[i], - NV_ENC_H264_PROFILE_HIGH_GUID)) { - g_value_set_static_string (&val, "high"); - gst_value_list_append_value (&list, &val); - n_profiles++; - } - /* TODO: map HIGH_444, STEREO, CONSTRAINED_HIGH, SVC_TEMPORAL_SCALABILITY */ - - g_value_unset (&val); - } - - if (n_profiles == 0) - return FALSE; - - GST_OBJECT_LOCK (nvenc); - nvenc->supported_profiles = g_new0 (GValue, 1); - *nvenc->supported_profiles = list; - GST_OBJECT_UNLOCK (nvenc); - - return TRUE; -} - static gboolean gst_nv_h264_enc_open (GstVideoEncoder * enc) { GstNvH264Enc *nvenc = GST_NV_H264_ENC (enc); + GstNvBaseEnc *base = GST_NV_BASE_ENC (enc); if (!GST_VIDEO_ENCODER_CLASS (gst_nv_h264_enc_parent_class)->open (enc)) return FALSE; @@ -205,8 +104,7 @@ gst_nv_h264_enc_open (GstVideoEncoder * enc) uint32_t i, num = 0; GUID guids[16]; - NvEncGetEncodeGUIDs (GST_NV_BASE_ENC (nvenc)->encoder, guids, - G_N_ELEMENTS (guids), &num); + NvEncGetEncodeGUIDs (base->encoder, guids, G_N_ELEMENTS (guids), &num); for (i = 0; i < num; ++i) { if (gst_nvenc_cmp_guid (guids[i], NV_ENC_CODEC_H264_GUID)) @@ -220,7 +118,10 @@ gst_nv_h264_enc_open (GstVideoEncoder * enc) } /* query supported input formats */ - if (!_get_supported_profiles (nvenc)) { + nvenc->supported_profiles = + gst_nv_enc_get_supported_codec_profiles (base->encoder, + NV_ENC_CODEC_H264_GUID); + if (!nvenc->supported_profiles) { GST_WARNING_OBJECT (nvenc, "No supported encoding profiles"); gst_nv_h264_enc_close (enc); return FALSE; @@ -244,82 +145,6 @@ gst_nv_h264_enc_close (GstVideoEncoder * enc) return GST_VIDEO_ENCODER_CLASS (gst_nv_h264_enc_parent_class)->close (enc); } -static GValue * -_get_interlace_modes (GstNvH264Enc * nvenc) -{ - NV_ENC_CAPS_PARAM caps_param = { 0, }; - GValue *list = g_new0 (GValue, 1); - GValue val = G_VALUE_INIT; - - g_value_init (list, GST_TYPE_LIST); - g_value_init (&val, G_TYPE_STRING); - - g_value_set_static_string (&val, "progressive"); - gst_value_list_append_value (list, &val); - - caps_param.version = NV_ENC_CAPS_PARAM_VER; - caps_param.capsToQuery = NV_ENC_CAPS_SUPPORT_FIELD_ENCODING; - - if (NvEncGetEncodeCaps (GST_NV_BASE_ENC (nvenc)->encoder, - NV_ENC_CODEC_H264_GUID, &caps_param, - &nvenc->interlace_modes) != NV_ENC_SUCCESS) - nvenc->interlace_modes = 0; - - if (nvenc->interlace_modes >= 1) { - g_value_set_static_string (&val, "interleaved"); - gst_value_list_append_value (list, &val); - g_value_set_static_string (&val, "mixed"); - gst_value_list_append_value (list, &val); - g_value_unset (&val); - } - /* TODO: figure out what nvenc frame based interlacing means in gst terms */ - - return list; -} - -static GstCaps * -gst_nv_h264_enc_getcaps (GstVideoEncoder * enc, GstCaps * filter) -{ - GstNvH264Enc *nvenc = GST_NV_H264_ENC (enc); - GstCaps *supported_incaps = NULL; - GstCaps *template_caps, *caps; - GValue *input_formats = GST_NV_BASE_ENC (enc)->input_formats; - - GST_OBJECT_LOCK (nvenc); - - if (input_formats != NULL) { - GValue *val; - - template_caps = gst_pad_get_pad_template_caps (enc->sinkpad); - supported_incaps = gst_caps_copy (template_caps); - gst_caps_set_value (supported_incaps, "format", input_formats); - - val = _get_interlace_modes (nvenc); - gst_caps_set_value (supported_incaps, "interlace-mode", val); - g_value_unset (val); - g_free (val); - - GST_LOG_OBJECT (enc, "codec input caps %" GST_PTR_FORMAT, supported_incaps); - GST_LOG_OBJECT (enc, " template caps %" GST_PTR_FORMAT, template_caps); - caps = gst_caps_intersect (template_caps, supported_incaps); - gst_caps_unref (template_caps); - gst_caps_unref (supported_incaps); - supported_incaps = caps; - GST_LOG_OBJECT (enc, " supported caps %" GST_PTR_FORMAT, supported_incaps); - } - - GST_OBJECT_UNLOCK (nvenc); - - caps = gst_video_encoder_proxy_getcaps (enc, supported_incaps, filter); - - if (supported_incaps) - gst_caps_unref (supported_incaps); - - GST_DEBUG_OBJECT (nvenc, " returning caps %" GST_PTR_FORMAT, caps); - - return caps; -} - static gboolean gst_nv_h264_enc_set_profile_and_level (GstNvH264Enc * nvenc, GstCaps * caps) { @@ -450,7 +275,8 @@ gst_nv_h264_enc_set_encoder_config (GstNvBaseEnc * nvenc, NV_ENC_CONFIG_H264 *h264_config = &config->encodeCodecConfig.h264Config; NV_ENC_CONFIG_H264_VUI_PARAMETERS *vui = &h264_config->h264VUIParameters; - template_caps = gst_static_pad_template_get_caps (&src_factory); + template_caps = + gst_pad_get_pad_template_caps (GST_VIDEO_ENCODER_SRC_PAD (h264enc)); allowed_caps = gst_pad_get_allowed_caps (GST_VIDEO_ENCODER_SRC_PAD (h264enc)); if (template_caps == allowed_caps) { diff --git a/sys/nvcodec/gstnvh265enc.c b/sys/nvcodec/gstnvh265enc.c index 5b96eb0e4b..82ae4a4ccf 100644 --- a/sys/nvcodec/gstnvh265enc.c +++ b/sys/nvcodec/gstnvh265enc.c @@ -34,43 +34,8 @@ GST_DEBUG_CATEGORY_STATIC (gst_nv_h265_enc_debug); #define parent_class gst_nv_h265_enc_parent_class G_DEFINE_TYPE (GstNvH265Enc, gst_nv_h265_enc, GST_TYPE_NV_BASE_ENC); -#if HAVE_NVCODEC_GST_GL -#define GL_CAPS_STR \ - ";" \ - "video/x-raw(memory:GLMemory), " \ - "format = (string) { NV12, Y444 }, " \ - "width = (int) [ 16, 4096 ], height = (int) [ 16, 4096 ], " \ - "framerate = (fraction) [0, MAX] " -#else -#define GL_CAPS_STR "" -#endif - -/* *INDENT-OFF* */ -static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("video/x-raw, " "format = (string) { NV12, I420 }, " // TODO: YV12, Y444 support - "width = (int) [ 16, 4096 ], height = (int) [ 16, 4096 ], " - "framerate = (fraction) [0, MAX] " - GL_CAPS_STR - )); - -static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("video/x-h265, " - "width = (int) [ 1, 4096 ], height = (int) [ 1, 4096 ], " - "framerate = (fraction) [0/1, MAX], " - "stream-format = (string) byte-stream, " - "alignment = (string) au, " - "profile = (string) { main }") // TODO: a couple of others - ); -/* *INDENT-ON* */ - static gboolean gst_nv_h265_enc_open (GstVideoEncoder * enc); static gboolean gst_nv_h265_enc_close (GstVideoEncoder * enc); -static GstCaps *gst_nv_h265_enc_getcaps (GstVideoEncoder * enc, - GstCaps * filter); static gboolean gst_nv_h265_enc_set_src_caps (GstNvBaseEnc * nvenc, GstVideoCodecState * state); static gboolean gst_nv_h265_enc_set_encoder_config (GstNvBaseEnc * nvenc, @@ -98,16 +63,11 @@ gst_nv_h265_enc_class_init (GstNvH265EncClass * klass) videoenc_class->open = GST_DEBUG_FUNCPTR (gst_nv_h265_enc_open); videoenc_class->close = GST_DEBUG_FUNCPTR (gst_nv_h265_enc_close); - videoenc_class->getcaps = GST_DEBUG_FUNCPTR (gst_nv_h265_enc_getcaps); - nvenc_class->codec_id = NV_ENC_CODEC_HEVC_GUID; nvenc_class->set_encoder_config = gst_nv_h265_enc_set_encoder_config; nvenc_class->set_src_caps = gst_nv_h265_enc_set_src_caps; nvenc_class->set_pic_params = gst_nv_h265_enc_set_pic_params; - gst_element_class_add_static_pad_template (element_class, &sink_factory); - gst_element_class_add_static_pad_template (element_class, &src_factory); - gst_element_class_set_static_metadata (element_class, "NVENC HEVC Video Encoder", "Codec/Encoder/Video/Hardware", @@ -131,60 +91,11 @@ gst_nv_h265_enc_finalize (GObject * obj) G_OBJECT_CLASS (gst_nv_h265_enc_parent_class)->finalize (obj); } -static gboolean -_get_supported_profiles (GstNvH265Enc * nvenc) -{ - NVENCSTATUS nv_ret; - GUID profile_guids[64]; - GValue list = G_VALUE_INIT; - GValue val = G_VALUE_INIT; - guint i, n, n_profiles; - - if (nvenc->supported_profiles) - return TRUE; - - nv_ret = - NvEncGetEncodeProfileGUIDCount (GST_NV_BASE_ENC (nvenc)->encoder, - NV_ENC_CODEC_HEVC_GUID, &n); - if (nv_ret != NV_ENC_SUCCESS) - return FALSE; - - nv_ret = - NvEncGetEncodeProfileGUIDs (GST_NV_BASE_ENC (nvenc)->encoder, - NV_ENC_CODEC_HEVC_GUID, profile_guids, G_N_ELEMENTS (profile_guids), &n); - if (nv_ret != NV_ENC_SUCCESS) - return FALSE; - - n_profiles = 0; - g_value_init (&list, GST_TYPE_LIST); - for (i = 0; i < n; i++) { - g_value_init (&val, G_TYPE_STRING); - - if (gst_nvenc_cmp_guid (profile_guids[i], NV_ENC_HEVC_PROFILE_MAIN_GUID)) { - g_value_set_static_string (&val, "main"); - gst_value_list_append_value (&list, &val); - n_profiles++; - } - /* TODO: map MAIN10, FREXT */ - - g_value_unset (&val); - } - - if (n_profiles == 0) - return FALSE; - - GST_OBJECT_LOCK (nvenc); - nvenc->supported_profiles = g_new0 (GValue, 1); - *nvenc->supported_profiles = list; - GST_OBJECT_UNLOCK (nvenc); - - return TRUE; -} - static gboolean gst_nv_h265_enc_open (GstVideoEncoder * enc) { GstNvH265Enc *nvenc = GST_NV_H265_ENC (enc); + GstNvBaseEnc *base = GST_NV_BASE_ENC (enc); if (!GST_VIDEO_ENCODER_CLASS (gst_nv_h265_enc_parent_class)->open (enc)) return FALSE; @@ -194,8 +105,7 @@ gst_nv_h265_enc_open (GstVideoEncoder * enc) uint32_t i, num = 0; GUID guids[16]; - NvEncGetEncodeGUIDs (GST_NV_BASE_ENC (nvenc)->encoder, guids, - G_N_ELEMENTS (guids), &num); + NvEncGetEncodeGUIDs (base->encoder, guids, G_N_ELEMENTS (guids), &num); for (i = 0; i < num; ++i) { if (gst_nvenc_cmp_guid (guids[i], NV_ENC_CODEC_HEVC_GUID)) @@ -209,7 +119,10 @@ gst_nv_h265_enc_open (GstVideoEncoder * enc) } /* query supported input formats */ - if (!_get_supported_profiles (nvenc)) { + nvenc->supported_profiles = + gst_nv_enc_get_supported_codec_profiles (base->encoder, + NV_ENC_CODEC_HEVC_GUID); + if (!nvenc->supported_profiles) { GST_WARNING_OBJECT (nvenc, "No supported encoding profiles"); gst_nv_h265_enc_close (enc); return FALSE; @@ -233,42 +146,6 @@ gst_nv_h265_enc_close (GstVideoEncoder * enc) return GST_VIDEO_ENCODER_CLASS (gst_nv_h265_enc_parent_class)->close (enc); } -static GstCaps * -gst_nv_h265_enc_getcaps (GstVideoEncoder * enc, GstCaps * filter) -{ - GstNvH265Enc *nvenc = GST_NV_H265_ENC (enc); - GstCaps *supported_incaps = NULL; - GstCaps *template_caps, *caps; - GValue *input_formats = GST_NV_BASE_ENC (enc)->input_formats; - - GST_OBJECT_LOCK (nvenc); - - if (input_formats != NULL) { - template_caps = gst_pad_get_pad_template_caps (enc->sinkpad); - supported_incaps = gst_caps_copy (template_caps); - gst_caps_set_value (supported_incaps, "format", input_formats); - - GST_LOG_OBJECT (enc, "codec input caps %" GST_PTR_FORMAT, supported_incaps); - GST_LOG_OBJECT (enc, " template caps %" GST_PTR_FORMAT, template_caps); - caps = gst_caps_intersect (template_caps, supported_incaps); - gst_caps_unref (template_caps); - gst_caps_unref (supported_incaps); - supported_incaps = caps; - GST_LOG_OBJECT (enc, " supported caps %" GST_PTR_FORMAT, supported_incaps); - } - - GST_OBJECT_UNLOCK (nvenc); - - caps = gst_video_encoder_proxy_getcaps (enc, supported_incaps, filter); - - if (supported_incaps) - gst_caps_unref (supported_incaps); - - GST_DEBUG_OBJECT (nvenc, " returning caps %" GST_PTR_FORMAT, caps); - - return caps; -} - static gboolean gst_nv_h265_enc_set_level_tier_and_profile (GstNvH265Enc * nvenc, GstCaps * caps) @@ -353,7 +230,8 @@ gst_nv_h265_enc_set_encoder_config (GstNvBaseEnc * nvenc, NV_ENC_CONFIG_HEVC *hevc_config = &config->encodeCodecConfig.hevcConfig; NV_ENC_CONFIG_HEVC_VUI_PARAMETERS *vui = &hevc_config->hevcVUIParameters; - template_caps = gst_static_pad_template_get_caps (&src_factory); + template_caps = + gst_pad_get_pad_template_caps (GST_VIDEO_ENCODER_SRC_PAD (h265enc)); allowed_caps = gst_pad_get_allowed_caps (GST_VIDEO_ENCODER_SRC_PAD (h265enc)); if (template_caps == allowed_caps) { @@ -378,6 +256,10 @@ gst_nv_h265_enc_set_encoder_config (GstNvBaseEnc * nvenc, if (profile) { if (!strcmp (profile, "main")) { selected_profile = NV_ENC_HEVC_PROFILE_MAIN_GUID; + } else if (g_str_has_prefix (profile, "main-10")) { + selected_profile = NV_ENC_HEVC_PROFILE_MAIN10_GUID; + } else if (g_str_has_prefix (profile, "main-444")) { + selected_profile = NV_ENC_HEVC_PROFILE_FREXT_GUID; } else { g_assert_not_reached (); } @@ -398,6 +280,20 @@ gst_nv_h265_enc_set_encoder_config (GstNvBaseEnc * nvenc, hevc_config->level = level_idc; hevc_config->idrPeriod = config->gopLength; + config->encodeCodecConfig.hevcConfig.chromaFormatIDC = 1; + if (GST_VIDEO_INFO_FORMAT (info) == GST_VIDEO_FORMAT_Y444) { + GST_DEBUG_OBJECT (h265enc, "have Y444 input, setting config accordingly"); + config->profileGUID = NV_ENC_HEVC_PROFILE_FREXT_GUID; + config->encodeCodecConfig.hevcConfig.chromaFormatIDC = 3; +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + } else if (GST_VIDEO_INFO_FORMAT (info) == GST_VIDEO_FORMAT_P010_10LE) { +#else + } else if (GST_VIDEO_INFO_FORMAT (info) == GST_VIDEO_FORMAT_P010_10BE) { +#endif + config->profileGUID = NV_ENC_HEVC_PROFILE_MAIN10_GUID; + config->encodeCodecConfig.hevcConfig.pixelBitDepthMinus8 = 2; + } + /* FIXME: make property */ hevc_config->outputAUD = 1;