Commit 92afa749 authored by Seungha Yang's avatar Seungha Yang Committed by Sebastian Dröge

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.
parent 0239152b
Pipeline #50448 passed with stages
in 47 minutes and 59 seconds
......@@ -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);
}
......@@ -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__ */
......@@ -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;