nvencoder: Add support for dynamic GPU device selection

Adding nvautogpu{h264,h265}enc class which will accept upstream logical
GPU device object (GstCudaContext or GstD3D11Device) instead of
using pre-assigned GPU instance.

If upstream logical GPU device object is not NVENC compatible
(e.g., D3D11 device of non-NVIDIA GPU) or it's system memory,
then user specified "cuda-device-id" or "adapter-luid" property
will be used for GPU device selection.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2666>
This commit is contained in:
Seungha Yang 2022-06-23 01:10:38 +09:00
parent bcacfc7ced
commit 30c8dbe032
7 changed files with 1632 additions and 572 deletions

View file

@ -62,9 +62,10 @@ struct _GstNvEncoderPrivate
GstD3D11Device *device; GstD3D11Device *device;
#endif #endif
GstNvEncoderDeviceMode subclass_device_mode;
GstNvEncoderDeviceMode selected_device_mode;
gint64 dxgi_adapter_luid; gint64 dxgi_adapter_luid;
guint cuda_device_id; guint cuda_device_id;
gboolean d3d11_mode;
NV_ENC_INITIALIZE_PARAMS init_params; NV_ENC_INITIALIZE_PARAMS init_params;
NV_ENC_CONFIG config; NV_ENC_CONFIG config;
@ -85,6 +86,8 @@ struct _GstNvEncoderPrivate
GMutex lock; GMutex lock;
GCond cond; GCond cond;
GRecMutex context_lock;
GThread *encoding_thread; GThread *encoding_thread;
GstFlowReturn last_flow; GstFlowReturn last_flow;
@ -159,6 +162,8 @@ gst_nv_encoder_init (GstNvEncoder * self)
g_mutex_init (&priv->lock); g_mutex_init (&priv->lock);
g_cond_init (&priv->cond); g_cond_init (&priv->cond);
g_rec_mutex_init (&priv->context_lock);
gst_video_encoder_set_min_pts (GST_VIDEO_ENCODER (self), gst_video_encoder_set_min_pts (GST_VIDEO_ENCODER (self),
GST_SECOND * 60 * 60 * 1000); GST_SECOND * 60 * 60 * 1000);
} }
@ -171,6 +176,11 @@ gst_nv_encoder_finalize (GObject * object)
g_array_unref (priv->task_pool); g_array_unref (priv->task_pool);
g_mutex_clear (&priv->lock);
g_cond_clear (&priv->cond);
g_rec_mutex_clear (&priv->context_lock);
G_OBJECT_CLASS (parent_class)->finalize (object); G_OBJECT_CLASS (parent_class)->finalize (object);
} }
@ -180,15 +190,24 @@ gst_nv_encoder_set_context (GstElement * element, GstContext * context)
GstNvEncoder *self = GST_NV_ENCODER (element); GstNvEncoder *self = GST_NV_ENCODER (element);
GstNvEncoderPrivate *priv = self->priv; GstNvEncoderPrivate *priv = self->priv;
g_rec_mutex_lock (&priv->context_lock);
switch (priv->selected_device_mode) {
#ifdef GST_CUDA_HAS_D3D #ifdef GST_CUDA_HAS_D3D
if (priv->d3d11_mode) { case GST_NV_ENCODER_DEVICE_D3D11:
gst_d3d11_handle_set_context_for_adapter_luid (element, gst_d3d11_handle_set_context_for_adapter_luid (element,
context, priv->dxgi_adapter_luid, &priv->device); context, priv->dxgi_adapter_luid, &priv->device);
} break;
#endif #endif
case GST_NV_ENCODER_DEVICE_CUDA:
gst_cuda_handle_set_context (element, context, priv->cuda_device_id, gst_cuda_handle_set_context (element, context, priv->cuda_device_id,
&priv->context); &priv->context);
break;
default:
break;
}
g_rec_mutex_unlock (&priv->context_lock);
GST_ELEMENT_CLASS (parent_class)->set_context (element, context); GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
} }
@ -224,30 +243,44 @@ static gboolean
gst_nv_encoder_device_lock (GstNvEncoder * self) gst_nv_encoder_device_lock (GstNvEncoder * self)
{ {
GstNvEncoderPrivate *priv = self->priv; GstNvEncoderPrivate *priv = self->priv;
gboolean ret = TRUE;
switch (priv->selected_device_mode) {
#ifdef GST_CUDA_HAS_D3D #ifdef GST_CUDA_HAS_D3D
if (priv->d3d11_mode) { case GST_NV_ENCODER_DEVICE_D3D11:
gst_d3d11_device_lock (priv->device); gst_d3d11_device_lock (priv->device);
return TRUE; break;
}
#endif #endif
case GST_NV_ENCODER_DEVICE_CUDA:
ret = gst_cuda_context_push (priv->context);
break;
default:
break;
}
return gst_cuda_context_push (priv->context); return ret;
} }
static gboolean static gboolean
gst_nv_encoder_device_unlock (GstNvEncoder * self) gst_nv_encoder_device_unlock (GstNvEncoder * self)
{ {
#ifdef GST_CUDA_HAS_D3D
GstNvEncoderPrivate *priv = self->priv; GstNvEncoderPrivate *priv = self->priv;
gboolean ret = TRUE;
if (priv->d3d11_mode) { switch (priv->selected_device_mode) {
#ifdef GST_CUDA_HAS_D3D
case GST_NV_ENCODER_DEVICE_D3D11:
gst_d3d11_device_unlock (priv->device); gst_d3d11_device_unlock (priv->device);
return TRUE; break;
}
#endif #endif
case GST_NV_ENCODER_DEVICE_CUDA:
ret = gst_cuda_context_pop (nullptr);
break;
default:
break;
}
return gst_cuda_context_pop (NULL); return ret;
} }
static GstFlowReturn static GstFlowReturn
@ -375,17 +408,25 @@ gst_nv_encoder_open (GstVideoEncoder * encoder)
GstNvEncoder *self = GST_NV_ENCODER (encoder); GstNvEncoder *self = GST_NV_ENCODER (encoder);
GstNvEncoderPrivate *priv = self->priv; GstNvEncoderPrivate *priv = self->priv;
switch (priv->selected_device_mode) {
case GST_NV_ENCODER_DEVICE_AUTO_SELECT:
/* Will open GPU later */
return TRUE;
#ifdef GST_CUDA_HAS_D3D #ifdef GST_CUDA_HAS_D3D
if (priv->d3d11_mode) { case GST_NV_ENCODER_DEVICE_D3D11:
return gst_nv_encoder_open_d3d11_device (self); return gst_nv_encoder_open_d3d11_device (self);
}
#endif #endif
case GST_NV_ENCODER_DEVICE_CUDA:
if (!gst_cuda_ensure_element_context (GST_ELEMENT_CAST (encoder), if (!gst_cuda_ensure_element_context (GST_ELEMENT_CAST (encoder),
priv->cuda_device_id, &priv->context)) { priv->cuda_device_id, &priv->context)) {
GST_ERROR_OBJECT (self, "failed to create CUDA context"); GST_ERROR_OBJECT (self, "failed to create CUDA context");
return FALSE; return FALSE;
} }
break;
default:
g_assert_not_reached ();
return FALSE;
}
return TRUE; return TRUE;
} }
@ -414,6 +455,14 @@ gst_nv_encoder_stop (GstVideoEncoder * encoder)
gst_nv_encoder_drain (self, FALSE); gst_nv_encoder_drain (self, FALSE);
if (priv->subclass_device_mode == GST_NV_ENCODER_DEVICE_AUTO_SELECT) {
gst_clear_object (&priv->context);
#ifdef GST_CUDA_HAS_D3D
gst_clear_object (&priv->device);
#endif
priv->selected_device_mode = GST_NV_ENCODER_DEVICE_AUTO_SELECT;
}
g_clear_pointer (&priv->input_state, gst_video_codec_state_unref); g_clear_pointer (&priv->input_state, gst_video_codec_state_unref);
return TRUE; return TRUE;
@ -423,16 +472,28 @@ static gboolean
gst_nv_encoder_handle_context_query (GstNvEncoder * self, GstQuery * query) gst_nv_encoder_handle_context_query (GstNvEncoder * self, GstQuery * query)
{ {
GstNvEncoderPrivate *priv = self->priv; GstNvEncoderPrivate *priv = self->priv;
gboolean ret = FALSE;
g_rec_mutex_lock (&priv->context_lock);
switch (priv->selected_device_mode) {
#ifdef GST_CUDA_HAS_D3D #ifdef GST_CUDA_HAS_D3D
if (priv->d3d11_mode) { case GST_NV_ENCODER_DEVICE_D3D11:
return gst_d3d11_handle_context_query (GST_ELEMENT (self), ret = gst_d3d11_handle_context_query (GST_ELEMENT (self),
query, priv->device); query, priv->device);
} break;
#endif #endif
case GST_NV_ENCODER_DEVICE_CUDA:
return gst_cuda_handle_context_query (GST_ELEMENT (self), ret = gst_cuda_handle_context_query (GST_ELEMENT (self),
query, priv->context); query, priv->context);
break;
default:
break;
}
g_rec_mutex_unlock (&priv->context_lock);
return ret;
} }
static gboolean static gboolean
@ -494,19 +555,35 @@ gst_nv_encoder_propose_allocation (GstVideoEncoder * encoder, GstQuery * query)
} }
features = gst_caps_get_features (caps, 0); features = gst_caps_get_features (caps, 0);
min_buffers = gst_nv_encoder_get_task_size (self);
switch (priv->subclass_device_mode) {
case GST_NV_ENCODER_DEVICE_AUTO_SELECT:
/* Use upstream pool in case of auto select mode. We don't know which
* GPU to use at this moment */
gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, nullptr);
gst_query_add_allocation_pool (query, nullptr, info.size, min_buffers, 0);
return TRUE;
#ifdef GST_CUDA_HAS_D3D #ifdef GST_CUDA_HAS_D3D
if (priv->d3d11_mode && features && gst_caps_features_contains (features, case GST_NV_ENCODER_DEVICE_D3D11:
if (features && gst_caps_features_contains (features,
GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY)) { GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY)) {
GST_DEBUG_OBJECT (self, "upstream support d3d11 memory"); GST_DEBUG_OBJECT (self, "upstream support d3d11 memory");
pool = gst_d3d11_buffer_pool_new (priv->device); pool = gst_d3d11_buffer_pool_new (priv->device);
} }
break;
#endif #endif
case GST_NV_ENCODER_DEVICE_CUDA:
if (!priv->d3d11_mode && features && gst_caps_features_contains (features, if (features && gst_caps_features_contains (features,
GST_CAPS_FEATURE_MEMORY_CUDA_MEMORY)) { GST_CAPS_FEATURE_MEMORY_CUDA_MEMORY)) {
GST_DEBUG_OBJECT (self, "upstream support CUDA memory"); GST_DEBUG_OBJECT (self, "upstream support CUDA memory");
pool = gst_cuda_buffer_pool_new (priv->context); pool = gst_cuda_buffer_pool_new (priv->context);
} }
break;
default:
g_assert_not_reached ();
return FALSE;
}
if (!pool) if (!pool)
pool = gst_video_buffer_pool_new (); pool = gst_video_buffer_pool_new ();
@ -514,7 +591,6 @@ gst_nv_encoder_propose_allocation (GstVideoEncoder * encoder, GstQuery * query)
config = gst_buffer_pool_get_config (pool); config = gst_buffer_pool_get_config (pool);
gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META); gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
min_buffers = gst_nv_encoder_get_task_size (GST_NV_ENCODER (self));
size = GST_VIDEO_INFO_SIZE (&info); size = GST_VIDEO_INFO_SIZE (&info);
gst_buffer_pool_config_set_params (config, caps, size, min_buffers, 0); gst_buffer_pool_config_set_params (config, caps, size, min_buffers, 0);
@ -968,15 +1044,20 @@ gst_nv_encoder_open_encode_session (GstNvEncoder * self, gpointer * session)
session_params.apiVersion = gst_nvenc_get_api_version (); session_params.apiVersion = gst_nvenc_get_api_version ();
NVENCSTATUS status; NVENCSTATUS status;
switch (priv->selected_device_mode) {
#ifdef GST_CUDA_HAS_D3D #ifdef GST_CUDA_HAS_D3D
if (priv->d3d11_mode) { case GST_NV_ENCODER_DEVICE_D3D11:
session_params.deviceType = NV_ENC_DEVICE_TYPE_DIRECTX; session_params.deviceType = NV_ENC_DEVICE_TYPE_DIRECTX;
session_params.device = gst_d3d11_device_get_device_handle (priv->device); session_params.device = gst_d3d11_device_get_device_handle (priv->device);
} else break;
#endif #endif
{ case GST_NV_ENCODER_DEVICE_CUDA:
session_params.deviceType = NV_ENC_DEVICE_TYPE_CUDA; session_params.deviceType = NV_ENC_DEVICE_TYPE_CUDA;
session_params.device = gst_cuda_context_get_handle (priv->context); session_params.device = gst_cuda_context_get_handle (priv->context);
break;
default:
g_assert_not_reached ();
return FALSE;
} }
status = NvEncOpenEncodeSessionEx (&session_params, session); status = NvEncOpenEncodeSessionEx (&session_params, session);
@ -1033,12 +1114,20 @@ gst_nv_encoder_create_pool (GstNvEncoder * self, GstVideoCodecState * state)
GstNvEncoderPrivate *priv = self->priv; GstNvEncoderPrivate *priv = self->priv;
GstStructure *config; GstStructure *config;
GstBufferPool *pool = NULL; GstBufferPool *pool = NULL;
/* At this moment device type must be selected already */
switch (priv->selected_device_mode) {
#ifdef GST_CUDA_HAS_D3D #ifdef GST_CUDA_HAS_D3D
if (priv->d3d11_mode) case GST_NV_ENCODER_DEVICE_D3D11:
return gst_nv_encoder_create_d3d11_pool (self, state); return gst_nv_encoder_create_d3d11_pool (self, state);
#endif #endif
case GST_NV_ENCODER_DEVICE_CUDA:
pool = gst_cuda_buffer_pool_new (priv->context); pool = gst_cuda_buffer_pool_new (priv->context);
break;
default:
g_assert_not_reached ();
return FALSE;
}
config = gst_buffer_pool_get_config (pool); config = gst_buffer_pool_get_config (pool);
gst_buffer_pool_config_set_params (config, state->caps, gst_buffer_pool_config_set_params (config, state->caps,
@ -1060,7 +1149,7 @@ gst_nv_encoder_create_pool (GstNvEncoder * self, GstVideoCodecState * state)
} }
static gboolean static gboolean
gst_nv_encoder_init_session (GstNvEncoder * self) gst_nv_encoder_init_session (GstNvEncoder * self, GstBuffer * in_buf)
{ {
GstNvEncoderPrivate *priv = self->priv; GstNvEncoderPrivate *priv = self->priv;
GstNvEncoderClass *klass = GST_NV_ENCODER_GET_CLASS (self); GstNvEncoderClass *klass = GST_NV_ENCODER_GET_CLASS (self);
@ -1077,6 +1166,52 @@ gst_nv_encoder_init_session (GstNvEncoder * self)
memset (&priv->init_params, 0, sizeof (NV_ENC_INITIALIZE_PARAMS)); memset (&priv->init_params, 0, sizeof (NV_ENC_INITIALIZE_PARAMS));
memset (&priv->config, 0, sizeof (NV_ENC_CONFIG)); memset (&priv->config, 0, sizeof (NV_ENC_CONFIG));
if (priv->selected_device_mode == GST_NV_ENCODER_DEVICE_AUTO_SELECT) {
GstNvEncoderDeviceData data;
gboolean ret;
if (!in_buf) {
GST_DEBUG_OBJECT (self, "Unknown device mode, open session later");
return TRUE;
}
if (!klass->select_device (self, info, in_buf, &data)) {
GST_ELEMENT_ERROR (self, STREAM, ENCODE, (NULL),
("Failed to select device mode"));
return FALSE;
}
GST_DEBUG_OBJECT (self,
"Selected device mode: %d, cuda-device-id: %d, adapter-luid %"
G_GINT64_FORMAT, data.device_mode, data.cuda_device_id,
data.adapter_luid);
g_assert (data.device_mode == GST_NV_ENCODER_DEVICE_CUDA ||
data.device_mode == GST_NV_ENCODER_DEVICE_D3D11);
g_rec_mutex_lock (&priv->context_lock);
priv->selected_device_mode = data.device_mode;
priv->cuda_device_id = data.cuda_device_id;
priv->dxgi_adapter_luid = data.adapter_luid;
gst_clear_object (&priv->context);
if (data.device_mode == GST_NV_ENCODER_DEVICE_CUDA)
priv->context = (GstCudaContext *) data.device;
#ifdef GST_CUDA_HAS_D3D
gst_clear_object (&priv->device);
if (data.device_mode == GST_NV_ENCODER_DEVICE_D3D11)
priv->device = (GstD3D11Device *) data.device;
#endif
ret = gst_nv_encoder_open (GST_VIDEO_ENCODER (self));
g_rec_mutex_unlock (&priv->context_lock);
if (!ret) {
GST_ELEMENT_ERROR (self, STREAM, ENCODE, (NULL),
("Failed to open device"));
return FALSE;
}
}
priv->internal_pool = gst_nv_encoder_create_pool (self, state); priv->internal_pool = gst_nv_encoder_create_pool (self, state);
if (!priv->internal_pool) { if (!priv->internal_pool) {
GST_ELEMENT_ERROR (self, STREAM, ENCODE, (NULL), GST_ELEMENT_ERROR (self, STREAM, ENCODE, (NULL),
@ -1200,7 +1335,7 @@ gst_nv_encoder_reconfigure_session (GstNvEncoder * self)
"Encoding session was not configured, open session"); "Encoding session was not configured, open session");
gst_nv_encoder_drain (self, TRUE); gst_nv_encoder_drain (self, TRUE);
return gst_nv_encoder_init_session (self); return gst_nv_encoder_init_session (self, nullptr);
} }
params.version = gst_nvenc_get_reconfigure_params_version (); params.version = gst_nvenc_get_reconfigure_params_version ();
@ -1213,7 +1348,7 @@ gst_nv_encoder_reconfigure_session (GstNvEncoder * self)
GST_NVENC_STATUS_FORMAT, GST_NVENC_STATUS_ARGS (status)); GST_NVENC_STATUS_FORMAT, GST_NVENC_STATUS_ARGS (status));
gst_nv_encoder_drain (self, TRUE); gst_nv_encoder_drain (self, TRUE);
return gst_nv_encoder_init_session (self); return gst_nv_encoder_init_session (self, nullptr);
} }
return TRUE; return TRUE;
@ -1230,10 +1365,13 @@ gst_nv_encoder_set_format (GstVideoEncoder * encoder,
g_clear_pointer (&priv->input_state, gst_video_codec_state_unref); g_clear_pointer (&priv->input_state, gst_video_codec_state_unref);
priv->input_state = gst_video_codec_state_ref (state); priv->input_state = gst_video_codec_state_ref (state);
priv->last_flow = GST_FLOW_OK; priv->last_flow = GST_FLOW_OK;
return gst_nv_encoder_init_session (self); /* select device again on next buffer */
if (priv->subclass_device_mode == GST_NV_ENCODER_DEVICE_AUTO_SELECT)
priv->selected_device_mode = GST_NV_ENCODER_DEVICE_AUTO_SELECT;
return gst_nv_encoder_init_session (self, nullptr);
} }
static NV_ENC_BUFFER_FORMAT static NV_ENC_BUFFER_FORMAT
@ -1679,16 +1817,26 @@ gst_nv_encoder_prepare_task_input (GstNvEncoder * self,
const GstVideoInfo * info, GstBuffer * buffer, gpointer session, const GstVideoInfo * info, GstBuffer * buffer, gpointer session,
GstBufferPool * pool, GstNvEncoderTask * task) GstBufferPool * pool, GstNvEncoderTask * task)
{ {
#ifdef GST_CUDA_HAS_D3D
GstNvEncoderPrivate *priv = self->priv; GstNvEncoderPrivate *priv = self->priv;
if (priv->d3d11_mode) { GstFlowReturn ret = GST_FLOW_ERROR;
return gst_nv_encoder_prepare_task_input_d3d11 (self, info, buffer,
session, pool, task);
}
#endif
return gst_nv_encoder_prepare_task_input_cuda (self, info, buffer, switch (priv->selected_device_mode) {
#ifdef GST_CUDA_HAS_D3D
case GST_NV_ENCODER_DEVICE_D3D11:
ret = gst_nv_encoder_prepare_task_input_d3d11 (self, info, buffer,
session, pool, task);
break;
#endif
case GST_NV_ENCODER_DEVICE_CUDA:
ret = gst_nv_encoder_prepare_task_input_cuda (self, info, buffer,
session, task); session, task);
break;
default:
g_assert_not_reached ();
break;
}
return ret;
} }
static GstFlowReturn static GstFlowReturn
@ -1701,6 +1849,7 @@ gst_nv_encoder_handle_frame (GstVideoEncoder * encoder,
GstFlowReturn ret = GST_FLOW_ERROR; GstFlowReturn ret = GST_FLOW_ERROR;
GstNvEncoderTask *task = NULL; GstNvEncoderTask *task = NULL;
GstNvEncoderReconfigure reconfig; GstNvEncoderReconfigure reconfig;
GstBuffer *in_buf = frame->input_buffer;
GST_TRACE_OBJECT (self, "Handle frame"); GST_TRACE_OBJECT (self, "Handle frame");
@ -1715,7 +1864,7 @@ gst_nv_encoder_handle_frame (GstVideoEncoder * encoder,
return ret; return ret;
} }
if (!priv->session && !gst_nv_encoder_init_session (self)) { if (!priv->session && !gst_nv_encoder_init_session (self, in_buf)) {
GST_ERROR_OBJECT (self, "Encoder object was not configured"); GST_ERROR_OBJECT (self, "Encoder object was not configured");
gst_video_encoder_finish_frame (encoder, frame); gst_video_encoder_finish_frame (encoder, frame);
@ -1733,7 +1882,7 @@ gst_nv_encoder_handle_frame (GstVideoEncoder * encoder,
case GST_NV_ENCODER_RECONFIGURE_FULL: case GST_NV_ENCODER_RECONFIGURE_FULL:
{ {
gst_nv_encoder_drain (self, TRUE); gst_nv_encoder_drain (self, TRUE);
if (!gst_nv_encoder_init_session (self)) { if (!gst_nv_encoder_init_session (self, nullptr)) {
gst_video_encoder_finish_frame (encoder, frame); gst_video_encoder_finish_frame (encoder, frame);
return GST_FLOW_NOT_NEGOTIATED; return GST_FLOW_NOT_NEGOTIATED;
} }
@ -1763,7 +1912,7 @@ gst_nv_encoder_handle_frame (GstVideoEncoder * encoder,
g_assert (task->buffer == NULL); g_assert (task->buffer == NULL);
ret = gst_nv_encoder_prepare_task_input (self, &priv->input_state->info, ret = gst_nv_encoder_prepare_task_input (self, &priv->input_state->info,
frame->input_buffer, priv->session, priv->internal_pool, task); in_buf, priv->session, priv->internal_pool, task);
gst_nv_encoder_device_unlock (self); gst_nv_encoder_device_unlock (self);
if (ret != GST_FLOW_OK) { if (ret != GST_FLOW_OK) {
@ -1826,22 +1975,15 @@ gst_nv_encoder_get_task_size (GstNvEncoder * encoder)
} }
void void
gst_nv_encoder_set_cuda_device_id (GstNvEncoder * encoder, guint device_id) gst_nv_encoder_set_device_mode (GstNvEncoder * encoder,
GstNvEncoderDeviceMode mode, guint cuda_device_id, gint64 adapter_luid)
{ {
g_return_if_fail (GST_IS_NV_ENCODER (encoder)); GstNvEncoderPrivate *priv = encoder->priv;
encoder->priv->cuda_device_id = device_id; priv->subclass_device_mode = mode;
encoder->priv->d3d11_mode = FALSE; priv->selected_device_mode = mode;
} priv->cuda_device_id = cuda_device_id;
priv->dxgi_adapter_luid = adapter_luid;
void
gst_nv_encoder_set_dxgi_adapter_luid (GstNvEncoder * encoder,
gint64 adapter_luid)
{
g_return_if_fail (GST_IS_NV_ENCODER (encoder));
encoder->priv->dxgi_adapter_luid = adapter_luid;
encoder->priv->d3d11_mode = TRUE;
} }
GType GType
@ -1995,3 +2137,204 @@ gst_nv_encoder_status_to_string (NVENCSTATUS status)
return "Unknown"; return "Unknown";
} }
GstNvEncoderClassData *
gst_nv_encoder_class_data_new (void)
{
GstNvEncoderClassData *data = g_new0 (GstNvEncoderClassData, 1);
data->ref_count = 1;
return data;
}
GstNvEncoderClassData *
gst_nv_encoder_class_data_ref (GstNvEncoderClassData * cdata)
{
g_atomic_int_add (&cdata->ref_count, 1);
return cdata;
}
void
gst_nv_encoder_class_data_unref (GstNvEncoderClassData * cdata)
{
if (g_atomic_int_dec_and_test (&cdata->ref_count)) {
gst_clear_caps (&cdata->sink_caps);
gst_clear_caps (&cdata->src_caps);
if (cdata->formats)
g_list_free_full (cdata->formats, (GDestroyNotify) g_free);
if (cdata->profiles)
g_list_free_full (cdata->profiles, (GDestroyNotify) g_free);
g_free (cdata);
}
}
void
gst_nv_encoder_get_encoder_caps (gpointer session, const GUID * encode_guid,
GstNvEncoderDeviceCaps * device_caps)
{
GstNvEncoderDeviceCaps dev_caps = { 0, };
NV_ENC_CAPS_PARAM caps_param = { 0, };
NVENCSTATUS status;
GUID guid = *encode_guid;
GST_DEBUG_CATEGORY_INIT (gst_nv_encoder_debug, "nvencoder", 0, "nvencoder");
caps_param.version = gst_nvenc_get_caps_param_version ();
#define CHECK_CAPS(to_query,val,default_val) G_STMT_START { \
gint _val; \
caps_param.capsToQuery = to_query; \
status = NvEncGetEncodeCaps (session, guid, &caps_param, \
&_val); \
if (status != NV_ENC_SUCCESS) { \
GST_WARNING ("Unable to query %s, status: %" \
GST_NVENC_STATUS_FORMAT, G_STRINGIFY (to_query), \
GST_NVENC_STATUS_ARGS (status)); \
val = default_val; \
} else { \
GST_DEBUG ("%s: %d", G_STRINGIFY (to_query), _val); \
val = _val; \
} \
} G_STMT_END
CHECK_CAPS (NV_ENC_CAPS_NUM_MAX_BFRAMES, dev_caps.max_bframes, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORTED_RATECONTROL_MODES,
dev_caps.ratecontrol_modes, NV_ENC_PARAMS_RC_VBR);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_FIELD_ENCODING, dev_caps.field_encoding, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_MONOCHROME, dev_caps.monochrome, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_FMO, dev_caps.fmo, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_QPELMV, dev_caps.qpelmv, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_BDIRECT_MODE, dev_caps.bdirect_mode, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_CABAC, dev_caps.cabac, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_ADAPTIVE_TRANSFORM,
dev_caps.adaptive_transform, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_STEREO_MVC, dev_caps.stereo_mvc, 0);
CHECK_CAPS (NV_ENC_CAPS_NUM_MAX_TEMPORAL_LAYERS, dev_caps.temoral_layers, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_HIERARCHICAL_PFRAMES,
dev_caps.hierarchical_pframes, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_HIERARCHICAL_BFRAMES,
dev_caps.hierarchical_bframes, 0);
CHECK_CAPS (NV_ENC_CAPS_LEVEL_MAX, dev_caps.level_max, 0);
CHECK_CAPS (NV_ENC_CAPS_LEVEL_MIN, dev_caps.level_min, 0);
CHECK_CAPS (NV_ENC_CAPS_SEPARATE_COLOUR_PLANE,
dev_caps.separate_colour_plane, 0);
CHECK_CAPS (NV_ENC_CAPS_WIDTH_MAX, dev_caps.width_max, 4096);
CHECK_CAPS (NV_ENC_CAPS_HEIGHT_MAX, dev_caps.height_max, 4096);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_TEMPORAL_SVC, dev_caps.temporal_svc, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_DYN_RES_CHANGE, dev_caps.dyn_res_change, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_DYN_BITRATE_CHANGE,
dev_caps.dyn_bitrate_change, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_DYN_FORCE_CONSTQP,
dev_caps.dyn_force_constqp, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_DYN_RCMODE_CHANGE,
dev_caps.dyn_rcmode_change, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_SUBFRAME_READBACK,
dev_caps.subframe_readback, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_CONSTRAINED_ENCODING,
dev_caps.constrained_encoding, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_INTRA_REFRESH, dev_caps.intra_refresh, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_CUSTOM_VBV_BUF_SIZE,
dev_caps.custom_vbv_buf_size, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_DYNAMIC_SLICE_MODE,
dev_caps.dynamic_slice_mode, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_REF_PIC_INVALIDATION,
dev_caps.ref_pic_invalidation, 0);
CHECK_CAPS (NV_ENC_CAPS_PREPROC_SUPPORT, dev_caps.preproc_support, 0);
/* NOTE: Async is Windows only */
#ifdef G_OS_WIN32
CHECK_CAPS (NV_ENC_CAPS_ASYNC_ENCODE_SUPPORT,
dev_caps.async_encoding_support, 0);
#endif
CHECK_CAPS (NV_ENC_CAPS_MB_NUM_MAX, dev_caps.mb_num_max, 0);
CHECK_CAPS (NV_ENC_CAPS_MB_PER_SEC_MAX, dev_caps.mb_per_sec_max, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_YUV444_ENCODE, dev_caps.yuv444_encode, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_LOSSLESS_ENCODE, dev_caps.lossless_encode, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_SAO, dev_caps.sao, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_MEONLY_MODE, dev_caps.meonly_mode, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_LOOKAHEAD, dev_caps.lookahead, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_TEMPORAL_AQ, dev_caps.temporal_aq, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_10BIT_ENCODE,
dev_caps.supports_10bit_encode, 0);
CHECK_CAPS (NV_ENC_CAPS_NUM_MAX_LTR_FRAMES, dev_caps.num_max_ltr_frames, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_WEIGHTED_PREDICTION,
dev_caps.weighted_prediction, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_BFRAME_REF_MODE, dev_caps.bframe_ref_mode, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_EMPHASIS_LEVEL_MAP,
dev_caps.emphasis_level_map, 0);
CHECK_CAPS (NV_ENC_CAPS_WIDTH_MIN, dev_caps.width_min, 16);
CHECK_CAPS (NV_ENC_CAPS_HEIGHT_MIN, dev_caps.height_min, 16);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_MULTIPLE_REF_FRAMES,
dev_caps.multiple_ref_frames, 0);
#undef CHECK_CAPS
*device_caps = dev_caps;
}
void
gst_nv_encoder_merge_device_caps (const GstNvEncoderDeviceCaps * a,
const GstNvEncoderDeviceCaps * b, GstNvEncoderDeviceCaps * merged)
{
GstNvEncoderDeviceCaps caps;
#define SELECT_MAX(value) G_STMT_START { \
caps.value = MAX (a->value, b->value); \
} G_STMT_END
#define SELECT_MIN(value) G_STMT_START { \
caps.value = MAX (MIN (a->value, b->value), 1); \
} G_STMT_END
SELECT_MAX (max_bframes);
SELECT_MAX (ratecontrol_modes);
SELECT_MAX (field_encoding);
SELECT_MAX (monochrome);
SELECT_MAX (fmo);
SELECT_MAX (qpelmv);
SELECT_MAX (bdirect_mode);
SELECT_MAX (cabac);
SELECT_MAX (adaptive_transform);
SELECT_MAX (stereo_mvc);
SELECT_MAX (temoral_layers);
SELECT_MAX (hierarchical_pframes);
SELECT_MAX (hierarchical_bframes);
SELECT_MAX (level_max);
SELECT_MAX (level_min);
SELECT_MAX (separate_colour_plane);
SELECT_MAX (width_max);
SELECT_MAX (height_max);
SELECT_MAX (temporal_svc);
SELECT_MAX (dyn_res_change);
SELECT_MAX (dyn_bitrate_change);
SELECT_MAX (dyn_force_constqp);
SELECT_MAX (dyn_rcmode_change);
SELECT_MAX (subframe_readback);
SELECT_MAX (constrained_encoding);
SELECT_MAX (intra_refresh);
SELECT_MAX (custom_vbv_buf_size);
SELECT_MAX (dynamic_slice_mode);
SELECT_MAX (ref_pic_invalidation);
SELECT_MAX (preproc_support);
SELECT_MAX (async_encoding_support);
SELECT_MAX (mb_num_max);
SELECT_MAX (mb_per_sec_max);
SELECT_MAX (yuv444_encode);
SELECT_MAX (lossless_encode);
SELECT_MAX (sao);
SELECT_MAX (meonly_mode);
SELECT_MAX (lookahead);
SELECT_MAX (temporal_aq);
SELECT_MAX (supports_10bit_encode);
SELECT_MAX (num_max_ltr_frames);
SELECT_MAX (weighted_prediction);
SELECT_MAX (bframe_ref_mode);
SELECT_MAX (emphasis_level_map);
SELECT_MIN (width_min);
SELECT_MIN (height_min);
SELECT_MAX (multiple_ref_frames);
#undef SELECT_MAX
#undef SELECT_MIN
*merged = caps;
}

