nvenc: Port to GstCudaContext

... and add support CUDA context sharing similar to glcontext sharing.
Multiple CUDA context per GPU is not the best practice. The context
sharing method is very similar to that of glcontext. The difference
is that there can be multiple context object on a pipeline since
the CUDA context is created per GPU id. For example, a pipeline
has nvh264dec (uses GPU #0) and nvh264device0dec (uses GPU #1),
then two CUDA context will propagated to all pipeline.
This commit is contained in:
Seungha Yang 2019-07-25 19:33:54 +09:00 committed by Matthew Waters
parent 094e4a9f5c
commit 5cf0351418
4 changed files with 39 additions and 108 deletions

View file

@ -22,6 +22,7 @@
#endif #endif
#include "gstnvbaseenc.h" #include "gstnvbaseenc.h"
#include "gstcudautils.h"
#include <gst/pbutils/codec-utils.h> #include <gst/pbutils/codec-utils.h>
@ -292,10 +293,9 @@ gst_nv_base_enc_open (GstVideoEncoder * enc)
GstNvBaseEncClass *klass = GST_NV_BASE_ENC_GET_CLASS (enc); GstNvBaseEncClass *klass = GST_NV_BASE_ENC_GET_CLASS (enc);
GValue *formats = NULL; GValue *formats = NULL;
nvenc->cuda_ctx = gst_nvenc_create_cuda_context (klass->cuda_device_id); if (!gst_cuda_ensure_element_context (GST_ELEMENT_CAST (enc),
if (nvenc->cuda_ctx == NULL) { klass->cuda_device_id, &nvenc->cuda_ctx)) {
GST_ELEMENT_ERROR (enc, LIBRARY, INIT, (NULL), GST_ERROR_OBJECT (nvenc, "failed to create CUDA context");
("Failed to create CUDA context, perhaps CUDA is not supported."));
return FALSE; return FALSE;
} }
@ -305,13 +305,12 @@ gst_nv_base_enc_open (GstVideoEncoder * enc)
params.version = NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER; params.version = NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER;
params.apiVersion = NVENCAPI_VERSION; params.apiVersion = NVENCAPI_VERSION;
params.device = nvenc->cuda_ctx; params.device = gst_cuda_context_get_handle (nvenc->cuda_ctx);
params.deviceType = NV_ENC_DEVICE_TYPE_CUDA; params.deviceType = NV_ENC_DEVICE_TYPE_CUDA;
nv_ret = NvEncOpenEncodeSessionEx (&params, &nvenc->encoder); nv_ret = NvEncOpenEncodeSessionEx (&params, &nvenc->encoder);
if (nv_ret != NV_ENC_SUCCESS) { if (nv_ret != NV_ENC_SUCCESS) {
GST_ERROR ("Failed to create NVENC encoder session, ret=%d", nv_ret); GST_ERROR ("Failed to create NVENC encoder session, ret=%d", nv_ret);
if (gst_nvenc_destroy_cuda_context (nvenc->cuda_ctx)) gst_clear_object (&nvenc->cuda_ctx);
nvenc->cuda_ctx = NULL;
return FALSE; return FALSE;
} }
GST_INFO ("created NVENC encoder %p", nvenc->encoder); GST_INFO ("created NVENC encoder %p", nvenc->encoder);
@ -333,9 +332,14 @@ gst_nv_base_enc_open (GstVideoEncoder * enc)
static void static void
gst_nv_base_enc_set_context (GstElement * element, GstContext * context) gst_nv_base_enc_set_context (GstElement * element, GstContext * context)
{ {
#if HAVE_NVCODEC_GST_GL
GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (element); GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (element);
GstNvBaseEncClass *klass = GST_NV_BASE_ENC_GET_CLASS (nvenc);
if (gst_cuda_handle_set_context (element, context, klass->cuda_device_id,
&nvenc->cuda_ctx)) {
goto done;
}
#if HAVE_NVCODEC_GST_GL
gst_gl_handle_set_context (element, context, gst_gl_handle_set_context (element, context,
(GstGLDisplay **) & nvenc->display, (GstGLDisplay **) & nvenc->display,
(GstGLContext **) & nvenc->other_context); (GstGLContext **) & nvenc->other_context);
@ -344,32 +348,38 @@ gst_nv_base_enc_set_context (GstElement * element, GstContext * context)
SUPPORTED_GL_APIS); SUPPORTED_GL_APIS);
#endif #endif
done:
GST_ELEMENT_CLASS (parent_class)->set_context (element, context); GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
} }
static gboolean static gboolean
gst_nv_base_enc_sink_query (GstVideoEncoder * enc, GstQuery * query) gst_nv_base_enc_sink_query (GstVideoEncoder * enc, GstQuery * query)
{ {
#if HAVE_NVCODEC_GST_GL
GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (enc); GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (enc);
#endif
switch (GST_QUERY_TYPE (query)) { switch (GST_QUERY_TYPE (query)) {
#if HAVE_NVCODEC_GST_GL
case GST_QUERY_CONTEXT:{ case GST_QUERY_CONTEXT:{
gboolean ret; if (gst_cuda_handle_context_query (GST_ELEMENT (nvenc),
query, nvenc->cuda_ctx))
return TRUE;
ret = gst_gl_handle_context_query ((GstElement *) nvenc, query, #if HAVE_NVCODEC_GST_GL
nvenc->display, NULL, nvenc->other_context); {
if (nvenc->display) gboolean ret;
gst_gl_display_filter_gl_api (GST_GL_DISPLAY (nvenc->display),
SUPPORTED_GL_APIS);
if (ret) ret = gst_gl_handle_context_query ((GstElement *) nvenc, query,
return ret; nvenc->display, NULL, nvenc->other_context);
if (nvenc->display) {
gst_gl_display_filter_gl_api (GST_GL_DISPLAY (nvenc->display),
SUPPORTED_GL_APIS);
}
if (ret)
return ret;
}
#endif
break; break;
} }
#endif
default: default:
break; break;
} }
@ -629,11 +639,7 @@ gst_nv_base_enc_close (GstVideoEncoder * enc)
nvenc->encoder = NULL; nvenc->encoder = NULL;
} }
if (nvenc->cuda_ctx) { gst_clear_object (&nvenc->cuda_ctx);
if (!gst_nvenc_destroy_cuda_context (nvenc->cuda_ctx))
return FALSE;
nvenc->cuda_ctx = NULL;
}
GST_OBJECT_LOCK (nvenc); GST_OBJECT_LOCK (nvenc);
if (nvenc->input_formats) if (nvenc->input_formats)
@ -977,7 +983,7 @@ gst_nv_base_enc_free_buffers (GstNvBaseEnc * nvenc)
if (nvenc->gl_input) { if (nvenc->gl_input) {
struct gl_input_resource *in_gl_resource = nvenc->input_bufs[i]; struct gl_input_resource *in_gl_resource = nvenc->input_bufs[i];
CuCtxPushCurrent (nvenc->cuda_ctx); gst_cuda_context_push (nvenc->cuda_ctx);
if (in_gl_resource->mapped) { if (in_gl_resource->mapped) {
GST_LOG_OBJECT (nvenc, "Unmap resource %p", in_gl_resource); GST_LOG_OBJECT (nvenc, "Unmap resource %p", in_gl_resource);
@ -1006,7 +1012,7 @@ gst_nv_base_enc_free_buffers (GstNvBaseEnc * nvenc)
} }
g_free (in_gl_resource); g_free (in_gl_resource);
CuCtxPopCurrent (NULL); gst_cuda_context_pop (NULL);
} else } else
#endif #endif
{ {
@ -1306,7 +1312,7 @@ gst_nv_base_enc_set_format (GstVideoEncoder * enc, GstVideoCodecState * state)
pixel_depth += GST_VIDEO_INFO_COMP_DEPTH (info, i); pixel_depth += GST_VIDEO_INFO_COMP_DEPTH (info, i);
} }
CuCtxPushCurrent (nvenc->cuda_ctx); gst_cuda_context_push (nvenc->cuda_ctx);
for (i = 0; i < nvenc->n_bufs; ++i) { for (i = 0; i < nvenc->n_bufs; ++i) {
struct gl_input_resource *in_gl_resource = struct gl_input_resource *in_gl_resource =
g_new0 (struct gl_input_resource, 1); g_new0 (struct gl_input_resource, 1);
@ -1353,7 +1359,7 @@ gst_nv_base_enc_set_format (GstVideoEncoder * enc, GstVideoCodecState * state)
g_async_queue_push (nvenc->in_bufs_pool, nvenc->input_bufs[i]); g_async_queue_push (nvenc->in_bufs_pool, nvenc->input_bufs[i]);
} }
CuCtxPopCurrent (NULL); gst_cuda_context_pop (NULL);
} else } else
#endif #endif
{ {
@ -1489,7 +1495,7 @@ _map_gl_input_buffer (GstGLContext * context, struct map_gl_input *data)
guint i; guint i;
CUDA_MEMCPY2D param; CUDA_MEMCPY2D param;
CuCtxPushCurrent (data->nvenc->cuda_ctx); gst_cuda_context_push (data->nvenc->cuda_ctx);
data_pointer = data->in_gl_resource->cuda_pointer; data_pointer = data->in_gl_resource->cuda_pointer;
for (i = 0; i < GST_VIDEO_INFO_N_PLANES (data->info); i++) { for (i = 0; i < GST_VIDEO_INFO_N_PLANES (data->info); i++) {
GstGLBuffer *gl_buf_obj; GstGLBuffer *gl_buf_obj;
@ -1585,7 +1591,7 @@ _map_gl_input_buffer (GstGLContext * context, struct map_gl_input *data)
data_pointer = data_pointer + data_pointer = data_pointer +
dest_stride * _get_plane_height (&data->nvenc->input_info, i); dest_stride * _get_plane_height (&data->nvenc->input_info, i);
} }
CuCtxPopCurrent (NULL); gst_cuda_context_pop (NULL);
} }
#endif #endif

