1 /* GStreamer NVENC plugin
2  * Copyright (C) 2015 Centricular Ltd
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 
24 #include "gstnvenc.h"
25 #include "gstnvh264enc.h"
26 #include "gstnvh265enc.h"
27 #include <gmodule.h>
28 
29 #ifdef _WIN32
30 #ifdef _WIN64
31 #define NVENC_LIBRARY_NAME "nvEncodeAPI64.dll"
32 #else
33 #define NVENC_LIBRARY_NAME "nvEncodeAPI.dll"
34 #endif
35 #else
36 #define NVENC_LIBRARY_NAME "libnvidia-encode.so.1"
37 #endif
38 
39 typedef NVENCSTATUS NVENCAPI
40 tNvEncodeAPICreateInstance (NV_ENCODE_API_FUNCTION_LIST * functionList);
41 tNvEncodeAPICreateInstance *nvEncodeAPICreateInstance;
42 
43 GST_DEBUG_CATEGORY (gst_nvenc_debug);
44 #define GST_CAT_DEFAULT gst_nvenc_debug
45 
46 static NV_ENCODE_API_FUNCTION_LIST nvenc_api;
47 
48 NVENCSTATUS
NvEncOpenEncodeSessionEx(NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS * params,void ** encoder)49 NvEncOpenEncodeSessionEx (NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS * params,
50     void **encoder)
51 {
52   g_assert (nvenc_api.nvEncOpenEncodeSessionEx != NULL);
53   return nvenc_api.nvEncOpenEncodeSessionEx (params, encoder);
54 }
55 
56 NVENCSTATUS
NvEncDestroyEncoder(void * encoder)57 NvEncDestroyEncoder (void *encoder)
58 {
59   g_assert (nvenc_api.nvEncDestroyEncoder != NULL);
60   return nvenc_api.nvEncDestroyEncoder (encoder);
61 }
62 
63 NVENCSTATUS
NvEncGetEncodeGUIDs(void * encoder,GUID * array,uint32_t array_size,uint32_t * count)64 NvEncGetEncodeGUIDs (void *encoder, GUID * array, uint32_t array_size,
65     uint32_t * count)
66 {
67   g_assert (nvenc_api.nvEncGetEncodeGUIDs != NULL);
68   return nvenc_api.nvEncGetEncodeGUIDs (encoder, array, array_size, count);
69 }
70 
71 NVENCSTATUS
NvEncGetEncodeProfileGUIDCount(void * encoder,GUID encodeGUID,uint32_t * encodeProfileGUIDCount)72 NvEncGetEncodeProfileGUIDCount (void *encoder, GUID encodeGUID,
73     uint32_t * encodeProfileGUIDCount)
74 {
75   g_assert (nvenc_api.nvEncGetEncodeProfileGUIDCount != NULL);
76   return nvenc_api.nvEncGetEncodeProfileGUIDCount (encoder, encodeGUID,
77       encodeProfileGUIDCount);
78 }
79 
80 NVENCSTATUS
NvEncGetEncodeProfileGUIDs(void * encoder,GUID encodeGUID,GUID * profileGUIDs,uint32_t guidArraySize,uint32_t * GUIDCount)81 NvEncGetEncodeProfileGUIDs (void *encoder, GUID encodeGUID,
82     GUID * profileGUIDs, uint32_t guidArraySize, uint32_t * GUIDCount)
83 {
84   g_assert (nvenc_api.nvEncGetEncodeProfileGUIDs != NULL);
85   return nvenc_api.nvEncGetEncodeProfileGUIDs (encoder, encodeGUID,
86       profileGUIDs, guidArraySize, GUIDCount);
87 }
88 
89 NVENCSTATUS
NvEncGetInputFormats(void * encoder,GUID enc_guid,NV_ENC_BUFFER_FORMAT * array,uint32_t size,uint32_t * num)90 NvEncGetInputFormats (void *encoder, GUID enc_guid,
91     NV_ENC_BUFFER_FORMAT * array, uint32_t size, uint32_t * num)
92 {
93   g_assert (nvenc_api.nvEncGetInputFormats != NULL);
94   return nvenc_api.nvEncGetInputFormats (encoder, enc_guid, array, size, num);
95 }
96 
97 NVENCSTATUS
NvEncGetEncodePresetCount(void * encoder,GUID encodeGUID,uint32_t * encodePresetGUIDCount)98 NvEncGetEncodePresetCount (void *encoder, GUID encodeGUID,
99     uint32_t * encodePresetGUIDCount)
100 {
101   g_assert (nvenc_api.nvEncGetEncodeProfileGUIDCount != NULL);
102   return nvenc_api.nvEncGetEncodePresetCount (encoder, encodeGUID,
103       encodePresetGUIDCount);
104 }
105 
106 NVENCSTATUS
NvEncGetEncodePresetGUIDs(void * encoder,GUID encodeGUID,GUID * presetGUIDs,uint32_t guidArraySize,uint32_t * GUIDCount)107 NvEncGetEncodePresetGUIDs (void *encoder, GUID encodeGUID,
108     GUID * presetGUIDs, uint32_t guidArraySize, uint32_t * GUIDCount)
109 {
110   g_assert (nvenc_api.nvEncGetEncodeProfileGUIDs != NULL);
111   return nvenc_api.nvEncGetEncodePresetGUIDs (encoder, encodeGUID,
112       presetGUIDs, guidArraySize, GUIDCount);
113 }
114 
115 NVENCSTATUS
NvEncGetEncodePresetConfig(void * encoder,GUID encodeGUID,GUID presetGUID,NV_ENC_PRESET_CONFIG * presetConfig)116 NvEncGetEncodePresetConfig (void *encoder, GUID encodeGUID,
117     GUID presetGUID, NV_ENC_PRESET_CONFIG * presetConfig)
118 {
119   g_assert (nvenc_api.nvEncGetEncodePresetConfig != NULL);
120   return nvenc_api.nvEncGetEncodePresetConfig (encoder, encodeGUID, presetGUID,
121       presetConfig);
122 }
123 
124 NVENCSTATUS
NvEncGetEncodeCaps(void * encoder,GUID encodeGUID,NV_ENC_CAPS_PARAM * capsParam,int * capsVal)125 NvEncGetEncodeCaps (void *encoder, GUID encodeGUID,
126     NV_ENC_CAPS_PARAM * capsParam, int *capsVal)
127 {
128   g_assert (nvenc_api.nvEncGetEncodeCaps != NULL);
129   return nvenc_api.nvEncGetEncodeCaps (encoder, encodeGUID, capsParam, capsVal);
130 }
131 
132 NVENCSTATUS
NvEncGetSequenceParams(void * encoder,NV_ENC_SEQUENCE_PARAM_PAYLOAD * sequenceParamPayload)133 NvEncGetSequenceParams (void *encoder,
134     NV_ENC_SEQUENCE_PARAM_PAYLOAD * sequenceParamPayload)
135 {
136   g_assert (nvenc_api.nvEncGetSequenceParams != NULL);
137   return nvenc_api.nvEncGetSequenceParams (encoder, sequenceParamPayload);
138 }
139 
140 NVENCSTATUS
NvEncInitializeEncoder(void * encoder,NV_ENC_INITIALIZE_PARAMS * params)141 NvEncInitializeEncoder (void *encoder, NV_ENC_INITIALIZE_PARAMS * params)
142 {
143   g_assert (nvenc_api.nvEncInitializeEncoder != NULL);
144   return nvenc_api.nvEncInitializeEncoder (encoder, params);
145 }
146 
147 NVENCSTATUS
NvEncReconfigureEncoder(void * encoder,NV_ENC_RECONFIGURE_PARAMS * params)148 NvEncReconfigureEncoder (void *encoder, NV_ENC_RECONFIGURE_PARAMS * params)
149 {
150   g_assert (nvenc_api.nvEncReconfigureEncoder != NULL);
151   return nvenc_api.nvEncReconfigureEncoder (encoder, params);
152 }
153 
154 NVENCSTATUS
NvEncRegisterResource(void * encoder,NV_ENC_REGISTER_RESOURCE * params)155 NvEncRegisterResource (void *encoder, NV_ENC_REGISTER_RESOURCE * params)
156 {
157   g_assert (nvenc_api.nvEncRegisterResource != NULL);
158   return nvenc_api.nvEncRegisterResource (encoder, params);
159 }
160 
161 NVENCSTATUS
NvEncUnregisterResource(void * encoder,NV_ENC_REGISTERED_PTR resource)162 NvEncUnregisterResource (void *encoder, NV_ENC_REGISTERED_PTR resource)
163 {
164   g_assert (nvenc_api.nvEncUnregisterResource != NULL);
165   return nvenc_api.nvEncUnregisterResource (encoder, resource);
166 }
167 
168 NVENCSTATUS
NvEncMapInputResource(void * encoder,NV_ENC_MAP_INPUT_RESOURCE * params)169 NvEncMapInputResource (void *encoder, NV_ENC_MAP_INPUT_RESOURCE * params)
170 {
171   g_assert (nvenc_api.nvEncMapInputResource != NULL);
172   return nvenc_api.nvEncMapInputResource (encoder, params);
173 }
174 
175 NVENCSTATUS
NvEncUnmapInputResource(void * encoder,NV_ENC_INPUT_PTR input_buffer)176 NvEncUnmapInputResource (void *encoder, NV_ENC_INPUT_PTR input_buffer)
177 {
178   g_assert (nvenc_api.nvEncUnmapInputResource != NULL);
179   return nvenc_api.nvEncUnmapInputResource (encoder, input_buffer);
180 }
181 
182 NVENCSTATUS
NvEncCreateInputBuffer(void * encoder,NV_ENC_CREATE_INPUT_BUFFER * input_buf)183 NvEncCreateInputBuffer (void *encoder, NV_ENC_CREATE_INPUT_BUFFER * input_buf)
184 {
185   g_assert (nvenc_api.nvEncCreateInputBuffer != NULL);
186   return nvenc_api.nvEncCreateInputBuffer (encoder, input_buf);
187 }
188 
189 NVENCSTATUS
NvEncLockInputBuffer(void * encoder,NV_ENC_LOCK_INPUT_BUFFER * input_buf)190 NvEncLockInputBuffer (void *encoder, NV_ENC_LOCK_INPUT_BUFFER * input_buf)
191 {
192   g_assert (nvenc_api.nvEncLockInputBuffer != NULL);
193   return nvenc_api.nvEncLockInputBuffer (encoder, input_buf);
194 }
195 
196 NVENCSTATUS
NvEncUnlockInputBuffer(void * encoder,NV_ENC_INPUT_PTR input_buf)197 NvEncUnlockInputBuffer (void *encoder, NV_ENC_INPUT_PTR input_buf)
198 {
199   g_assert (nvenc_api.nvEncUnlockInputBuffer != NULL);
200   return nvenc_api.nvEncUnlockInputBuffer (encoder, input_buf);
201 }
202 
203 NVENCSTATUS
NvEncDestroyInputBuffer(void * encoder,NV_ENC_INPUT_PTR input_buf)204 NvEncDestroyInputBuffer (void *encoder, NV_ENC_INPUT_PTR input_buf)
205 {
206   g_assert (nvenc_api.nvEncDestroyInputBuffer != NULL);
207   return nvenc_api.nvEncDestroyInputBuffer (encoder, input_buf);
208 }
209 
210 NVENCSTATUS
NvEncCreateBitstreamBuffer(void * encoder,NV_ENC_CREATE_BITSTREAM_BUFFER * bb)211 NvEncCreateBitstreamBuffer (void *encoder, NV_ENC_CREATE_BITSTREAM_BUFFER * bb)
212 {
213   g_assert (nvenc_api.nvEncCreateBitstreamBuffer != NULL);
214   return nvenc_api.nvEncCreateBitstreamBuffer (encoder, bb);
215 }
216 
217 NVENCSTATUS
NvEncLockBitstream(void * encoder,NV_ENC_LOCK_BITSTREAM * lock_bs)218 NvEncLockBitstream (void *encoder, NV_ENC_LOCK_BITSTREAM * lock_bs)
219 {
220   g_assert (nvenc_api.nvEncLockBitstream != NULL);
221   return nvenc_api.nvEncLockBitstream (encoder, lock_bs);
222 }
223 
224 NVENCSTATUS
NvEncUnlockBitstream(void * encoder,NV_ENC_OUTPUT_PTR bb)225 NvEncUnlockBitstream (void *encoder, NV_ENC_OUTPUT_PTR bb)
226 {
227   g_assert (nvenc_api.nvEncUnlockBitstream != NULL);
228   return nvenc_api.nvEncUnlockBitstream (encoder, bb);
229 }
230 
231 NVENCSTATUS
NvEncDestroyBitstreamBuffer(void * encoder,NV_ENC_OUTPUT_PTR bit_buf)232 NvEncDestroyBitstreamBuffer (void *encoder, NV_ENC_OUTPUT_PTR bit_buf)
233 {
234   g_assert (nvenc_api.nvEncDestroyBitstreamBuffer != NULL);
235   return nvenc_api.nvEncDestroyBitstreamBuffer (encoder, bit_buf);
236 }
237 
238 NVENCSTATUS
NvEncEncodePicture(void * encoder,NV_ENC_PIC_PARAMS * pic_params)239 NvEncEncodePicture (void *encoder, NV_ENC_PIC_PARAMS * pic_params)
240 {
241   g_assert (nvenc_api.nvEncEncodePicture != NULL);
242   return nvenc_api.nvEncEncodePicture (encoder, pic_params);
243 }
244 
245 gboolean
gst_nvenc_cmp_guid(GUID g1,GUID g2)246 gst_nvenc_cmp_guid (GUID g1, GUID g2)
247 {
248   return (g1.Data1 == g2.Data1 && g1.Data2 == g2.Data2 && g1.Data3 == g2.Data3
249       && g1.Data4[0] == g2.Data4[0] && g1.Data4[1] == g2.Data4[1]
250       && g1.Data4[2] == g2.Data4[2] && g1.Data4[3] == g2.Data4[3]
251       && g1.Data4[4] == g2.Data4[4] && g1.Data4[5] == g2.Data4[5]
252       && g1.Data4[6] == g2.Data4[6] && g1.Data4[7] == g2.Data4[7]);
253 }
254 
255 NV_ENC_BUFFER_FORMAT
gst_nvenc_get_nv_buffer_format(GstVideoFormat fmt)256 gst_nvenc_get_nv_buffer_format (GstVideoFormat fmt)
257 {
258   switch (fmt) {
259     case GST_VIDEO_FORMAT_NV12:
260       return NV_ENC_BUFFER_FORMAT_NV12_PL;
261     case GST_VIDEO_FORMAT_YV12:
262       return NV_ENC_BUFFER_FORMAT_YV12_PL;
263     case GST_VIDEO_FORMAT_I420:
264       return NV_ENC_BUFFER_FORMAT_IYUV_PL;
265     case GST_VIDEO_FORMAT_Y444:
266       return NV_ENC_BUFFER_FORMAT_YUV444_PL;
267     default:
268       break;
269   }
270   return NV_ENC_BUFFER_FORMAT_UNDEFINED;
271 }
272 
273 CUcontext
gst_nvenc_create_cuda_context(guint device_id)274 gst_nvenc_create_cuda_context (guint device_id)
275 {
276   CUcontext cuda_ctx, old_ctx;
277   CUresult cres = CUDA_SUCCESS;
278   CUdevice cdev = 0, cuda_dev = -1;
279   int dev_count = 0;
280   char name[256];
281   int min = 0, maj = 0;
282   int i;
283 
284   GST_INFO ("Initialising CUDA..");
285 
286   cres = cuInit (0);
287 
288   if (cres != CUDA_SUCCESS) {
289     GST_WARNING ("Failed to initialise CUDA, error code: 0x%08x", cres);
290     return NULL;
291   }
292 
293   GST_INFO ("Initialised CUDA");
294 
295   cres = cuDeviceGetCount (&dev_count);
296   if (cres != CUDA_SUCCESS || dev_count == 0) {
297     GST_WARNING ("No CUDA devices detected");
298     return NULL;
299   }
300 
301   GST_INFO ("%d CUDA device(s) detected", dev_count);
302   for (i = 0; i < dev_count; ++i) {
303     if (cuDeviceGet (&cdev, i) == CUDA_SUCCESS
304         && cuDeviceGetName (name, sizeof (name), cdev) == CUDA_SUCCESS
305         && cuDeviceGetAttribute (&maj,
306             CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, cdev) == CUDA_SUCCESS
307         && cuDeviceGetAttribute (&min,
308             CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR,
309             cdev) == CUDA_SUCCESS) {
310       GST_INFO ("GPU #%d supports NVENC: %s (%s) (Compute SM %d.%d)", i,
311           (((maj << 4) + min) >= 0x30) ? "yes" : "no", name, maj, min);
312       if (i == device_id) {
313         cuda_dev = cdev;
314       }
315     }
316   }
317 
318   if (cuda_dev == -1) {
319     GST_WARNING ("Device with id %d does not exist or does not support NVENC",
320         device_id);
321     return NULL;
322   }
323 
324   if (cuCtxCreate (&cuda_ctx, 0, cuda_dev) != CUDA_SUCCESS) {
325     GST_WARNING ("Failed to create CUDA context for cuda device %d", cuda_dev);
326     return NULL;
327   }
328 
329   if (cuCtxPopCurrent (&old_ctx) != CUDA_SUCCESS) {
330     return NULL;
331   }
332 
333   GST_INFO ("Created CUDA context %p", cuda_ctx);
334 
335   return cuda_ctx;
336 }
337 
338 gboolean
gst_nvenc_destroy_cuda_context(CUcontext ctx)339 gst_nvenc_destroy_cuda_context (CUcontext ctx)
340 {
341   GST_INFO ("Destroying CUDA context %p", ctx);
342   return (cuCtxDestroy (ctx) == CUDA_SUCCESS);
343 }
344 
345 static gboolean
load_nvenc_library(void)346 load_nvenc_library (void)
347 {
348   GModule *module;
349 
350   module = g_module_open (NVENC_LIBRARY_NAME, G_MODULE_BIND_LAZY);
351   if (module == NULL) {
352     GST_ERROR ("%s", g_module_error ());
353     return FALSE;
354   }
355 
356   if (!g_module_symbol (module, "NvEncodeAPICreateInstance",
357           (gpointer *) & nvEncodeAPICreateInstance)) {
358     GST_ERROR ("%s", g_module_error ());
359     return FALSE;
360   }
361 
362   return TRUE;
363 }
364 
365 static gboolean
plugin_init(GstPlugin * plugin)366 plugin_init (GstPlugin * plugin)
367 {
368   GST_DEBUG_CATEGORY_INIT (gst_nvenc_debug, "nvenc", 0, "Nvidia NVENC encoder");
369 
370   nvenc_api.version = NV_ENCODE_API_FUNCTION_LIST_VER;
371   if (!load_nvenc_library ())
372     return FALSE;
373 
374   if (nvEncodeAPICreateInstance (&nvenc_api) != NV_ENC_SUCCESS) {
375     GST_ERROR ("Failed to get NVEncodeAPI function table!");
376   } else {
377     GST_INFO ("Created NVEncodeAPI instance, got function table");
378 
379     gst_element_register (plugin, "nvh264enc", GST_RANK_PRIMARY * 2,
380         gst_nv_h264_enc_get_type ());
381     gst_element_register (plugin, "nvh265enc", GST_RANK_PRIMARY * 2,
382         gst_nv_h265_enc_get_type ());
383   }
384 
385   return TRUE;
386 }
387 
388 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
389     GST_VERSION_MINOR,
390     nvenc,
391     "GStreamer NVENC plugin",
392     plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
393