View file

@ -80,6 +80,88 @@ typedef enum
GST_NV_ENCODER_RC_MODE_VBR_HQ, GST_NV_ENCODER_RC_MODE_VBR_HQ,
} GstNvEncoderRCMode; } GstNvEncoderRCMode;
typedef struct
{
gint max_bframes;
gint ratecontrol_modes;
gint field_encoding;
gint monochrome;
gint fmo;
gint qpelmv;
gint bdirect_mode;
gint cabac;
gint adaptive_transform;
gint stereo_mvc;
gint temoral_layers;
gint hierarchical_pframes;
gint hierarchical_bframes;
gint level_max;
gint level_min;
gint separate_colour_plane;
gint width_max;
gint height_max;
gint temporal_svc;
gint dyn_res_change;
gint dyn_bitrate_change;
gint dyn_force_constqp;
gint dyn_rcmode_change;
gint subframe_readback;
gint constrained_encoding;
gint intra_refresh;
gint custom_vbv_buf_size;
gint dynamic_slice_mode;
gint ref_pic_invalidation;
gint preproc_support;
gint async_encoding_support;
gint mb_num_max;
gint mb_per_sec_max;
gint yuv444_encode;
gint lossless_encode;
gint sao;
gint meonly_mode;
gint lookahead;
gint temporal_aq;
gint supports_10bit_encode;
gint num_max_ltr_frames;
gint weighted_prediction;
gint bframe_ref_mode;
gint emphasis_level_map;
gint width_min;
gint height_min;
gint multiple_ref_frames;
} GstNvEncoderDeviceCaps;
typedef enum
{
GST_NV_ENCODER_DEVICE_D3D11,
GST_NV_ENCODER_DEVICE_CUDA,
GST_NV_ENCODER_DEVICE_AUTO_SELECT,
} GstNvEncoderDeviceMode;
typedef struct
{
GstCaps *sink_caps;
GstCaps *src_caps;
guint cuda_device_id;
gint64 adapter_luid;
GstNvEncoderDeviceMode device_mode;
GstNvEncoderDeviceCaps device_caps;
GList *formats;
GList *profiles;
/* auto gpu select mode */
guint adapter_luid_size;
gint64 adapter_luid_list[8];
guint cuda_device_id_size;
guint cuda_device_id_list[8];
gint ref_count;
} GstNvEncoderClassData;
typedef struct typedef struct
{ {
/* without ref */ /* without ref */
@ -101,6 +183,14 @@ typedef struct
gboolean is_eos; gboolean is_eos;
} GstNvEncoderTask; } GstNvEncoderTask;
typedef struct
{
GstNvEncoderDeviceMode device_mode;
guint cuda_device_id;
gint64 adapter_luid;
GstObject *device;
} GstNvEncoderDeviceData;
struct _GstNvEncoder struct _GstNvEncoder
{ {
GstVideoEncoder parent; GstVideoEncoder parent;
@ -127,6 +217,11 @@ struct _GstNvEncoderClass
GstNvEncoderReconfigure (*check_reconfigure) (GstNvEncoder * encoder, GstNvEncoderReconfigure (*check_reconfigure) (GstNvEncoder * encoder,
NV_ENC_CONFIG * config); NV_ENC_CONFIG * config);
gboolean (*select_device) (GstNvEncoder * encoder,
const GstVideoInfo * info,
GstBuffer * buffer,
GstNvEncoderDeviceData * data);
}; };
GType gst_nv_encoder_get_type (void); GType gst_nv_encoder_get_type (void);
@ -142,12 +237,26 @@ void gst_nv_encoder_preset_to_guid (GstNvEncoderPreset preset,
NV_ENC_PARAMS_RC_MODE gst_nv_encoder_rc_mode_to_native (GstNvEncoderRCMode rc_mode); NV_ENC_PARAMS_RC_MODE gst_nv_encoder_rc_mode_to_native (GstNvEncoderRCMode rc_mode);
void gst_nv_encoder_set_cuda_device_id (GstNvEncoder * encoder, void gst_nv_encoder_set_device_mode (GstNvEncoder * encoder,
guint device_id); GstNvEncoderDeviceMode mode,
guint cuda_device_id,
void gst_nv_encoder_set_dxgi_adapter_luid (GstNvEncoder * encoder,
gint64 adapter_luid); gint64 adapter_luid);
GstNvEncoderClassData * gst_nv_encoder_class_data_new (void);
GstNvEncoderClassData * gst_nv_encoder_class_data_ref (GstNvEncoderClassData * cdata);
void gst_nv_encoder_class_data_unref (GstNvEncoderClassData * cdata);
void gst_nv_encoder_get_encoder_caps (gpointer session,
const GUID * encode_guid,
GstNvEncoderDeviceCaps * device_caps);
void gst_nv_encoder_merge_device_caps (const GstNvEncoderDeviceCaps * a,
const GstNvEncoderDeviceCaps * b,
GstNvEncoderDeviceCaps * merged);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstNvEncoder, gst_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstNvEncoder, gst_object_unref)
G_END_DECLS G_END_DECLS