View file

@ -23,6 +23,7 @@
#include "gstnvenc.h" #include "gstnvenc.h"
#include <gst/video/gstvideoencoder.h> #include <gst/video/gstvideoencoder.h>
#include "gstcudacontext.h"
#define GST_TYPE_NV_BASE_ENC \ #define GST_TYPE_NV_BASE_ENC \
(gst_nv_base_enc_get_type()) (gst_nv_base_enc_get_type())
@ -70,7 +71,7 @@ typedef struct {
guint bitrate; guint bitrate;
gint gop_size; gint gop_size;
CUcontext cuda_ctx; GstCudaContext * cuda_ctx;
void * encoder; void * encoder;
/* the supported input formats */ /* the supported input formats */

View file

@ -285,78 +285,6 @@ gst_nvenc_get_nv_buffer_format (GstVideoFormat fmt)
return NV_ENC_BUFFER_FORMAT_UNDEFINED; return NV_ENC_BUFFER_FORMAT_UNDEFINED;
} }
CUcontext
gst_nvenc_create_cuda_context (guint device_id)
{
CUcontext cuda_ctx, old_ctx;
CUresult cres = CUDA_SUCCESS;
CUdevice cdev = 0, cuda_dev = -1;
int dev_count = 0;
char name[256];
int min = 0, maj = 0;
int i;
GST_INFO ("Initialising CUDA..");
cres = CuInit (0);
if (cres != CUDA_SUCCESS) {
GST_WARNING ("Failed to initialise CUDA, error code: 0x%08x", cres);
return NULL;
}
GST_INFO ("Initialised CUDA");
cres = CuDeviceGetCount (&dev_count);
if (cres != CUDA_SUCCESS || dev_count == 0) {
GST_WARNING ("No CUDA devices detected");
return NULL;
}
GST_INFO ("%d CUDA device(s) detected", dev_count);
for (i = 0; i < dev_count; ++i) {
if (CuDeviceGet (&cdev, i) == CUDA_SUCCESS
&& CuDeviceGetName (name, sizeof (name), cdev) == CUDA_SUCCESS
&& CuDeviceGetAttribute (&maj,
CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, cdev) == CUDA_SUCCESS
&& CuDeviceGetAttribute (&min,
CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR,
cdev) == CUDA_SUCCESS) {
GST_INFO ("GPU #%d supports NVENC: %s (%s) (Compute SM %d.%d)", i,
(((maj << 4) + min) >= 0x30) ? "yes" : "no", name, maj, min);
if (i == device_id) {
cuda_dev = cdev;
}
}
}
if (cuda_dev == -1) {
GST_WARNING ("Device with id %d does not exist or does not support NVENC",
device_id);
return NULL;
}
if (CuCtxCreate (&cuda_ctx, 0, cuda_dev) != CUDA_SUCCESS) {
GST_WARNING ("Failed to create CUDA context for cuda device %d", cuda_dev);
return NULL;
}
if (CuCtxPopCurrent (&old_ctx) != CUDA_SUCCESS) {
return NULL;
}
GST_INFO ("Created CUDA context %p", cuda_ctx);
return cuda_ctx;
}
gboolean
gst_nvenc_destroy_cuda_context (CUcontext ctx)
{
GST_INFO ("Destroying CUDA context %p", ctx);
return (CuCtxDestroy (ctx) == CUDA_SUCCESS);
}
static gboolean static gboolean
load_nvenc_library (void) load_nvenc_library (void)
{ {

View file

@ -28,10 +28,6 @@
GST_DEBUG_CATEGORY_EXTERN (gst_nvenc_debug); GST_DEBUG_CATEGORY_EXTERN (gst_nvenc_debug);
CUcontext gst_nvenc_create_cuda_context (guint device_id);
gboolean gst_nvenc_destroy_cuda_context (CUcontext ctx);
gboolean gst_nvenc_cmp_guid (GUID g1, GUID g2); gboolean gst_nvenc_cmp_guid (GUID g1, GUID g2);
NV_ENC_BUFFER_FORMAT gst_nvenc_get_nv_buffer_format (GstVideoFormat fmt); NV_ENC_BUFFER_FORMAT gst_nvenc_get_nv_buffer_format (GstVideoFormat fmt);