gstreamer/sys/nvcodec/gstnvenc.c
Seungha Yang b4efdeba11 nvdec: Don't hardcode DPB size
Too many decode surface would waste GPU memory. Also it seems to be
introducing additional latency depending on stream. Since nvcodec
sdk version 9.0, CUVID parser API has been providing the minimum
required number of surface. By using it, we can save GPU memory
and reduce possible latency.
2020-04-09 16:30:58 +09:00

1118 lines
35 KiB
C

/* GStreamer NVENC plugin
* Copyright (C) 2015 Centricular Ltd
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstnvenc.h"
#include "gstnvh264enc.h"
#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"
#else
#define NVENC_LIBRARY_NAME "nvEncodeAPI.dll"
#endif
#else
#define NVENC_LIBRARY_NAME "libnvidia-encode.so.1"
#endif
/* For backward compatibility */
#define GST_NVENC_MIN_API_MAJOR_VERSION 8
#define GST_NVENC_MIN_API_MINOR_VERSION 1
#define GST_NVENCAPI_VERSION(major,minor) ((major) | ((minor) << 24))
#define GST_NVENCAPI_STRUCT_VERSION(ver,api_ver) ((uint32_t)(api_ver) | ((ver)<<16) | (0x7 << 28))
static guint32 gst_nvenc_api_version = NVENCAPI_VERSION;
typedef NVENCSTATUS NVENCAPI
tNvEncodeAPICreateInstance (NV_ENCODE_API_FUNCTION_LIST * functionList);
tNvEncodeAPICreateInstance *nvEncodeAPICreateInstance;
typedef NVENCSTATUS NVENCAPI
tNvEncodeAPIGetMaxSupportedVersion (uint32_t * version);
tNvEncodeAPIGetMaxSupportedVersion *nvEncodeAPIGetMaxSupportedVersion;
GST_DEBUG_CATEGORY_EXTERN (gst_nvenc_debug);
#define GST_CAT_DEFAULT gst_nvenc_debug
static NV_ENCODE_API_FUNCTION_LIST nvenc_api;
NVENCSTATUS NVENCAPI
NvEncOpenEncodeSessionEx (NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS * params,
void **encoder)
{
g_assert (nvenc_api.nvEncOpenEncodeSessionEx != NULL);
return nvenc_api.nvEncOpenEncodeSessionEx (params, encoder);
}
NVENCSTATUS NVENCAPI
NvEncDestroyEncoder (void *encoder)
{
g_assert (nvenc_api.nvEncDestroyEncoder != NULL);
return nvenc_api.nvEncDestroyEncoder (encoder);
}
NVENCSTATUS NVENCAPI
NvEncGetEncodeGUIDs (void *encoder, GUID * array, uint32_t array_size,
uint32_t * count)
{
g_assert (nvenc_api.nvEncGetEncodeGUIDs != NULL);
return nvenc_api.nvEncGetEncodeGUIDs (encoder, array, array_size, count);
}
NVENCSTATUS NVENCAPI
NvEncGetEncodeProfileGUIDCount (void *encoder, GUID encodeGUID,
uint32_t * encodeProfileGUIDCount)
{
g_assert (nvenc_api.nvEncGetEncodeProfileGUIDCount != NULL);
return nvenc_api.nvEncGetEncodeProfileGUIDCount (encoder, encodeGUID,
encodeProfileGUIDCount);
}
NVENCSTATUS NVENCAPI
NvEncGetEncodeProfileGUIDs (void *encoder, GUID encodeGUID,
GUID * profileGUIDs, uint32_t guidArraySize, uint32_t * GUIDCount)
{
g_assert (nvenc_api.nvEncGetEncodeProfileGUIDs != NULL);
return nvenc_api.nvEncGetEncodeProfileGUIDs (encoder, encodeGUID,
profileGUIDs, guidArraySize, GUIDCount);
}
NVENCSTATUS NVENCAPI
NvEncGetInputFormats (void *encoder, GUID enc_guid,
NV_ENC_BUFFER_FORMAT * array, uint32_t size, uint32_t * num)
{
g_assert (nvenc_api.nvEncGetInputFormats != NULL);
return nvenc_api.nvEncGetInputFormats (encoder, enc_guid, array, size, num);
}
NVENCSTATUS NVENCAPI
NvEncGetEncodePresetCount (void *encoder, GUID encodeGUID,
uint32_t * encodePresetGUIDCount)
{
g_assert (nvenc_api.nvEncGetEncodeProfileGUIDCount != NULL);
return nvenc_api.nvEncGetEncodePresetCount (encoder, encodeGUID,
encodePresetGUIDCount);
}
NVENCSTATUS NVENCAPI
NvEncGetEncodePresetGUIDs (void *encoder, GUID encodeGUID,
GUID * presetGUIDs, uint32_t guidArraySize, uint32_t * GUIDCount)
{
g_assert (nvenc_api.nvEncGetEncodeProfileGUIDs != NULL);
return nvenc_api.nvEncGetEncodePresetGUIDs (encoder, encodeGUID,
presetGUIDs, guidArraySize, GUIDCount);
}
NVENCSTATUS NVENCAPI
NvEncGetEncodePresetConfig (void *encoder, GUID encodeGUID,
GUID presetGUID, NV_ENC_PRESET_CONFIG * presetConfig)
{
g_assert (nvenc_api.nvEncGetEncodePresetConfig != NULL);
return nvenc_api.nvEncGetEncodePresetConfig (encoder, encodeGUID, presetGUID,
presetConfig);
}
NVENCSTATUS NVENCAPI
NvEncGetEncodeCaps (void *encoder, GUID encodeGUID,
NV_ENC_CAPS_PARAM * capsParam, int *capsVal)
{
g_assert (nvenc_api.nvEncGetEncodeCaps != NULL);
return nvenc_api.nvEncGetEncodeCaps (encoder, encodeGUID, capsParam, capsVal);
}
NVENCSTATUS NVENCAPI
NvEncGetSequenceParams (void *encoder,
NV_ENC_SEQUENCE_PARAM_PAYLOAD * sequenceParamPayload)
{
g_assert (nvenc_api.nvEncGetSequenceParams != NULL);
return nvenc_api.nvEncGetSequenceParams (encoder, sequenceParamPayload);
}
NVENCSTATUS NVENCAPI
NvEncInitializeEncoder (void *encoder, NV_ENC_INITIALIZE_PARAMS * params)
{
g_assert (nvenc_api.nvEncInitializeEncoder != NULL);
return nvenc_api.nvEncInitializeEncoder (encoder, params);
}
NVENCSTATUS NVENCAPI
NvEncReconfigureEncoder (void *encoder, NV_ENC_RECONFIGURE_PARAMS * params)
{
g_assert (nvenc_api.nvEncReconfigureEncoder != NULL);
return nvenc_api.nvEncReconfigureEncoder (encoder, params);
}
NVENCSTATUS NVENCAPI
NvEncRegisterResource (void *encoder, NV_ENC_REGISTER_RESOURCE * params)
{
g_assert (nvenc_api.nvEncRegisterResource != NULL);
return nvenc_api.nvEncRegisterResource (encoder, params);
}
NVENCSTATUS NVENCAPI
NvEncUnregisterResource (void *encoder, NV_ENC_REGISTERED_PTR resource)
{
g_assert (nvenc_api.nvEncUnregisterResource != NULL);
return nvenc_api.nvEncUnregisterResource (encoder, resource);
}
NVENCSTATUS NVENCAPI
NvEncMapInputResource (void *encoder, NV_ENC_MAP_INPUT_RESOURCE * params)
{
g_assert (nvenc_api.nvEncMapInputResource != NULL);
return nvenc_api.nvEncMapInputResource (encoder, params);
}
NVENCSTATUS NVENCAPI
NvEncUnmapInputResource (void *encoder, NV_ENC_INPUT_PTR input_buffer)
{
g_assert (nvenc_api.nvEncUnmapInputResource != NULL);
return nvenc_api.nvEncUnmapInputResource (encoder, input_buffer);
}
NVENCSTATUS NVENCAPI
NvEncCreateInputBuffer (void *encoder, NV_ENC_CREATE_INPUT_BUFFER * input_buf)
{
g_assert (nvenc_api.nvEncCreateInputBuffer != NULL);
return nvenc_api.nvEncCreateInputBuffer (encoder, input_buf);
}
NVENCSTATUS NVENCAPI
NvEncLockInputBuffer (void *encoder, NV_ENC_LOCK_INPUT_BUFFER * input_buf)
{
g_assert (nvenc_api.nvEncLockInputBuffer != NULL);
return nvenc_api.nvEncLockInputBuffer (encoder, input_buf);
}
NVENCSTATUS NVENCAPI
NvEncUnlockInputBuffer (void *encoder, NV_ENC_INPUT_PTR input_buf)
{
g_assert (nvenc_api.nvEncUnlockInputBuffer != NULL);
return nvenc_api.nvEncUnlockInputBuffer (encoder, input_buf);
}
NVENCSTATUS NVENCAPI
NvEncDestroyInputBuffer (void *encoder, NV_ENC_INPUT_PTR input_buf)
{
g_assert (nvenc_api.nvEncDestroyInputBuffer != NULL);
return nvenc_api.nvEncDestroyInputBuffer (encoder, input_buf);
}
NVENCSTATUS NVENCAPI
NvEncCreateBitstreamBuffer (void *encoder, NV_ENC_CREATE_BITSTREAM_BUFFER * bb)
{
g_assert (nvenc_api.nvEncCreateBitstreamBuffer != NULL);
return nvenc_api.nvEncCreateBitstreamBuffer (encoder, bb);
}
NVENCSTATUS NVENCAPI
NvEncLockBitstream (void *encoder, NV_ENC_LOCK_BITSTREAM * lock_bs)
{
g_assert (nvenc_api.nvEncLockBitstream != NULL);
return nvenc_api.nvEncLockBitstream (encoder, lock_bs);
}
NVENCSTATUS NVENCAPI
NvEncUnlockBitstream (void *encoder, NV_ENC_OUTPUT_PTR bb)
{
g_assert (nvenc_api.nvEncUnlockBitstream != NULL);
return nvenc_api.nvEncUnlockBitstream (encoder, bb);
}
NVENCSTATUS NVENCAPI
NvEncDestroyBitstreamBuffer (void *encoder, NV_ENC_OUTPUT_PTR bit_buf)
{
g_assert (nvenc_api.nvEncDestroyBitstreamBuffer != NULL);
return nvenc_api.nvEncDestroyBitstreamBuffer (encoder, bit_buf);
}
NVENCSTATUS NVENCAPI
NvEncEncodePicture (void *encoder, NV_ENC_PIC_PARAMS * pic_params)
{
g_assert (nvenc_api.nvEncEncodePicture != NULL);
return nvenc_api.nvEncEncodePicture (encoder, pic_params);
}
gboolean
gst_nvenc_cmp_guid (GUID g1, GUID g2)
{
return (g1.Data1 == g2.Data1 && g1.Data2 == g2.Data2 && g1.Data3 == g2.Data3
&& g1.Data4[0] == g2.Data4[0] && g1.Data4[1] == g2.Data4[1]
&& g1.Data4[2] == g2.Data4[2] && g1.Data4[3] == g2.Data4[3]
&& g1.Data4[4] == g2.Data4[4] && g1.Data4[5] == g2.Data4[5]
&& g1.Data4[6] == g2.Data4[6] && g1.Data4[7] == g2.Data4[7]);
}
NV_ENC_BUFFER_FORMAT
gst_nvenc_get_nv_buffer_format (GstVideoFormat fmt)
{
switch (fmt) {
case GST_VIDEO_FORMAT_NV12:
return NV_ENC_BUFFER_FORMAT_NV12_PL;
case GST_VIDEO_FORMAT_YV12:
return NV_ENC_BUFFER_FORMAT_YV12_PL;
case GST_VIDEO_FORMAT_I420:
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;
case GST_VIDEO_FORMAT_BGRA:
return NV_ENC_BUFFER_FORMAT_ARGB;
case GST_VIDEO_FORMAT_RGBA:
return NV_ENC_BUFFER_FORMAT_ABGR;
case GST_VIDEO_FORMAT_BGR10A2_LE:
return NV_ENC_BUFFER_FORMAT_ARGB10;
case GST_VIDEO_FORMAT_RGB10A2_LE:
return NV_ENC_BUFFER_FORMAT_ABGR10;
case GST_VIDEO_FORMAT_Y444_16LE:
case GST_VIDEO_FORMAT_Y444_16BE:
return NV_ENC_BUFFER_FORMAT_YUV444_10BIT;
case GST_VIDEO_FORMAT_VUYA:
return NV_ENC_BUFFER_FORMAT_AYUV;
default:
break;
}
return NV_ENC_BUFFER_FORMAT_UNDEFINED;
}
typedef struct
{
GstVideoFormat gst_format;
NV_ENC_BUFFER_FORMAT nv_format;
gboolean is_10bit;
gboolean supported;
} GstNvEncFormat;
gboolean
gst_nvenc_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_BGRA, NV_ENC_BUFFER_FORMAT_ARGB, FALSE, FALSE},
{GST_VIDEO_FORMAT_RGBA, NV_ENC_BUFFER_FORMAT_ABGR, FALSE, FALSE},
{GST_VIDEO_FORMAT_Y444, NV_ENC_BUFFER_FORMAT_YUV444, FALSE, FALSE},
{GST_VIDEO_FORMAT_VUYA, NV_ENC_BUFFER_FORMAT_AYUV, FALSE, FALSE},
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
{GST_VIDEO_FORMAT_P010_10LE, NV_ENC_BUFFER_FORMAT_YUV420_10BIT, TRUE,
FALSE},
{GST_VIDEO_FORMAT_BGR10A2_LE, NV_ENC_BUFFER_FORMAT_ARGB10, TRUE,
FALSE},
{GST_VIDEO_FORMAT_RGB10A2_LE, NV_ENC_BUFFER_FORMAT_ABGR10, TRUE,
FALSE},
{GST_VIDEO_FORMAT_Y444_16LE, NV_ENC_BUFFER_FORMAT_YUV444_10BIT, TRUE,
FALSE},
#else
{GST_VIDEO_FORMAT_P010_10BE, NV_ENC_BUFFER_FORMAT_YUV420_10BIT, TRUE,
FALSE},
{GST_VIDEO_FORMAT_Y444_16BE, NV_ENC_BUFFER_FORMAT_YUV444_10BIT, TRUE,
FALSE},
/* FIXME: No 10bits big-endian ARGB10 format is defined */
#endif
};
param.version = gst_nvenc_get_caps_param_version ();
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:
case NV_ENC_BUFFER_FORMAT_YV12:
case NV_ENC_BUFFER_FORMAT_IYUV:
case NV_ENC_BUFFER_FORMAT_ARGB:
case NV_ENC_BUFFER_FORMAT_ABGR:
if (!format_map[i].supported) {
format_map[i].supported = TRUE;
num_format++;
}
break;
case NV_ENC_BUFFER_FORMAT_YUV444:
case NV_ENC_BUFFER_FORMAT_AYUV:
if (support_yuv444 && !format_map[i].supported) {
format_map[i].supported = TRUE;
num_format++;
}
break;
case NV_ENC_BUFFER_FORMAT_YUV420_10BIT:
if (support_10bit && !format_map[i].supported) {
format_map[i].supported = TRUE;
num_format++;
}
break;
case NV_ENC_BUFFER_FORMAT_YUV444_10BIT:
if (support_yuv444 && support_10bit && !format_map[i].supported) {
format_map[i].supported = TRUE;
num_format++;
}
break;
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
case NV_ENC_BUFFER_FORMAT_ARGB10:
case NV_ENC_BUFFER_FORMAT_ABGR10:
if (support_10bit && !format_map[i].supported) {
format_map[i].supported = TRUE;
num_format++;
}
break;
#endif
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_nvenc_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 = gst_nvenc_get_caps_param_version ();
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_nvenc_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 */
{"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},
/* put baseline to last since it does not support bframe */
{"baseline", NV_ENC_H264_PROFILE_BASELINE_GUID, NV_ENC_CODEC_H264_GUID,
FALSE, 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 = gst_nvenc_get_caps_param_version ();
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;
}
#define DEBUG_DEVICE_CAPS(d,c,caps,s) \
GST_DEBUG ("[device-%d %s] %s: %s", \
d, c, caps, s ? "supported" : "not supported");
#define ERROR_DETAILS "codec %s, device %i, error code %i"
static void
gst_nv_enc_register (GstPlugin * plugin, GUID codec_id, const gchar * codec,
guint rank, gint device_index, CUcontext cuda_ctx)
{
{
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;
gint min_width = 16;
gint min_height = 16;
GstCaps *sink_templ = NULL;
GstCaps *src_templ = NULL;
gchar *name;
gint j;
GstNvEncDeviceCaps device_caps = { 0, };
NVENCSTATUS status;
CUresult cu_res;
params.version = gst_nvenc_get_open_encode_session_ex_params_version ();
params.apiVersion = gst_nvenc_get_api_version ();
params.device = cuda_ctx;
params.deviceType = NV_ENC_DEVICE_TYPE_CUDA;
if ((cu_res = CuCtxPushCurrent (cuda_ctx)) != CUDA_SUCCESS) {
GST_ERROR ("CuCtxPushCurrent failed: " ERROR_DETAILS, codec,
device_index, cu_res);
goto done;
}
if ((status = NvEncOpenEncodeSessionEx (&params, &enc)) != NV_ENC_SUCCESS) {
CuCtxPopCurrent (NULL);
GST_ERROR ("NvEncOpenEncodeSessionEx failed: " ERROR_DETAILS, codec,
device_index, status);
goto done;
}
if ((status = NvEncGetEncodeGUIDs (enc, guids, G_N_ELEMENTS (guids),
&count)) != NV_ENC_SUCCESS) {
GST_ERROR ("NvEncGetEncodeGUIDs failed: " ERROR_DETAILS, codec,
device_index, status);
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_nvenc_get_supported_input_formats (enc, codec_id, &formats))
goto enc_free;
profiles = gst_nvenc_get_supported_codec_profiles (enc, codec_id);
if (!profiles)
goto free_format;
caps_param.version = gst_nvenc_get_caps_param_version ();
caps_param.capsToQuery = NV_ENC_CAPS_WIDTH_MAX;
if ((status = NvEncGetEncodeCaps (enc,
codec_id, &caps_param, &max_width)) != NV_ENC_SUCCESS) {
max_width = 4096;
GST_WARNING ("could not query max width, setting as %i: "
ERROR_DETAILS, max_width, codec, device_index, status);
} 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 ((status = NvEncGetEncodeCaps (enc,
codec_id, &caps_param, &max_height)) != NV_ENC_SUCCESS) {
GST_WARNING ("could not query max height, setting as %i: "
ERROR_DETAILS, max_height, codec, device_index, status);
max_height = 4096;
} else if (max_height < 4096) {
GST_WARNING ("max height %d is less than expected value", max_height);
max_height = 4096;
}
caps_param.capsToQuery = NV_ENC_CAPS_WIDTH_MIN;
if ((status = NvEncGetEncodeCaps (enc,
codec_id, &caps_param, &min_width)) != NV_ENC_SUCCESS) {
GST_WARNING ("could not query min width, setting as %i: "
ERROR_DETAILS, min_width, codec, device_index, status);
min_width = 16;
}
caps_param.capsToQuery = NV_ENC_CAPS_HEIGHT_MIN;
if ((status = NvEncGetEncodeCaps (enc,
codec_id, &caps_param, &min_height)) != NV_ENC_SUCCESS) {
GST_WARNING ("could not query min height, setting as %i: "
ERROR_DETAILS, min_height, codec, device_index, status);
min_height = 16;
}
caps_param.capsToQuery = NV_ENC_CAPS_SUPPORTED_RATECONTROL_MODES;
if (NvEncGetEncodeCaps (enc, codec_id, &caps_param,
&device_caps.rc_modes) != NV_ENC_SUCCESS) {
device_caps.rc_modes = 0;
} else {
GST_DEBUG ("[device-%d %s] rate control modes: 0x%x",
device_index, codec, device_caps.rc_modes);
#define IS_SUPPORTED_RC(rc_modes,mode) \
((((rc_modes) & (mode)) == mode) ? "supported" : "not supported")
GST_DEBUG ("\tconst-qp: %s",
IS_SUPPORTED_RC (device_caps.rc_modes, NV_ENC_PARAMS_RC_CONSTQP));
GST_DEBUG ("\tvbr: %s",
IS_SUPPORTED_RC (device_caps.rc_modes, NV_ENC_PARAMS_RC_VBR));
GST_DEBUG ("\tcbr: %s",
IS_SUPPORTED_RC (device_caps.rc_modes, NV_ENC_PARAMS_RC_CBR));
GST_DEBUG ("\tcbr-lowdelay-hq: %s",
IS_SUPPORTED_RC (device_caps.rc_modes,
NV_ENC_PARAMS_RC_CBR_LOWDELAY_HQ));
GST_DEBUG ("\tcbr-hq: %s",
IS_SUPPORTED_RC (device_caps.rc_modes, NV_ENC_PARAMS_RC_CBR_HQ));
GST_DEBUG ("\tvbr-hq: %s",
IS_SUPPORTED_RC (device_caps.rc_modes, NV_ENC_PARAMS_RC_VBR_HQ));
GST_DEBUG ("\tvbr-minqp: %s (deprecated)",
IS_SUPPORTED_RC (device_caps.rc_modes, NV_ENC_PARAMS_RC_VBR_MINQP));
#undef IS_SUPPORTED_RC
}
caps_param.capsToQuery = NV_ENC_CAPS_SUPPORT_WEIGHTED_PREDICTION;
if (NvEncGetEncodeCaps (enc, codec_id, &caps_param,
&device_caps.weighted_prediction) != NV_ENC_SUCCESS) {
device_caps.weighted_prediction = FALSE;
}
caps_param.capsToQuery = NV_ENC_CAPS_SUPPORT_CUSTOM_VBV_BUF_SIZE;
if (NvEncGetEncodeCaps (enc, codec_id, &caps_param,
&device_caps.custom_vbv_bufsize) != NV_ENC_SUCCESS) {
device_caps.custom_vbv_bufsize = FALSE;
}
caps_param.capsToQuery = NV_ENC_CAPS_SUPPORT_LOOKAHEAD;
if (NvEncGetEncodeCaps (enc,
codec_id, &caps_param, &device_caps.lookahead) != NV_ENC_SUCCESS) {
device_caps.lookahead = FALSE;
}
caps_param.capsToQuery = NV_ENC_CAPS_SUPPORT_TEMPORAL_AQ;
if (NvEncGetEncodeCaps (enc, codec_id, &caps_param,
&device_caps.temporal_aq) != NV_ENC_SUCCESS) {
device_caps.temporal_aq = FALSE;
}
caps_param.capsToQuery = NV_ENC_CAPS_NUM_MAX_BFRAMES;
if (NvEncGetEncodeCaps (enc, codec_id, &caps_param,
&device_caps.bframes) != NV_ENC_SUCCESS) {
device_caps.bframes = 0;
}
DEBUG_DEVICE_CAPS (device_index,
codec, "weighted prediction", device_caps.weighted_prediction);
DEBUG_DEVICE_CAPS (device_index, codec, "custom vbv-buffer-size",
device_caps.custom_vbv_bufsize);
DEBUG_DEVICE_CAPS (device_index, codec, "rc-loockahead",
device_caps.lookahead);
DEBUG_DEVICE_CAPS (device_index, codec, "temporal adaptive quantization",
device_caps.temporal_aq);
GST_DEBUG ("[device-%d %s] max bframes: %d", device_index, codec,
device_caps.bframes);
interlace_modes = gst_nvenc_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, min_width, max_width,
"height", GST_TYPE_INT_RANGE, min_height, 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, min_width, max_width,
"height", GST_TYPE_INT_RANGE, min_height, 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);
CuCtxPopCurrent (NULL);
/* fall-through */
done:
if (sink_templ && src_templ) {
if (gst_nvenc_cmp_guid (codec_id, NV_ENC_CODEC_H264_GUID)) {
gst_nv_h264_enc_register (plugin, device_index, rank, sink_templ,
src_templ, &device_caps);
} else if (gst_nvenc_cmp_guid (codec_id, NV_ENC_CODEC_HEVC_GUID)) {
gst_nv_h265_enc_register (plugin, device_index, rank, sink_templ,
src_templ, &device_caps);
} else {
g_assert_not_reached ();
}
}
gst_clear_caps (&sink_templ);
gst_clear_caps (&src_templ);
}
}
typedef struct
{
gint major;
gint minor;
} GstNvEncVersion;
gboolean
gst_nvenc_load_library (guint * api_major_ver, guint * api_minor_ver)
{
GModule *module;
NVENCSTATUS ret = NV_ENC_SUCCESS;
uint32_t max_supported_version;
gint major_ver, minor_ver;
gint i;
static const GstNvEncVersion version_list[] = {
{NVENCAPI_MAJOR_VERSION, NVENCAPI_MINOR_VERSION},
{9, 0},
{GST_NVENC_MIN_API_MAJOR_VERSION, GST_NVENC_MIN_API_MINOR_VERSION}
};
module = g_module_open (NVENC_LIBRARY_NAME, G_MODULE_BIND_LAZY);
if (module == NULL) {
GST_WARNING ("Could not open library %s, %s",
NVENC_LIBRARY_NAME, g_module_error ());
return FALSE;
}
if (!g_module_symbol (module, "NvEncodeAPICreateInstance",
(gpointer *) & nvEncodeAPICreateInstance)) {
GST_ERROR ("%s", g_module_error ());
return FALSE;
}
if (!g_module_symbol (module, "NvEncodeAPIGetMaxSupportedVersion",
(gpointer *) & nvEncodeAPIGetMaxSupportedVersion)) {
GST_ERROR ("NvEncodeAPIGetMaxSupportedVersion unavailable");
return FALSE;
}
/* WARNING: Any developers who want to bump SDK version must ensure that
* following macro values were not changed and also need to check ABI compatibility.
* Otherwise, gst_nvenc_get_ helpers also should be updated.
* Currently SDK 8.1 and 9.0 compatible
*
* NVENCAPI_VERSION (NVENCAPI_MAJOR_VERSION | (NVENCAPI_MINOR_VERSION << 24))
*
* NVENCAPI_STRUCT_VERSION(ver) ((uint32_t)NVENCAPI_VERSION | ((ver)<<16) | (0x7 << 28))
*
* NV_ENC_CAPS_PARAM_VER NVENCAPI_STRUCT_VERSION(1)
* NV_ENC_ENCODE_OUT_PARAMS_VER NVENCAPI_STRUCT_VERSION(1)
* NV_ENC_CREATE_INPUT_BUFFER_VER NVENCAPI_STRUCT_VERSION(1)
* NV_ENC_CREATE_BITSTREAM_BUFFER_VER NVENCAPI_STRUCT_VERSION(1)
* NV_ENC_CREATE_MV_BUFFER_VER NVENCAPI_STRUCT_VERSION(1)
* NV_ENC_RC_PARAMS_VER NVENCAPI_STRUCT_VERSION(1)
* NV_ENC_CONFIG_VER (NVENCAPI_STRUCT_VERSION(7) | ( 1<<31 ))
* NV_ENC_INITIALIZE_PARAMS_VER (NVENCAPI_STRUCT_VERSION(5) | ( 1<<31 ))
* NV_ENC_RECONFIGURE_PARAMS_VER (NVENCAPI_STRUCT_VERSION(1) | ( 1<<31 ))
* NV_ENC_PRESET_CONFIG_VER (NVENCAPI_STRUCT_VERSION(4) | ( 1<<31 ))
* NV_ENC_PIC_PARAMS_VER (NVENCAPI_STRUCT_VERSION(4) | ( 1<<31 ))
* NV_ENC_MEONLY_PARAMS_VER NVENCAPI_STRUCT_VERSION(3)
* NV_ENC_LOCK_BITSTREAM_VER NVENCAPI_STRUCT_VERSION(1)
* NV_ENC_LOCK_INPUT_BUFFER_VER NVENCAPI_STRUCT_VERSION(1)
* NV_ENC_MAP_INPUT_RESOURCE_VER NVENCAPI_STRUCT_VERSION(4)
* NV_ENC_REGISTER_RESOURCE_VER NVENCAPI_STRUCT_VERSION(3)
* NV_ENC_STAT_VER NVENCAPI_STRUCT_VERSION(1)
* NV_ENC_SEQUENCE_PARAM_PAYLOAD_VER NVENCAPI_STRUCT_VERSION(1)
* NV_ENC_EVENT_PARAMS_VER NVENCAPI_STRUCT_VERSION(1)
* NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER NVENCAPI_STRUCT_VERSION(1)
* NV_ENCODE_API_FUNCTION_LIST_VER NVENCAPI_STRUCT_VERSION(2)
*/
ret = nvEncodeAPIGetMaxSupportedVersion (&max_supported_version);
if (ret != NV_ENC_SUCCESS) {
GST_ERROR ("Could not query max supported api version, ret %d", ret);
return FALSE;
}
/* 4 LSB: minor version
* the rest: major version */
major_ver = max_supported_version >> 4;
minor_ver = max_supported_version & 0xf;
GST_INFO ("Maximum supported API version by driver: %d.%d",
major_ver, minor_ver);
ret = NV_ENC_ERR_INVALID_VERSION;
for (i = 0; i < G_N_ELEMENTS (version_list); i++) {
if (version_list[i].major > major_ver ||
(version_list[i].major == major_ver
&& version_list[i].minor > minor_ver)) {
continue;
}
gst_nvenc_api_version =
GST_NVENCAPI_VERSION (version_list[i].major, version_list[i].minor);
nvenc_api.version = GST_NVENCAPI_STRUCT_VERSION (2, gst_nvenc_api_version);
ret = nvEncodeAPICreateInstance (&nvenc_api);
if (ret == NV_ENC_SUCCESS) {
GST_INFO ("API version %d.%d load done",
version_list[i].major, version_list[i].minor);
*api_major_ver = version_list[i].major;
*api_minor_ver = version_list[i].minor;
break;
}
}
return ret == NV_ENC_SUCCESS;
}
void
gst_nvenc_plugin_init (GstPlugin * plugin, guint device_index,
CUcontext cuda_ctx)
{
gst_nv_enc_register (plugin, NV_ENC_CODEC_H264_GUID,
"h264", GST_RANK_PRIMARY * 2, device_index, cuda_ctx);
gst_nv_enc_register (plugin, NV_ENC_CODEC_HEVC_GUID,
"h265", GST_RANK_PRIMARY * 2, device_index, cuda_ctx);
}
guint32
gst_nvenc_get_api_version (void)
{
/* NVENCAPI_VERSION == (NVENCAPI_MAJOR_VERSION | (NVENCAPI_MINOR_VERSION << 24)) */
return gst_nvenc_api_version;
}
guint32
gst_nvenc_get_caps_param_version (void)
{
/* NV_ENC_CAPS_PARAM_VER == NVENCAPI_STRUCT_VERSION(1) */
return GST_NVENCAPI_STRUCT_VERSION (1, gst_nvenc_api_version);
}
guint32
gst_nvenc_get_encode_out_params_version (void)
{
/* NV_ENC_ENCODE_OUT_PARAMS_VER == NVENCAPI_STRUCT_VERSION(1) */
return GST_NVENCAPI_STRUCT_VERSION (1, gst_nvenc_api_version);
}
guint32
gst_nvenc_get_create_input_buffer_version (void)
{
/* NV_ENC_CREATE_INPUT_BUFFER_VER == NVENCAPI_STRUCT_VERSION(1) */
return GST_NVENCAPI_STRUCT_VERSION (1, gst_nvenc_api_version);
}
guint32
gst_nvenc_get_create_bitstream_buffer_version (void)
{
/* NV_ENC_CREATE_BITSTREAM_BUFFER_VER == NVENCAPI_STRUCT_VERSION(1) */
return GST_NVENCAPI_STRUCT_VERSION (1, gst_nvenc_api_version);
}
guint32
gst_nvenc_get_create_mv_buffer_version (void)
{
/* NV_ENC_CREATE_MV_BUFFER_VER == NVENCAPI_STRUCT_VERSION(1) */
return GST_NVENCAPI_STRUCT_VERSION (1, gst_nvenc_api_version);
}
guint32
gst_nvenc_get_rc_params_version (void)
{
/* NV_ENC_RC_PARAMS_VER == NVENCAPI_STRUCT_VERSION(1) */
return GST_NVENCAPI_STRUCT_VERSION (1, gst_nvenc_api_version);
}
guint32
gst_nvenc_get_config_version (void)
{
/* NV_ENC_CONFIG_VER ==
* (NVENCAPI_STRUCT_VERSION(7) | ( 1<<31 )) */
return GST_NVENCAPI_STRUCT_VERSION (7, gst_nvenc_api_version) | (1 << 31);
}
guint32
gst_nvenc_get_initialize_params_version (void)
{
/* NV_ENC_INITIALIZE_PARAMS_VER ==
* (NVENCAPI_STRUCT_VERSION(5) | ( 1<<31 )) */
return GST_NVENCAPI_STRUCT_VERSION (5, gst_nvenc_api_version) | (1 << 31);
}
guint32
gst_nvenc_get_reconfigure_params_version (void)
{
/* NV_ENC_RECONFIGURE_PARAMS_VER ==
* (NVENCAPI_STRUCT_VERSION(1) | ( 1<<31 )) */
return GST_NVENCAPI_STRUCT_VERSION (1, gst_nvenc_api_version) | (1 << 31);
}
guint32
gst_nvenc_get_preset_config_version (void)
{
/* NV_ENC_PRESET_CONFIG_VER ==
* (NVENCAPI_STRUCT_VERSION(4) | ( 1<<31 )) */
return GST_NVENCAPI_STRUCT_VERSION (4, gst_nvenc_api_version) | (1 << 31);
}
guint32
gst_nvenc_get_pic_params_version (void)
{
/* NV_ENC_PIC_PARAMS_VER ==
* (NVENCAPI_STRUCT_VERSION(4) | ( 1<<31 )) */
return GST_NVENCAPI_STRUCT_VERSION (4, gst_nvenc_api_version) | (1 << 31);
}
guint32
gst_nvenc_get_meonly_params_version (void)
{
/* NV_ENC_MEONLY_PARAMS_VER == NVENCAPI_STRUCT_VERSION(3) */
return GST_NVENCAPI_STRUCT_VERSION (3, gst_nvenc_api_version);
}
guint32
gst_nvenc_get_lock_bitstream_version (void)
{
/* NV_ENC_LOCK_BITSTREAM_VER == NVENCAPI_STRUCT_VERSION(1) */
return GST_NVENCAPI_STRUCT_VERSION (1, gst_nvenc_api_version);
}
guint32
gst_nvenc_get_lock_input_buffer_version (void)
{
/* NV_ENC_LOCK_INPUT_BUFFER_VER == NVENCAPI_STRUCT_VERSION(1) */
return GST_NVENCAPI_STRUCT_VERSION (1, gst_nvenc_api_version);
}
guint32
gst_nvenc_get_map_input_resource_version (void)
{
/* NV_ENC_MAP_INPUT_RESOURCE_VER == NVENCAPI_STRUCT_VERSION(4) */
return GST_NVENCAPI_STRUCT_VERSION (4, gst_nvenc_api_version);
}
guint32
gst_nvenc_get_registure_resource_version (void)
{
/* NV_ENC_REGISTER_RESOURCE_VER == NVENCAPI_STRUCT_VERSION(3) */
return GST_NVENCAPI_STRUCT_VERSION (3, gst_nvenc_api_version);
}
guint32
gst_nvenc_get_stat_version (void)
{
/* NV_ENC_STAT_VER == NVENCAPI_STRUCT_VERSION(1) */
return GST_NVENCAPI_STRUCT_VERSION (1, gst_nvenc_api_version);
}
guint32
gst_nvenc_get_sequence_param_payload_version (void)
{
/* NV_ENC_SEQUENCE_PARAM_PAYLOAD_VER == NVENCAPI_STRUCT_VERSION(1) */
return GST_NVENCAPI_STRUCT_VERSION (1, gst_nvenc_api_version);
}
guint32
gst_nvenc_get_event_params_version (void)
{
/* NV_ENC_EVENT_PARAMS_VER == NVENCAPI_STRUCT_VERSION(1) */
return GST_NVENCAPI_STRUCT_VERSION (1, gst_nvenc_api_version);
}
guint32
gst_nvenc_get_open_encode_session_ex_params_version (void)
{
/* NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER == NVENCAPI_STRUCT_VERSION(1) */
return GST_NVENCAPI_STRUCT_VERSION (1, gst_nvenc_api_version);
}