File diff suppressed because it is too large Load diff

View file

@ -23,15 +23,18 @@
G_BEGIN_DECLS G_BEGIN_DECLS
void gst_nv_h264_encoder_register_cuda (GstPlugin * plugin, GstNvEncoderClassData * gst_nv_h264_encoder_register_cuda (GstPlugin * plugin,
GstCudaContext * context, GstCudaContext * context,
guint rank); guint rank);
#ifdef GST_CUDA_HAS_D3D #ifdef GST_CUDA_HAS_D3D
void gst_nv_h264_encoder_register_d3d11 (GstPlugin * plugin, GstNvEncoderClassData * gst_nv_h264_encoder_register_d3d11 (GstPlugin * plugin,
GstD3D11Device * device, GstD3D11Device * device,
guint rank); guint rank);
#endif #endif
void gst_nv_h264_encoder_register_auto_select (GstPlugin * plugin,
GList * device_caps,
guint rank);
G_END_DECLS G_END_DECLS

View file

@ -33,69 +33,6 @@ GST_DEBUG_CATEGORY_STATIC (gst_nv_h265_encoder_debug);
static GTypeClass *parent_class = NULL; static GTypeClass *parent_class = NULL;
typedef struct
{
gint max_bframes;
gint ratecontrol_modes;
gint field_encoding;
gint monochrome;
gint fmo;
gint qpelmv;
gint bdirect_mode;
gint cabac;
gint adaptive_transform;
gint stereo_mvc;
gint temoral_layers;
gint hierarchical_pframes;
gint hierarchical_bframes;
gint level_max;
gint level_min;
gint separate_colour_plane;
gint width_max;
gint height_max;
gint temporal_svc;
gint dyn_res_change;
gint dyn_bitrate_change;
gint dyn_force_constqp;
gint dyn_rcmode_change;
gint subframe_readback;
gint constrained_encoding;
gint intra_refresh;
gint custom_vbv_buf_size;
gint dynamic_slice_mode;
gint ref_pic_invalidation;
gint preproc_support;
gint async_encoding_support;
gint mb_num_max;
gint mb_per_sec_max;
gint yuv444_encode;
gint lossless_encode;
gint sao;
gint meonly_mode;
gint lookahead;
gint temporal_aq;
gint supports_10bit_encode;
gint num_max_ltr_frames;
gint weighted_prediction;
gint bframe_ref_mode;
gint emphasis_level_map;
gint width_min;
gint height_min;
gint multiple_ref_frames;
} GstNvH265EncoderDeviceCaps;
typedef struct
{
GstCaps *sink_caps;
GstCaps *src_caps;
guint cuda_device_id;
gint64 adapter_luid;
gboolean d3d11_mode;
GstNvH265EncoderDeviceCaps dev_caps;
} GstNvH265EncoderClassData;
enum enum
{ {
PROP_0, PROP_0,
@ -187,7 +124,12 @@ typedef struct _GstNvH265Encoder
GstNvH265EncoderStreamFormat stream_format; GstNvH265EncoderStreamFormat stream_format;
GstH265Parser *parser; GstH265Parser *parser;
GstNvEncoderDeviceMode selected_device_mode;
/* Properties */ /* Properties */
guint cuda_device_id;
gint64 adapter_luid;
GstNvEncoderPreset preset; GstNvEncoderPreset preset;
gboolean weighted_pred; gboolean weighted_pred;
@ -226,11 +168,20 @@ typedef struct _GstNvH265EncoderClass
{ {
GstNvEncoderClass parent_class; GstNvEncoderClass parent_class;
GstNvH265EncoderDeviceCaps dev_caps;
guint cuda_device_id; guint cuda_device_id;
gint64 adapter_luid; gint64 adapter_luid;
gboolean d3d11_mode;
GstNvEncoderDeviceMode device_mode;
/* representative device caps */
GstNvEncoderDeviceCaps device_caps;
/* auto gpu select mode */
guint cuda_device_id_list[8];
guint cuda_device_id_size;
gint64 adapter_luid_list[8];
guint adapter_luid_size;
} GstNvH265EncoderClass; } GstNvH265EncoderClass;
#define GST_NV_H265_ENCODER(object) ((GstNvH265Encoder *) (object)) #define GST_NV_H265_ENCODER(object) ((GstNvH265Encoder *) (object))
@ -254,6 +205,9 @@ static GstBuffer *gst_nv_h265_encoder_create_output_buffer (GstNvEncoder *
static GstNvEncoderReconfigure static GstNvEncoderReconfigure
gst_nv_h265_encoder_check_reconfigure (GstNvEncoder * encoder, gst_nv_h265_encoder_check_reconfigure (GstNvEncoder * encoder,
NV_ENC_CONFIG * config); NV_ENC_CONFIG * config);
static gboolean gst_nv_h265_encoder_select_device (GstNvEncoder * encoder,
const GstVideoInfo * info, GstBuffer * buffer,
GstNvEncoderDeviceData * data);
static void static void
gst_nv_h265_encoder_class_init (GstNvH265EncoderClass * klass, gpointer data) gst_nv_h265_encoder_class_init (GstNvH265EncoderClass * klass, gpointer data)
@ -262,8 +216,8 @@ gst_nv_h265_encoder_class_init (GstNvH265EncoderClass * klass, gpointer data)
GstElementClass *element_class = GST_ELEMENT_CLASS (klass); GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
GstVideoEncoderClass *videoenc_class = GST_VIDEO_ENCODER_CLASS (klass); GstVideoEncoderClass *videoenc_class = GST_VIDEO_ENCODER_CLASS (klass);
GstNvEncoderClass *nvenc_class = GST_NV_ENCODER_CLASS (klass); GstNvEncoderClass *nvenc_class = GST_NV_ENCODER_CLASS (klass);
GstNvH265EncoderClassData *cdata = (GstNvH265EncoderClassData *) data; GstNvEncoderClassData *cdata = (GstNvEncoderClassData *) data;
GstNvH265EncoderDeviceCaps *dev_caps = &cdata->dev_caps; GstNvEncoderDeviceCaps *dev_caps = &cdata->device_caps;
GParamFlags param_flags = (GParamFlags) (G_PARAM_READWRITE | GParamFlags param_flags = (GParamFlags) (G_PARAM_READWRITE |
GST_PARAM_MUTABLE_PLAYING | G_PARAM_STATIC_STRINGS); GST_PARAM_MUTABLE_PLAYING | G_PARAM_STATIC_STRINGS);
GParamFlags conditional_param_flags = (GParamFlags) (G_PARAM_READWRITE | GParamFlags conditional_param_flags = (GParamFlags) (G_PARAM_READWRITE |
@ -276,19 +230,41 @@ gst_nv_h265_encoder_class_init (GstNvH265EncoderClass * klass, gpointer data)
object_class->set_property = gst_nv_h265_encoder_set_property; object_class->set_property = gst_nv_h265_encoder_set_property;
object_class->get_property = gst_nv_h265_encoder_get_property; object_class->get_property = gst_nv_h265_encoder_get_property;
if (cdata->d3d11_mode) { switch (cdata->device_mode) {
g_object_class_install_property (object_class, PROP_ADAPTER_LUID, case GST_NV_ENCODER_DEVICE_CUDA:
g_param_spec_int64 ("adapter-luid", "Adapter LUID",
"DXGI Adapter LUID (Locally Unique Identifier) of associated GPU",
G_MININT64, G_MAXINT64, cdata->adapter_luid,
(GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
} else {
g_object_class_install_property (object_class, PROP_CUDA_DEVICE_ID, g_object_class_install_property (object_class, PROP_CUDA_DEVICE_ID,
g_param_spec_uint ("cuda-device-id", "CUDA Device ID", g_param_spec_uint ("cuda-device-id", "CUDA Device ID",
"CUDA device ID of associated GPU", "CUDA device ID of associated GPU",
0, G_MAXINT, cdata->cuda_device_id, 0, G_MAXINT, cdata->cuda_device_id,
(GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS))); (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
break;
case GST_NV_ENCODER_DEVICE_D3D11:
g_object_class_install_property (object_class, PROP_ADAPTER_LUID,
g_param_spec_int64 ("adapter-luid", "Adapter LUID",
"DXGI Adapter LUID (Locally Unique Identifier) of associated GPU",
G_MININT64, G_MAXINT64, cdata->adapter_luid,
(GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
break;
case GST_NV_ENCODER_DEVICE_AUTO_SELECT:
if (cdata->cuda_device_id_size > 0) {
g_object_class_install_property (object_class, PROP_CUDA_DEVICE_ID,
g_param_spec_uint ("cuda-device-id", "CUDA Device ID",
"CUDA device ID to use",
0, G_MAXINT, cdata->cuda_device_id, conditional_param_flags));
} }
if (cdata->adapter_luid_size > 0) {
g_object_class_install_property (object_class, PROP_ADAPTER_LUID,
g_param_spec_int64 ("adapter-luid", "Adapter LUID",
"DXGI Adapter LUID (Locally Unique Identifier) to use",
G_MININT64, G_MAXINT64, cdata->adapter_luid,
conditional_param_flags));
}
break;
default:
g_assert_not_reached ();
break;
}
g_object_class_install_property (object_class, PROP_PRESET, g_object_class_install_property (object_class, PROP_PRESET,
g_param_spec_enum ("preset", "Encoding Preset", g_param_spec_enum ("preset", "Encoding Preset",
"Encoding Preset", GST_TYPE_NV_ENCODER_PRESET, "Encoding Preset", GST_TYPE_NV_ENCODER_PRESET,
@ -418,18 +394,31 @@ gst_nv_h265_encoder_class_init (GstNvH265EncoderClass * klass, gpointer data)
"ignored if negotiated stream-format is \"hvc1\"", "ignored if negotiated stream-format is \"hvc1\"",
DEFAULT_REPEAT_SEQUENCE_HEADER, param_flags)); DEFAULT_REPEAT_SEQUENCE_HEADER, param_flags));
if (cdata->d3d11_mode) { switch (cdata->device_mode) {
gst_element_class_set_metadata (element_class, case GST_NV_ENCODER_DEVICE_CUDA:
"NVENC H.265 Video Encoder Direct3D11 Mode", gst_element_class_set_static_metadata (element_class,
"Codec/Encoder/Video/Hardware",
"Encode H.265 video streams using NVCODEC API Direct3D11 Mode",
"Seungha Yang <seungha@centricular.com>");
} else {
gst_element_class_set_metadata (element_class,
"NVENC H.265 Video Encoder CUDA Mode", "NVENC H.265 Video Encoder CUDA Mode",
"Codec/Encoder/Video/Hardware", "Codec/Encoder/Video/Hardware",
"Encode H.265 video streams using NVCODEC API CUDA Mode", "Encode H.265 video streams using NVCODEC API CUDA Mode",
"Seungha Yang <seungha@centricular.com>"); "Seungha Yang <seungha@centricular.com>");
break;
case GST_NV_ENCODER_DEVICE_D3D11:
gst_element_class_set_static_metadata (element_class,
"NVENC H.265 Video Encoder Direct3D11 Mode",
"Codec/Encoder/Video/Hardware",
"Encode H.264 video streams using NVCODEC API Direct3D11 Mode",
"Seungha Yang <seungha@centricular.com>");
break;
case GST_NV_ENCODER_DEVICE_AUTO_SELECT:
gst_element_class_set_static_metadata (element_class,
"NVENC H.265 Video Encoder Auto GPU select Mode",
"Codec/Encoder/Video/Hardware",
"Encode H.264 video streams using NVCODEC API auto GPU select Mode",
"Seungha Yang <seungha@centricular.com>");
break;
default:
g_assert_not_reached ();
break;
} }
gst_element_class_add_pad_template (element_class, gst_element_class_add_pad_template (element_class,
@ -448,15 +437,21 @@ gst_nv_h265_encoder_class_init (GstNvH265EncoderClass * klass, gpointer data)
GST_DEBUG_FUNCPTR (gst_nv_h265_encoder_create_output_buffer); GST_DEBUG_FUNCPTR (gst_nv_h265_encoder_create_output_buffer);
nvenc_class->check_reconfigure = nvenc_class->check_reconfigure =
GST_DEBUG_FUNCPTR (gst_nv_h265_encoder_check_reconfigure); GST_DEBUG_FUNCPTR (gst_nv_h265_encoder_check_reconfigure);
nvenc_class->select_device =
GST_DEBUG_FUNCPTR (gst_nv_h265_encoder_select_device);
klass->dev_caps = cdata->dev_caps; klass->device_caps = cdata->device_caps;
klass->cuda_device_id = cdata->cuda_device_id; klass->cuda_device_id = cdata->cuda_device_id;
klass->adapter_luid = cdata->adapter_luid; klass->adapter_luid = cdata->adapter_luid;
klass->d3d11_mode = cdata->d3d11_mode; klass->device_mode = cdata->device_mode;
klass->cuda_device_id_size = cdata->cuda_device_id_size;
klass->adapter_luid_size = cdata->adapter_luid_size;
memcpy (klass->cuda_device_id_list, cdata->cuda_device_id_list,
sizeof (klass->cuda_device_id_list));
memcpy (klass->adapter_luid_list, cdata->adapter_luid_list,
sizeof (klass->adapter_luid_list));
gst_caps_unref (cdata->sink_caps); gst_nv_encoder_class_data_unref (cdata);
gst_caps_unref (cdata->src_caps);
g_free (cdata);
} }
static void static void
@ -466,6 +461,9 @@ gst_nv_h265_encoder_init (GstNvH265Encoder * self)
g_mutex_init (&self->prop_lock); g_mutex_init (&self->prop_lock);
self->selected_device_mode = klass->device_mode;
self->cuda_device_id = klass->cuda_device_id;
self->adapter_luid = klass->adapter_luid;
self->preset = DEFAULT_PRESET; self->preset = DEFAULT_PRESET;
self->weighted_pred = DEFAULT_WEIGHTED_PRED; self->weighted_pred = DEFAULT_WEIGHTED_PRED;
self->gop_size = DEFAULT_GOP_SIZE; self->gop_size = DEFAULT_GOP_SIZE;
@ -498,13 +496,8 @@ gst_nv_h265_encoder_init (GstNvH265Encoder * self)
self->parser = gst_h265_parser_new (); self->parser = gst_h265_parser_new ();
if (klass->d3d11_mode) { gst_nv_encoder_set_device_mode (GST_NV_ENCODER (self), klass->device_mode,
gst_nv_encoder_set_dxgi_adapter_luid (GST_NV_ENCODER (self), klass->cuda_device_id, klass->adapter_luid);
klass->adapter_luid);
} else {
gst_nv_encoder_set_cuda_device_id (GST_NV_ENCODER (self),
klass->cuda_device_id);
}
} }
static void static void
@ -622,9 +615,43 @@ gst_nv_h265_encoder_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec) const GValue * value, GParamSpec * pspec)
{ {
GstNvH265Encoder *self = GST_NV_H265_ENCODER (object); GstNvH265Encoder *self = GST_NV_H265_ENCODER (object);
GstNvH265EncoderClass *klass = GST_NV_H265_ENCODER_GET_CLASS (self);
g_mutex_lock (&self->prop_lock); g_mutex_lock (&self->prop_lock);
switch (prop_id) { switch (prop_id) {
case PROP_ADAPTER_LUID:{
gint64 adapter_luid = g_value_get_int64 (value);
gboolean is_valid = FALSE;
for (guint i = 0; i < klass->adapter_luid_size; i++) {
if (klass->adapter_luid_list[i] == adapter_luid) {
self->adapter_luid = adapter_luid;
is_valid = TRUE;
break;
}
}
if (!is_valid)
g_warning ("%" G_GINT64_FORMAT " is not a valid adapter luid",
adapter_luid);
break;
}
case PROP_CUDA_DEVICE_ID:{
guint cuda_device_id = g_value_get_uint (value);
gboolean is_valid = FALSE;
for (guint i = 0; i < klass->cuda_device_id_size; i++) {
if (klass->cuda_device_id_list[i] == cuda_device_id) {
self->cuda_device_id = cuda_device_id;
is_valid = TRUE;
break;
}
}
if (!is_valid)
g_warning ("%d is not a valid cuda device id", cuda_device_id);
break;
}
case PROP_PRESET:{ case PROP_PRESET:{
GstNvEncoderPreset preset = (GstNvEncoderPreset) g_value_get_enum (value); GstNvEncoderPreset preset = (GstNvEncoderPreset) g_value_get_enum (value);
if (preset != self->preset) { if (preset != self->preset) {
@ -737,14 +764,13 @@ gst_nv_h265_encoder_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec) GValue * value, GParamSpec * pspec)
{ {
GstNvH265Encoder *self = GST_NV_H265_ENCODER (object); GstNvH265Encoder *self = GST_NV_H265_ENCODER (object);
GstNvH265EncoderClass *klass = GST_NV_H265_ENCODER_GET_CLASS (self);
switch (prop_id) { switch (prop_id) {
case PROP_ADAPTER_LUID: case PROP_ADAPTER_LUID:
g_value_set_int64 (value, klass->adapter_luid); g_value_set_int64 (value, self->adapter_luid);
break; break;
case PROP_CUDA_DEVICE_ID: case PROP_CUDA_DEVICE_ID:
g_value_set_uint (value, klass->cuda_device_id); g_value_set_uint (value, self->cuda_device_id);
break; break;
case PROP_PRESET: case PROP_PRESET:
g_value_set_enum (value, self->preset); g_value_set_enum (value, self->preset);
@ -967,7 +993,7 @@ gst_nv_h265_encoder_set_format (GstNvEncoder * encoder,
{ {
GstNvH265Encoder *self = GST_NV_H265_ENCODER (encoder); GstNvH265Encoder *self = GST_NV_H265_ENCODER (encoder);
GstNvH265EncoderClass *klass = GST_NV_H265_ENCODER_GET_CLASS (self); GstNvH265EncoderClass *klass = GST_NV_H265_ENCODER_GET_CLASS (self);
GstNvH265EncoderDeviceCaps *dev_caps = &klass->dev_caps; GstNvEncoderDeviceCaps *dev_caps = &klass->device_caps;
NV_ENC_RC_PARAMS *rc_params; NV_ENC_RC_PARAMS *rc_params;
GstVideoInfo *info = &state->info; GstVideoInfo *info = &state->info;
NVENCSTATUS status; NVENCSTATUS status;
@ -980,6 +1006,11 @@ gst_nv_h265_encoder_set_format (GstNvEncoder * encoder,
GUID selected_profile = NV_ENC_CODEC_PROFILE_AUTOSELECT_GUID; GUID selected_profile = NV_ENC_CODEC_PROFILE_AUTOSELECT_GUID;
guint chroma_format_index = 1; guint chroma_format_index = 1;
guint bitdepth_minus8 = 0; guint bitdepth_minus8 = 0;
gboolean bframe_aborted = FALSE;
gboolean weight_pred_aborted = FALSE;
gboolean vbv_buffer_size_aborted = FALSE;
gboolean lookahead_aborted = FALSE;
gboolean temporal_aq_aborted = FALSE;
self->stream_format = GST_NV_H265_ENCODER_BYTE_STREAM; self->stream_format = GST_NV_H265_ENCODER_BYTE_STREAM;
@ -1040,6 +1071,52 @@ gst_nv_h265_encoder_set_format (GstNvEncoder * encoder,
g_mutex_lock (&self->prop_lock); g_mutex_lock (&self->prop_lock);
if (klass->device_mode == GST_NV_ENCODER_DEVICE_AUTO_SELECT) {
GstNvEncoderDeviceCaps dev_caps;
gst_nv_encoder_get_encoder_caps (session,
&NV_ENC_CODEC_HEVC_GUID, &dev_caps);
if (self->bframes > 0 && !dev_caps.max_bframes) {
self->bframes = 0;
bframe_aborted = TRUE;
GST_INFO_OBJECT (self, "B-frame was enabled but not support by device");
}
if (self->weighted_pred && !dev_caps.weighted_prediction) {
self->weighted_pred = FALSE;
weight_pred_aborted = TRUE;
GST_INFO_OBJECT (self,
"Weighted prediction was enabled but not support by device");
}
if (self->vbv_buffer_size && !dev_caps.custom_vbv_buf_size) {
self->vbv_buffer_size = 0;
vbv_buffer_size_aborted = TRUE;
GST_INFO_OBJECT (self,
"VBV buffer size was specified but not supported by device");
}
if (self->rc_lookahead && !dev_caps.lookahead) {
self->rc_lookahead = 0;
lookahead_aborted = TRUE;
GST_INFO_OBJECT (self,
"VBV buffer size was specified but not supported by device");
}
if (self->temporal_aq && !dev_caps.temporal_aq) {
self->temporal_aq = FALSE;
temporal_aq_aborted = TRUE;
GST_INFO_OBJECT (self,
"temporal-aq was enabled but not supported by device");
}
}
init_params->version = gst_nvenc_get_initialize_params_version (); init_params->version = gst_nvenc_get_initialize_params_version ();
init_params->encodeGUID = NV_ENC_CODEC_HEVC_GUID; init_params->encodeGUID = NV_ENC_CODEC_HEVC_GUID;
@ -1219,6 +1296,17 @@ gst_nv_h265_encoder_set_format (GstNvEncoder * encoder,
g_mutex_unlock (&self->prop_lock); g_mutex_unlock (&self->prop_lock);
if (bframe_aborted)
g_object_notify (G_OBJECT (self), "b-frames");
if (weight_pred_aborted)
g_object_notify (G_OBJECT (self), "weighted-pred");
if (vbv_buffer_size_aborted)
g_object_notify (G_OBJECT (self), "vbv-buffer-size");
if (lookahead_aborted)
g_object_notify (G_OBJECT (self), "rc-lookahead");
if (temporal_aq_aborted)
g_object_notify (G_OBJECT (self), "temporal-aq");
return TRUE; return TRUE;
} }
@ -1514,7 +1602,7 @@ gst_nv_h265_encoder_check_reconfigure (GstNvEncoder * encoder,
if (self->bitrate_updated) { if (self->bitrate_updated) {
GstNvH265EncoderClass *klass = GST_NV_H265_ENCODER_GET_CLASS (self); GstNvH265EncoderClass *klass = GST_NV_H265_ENCODER_GET_CLASS (self);
if (klass->dev_caps.dyn_bitrate_change > 0) { if (klass->device_caps.dyn_bitrate_change > 0) {
config->rcParams.averageBitRate = self->bitrate * 1024; config->rcParams.averageBitRate = self->bitrate * 1024;
config->rcParams.maxBitRate = self->max_bitrate * 1024; config->rcParams.maxBitRate = self->max_bitrate * 1024;
reconfig = GST_NV_ENCODER_RECONFIGURE_BITRATE; reconfig = GST_NV_ENCODER_RECONFIGURE_BITRATE;
@ -1532,13 +1620,113 @@ done:
return reconfig; return reconfig;
} }
static GstNvH265EncoderClassData * static gboolean
gst_nv_h265_encoder_select_device (GstNvEncoder * encoder,
const GstVideoInfo * info, GstBuffer * buffer,
GstNvEncoderDeviceData * data)
{
GstNvH265Encoder *self = GST_NV_H265_ENCODER (encoder);
GstNvH265EncoderClass *klass = GST_NV_H265_ENCODER_GET_CLASS (self);
GstMemory *mem;
memset (data, 0, sizeof (GstNvEncoderDeviceData));
g_assert (klass->device_mode == GST_NV_ENCODER_DEVICE_AUTO_SELECT);
mem = gst_buffer_peek_memory (buffer, 0);
if (klass->cuda_device_id_size > 0 && gst_is_cuda_memory (mem)) {
GstCudaMemory *cmem = GST_CUDA_MEMORY_CAST (mem);
GstCudaContext *context = cmem->context;
guint device_id;
gboolean found = FALSE;
g_object_get (context, "cuda-device-id", &device_id, nullptr);
data->device_mode = GST_NV_ENCODER_DEVICE_CUDA;
self->selected_device_mode = GST_NV_ENCODER_DEVICE_CUDA;
for (guint i = 0; i < klass->cuda_device_id_size; i++) {
if (klass->cuda_device_id_list[i] == device_id) {
data->cuda_device_id = device_id;
found = TRUE;
break;
}
}
if (!found) {
GST_INFO_OBJECT (self,
"Upstream CUDA device is not in supported device list");
data->cuda_device_id = self->cuda_device_id;
} else {
data->device = (GstObject *) gst_object_ref (context);
}
if (data->cuda_device_id != self->cuda_device_id) {
self->cuda_device_id = data->cuda_device_id;
g_object_notify (G_OBJECT (self), "cuda-device-id");
}
return TRUE;
}
#ifdef GST_CUDA_HAS_D3D
if (klass->adapter_luid_size > 0 && gst_is_d3d11_memory (mem)) {
GstD3D11Memory *dmem = GST_D3D11_MEMORY_CAST (mem);
GstD3D11Device *device = dmem->device;
gint64 adapter_luid;
gboolean found = FALSE;
g_object_get (device, "adapter-luid", &adapter_luid, nullptr);
data->device_mode = GST_NV_ENCODER_DEVICE_D3D11;
self->selected_device_mode = GST_NV_ENCODER_DEVICE_D3D11;
for (guint i = 0; i < klass->cuda_device_id_size; i++) {
if (klass->adapter_luid_list[i] == adapter_luid) {
data->adapter_luid = adapter_luid;
found = TRUE;
break;
}
}
if (!found) {
GST_INFO_OBJECT (self,
"Upstream D3D11 device is not in supported device list");
data->adapter_luid = self->adapter_luid;
} else {
data->device = (GstObject *) gst_object_ref (device);
}
if (data->adapter_luid != self->adapter_luid) {
self->adapter_luid = data->adapter_luid;
g_object_notify (G_OBJECT (self), "adapter-luid");
}
return TRUE;
}
#endif
if (klass->cuda_device_id_size > 0 &&
(self->selected_device_mode != GST_NV_ENCODER_DEVICE_D3D11)) {
GST_INFO_OBJECT (self, "Upstream is system memory, use CUDA mode");
data->device_mode = GST_NV_ENCODER_DEVICE_CUDA;
data->cuda_device_id = self->cuda_device_id;
} else {
GST_INFO_OBJECT (self, "Upstream is system memory, use CUDA mode");
data->device_mode = GST_NV_ENCODER_DEVICE_D3D11;
data->adapter_luid = self->adapter_luid;
}
self->selected_device_mode = data->device_mode;
return TRUE;
}
static GstNvEncoderClassData *
gst_nv_h265_encoder_create_class_data (GstObject * device, gpointer session, gst_nv_h265_encoder_create_class_data (GstObject * device, gpointer session,
gboolean d3d11_mode) GstNvEncoderDeviceMode device_mode)
{ {
NVENCSTATUS status; NVENCSTATUS status;
GstNvH265EncoderDeviceCaps dev_caps = { 0, }; GstNvEncoderDeviceCaps dev_caps = { 0, };
NV_ENC_CAPS_PARAM caps_param = { 0, };
GUID profile_guids[16]; GUID profile_guids[16];
NV_ENC_BUFFER_FORMAT input_formats[16]; NV_ENC_BUFFER_FORMAT input_formats[16];
guint32 profile_guid_count = 0; guint32 profile_guid_count = 0;
@ -1550,7 +1738,7 @@ gst_nv_h265_encoder_create_class_data (GstObject * device, gpointer session,
std::set < std::string > profiles; std::set < std::string > profiles;
std::string profile_str; std::string profile_str;
std::string resolution_str; std::string resolution_str;
GstNvH265EncoderClassData *cdata; GstNvEncoderClassData *cdata;
GstCaps *sink_caps; GstCaps *sink_caps;
GstCaps *system_caps; GstCaps *system_caps;
@ -1568,93 +1756,7 @@ gst_nv_h265_encoder_create_class_data (GstObject * device, gpointer session,
return NULL; return NULL;
} }
caps_param.version = gst_nvenc_get_caps_param_version (); gst_nv_encoder_get_encoder_caps (session, &NV_ENC_CODEC_HEVC_GUID, &dev_caps);
#define CHECK_CAPS(to_query,val,default_val) G_STMT_START { \
gint _val; \
caps_param.capsToQuery = to_query; \
status = NvEncGetEncodeCaps (session, NV_ENC_CODEC_HEVC_GUID, &caps_param, \
&_val); \
if (status != NV_ENC_SUCCESS) { \
GST_WARNING_OBJECT (device, "Unable to query %s, status: %" \
GST_NVENC_STATUS_FORMAT, G_STRINGIFY (to_query), \
GST_NVENC_STATUS_ARGS (status)); \
val = default_val; \
} else { \
GST_DEBUG_OBJECT (device, "%s: %d", G_STRINGIFY (to_query), _val); \
val = _val; \
} \
} G_STMT_END
CHECK_CAPS (NV_ENC_CAPS_NUM_MAX_BFRAMES, dev_caps.max_bframes, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORTED_RATECONTROL_MODES,
dev_caps.ratecontrol_modes, NV_ENC_PARAMS_RC_VBR);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_FIELD_ENCODING, dev_caps.field_encoding, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_MONOCHROME, dev_caps.monochrome, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_FMO, dev_caps.fmo, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_QPELMV, dev_caps.qpelmv, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_BDIRECT_MODE, dev_caps.bdirect_mode, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_CABAC, dev_caps.cabac, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_ADAPTIVE_TRANSFORM,
dev_caps.adaptive_transform, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_STEREO_MVC, dev_caps.stereo_mvc, 0);
CHECK_CAPS (NV_ENC_CAPS_NUM_MAX_TEMPORAL_LAYERS, dev_caps.temoral_layers, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_HIERARCHICAL_PFRAMES,
dev_caps.hierarchical_pframes, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_HIERARCHICAL_BFRAMES,
dev_caps.hierarchical_bframes, 0);
CHECK_CAPS (NV_ENC_CAPS_LEVEL_MAX, dev_caps.level_max, 0);
CHECK_CAPS (NV_ENC_CAPS_LEVEL_MIN, dev_caps.level_min, 0);
CHECK_CAPS (NV_ENC_CAPS_SEPARATE_COLOUR_PLANE,
dev_caps.separate_colour_plane, 0);
CHECK_CAPS (NV_ENC_CAPS_WIDTH_MAX, dev_caps.width_max, 4096);
CHECK_CAPS (NV_ENC_CAPS_HEIGHT_MAX, dev_caps.height_max, 4096);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_TEMPORAL_SVC, dev_caps.temporal_svc, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_DYN_RES_CHANGE, dev_caps.dyn_res_change, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_DYN_BITRATE_CHANGE,
dev_caps.dyn_bitrate_change, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_DYN_FORCE_CONSTQP,
dev_caps.dyn_force_constqp, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_DYN_RCMODE_CHANGE,
dev_caps.dyn_rcmode_change, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_SUBFRAME_READBACK,
dev_caps.subframe_readback, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_CONSTRAINED_ENCODING,
dev_caps.constrained_encoding, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_INTRA_REFRESH, dev_caps.intra_refresh, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_CUSTOM_VBV_BUF_SIZE,
dev_caps.custom_vbv_buf_size, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_DYNAMIC_SLICE_MODE,
dev_caps.dynamic_slice_mode, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_REF_PIC_INVALIDATION,
dev_caps.ref_pic_invalidation, 0);
CHECK_CAPS (NV_ENC_CAPS_PREPROC_SUPPORT, dev_caps.preproc_support, 0);
/* NOTE: Async is Windows only */
#ifdef G_OS_WIN32
CHECK_CAPS (NV_ENC_CAPS_ASYNC_ENCODE_SUPPORT,
dev_caps.async_encoding_support, 0);
#endif
CHECK_CAPS (NV_ENC_CAPS_MB_NUM_MAX, dev_caps.mb_num_max, 0);
CHECK_CAPS (NV_ENC_CAPS_MB_PER_SEC_MAX, dev_caps.mb_per_sec_max, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_YUV444_ENCODE, dev_caps.yuv444_encode, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_LOSSLESS_ENCODE, dev_caps.lossless_encode, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_SAO, dev_caps.sao, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_MEONLY_MODE, dev_caps.meonly_mode, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_LOOKAHEAD, dev_caps.lookahead, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_TEMPORAL_AQ, dev_caps.temporal_aq, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_10BIT_ENCODE,
dev_caps.supports_10bit_encode, 0);
CHECK_CAPS (NV_ENC_CAPS_NUM_MAX_LTR_FRAMES, dev_caps.num_max_ltr_frames, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_WEIGHTED_PREDICTION,
dev_caps.weighted_prediction, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_BFRAME_REF_MODE, dev_caps.bframe_ref_mode, 0);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_EMPHASIS_LEVEL_MAP,
dev_caps.emphasis_level_map, 0);
CHECK_CAPS (NV_ENC_CAPS_WIDTH_MIN, dev_caps.width_min, 16);
CHECK_CAPS (NV_ENC_CAPS_HEIGHT_MIN, dev_caps.height_min, 16);
CHECK_CAPS (NV_ENC_CAPS_SUPPORT_MULTIPLE_REF_FRAMES,
dev_caps.multiple_ref_frames, 0);
#undef CHECK_CAPS
for (guint32 i = 0; i < input_format_count; i++) { for (guint32 i = 0; i < input_format_count; i++) {
switch (input_formats[i]) { switch (input_formats[i]) {
@ -1662,7 +1764,7 @@ gst_nv_h265_encoder_create_class_data (GstObject * device, gpointer session,
formats.insert ("NV12"); formats.insert ("NV12");
break; break;
case NV_ENC_BUFFER_FORMAT_YUV444: case NV_ENC_BUFFER_FORMAT_YUV444:
if (!d3d11_mode && dev_caps.yuv444_encode) if (dev_caps.yuv444_encode)
formats.insert ("Y444"); formats.insert ("Y444");
break; break;
case NV_ENC_BUFFER_FORMAT_YUV420_10BIT: case NV_ENC_BUFFER_FORMAT_YUV420_10BIT:
@ -1753,27 +1855,38 @@ gst_nv_h265_encoder_create_class_data (GstObject * device, gpointer session,
system_caps = gst_caps_from_string (sink_caps_str.c_str ()); system_caps = gst_caps_from_string (sink_caps_str.c_str ());
sink_caps = gst_caps_copy (system_caps); sink_caps = gst_caps_copy (system_caps);
#ifdef GST_CUDA_HAS_D3D #ifdef GST_CUDA_HAS_D3D
if (d3d11_mode) { if (device_mode == GST_NV_ENCODER_DEVICE_D3D11) {
gst_caps_set_features (sink_caps, 0, gst_caps_set_features (sink_caps, 0,
gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY, NULL)); gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY, nullptr));
} else }
#endif #endif
{
if (device_mode == GST_NV_ENCODER_DEVICE_CUDA) {
gst_caps_set_features (sink_caps, 0, gst_caps_set_features (sink_caps, 0,
gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_CUDA_MEMORY, NULL)); gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_CUDA_MEMORY, nullptr));
} }
gst_caps_append (sink_caps, system_caps); gst_caps_append (sink_caps, system_caps);
cdata = g_new0 (GstNvH265EncoderClassData, 1); cdata = gst_nv_encoder_class_data_new ();
cdata->sink_caps = sink_caps; cdata->sink_caps = sink_caps;
cdata->src_caps = gst_caps_from_string (src_caps_str.c_str ()); cdata->src_caps = gst_caps_from_string (src_caps_str.c_str ());
cdata->dev_caps = dev_caps; cdata->device_caps = dev_caps;
cdata->d3d11_mode = d3d11_mode; cdata->device_mode = device_mode;
if (d3d11_mode)
g_object_get (device, "adapter-luid", &cdata->adapter_luid, NULL); /* *INDENT-OFF* */
else for (const auto &iter: formats)
g_object_get (device, "cuda-device-id", &cdata->cuda_device_id, NULL); cdata->formats = g_list_append (cdata->formats, g_strdup (iter.c_str()));
for (const auto &iter: profiles)
cdata->profiles = g_list_append (cdata->profiles, g_strdup (iter.c_str()));
/* *INDENT-ON* */
if (device_mode == GST_NV_ENCODER_DEVICE_D3D11)
g_object_get (device, "adapter-luid", &cdata->adapter_luid, nullptr);
if (device_mode == GST_NV_ENCODER_DEVICE_CUDA)
g_object_get (device, "cuda-device-id", &cdata->cuda_device_id, nullptr);
GST_MINI_OBJECT_FLAG_SET (cdata->sink_caps, GST_MINI_OBJECT_FLAG_SET (cdata->sink_caps,
GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED); GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED);
@ -1783,14 +1896,14 @@ gst_nv_h265_encoder_create_class_data (GstObject * device, gpointer session,
return cdata; return cdata;
} }
void GstNvEncoderClassData *
gst_nv_h265_encoder_register_cuda (GstPlugin * plugin, GstCudaContext * context, gst_nv_h265_encoder_register_cuda (GstPlugin * plugin, GstCudaContext * context,
guint rank) guint rank)
{ {
NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS session_params = { 0, }; NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS session_params = { 0, };
gpointer session; gpointer session;
NVENCSTATUS status; NVENCSTATUS status;
GstNvH265EncoderClassData *cdata; GstNvEncoderClassData *cdata;
GST_DEBUG_CATEGORY_INIT (gst_nv_h265_encoder_debug, "nvh265encoder", 0, GST_DEBUG_CATEGORY_INIT (gst_nv_h265_encoder_debug, "nvh265encoder", 0,
"nvh265encoder"); "nvh265encoder");
@ -1804,15 +1917,17 @@ gst_nv_h265_encoder_register_cuda (GstPlugin * plugin, GstCudaContext * context,
status = NvEncOpenEncodeSessionEx (&session_params, &session); status = NvEncOpenEncodeSessionEx (&session_params, &session);
if (status != NV_ENC_SUCCESS) { if (status != NV_ENC_SUCCESS) {
GST_WARNING_OBJECT (context, "Failed to open session"); GST_WARNING_OBJECT (context, "Failed to open session");
return; return nullptr;
} }
cdata = gst_nv_h265_encoder_create_class_data (GST_OBJECT (context), session, cdata = gst_nv_h265_encoder_create_class_data (GST_OBJECT (context), session,
FALSE); GST_NV_ENCODER_DEVICE_CUDA);
NvEncDestroyEncoder (session); NvEncDestroyEncoder (session);
if (!cdata) if (!cdata)
return; return nullptr;
gst_nv_encoder_class_data_ref (cdata);
GType type; GType type;
gchar *type_name; gchar *type_name;
@ -1852,17 +1967,19 @@ gst_nv_h265_encoder_register_cuda (GstPlugin * plugin, GstCudaContext * context,
g_free (type_name); g_free (type_name);
g_free (feature_name); g_free (feature_name);
return cdata;
} }
#ifdef GST_CUDA_HAS_D3D #ifdef GST_CUDA_HAS_D3D
void GstNvEncoderClassData *
gst_nv_h265_encoder_register_d3d11 (GstPlugin * plugin, GstD3D11Device * device, gst_nv_h265_encoder_register_d3d11 (GstPlugin * plugin, GstD3D11Device * device,
guint rank) guint rank)
{ {
NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS session_params = { 0, }; NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS session_params = { 0, };
gpointer session; gpointer session;
NVENCSTATUS status; NVENCSTATUS status;
GstNvH265EncoderClassData *cdata; GstNvEncoderClassData *cdata;
GST_DEBUG_CATEGORY_INIT (gst_nv_h265_encoder_debug, "nvh265encoder", 0, GST_DEBUG_CATEGORY_INIT (gst_nv_h265_encoder_debug, "nvh265encoder", 0,
"nvh265encoder"); "nvh265encoder");
@ -1876,15 +1993,17 @@ gst_nv_h265_encoder_register_d3d11 (GstPlugin * plugin, GstD3D11Device * device,
status = NvEncOpenEncodeSessionEx (&session_params, &session); status = NvEncOpenEncodeSessionEx (&session_params, &session);
if (status != NV_ENC_SUCCESS) { if (status != NV_ENC_SUCCESS) {
GST_WARNING_OBJECT (device, "Failed to open session"); GST_WARNING_OBJECT (device, "Failed to open session");
return; return nullptr;
} }
cdata = gst_nv_h265_encoder_create_class_data (GST_OBJECT (device), session, cdata = gst_nv_h265_encoder_create_class_data (GST_OBJECT (device), session,
TRUE); GST_NV_ENCODER_DEVICE_D3D11);
NvEncDestroyEncoder (session); NvEncDestroyEncoder (session);
if (!cdata) if (!cdata)
return; return nullptr;
gst_nv_encoder_class_data_ref (cdata);
GType type; GType type;
gchar *type_name; gchar *type_name;
@ -1924,5 +2043,170 @@ gst_nv_h265_encoder_register_d3d11 (GstPlugin * plugin, GstD3D11Device * device,
g_free (type_name); g_free (type_name);
g_free (feature_name); g_free (feature_name);
return cdata;
} }
#endif #endif
void
gst_nv_h265_encoder_register_auto_select (GstPlugin * plugin,
GList * device_caps_list, guint rank)
{
std::set < std::string > formats;
std::set < std::string > profiles;
std::string sink_caps_str;
std::string src_caps_str;
std::string format_str;
std::string profile_str;
std::string resolution_str;
GList *iter;
guint adapter_luid_size = 0;
gint64 adapter_luid_list[8];
guint cuda_device_id_size = 0;
guint cuda_device_id_list[8];
GstNvEncoderDeviceCaps dev_caps;
GstNvEncoderClassData *cdata;
GstCaps *sink_caps = nullptr;
GstCaps *system_caps;
GST_DEBUG_CATEGORY_INIT (gst_nv_h265_encoder_debug, "nvh265encoder", 0,
"nvh265encoder");
for (iter = device_caps_list; iter; iter = g_list_next (iter)) {
GstNvEncoderClassData *cdata = (GstNvEncoderClassData *) iter->data;
GList *walk;
for (walk = cdata->formats; walk; walk = g_list_next (walk))
formats.insert ((gchar *) walk->data);
for (walk = cdata->profiles; walk; walk = g_list_next (walk))
profiles.insert ((gchar *) walk->data);
if (cdata->device_mode == GST_NV_ENCODER_DEVICE_D3D11 &&
adapter_luid_size < G_N_ELEMENTS (adapter_luid_list) - 1) {
adapter_luid_list[adapter_luid_size] = cdata->adapter_luid;
adapter_luid_size++;
}
if (cdata->device_mode == GST_NV_ENCODER_DEVICE_CUDA &&
cuda_device_id_size < G_N_ELEMENTS (cuda_device_id_list) - 1) {
cuda_device_id_list[cuda_device_id_size] = cdata->cuda_device_id;
cuda_device_id_size++;
}
if (iter == device_caps_list) {
dev_caps = cdata->device_caps;
} else {
gst_nv_encoder_merge_device_caps (&dev_caps, &cdata->device_caps,
&dev_caps);
}
}
g_list_free_full (device_caps_list,
(GDestroyNotify) gst_nv_encoder_class_data_unref);
if (formats.empty () || profiles.empty ())
return;
#define APPEND_STRING(dst,set,str) G_STMT_START { \
if (set.find(str) != set.end()) { \
if (!first) \
dst += ", "; \
dst += str; \
first = false; \
} \
} G_STMT_END
if (formats.size () == 1) {
format_str = "format = (string) " + *(formats.begin ());
} else {
bool first = true;
format_str = "format = (string) { ";
APPEND_STRING (format_str, formats, "NV12");
APPEND_STRING (format_str, formats, "P010_10LE");
APPEND_STRING (format_str, formats, "Y444");
APPEND_STRING (format_str, formats, "Y444_16LE");
format_str += " }";
}
if (profiles.size () == 1) {
profile_str = "profile = (string) " + *(profiles.begin ());
} else {
bool first = true;
profile_str = "profile = (string) { ";
APPEND_STRING (profile_str, profiles, "main");
APPEND_STRING (profile_str, profiles, "main-10");
APPEND_STRING (profile_str, profiles, "main-444");
APPEND_STRING (profile_str, profiles, "main-444-10");
profile_str += " }";
}
#undef APPEND_STRING
resolution_str = "width = (int) [ " +
std::to_string (GST_ROUND_UP_16 (dev_caps.width_min))
+ ", " + std::to_string (dev_caps.width_max) + " ]";
resolution_str += ", height = (int) [ " +
std::to_string (GST_ROUND_UP_16 (dev_caps.height_min))
+ ", " + std::to_string (dev_caps.height_max) + " ]";
sink_caps_str = "video/x-raw, " + format_str + ", " + resolution_str
+ ", interlace-mode = (string) progressive";
src_caps_str = "video/x-h265, " + resolution_str + ", " + profile_str +
", stream-format = (string) { hvc1, hev1, byte-stream }" +
", alignment = (string) au";
system_caps = gst_caps_from_string (sink_caps_str.c_str ());
sink_caps = gst_caps_new_empty ();
if (cuda_device_id_size > 0) {
GstCaps *cuda_caps = gst_caps_copy (system_caps);
gst_caps_set_features (cuda_caps, 0,
gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_CUDA_MEMORY, nullptr));
gst_caps_append (sink_caps, cuda_caps);
}
#ifdef GST_CUDA_HAS_D3D
if (adapter_luid_size > 0) {
GstCaps *d3d11_caps = gst_caps_copy (system_caps);
gst_caps_set_features (d3d11_caps, 0,
gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY, nullptr));
gst_caps_append (sink_caps, d3d11_caps);
}
#endif
gst_caps_append (sink_caps, system_caps);
cdata = gst_nv_encoder_class_data_new ();
cdata->sink_caps = sink_caps;
cdata->src_caps = gst_caps_from_string (src_caps_str.c_str ());
cdata->device_caps = dev_caps;
cdata->device_mode = GST_NV_ENCODER_DEVICE_AUTO_SELECT;
cdata->adapter_luid = adapter_luid_list[0];
cdata->adapter_luid_size = adapter_luid_size;
memcpy (&cdata->adapter_luid_list,
adapter_luid_list, sizeof (adapter_luid_list));
cdata->cuda_device_id = cuda_device_id_list[0];
cdata->cuda_device_id_size = cuda_device_id_size;
memcpy (&cdata->cuda_device_id_list,
cuda_device_id_list, sizeof (cuda_device_id_list));
GType type;
GTypeInfo type_info = {
sizeof (GstNvH265EncoderClass),
nullptr,
nullptr,
(GClassInitFunc) gst_nv_h265_encoder_class_init,
nullptr,
cdata,
sizeof (GstNvH265Encoder),
0,
(GInstanceInitFunc) gst_nv_h265_encoder_init,
};
type = g_type_register_static (GST_TYPE_NV_ENCODER, "GstNvAutoGpuH265Enc",
&type_info, (GTypeFlags) 0);
if (!gst_element_register (plugin, "nvautogpuh265enc", rank, type))
GST_WARNING ("Failed to register plugin 'GstNvAutoGpuH265Enc'");
}

View file

@ -23,14 +23,19 @@
G_BEGIN_DECLS G_BEGIN_DECLS
void gst_nv_h265_encoder_register_cuda (GstPlugin * plugin, GstNvEncoderClassData * gst_nv_h265_encoder_register_cuda (GstPlugin * plugin,
GstCudaContext * context, GstCudaContext * context,
guint rank); guint rank);
#ifdef GST_CUDA_HAS_D3D #ifdef GST_CUDA_HAS_D3D
void gst_nv_h265_encoder_register_d3d11 (GstPlugin * plugin, GstNvEncoderClassData * gst_nv_h265_encoder_register_d3d11 (GstPlugin * plugin,
GstD3D11Device * device, GstD3D11Device * device,
guint rank); guint rank);
#endif #endif
void gst_nv_h265_encoder_register_auto_select (GstPlugin * plugin,
GList * device_caps_list,
guint rank);
G_END_DECLS G_END_DECLS

View file

@ -77,6 +77,8 @@ plugin_init (GstPlugin * plugin)
gboolean use_h265_sl_dec = FALSE; gboolean use_h265_sl_dec = FALSE;
gboolean use_vp8_sl_dec = FALSE; gboolean use_vp8_sl_dec = FALSE;
gboolean use_vp9_sl_dec = FALSE; gboolean use_vp9_sl_dec = FALSE;
GList *h264_enc_cdata = NULL;
GList *h265_enc_cdata = NULL;
GST_DEBUG_CATEGORY_INIT (gst_nvcodec_debug, "nvcodec", 0, "nvcodec"); GST_DEBUG_CATEGORY_INIT (gst_nvcodec_debug, "nvcodec", 0, "nvcodec");
GST_DEBUG_CATEGORY_INIT (gst_nvdec_debug, "nvdec", 0, "nvdec"); GST_DEBUG_CATEGORY_INIT (gst_nvdec_debug, "nvdec", 0, "nvdec");
@ -242,6 +244,8 @@ plugin_init (GstPlugin * plugin)
} }
if (nvenc_available) { if (nvenc_available) {
GstNvEncoderClassData *cdata;
#ifdef GST_CUDA_HAS_D3D #ifdef GST_CUDA_HAS_D3D
if (g_win32_check_windows_version (6, 0, 0, G_WIN32_OS_ANY)) { if (g_win32_check_windows_version (6, 0, 0, G_WIN32_OS_ANY)) {
gint64 adapter_luid; gint64 adapter_luid;
@ -253,22 +257,45 @@ plugin_init (GstPlugin * plugin)
if (!d3d11_device) { if (!d3d11_device) {
GST_WARNING ("Failed to d3d11 create device"); GST_WARNING ("Failed to d3d11 create device");
} else { } else {
gst_nv_h264_encoder_register_d3d11 (plugin, cdata = gst_nv_h264_encoder_register_d3d11 (plugin,
d3d11_device, GST_RANK_NONE); d3d11_device, GST_RANK_NONE);
gst_nv_h265_encoder_register_d3d11 (plugin, if (cdata)
h264_enc_cdata = g_list_append (h264_enc_cdata, cdata);
cdata = gst_nv_h265_encoder_register_d3d11 (plugin,
d3d11_device, GST_RANK_NONE); d3d11_device, GST_RANK_NONE);
if (cdata)
h265_enc_cdata = g_list_append (h265_enc_cdata, cdata);
gst_object_unref (d3d11_device); gst_object_unref (d3d11_device);
} }
} }
#endif #endif
cdata =
gst_nv_h264_encoder_register_cuda (plugin, context, GST_RANK_NONE); gst_nv_h264_encoder_register_cuda (plugin, context, GST_RANK_NONE);
if (cdata)
h264_enc_cdata = g_list_append (h264_enc_cdata, cdata);
cdata =
gst_nv_h265_encoder_register_cuda (plugin, context, GST_RANK_NONE); gst_nv_h265_encoder_register_cuda (plugin, context, GST_RANK_NONE);
if (cdata)
h265_enc_cdata = g_list_append (h265_enc_cdata, cdata);
gst_nvenc_plugin_init (plugin, i, cuda_ctx); gst_nvenc_plugin_init (plugin, i, cuda_ctx);
} }
gst_object_unref (context); gst_object_unref (context);
} }
if (h264_enc_cdata) {
gst_nv_h264_encoder_register_auto_select (plugin, h264_enc_cdata,
GST_RANK_NONE);
}
if (h265_enc_cdata) {
gst_nv_h265_encoder_register_auto_select (plugin, h265_enc_cdata,
GST_RANK_NONE);
}
gst_cuda_memory_copy_register (plugin, GST_RANK_NONE); gst_cuda_memory_copy_register (plugin, GST_RANK_NONE);
gst_cuda_filter_plugin_init (plugin); gst_cuda_filter_plugin_init (plugin);