nvenc: Register elements per GPU device with capability check

* By this commit, if there are more than one device,
nvenc element factory will be created per
device like nvh264device{device-id}enc and nvh265device{device-id}enc
in addition to nvh264enc and nvh265enc, so that the element factory
can expose the exact capability of the device for the codec.

* Each element factory will have fixed cuda-device-id
which is determined during plugin initialization
depending on the capability of corresponding device.
(e.g., when only the second device can encode h265 among two GPU,
then nvh265enc will choose "1" (zero-based numbering)
as it's target cuda-device-id. As we have element factory
per GPU device, "cuda-device-id" property is changed to read-only.

* nvh265enc gains ability to encoding
4:4:4 8bits, 4:2:0 10 bits formats and up to 8K resolution
depending on device capability.
Additionally, I420 GLMemory input is supported by nvenc.
This commit is contained in:
Seungha Yang 2019-07-09 13:31:27 +09:00 committed by Sebastian Dröge
parent 0239152bca
commit 92afa74939
6 changed files with 626 additions and 511 deletions

View file

@ -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 (&param);
@ -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);
}

View file

@ -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__ */

View file

@ -26,6 +26,10 @@
#include "gstnvh265enc.h"
#include <gmodule.h>
#if HAVE_NVCODEC_GST_GL
#include <gst/gl/gl.h>
#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, &param, &support_yuv444) != NV_ENC_SUCCESS) {
support_yuv444 = 0;
}
param.capsToQuery = NV_ENC_CAPS_SUPPORT_10BIT_ENCODE;
if (NvEncGetEncodeCaps (encoder,
codec_id, &param, &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, &param, &support_yuv444) != NV_ENC_SUCCESS) {
support_yuv444 = 0;
}
param.capsToQuery = NV_ENC_CAPS_SUPPORT_10BIT_ENCODE;
if (NvEncGetEncodeCaps (enc,
codec_id, &param, &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 (&params, &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;

View file

@ -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);

View file

@ -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 <tim@centricular.com>\n"
"Matthew Waters <matthew@centricular.com>");
"Tim-Philipp Müller <tim@centricular.com>, "
"Matthew Waters <matthew@centricular.com>, "
"Seungha Yang <seungha.yang@navercorp.com>");
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) {

View file

@ -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;