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 "gstnvbaseenc.h"
25 
26 #include <gst/pbutils/codec-utils.h>
27 
28 #include <string.h>
29 
30 #define GST_CAT_DEFAULT gst_nvenc_debug
31 
32 #if HAVE_NVENC_GST_GL
33 #include <cuda.h>
34 #include <cuda_runtime_api.h>
35 #include <cuda_gl_interop.h>
36 #include <cudaGL.h>
37 #include <gst/gl/gl.h>
38 #endif
39 
40 /* TODO:
41  *  - reset last_flow on FLUSH_STOP (seeking)
42  */
43 
44 /* This currently supports both 5.x and 6.x versions of the NvEncodeAPI.h
45  * header which are mostly API compatible. */
46 
47 #define N_BUFFERS_PER_FRAME 1
48 #define SUPPORTED_GL_APIS GST_GL_API_OPENGL3
49 
50 /* magic pointer value we can put in the async queue to signal shut down */
51 #define SHUTDOWN_COOKIE ((gpointer)GINT_TO_POINTER (1))
52 
53 #define parent_class gst_nv_base_enc_parent_class
54 G_DEFINE_ABSTRACT_TYPE (GstNvBaseEnc, gst_nv_base_enc, GST_TYPE_VIDEO_ENCODER);
55 
56 #define GST_TYPE_NV_PRESET (gst_nv_preset_get_type())
57 static GType
gst_nv_preset_get_type(void)58 gst_nv_preset_get_type (void)
59 {
60   static GType nv_preset_type = 0;
61 
62   static const GEnumValue presets[] = {
63     {GST_NV_PRESET_DEFAULT, "Default", "default"},
64     {GST_NV_PRESET_HP, "High Performance", "hp"},
65     {GST_NV_PRESET_HQ, "High Quality", "hq"},
66 /*    {GST_NV_PRESET_BD, "BD", "bd"}, */
67     {GST_NV_PRESET_LOW_LATENCY_DEFAULT, "Low Latency", "low-latency"},
68     {GST_NV_PRESET_LOW_LATENCY_HQ, "Low Latency, High Quality",
69         "low-latency-hq"},
70     {GST_NV_PRESET_LOW_LATENCY_HP, "Low Latency, High Performance",
71         "low-latency-hp"},
72     {GST_NV_PRESET_LOSSLESS_DEFAULT, "Lossless", "lossless"},
73     {GST_NV_PRESET_LOSSLESS_HP, "Lossless, High Performance", "lossless-hp"},
74     {0, NULL, NULL},
75   };
76 
77   if (!nv_preset_type) {
78     nv_preset_type = g_enum_register_static ("GstNvPreset", presets);
79   }
80   return nv_preset_type;
81 }
82 
83 static GUID
_nv_preset_to_guid(GstNvPreset preset)84 _nv_preset_to_guid (GstNvPreset preset)
85 {
86   GUID null = { 0, };
87 
88   switch (preset) {
89 #define CASE(gst,nv) case G_PASTE(GST_NV_PRESET_,gst): return G_PASTE(G_PASTE(NV_ENC_PRESET_,nv),_GUID)
90       CASE (DEFAULT, DEFAULT);
91       CASE (HP, HP);
92       CASE (HQ, HQ);
93 /*    CASE (BD, BD);*/
94       CASE (LOW_LATENCY_DEFAULT, LOW_LATENCY_DEFAULT);
95       CASE (LOW_LATENCY_HQ, LOW_LATENCY_HQ);
96       CASE (LOW_LATENCY_HP, LOW_LATENCY_HQ);
97       CASE (LOSSLESS_DEFAULT, LOSSLESS_DEFAULT);
98       CASE (LOSSLESS_HP, LOSSLESS_HP);
99 #undef CASE
100     default:
101       return null;
102   }
103 }
104 
105 #define GST_TYPE_NV_RC_MODE (gst_nv_rc_mode_get_type())
106 static GType
gst_nv_rc_mode_get_type(void)107 gst_nv_rc_mode_get_type (void)
108 {
109   static GType nv_rc_mode_type = 0;
110 
111   static const GEnumValue modes[] = {
112     {GST_NV_RC_MODE_DEFAULT, "Default (from NVENC preset)", "default"},
113     {GST_NV_RC_MODE_CONSTQP, "Constant Quantization", "constqp"},
114     {GST_NV_RC_MODE_CBR, "Constant Bit Rate", "cbr"},
115     {GST_NV_RC_MODE_VBR, "Variable Bit Rate", "vbr"},
116     {GST_NV_RC_MODE_VBR_MINQP,
117           "Variable Bit Rate (with minimum quantization parameter)",
118         "vbr-minqp"},
119     {0, NULL, NULL},
120   };
121 
122   if (!nv_rc_mode_type) {
123     nv_rc_mode_type = g_enum_register_static ("GstNvRCMode", modes);
124   }
125   return nv_rc_mode_type;
126 }
127 
128 static NV_ENC_PARAMS_RC_MODE
_rc_mode_to_nv(GstNvRCMode mode)129 _rc_mode_to_nv (GstNvRCMode mode)
130 {
131   switch (mode) {
132     case GST_NV_RC_MODE_DEFAULT:
133       return -1;
134 #define CASE(gst,nv) case G_PASTE(GST_NV_RC_MODE_,gst): return G_PASTE(NV_ENC_PARAMS_RC_,nv)
135       CASE (CONSTQP, CONSTQP);
136       CASE (CBR, CBR);
137       CASE (VBR, VBR);
138       CASE (VBR_MINQP, VBR_MINQP);
139 #undef CASE
140     default:
141       return -1;
142   }
143 }
144 
145 enum
146 {
147   PROP_0,
148   PROP_DEVICE_ID,
149   PROP_PRESET,
150   PROP_BITRATE,
151   PROP_RC_MODE,
152   PROP_QP_MIN,
153   PROP_QP_MAX,
154   PROP_QP_CONST,
155   PROP_GOP_SIZE,
156 };
157 
158 #define DEFAULT_PRESET GST_NV_PRESET_DEFAULT
159 #define DEFAULT_BITRATE 0
160 #define DEFAULT_RC_MODE GST_NV_RC_MODE_DEFAULT
161 #define DEFAULT_QP_MIN -1
162 #define DEFAULT_QP_MAX -1
163 #define DEFAULT_QP_CONST -1
164 #define DEFAULT_GOP_SIZE 75
165 
166 /* This lock is needed to prevent the situation where multiple encoders are
167  * initialised at the same time which appears to cause excessive CPU usage over
168  * some period of time. */
169 G_LOCK_DEFINE_STATIC (initialization_lock);
170 
171 #if HAVE_NVENC_GST_GL
172 struct gl_input_resource
173 {
174   GstGLMemory *gl_mem[GST_VIDEO_MAX_PLANES];
175   CUgraphicsResource cuda_texture;
176   CUdeviceptr cuda_plane_pointers[GST_VIDEO_MAX_PLANES];
177   gpointer cuda_pointer;
178   gsize cuda_stride;
179   gsize cuda_num_bytes;
180   NV_ENC_REGISTER_RESOURCE nv_resource;
181   NV_ENC_MAP_INPUT_RESOURCE nv_mapped_resource;
182 
183   /* whether nv_mapped_resource was mapped via NvEncMapInputResource()
184    * and therefore should unmap via NvEncUnmapInputResource or not */
185   gboolean mapped;
186 };
187 #endif
188 
189 struct frame_state
190 {
191   gint n_buffers;
192   gpointer in_bufs[N_BUFFERS_PER_FRAME];
193   gpointer out_bufs[N_BUFFERS_PER_FRAME];
194 };
195 
196 static gboolean gst_nv_base_enc_open (GstVideoEncoder * enc);
197 static gboolean gst_nv_base_enc_close (GstVideoEncoder * enc);
198 static gboolean gst_nv_base_enc_start (GstVideoEncoder * enc);
199 static gboolean gst_nv_base_enc_stop (GstVideoEncoder * enc);
200 static void gst_nv_base_enc_set_context (GstElement * element,
201     GstContext * context);
202 static gboolean gst_nv_base_enc_sink_query (GstVideoEncoder * enc,
203     GstQuery * query);
204 static gboolean gst_nv_base_enc_set_format (GstVideoEncoder * enc,
205     GstVideoCodecState * state);
206 static GstFlowReturn gst_nv_base_enc_handle_frame (GstVideoEncoder * enc,
207     GstVideoCodecFrame * frame);
208 static void gst_nv_base_enc_free_buffers (GstNvBaseEnc * nvenc);
209 static GstFlowReturn gst_nv_base_enc_finish (GstVideoEncoder * enc);
210 static void gst_nv_base_enc_set_property (GObject * object, guint prop_id,
211     const GValue * value, GParamSpec * pspec);
212 static void gst_nv_base_enc_get_property (GObject * object, guint prop_id,
213     GValue * value, GParamSpec * pspec);
214 static void gst_nv_base_enc_finalize (GObject * obj);
215 static GstCaps *gst_nv_base_enc_getcaps (GstVideoEncoder * enc,
216     GstCaps * filter);
217 static gboolean gst_nv_base_enc_stop_bitstream_thread (GstNvBaseEnc * nvenc,
218     gboolean force);
219 
220 static void
gst_nv_base_enc_class_init(GstNvBaseEncClass * klass)221 gst_nv_base_enc_class_init (GstNvBaseEncClass * klass)
222 {
223   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
224   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
225   GstVideoEncoderClass *videoenc_class = GST_VIDEO_ENCODER_CLASS (klass);
226 
227   gobject_class->set_property = gst_nv_base_enc_set_property;
228   gobject_class->get_property = gst_nv_base_enc_get_property;
229   gobject_class->finalize = gst_nv_base_enc_finalize;
230 
231   element_class->set_context = GST_DEBUG_FUNCPTR (gst_nv_base_enc_set_context);
232 
233   videoenc_class->open = GST_DEBUG_FUNCPTR (gst_nv_base_enc_open);
234   videoenc_class->close = GST_DEBUG_FUNCPTR (gst_nv_base_enc_close);
235 
236   videoenc_class->start = GST_DEBUG_FUNCPTR (gst_nv_base_enc_start);
237   videoenc_class->stop = GST_DEBUG_FUNCPTR (gst_nv_base_enc_stop);
238 
239   videoenc_class->set_format = GST_DEBUG_FUNCPTR (gst_nv_base_enc_set_format);
240   videoenc_class->getcaps = GST_DEBUG_FUNCPTR (gst_nv_base_enc_getcaps);
241   videoenc_class->handle_frame =
242       GST_DEBUG_FUNCPTR (gst_nv_base_enc_handle_frame);
243   videoenc_class->finish = GST_DEBUG_FUNCPTR (gst_nv_base_enc_finish);
244   videoenc_class->sink_query = GST_DEBUG_FUNCPTR (gst_nv_base_enc_sink_query);
245 
246   g_object_class_install_property (gobject_class, PROP_DEVICE_ID,
247       g_param_spec_uint ("cuda-device-id",
248           "Cuda Device ID",
249           "Set the GPU device to use for operations",
250           0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
251   g_object_class_install_property (gobject_class, PROP_PRESET,
252       g_param_spec_enum ("preset", "Encoding Preset",
253           "Encoding Preset",
254           GST_TYPE_NV_PRESET, DEFAULT_PRESET,
255           G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
256           G_PARAM_STATIC_STRINGS));
257   g_object_class_install_property (gobject_class, PROP_RC_MODE,
258       g_param_spec_enum ("rc-mode", "RC Mode", "Rate Control Mode",
259           GST_TYPE_NV_RC_MODE, DEFAULT_RC_MODE,
260           G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
261           G_PARAM_STATIC_STRINGS));
262   g_object_class_install_property (gobject_class, PROP_QP_MIN,
263       g_param_spec_int ("qp-min", "Minimum Quantizer",
264           "Minimum quantizer (-1 = from NVENC preset)", -1, 51, DEFAULT_QP_MIN,
265           G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
266           G_PARAM_STATIC_STRINGS));
267   g_object_class_install_property (gobject_class, PROP_QP_MAX,
268       g_param_spec_int ("qp-max", "Maximum Quantizer",
269           "Maximum quantizer (-1 = from NVENC preset)", -1, 51, DEFAULT_QP_MAX,
270           G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
271           G_PARAM_STATIC_STRINGS));
272   g_object_class_install_property (gobject_class, PROP_QP_CONST,
273       g_param_spec_int ("qp-const", "Constant Quantizer",
274           "Constant quantizer (-1 = from NVENC preset)", -1, 51,
275           DEFAULT_QP_CONST,
276           G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
277           G_PARAM_STATIC_STRINGS));
278   g_object_class_install_property (gobject_class, PROP_GOP_SIZE,
279       g_param_spec_int ("gop-size", "GOP size",
280           "Number of frames between intra frames (-1 = infinite)",
281           -1, G_MAXINT, DEFAULT_GOP_SIZE,
282           (GParamFlags) (G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
283               G_PARAM_STATIC_STRINGS)));
284   g_object_class_install_property (gobject_class, PROP_BITRATE,
285       g_param_spec_uint ("bitrate", "Bitrate",
286           "Bitrate in kbit/sec (0 = from NVENC preset)", 0, 2000 * 1024,
287           DEFAULT_BITRATE,
288           G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
289           G_PARAM_STATIC_STRINGS));
290 }
291 
292 static gboolean
_get_supported_input_formats(GstNvBaseEnc * nvenc)293 _get_supported_input_formats (GstNvBaseEnc * nvenc)
294 {
295   GstNvBaseEncClass *nvenc_class = GST_NV_BASE_ENC_GET_CLASS (nvenc);
296   guint64 format_mask = 0;
297   uint32_t i, num = 0;
298   NV_ENC_BUFFER_FORMAT formats[64];
299   GValue val = G_VALUE_INIT;
300 
301   if (nvenc->input_formats)
302     return TRUE;
303 
304   NvEncGetInputFormats (nvenc->encoder, nvenc_class->codec_id, formats,
305       G_N_ELEMENTS (formats), &num);
306 
307   for (i = 0; i < num; ++i) {
308     GST_INFO_OBJECT (nvenc, "input format: 0x%08x", formats[i]);
309     /* Apparently we can just ignore the tiled formats and can feed
310      * it the respective untiled planar format instead ?! */
311     switch (formats[i]) {
312       case NV_ENC_BUFFER_FORMAT_NV12_PL:
313 #if defined (NV_ENC_BUFFER_FORMAT_NV12_TILED16x16)
314       case NV_ENC_BUFFER_FORMAT_NV12_TILED16x16:
315 #endif
316 #if defined (NV_ENC_BUFFER_FORMAT_NV12_TILED64x16)
317       case NV_ENC_BUFFER_FORMAT_NV12_TILED64x16:
318 #endif
319         format_mask |= (1 << GST_VIDEO_FORMAT_NV12);
320         break;
321       case NV_ENC_BUFFER_FORMAT_YV12_PL:
322 #if defined(NV_ENC_BUFFER_FORMAT_YV12_TILED16x16)
323       case NV_ENC_BUFFER_FORMAT_YV12_TILED16x16:
324 #endif
325 #if defined (NV_ENC_BUFFER_FORMAT_YV12_TILED64x16)
326       case NV_ENC_BUFFER_FORMAT_YV12_TILED64x16:
327 #endif
328         format_mask |= (1 << GST_VIDEO_FORMAT_YV12);
329         break;
330       case NV_ENC_BUFFER_FORMAT_IYUV_PL:
331 #if defined (NV_ENC_BUFFER_FORMAT_IYUV_TILED16x16)
332       case NV_ENC_BUFFER_FORMAT_IYUV_TILED16x16:
333 #endif
334 #if defined (NV_ENC_BUFFER_FORMAT_IYUV_TILED64x16)
335       case NV_ENC_BUFFER_FORMAT_IYUV_TILED64x16:
336 #endif
337         format_mask |= (1 << GST_VIDEO_FORMAT_I420);
338         break;
339       case NV_ENC_BUFFER_FORMAT_YUV444_PL:
340 #if defined (NV_ENC_BUFFER_FORMAT_YUV444_TILED16x16)
341       case NV_ENC_BUFFER_FORMAT_YUV444_TILED16x16:
342 #endif
343 #if defined (NV_ENC_BUFFER_FORMAT_YUV444_TILED64x16)
344       case NV_ENC_BUFFER_FORMAT_YUV444_TILED64x16:
345 #endif
346       {
347         NV_ENC_CAPS_PARAM caps_param = { 0, };
348         int yuv444_supported = 0;
349 
350         caps_param.version = NV_ENC_CAPS_PARAM_VER;
351         caps_param.capsToQuery = NV_ENC_CAPS_SUPPORT_YUV444_ENCODE;
352 
353         if (NvEncGetEncodeCaps (nvenc->encoder, nvenc_class->codec_id,
354                 &caps_param, &yuv444_supported) != NV_ENC_SUCCESS)
355           yuv444_supported = 0;
356 
357         if (yuv444_supported)
358           format_mask |= (1 << GST_VIDEO_FORMAT_Y444);
359         break;
360       }
361       default:
362         GST_FIXME ("unmapped input format: 0x%08x", formats[i]);
363         break;
364     }
365   }
366 
367   if (format_mask == 0)
368     return FALSE;
369 
370   GST_OBJECT_LOCK (nvenc);
371   nvenc->input_formats = g_new0 (GValue, 1);
372 
373   /* process a second time so we can add formats in the order we want */
374   g_value_init (nvenc->input_formats, GST_TYPE_LIST);
375   g_value_init (&val, G_TYPE_STRING);
376   if ((format_mask & (1 << GST_VIDEO_FORMAT_NV12))) {
377     g_value_set_static_string (&val, "NV12");
378     gst_value_list_append_value (nvenc->input_formats, &val);
379   }
380   if ((format_mask & (1 << GST_VIDEO_FORMAT_YV12))) {
381     g_value_set_static_string (&val, "YV12");
382     gst_value_list_append_value (nvenc->input_formats, &val);
383   }
384   if ((format_mask & (1 << GST_VIDEO_FORMAT_I420))) {
385     g_value_set_static_string (&val, "I420");
386     gst_value_list_append_value (nvenc->input_formats, &val);
387   }
388   if ((format_mask & (1 << GST_VIDEO_FORMAT_Y444))) {
389     g_value_set_static_string (&val, "Y444");
390     gst_value_list_append_value (nvenc->input_formats, &val);
391   }
392   g_value_unset (&val);
393 
394   GST_OBJECT_UNLOCK (nvenc);
395 
396   return TRUE;
397 }
398 
399 static gboolean
gst_nv_base_enc_open(GstVideoEncoder * enc)400 gst_nv_base_enc_open (GstVideoEncoder * enc)
401 {
402   GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (enc);
403 
404   nvenc->cuda_ctx = gst_nvenc_create_cuda_context (nvenc->cuda_device_id);
405   if (nvenc->cuda_ctx == NULL) {
406     GST_ELEMENT_ERROR (enc, LIBRARY, INIT, (NULL),
407         ("Failed to create CUDA context, perhaps CUDA is not supported."));
408     return FALSE;
409   }
410 
411   {
412     NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS params = { 0, };
413     NVENCSTATUS nv_ret;
414 
415     params.version = NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER;
416     params.apiVersion = NVENCAPI_VERSION;
417     params.device = nvenc->cuda_ctx;
418     params.deviceType = NV_ENC_DEVICE_TYPE_CUDA;
419     nv_ret = NvEncOpenEncodeSessionEx (&params, &nvenc->encoder);
420     if (nv_ret != NV_ENC_SUCCESS) {
421       GST_ERROR ("Failed to create NVENC encoder session, ret=%d", nv_ret);
422       if (gst_nvenc_destroy_cuda_context (nvenc->cuda_ctx))
423         nvenc->cuda_ctx = NULL;
424       return FALSE;
425     }
426     GST_INFO ("created NVENC encoder %p", nvenc->encoder);
427   }
428 
429   /* query supported input formats */
430   if (!_get_supported_input_formats (nvenc)) {
431     GST_WARNING_OBJECT (nvenc, "No supported input formats");
432     gst_nv_base_enc_close (enc);
433     return FALSE;
434   }
435 
436   return TRUE;
437 }
438 
439 static void
gst_nv_base_enc_set_context(GstElement * element,GstContext * context)440 gst_nv_base_enc_set_context (GstElement * element, GstContext * context)
441 {
442 #if HAVE_NVENC_GST_GL
443   GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (element);
444 
445   gst_gl_handle_set_context (element, context,
446       (GstGLDisplay **) & nvenc->display,
447       (GstGLContext **) & nvenc->other_context);
448   if (nvenc->display)
449     gst_gl_display_filter_gl_api (GST_GL_DISPLAY (nvenc->display),
450         SUPPORTED_GL_APIS);
451 #endif
452 
453   GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
454 }
455 
456 static gboolean
gst_nv_base_enc_sink_query(GstVideoEncoder * enc,GstQuery * query)457 gst_nv_base_enc_sink_query (GstVideoEncoder * enc, GstQuery * query)
458 {
459 #if HAVE_NVENC_GST_GL
460   GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (enc);
461 #endif
462 
463   switch (GST_QUERY_TYPE (query)) {
464 #if HAVE_NVENC_GST_GL
465     case GST_QUERY_CONTEXT:{
466       gboolean ret;
467 
468       ret = gst_gl_handle_context_query ((GstElement *) nvenc, query,
469           nvenc->display, NULL, nvenc->other_context);
470       if (nvenc->display)
471         gst_gl_display_filter_gl_api (GST_GL_DISPLAY (nvenc->display),
472             SUPPORTED_GL_APIS);
473 
474       if (ret)
475         return ret;
476       break;
477     }
478 #endif
479     default:
480       break;
481   }
482 
483   return GST_VIDEO_ENCODER_CLASS (parent_class)->sink_query (enc, query);
484 }
485 
486 static gboolean
gst_nv_base_enc_start(GstVideoEncoder * enc)487 gst_nv_base_enc_start (GstVideoEncoder * enc)
488 {
489   GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (enc);
490 
491   nvenc->bitstream_pool = g_async_queue_new ();
492   nvenc->bitstream_queue = g_async_queue_new ();
493   nvenc->in_bufs_pool = g_async_queue_new ();
494 
495   nvenc->last_flow = GST_FLOW_OK;
496 
497 #if HAVE_NVENC_GST_GL
498   {
499     gst_gl_ensure_element_data (GST_ELEMENT (nvenc),
500         (GstGLDisplay **) & nvenc->display,
501         (GstGLContext **) & nvenc->other_context);
502     if (nvenc->display)
503       gst_gl_display_filter_gl_api (GST_GL_DISPLAY (nvenc->display),
504           SUPPORTED_GL_APIS);
505   }
506 #endif
507 
508   return TRUE;
509 }
510 
511 static gboolean
gst_nv_base_enc_stop(GstVideoEncoder * enc)512 gst_nv_base_enc_stop (GstVideoEncoder * enc)
513 {
514   GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (enc);
515 
516   gst_nv_base_enc_stop_bitstream_thread (nvenc, TRUE);
517 
518   gst_nv_base_enc_free_buffers (nvenc);
519 
520   if (nvenc->input_state) {
521     gst_video_codec_state_unref (nvenc->input_state);
522     nvenc->input_state = NULL;
523   }
524 
525   if (nvenc->bitstream_pool) {
526     g_async_queue_unref (nvenc->bitstream_pool);
527     nvenc->bitstream_pool = NULL;
528   }
529   if (nvenc->bitstream_queue) {
530     g_async_queue_unref (nvenc->bitstream_queue);
531     nvenc->bitstream_queue = NULL;
532   }
533   if (nvenc->in_bufs_pool) {
534     g_async_queue_unref (nvenc->in_bufs_pool);
535     nvenc->in_bufs_pool = NULL;
536   }
537   if (nvenc->display) {
538     gst_object_unref (nvenc->display);
539     nvenc->display = NULL;
540   }
541   if (nvenc->other_context) {
542     gst_object_unref (nvenc->other_context);
543     nvenc->other_context = NULL;
544   }
545 
546   return TRUE;
547 }
548 
549 static GValue *
_get_interlace_modes(GstNvBaseEnc * nvenc)550 _get_interlace_modes (GstNvBaseEnc * nvenc)
551 {
552   GstNvBaseEncClass *nvenc_class = GST_NV_BASE_ENC_GET_CLASS (nvenc);
553   NV_ENC_CAPS_PARAM caps_param = { 0, };
554   GValue *list = g_new0 (GValue, 1);
555   GValue val = G_VALUE_INIT;
556 
557   g_value_init (list, GST_TYPE_LIST);
558   g_value_init (&val, G_TYPE_STRING);
559 
560   g_value_set_static_string (&val, "progressive");
561   gst_value_list_append_value (list, &val);
562 
563   caps_param.version = NV_ENC_CAPS_PARAM_VER;
564   caps_param.capsToQuery = NV_ENC_CAPS_SUPPORT_FIELD_ENCODING;
565 
566   if (NvEncGetEncodeCaps (nvenc->encoder, nvenc_class->codec_id,
567           &caps_param, &nvenc->interlace_modes) != NV_ENC_SUCCESS)
568     nvenc->interlace_modes = 0;
569 
570   if (nvenc->interlace_modes >= 1) {
571     g_value_set_static_string (&val, "interleaved");
572     gst_value_list_append_value (list, &val);
573     g_value_set_static_string (&val, "mixed");
574     gst_value_list_append_and_take_value (list, &val);
575   }
576   /* TODO: figure out what nvenc frame based interlacing means in gst terms */
577 
578   return list;
579 }
580 
581 static GstCaps *
gst_nv_base_enc_getcaps(GstVideoEncoder * enc,GstCaps * filter)582 gst_nv_base_enc_getcaps (GstVideoEncoder * enc, GstCaps * filter)
583 {
584   GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (enc);
585   GstCaps *supported_incaps = NULL;
586   GstCaps *template_caps, *caps;
587 
588   GST_OBJECT_LOCK (nvenc);
589 
590   if (nvenc->input_formats != NULL) {
591     GValue *val;
592 
593     template_caps = gst_pad_get_pad_template_caps (enc->sinkpad);
594     supported_incaps = gst_caps_copy (template_caps);
595     gst_caps_set_value (supported_incaps, "format", nvenc->input_formats);
596 
597     val = _get_interlace_modes (nvenc);
598     gst_caps_set_value (supported_incaps, "interlace-mode", val);
599     g_value_unset (val);
600     g_free (val);
601 
602     GST_LOG_OBJECT (enc, "codec input caps %" GST_PTR_FORMAT, supported_incaps);
603     GST_LOG_OBJECT (enc, "   template caps %" GST_PTR_FORMAT, template_caps);
604     caps = gst_caps_intersect (template_caps, supported_incaps);
605     gst_caps_unref (template_caps);
606     gst_caps_unref (supported_incaps);
607     supported_incaps = caps;
608     GST_LOG_OBJECT (enc, "  supported caps %" GST_PTR_FORMAT, supported_incaps);
609   }
610 
611   GST_OBJECT_UNLOCK (nvenc);
612 
613   caps = gst_video_encoder_proxy_getcaps (enc, supported_incaps, filter);
614 
615   if (supported_incaps)
616     gst_caps_unref (supported_incaps);
617 
618   GST_DEBUG_OBJECT (nvenc, "  returning caps %" GST_PTR_FORMAT, caps);
619 
620   return caps;
621 }
622 
623 static gboolean
gst_nv_base_enc_close(GstVideoEncoder * enc)624 gst_nv_base_enc_close (GstVideoEncoder * enc)
625 {
626   GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (enc);
627 
628   if (nvenc->encoder) {
629     if (NvEncDestroyEncoder (nvenc->encoder) != NV_ENC_SUCCESS)
630       return FALSE;
631     nvenc->encoder = NULL;
632   }
633 
634   if (nvenc->cuda_ctx) {
635     if (!gst_nvenc_destroy_cuda_context (nvenc->cuda_ctx))
636       return FALSE;
637     nvenc->cuda_ctx = NULL;
638   }
639 
640   GST_OBJECT_LOCK (nvenc);
641   if (nvenc->input_formats)
642     g_value_unset (nvenc->input_formats);
643   g_free (nvenc->input_formats);
644   nvenc->input_formats = NULL;
645   GST_OBJECT_UNLOCK (nvenc);
646 
647   if (nvenc->input_state) {
648     gst_video_codec_state_unref (nvenc->input_state);
649     nvenc->input_state = NULL;
650   }
651 
652   if (nvenc->bitstream_pool != NULL) {
653     g_assert (g_async_queue_length (nvenc->bitstream_pool) == 0);
654     g_async_queue_unref (nvenc->bitstream_pool);
655     nvenc->bitstream_pool = NULL;
656   }
657 
658   return TRUE;
659 }
660 
661 static void
gst_nv_base_enc_init(GstNvBaseEnc * nvenc)662 gst_nv_base_enc_init (GstNvBaseEnc * nvenc)
663 {
664   GstVideoEncoder *encoder = GST_VIDEO_ENCODER (nvenc);
665 
666   nvenc->preset_enum = DEFAULT_PRESET;
667   nvenc->selected_preset = _nv_preset_to_guid (nvenc->preset_enum);
668   nvenc->rate_control_mode = DEFAULT_RC_MODE;
669   nvenc->qp_min = DEFAULT_QP_MIN;
670   nvenc->qp_max = DEFAULT_QP_MAX;
671   nvenc->qp_const = DEFAULT_QP_CONST;
672   nvenc->bitrate = DEFAULT_BITRATE;
673   nvenc->gop_size = DEFAULT_GOP_SIZE;
674 
675   GST_VIDEO_ENCODER_STREAM_LOCK (encoder);
676   GST_VIDEO_ENCODER_STREAM_UNLOCK (encoder);
677 }
678 
679 static void
gst_nv_base_enc_finalize(GObject * obj)680 gst_nv_base_enc_finalize (GObject * obj)
681 {
682   G_OBJECT_CLASS (gst_nv_base_enc_parent_class)->finalize (obj);
683 }
684 
685 static GstVideoCodecFrame *
_find_frame_with_output_buffer(GstNvBaseEnc * nvenc,NV_ENC_OUTPUT_PTR out_buf)686 _find_frame_with_output_buffer (GstNvBaseEnc * nvenc, NV_ENC_OUTPUT_PTR out_buf)
687 {
688   GList *l, *walk = gst_video_encoder_get_frames (GST_VIDEO_ENCODER (nvenc));
689   GstVideoCodecFrame *ret = NULL;
690   gint i;
691 
692   for (l = walk; l; l = l->next) {
693     GstVideoCodecFrame *frame = (GstVideoCodecFrame *) l->data;
694     struct frame_state *state = frame->user_data;
695 
696     if (!state)
697       continue;
698 
699     for (i = 0; i < N_BUFFERS_PER_FRAME; i++) {
700 
701       if (!state->out_bufs[i])
702         break;
703 
704       if (state->out_bufs[i] == out_buf)
705         ret = frame;
706     }
707   }
708 
709   if (ret)
710     gst_video_codec_frame_ref (ret);
711 
712   g_list_free_full (walk, (GDestroyNotify) gst_video_codec_frame_unref);
713 
714   return ret;
715 }
716 
717 static gpointer
gst_nv_base_enc_bitstream_thread(gpointer user_data)718 gst_nv_base_enc_bitstream_thread (gpointer user_data)
719 {
720   GstVideoEncoder *enc = user_data;
721   GstNvBaseEnc *nvenc = user_data;
722 
723   /* overview of operation:
724    * 1. retreive the next buffer submitted to the bitstream pool
725    * 2. wait for that buffer to be ready from nvenc (LockBitsream)
726    * 3. retreive the GstVideoCodecFrame associated with that buffer
727    * 4. for each buffer in the frame
728    * 4.1 (step 2): wait for that buffer to be ready from nvenc (LockBitsream)
729    * 4.2 create an output GstBuffer from the nvenc buffers
730    * 4.3 unlock the nvenc bitstream buffers UnlockBitsream
731    * 5. finish_frame()
732    * 6. cleanup
733    */
734   do {
735     GstBuffer *buffers[N_BUFFERS_PER_FRAME];
736     struct frame_state *state = NULL;
737     GstVideoCodecFrame *frame = NULL;
738     NVENCSTATUS nv_ret;
739     GstFlowReturn flow = GST_FLOW_OK;
740     gint i;
741 
742     {
743       NV_ENC_LOCK_BITSTREAM lock_bs = { 0, };
744       NV_ENC_OUTPUT_PTR out_buf;
745 
746       for (i = 0; i < N_BUFFERS_PER_FRAME; i++) {
747         /* get and lock bitstream buffers */
748         GstVideoCodecFrame *tmp_frame;
749 
750         if (state && i >= state->n_buffers)
751           break;
752 
753         GST_LOG_OBJECT (enc, "wait for bitstream buffer..");
754 
755         /* assumes buffers are submitted in order */
756         out_buf = g_async_queue_pop (nvenc->bitstream_queue);
757         if ((gpointer) out_buf == SHUTDOWN_COOKIE)
758           break;
759 
760         GST_LOG_OBJECT (nvenc, "waiting for output buffer %p to be ready",
761             out_buf);
762 
763         lock_bs.version = NV_ENC_LOCK_BITSTREAM_VER;
764         lock_bs.outputBitstream = out_buf;
765         lock_bs.doNotWait = 0;
766 
767         /* FIXME: this would need to be updated for other slice modes */
768         lock_bs.sliceOffsets = NULL;
769 
770         nv_ret = NvEncLockBitstream (nvenc->encoder, &lock_bs);
771         if (nv_ret != NV_ENC_SUCCESS) {
772           /* FIXME: what to do here? */
773           GST_ELEMENT_ERROR (nvenc, STREAM, ENCODE, (NULL),
774               ("Failed to lock bitstream buffer %p, ret %d",
775                   lock_bs.outputBitstream, nv_ret));
776           out_buf = SHUTDOWN_COOKIE;
777           break;
778         }
779 
780         GST_LOG_OBJECT (nvenc, "picture type %d", lock_bs.pictureType);
781 
782         tmp_frame = _find_frame_with_output_buffer (nvenc, out_buf);
783         g_assert (tmp_frame != NULL);
784         if (frame)
785           g_assert (frame == tmp_frame);
786         frame = tmp_frame;
787 
788         state = frame->user_data;
789         g_assert (state->out_bufs[i] == out_buf);
790 
791         /* copy into output buffer */
792         buffers[i] =
793             gst_buffer_new_allocate (NULL, lock_bs.bitstreamSizeInBytes, NULL);
794         gst_buffer_fill (buffers[i], 0, lock_bs.bitstreamBufferPtr,
795             lock_bs.bitstreamSizeInBytes);
796 
797         if (lock_bs.pictureType == NV_ENC_PIC_TYPE_IDR) {
798           GST_DEBUG_OBJECT (nvenc, "This is a keyframe");
799           GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame);
800         }
801 
802         /* TODO: use lock_bs.outputTimeStamp and lock_bs.outputDuration */
803         /* TODO: check pts/dts is handled properly if there are B-frames */
804 
805         nv_ret = NvEncUnlockBitstream (nvenc->encoder, state->out_bufs[i]);
806         if (nv_ret != NV_ENC_SUCCESS) {
807           /* FIXME: what to do here? */
808           GST_ELEMENT_ERROR (nvenc, STREAM, ENCODE, (NULL),
809               ("Failed to unlock bitstream buffer %p, ret %d",
810                   lock_bs.outputBitstream, nv_ret));
811           state->out_bufs[i] = SHUTDOWN_COOKIE;
812           break;
813         }
814 
815         GST_LOG_OBJECT (nvenc, "returning bitstream buffer %p to pool",
816             state->out_bufs[i]);
817         g_async_queue_push (nvenc->bitstream_pool, state->out_bufs[i]);
818       }
819 
820       if (out_buf == SHUTDOWN_COOKIE)
821         break;
822     }
823 
824     {
825       GstBuffer *output_buffer = gst_buffer_new ();
826 
827       for (i = 0; i < state->n_buffers; i++)
828         output_buffer = gst_buffer_append (output_buffer, buffers[i]);
829 
830       frame->output_buffer = output_buffer;
831     }
832 
833     for (i = 0; i < state->n_buffers; i++) {
834       void *in_buf = state->in_bufs[i];
835       g_assert (in_buf != NULL);
836 
837 #if HAVE_NVENC_GST_GL
838       if (nvenc->gl_input) {
839         struct gl_input_resource *in_gl_resource = in_buf;
840 
841         nv_ret =
842             NvEncUnmapInputResource (nvenc->encoder,
843             in_gl_resource->nv_mapped_resource.mappedResource);
844         in_gl_resource->mapped = FALSE;
845 
846         if (nv_ret != NV_ENC_SUCCESS) {
847           GST_ERROR_OBJECT (nvenc, "Failed to unmap input resource %p, ret %d",
848               in_gl_resource, nv_ret);
849           break;
850         }
851 
852         memset (&in_gl_resource->nv_mapped_resource, 0,
853             sizeof (in_gl_resource->nv_mapped_resource));
854       }
855 #endif
856 
857       g_async_queue_push (nvenc->in_bufs_pool, in_buf);
858     }
859 
860     flow = gst_video_encoder_finish_frame (enc, frame);
861     frame = NULL;
862 
863     if (flow != GST_FLOW_OK) {
864       GST_INFO_OBJECT (enc, "got flow %s", gst_flow_get_name (flow));
865       g_atomic_int_set (&nvenc->last_flow, flow);
866       break;
867     }
868   }
869   while (TRUE);
870 
871   GST_INFO_OBJECT (nvenc, "exiting thread");
872 
873   return NULL;
874 }
875 
876 static gboolean
gst_nv_base_enc_start_bitstream_thread(GstNvBaseEnc * nvenc)877 gst_nv_base_enc_start_bitstream_thread (GstNvBaseEnc * nvenc)
878 {
879   gchar *name = g_strdup_printf ("%s-read-bits", GST_OBJECT_NAME (nvenc));
880 
881   g_assert (nvenc->bitstream_thread == NULL);
882 
883   g_assert (g_async_queue_length (nvenc->bitstream_queue) == 0);
884 
885   nvenc->bitstream_thread =
886       g_thread_try_new (name, gst_nv_base_enc_bitstream_thread, nvenc, NULL);
887 
888   g_free (name);
889 
890   if (nvenc->bitstream_thread == NULL)
891     return FALSE;
892 
893   GST_INFO_OBJECT (nvenc, "started thread to read bitstream");
894   return TRUE;
895 }
896 
897 static gboolean
gst_nv_base_enc_stop_bitstream_thread(GstNvBaseEnc * nvenc,gboolean force)898 gst_nv_base_enc_stop_bitstream_thread (GstNvBaseEnc * nvenc, gboolean force)
899 {
900   gpointer out_buf;
901 
902   if (nvenc->bitstream_thread == NULL)
903     return TRUE;
904 
905   if (force) {
906     g_async_queue_lock (nvenc->bitstream_queue);
907     g_async_queue_lock (nvenc->bitstream_pool);
908     while ((out_buf = g_async_queue_try_pop_unlocked (nvenc->bitstream_queue))) {
909       GST_INFO_OBJECT (nvenc, "stole bitstream buffer %p from queue", out_buf);
910       g_async_queue_push_unlocked (nvenc->bitstream_pool, out_buf);
911     }
912     g_async_queue_push_unlocked (nvenc->bitstream_queue, SHUTDOWN_COOKIE);
913     g_async_queue_unlock (nvenc->bitstream_pool);
914     g_async_queue_unlock (nvenc->bitstream_queue);
915   } else {
916     /* wait for encoder to drain the remaining buffers */
917     g_async_queue_push (nvenc->bitstream_queue, SHUTDOWN_COOKIE);
918   }
919 
920   if (!force) {
921     /* temporary unlock during finish, so other thread can find and push frame */
922     GST_VIDEO_ENCODER_STREAM_UNLOCK (nvenc);
923   }
924 
925   g_thread_join (nvenc->bitstream_thread);
926 
927   if (!force)
928     GST_VIDEO_ENCODER_STREAM_LOCK (nvenc);
929 
930   nvenc->bitstream_thread = NULL;
931   return TRUE;
932 }
933 
934 static void
gst_nv_base_enc_reset_queues(GstNvBaseEnc * nvenc,gboolean refill)935 gst_nv_base_enc_reset_queues (GstNvBaseEnc * nvenc, gboolean refill)
936 {
937   gpointer ptr;
938   gint i;
939 
940   GST_INFO_OBJECT (nvenc, "clearing queues");
941 
942   while ((ptr = g_async_queue_try_pop (nvenc->bitstream_queue))) {
943     /* do nothing */
944   }
945   while ((ptr = g_async_queue_try_pop (nvenc->bitstream_pool))) {
946     /* do nothing */
947   }
948   while ((ptr = g_async_queue_try_pop (nvenc->in_bufs_pool))) {
949     /* do nothing */
950   }
951 
952   if (refill) {
953     GST_INFO_OBJECT (nvenc, "refilling buffer pools");
954     for (i = 0; i < nvenc->n_bufs; ++i) {
955       g_async_queue_push (nvenc->bitstream_pool, nvenc->input_bufs[i]);
956       g_async_queue_push (nvenc->in_bufs_pool, nvenc->output_bufs[i]);
957     }
958   }
959 }
960 
961 static void
gst_nv_base_enc_free_buffers(GstNvBaseEnc * nvenc)962 gst_nv_base_enc_free_buffers (GstNvBaseEnc * nvenc)
963 {
964   NVENCSTATUS nv_ret;
965   guint i;
966 
967   if (nvenc->encoder == NULL)
968     return;
969 
970   gst_nv_base_enc_reset_queues (nvenc, FALSE);
971 
972   for (i = 0; i < nvenc->n_bufs; ++i) {
973     NV_ENC_OUTPUT_PTR out_buf = nvenc->output_bufs[i];
974 
975 #if HAVE_NVENC_GST_GL
976     if (nvenc->gl_input) {
977       struct gl_input_resource *in_gl_resource = nvenc->input_bufs[i];
978 
979       cuCtxPushCurrent (nvenc->cuda_ctx);
980 
981       if (in_gl_resource->mapped) {
982         GST_LOG_OBJECT (nvenc, "Unmap resource %p", in_gl_resource);
983 
984         nv_ret =
985             NvEncUnmapInputResource (nvenc->encoder,
986             in_gl_resource->nv_mapped_resource.mappedResource);
987 
988         if (nv_ret != NV_ENC_SUCCESS) {
989           GST_ERROR_OBJECT (nvenc, "Failed to unmap input resource %p, ret %d",
990               in_gl_resource, nv_ret);
991         }
992       }
993 
994       nv_ret =
995           NvEncUnregisterResource (nvenc->encoder,
996           in_gl_resource->nv_resource.registeredResource);
997       if (nv_ret != NV_ENC_SUCCESS)
998         GST_ERROR_OBJECT (nvenc, "Failed to unregister resource %p, ret %d",
999             in_gl_resource, nv_ret);
1000 
1001       nv_ret = cuMemFree ((CUdeviceptr) in_gl_resource->cuda_pointer);
1002       if (nv_ret != NV_ENC_SUCCESS) {
1003         GST_ERROR_OBJECT (nvenc, "Failed to free CUDA device memory, ret %d",
1004             nv_ret);
1005       }
1006 
1007       g_free (in_gl_resource);
1008       cuCtxPopCurrent (NULL);
1009     } else
1010 #endif
1011     {
1012       NV_ENC_INPUT_PTR in_buf = (NV_ENC_INPUT_PTR) nvenc->input_bufs[i];
1013 
1014       GST_DEBUG_OBJECT (nvenc, "Destroying input buffer %p", in_buf);
1015       nv_ret = NvEncDestroyInputBuffer (nvenc->encoder, in_buf);
1016       if (nv_ret != NV_ENC_SUCCESS) {
1017         GST_ERROR_OBJECT (nvenc, "Failed to destroy input buffer %p, ret %d",
1018             in_buf, nv_ret);
1019       }
1020     }
1021 
1022     GST_DEBUG_OBJECT (nvenc, "Destroying output bitstream buffer %p", out_buf);
1023     nv_ret = NvEncDestroyBitstreamBuffer (nvenc->encoder, out_buf);
1024     if (nv_ret != NV_ENC_SUCCESS) {
1025       GST_ERROR_OBJECT (nvenc, "Failed to destroy output buffer %p, ret %d",
1026           out_buf, nv_ret);
1027     }
1028   }
1029 
1030   nvenc->n_bufs = 0;
1031   g_free (nvenc->output_bufs);
1032   nvenc->output_bufs = NULL;
1033   g_free (nvenc->input_bufs);
1034   nvenc->input_bufs = NULL;
1035 }
1036 
1037 static inline guint
_get_plane_width(GstVideoInfo * info,guint plane)1038 _get_plane_width (GstVideoInfo * info, guint plane)
1039 {
1040   if (GST_VIDEO_INFO_IS_YUV (info))
1041     /* For now component width and plane width are the same and the
1042      * plane-component mapping matches
1043      */
1044     return GST_VIDEO_INFO_COMP_WIDTH (info, plane);
1045   else                          /* RGB, GRAY */
1046     return GST_VIDEO_INFO_WIDTH (info);
1047 }
1048 
1049 static inline guint
_get_plane_height(GstVideoInfo * info,guint plane)1050 _get_plane_height (GstVideoInfo * info, guint plane)
1051 {
1052   if (GST_VIDEO_INFO_IS_YUV (info))
1053     /* For now component width and plane width are the same and the
1054      * plane-component mapping matches
1055      */
1056     return GST_VIDEO_INFO_COMP_HEIGHT (info, plane);
1057   else                          /* RGB, GRAY */
1058     return GST_VIDEO_INFO_HEIGHT (info);
1059 }
1060 
1061 static inline gsize
_get_frame_data_height(GstVideoInfo * info)1062 _get_frame_data_height (GstVideoInfo * info)
1063 {
1064   gsize ret = 0;
1065   gint i;
1066 
1067   for (i = 0; i < GST_VIDEO_INFO_N_PLANES (info); i++) {
1068     ret += _get_plane_height (info, i);
1069   }
1070 
1071   return ret;
1072 }
1073 
1074 void
gst_nv_base_enc_set_max_encode_size(GstNvBaseEnc * nvenc,guint max_width,guint max_height)1075 gst_nv_base_enc_set_max_encode_size (GstNvBaseEnc * nvenc, guint max_width,
1076     guint max_height)
1077 {
1078   nvenc->max_encode_width = max_width;
1079   nvenc->max_encode_height = max_height;
1080 }
1081 
1082 void
gst_nv_base_enc_get_max_encode_size(GstNvBaseEnc * nvenc,guint * max_width,guint * max_height)1083 gst_nv_base_enc_get_max_encode_size (GstNvBaseEnc * nvenc, guint * max_width,
1084     guint * max_height)
1085 {
1086   *max_width = nvenc->max_encode_width;
1087   *max_height = nvenc->max_encode_height;
1088 }
1089 
1090 static gboolean
gst_nv_base_enc_set_format(GstVideoEncoder * enc,GstVideoCodecState * state)1091 gst_nv_base_enc_set_format (GstVideoEncoder * enc, GstVideoCodecState * state)
1092 {
1093   GstNvBaseEncClass *nvenc_class = GST_NV_BASE_ENC_GET_CLASS (enc);
1094   GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (enc);
1095   GstVideoInfo *info = &state->info;
1096   GstVideoCodecState *old_state = nvenc->input_state;
1097   NV_ENC_RECONFIGURE_PARAMS reconfigure_params = { 0, };
1098   NV_ENC_INITIALIZE_PARAMS init_params = { 0, };
1099   NV_ENC_INITIALIZE_PARAMS *params;
1100   NV_ENC_PRESET_CONFIG preset_config = { 0, };
1101   NVENCSTATUS nv_ret;
1102 
1103   g_atomic_int_set (&nvenc->reconfig, FALSE);
1104 
1105   if (old_state) {
1106     reconfigure_params.version = NV_ENC_RECONFIGURE_PARAMS_VER;
1107     params = &reconfigure_params.reInitEncodeParams;
1108   } else {
1109     params = &init_params;
1110   }
1111 
1112   params->version = NV_ENC_INITIALIZE_PARAMS_VER;
1113   params->encodeGUID = nvenc_class->codec_id;
1114   params->encodeWidth = GST_VIDEO_INFO_WIDTH (info);
1115   params->encodeHeight = GST_VIDEO_INFO_HEIGHT (info);
1116 
1117   {
1118     guint32 n_presets;
1119     GUID *presets;
1120     guint32 i;
1121 
1122     nv_ret =
1123         NvEncGetEncodePresetCount (nvenc->encoder,
1124         params->encodeGUID, &n_presets);
1125     if (nv_ret != NV_ENC_SUCCESS) {
1126       GST_ELEMENT_ERROR (nvenc, LIBRARY, SETTINGS, (NULL),
1127           ("Failed to get encoder presets"));
1128       return FALSE;
1129     }
1130 
1131     presets = g_new0 (GUID, n_presets);
1132     nv_ret =
1133         NvEncGetEncodePresetGUIDs (nvenc->encoder,
1134         params->encodeGUID, presets, n_presets, &n_presets);
1135     if (nv_ret != NV_ENC_SUCCESS) {
1136       GST_ELEMENT_ERROR (nvenc, LIBRARY, SETTINGS, (NULL),
1137           ("Failed to get encoder presets"));
1138       g_free (presets);
1139       return FALSE;
1140     }
1141 
1142     for (i = 0; i < n_presets; i++) {
1143       if (gst_nvenc_cmp_guid (presets[i], nvenc->selected_preset))
1144         break;
1145     }
1146     g_free (presets);
1147     if (i >= n_presets) {
1148       GST_ELEMENT_ERROR (nvenc, LIBRARY, SETTINGS, (NULL),
1149           ("Selected preset not supported"));
1150       return FALSE;
1151     }
1152 
1153     params->presetGUID = nvenc->selected_preset;
1154   }
1155 
1156   params->enablePTD = 1;
1157   if (!old_state) {
1158     /* this sets the required buffer size and the maximum allowed size on
1159      * subsequent reconfigures */
1160     /* FIXME: propertise this */
1161     params->maxEncodeWidth = GST_VIDEO_INFO_WIDTH (info);
1162     params->maxEncodeHeight = GST_VIDEO_INFO_HEIGHT (info);
1163     gst_nv_base_enc_set_max_encode_size (nvenc, params->maxEncodeWidth,
1164         params->maxEncodeHeight);
1165   } else {
1166     guint max_width, max_height;
1167 
1168     gst_nv_base_enc_get_max_encode_size (nvenc, &max_width, &max_height);
1169 
1170     if (GST_VIDEO_INFO_WIDTH (info) > max_width
1171         || GST_VIDEO_INFO_HEIGHT (info) > max_height) {
1172       GST_ELEMENT_ERROR (nvenc, STREAM, FORMAT, ("%s", "Requested stream "
1173               "size is larger than the maximum configured size"), (NULL));
1174       return FALSE;
1175     }
1176   }
1177 
1178   preset_config.version = NV_ENC_PRESET_CONFIG_VER;
1179   preset_config.presetCfg.version = NV_ENC_CONFIG_VER;
1180 
1181   nv_ret =
1182       NvEncGetEncodePresetConfig (nvenc->encoder,
1183       params->encodeGUID, params->presetGUID, &preset_config);
1184   if (nv_ret != NV_ENC_SUCCESS) {
1185     GST_ELEMENT_ERROR (nvenc, LIBRARY, SETTINGS, (NULL),
1186         ("Failed to get encode preset configuration: %d", nv_ret));
1187     return FALSE;
1188   }
1189 
1190   params->encodeConfig = &preset_config.presetCfg;
1191 
1192   if (GST_VIDEO_INFO_IS_INTERLACED (info)) {
1193     if (GST_VIDEO_INFO_INTERLACE_MODE (info) ==
1194         GST_VIDEO_INTERLACE_MODE_INTERLEAVED
1195         || GST_VIDEO_INFO_INTERLACE_MODE (info) ==
1196         GST_VIDEO_INTERLACE_MODE_MIXED) {
1197       preset_config.presetCfg.frameFieldMode =
1198           NV_ENC_PARAMS_FRAME_FIELD_MODE_FIELD;
1199     }
1200   }
1201 
1202   if (info->fps_d > 0 && info->fps_n > 0) {
1203     params->frameRateNum = info->fps_n;
1204     params->frameRateDen = info->fps_d;
1205   } else {
1206     GST_FIXME_OBJECT (nvenc, "variable framerate");
1207   }
1208 
1209   if (nvenc->rate_control_mode != GST_NV_RC_MODE_DEFAULT) {
1210     params->encodeConfig->rcParams.rateControlMode =
1211         _rc_mode_to_nv (nvenc->rate_control_mode);
1212     if (nvenc->bitrate > 0) {
1213       /* FIXME: this produces larger bitrates?! */
1214       params->encodeConfig->rcParams.averageBitRate = nvenc->bitrate * 1024;
1215       params->encodeConfig->rcParams.maxBitRate = nvenc->bitrate * 1024;
1216     }
1217     if (nvenc->qp_const > 0) {
1218       params->encodeConfig->rcParams.constQP.qpInterB = nvenc->qp_const;
1219       params->encodeConfig->rcParams.constQP.qpInterP = nvenc->qp_const;
1220       params->encodeConfig->rcParams.constQP.qpIntra = nvenc->qp_const;
1221     }
1222     if (nvenc->qp_min >= 0) {
1223       params->encodeConfig->rcParams.enableMinQP = 1;
1224       params->encodeConfig->rcParams.minQP.qpInterB = nvenc->qp_min;
1225       params->encodeConfig->rcParams.minQP.qpInterP = nvenc->qp_min;
1226       params->encodeConfig->rcParams.minQP.qpIntra = nvenc->qp_min;
1227     }
1228     if (nvenc->qp_max >= 0) {
1229       params->encodeConfig->rcParams.enableMaxQP = 1;
1230       params->encodeConfig->rcParams.maxQP.qpInterB = nvenc->qp_max;
1231       params->encodeConfig->rcParams.maxQP.qpInterP = nvenc->qp_max;
1232       params->encodeConfig->rcParams.maxQP.qpIntra = nvenc->qp_max;
1233     }
1234   }
1235 
1236   if (nvenc->gop_size < 0) {
1237     params->encodeConfig->gopLength = NVENC_INFINITE_GOPLENGTH;
1238     params->encodeConfig->frameIntervalP = 1;
1239   } else if (nvenc->gop_size > 0) {
1240     params->encodeConfig->gopLength = nvenc->gop_size;
1241   }
1242 
1243   g_assert (nvenc_class->set_encoder_config);
1244   if (!nvenc_class->set_encoder_config (nvenc, state, params->encodeConfig)) {
1245     GST_ERROR_OBJECT (enc, "Subclass failed to set encoder configuration");
1246     return FALSE;
1247   }
1248 
1249   G_LOCK (initialization_lock);
1250   if (old_state) {
1251     nv_ret = NvEncReconfigureEncoder (nvenc->encoder, &reconfigure_params);
1252   } else {
1253     nv_ret = NvEncInitializeEncoder (nvenc->encoder, params);
1254   }
1255   G_UNLOCK (initialization_lock);
1256 
1257   if (nv_ret != NV_ENC_SUCCESS) {
1258     GST_ELEMENT_ERROR (nvenc, LIBRARY, SETTINGS, (NULL),
1259         ("Failed to %sinit encoder: %d", old_state ? "re" : "", nv_ret));
1260     return FALSE;
1261   }
1262   GST_INFO_OBJECT (nvenc, "configured encoder");
1263 
1264   if (!old_state) {
1265     nvenc->input_info = *info;
1266     nvenc->gl_input = FALSE;
1267   }
1268 
1269   if (nvenc->input_state)
1270     gst_video_codec_state_unref (nvenc->input_state);
1271   nvenc->input_state = gst_video_codec_state_ref (state);
1272   GST_INFO_OBJECT (nvenc, "configured encoder");
1273 
1274   /* now allocate some buffers only on first configuration */
1275   if (!old_state) {
1276 #if HAVE_NVENC_GST_GL
1277     GstCapsFeatures *features;
1278 #endif
1279     guint num_macroblocks, i;
1280     guint input_width, input_height;
1281 
1282     input_width = GST_VIDEO_INFO_WIDTH (info);
1283     input_height = GST_VIDEO_INFO_HEIGHT (info);
1284 
1285     num_macroblocks = (GST_ROUND_UP_16 (input_width) >> 4)
1286         * (GST_ROUND_UP_16 (input_height) >> 4);
1287     nvenc->n_bufs = (num_macroblocks >= 8160) ? 32 : 48;
1288 
1289     /* input buffers */
1290     nvenc->input_bufs = g_new0 (gpointer, nvenc->n_bufs);
1291 
1292 #if HAVE_NVENC_GST_GL
1293     features = gst_caps_get_features (state->caps, 0);
1294     if (gst_caps_features_contains (features,
1295             GST_CAPS_FEATURE_MEMORY_GL_MEMORY)) {
1296       guint pixel_depth = 0;
1297       nvenc->gl_input = TRUE;
1298 
1299       for (i = 0; i < GST_VIDEO_INFO_N_COMPONENTS (info); i++) {
1300         pixel_depth += GST_VIDEO_INFO_COMP_DEPTH (info, i);
1301       }
1302 
1303       cuCtxPushCurrent (nvenc->cuda_ctx);
1304       for (i = 0; i < nvenc->n_bufs; ++i) {
1305         struct gl_input_resource *in_gl_resource =
1306             g_new0 (struct gl_input_resource, 1);
1307         CUresult cu_ret;
1308 
1309         memset (&in_gl_resource->nv_resource, 0,
1310             sizeof (in_gl_resource->nv_resource));
1311         memset (&in_gl_resource->nv_mapped_resource, 0,
1312             sizeof (in_gl_resource->nv_mapped_resource));
1313 
1314         /* scratch buffer for non-contigious planer into a contigious buffer */
1315         cu_ret =
1316             cuMemAllocPitch ((CUdeviceptr *) & in_gl_resource->cuda_pointer,
1317             &in_gl_resource->cuda_stride, input_width,
1318             _get_frame_data_height (info), 16);
1319         if (cu_ret != CUDA_SUCCESS) {
1320           const gchar *err;
1321 
1322           cuGetErrorString (cu_ret, &err);
1323           GST_ERROR_OBJECT (nvenc, "failed to alocate cuda scratch buffer "
1324               "ret %d error :%s", cu_ret, err);
1325           g_assert_not_reached ();
1326         }
1327 
1328         in_gl_resource->nv_resource.version = NV_ENC_REGISTER_RESOURCE_VER;
1329         in_gl_resource->nv_resource.resourceType =
1330             NV_ENC_INPUT_RESOURCE_TYPE_CUDADEVICEPTR;
1331         in_gl_resource->nv_resource.width = input_width;
1332         in_gl_resource->nv_resource.height = input_height;
1333         in_gl_resource->nv_resource.pitch = in_gl_resource->cuda_stride;
1334         in_gl_resource->nv_resource.bufferFormat =
1335             gst_nvenc_get_nv_buffer_format (GST_VIDEO_INFO_FORMAT (info));
1336         in_gl_resource->nv_resource.resourceToRegister =
1337             in_gl_resource->cuda_pointer;
1338 
1339         nv_ret =
1340             NvEncRegisterResource (nvenc->encoder,
1341             &in_gl_resource->nv_resource);
1342         if (nv_ret != NV_ENC_SUCCESS)
1343           GST_ERROR_OBJECT (nvenc, "Failed to register resource %p, ret %d",
1344               in_gl_resource, nv_ret);
1345 
1346         nvenc->input_bufs[i] = in_gl_resource;
1347         g_async_queue_push (nvenc->in_bufs_pool, nvenc->input_bufs[i]);
1348       }
1349 
1350       cuCtxPopCurrent (NULL);
1351     } else
1352 #endif
1353     {
1354       for (i = 0; i < nvenc->n_bufs; ++i) {
1355         NV_ENC_CREATE_INPUT_BUFFER cin_buf = { 0, };
1356 
1357         cin_buf.version = NV_ENC_CREATE_INPUT_BUFFER_VER;
1358 
1359         cin_buf.width = GST_ROUND_UP_32 (input_width);
1360         cin_buf.height = GST_ROUND_UP_32 (input_height);
1361 
1362         cin_buf.memoryHeap = NV_ENC_MEMORY_HEAP_SYSMEM_CACHED;
1363         cin_buf.bufferFmt =
1364             gst_nvenc_get_nv_buffer_format (GST_VIDEO_INFO_FORMAT (info));
1365 
1366         nv_ret = NvEncCreateInputBuffer (nvenc->encoder, &cin_buf);
1367 
1368         if (nv_ret != NV_ENC_SUCCESS) {
1369           GST_WARNING_OBJECT (enc, "Failed to allocate input buffer: %d",
1370               nv_ret);
1371           /* FIXME: clean up */
1372           return FALSE;
1373         }
1374 
1375         nvenc->input_bufs[i] = cin_buf.inputBuffer;
1376 
1377         GST_INFO_OBJECT (nvenc, "allocated  input buffer %2d: %p", i,
1378             nvenc->input_bufs[i]);
1379 
1380         g_async_queue_push (nvenc->in_bufs_pool, nvenc->input_bufs[i]);
1381       }
1382     }
1383 
1384     /* output buffers */
1385     nvenc->output_bufs = g_new0 (NV_ENC_OUTPUT_PTR, nvenc->n_bufs);
1386     for (i = 0; i < nvenc->n_bufs; ++i) {
1387       NV_ENC_CREATE_BITSTREAM_BUFFER cout_buf = { 0, };
1388 
1389       cout_buf.version = NV_ENC_CREATE_BITSTREAM_BUFFER_VER;
1390 
1391       /* 1 MB should be large enough to hold most output frames.
1392        * NVENC will automatically increase this if it's not enough. */
1393       cout_buf.size = 1024 * 1024;
1394       cout_buf.memoryHeap = NV_ENC_MEMORY_HEAP_SYSMEM_CACHED;
1395 
1396       G_LOCK (initialization_lock);
1397       nv_ret = NvEncCreateBitstreamBuffer (nvenc->encoder, &cout_buf);
1398       G_UNLOCK (initialization_lock);
1399 
1400       if (nv_ret != NV_ENC_SUCCESS) {
1401         GST_WARNING_OBJECT (enc, "Failed to allocate input buffer: %d", nv_ret);
1402         /* FIXME: clean up */
1403         return FALSE;
1404       }
1405 
1406       nvenc->output_bufs[i] = cout_buf.bitstreamBuffer;
1407 
1408       GST_INFO_OBJECT (nvenc, "allocated output buffer %2d: %p", i,
1409           nvenc->output_bufs[i]);
1410 
1411       g_async_queue_push (nvenc->bitstream_pool, nvenc->output_bufs[i]);
1412     }
1413 
1414 #if 0
1415     /* Get SPS/PPS */
1416     {
1417       NV_ENC_SEQUENCE_PARAM_PAYLOAD seq_param = { 0 };
1418       uint32_t seq_size = 0;
1419 
1420       seq_param.version = NV_ENC_SEQUENCE_PARAM_PAYLOAD_VER;
1421       seq_param.spsppsBuffer = g_alloca (1024);
1422       seq_param.inBufferSize = 1024;
1423       seq_param.outSPSPPSPayloadSize = &seq_size;
1424 
1425       nv_ret = NvEncGetSequenceParams (nvenc->encoder, &seq_param);
1426       if (nv_ret != NV_ENC_SUCCESS) {
1427         GST_WARNING_OBJECT (enc, "Failed to retrieve SPS/PPS: %d", nv_ret);
1428         return FALSE;
1429       }
1430 
1431       /* FIXME: use SPS/PPS */
1432       GST_MEMDUMP_OBJECT (enc, "SPS/PPS", seq_param.spsppsBuffer, seq_size);
1433     }
1434 #endif
1435   }
1436 
1437   g_assert (nvenc_class->set_src_caps);
1438   if (!nvenc_class->set_src_caps (nvenc, state)) {
1439     GST_ERROR_OBJECT (nvenc, "Subclass failed to set output caps");
1440     /* FIXME: clean up */
1441     return FALSE;
1442   }
1443 
1444   return TRUE;
1445 }
1446 
1447 static inline guint
_plane_get_n_components(GstVideoInfo * info,guint plane)1448 _plane_get_n_components (GstVideoInfo * info, guint plane)
1449 {
1450   switch (GST_VIDEO_INFO_FORMAT (info)) {
1451     case GST_VIDEO_FORMAT_RGBx:
1452     case GST_VIDEO_FORMAT_BGRx:
1453     case GST_VIDEO_FORMAT_xRGB:
1454     case GST_VIDEO_FORMAT_xBGR:
1455     case GST_VIDEO_FORMAT_RGBA:
1456     case GST_VIDEO_FORMAT_BGRA:
1457     case GST_VIDEO_FORMAT_ARGB:
1458     case GST_VIDEO_FORMAT_ABGR:
1459     case GST_VIDEO_FORMAT_AYUV:
1460       return 4;
1461     case GST_VIDEO_FORMAT_RGB:
1462     case GST_VIDEO_FORMAT_BGR:
1463     case GST_VIDEO_FORMAT_RGB16:
1464     case GST_VIDEO_FORMAT_BGR16:
1465       return 3;
1466     case GST_VIDEO_FORMAT_GRAY16_BE:
1467     case GST_VIDEO_FORMAT_GRAY16_LE:
1468     case GST_VIDEO_FORMAT_YUY2:
1469     case GST_VIDEO_FORMAT_UYVY:
1470       return 2;
1471     case GST_VIDEO_FORMAT_NV12:
1472     case GST_VIDEO_FORMAT_NV21:
1473       return plane == 0 ? 1 : 2;
1474     case GST_VIDEO_FORMAT_GRAY8:
1475     case GST_VIDEO_FORMAT_Y444:
1476     case GST_VIDEO_FORMAT_Y42B:
1477     case GST_VIDEO_FORMAT_Y41B:
1478     case GST_VIDEO_FORMAT_I420:
1479     case GST_VIDEO_FORMAT_YV12:
1480       return 1;
1481     default:
1482       g_assert_not_reached ();
1483       return 1;
1484   }
1485 }
1486 
1487 #if HAVE_NVENC_GST_GL
1488 struct map_gl_input
1489 {
1490   GstNvBaseEnc *nvenc;
1491   GstVideoCodecFrame *frame;
1492   GstVideoInfo *info;
1493   struct gl_input_resource *in_gl_resource;
1494 };
1495 
1496 static void
_map_gl_input_buffer(GstGLContext * context,struct map_gl_input * data)1497 _map_gl_input_buffer (GstGLContext * context, struct map_gl_input *data)
1498 {
1499   cudaError_t cuda_ret;
1500   guint8 *data_pointer;
1501   guint i;
1502   CUDA_MEMCPY2D param;
1503 
1504   cuCtxPushCurrent (data->nvenc->cuda_ctx);
1505   data_pointer = data->in_gl_resource->cuda_pointer;
1506   for (i = 0; i < GST_VIDEO_INFO_N_PLANES (data->info); i++) {
1507     guint plane_n_components;
1508     GstGLBuffer *gl_buf_obj;
1509     GstGLMemoryPBO *gl_mem;
1510     guint src_stride, dest_stride;
1511 
1512     gl_mem =
1513         (GstGLMemoryPBO *) gst_buffer_peek_memory (data->frame->input_buffer,
1514         i);
1515     g_return_if_fail (gst_is_gl_memory_pbo ((GstMemory *) gl_mem));
1516     data->in_gl_resource->gl_mem[i] = GST_GL_MEMORY_CAST (gl_mem);
1517     plane_n_components = _plane_get_n_components (data->info, i);
1518 
1519     gl_buf_obj = (GstGLBuffer *) gl_mem->pbo;
1520     g_return_if_fail (gl_buf_obj != NULL);
1521 
1522     /* get the texture into the PBO */
1523     gst_gl_memory_pbo_upload_transfer (gl_mem);
1524     gst_gl_memory_pbo_download_transfer (gl_mem);
1525 
1526     GST_LOG_OBJECT (data->nvenc, "attempting to copy texture %u into cuda",
1527         gl_mem->mem.tex_id);
1528 
1529     cuda_ret =
1530         cuGraphicsGLRegisterBuffer (&data->in_gl_resource->cuda_texture,
1531         gl_buf_obj->id, cudaGraphicsRegisterFlagsReadOnly);
1532     if (cuda_ret != cudaSuccess) {
1533       GST_ERROR_OBJECT (data->nvenc, "failed to register GL texture %u to cuda "
1534           "ret :%d", gl_mem->mem.tex_id, cuda_ret);
1535       g_assert_not_reached ();
1536     }
1537 
1538     cuda_ret =
1539         cuGraphicsMapResources (1, &data->in_gl_resource->cuda_texture, 0);
1540     if (cuda_ret != cudaSuccess) {
1541       GST_ERROR_OBJECT (data->nvenc, "failed to map GL texture %u into cuda "
1542           "ret :%d", gl_mem->mem.tex_id, cuda_ret);
1543       g_assert_not_reached ();
1544     }
1545 
1546     cuda_ret =
1547         cuGraphicsResourceGetMappedPointer (&data->in_gl_resource->
1548         cuda_plane_pointers[i], &data->in_gl_resource->cuda_num_bytes,
1549         data->in_gl_resource->cuda_texture);
1550     if (cuda_ret != cudaSuccess) {
1551       GST_ERROR_OBJECT (data->nvenc, "failed to get mapped pointer of map GL "
1552           "texture %u in cuda ret :%d", gl_mem->mem.tex_id, cuda_ret);
1553       g_assert_not_reached ();
1554     }
1555 
1556     src_stride = GST_VIDEO_INFO_PLANE_STRIDE (data->info, i);
1557     dest_stride = data->in_gl_resource->cuda_stride;
1558 
1559     /* copy into scratch buffer */
1560     param.srcXInBytes = 0;
1561     param.srcY = 0;
1562     param.srcMemoryType = CU_MEMORYTYPE_DEVICE;
1563     param.srcDevice = data->in_gl_resource->cuda_plane_pointers[i];
1564     param.srcPitch = src_stride;
1565 
1566     param.dstXInBytes = 0;
1567     param.dstY = 0;
1568     param.dstMemoryType = CU_MEMORYTYPE_DEVICE;
1569     param.dstDevice = (CUdeviceptr) data_pointer;
1570     param.dstPitch = dest_stride;
1571     param.WidthInBytes = _get_plane_width (data->info, i) * plane_n_components;
1572     param.Height = _get_plane_height (data->info, i);
1573 
1574     cuda_ret = cuMemcpy2D (&param);
1575     if (cuda_ret != cudaSuccess) {
1576       GST_ERROR_OBJECT (data->nvenc, "failed to copy GL texture %u into cuda "
1577           "ret :%d", gl_mem->mem.tex_id, cuda_ret);
1578       g_assert_not_reached ();
1579     }
1580 
1581     cuda_ret =
1582         cuGraphicsUnmapResources (1, &data->in_gl_resource->cuda_texture, 0);
1583     if (cuda_ret != cudaSuccess) {
1584       GST_ERROR_OBJECT (data->nvenc, "failed to unmap GL texture %u from cuda "
1585           "ret :%d", gl_mem->mem.tex_id, cuda_ret);
1586       g_assert_not_reached ();
1587     }
1588 
1589     cuda_ret =
1590         cuGraphicsUnregisterResource (data->in_gl_resource->cuda_texture);
1591     if (cuda_ret != cudaSuccess) {
1592       GST_ERROR_OBJECT (data->nvenc, "failed to unregister GL texture %u from "
1593           "cuda ret :%d", gl_mem->mem.tex_id, cuda_ret);
1594       g_assert_not_reached ();
1595     }
1596 
1597     data_pointer =
1598         data_pointer +
1599         data->in_gl_resource->cuda_stride *
1600         _get_plane_height (&data->nvenc->input_info, i);
1601   }
1602   cuCtxPopCurrent (NULL);
1603 }
1604 #endif
1605 
1606 static GstFlowReturn
_acquire_input_buffer(GstNvBaseEnc * nvenc,gpointer * input)1607 _acquire_input_buffer (GstNvBaseEnc * nvenc, gpointer * input)
1608 {
1609   g_assert (input);
1610 
1611   GST_LOG_OBJECT (nvenc, "acquiring input buffer..");
1612   GST_VIDEO_ENCODER_STREAM_UNLOCK (nvenc);
1613   *input = g_async_queue_pop (nvenc->in_bufs_pool);
1614   GST_VIDEO_ENCODER_STREAM_LOCK (nvenc);
1615 
1616   return GST_FLOW_OK;
1617 }
1618 
1619 static GstFlowReturn
_submit_input_buffer(GstNvBaseEnc * nvenc,GstVideoCodecFrame * frame,GstVideoFrame * vframe,void * inputBuffer,void * inputBufferPtr,NV_ENC_BUFFER_FORMAT bufferFormat,void * outputBufferPtr)1620 _submit_input_buffer (GstNvBaseEnc * nvenc, GstVideoCodecFrame * frame,
1621     GstVideoFrame * vframe, void *inputBuffer, void *inputBufferPtr,
1622     NV_ENC_BUFFER_FORMAT bufferFormat, void *outputBufferPtr)
1623 {
1624   GstNvBaseEncClass *nvenc_class = GST_NV_BASE_ENC_GET_CLASS (nvenc);
1625   NV_ENC_PIC_PARAMS pic_params = { 0, };
1626   NVENCSTATUS nv_ret;
1627 
1628   GST_LOG_OBJECT (nvenc, "%u: input buffer %p, output buffer %p, "
1629       "pts %" GST_TIME_FORMAT, frame->system_frame_number, inputBuffer,
1630       outputBufferPtr, GST_TIME_ARGS (frame->pts));
1631 
1632   pic_params.version = NV_ENC_PIC_PARAMS_VER;
1633   pic_params.inputBuffer = inputBufferPtr;
1634   pic_params.bufferFmt = bufferFormat;
1635 
1636   pic_params.inputWidth = GST_VIDEO_FRAME_WIDTH (vframe);
1637   pic_params.inputHeight = GST_VIDEO_FRAME_HEIGHT (vframe);
1638   pic_params.outputBitstream = outputBufferPtr;
1639   pic_params.completionEvent = NULL;
1640   if (GST_VIDEO_FRAME_IS_INTERLACED (vframe)) {
1641     if (GST_VIDEO_FRAME_IS_TFF (vframe))
1642       pic_params.pictureStruct = NV_ENC_PIC_STRUCT_FIELD_TOP_BOTTOM;
1643     else
1644       pic_params.pictureStruct = NV_ENC_PIC_STRUCT_FIELD_BOTTOM_TOP;
1645   } else {
1646     pic_params.pictureStruct = NV_ENC_PIC_STRUCT_FRAME;
1647   }
1648   pic_params.inputTimeStamp = frame->pts;
1649   pic_params.inputDuration =
1650       GST_CLOCK_TIME_IS_VALID (frame->duration) ? frame->duration : 0;
1651   pic_params.frameIdx = frame->system_frame_number;
1652 
1653   if (GST_VIDEO_CODEC_FRAME_IS_FORCE_KEYFRAME (frame))
1654     pic_params.encodePicFlags = NV_ENC_PIC_FLAG_FORCEIDR;
1655   else
1656     pic_params.encodePicFlags = 0;
1657 
1658   if (nvenc_class->set_pic_params
1659       && !nvenc_class->set_pic_params (nvenc, frame, &pic_params)) {
1660     GST_ERROR_OBJECT (nvenc, "Subclass failed to submit buffer");
1661     return GST_FLOW_ERROR;
1662   }
1663 
1664   nv_ret = NvEncEncodePicture (nvenc->encoder, &pic_params);
1665   if (nv_ret == NV_ENC_SUCCESS) {
1666     GST_LOG_OBJECT (nvenc, "Encoded picture");
1667   } else if (nv_ret == NV_ENC_ERR_NEED_MORE_INPUT) {
1668     /* FIXME: we should probably queue pending output buffers here and only
1669      * submit them to the async queue once we got sucess back */
1670     GST_DEBUG_OBJECT (nvenc, "Encoded picture (encoder needs more input)");
1671   } else {
1672     GST_ERROR_OBJECT (nvenc, "Failed to encode picture: %d", nv_ret);
1673     GST_DEBUG_OBJECT (nvenc, "re-enqueueing input buffer %p", inputBuffer);
1674     g_async_queue_push (nvenc->in_bufs_pool, inputBuffer);
1675     GST_DEBUG_OBJECT (nvenc, "re-enqueueing output buffer %p", outputBufferPtr);
1676     g_async_queue_push (nvenc->bitstream_pool, outputBufferPtr);
1677 
1678     return GST_FLOW_ERROR;
1679   }
1680 
1681   g_async_queue_push (nvenc->bitstream_queue, outputBufferPtr);
1682 
1683   return GST_FLOW_OK;
1684 }
1685 
1686 static GstFlowReturn
gst_nv_base_enc_handle_frame(GstVideoEncoder * enc,GstVideoCodecFrame * frame)1687 gst_nv_base_enc_handle_frame (GstVideoEncoder * enc, GstVideoCodecFrame * frame)
1688 {
1689   gpointer input_buffer = NULL;
1690   GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (enc);
1691   NV_ENC_OUTPUT_PTR out_buf;
1692   NVENCSTATUS nv_ret;
1693   GstVideoFrame vframe;
1694   GstVideoInfo *info = &nvenc->input_state->info;
1695   GstFlowReturn flow = GST_FLOW_OK;
1696   GstMapFlags in_map_flags = GST_MAP_READ;
1697   struct frame_state *state = NULL;
1698   guint frame_n = 0;
1699 
1700   g_assert (nvenc->encoder != NULL);
1701 
1702   if (g_atomic_int_compare_and_exchange (&nvenc->reconfig, TRUE, FALSE)) {
1703     if (!gst_nv_base_enc_set_format (enc, nvenc->input_state))
1704       return GST_FLOW_ERROR;
1705   }
1706 #if HAVE_NVENC_GST_GL
1707   if (nvenc->gl_input)
1708     in_map_flags |= GST_MAP_GL;
1709 #endif
1710 
1711   if (!gst_video_frame_map (&vframe, info, frame->input_buffer, in_map_flags))
1712     return GST_FLOW_ERROR;
1713 
1714   /* make sure our thread that waits for output to be ready is started */
1715   if (nvenc->bitstream_thread == NULL) {
1716     if (!gst_nv_base_enc_start_bitstream_thread (nvenc))
1717       goto error;
1718   }
1719 
1720   flow = _acquire_input_buffer (nvenc, &input_buffer);
1721   if (flow != GST_FLOW_OK)
1722     goto out;
1723   if (input_buffer == NULL)
1724     goto error;
1725 
1726   state = frame->user_data;
1727   if (!state)
1728     state = g_new0 (struct frame_state, 1);
1729   state->n_buffers = 1;
1730 
1731 #if HAVE_NVENC_GST_GL
1732   if (nvenc->gl_input) {
1733     struct gl_input_resource *in_gl_resource = input_buffer;
1734     struct map_gl_input data;
1735 
1736     GST_LOG_OBJECT (enc, "got input buffer %p", in_gl_resource);
1737 
1738     in_gl_resource->gl_mem[0] =
1739         (GstGLMemory *) gst_buffer_peek_memory (frame->input_buffer, 0);
1740     g_assert (gst_is_gl_memory ((GstMemory *) in_gl_resource->gl_mem[0]));
1741 
1742     data.nvenc = nvenc;
1743     data.frame = frame;
1744     data.info = &vframe.info;
1745     data.in_gl_resource = in_gl_resource;
1746 
1747     gst_gl_context_thread_add (in_gl_resource->gl_mem[0]->mem.context,
1748         (GstGLContextThreadFunc) _map_gl_input_buffer, &data);
1749 
1750     in_gl_resource->nv_mapped_resource.version = NV_ENC_MAP_INPUT_RESOURCE_VER;
1751     in_gl_resource->nv_mapped_resource.registeredResource =
1752         in_gl_resource->nv_resource.registeredResource;
1753 
1754     nv_ret =
1755         NvEncMapInputResource (nvenc->encoder,
1756         &in_gl_resource->nv_mapped_resource);
1757     if (nv_ret != NV_ENC_SUCCESS) {
1758       GST_ERROR_OBJECT (nvenc, "Failed to map input resource %p, ret %d",
1759           in_gl_resource, nv_ret);
1760       goto error;
1761     }
1762 
1763     in_gl_resource->mapped = TRUE;
1764 
1765     out_buf = g_async_queue_try_pop (nvenc->bitstream_pool);
1766     if (out_buf == NULL) {
1767       GST_DEBUG_OBJECT (nvenc, "wait for output buf to become available again");
1768       out_buf = g_async_queue_pop (nvenc->bitstream_pool);
1769     }
1770 
1771     state->in_bufs[frame_n] = in_gl_resource;
1772     state->out_bufs[frame_n++] = out_buf;
1773 
1774     frame->user_data = state;
1775     frame->user_data_destroy_notify = (GDestroyNotify) g_free;
1776 
1777     flow =
1778         _submit_input_buffer (nvenc, frame, &vframe, in_gl_resource,
1779         in_gl_resource->nv_mapped_resource.mappedResource,
1780         in_gl_resource->nv_mapped_resource.mappedBufferFmt, out_buf);
1781 
1782     /* encoder will keep frame in list internally, we'll look it up again later
1783      * in the thread where we get the output buffers and finish it there */
1784     gst_video_codec_frame_unref (frame);
1785     frame = NULL;
1786   }
1787 #endif
1788 
1789   if (!nvenc->gl_input) {
1790     NV_ENC_LOCK_INPUT_BUFFER in_buf_lock = { 0, };
1791     NV_ENC_INPUT_PTR in_buf = input_buffer;
1792     guint8 *src, *dest;
1793     guint src_stride, dest_stride;
1794     guint height, width;
1795     guint y;
1796 
1797     GST_LOG_OBJECT (enc, "got input buffer %p", in_buf);
1798 
1799     in_buf_lock.version = NV_ENC_LOCK_INPUT_BUFFER_VER;
1800     in_buf_lock.inputBuffer = in_buf;
1801 
1802     nv_ret = NvEncLockInputBuffer (nvenc->encoder, &in_buf_lock);
1803     if (nv_ret != NV_ENC_SUCCESS) {
1804       GST_ERROR_OBJECT (nvenc, "Failed to lock input buffer: %d", nv_ret);
1805       /* FIXME: post proper error message */
1806       goto error;
1807     }
1808     GST_LOG_OBJECT (nvenc, "Locked input buffer %p", in_buf);
1809 
1810     width = GST_VIDEO_FRAME_WIDTH (&vframe);
1811     height = GST_VIDEO_FRAME_HEIGHT (&vframe);
1812 
1813     /* copy Y plane */
1814     src = GST_VIDEO_FRAME_PLANE_DATA (&vframe, 0);
1815     src_stride = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 0);
1816     dest = in_buf_lock.bufferDataPtr;
1817     dest_stride = in_buf_lock.pitch;
1818     for (y = 0; y < height; ++y) {
1819       memcpy (dest, src, width);
1820       dest += dest_stride;
1821       src += src_stride;
1822     }
1823 
1824     if (GST_VIDEO_FRAME_FORMAT (&vframe) == GST_VIDEO_FORMAT_NV12) {
1825       /* copy UV plane */
1826       src = GST_VIDEO_FRAME_PLANE_DATA (&vframe, 1);
1827       src_stride = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 1);
1828       dest =
1829           (guint8 *) in_buf_lock.bufferDataPtr +
1830           GST_ROUND_UP_32 (height) * in_buf_lock.pitch;
1831       dest_stride = in_buf_lock.pitch;
1832       for (y = 0; y < GST_ROUND_UP_2 (height) / 2; ++y) {
1833         memcpy (dest, src, width);
1834         dest += dest_stride;
1835         src += src_stride;
1836       }
1837     } else if (GST_VIDEO_FRAME_FORMAT (&vframe) == GST_VIDEO_FORMAT_I420) {
1838       guint8 *dest_u, *dest_v;
1839 
1840       dest_u = (guint8 *) in_buf_lock.bufferDataPtr +
1841           GST_ROUND_UP_32 (height) * in_buf_lock.pitch;
1842       dest_v = dest_u + ((GST_ROUND_UP_32 (height) / 2) *
1843           (in_buf_lock.pitch / 2));
1844       dest_stride = in_buf_lock.pitch / 2;
1845 
1846       /* copy U plane */
1847       src = GST_VIDEO_FRAME_PLANE_DATA (&vframe, 1);
1848       src_stride = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 1);
1849       dest = dest_u;
1850       for (y = 0; y < GST_ROUND_UP_2 (height) / 2; ++y) {
1851         memcpy (dest, src, width / 2);
1852         dest += dest_stride;
1853         src += src_stride;
1854       }
1855 
1856       /* copy V plane */
1857       src = GST_VIDEO_FRAME_PLANE_DATA (&vframe, 2);
1858       src_stride = GST_VIDEO_FRAME_PLANE_STRIDE (&vframe, 2);
1859       dest = dest_v;
1860       for (y = 0; y < GST_ROUND_UP_2 (height) / 2; ++y) {
1861         memcpy (dest, src, width / 2);
1862         dest += dest_stride;
1863         src += src_stride;
1864       }
1865     } else {
1866       // FIXME: this only works for NV12 and I420
1867       g_assert_not_reached ();
1868     }
1869 
1870     nv_ret = NvEncUnlockInputBuffer (nvenc->encoder, in_buf);
1871     if (nv_ret != NV_ENC_SUCCESS) {
1872       GST_ERROR_OBJECT (nvenc, "Failed to unlock input buffer: %d", nv_ret);
1873       goto error;
1874     }
1875 
1876     out_buf = g_async_queue_try_pop (nvenc->bitstream_pool);
1877     if (out_buf == NULL) {
1878       GST_DEBUG_OBJECT (nvenc, "wait for output buf to become available again");
1879       out_buf = g_async_queue_pop (nvenc->bitstream_pool);
1880     }
1881 
1882     state->in_bufs[frame_n] = in_buf;
1883     state->out_bufs[frame_n++] = out_buf;
1884     frame->user_data = state;
1885     frame->user_data_destroy_notify = (GDestroyNotify) g_free;
1886 
1887     flow =
1888         _submit_input_buffer (nvenc, frame, &vframe, in_buf, in_buf,
1889         gst_nvenc_get_nv_buffer_format (GST_VIDEO_INFO_FORMAT (info)), out_buf);
1890 
1891     /* encoder will keep frame in list internally, we'll look it up again later
1892      * in the thread where we get the output buffers and finish it there */
1893     gst_video_codec_frame_unref (frame);
1894     frame = NULL;
1895   }
1896 
1897   if (flow != GST_FLOW_OK)
1898     goto out;
1899 
1900   flow = g_atomic_int_get (&nvenc->last_flow);
1901 
1902 out:
1903 
1904   gst_video_frame_unmap (&vframe);
1905 
1906   return flow;
1907 
1908 error:
1909   flow = GST_FLOW_ERROR;
1910   if (state)
1911     g_free (state);
1912   if (input_buffer)
1913     g_free (input_buffer);
1914   goto out;
1915 }
1916 
1917 static gboolean
gst_nv_base_enc_drain_encoder(GstNvBaseEnc * nvenc)1918 gst_nv_base_enc_drain_encoder (GstNvBaseEnc * nvenc)
1919 {
1920   NV_ENC_PIC_PARAMS pic_params = { 0, };
1921   NVENCSTATUS nv_ret;
1922 
1923   GST_INFO_OBJECT (nvenc, "draining encoder");
1924 
1925   if (nvenc->input_state == NULL) {
1926     GST_DEBUG_OBJECT (nvenc, "no input state, nothing to do");
1927     return TRUE;
1928   }
1929 
1930   pic_params.version = NV_ENC_PIC_PARAMS_VER;
1931   pic_params.encodePicFlags = NV_ENC_PIC_FLAG_EOS;
1932 
1933   nv_ret = NvEncEncodePicture (nvenc->encoder, &pic_params);
1934   if (nv_ret != NV_ENC_SUCCESS) {
1935     GST_LOG_OBJECT (nvenc, "Failed to drain encoder, ret %d", nv_ret);
1936     return FALSE;
1937   }
1938 
1939   return TRUE;
1940 }
1941 
1942 static GstFlowReturn
gst_nv_base_enc_finish(GstVideoEncoder * enc)1943 gst_nv_base_enc_finish (GstVideoEncoder * enc)
1944 {
1945   GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (enc);
1946 
1947   if (!gst_nv_base_enc_drain_encoder (nvenc))
1948     return GST_FLOW_ERROR;
1949 
1950   gst_nv_base_enc_stop_bitstream_thread (nvenc, FALSE);
1951 
1952   return GST_FLOW_OK;
1953 }
1954 
1955 #if 0
1956 static gboolean
1957 gst_nv_base_enc_flush (GstVideoEncoder * enc)
1958 {
1959   GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (enc);
1960   GST_INFO_OBJECT (nvenc, "done flushing encoder");
1961   return TRUE;
1962 }
1963 #endif
1964 
1965 static void
gst_nv_base_enc_schedule_reconfig(GstNvBaseEnc * nvenc)1966 gst_nv_base_enc_schedule_reconfig (GstNvBaseEnc * nvenc)
1967 {
1968   g_atomic_int_set (&nvenc->reconfig, TRUE);
1969 }
1970 
1971 static void
gst_nv_base_enc_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1972 gst_nv_base_enc_set_property (GObject * object, guint prop_id,
1973     const GValue * value, GParamSpec * pspec)
1974 {
1975   GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (object);
1976 
1977   switch (prop_id) {
1978     case PROP_DEVICE_ID:
1979       nvenc->cuda_device_id = g_value_get_uint (value);
1980       break;
1981     case PROP_PRESET:
1982       nvenc->preset_enum = g_value_get_enum (value);
1983       nvenc->selected_preset = _nv_preset_to_guid (nvenc->preset_enum);
1984       gst_nv_base_enc_schedule_reconfig (nvenc);
1985       break;
1986     case PROP_RC_MODE:
1987       nvenc->rate_control_mode = g_value_get_enum (value);
1988       gst_nv_base_enc_schedule_reconfig (nvenc);
1989       break;
1990     case PROP_QP_MIN:
1991       nvenc->qp_min = g_value_get_int (value);
1992       gst_nv_base_enc_schedule_reconfig (nvenc);
1993       break;
1994     case PROP_QP_MAX:
1995       nvenc->qp_max = g_value_get_int (value);
1996       gst_nv_base_enc_schedule_reconfig (nvenc);
1997       break;
1998     case PROP_QP_CONST:
1999       nvenc->qp_const = g_value_get_int (value);
2000       gst_nv_base_enc_schedule_reconfig (nvenc);
2001       break;
2002     case PROP_BITRATE:
2003       nvenc->bitrate = g_value_get_uint (value);
2004       gst_nv_base_enc_schedule_reconfig (nvenc);
2005       break;
2006     case PROP_GOP_SIZE:
2007       nvenc->gop_size = g_value_get_int (value);
2008       gst_nv_base_enc_schedule_reconfig (nvenc);
2009       break;
2010     default:
2011       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2012       break;
2013   }
2014 }
2015 
2016 static void
gst_nv_base_enc_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)2017 gst_nv_base_enc_get_property (GObject * object, guint prop_id, GValue * value,
2018     GParamSpec * pspec)
2019 {
2020   GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (object);
2021 
2022   switch (prop_id) {
2023     case PROP_DEVICE_ID:
2024       g_value_set_uint (value, nvenc->cuda_device_id);
2025       break;
2026     case PROP_PRESET:
2027       g_value_set_enum (value, nvenc->preset_enum);
2028       break;
2029     case PROP_RC_MODE:
2030       g_value_set_enum (value, nvenc->rate_control_mode);
2031       break;
2032     case PROP_QP_MIN:
2033       g_value_set_int (value, nvenc->qp_min);
2034       break;
2035     case PROP_QP_MAX:
2036       g_value_set_int (value, nvenc->qp_max);
2037       break;
2038     case PROP_QP_CONST:
2039       g_value_set_int (value, nvenc->qp_const);
2040       break;
2041     case PROP_BITRATE:
2042       g_value_set_uint (value, nvenc->bitrate);
2043       break;
2044     case PROP_GOP_SIZE:
2045       g_value_set_int (value, nvenc->gop_size);
2046       break;
2047     default:
2048       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2049       break;
2050   }
2051 }
2052