nvcodec: Fix various typos
[platform/upstream/gstreamer.git] / sys / nvcodec / gstnvbaseenc.c
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 #include "gstcudautils.h"
26 #include "gstcudabufferpool.h"
27
28 #include <gst/pbutils/codec-utils.h>
29
30 #include <string.h>
31
32 GST_DEBUG_CATEGORY_EXTERN (gst_nvenc_debug);
33 #define GST_CAT_DEFAULT gst_nvenc_debug
34
35 #if HAVE_NVCODEC_GST_GL
36 #include <gst/gl/gl.h>
37 #endif
38
39 /* This currently supports both 5.x and 6.x versions of the NvEncodeAPI.h
40  * header which are mostly API compatible. */
41
42 #define SUPPORTED_GL_APIS GST_GL_API_OPENGL3
43
44 /* magic pointer value we can put in the async queue to signal shut down */
45 #define SHUTDOWN_COOKIE ((gpointer)GINT_TO_POINTER (1))
46
47 #define parent_class gst_nv_base_enc_parent_class
48 G_DEFINE_ABSTRACT_TYPE (GstNvBaseEnc, gst_nv_base_enc, GST_TYPE_VIDEO_ENCODER);
49
50 #define GST_TYPE_NV_PRESET (gst_nv_preset_get_type())
51 static GType
52 gst_nv_preset_get_type (void)
53 {
54   static GType nv_preset_type = 0;
55
56   static const GEnumValue presets[] = {
57     {GST_NV_PRESET_DEFAULT, "Default", "default"},
58     {GST_NV_PRESET_HP, "High Performance", "hp"},
59     {GST_NV_PRESET_HQ, "High Quality", "hq"},
60 /*    {GST_NV_PRESET_BD, "BD", "bd"}, */
61     {GST_NV_PRESET_LOW_LATENCY_DEFAULT, "Low Latency", "low-latency"},
62     {GST_NV_PRESET_LOW_LATENCY_HQ, "Low Latency, High Quality",
63         "low-latency-hq"},
64     {GST_NV_PRESET_LOW_LATENCY_HP, "Low Latency, High Performance",
65         "low-latency-hp"},
66     {GST_NV_PRESET_LOSSLESS_DEFAULT, "Lossless", "lossless"},
67     {GST_NV_PRESET_LOSSLESS_HP, "Lossless, High Performance", "lossless-hp"},
68     {0, NULL, NULL},
69   };
70
71   if (!nv_preset_type) {
72     nv_preset_type = g_enum_register_static ("GstNvPreset", presets);
73   }
74   return nv_preset_type;
75 }
76
77 static GUID
78 _nv_preset_to_guid (GstNvPreset preset)
79 {
80   GUID null = { 0, };
81
82   switch (preset) {
83 #define CASE(gst,nv) case G_PASTE(GST_NV_PRESET_,gst): return G_PASTE(G_PASTE(NV_ENC_PRESET_,nv),_GUID)
84       CASE (DEFAULT, DEFAULT);
85       CASE (HP, HP);
86       CASE (HQ, HQ);
87 /*    CASE (BD, BD);*/
88       CASE (LOW_LATENCY_DEFAULT, LOW_LATENCY_DEFAULT);
89       CASE (LOW_LATENCY_HQ, LOW_LATENCY_HQ);
90       CASE (LOW_LATENCY_HP, LOW_LATENCY_HQ);
91       CASE (LOSSLESS_DEFAULT, LOSSLESS_DEFAULT);
92       CASE (LOSSLESS_HP, LOSSLESS_HP);
93 #undef CASE
94     default:
95       return null;
96   }
97 }
98
99 #define GST_TYPE_NV_RC_MODE (gst_nv_rc_mode_get_type())
100 static GType
101 gst_nv_rc_mode_get_type (void)
102 {
103   static GType nv_rc_mode_type = 0;
104
105   static const GEnumValue modes[] = {
106     {GST_NV_RC_MODE_DEFAULT, "Default", "default"},
107     {GST_NV_RC_MODE_CONSTQP, "Constant Quantization", "constqp"},
108     {GST_NV_RC_MODE_CBR, "Constant Bit Rate", "cbr"},
109     {GST_NV_RC_MODE_VBR, "Variable Bit Rate", "vbr"},
110     {GST_NV_RC_MODE_VBR_MINQP,
111         "Variable Bit Rate "
112           "(with minimum quantization parameter, DEPRECATED)", "vbr-minqp"},
113     {GST_NV_RC_MODE_CBR_LOWDELAY_HQ,
114         "Low-Delay CBR, High Quality", "cbr-ld-hq"},
115     {GST_NV_RC_MODE_CBR_HQ, "CBR, High Quality (slower)", "cbr-hq"},
116     {GST_NV_RC_MODE_VBR_HQ, "VBR, High Quality (slower)", "vbr-hq"},
117     {0, NULL, NULL},
118   };
119
120   if (!nv_rc_mode_type) {
121     nv_rc_mode_type = g_enum_register_static ("GstNvRCMode", modes);
122   }
123   return nv_rc_mode_type;
124 }
125
126 static NV_ENC_PARAMS_RC_MODE
127 _rc_mode_to_nv (GstNvRCMode mode)
128 {
129   switch (mode) {
130     case GST_NV_RC_MODE_DEFAULT:
131       return NV_ENC_PARAMS_RC_VBR;
132 #define CASE(gst,nv) case G_PASTE(GST_NV_RC_MODE_,gst): return G_PASTE(NV_ENC_PARAMS_RC_,nv)
133       CASE (CONSTQP, CONSTQP);
134       CASE (CBR, CBR);
135       CASE (VBR, VBR);
136       CASE (VBR_MINQP, VBR_MINQP);
137       CASE (CBR_LOWDELAY_HQ, CBR_LOWDELAY_HQ);
138       CASE (CBR_HQ, CBR_HQ);
139       CASE (VBR_HQ, VBR_HQ);
140 #undef CASE
141     default:
142       return NV_ENC_PARAMS_RC_VBR;
143   }
144 }
145
146 enum
147 {
148   PROP_0,
149   PROP_DEVICE_ID,
150   PROP_PRESET,
151   PROP_BITRATE,
152   PROP_RC_MODE,
153   PROP_QP_MIN,
154   PROP_QP_MAX,
155   PROP_QP_CONST,
156   PROP_GOP_SIZE,
157   PROP_MAX_BITRATE,
158   PROP_SPATIAL_AQ,
159   PROP_AQ_STRENGTH,
160   PROP_NON_REF_P,
161   PROP_ZEROLATENCY,
162   PROP_STRICT_GOP,
163   PROP_CONST_QUALITY,
164   PROP_I_ADAPT,
165   PROP_QP_MIN_I,
166   PROP_QP_MIN_P,
167   PROP_QP_MIN_B,
168   PROP_QP_MAX_I,
169   PROP_QP_MAX_P,
170   PROP_QP_MAX_B,
171   PROP_QP_CONST_I,
172   PROP_QP_CONST_P,
173   PROP_QP_CONST_B,
174 };
175
176 #define DEFAULT_PRESET GST_NV_PRESET_DEFAULT
177 #define DEFAULT_BITRATE 0
178 #define DEFAULT_RC_MODE GST_NV_RC_MODE_DEFAULT
179 #define DEFAULT_QP_MIN -1
180 #define DEFAULT_QP_MAX -1
181 #define DEFAULT_QP_CONST -1
182 #define DEFAULT_GOP_SIZE 75
183 #define DEFAULT_MAX_BITRATE 0
184 #define DEFAULT_SPATIAL_AQ FALSE
185 #define DEFAULT_AQ_STRENGTH 0
186 #define DEFAULT_NON_REF_P FALSE
187 #define DEFAULT_ZEROLATENCY FALSE
188 #define DEFAULT_STRICT_GOP FALSE
189 #define DEFAULT_CONST_QUALITY 0
190 #define DEFAULT_I_ADAPT FALSE
191 #define DEFAULT_QP_DETAIL -1
192
193 /* This lock is needed to prevent the situation where multiple encoders are
194  * initialised at the same time which appears to cause excessive CPU usage over
195  * some period of time. */
196 G_LOCK_DEFINE_STATIC (initialization_lock);
197
198 typedef struct
199 {
200   /* Allocated CUDA device memory and registered to NVENC to be used as input
201    * buffer regardless of the input memory type (OpenGL or System memory) */
202   CUdeviceptr cuda_pointer;
203
204   /* The stride of allocated CUDA device memory (CuMemAllocPitch).
205    * This might be different from the stride of GstVideoInfo */
206   gsize cuda_stride;
207
208   /* Registered NVENC resource (cuda_pointer is used for this) */
209   NV_ENC_REGISTER_RESOURCE nv_resource;
210
211   /* Mapped resource of nv_resource */
212   NV_ENC_MAP_INPUT_RESOURCE nv_mapped_resource;
213
214   /* whether nv_mapped_resource was mapped via NvEncMapInputResource()
215    * and therefore should unmap via NvEncUnmapInputResource or not */
216   gboolean mapped;
217 } GstNvEncInputResource;
218
219 /* The pair of GstNvEncInputResource () and NV_ENC_OUTPUT_PTR.
220  * The number of input/output resource are always identical */
221 typedef struct
222 {
223   GstNvEncInputResource *in_buf;
224   NV_ENC_OUTPUT_PTR out_buf;
225 } GstNvEncFrameState;
226
227 static gboolean gst_nv_base_enc_open (GstVideoEncoder * enc);
228 static gboolean gst_nv_base_enc_close (GstVideoEncoder * enc);
229 static gboolean gst_nv_base_enc_start (GstVideoEncoder * enc);
230 static gboolean gst_nv_base_enc_stop (GstVideoEncoder * enc);
231 static void gst_nv_base_enc_set_context (GstElement * element,
232     GstContext * context);
233 static gboolean gst_nv_base_enc_sink_query (GstVideoEncoder * enc,
234     GstQuery * query);
235 static gboolean gst_nv_base_enc_sink_event (GstVideoEncoder * enc,
236     GstEvent * event);
237 static gboolean gst_nv_base_enc_set_format (GstVideoEncoder * enc,
238     GstVideoCodecState * state);
239 static GstFlowReturn gst_nv_base_enc_handle_frame (GstVideoEncoder * enc,
240     GstVideoCodecFrame * frame);
241 static void gst_nv_base_enc_free_buffers (GstNvBaseEnc * nvenc);
242 static GstFlowReturn gst_nv_base_enc_finish (GstVideoEncoder * enc);
243 static void gst_nv_base_enc_set_property (GObject * object, guint prop_id,
244     const GValue * value, GParamSpec * pspec);
245 static void gst_nv_base_enc_get_property (GObject * object, guint prop_id,
246     GValue * value, GParamSpec * pspec);
247 static void gst_nv_base_enc_finalize (GObject * obj);
248 static GstCaps *gst_nv_base_enc_getcaps (GstVideoEncoder * enc,
249     GstCaps * filter);
250 static gboolean gst_nv_base_enc_stop_bitstream_thread (GstNvBaseEnc * nvenc,
251     gboolean force);
252 static gboolean gst_nv_base_enc_drain_encoder (GstNvBaseEnc * nvenc);
253 static gboolean gst_nv_base_enc_propose_allocation (GstVideoEncoder * enc,
254     GstQuery * query);
255
256 static void
257 gst_nv_base_enc_class_init (GstNvBaseEncClass * klass)
258 {
259   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
260   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
261   GstVideoEncoderClass *videoenc_class = GST_VIDEO_ENCODER_CLASS (klass);
262
263   gobject_class->set_property = gst_nv_base_enc_set_property;
264   gobject_class->get_property = gst_nv_base_enc_get_property;
265   gobject_class->finalize = gst_nv_base_enc_finalize;
266
267   element_class->set_context = GST_DEBUG_FUNCPTR (gst_nv_base_enc_set_context);
268
269   videoenc_class->open = GST_DEBUG_FUNCPTR (gst_nv_base_enc_open);
270   videoenc_class->close = GST_DEBUG_FUNCPTR (gst_nv_base_enc_close);
271
272   videoenc_class->start = GST_DEBUG_FUNCPTR (gst_nv_base_enc_start);
273   videoenc_class->stop = GST_DEBUG_FUNCPTR (gst_nv_base_enc_stop);
274
275   videoenc_class->set_format = GST_DEBUG_FUNCPTR (gst_nv_base_enc_set_format);
276   videoenc_class->getcaps = GST_DEBUG_FUNCPTR (gst_nv_base_enc_getcaps);
277   videoenc_class->handle_frame =
278       GST_DEBUG_FUNCPTR (gst_nv_base_enc_handle_frame);
279   videoenc_class->finish = GST_DEBUG_FUNCPTR (gst_nv_base_enc_finish);
280   videoenc_class->sink_query = GST_DEBUG_FUNCPTR (gst_nv_base_enc_sink_query);
281   videoenc_class->sink_event = GST_DEBUG_FUNCPTR (gst_nv_base_enc_sink_event);
282   videoenc_class->propose_allocation =
283       GST_DEBUG_FUNCPTR (gst_nv_base_enc_propose_allocation);
284
285   g_object_class_install_property (gobject_class, PROP_DEVICE_ID,
286       g_param_spec_uint ("cuda-device-id",
287           "Cuda Device ID",
288           "Get the GPU device to use for operations",
289           0, G_MAXUINT, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
290   g_object_class_install_property (gobject_class, PROP_PRESET,
291       g_param_spec_enum ("preset", "Encoding Preset",
292           "Encoding Preset",
293           GST_TYPE_NV_PRESET, DEFAULT_PRESET,
294           G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
295           G_PARAM_STATIC_STRINGS));
296   g_object_class_install_property (gobject_class, PROP_RC_MODE,
297       g_param_spec_enum ("rc-mode", "RC Mode", "Rate Control Mode",
298           GST_TYPE_NV_RC_MODE, DEFAULT_RC_MODE,
299           G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
300           G_PARAM_STATIC_STRINGS));
301   g_object_class_install_property (gobject_class, PROP_QP_MIN,
302       g_param_spec_int ("qp-min", "Minimum Quantizer",
303           "Minimum quantizer (-1 = from NVENC preset)", -1, 51, DEFAULT_QP_MIN,
304           G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
305           G_PARAM_STATIC_STRINGS));
306   g_object_class_install_property (gobject_class, PROP_QP_MAX,
307       g_param_spec_int ("qp-max", "Maximum Quantizer",
308           "Maximum quantizer (-1 = from NVENC preset)", -1, 51, DEFAULT_QP_MAX,
309           G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
310           G_PARAM_STATIC_STRINGS));
311   g_object_class_install_property (gobject_class, PROP_QP_CONST,
312       g_param_spec_int ("qp-const", "Constant Quantizer",
313           "Constant quantizer (-1 = from NVENC preset)", -1, 51,
314           DEFAULT_QP_CONST,
315           G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
316           G_PARAM_STATIC_STRINGS));
317   g_object_class_install_property (gobject_class, PROP_GOP_SIZE,
318       g_param_spec_int ("gop-size", "GOP size",
319           "Number of frames between intra frames (-1 = infinite)",
320           -1, G_MAXINT, DEFAULT_GOP_SIZE,
321           (GParamFlags) (G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
322               G_PARAM_STATIC_STRINGS)));
323   g_object_class_install_property (gobject_class, PROP_BITRATE,
324       g_param_spec_uint ("bitrate", "Bitrate",
325           "Bitrate in kbit/sec (0 = from NVENC preset)", 0, 2000 * 1024,
326           DEFAULT_BITRATE,
327           G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
328           G_PARAM_STATIC_STRINGS));
329   g_object_class_install_property (gobject_class, PROP_MAX_BITRATE,
330       g_param_spec_uint ("max-bitrate", "Max Bitrate",
331           "Maximum Bitrate in kbit/sec (ignored for CBR mode)", 0, 2000 * 1024,
332           DEFAULT_MAX_BITRATE,
333           G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
334           G_PARAM_STATIC_STRINGS));
335   g_object_class_install_property (gobject_class, PROP_SPATIAL_AQ,
336       g_param_spec_boolean ("spatial-aq", "Spatial AQ",
337           "Spatial Adaptive Quantization",
338           DEFAULT_SPATIAL_AQ,
339           G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
340           G_PARAM_STATIC_STRINGS));
341   g_object_class_install_property (gobject_class, PROP_AQ_STRENGTH,
342       g_param_spec_uint ("aq-strength", "AQ Strength",
343           "Adaptive Quantization Strength when spatial-aq is enabled"
344           " from 1 (low) to 15 (aggressive), (0 = autoselect)",
345           0, 15, DEFAULT_AQ_STRENGTH,
346           G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
347           G_PARAM_STATIC_STRINGS));
348   g_object_class_install_property (gobject_class, PROP_NON_REF_P,
349       g_param_spec_boolean ("nonref-p", "Nonref P",
350           "Automatic insertion of non-reference P-frames", DEFAULT_NON_REF_P,
351           G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
352           G_PARAM_STATIC_STRINGS));
353   g_object_class_install_property (gobject_class, PROP_ZEROLATENCY,
354       g_param_spec_boolean ("zerolatency", "Zerolatency",
355           "Zero latency operation (no reordering delay)", DEFAULT_ZEROLATENCY,
356           G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
357           G_PARAM_STATIC_STRINGS));
358   g_object_class_install_property (gobject_class, PROP_STRICT_GOP,
359       g_param_spec_boolean ("strict-gop", "Strict GOP",
360           "Minimize GOP-to-GOP rate fluctuations", DEFAULT_STRICT_GOP,
361           G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
362           G_PARAM_STATIC_STRINGS));
363   g_object_class_install_property (gobject_class, PROP_CONST_QUALITY,
364       g_param_spec_double ("const-quality", "Constant Quality",
365           "Target Constant Quality level for VBR mode (0 = automatic)",
366           0, 51, DEFAULT_CONST_QUALITY,
367           G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
368           G_PARAM_STATIC_STRINGS));
369   g_object_class_install_property (gobject_class, PROP_I_ADAPT,
370       g_param_spec_boolean ("i-adapt", "I Adapt",
371           "Enable adaptive I-frame insert when lookahead is enabled",
372           DEFAULT_I_ADAPT,
373           G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY |
374           G_PARAM_STATIC_STRINGS));
375   g_object_class_install_property (gobject_class, PROP_QP_MIN_I,
376       g_param_spec_int ("qp-min-i", "QP Min I",
377           "Minimum QP value for I frame, When >= 0, \"qp-min-p\" and "
378           "\"qp-min-b\" should be also >= 0. Overwritten by \"qp-min\""
379           " (-1 = from NVENC preset)", -1, 51,
380           DEFAULT_QP_DETAIL,
381           G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
382           G_PARAM_STATIC_STRINGS));
383   g_object_class_install_property (gobject_class, PROP_QP_MIN_P,
384       g_param_spec_int ("qp-min-p", "QP Min P",
385           "Minimum QP value for P frame, When >= 0, \"qp-min-i\" and "
386           "\"qp-min-b\" should be also >= 0. Overwritten by \"qp-min\""
387           " (-1 = from NVENC preset)", -1, 51,
388           DEFAULT_QP_DETAIL,
389           G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
390           G_PARAM_STATIC_STRINGS));
391   g_object_class_install_property (gobject_class, PROP_QP_MIN_B,
392       g_param_spec_int ("qp-min-b", "QP Min B",
393           "Minimum QP value for B frame, When >= 0, \"qp-min-i\" and "
394           "\"qp-min-p\" should be also >= 0. Overwritten by \"qp-min\""
395           " (-1 = from NVENC preset)", -1, 51,
396           DEFAULT_QP_DETAIL,
397           G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
398           G_PARAM_STATIC_STRINGS));
399   g_object_class_install_property (gobject_class, PROP_QP_MAX_I,
400       g_param_spec_int ("qp-max-i", "QP Max I",
401           "Maximum QP value for I frame, When >= 0, \"qp-max-p\" and "
402           "\"qp-max-b\" should be also >= 0. Overwritten by \"qp-max\""
403           " (-1 = from NVENC preset)", -1, 51,
404           DEFAULT_QP_DETAIL,
405           G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
406           G_PARAM_STATIC_STRINGS));
407   g_object_class_install_property (gobject_class, PROP_QP_MAX_P,
408       g_param_spec_int ("qp-max-p", "QP Max P",
409           "Maximum QP value for P frame, When >= 0, \"qp-max-i\" and "
410           "\"qp-max-b\" should be also >= 0. Overwritten by \"qp-max\""
411           " (-1 = from NVENC preset)", -1, 51,
412           DEFAULT_QP_DETAIL,
413           G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
414           G_PARAM_STATIC_STRINGS));
415   g_object_class_install_property (gobject_class, PROP_QP_MAX_B,
416       g_param_spec_int ("qp-max-b", "QP Max B",
417           "Maximum QP value for B frame, When >= 0, \"qp-max-i\" and "
418           "\"qp-max-p\" should be also >= 0. Overwritten by \"qp-max\""
419           " (-1 = from NVENC preset)", -1, 51,
420           DEFAULT_QP_DETAIL,
421           G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
422           G_PARAM_STATIC_STRINGS));
423   g_object_class_install_property (gobject_class, PROP_QP_CONST_I,
424       g_param_spec_int ("qp-const-i", "QP Const I",
425           "Constant QP value for I frame, When >= 0, \"qp-const-p\" and "
426           "\"qp-const-b\" should be also >= 0. Overwritten by \"qp-const\""
427           " (-1 = from NVENC preset)", -1, 51,
428           DEFAULT_QP_DETAIL,
429           G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
430           G_PARAM_STATIC_STRINGS));
431   g_object_class_install_property (gobject_class, PROP_QP_CONST_P,
432       g_param_spec_int ("qp-const-p", "QP Const P",
433           "Constant QP value for P frame, When >= 0, \"qp-const-i\" and "
434           "\"qp-const-b\" should be also >= 0. Overwritten by \"qp-const\""
435           " (-1 = from NVENC preset)", -1, 51,
436           DEFAULT_QP_DETAIL,
437           G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
438           G_PARAM_STATIC_STRINGS));
439   g_object_class_install_property (gobject_class, PROP_QP_CONST_B,
440       g_param_spec_int ("qp-const-b", "QP Const B",
441           "Constant QP value for B frame, When >= 0, \"qp-const-i\" and "
442           "\"qp-const-p\" should be also >= 0. Overwritten by \"qp-const\""
443           " (-1 = from NVENC preset)", -1, 51,
444           DEFAULT_QP_DETAIL,
445           G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
446           G_PARAM_STATIC_STRINGS));
447
448   gst_type_mark_as_plugin_api (GST_TYPE_NV_BASE_ENC, 0);
449   gst_type_mark_as_plugin_api (GST_TYPE_NV_PRESET, 0);
450   gst_type_mark_as_plugin_api (GST_TYPE_NV_RC_MODE, 0);
451 }
452
453 static gboolean
454 gst_nv_base_enc_open_encode_session (GstNvBaseEnc * nvenc)
455 {
456   NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS params = { 0, };
457   NVENCSTATUS nv_ret;
458
459   params.version = gst_nvenc_get_open_encode_session_ex_params_version ();
460   params.apiVersion = gst_nvenc_get_api_version ();
461   params.device = gst_cuda_context_get_handle (nvenc->cuda_ctx);
462   params.deviceType = NV_ENC_DEVICE_TYPE_CUDA;
463   nv_ret = NvEncOpenEncodeSessionEx (&params, &nvenc->encoder);
464
465   return nv_ret == NV_ENC_SUCCESS;
466 }
467
468 static gboolean
469 gst_nv_base_enc_open (GstVideoEncoder * enc)
470 {
471   GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (enc);
472   GstNvBaseEncClass *klass = GST_NV_BASE_ENC_GET_CLASS (enc);
473   GValue *formats = NULL;
474   CUresult cuda_ret;
475
476   if (!gst_cuda_ensure_element_context (GST_ELEMENT_CAST (enc),
477           klass->cuda_device_id, &nvenc->cuda_ctx)) {
478     GST_ERROR_OBJECT (nvenc, "failed to create CUDA context");
479     return FALSE;
480   }
481
482   if (gst_cuda_context_push (nvenc->cuda_ctx)) {
483     cuda_ret = CuStreamCreate (&nvenc->cuda_stream, CU_STREAM_DEFAULT);
484     if (!gst_cuda_result (cuda_ret)) {
485       GST_WARNING_OBJECT (nvenc,
486           "Could not create cuda stream, will use default stream");
487       nvenc->cuda_stream = NULL;
488     }
489     gst_cuda_context_pop (NULL);
490   }
491
492   if (!gst_nv_base_enc_open_encode_session (nvenc)) {
493     GST_ERROR ("Failed to create NVENC encoder session");
494     gst_clear_object (&nvenc->cuda_ctx);
495     return FALSE;
496   }
497
498   GST_INFO ("created NVENC encoder %p", nvenc->encoder);
499
500   /* query supported input formats */
501   if (!gst_nvenc_get_supported_input_formats (nvenc->encoder, klass->codec_id,
502           &formats)) {
503     GST_WARNING_OBJECT (nvenc, "No supported input formats");
504     gst_nv_base_enc_close (enc);
505     return FALSE;
506   }
507
508   nvenc->input_formats = formats;
509
510   return TRUE;
511 }
512
513 static void
514 gst_nv_base_enc_set_context (GstElement * element, GstContext * context)
515 {
516   GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (element);
517   GstNvBaseEncClass *klass = GST_NV_BASE_ENC_GET_CLASS (nvenc);
518
519   if (gst_cuda_handle_set_context (element, context, klass->cuda_device_id,
520           &nvenc->cuda_ctx)) {
521     goto done;
522   }
523 #if HAVE_NVCODEC_GST_GL
524   gst_gl_handle_set_context (element, context,
525       (GstGLDisplay **) & nvenc->display,
526       (GstGLContext **) & nvenc->other_context);
527   if (nvenc->display)
528     gst_gl_display_filter_gl_api (GST_GL_DISPLAY (nvenc->display),
529         SUPPORTED_GL_APIS);
530 #endif
531
532 done:
533   GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
534 }
535
536 static gboolean
537 gst_nv_base_enc_sink_query (GstVideoEncoder * enc, GstQuery * query)
538 {
539   GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (enc);
540
541   switch (GST_QUERY_TYPE (query)) {
542     case GST_QUERY_CONTEXT:{
543       if (gst_cuda_handle_context_query (GST_ELEMENT (nvenc),
544               query, nvenc->cuda_ctx))
545         return TRUE;
546
547 #if HAVE_NVCODEC_GST_GL
548       {
549         gboolean ret;
550
551         ret = gst_gl_handle_context_query ((GstElement *) nvenc, query,
552             (GstGLDisplay *) nvenc->display, NULL,
553             (GstGLContext *) nvenc->other_context);
554         if (nvenc->display) {
555           gst_gl_display_filter_gl_api (GST_GL_DISPLAY (nvenc->display),
556               SUPPORTED_GL_APIS);
557         }
558
559         if (ret)
560           return ret;
561       }
562 #endif
563       break;
564     }
565     default:
566       break;
567   }
568
569   return GST_VIDEO_ENCODER_CLASS (parent_class)->sink_query (enc, query);
570 }
571
572 #ifdef HAVE_NVCODEC_GST_GL
573 static gboolean
574 gst_nv_base_enc_ensure_gl_context (GstNvBaseEnc * nvenc)
575 {
576   if (!nvenc->display) {
577     GST_DEBUG_OBJECT (nvenc, "No available OpenGL display");
578     return FALSE;
579   }
580
581   if (!gst_gl_query_local_gl_context (GST_ELEMENT (nvenc), GST_PAD_SINK,
582           (GstGLContext **) & nvenc->gl_context)) {
583     GST_INFO_OBJECT (nvenc, "failed to query local OpenGL context");
584     if (nvenc->gl_context)
585       gst_object_unref (nvenc->gl_context);
586     nvenc->gl_context =
587         (GstObject *) gst_gl_display_get_gl_context_for_thread ((GstGLDisplay *)
588         nvenc->display, NULL);
589     if (!nvenc->gl_context
590         || !gst_gl_display_add_context ((GstGLDisplay *) nvenc->display,
591             (GstGLContext *) nvenc->gl_context)) {
592       if (nvenc->gl_context)
593         gst_object_unref (nvenc->gl_context);
594       if (!gst_gl_display_create_context ((GstGLDisplay *) nvenc->display,
595               (GstGLContext *) nvenc->other_context,
596               (GstGLContext **) & nvenc->gl_context, NULL)) {
597         GST_ERROR_OBJECT (nvenc, "failed to create OpenGL context");
598         return FALSE;
599       }
600       if (!gst_gl_display_add_context ((GstGLDisplay *) nvenc->display,
601               (GstGLContext *) nvenc->gl_context)) {
602         GST_ERROR_OBJECT (nvenc,
603             "failed to add the OpenGL context to the display");
604         return FALSE;
605       }
606     }
607   }
608
609   if (!gst_gl_context_check_gl_version ((GstGLContext *) nvenc->gl_context,
610           SUPPORTED_GL_APIS, 3, 0)) {
611     GST_WARNING_OBJECT (nvenc, "OpenGL context could not support PBO download");
612     return FALSE;
613   }
614
615   return TRUE;
616 }
617 #endif
618
619 static gboolean
620 gst_nv_base_enc_propose_allocation (GstVideoEncoder * enc, GstQuery * query)
621 {
622   GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (enc);
623   GstCaps *caps;
624   GstVideoInfo info;
625   GstBufferPool *pool;
626   GstStructure *config;
627   GstCapsFeatures *features;
628
629   GST_DEBUG_OBJECT (nvenc, "propose allocation");
630
631   gst_query_parse_allocation (query, &caps, NULL);
632
633   if (caps == NULL)
634     return FALSE;
635
636   if (!gst_video_info_from_caps (&info, caps)) {
637     GST_WARNING_OBJECT (nvenc, "failed to get video info");
638     return FALSE;
639   }
640
641   features = gst_caps_get_features (caps, 0);
642 #if HAVE_NVCODEC_GST_GL
643   if (features && gst_caps_features_contains (features,
644           GST_CAPS_FEATURE_MEMORY_GL_MEMORY)) {
645     GST_DEBUG_OBJECT (nvenc, "upsteram support GL memory");
646     if (!gst_nv_base_enc_ensure_gl_context (nvenc)) {
647       GST_WARNING_OBJECT (nvenc, "Could not get gl context");
648       goto done;
649     }
650
651     pool = gst_gl_buffer_pool_new ((GstGLContext *) nvenc->gl_context);
652   } else
653 #endif
654   if (features && gst_caps_features_contains (features,
655           GST_CAPS_FEATURE_MEMORY_CUDA_MEMORY)) {
656     GST_DEBUG_OBJECT (nvenc, "upstream support CUDA memory");
657     pool = gst_cuda_buffer_pool_new (nvenc->cuda_ctx);
658   } else {
659     GST_DEBUG_OBJECT (nvenc, "use system memory");
660     goto done;
661   }
662
663   if (G_UNLIKELY (pool == NULL)) {
664     GST_WARNING_OBJECT (nvenc, "cannot create buffer pool");
665     goto done;
666   }
667
668   config = gst_buffer_pool_get_config (pool);
669   gst_buffer_pool_config_set_params (config, caps, GST_VIDEO_INFO_SIZE (&info),
670       nvenc->items->len, nvenc->items->len);
671
672   gst_query_add_allocation_pool (query, pool, GST_VIDEO_INFO_SIZE (&info),
673       nvenc->items->len, nvenc->items->len);
674   gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
675   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
676
677   if (!gst_buffer_pool_set_config (pool, config))
678     goto error_pool_config;
679
680   gst_object_unref (pool);
681
682 done:
683   return GST_VIDEO_ENCODER_CLASS (parent_class)->propose_allocation (enc,
684       query);
685
686 error_pool_config:
687   {
688     if (pool)
689       gst_object_unref (pool);
690     GST_WARNING_OBJECT (nvenc, "failed to set config");
691     return FALSE;
692   }
693 }
694
695 static gboolean
696 gst_nv_base_enc_sink_event (GstVideoEncoder * enc, GstEvent * event)
697 {
698   GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (enc);
699   gboolean ret;
700
701   ret = GST_VIDEO_ENCODER_CLASS (parent_class)->sink_event (enc, event);
702
703   switch (GST_EVENT_TYPE (event)) {
704     case GST_EVENT_STREAM_START:
705     case GST_EVENT_FLUSH_STOP:
706       nvenc->last_flow = GST_FLOW_OK;
707       break;
708     default:
709       break;
710   }
711
712   return ret;
713 }
714
715 static gboolean
716 gst_nv_base_enc_start (GstVideoEncoder * enc)
717 {
718   GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (enc);
719
720   nvenc->available_queue = g_async_queue_new ();
721   nvenc->pending_queue = g_async_queue_new ();
722   nvenc->bitstream_queue = g_async_queue_new ();
723   nvenc->items = g_array_new (FALSE, TRUE, sizeof (GstNvEncFrameState));
724
725   nvenc->last_flow = GST_FLOW_OK;
726   memset (&nvenc->init_params, 0, sizeof (NV_ENC_INITIALIZE_PARAMS));
727   memset (&nvenc->config, 0, sizeof (NV_ENC_CONFIG));
728
729 #if HAVE_NVCODEC_GST_GL
730   {
731     gst_gl_ensure_element_data (GST_ELEMENT (nvenc),
732         (GstGLDisplay **) & nvenc->display,
733         (GstGLContext **) & nvenc->other_context);
734     if (nvenc->display)
735       gst_gl_display_filter_gl_api (GST_GL_DISPLAY (nvenc->display),
736           SUPPORTED_GL_APIS);
737   }
738 #endif
739
740   /* DTS can be negative if bframe was enabled */
741   gst_video_encoder_set_min_pts (enc, GST_SECOND * 60 * 60 * 1000);
742
743   return TRUE;
744 }
745
746 static gboolean
747 gst_nv_base_enc_stop (GstVideoEncoder * enc)
748 {
749   GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (enc);
750
751   gst_nv_base_enc_stop_bitstream_thread (nvenc, TRUE);
752
753   gst_nv_base_enc_free_buffers (nvenc);
754
755   if (nvenc->input_state) {
756     gst_video_codec_state_unref (nvenc->input_state);
757     nvenc->input_state = NULL;
758   }
759
760   if (nvenc->available_queue) {
761     g_async_queue_unref (nvenc->available_queue);
762     nvenc->available_queue = NULL;
763   }
764   if (nvenc->pending_queue) {
765     g_async_queue_unref (nvenc->pending_queue);
766     nvenc->pending_queue = NULL;
767   }
768   if (nvenc->bitstream_queue) {
769     g_async_queue_unref (nvenc->bitstream_queue);
770     nvenc->bitstream_queue = NULL;
771   }
772   if (nvenc->display) {
773     gst_object_unref (nvenc->display);
774     nvenc->display = NULL;
775   }
776   if (nvenc->other_context) {
777     gst_object_unref (nvenc->other_context);
778     nvenc->other_context = NULL;
779   }
780   if (nvenc->gl_context) {
781     gst_object_unref (nvenc->gl_context);
782     nvenc->gl_context = NULL;
783   }
784
785   if (nvenc->items) {
786     g_array_free (nvenc->items, TRUE);
787     nvenc->items = NULL;
788   }
789
790   return TRUE;
791 }
792
793 static void
794 check_formats (const gchar * str, guint * max_chroma, guint * max_bit_minus8)
795 {
796   if (!str)
797     return;
798
799   if (g_strrstr (str, "-444") || g_strrstr (str, "-4:4:4"))
800     *max_chroma = 2;
801   else if ((g_strrstr (str, "-4:2:2") || g_strrstr (str, "-422"))
802       && *max_chroma < 1)
803     *max_chroma = 1;
804
805   if (g_strrstr (str, "-12"))
806     *max_bit_minus8 = 4;
807   else if (g_strrstr (str, "-10") && *max_bit_minus8 < 2)
808     *max_bit_minus8 = 2;
809 }
810
811 static gboolean
812 gst_nv_base_enc_set_filtered_input_formats (GstNvBaseEnc * nvenc,
813     GstCaps * caps, const GValue * input_formats, guint max_chroma,
814     guint max_bit_minus8)
815 {
816   gint i;
817   GValue supported_format = G_VALUE_INIT;
818   gint num_format = 0;
819   const GValue *last_format = NULL;
820
821   g_value_init (&supported_format, GST_TYPE_LIST);
822
823   for (i = 0; i < gst_value_list_get_size (input_formats); i++) {
824     const GValue *val;
825     GstVideoFormat format;
826
827     val = gst_value_list_get_value (input_formats, i);
828     format = gst_video_format_from_string (g_value_get_string (val));
829
830     switch (format) {
831       case GST_VIDEO_FORMAT_NV12:
832       case GST_VIDEO_FORMAT_YV12:
833       case GST_VIDEO_FORMAT_I420:
834         /* 8bits 4:2:0 formats are always supported */
835       case GST_VIDEO_FORMAT_BGRA:
836       case GST_VIDEO_FORMAT_RGBA:
837         /* NOTE: RGB formats seems to also supported format, which are
838          * encoded to 4:2:0 formats */
839         gst_value_list_append_value (&supported_format, val);
840         last_format = val;
841         num_format++;
842         break;
843       case GST_VIDEO_FORMAT_Y444:
844       case GST_VIDEO_FORMAT_VUYA:
845         if (max_chroma >= 2) {
846           gst_value_list_append_value (&supported_format, val);
847           last_format = val;
848           num_format++;
849         }
850         break;
851       case GST_VIDEO_FORMAT_P010_10LE:
852       case GST_VIDEO_FORMAT_P010_10BE:
853       case GST_VIDEO_FORMAT_BGR10A2_LE:
854       case GST_VIDEO_FORMAT_RGB10A2_LE:
855       case GST_VIDEO_FORMAT_Y444_16LE:
856       case GST_VIDEO_FORMAT_Y444_16BE:
857         if (max_bit_minus8 >= 2) {
858           gst_value_list_append_value (&supported_format, val);
859           last_format = val;
860           num_format++;
861         }
862         break;
863       default:
864         break;
865     }
866   }
867
868   if (num_format == 0) {
869     g_value_unset (&supported_format);
870     GST_WARNING_OBJECT (nvenc, "Cannot find matching input format");
871     return FALSE;
872   }
873
874   if (num_format > 1)
875     gst_caps_set_value (caps, "format", &supported_format);
876   else
877     gst_caps_set_value (caps, "format", last_format);
878
879   g_value_unset (&supported_format);
880
881   return TRUE;
882 }
883
884 static GstCaps *
885 gst_nv_base_enc_getcaps (GstVideoEncoder * enc, GstCaps * filter)
886 {
887   GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (enc);
888   GstNvBaseEncClass *klass = GST_NV_BASE_ENC_GET_CLASS (enc);
889   GstCaps *supported_incaps = NULL;
890   GstCaps *template_caps, *caps, *allowed;
891
892   template_caps = gst_pad_get_pad_template_caps (enc->sinkpad);
893   allowed = gst_pad_get_allowed_caps (enc->srcpad);
894
895   GST_LOG_OBJECT (enc, "template caps %" GST_PTR_FORMAT, template_caps);
896   GST_LOG_OBJECT (enc, "allowed caps %" GST_PTR_FORMAT, allowed);
897
898   if (!allowed) {
899     /* no peer */
900     supported_incaps = template_caps;
901     template_caps = NULL;
902     goto done;
903   } else if (gst_caps_is_empty (allowed)) {
904     /* couldn't be negotiated, just return empty caps */
905     gst_caps_unref (template_caps);
906     return allowed;
907   }
908
909   GST_OBJECT_LOCK (nvenc);
910
911   if (nvenc->input_formats != NULL) {
912     gboolean has_profile = FALSE;
913     guint max_chroma_index = 0;
914     guint max_bit_minus8 = 0;
915     gint i, j;
916
917     for (i = 0; i < gst_caps_get_size (allowed); i++) {
918       const GstStructure *allowed_s = gst_caps_get_structure (allowed, i);
919       const GValue *val;
920
921       if ((val = gst_structure_get_value (allowed_s, "profile"))) {
922         if (G_VALUE_HOLDS_STRING (val)) {
923           check_formats (g_value_get_string (val), &max_chroma_index,
924               &max_bit_minus8);
925           has_profile = TRUE;
926         } else if (GST_VALUE_HOLDS_LIST (val)) {
927           for (j = 0; j < gst_value_list_get_size (val); j++) {
928             const GValue *vlist = gst_value_list_get_value (val, j);
929
930             if (G_VALUE_HOLDS_STRING (vlist)) {
931               check_formats (g_value_get_string (vlist), &max_chroma_index,
932                   &max_bit_minus8);
933               has_profile = TRUE;
934             }
935           }
936         }
937       }
938     }
939
940     GST_LOG_OBJECT (enc,
941         "downstream requested profile %d, max bitdepth %d, max chroma %d",
942         has_profile, max_bit_minus8 + 8, max_chroma_index);
943
944     supported_incaps = gst_caps_copy (template_caps);
945     if (!has_profile ||
946         !gst_nv_base_enc_set_filtered_input_formats (nvenc, supported_incaps,
947             nvenc->input_formats, max_chroma_index, max_bit_minus8)) {
948       gst_caps_set_value (supported_incaps, "format", nvenc->input_formats);
949     }
950
951     if (nvenc->encoder) {
952       GValue *interlace_mode;
953
954       interlace_mode =
955           gst_nvenc_get_interlace_modes (nvenc->encoder, klass->codec_id);
956       gst_caps_set_value (supported_incaps, "interlace-mode", interlace_mode);
957       g_value_unset (interlace_mode);
958       g_free (interlace_mode);
959     }
960
961     GST_LOG_OBJECT (enc, "codec input caps %" GST_PTR_FORMAT, supported_incaps);
962     GST_LOG_OBJECT (enc, "   template caps %" GST_PTR_FORMAT, template_caps);
963     caps = gst_caps_intersect (template_caps, supported_incaps);
964     gst_caps_unref (supported_incaps);
965     supported_incaps = caps;
966     GST_LOG_OBJECT (enc, "  supported caps %" GST_PTR_FORMAT, supported_incaps);
967   }
968
969   GST_OBJECT_UNLOCK (nvenc);
970
971 done:
972   caps = gst_video_encoder_proxy_getcaps (enc, supported_incaps, filter);
973
974   if (supported_incaps)
975     gst_caps_unref (supported_incaps);
976   gst_clear_caps (&allowed);
977   gst_clear_caps (&template_caps);
978
979   GST_DEBUG_OBJECT (nvenc, "  returning caps %" GST_PTR_FORMAT, caps);
980
981   return caps;
982 }
983
984 static gboolean
985 gst_nv_base_enc_close (GstVideoEncoder * enc)
986 {
987   GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (enc);
988   gboolean ret = TRUE;
989
990   if (nvenc->encoder) {
991     if (NvEncDestroyEncoder (nvenc->encoder) != NV_ENC_SUCCESS)
992       ret = FALSE;
993
994     nvenc->encoder = NULL;
995   }
996
997   if (nvenc->cuda_ctx && nvenc->cuda_stream) {
998     if (gst_cuda_context_push (nvenc->cuda_ctx)) {
999       gst_cuda_result (CuStreamDestroy (nvenc->cuda_stream));
1000       gst_cuda_context_pop (NULL);
1001     }
1002   }
1003
1004   gst_clear_object (&nvenc->cuda_ctx);
1005   nvenc->cuda_stream = NULL;
1006
1007   GST_OBJECT_LOCK (nvenc);
1008   if (nvenc->input_formats)
1009     g_value_unset (nvenc->input_formats);
1010   g_free (nvenc->input_formats);
1011   nvenc->input_formats = NULL;
1012   GST_OBJECT_UNLOCK (nvenc);
1013
1014   if (nvenc->input_state) {
1015     gst_video_codec_state_unref (nvenc->input_state);
1016     nvenc->input_state = NULL;
1017   }
1018
1019   return ret;
1020 }
1021
1022 static void
1023 gst_nv_base_enc_init (GstNvBaseEnc * nvenc)
1024 {
1025   GstVideoEncoder *encoder = GST_VIDEO_ENCODER (nvenc);
1026   GstNvEncQP qp_detail =
1027       { DEFAULT_QP_DETAIL, DEFAULT_QP_DETAIL, DEFAULT_QP_DETAIL };
1028
1029   nvenc->preset_enum = DEFAULT_PRESET;
1030   nvenc->selected_preset = _nv_preset_to_guid (nvenc->preset_enum);
1031   nvenc->rate_control_mode = DEFAULT_RC_MODE;
1032   nvenc->qp_min = DEFAULT_QP_MIN;
1033   nvenc->qp_max = DEFAULT_QP_MAX;
1034   nvenc->qp_const = DEFAULT_QP_CONST;
1035   nvenc->bitrate = DEFAULT_BITRATE;
1036   nvenc->gop_size = DEFAULT_GOP_SIZE;
1037   nvenc->max_bitrate = DEFAULT_MAX_BITRATE;
1038   nvenc->spatial_aq = DEFAULT_SPATIAL_AQ;
1039   nvenc->aq_strength = DEFAULT_AQ_STRENGTH;
1040   nvenc->non_refp = DEFAULT_NON_REF_P;
1041   nvenc->zerolatency = DEFAULT_ZEROLATENCY;
1042   nvenc->strict_gop = DEFAULT_STRICT_GOP;
1043   nvenc->const_quality = DEFAULT_CONST_QUALITY;
1044   nvenc->i_adapt = DEFAULT_I_ADAPT;
1045   nvenc->qp_min_detail = qp_detail;
1046   nvenc->qp_max_detail = qp_detail;
1047   nvenc->qp_const_detail = qp_detail;
1048
1049   GST_VIDEO_ENCODER_STREAM_LOCK (encoder);
1050   GST_VIDEO_ENCODER_STREAM_UNLOCK (encoder);
1051
1052   GST_PAD_SET_ACCEPT_INTERSECT (GST_VIDEO_ENCODER_SINK_PAD (encoder));
1053 }
1054
1055 static void
1056 gst_nv_base_enc_finalize (GObject * obj)
1057 {
1058   G_OBJECT_CLASS (gst_nv_base_enc_parent_class)->finalize (obj);
1059 }
1060
1061 static GstVideoCodecFrame *
1062 _find_frame_with_output_buffer (GstNvBaseEnc * nvenc, NV_ENC_OUTPUT_PTR out_buf)
1063 {
1064   GList *l, *walk = gst_video_encoder_get_frames (GST_VIDEO_ENCODER (nvenc));
1065   GstVideoCodecFrame *ret = NULL;
1066
1067   for (l = walk; l; l = l->next) {
1068     GstVideoCodecFrame *frame = (GstVideoCodecFrame *) l->data;
1069     GstNvEncFrameState *state = gst_video_codec_frame_get_user_data (frame);
1070
1071     if (!state || !state->out_buf)
1072       continue;
1073
1074     if (state->out_buf == out_buf) {
1075       ret = frame;
1076       break;
1077     }
1078   }
1079
1080   if (ret)
1081     gst_video_codec_frame_ref (ret);
1082
1083   g_list_free_full (walk, (GDestroyNotify) gst_video_codec_frame_unref);
1084
1085   return ret;
1086 }
1087
1088 static const gchar *
1089 picture_type_to_string (NV_ENC_PIC_TYPE type)
1090 {
1091   switch (type) {
1092     case NV_ENC_PIC_TYPE_P:
1093       return "P";
1094     case NV_ENC_PIC_TYPE_B:
1095       return "B";
1096     case NV_ENC_PIC_TYPE_I:
1097       return "I";
1098     case NV_ENC_PIC_TYPE_IDR:
1099       return "IDR";
1100     case NV_ENC_PIC_TYPE_BI:
1101       return "BI";
1102     case NV_ENC_PIC_TYPE_SKIPPED:
1103       return "SKIPPED";
1104     case NV_ENC_PIC_TYPE_INTRA_REFRESH:
1105       return "INTRA-REFRESH";
1106     case NV_ENC_PIC_TYPE_UNKNOWN:
1107     default:
1108       break;
1109   }
1110
1111   return "UNKNOWN";
1112 }
1113
1114 static gpointer
1115 gst_nv_base_enc_bitstream_thread (gpointer user_data)
1116 {
1117   GstVideoEncoder *enc = user_data;
1118   GstNvBaseEnc *nvenc = user_data;
1119   GstFlowReturn flow = GST_FLOW_OK;
1120
1121   /* overview of operation:
1122    * 1. retrieve the next buffer submitted to the bitstream pool
1123    * 2. wait for that buffer to be ready from nvenc (LockBitsream)
1124    * 3. retrieve the GstVideoCodecFrame associated with that buffer
1125    * 4. for each buffer in the frame
1126    * 4.1 (step 2): wait for that buffer to be ready from nvenc (LockBitsream)
1127    * 4.2 create an output GstBuffer from the nvenc buffers
1128    * 4.3 unlock the nvenc bitstream buffers UnlockBitsream
1129    * 5. finish_frame()
1130    * 6. cleanup
1131    */
1132   do {
1133     GstBuffer *buffer = NULL;
1134     GstNvEncFrameState *state_in_queue = NULL;
1135     GstNvEncFrameState *state = NULL;
1136     GstVideoCodecFrame *frame = NULL;
1137     NVENCSTATUS nv_ret;
1138     NV_ENC_LOCK_BITSTREAM lock_bs = { 0, };
1139     NV_ENC_OUTPUT_PTR out_buf;
1140     GstNvEncInputResource *resource;
1141
1142     GST_LOG_OBJECT (enc, "wait for bitstream buffer..");
1143
1144     state_in_queue = g_async_queue_pop (nvenc->bitstream_queue);
1145     if ((gpointer) state_in_queue == SHUTDOWN_COOKIE)
1146       goto exit_thread;
1147
1148     out_buf = state_in_queue->out_buf;
1149     resource = state_in_queue->in_buf;
1150
1151     GST_LOG_OBJECT (nvenc, "waiting for output buffer %p to be ready", out_buf);
1152
1153     lock_bs.version = gst_nvenc_get_lock_bitstream_version ();
1154     lock_bs.outputBitstream = out_buf;
1155     lock_bs.doNotWait = 0;
1156
1157     /* FIXME: this would need to be updated for other slice modes */
1158     lock_bs.sliceOffsets = NULL;
1159
1160     if (!gst_cuda_context_push (nvenc->cuda_ctx)) {
1161       GST_ELEMENT_ERROR (nvenc, LIBRARY, ENCODE, (NULL),
1162           ("Failed to push current context"));
1163       goto error_shutdown;
1164     }
1165
1166     nv_ret = NvEncLockBitstream (nvenc->encoder, &lock_bs);
1167     if (nv_ret != NV_ENC_SUCCESS) {
1168       gst_cuda_context_pop (NULL);
1169
1170       GST_ELEMENT_ERROR (nvenc, STREAM, ENCODE, (NULL),
1171           ("Failed to lock bitstream buffer %p, ret %d",
1172               lock_bs.outputBitstream, nv_ret));
1173       goto error_shutdown;
1174     }
1175
1176     frame = _find_frame_with_output_buffer (nvenc, out_buf);
1177     state = gst_video_codec_frame_get_user_data (frame);
1178     g_assert (state->out_buf == out_buf);
1179
1180     /* copy into output buffer */
1181     buffer = gst_buffer_new_allocate (NULL, lock_bs.bitstreamSizeInBytes, NULL);
1182     gst_buffer_fill (buffer, 0, lock_bs.bitstreamBufferPtr,
1183         lock_bs.bitstreamSizeInBytes);
1184
1185     if (lock_bs.pictureType == NV_ENC_PIC_TYPE_IDR) {
1186       GST_DEBUG_OBJECT (nvenc, "This is a keyframe");
1187       GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame);
1188     }
1189
1190     nv_ret = NvEncUnlockBitstream (nvenc->encoder, state->out_buf);
1191
1192     if (nv_ret != NV_ENC_SUCCESS) {
1193       gst_cuda_context_pop (NULL);
1194
1195       GST_ELEMENT_ERROR (nvenc, STREAM, ENCODE, (NULL),
1196           ("Failed to unlock bitstream buffer %p, ret %d",
1197               lock_bs.outputBitstream, nv_ret));
1198       gst_buffer_unref (buffer);
1199       gst_video_encoder_finish_frame (enc, frame);
1200
1201       goto error_shutdown;
1202     }
1203
1204     frame->dts = frame->pts;
1205     frame->pts = lock_bs.outputTimeStamp;
1206     frame->duration = lock_bs.outputDuration;
1207
1208     GST_LOG_OBJECT (nvenc, "frame index %" G_GUINT32_FORMAT
1209         ", frame type %s, dts %" GST_TIME_FORMAT
1210         ", pts %" GST_TIME_FORMAT,
1211         lock_bs.frameIdx, picture_type_to_string (lock_bs.pictureType),
1212         GST_TIME_ARGS (frame->dts), GST_TIME_ARGS (frame->pts));
1213
1214     frame->output_buffer = buffer;
1215
1216     nv_ret =
1217         NvEncUnmapInputResource (nvenc->encoder,
1218         resource->nv_mapped_resource.mappedResource);
1219     resource->mapped = FALSE;
1220
1221     if (nv_ret != NV_ENC_SUCCESS) {
1222       GST_ERROR_OBJECT (nvenc, "Failed to unmap input resource %p, ret %d",
1223           resource, nv_ret);
1224     }
1225
1226     gst_cuda_context_pop (NULL);
1227
1228     memset (&resource->nv_mapped_resource, 0,
1229         sizeof (resource->nv_mapped_resource));
1230
1231     g_async_queue_push (nvenc->available_queue, state_in_queue);
1232
1233     /* Ugly but no other way to get DTS offset since nvenc dose not adjust
1234      * dts/pts even if bframe was enabled. So the output PTS can be smaller
1235      * than DTS. The maximum difference between DTS and PTS can be calculated
1236      * using the PTS difference between the first frame and the second frame.
1237      */
1238     if (nvenc->bframes > 0) {
1239       if (nvenc->dts_offset == 0) {
1240         if (!nvenc->first_frame) {
1241           /* store the first frame to get dts offset */
1242           nvenc->first_frame = frame;
1243           continue;
1244         } else {
1245           if (nvenc->first_frame->pts >= frame->pts) {
1246             GstClockTime duration = 0;
1247
1248             GST_WARNING_OBJECT (enc, "Could not calculate DTS offset");
1249
1250             if (nvenc->input_info.fps_n > 0 && nvenc->input_info.fps_d > 0) {
1251               duration =
1252                   gst_util_uint64_scale (GST_SECOND, nvenc->input_info.fps_d,
1253                   nvenc->input_info.fps_n);
1254             } else if (nvenc->first_frame->duration > 0 &&
1255                 GST_CLOCK_TIME_IS_VALID (nvenc->first_frame->duration)) {
1256               duration = nvenc->first_frame->duration;
1257             } else {
1258               GST_WARNING_OBJECT (enc,
1259                   "No way to get frame duration, assuming 30fps");
1260               duration = gst_util_uint64_scale (GST_SECOND, 1, 30);
1261             }
1262
1263             nvenc->dts_offset = duration * nvenc->bframes;
1264           } else {
1265             nvenc->dts_offset = frame->pts - nvenc->first_frame->pts;
1266           }
1267
1268           /* + 1 to dts_offset to adjust fraction */
1269           nvenc->dts_offset++;
1270
1271           GST_DEBUG_OBJECT (enc,
1272               "Calculated DTS offset %" GST_TIME_FORMAT,
1273               GST_TIME_ARGS (nvenc->dts_offset));
1274         }
1275
1276         nvenc->first_frame->dts -= nvenc->dts_offset;
1277         gst_video_encoder_finish_frame (enc, nvenc->first_frame);
1278         nvenc->first_frame = NULL;
1279       }
1280
1281       frame->dts -= nvenc->dts_offset;
1282     }
1283
1284     flow = gst_video_encoder_finish_frame (enc, frame);
1285
1286     if (flow != GST_FLOW_OK) {
1287       GST_INFO_OBJECT (enc, "got flow %s", gst_flow_get_name (flow));
1288       g_atomic_int_set (&nvenc->last_flow, flow);
1289       g_async_queue_push (nvenc->available_queue, SHUTDOWN_COOKIE);
1290       goto exit_thread;
1291     }
1292   }
1293   while (TRUE);
1294
1295 error_shutdown:
1296   {
1297     if (nvenc->first_frame) {
1298       gst_clear_buffer (&nvenc->first_frame->output_buffer);
1299       gst_video_encoder_finish_frame (enc, nvenc->first_frame);
1300       nvenc->first_frame = NULL;
1301     }
1302     g_atomic_int_set (&nvenc->last_flow, GST_FLOW_ERROR);
1303     g_async_queue_push (nvenc->available_queue, SHUTDOWN_COOKIE);
1304
1305     goto exit_thread;
1306   }
1307
1308 exit_thread:
1309   {
1310     if (nvenc->first_frame) {
1311       gst_video_encoder_finish_frame (enc, nvenc->first_frame);
1312       nvenc->first_frame = NULL;
1313     }
1314
1315     GST_INFO_OBJECT (nvenc, "exiting thread");
1316
1317     return NULL;
1318   }
1319 }
1320
1321 static gboolean
1322 gst_nv_base_enc_start_bitstream_thread (GstNvBaseEnc * nvenc)
1323 {
1324   gchar *name = g_strdup_printf ("%s-read-bits", GST_OBJECT_NAME (nvenc));
1325
1326   g_assert (nvenc->bitstream_thread == NULL);
1327
1328   g_assert (g_async_queue_length (nvenc->bitstream_queue) == 0);
1329
1330   nvenc->bitstream_thread =
1331       g_thread_try_new (name, gst_nv_base_enc_bitstream_thread, nvenc, NULL);
1332
1333   g_free (name);
1334
1335   if (nvenc->bitstream_thread == NULL)
1336     return FALSE;
1337
1338   GST_INFO_OBJECT (nvenc, "started thread to read bitstream");
1339   return TRUE;
1340 }
1341
1342 static gboolean
1343 gst_nv_base_enc_stop_bitstream_thread (GstNvBaseEnc * nvenc, gboolean force)
1344 {
1345   GstNvEncFrameState *state;
1346
1347   if (nvenc->bitstream_thread == NULL)
1348     return TRUE;
1349
1350   /* Always send EOS packet to flush GPU. Otherwise, randomly crash happens
1351    * during NvEncDestroyEncoder especially when rc-lookahead or bframe was
1352    * enabled */
1353   gst_nv_base_enc_drain_encoder (nvenc);
1354
1355   if (force) {
1356     g_async_queue_lock (nvenc->available_queue);
1357     g_async_queue_lock (nvenc->pending_queue);
1358     g_async_queue_lock (nvenc->bitstream_queue);
1359     while ((state = g_async_queue_try_pop_unlocked (nvenc->bitstream_queue))) {
1360       GST_INFO_OBJECT (nvenc, "stole bitstream buffer %p from queue", state);
1361       g_async_queue_push_unlocked (nvenc->available_queue, state);
1362     }
1363     g_async_queue_push_unlocked (nvenc->bitstream_queue, SHUTDOWN_COOKIE);
1364     g_async_queue_unlock (nvenc->available_queue);
1365     g_async_queue_unlock (nvenc->pending_queue);
1366     g_async_queue_unlock (nvenc->bitstream_queue);
1367   } else {
1368     /* wait for encoder to drain the remaining buffers */
1369     g_async_queue_push (nvenc->bitstream_queue, SHUTDOWN_COOKIE);
1370   }
1371
1372   if (!force) {
1373     /* temporary unlock during finish, so other thread can find and push frame */
1374     GST_VIDEO_ENCODER_STREAM_UNLOCK (nvenc);
1375   }
1376
1377   g_thread_join (nvenc->bitstream_thread);
1378
1379   if (!force)
1380     GST_VIDEO_ENCODER_STREAM_LOCK (nvenc);
1381
1382   nvenc->bitstream_thread = NULL;
1383   return TRUE;
1384 }
1385
1386 static void
1387 gst_nv_base_enc_reset_queues (GstNvBaseEnc * nvenc)
1388 {
1389   gpointer ptr;
1390
1391   GST_INFO_OBJECT (nvenc, "clearing queues");
1392
1393   while ((ptr = g_async_queue_try_pop (nvenc->available_queue))) {
1394     /* do nothing */
1395   }
1396   while ((ptr = g_async_queue_try_pop (nvenc->pending_queue))) {
1397     /* do nothing */
1398   }
1399   while ((ptr = g_async_queue_try_pop (nvenc->bitstream_queue))) {
1400     /* do nothing */
1401   }
1402 }
1403
1404 static void
1405 gst_nv_base_enc_free_buffers (GstNvBaseEnc * nvenc)
1406 {
1407   NVENCSTATUS nv_ret;
1408   CUresult cuda_ret;
1409   guint i;
1410
1411   if (nvenc->encoder == NULL)
1412     return;
1413
1414   gst_nv_base_enc_reset_queues (nvenc);
1415
1416   if (!nvenc->items || !nvenc->items->len)
1417     return;
1418
1419   gst_cuda_context_push (nvenc->cuda_ctx);
1420   for (i = 0; i < nvenc->items->len; ++i) {
1421     NV_ENC_OUTPUT_PTR out_buf =
1422         g_array_index (nvenc->items, GstNvEncFrameState, i).out_buf;
1423     GstNvEncInputResource *in_buf =
1424         g_array_index (nvenc->items, GstNvEncFrameState, i).in_buf;
1425
1426     if (in_buf->mapped) {
1427       GST_LOG_OBJECT (nvenc, "Unmap resource %p", in_buf);
1428
1429       nv_ret =
1430           NvEncUnmapInputResource (nvenc->encoder,
1431           in_buf->nv_mapped_resource.mappedResource);
1432
1433       if (nv_ret != NV_ENC_SUCCESS) {
1434         GST_ERROR_OBJECT (nvenc, "Failed to unmap input resource %p, ret %d",
1435             in_buf, nv_ret);
1436       }
1437     }
1438
1439     nv_ret =
1440         NvEncUnregisterResource (nvenc->encoder,
1441         in_buf->nv_resource.registeredResource);
1442     if (nv_ret != NV_ENC_SUCCESS)
1443       GST_ERROR_OBJECT (nvenc, "Failed to unregister resource %p, ret %d",
1444           in_buf, nv_ret);
1445
1446     cuda_ret = CuMemFree (in_buf->cuda_pointer);
1447     if (!gst_cuda_result (cuda_ret)) {
1448       GST_ERROR_OBJECT (nvenc, "Failed to free CUDA device memory, ret %d",
1449           cuda_ret);
1450     }
1451
1452     g_free (in_buf);
1453
1454     GST_DEBUG_OBJECT (nvenc, "Destroying output bitstream buffer %p", out_buf);
1455     nv_ret = NvEncDestroyBitstreamBuffer (nvenc->encoder, out_buf);
1456     if (nv_ret != NV_ENC_SUCCESS) {
1457       GST_ERROR_OBJECT (nvenc, "Failed to destroy output buffer %p, ret %d",
1458           out_buf, nv_ret);
1459     }
1460   }
1461   gst_cuda_context_pop (NULL);
1462   g_array_set_size (nvenc->items, 0);
1463 }
1464
1465 static inline guint
1466 _get_plane_width (GstVideoInfo * info, guint plane)
1467 {
1468   return GST_VIDEO_INFO_COMP_WIDTH (info, plane)
1469       * GST_VIDEO_INFO_COMP_PSTRIDE (info, plane);
1470 }
1471
1472 static inline guint
1473 _get_plane_height (GstVideoInfo * info, guint plane)
1474 {
1475   if (GST_VIDEO_INFO_IS_YUV (info))
1476     /* For now component width and plane width are the same and the
1477      * plane-component mapping matches
1478      */
1479     return GST_VIDEO_INFO_COMP_HEIGHT (info, plane);
1480   else                          /* RGB, GRAY */
1481     return GST_VIDEO_INFO_HEIGHT (info);
1482 }
1483
1484 static inline gsize
1485 _get_frame_data_height (GstVideoInfo * info)
1486 {
1487   gsize ret = 0;
1488   gint i;
1489
1490   for (i = 0; i < GST_VIDEO_INFO_N_PLANES (info); i++) {
1491     ret += _get_plane_height (info, i);
1492   }
1493
1494   return ret;
1495 }
1496
1497 static gboolean
1498 qp_has_values (const GstNvEncQP * qp)
1499 {
1500   return qp->qp_i >= 0 && qp->qp_p >= 0 && qp->qp_b >= 0;
1501 }
1502
1503 static void
1504 gst_nv_base_enc_setup_rate_control (GstNvBaseEnc * nvenc,
1505     NV_ENC_RC_PARAMS * rc_params)
1506 {
1507   GstNvRCMode rc_mode = nvenc->rate_control_mode;
1508   NV_ENC_PARAMS_RC_MODE nv_rcmode;
1509
1510   if (nvenc->bitrate)
1511     rc_params->averageBitRate = nvenc->bitrate * 1024;
1512
1513   if (nvenc->max_bitrate)
1514     rc_params->maxBitRate = nvenc->max_bitrate * 1024;
1515
1516   if (nvenc->vbv_buffersize)
1517     rc_params->vbvBufferSize = nvenc->vbv_buffersize * 1024;
1518
1519   /* Guess the best matching mode */
1520   if (rc_mode == GST_NV_RC_MODE_DEFAULT) {
1521     if (nvenc->qp_const >= 0) {
1522       /* constQP is used only for RC_CONSTQP mode */
1523       rc_mode = GST_NV_RC_MODE_CONSTQP;
1524     }
1525   }
1526
1527   if (nvenc->qp_min >= 0) {
1528     rc_params->enableMinQP = 1;
1529     rc_params->minQP.qpInterB = nvenc->qp_min;
1530     rc_params->minQP.qpInterP = nvenc->qp_min;
1531     rc_params->minQP.qpIntra = nvenc->qp_min;
1532   } else if (qp_has_values (&nvenc->qp_min_detail)) {
1533     rc_params->enableMinQP = 1;
1534     rc_params->minQP.qpInterB = nvenc->qp_min_detail.qp_b;
1535     rc_params->minQP.qpInterP = nvenc->qp_min_detail.qp_p;
1536     rc_params->minQP.qpIntra = nvenc->qp_min_detail.qp_i;
1537   }
1538
1539   if (nvenc->qp_max >= 0) {
1540     rc_params->enableMaxQP = 1;
1541     rc_params->maxQP.qpInterB = nvenc->qp_max;
1542     rc_params->maxQP.qpInterP = nvenc->qp_max;
1543     rc_params->maxQP.qpIntra = nvenc->qp_max;
1544   } else if (qp_has_values (&nvenc->qp_max_detail)) {
1545     rc_params->enableMaxQP = 1;
1546     rc_params->maxQP.qpInterB = nvenc->qp_max_detail.qp_b;
1547     rc_params->maxQP.qpInterP = nvenc->qp_max_detail.qp_p;
1548     rc_params->maxQP.qpIntra = nvenc->qp_max_detail.qp_i;
1549   }
1550
1551   if (nvenc->qp_const >= 0) {
1552     rc_params->constQP.qpInterB = nvenc->qp_const;
1553     rc_params->constQP.qpInterP = nvenc->qp_const;
1554     rc_params->constQP.qpIntra = nvenc->qp_const;
1555   } else if (qp_has_values (&nvenc->qp_const_detail)) {
1556     rc_params->constQP.qpInterB = nvenc->qp_const_detail.qp_b;
1557     rc_params->constQP.qpInterP = nvenc->qp_const_detail.qp_p;
1558     rc_params->constQP.qpIntra = nvenc->qp_const_detail.qp_i;
1559   }
1560
1561   nv_rcmode = _rc_mode_to_nv (rc_mode);
1562   if (nv_rcmode == NV_ENC_PARAMS_RC_VBR_MINQP && nvenc->qp_min < 0) {
1563     GST_WARNING_OBJECT (nvenc, "vbr-minqp was requested without qp-min");
1564     nv_rcmode = NV_ENC_PARAMS_RC_VBR;
1565   }
1566
1567   rc_params->rateControlMode = nv_rcmode;
1568
1569   if (nvenc->spatial_aq) {
1570     rc_params->enableAQ = 1;
1571     rc_params->aqStrength = nvenc->aq_strength;
1572   }
1573
1574   rc_params->enableTemporalAQ = nvenc->temporal_aq;
1575
1576   if (nvenc->rc_lookahead) {
1577     rc_params->enableLookahead = 1;
1578     rc_params->lookaheadDepth = nvenc->rc_lookahead;
1579     rc_params->disableIadapt = !nvenc->i_adapt;
1580     rc_params->disableBadapt = !nvenc->b_adapt;
1581   }
1582
1583   rc_params->strictGOPTarget = nvenc->strict_gop;
1584   rc_params->enableNonRefP = nvenc->non_refp;
1585   rc_params->zeroReorderDelay = nvenc->zerolatency;
1586
1587   if (nvenc->const_quality) {
1588     guint scaled = (gint) (nvenc->const_quality * 256.0);
1589
1590     rc_params->targetQuality = (guint8) (scaled >> 8);
1591     rc_params->targetQualityLSB = (guint8) (scaled & 0xff);
1592   }
1593 }
1594
1595 static guint
1596 gst_nv_base_enc_calculate_num_prealloc_buffers (GstNvBaseEnc * enc,
1597     NV_ENC_CONFIG * config)
1598 {
1599   guint num_buffers;
1600
1601   /* At least 4 surfaces are required as documented by Nvidia Encoder guide */
1602   num_buffers = 4;
1603
1604   /* + lookahead depth */
1605   num_buffers += config->rcParams.lookaheadDepth;
1606
1607   /* + GOP size */
1608   num_buffers += config->frameIntervalP;
1609
1610   /* hardcoded upper bound "48"
1611    * The worst case
1612    *   default num buffers: 4
1613    *   maximum allowed lookahead: 32
1614    *   max bfraems: 4 -> frameIntervalP: 5
1615    * "4 + 32 + 5" < "48" so it seems to sufficiently safe upper bound */
1616   num_buffers = MIN (num_buffers, 48);
1617
1618   GST_DEBUG_OBJECT (enc, "Calculated num buffers: %d "
1619       "(lookahead %d, frameIntervalP %d)",
1620       num_buffers, config->rcParams.lookaheadDepth, config->frameIntervalP);
1621
1622   return num_buffers;
1623 }
1624
1625 /* GstVideoEncoder::set_format or by nvenc self if new properties were set.
1626  *
1627  * NvEncReconfigureEncoder with following conditions are not allowed
1628  * 1) GOP structure change
1629  * 2) sync-Async mode change (Async mode is Windows only and we didn't support it)
1630  * 3) MaxWidth, MaxHeight
1631  * 4) PTDmode (Picture Type Decision mode)
1632  *
1633  * So we will force to re-init the encode session if
1634  * 1) New resolution is larger than previous config
1635  * 2) GOP size changed
1636  * 3) Input pixel format change
1637  *    pre-allocated CUDA memory could not ensure stride, width and height
1638  *
1639  * TODO: bframe also considered as force re-init case
1640  */
1641 static gboolean
1642 gst_nv_base_enc_set_format (GstVideoEncoder * enc, GstVideoCodecState * state)
1643 {
1644   GstNvBaseEncClass *nvenc_class = GST_NV_BASE_ENC_GET_CLASS (enc);
1645   GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (enc);
1646   GstVideoInfo *info = &state->info;
1647   GstVideoCodecState *old_state = nvenc->input_state;
1648   NV_ENC_RECONFIGURE_PARAMS reconfigure_params = { 0, };
1649   NV_ENC_INITIALIZE_PARAMS *params = &nvenc->init_params;
1650   NV_ENC_PRESET_CONFIG preset_config = { 0, };
1651   NVENCSTATUS nv_ret;
1652   gint dar_n, dar_d;
1653   gboolean reconfigure = FALSE;
1654
1655   g_atomic_int_set (&nvenc->reconfig, FALSE);
1656
1657   if (!nvenc->encoder && !gst_nv_base_enc_open_encode_session (nvenc)) {
1658     GST_ELEMENT_ERROR (nvenc, LIBRARY, INIT, (NULL),
1659         ("Failed to open encode session"));
1660     return FALSE;
1661   }
1662
1663   if (old_state) {
1664     gboolean larger_resolution;
1665     gboolean format_changed;
1666     gboolean gop_size_changed;
1667
1668     larger_resolution =
1669         (GST_VIDEO_INFO_WIDTH (info) > nvenc->init_params.maxEncodeWidth ||
1670         GST_VIDEO_INFO_HEIGHT (info) > nvenc->init_params.maxEncodeHeight);
1671     format_changed =
1672         GST_VIDEO_INFO_FORMAT (info) !=
1673         GST_VIDEO_INFO_FORMAT (&old_state->info);
1674
1675     if (nvenc->config.gopLength == NVENC_INFINITE_GOPLENGTH
1676         && nvenc->gop_size == -1) {
1677       gop_size_changed = FALSE;
1678     } else if (nvenc->config.gopLength != nvenc->gop_size) {
1679       gop_size_changed = TRUE;
1680     } else {
1681       gop_size_changed = FALSE;
1682     }
1683
1684     if (larger_resolution || format_changed || gop_size_changed) {
1685       GST_DEBUG_OBJECT (nvenc,
1686           "resolution %dx%d -> %dx%d, format %s -> %s, re-init",
1687           nvenc->init_params.maxEncodeWidth, nvenc->init_params.maxEncodeHeight,
1688           GST_VIDEO_INFO_WIDTH (info), GST_VIDEO_INFO_HEIGHT (info),
1689           gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (&old_state->info)),
1690           gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (info)));
1691
1692       gst_nv_base_enc_drain_encoder (nvenc);
1693       gst_nv_base_enc_stop_bitstream_thread (nvenc, FALSE);
1694       gst_nv_base_enc_free_buffers (nvenc);
1695       NvEncDestroyEncoder (nvenc->encoder);
1696       nvenc->encoder = NULL;
1697
1698       if (!gst_nv_base_enc_open_encode_session (nvenc)) {
1699         GST_ERROR_OBJECT (nvenc, "Failed to open encode session");
1700         return FALSE;
1701       }
1702     } else {
1703       reconfigure_params.version = gst_nvenc_get_reconfigure_params_version ();
1704       /* reset rate control state and start from IDR */
1705       reconfigure_params.resetEncoder = TRUE;
1706       reconfigure_params.forceIDR = TRUE;
1707       reconfigure = TRUE;
1708     }
1709   }
1710
1711   params->version = gst_nvenc_get_initialize_params_version ();
1712   params->encodeGUID = nvenc_class->codec_id;
1713   params->encodeWidth = GST_VIDEO_INFO_WIDTH (info);
1714   params->encodeHeight = GST_VIDEO_INFO_HEIGHT (info);
1715
1716   {
1717     guint32 n_presets;
1718     GUID *presets;
1719     guint32 i;
1720
1721     nv_ret =
1722         NvEncGetEncodePresetCount (nvenc->encoder,
1723         params->encodeGUID, &n_presets);
1724     if (nv_ret != NV_ENC_SUCCESS) {
1725       GST_ELEMENT_ERROR (nvenc, LIBRARY, SETTINGS, (NULL),
1726           ("Failed to get encoder presets"));
1727       return FALSE;
1728     }
1729
1730     presets = g_new0 (GUID, n_presets);
1731     nv_ret =
1732         NvEncGetEncodePresetGUIDs (nvenc->encoder,
1733         params->encodeGUID, presets, n_presets, &n_presets);
1734     if (nv_ret != NV_ENC_SUCCESS) {
1735       GST_ELEMENT_ERROR (nvenc, LIBRARY, SETTINGS, (NULL),
1736           ("Failed to get encoder presets"));
1737       g_free (presets);
1738       return FALSE;
1739     }
1740
1741     for (i = 0; i < n_presets; i++) {
1742       if (gst_nvenc_cmp_guid (presets[i], nvenc->selected_preset))
1743         break;
1744     }
1745     g_free (presets);
1746     if (i >= n_presets) {
1747       GST_ELEMENT_ERROR (nvenc, LIBRARY, SETTINGS, (NULL),
1748           ("Selected preset not supported"));
1749       return FALSE;
1750     }
1751
1752     params->presetGUID = nvenc->selected_preset;
1753   }
1754
1755   params->enablePTD = 1;
1756   if (!reconfigure) {
1757     /* this sets the required buffer size and the maximum allowed size on
1758      * subsequent reconfigures */
1759     params->maxEncodeWidth = GST_VIDEO_INFO_WIDTH (info);
1760     params->maxEncodeHeight = GST_VIDEO_INFO_HEIGHT (info);
1761   }
1762
1763   preset_config.version = gst_nvenc_get_preset_config_version ();
1764   preset_config.presetCfg.version = gst_nvenc_get_config_version ();
1765
1766   nv_ret =
1767       NvEncGetEncodePresetConfig (nvenc->encoder,
1768       params->encodeGUID, params->presetGUID, &preset_config);
1769   if (nv_ret != NV_ENC_SUCCESS) {
1770     GST_ELEMENT_ERROR (nvenc, LIBRARY, SETTINGS, (NULL),
1771         ("Failed to get encode preset configuration: %d", nv_ret));
1772     return FALSE;
1773   }
1774
1775   params->encodeConfig = &preset_config.presetCfg;
1776
1777   if (GST_VIDEO_INFO_IS_INTERLACED (info)) {
1778     if (GST_VIDEO_INFO_INTERLACE_MODE (info) ==
1779         GST_VIDEO_INTERLACE_MODE_INTERLEAVED
1780         || GST_VIDEO_INFO_INTERLACE_MODE (info) ==
1781         GST_VIDEO_INTERLACE_MODE_MIXED) {
1782       preset_config.presetCfg.frameFieldMode =
1783           NV_ENC_PARAMS_FRAME_FIELD_MODE_FIELD;
1784     }
1785   }
1786
1787   if (info->fps_d > 0 && info->fps_n > 0) {
1788     params->frameRateNum = info->fps_n;
1789     params->frameRateDen = info->fps_d;
1790   } else {
1791     params->frameRateNum = 0;
1792     params->frameRateDen = 1;
1793   }
1794
1795   if (gst_util_fraction_multiply (GST_VIDEO_INFO_WIDTH (info),
1796           GST_VIDEO_INFO_HEIGHT (info), GST_VIDEO_INFO_PAR_N (info),
1797           GST_VIDEO_INFO_PAR_D (info), &dar_n, &dar_d) && dar_n > 0
1798       && dar_d > 0) {
1799     params->darWidth = dar_n;
1800     params->darHeight = dar_d;
1801   }
1802
1803   gst_nv_base_enc_setup_rate_control (nvenc, &params->encodeConfig->rcParams);
1804
1805   params->enableWeightedPrediction = nvenc->weighted_pred;
1806
1807   if (nvenc->gop_size < 0) {
1808     params->encodeConfig->gopLength = NVENC_INFINITE_GOPLENGTH;
1809     params->encodeConfig->frameIntervalP = 1;
1810   } else if (nvenc->gop_size > 0) {
1811     params->encodeConfig->gopLength = nvenc->gop_size;
1812     /* frameIntervalP
1813      * 0: All Intra frames
1814      * 1: I/P only
1815      * n ( > 1): n - 1 bframes
1816      */
1817     params->encodeConfig->frameIntervalP = nvenc->bframes + 1;
1818   } else {
1819     /* gop size == 0 means all intra frames */
1820     params->encodeConfig->gopLength = 1;
1821     params->encodeConfig->frameIntervalP = 0;
1822   }
1823
1824   g_assert (nvenc_class->set_encoder_config);
1825   if (!nvenc_class->set_encoder_config (nvenc, state, params->encodeConfig)) {
1826     GST_ERROR_OBJECT (enc, "Subclass failed to set encoder configuration");
1827     return FALSE;
1828   }
1829
1830   /* store the last config to reconfig/re-init decision in the next time */
1831   nvenc->config = *params->encodeConfig;
1832
1833   G_LOCK (initialization_lock);
1834   if (reconfigure) {
1835     reconfigure_params.reInitEncodeParams = nvenc->init_params;
1836     nv_ret = NvEncReconfigureEncoder (nvenc->encoder, &reconfigure_params);
1837   } else {
1838     nv_ret = NvEncInitializeEncoder (nvenc->encoder, params);
1839   }
1840   G_UNLOCK (initialization_lock);
1841
1842   if (nv_ret != NV_ENC_SUCCESS) {
1843     GST_ELEMENT_ERROR (nvenc, LIBRARY, SETTINGS, (NULL),
1844         ("Failed to %sinit encoder: %d", reconfigure ? "re" : "", nv_ret));
1845     NvEncDestroyEncoder (nvenc->encoder);
1846     nvenc->encoder = NULL;
1847     return FALSE;
1848   }
1849
1850   if (!reconfigure) {
1851     nvenc->input_info = *info;
1852   }
1853
1854   if (nvenc->input_state)
1855     gst_video_codec_state_unref (nvenc->input_state);
1856   nvenc->input_state = gst_video_codec_state_ref (state);
1857   GST_INFO_OBJECT (nvenc, "%sconfigured encoder", reconfigure ? "re" : "");
1858
1859   /* now allocate some buffers only on first configuration */
1860   if (!reconfigure) {
1861     GstCapsFeatures *features;
1862     guint i;
1863     guint input_width, input_height;
1864     guint n_bufs;
1865
1866     input_width = GST_VIDEO_INFO_WIDTH (info);
1867     input_height = GST_VIDEO_INFO_HEIGHT (info);
1868
1869     n_bufs =
1870         gst_nv_base_enc_calculate_num_prealloc_buffers (nvenc,
1871         params->encodeConfig);
1872
1873     /* input buffers */
1874     g_array_set_size (nvenc->items, n_bufs);
1875
1876     nvenc->mem_type = GST_NVENC_MEM_TYPE_SYSTEM;
1877
1878     features = gst_caps_get_features (state->caps, 0);
1879     if (gst_caps_features_contains (features,
1880             GST_CAPS_FEATURE_MEMORY_CUDA_MEMORY)) {
1881       nvenc->mem_type = GST_NVENC_MEM_TYPE_CUDA;
1882     }
1883 #if HAVE_NVCODEC_GST_GL
1884     else if (gst_caps_features_contains (features,
1885             GST_CAPS_FEATURE_MEMORY_GL_MEMORY)) {
1886       nvenc->mem_type = GST_NVENC_MEM_TYPE_GL;
1887     }
1888 #endif
1889
1890     gst_cuda_context_push (nvenc->cuda_ctx);
1891     for (i = 0; i < nvenc->items->len; ++i) {
1892       GstNvEncInputResource *resource = g_new0 (GstNvEncInputResource, 1);
1893       CUresult cu_ret;
1894
1895       memset (&resource->nv_resource, 0, sizeof (resource->nv_resource));
1896       memset (&resource->nv_mapped_resource, 0,
1897           sizeof (resource->nv_mapped_resource));
1898
1899       /* scratch buffer for non-contiguous planer into a contiguous buffer */
1900       cu_ret =
1901           CuMemAllocPitch (&resource->cuda_pointer,
1902           &resource->cuda_stride, _get_plane_width (info, 0),
1903           _get_frame_data_height (info), 16);
1904       if (!gst_cuda_result (cu_ret)) {
1905         GST_ERROR_OBJECT (nvenc, "failed to allocate cuda scratch buffer "
1906             "ret %d", cu_ret);
1907         g_assert_not_reached ();
1908       }
1909
1910       resource->nv_resource.version =
1911           gst_nvenc_get_register_resource_version ();
1912       resource->nv_resource.resourceType =
1913           NV_ENC_INPUT_RESOURCE_TYPE_CUDADEVICEPTR;
1914       resource->nv_resource.width = input_width;
1915       resource->nv_resource.height = input_height;
1916       resource->nv_resource.pitch = resource->cuda_stride;
1917       resource->nv_resource.bufferFormat =
1918           gst_nvenc_get_nv_buffer_format (GST_VIDEO_INFO_FORMAT (info));
1919       resource->nv_resource.resourceToRegister =
1920           (gpointer) resource->cuda_pointer;
1921
1922       nv_ret = NvEncRegisterResource (nvenc->encoder, &resource->nv_resource);
1923       if (nv_ret != NV_ENC_SUCCESS)
1924         GST_ERROR_OBJECT (nvenc, "Failed to register resource %p, ret %d",
1925             resource, nv_ret);
1926
1927       g_array_index (nvenc->items, GstNvEncFrameState, i).in_buf = resource;
1928     }
1929     gst_cuda_context_pop (NULL);
1930
1931     /* output buffers */
1932     for (i = 0; i < nvenc->items->len; ++i) {
1933       NV_ENC_CREATE_BITSTREAM_BUFFER cout_buf = { 0, };
1934
1935       cout_buf.version = gst_nvenc_get_create_bitstream_buffer_version ();
1936
1937       /* 1 MB should be large enough to hold most output frames.
1938        * NVENC will automatically increase this if it's not enough. */
1939       cout_buf.size = 1024 * 1024;
1940       cout_buf.memoryHeap = NV_ENC_MEMORY_HEAP_SYSMEM_CACHED;
1941
1942       G_LOCK (initialization_lock);
1943       nv_ret = NvEncCreateBitstreamBuffer (nvenc->encoder, &cout_buf);
1944       G_UNLOCK (initialization_lock);
1945
1946       if (nv_ret != NV_ENC_SUCCESS) {
1947         GST_WARNING_OBJECT (enc, "Failed to allocate input buffer: %d", nv_ret);
1948         /* FIXME: clean up */
1949         return FALSE;
1950       }
1951
1952       GST_INFO_OBJECT (nvenc, "allocated output buffer %2d: %p", i,
1953           cout_buf.bitstreamBuffer);
1954
1955       g_array_index (nvenc->items, GstNvEncFrameState, i).out_buf =
1956           cout_buf.bitstreamBuffer;
1957
1958       g_async_queue_push (nvenc->available_queue, &g_array_index (nvenc->items,
1959               GstNvEncFrameState, i));
1960     }
1961
1962 #if 0
1963     /* Get SPS/PPS */
1964     {
1965       NV_ENC_SEQUENCE_PARAM_PAYLOAD seq_param = { 0 };
1966       uint32_t seq_size = 0;
1967
1968       seq_param.version = gst_nvenc_get_sequence_param_payload_version ();
1969       seq_param.spsppsBuffer = g_alloca (1024);
1970       seq_param.inBufferSize = 1024;
1971       seq_param.outSPSPPSPayloadSize = &seq_size;
1972
1973       nv_ret = NvEncGetSequenceParams (nvenc->encoder, &seq_param);
1974       if (nv_ret != NV_ENC_SUCCESS) {
1975         GST_WARNING_OBJECT (enc, "Failed to retrieve SPS/PPS: %d", nv_ret);
1976         return FALSE;
1977       }
1978
1979       /* FIXME: use SPS/PPS */
1980       GST_MEMDUMP_OBJECT (enc, "SPS/PPS", seq_param.spsppsBuffer, seq_size);
1981     }
1982 #endif
1983   }
1984
1985   g_assert (nvenc_class->set_src_caps);
1986   if (!nvenc_class->set_src_caps (nvenc, state)) {
1987     GST_ERROR_OBJECT (nvenc, "Subclass failed to set output caps");
1988     /* FIXME: clean up */
1989     return FALSE;
1990   }
1991
1992   return TRUE;
1993 }
1994
1995 static guint
1996 _get_cuda_device_stride (GstVideoInfo * info, guint plane, gsize cuda_stride)
1997 {
1998   switch (GST_VIDEO_INFO_FORMAT (info)) {
1999     case GST_VIDEO_FORMAT_NV12:
2000     case GST_VIDEO_FORMAT_NV21:
2001     case GST_VIDEO_FORMAT_P010_10LE:
2002     case GST_VIDEO_FORMAT_P010_10BE:
2003     case GST_VIDEO_FORMAT_Y444:
2004     case GST_VIDEO_FORMAT_BGRA:
2005     case GST_VIDEO_FORMAT_RGBA:
2006     case GST_VIDEO_FORMAT_BGR10A2_LE:
2007     case GST_VIDEO_FORMAT_RGB10A2_LE:
2008     case GST_VIDEO_FORMAT_Y444_16LE:
2009     case GST_VIDEO_FORMAT_Y444_16BE:
2010     case GST_VIDEO_FORMAT_VUYA:
2011       return cuda_stride;
2012     case GST_VIDEO_FORMAT_I420:
2013     case GST_VIDEO_FORMAT_YV12:
2014       return plane == 0 ? cuda_stride : (GST_ROUND_UP_2 (cuda_stride) / 2);
2015     default:
2016       g_assert_not_reached ();
2017       return cuda_stride;
2018   }
2019 }
2020
2021 #if HAVE_NVCODEC_GST_GL
2022 typedef struct _GstNvEncRegisterResourceData
2023 {
2024   GstMemory *mem;
2025   GstCudaGraphicsResource *resource;
2026   GstNvBaseEnc *nvenc;
2027   gboolean ret;
2028 } GstNvEncRegisterResourceData;
2029
2030 static void
2031 register_cuda_resource (GstGLContext * context,
2032     GstNvEncRegisterResourceData * data)
2033 {
2034   GstMemory *mem = data->mem;
2035   GstCudaGraphicsResource *resource = data->resource;
2036   GstNvBaseEnc *nvenc = data->nvenc;
2037   GstMapInfo map_info = GST_MAP_INFO_INIT;
2038   GstGLBuffer *gl_buf_obj;
2039
2040   data->ret = FALSE;
2041
2042   if (!gst_cuda_context_push (nvenc->cuda_ctx)) {
2043     GST_WARNING_OBJECT (nvenc, "failed to push CUDA context");
2044     return;
2045   }
2046
2047   if (gst_memory_map (mem, &map_info, GST_MAP_READ | GST_MAP_GL)) {
2048     GstGLMemoryPBO *gl_mem = (GstGLMemoryPBO *) data->mem;
2049     gl_buf_obj = gl_mem->pbo;
2050
2051     GST_LOG_OBJECT (nvenc,
2052         "register glbuffer %d to CUDA resource", gl_buf_obj->id);
2053
2054     if (gst_cuda_graphics_resource_register_gl_buffer (resource,
2055             gl_buf_obj->id, CU_GRAPHICS_REGISTER_FLAGS_NONE)) {
2056       data->ret = TRUE;
2057     } else {
2058       GST_WARNING_OBJECT (nvenc, "failed to register memory");
2059     }
2060
2061     gst_memory_unmap (mem, &map_info);
2062   } else {
2063     GST_WARNING_OBJECT (nvenc, "failed to map memory");
2064   }
2065
2066   if (!gst_cuda_context_pop (NULL))
2067     GST_WARNING_OBJECT (nvenc, "failed to unlock CUDA context");
2068 }
2069
2070 static GstCudaGraphicsResource *
2071 ensure_cuda_graphics_resource (GstMemory * mem, GstNvBaseEnc * nvenc)
2072 {
2073   GQuark quark;
2074   GstCudaGraphicsResource *cgr_info;
2075   GstNvEncRegisterResourceData data;
2076
2077   if (!gst_is_gl_memory_pbo (mem)) {
2078     GST_WARNING_OBJECT (nvenc, "memory is not GL PBO memory, %s",
2079         mem->allocator->mem_type);
2080     return NULL;
2081   }
2082
2083   quark = gst_cuda_quark_from_id (GST_CUDA_QUARK_GRAPHICS_RESOURCE);
2084
2085   cgr_info = gst_mini_object_get_qdata (GST_MINI_OBJECT (mem), quark);
2086   if (!cgr_info) {
2087     cgr_info = gst_cuda_graphics_resource_new (nvenc->cuda_ctx,
2088         GST_OBJECT (GST_GL_BASE_MEMORY_CAST (mem)->context),
2089         GST_CUDA_GRAPHICS_RESOURCE_GL_BUFFER);
2090     data.mem = mem;
2091     data.resource = cgr_info;
2092     data.nvenc = nvenc;
2093     gst_gl_context_thread_add ((GstGLContext *) cgr_info->graphics_context,
2094         (GstGLContextThreadFunc) register_cuda_resource, &data);
2095     if (!data.ret) {
2096       GST_WARNING_OBJECT (nvenc, "could not register resource");
2097       gst_cuda_graphics_resource_free (cgr_info);
2098
2099       return NULL;
2100     }
2101
2102     gst_mini_object_set_qdata (GST_MINI_OBJECT (mem), quark, cgr_info,
2103         (GDestroyNotify) gst_cuda_graphics_resource_free);
2104   }
2105
2106   return cgr_info;
2107 }
2108
2109 typedef struct _GstNvEncGLMapData
2110 {
2111   GstNvBaseEnc *nvenc;
2112   GstBuffer *buffer;
2113   GstVideoInfo *info;
2114   GstNvEncInputResource *resource;
2115
2116   gboolean ret;
2117 } GstNvEncGLMapData;
2118
2119 static void
2120 _map_gl_input_buffer (GstGLContext * context, GstNvEncGLMapData * data)
2121 {
2122   GstNvBaseEnc *nvenc = data->nvenc;
2123   CUresult cuda_ret;
2124   CUdeviceptr data_pointer;
2125   guint i;
2126   CUDA_MEMCPY2D param;
2127   GstCudaGraphicsResource **resources;
2128   guint num_resources;
2129
2130   data->ret = FALSE;
2131
2132   num_resources = gst_buffer_n_memory (data->buffer);
2133   resources = g_newa (GstCudaGraphicsResource *, num_resources);
2134
2135   for (i = 0; i < num_resources; i++) {
2136     GstMemory *mem;
2137
2138     mem = gst_buffer_peek_memory (data->buffer, i);
2139     resources[i] = ensure_cuda_graphics_resource (mem, nvenc);
2140     if (!resources[i]) {
2141       GST_ERROR_OBJECT (nvenc, "could not register %dth memory", i);
2142       return;
2143     }
2144   }
2145
2146   gst_cuda_context_push (nvenc->cuda_ctx);
2147   data_pointer = data->resource->cuda_pointer;
2148   for (i = 0; i < GST_VIDEO_INFO_N_PLANES (data->info); i++) {
2149     GstGLBuffer *gl_buf_obj;
2150     GstGLMemoryPBO *gl_mem;
2151     guint src_stride, dest_stride;
2152     CUgraphicsResource cuda_resource;
2153     gsize cuda_num_bytes;
2154     CUdeviceptr cuda_plane_pointer;
2155
2156     gl_mem = (GstGLMemoryPBO *) gst_buffer_peek_memory (data->buffer, i);
2157     g_return_if_fail (gst_is_gl_memory_pbo ((GstMemory *) gl_mem));
2158
2159     gl_buf_obj = (GstGLBuffer *) gl_mem->pbo;
2160     g_return_if_fail (gl_buf_obj != NULL);
2161
2162     /* get the texture into the PBO */
2163     gst_gl_memory_pbo_upload_transfer (gl_mem);
2164     gst_gl_memory_pbo_download_transfer (gl_mem);
2165
2166     GST_LOG_OBJECT (nvenc, "attempting to copy texture %u into cuda",
2167         gl_mem->mem.tex_id);
2168
2169     cuda_resource =
2170         gst_cuda_graphics_resource_map (resources[i], nvenc->cuda_stream,
2171         CU_GRAPHICS_MAP_RESOURCE_FLAGS_READ_ONLY);
2172
2173     if (!cuda_resource) {
2174       GST_ERROR_OBJECT (nvenc, "failed to map GL texture %u into cuda",
2175           gl_mem->mem.tex_id);
2176       g_assert_not_reached ();
2177     }
2178
2179     cuda_ret =
2180         CuGraphicsResourceGetMappedPointer (&cuda_plane_pointer,
2181         &cuda_num_bytes, cuda_resource);
2182
2183     if (!gst_cuda_result (cuda_ret)) {
2184       GST_ERROR_OBJECT (nvenc, "failed to get mapped pointer of map GL "
2185           "texture %u in cuda ret :%d", gl_mem->mem.tex_id, cuda_ret);
2186       g_assert_not_reached ();
2187     }
2188
2189     src_stride = GST_VIDEO_INFO_PLANE_STRIDE (data->info, i);
2190     dest_stride = _get_cuda_device_stride (&nvenc->input_info,
2191         i, data->resource->cuda_stride);
2192
2193     /* copy into scratch buffer */
2194     param.srcXInBytes = 0;
2195     param.srcY = 0;
2196     param.srcMemoryType = CU_MEMORYTYPE_DEVICE;
2197     param.srcDevice = cuda_plane_pointer;
2198     param.srcPitch = src_stride;
2199
2200     param.dstXInBytes = 0;
2201     param.dstY = 0;
2202     param.dstMemoryType = CU_MEMORYTYPE_DEVICE;
2203     param.dstDevice = data_pointer;
2204     param.dstPitch = dest_stride;
2205     param.WidthInBytes = _get_plane_width (data->info, i);
2206     param.Height = _get_plane_height (data->info, i);
2207
2208     cuda_ret = CuMemcpy2DAsync (&param, nvenc->cuda_stream);
2209     if (!gst_cuda_result (cuda_ret)) {
2210       GST_ERROR_OBJECT (data->nvenc, "failed to copy GL texture %u into cuda "
2211           "ret :%d", gl_mem->mem.tex_id, cuda_ret);
2212       g_assert_not_reached ();
2213     }
2214
2215     gst_cuda_graphics_resource_unmap (resources[i], nvenc->cuda_stream);
2216
2217     data_pointer += dest_stride * _get_plane_height (&nvenc->input_info, i);
2218   }
2219   gst_cuda_result (CuStreamSynchronize (nvenc->cuda_stream));
2220   gst_cuda_context_pop (NULL);
2221
2222   data->ret = TRUE;
2223 }
2224 #endif
2225
2226 static gboolean
2227 gst_nv_base_enc_upload_frame (GstNvBaseEnc * nvenc, GstVideoFrame * frame,
2228     GstNvEncInputResource * resource, gboolean use_device_memory)
2229 {
2230   gint i;
2231   CUdeviceptr dst = resource->cuda_pointer;
2232   GstVideoInfo *info = &frame->info;
2233   CUresult cuda_ret;
2234   GstCudaMemory *cuda_mem = NULL;
2235
2236   if (!gst_cuda_context_push (nvenc->cuda_ctx)) {
2237     GST_ERROR_OBJECT (nvenc, "cannot push context");
2238     return FALSE;
2239   }
2240
2241   if (use_device_memory) {
2242     cuda_mem = (GstCudaMemory *) gst_buffer_peek_memory (frame->buffer, 0);
2243   }
2244
2245   for (i = 0; i < GST_VIDEO_FRAME_N_PLANES (frame); i++) {
2246     CUDA_MEMCPY2D param = { 0, };
2247     guint dest_stride = _get_cuda_device_stride (&nvenc->input_info, i,
2248         resource->cuda_stride);
2249
2250     if (use_device_memory) {
2251       param.srcMemoryType = CU_MEMORYTYPE_DEVICE;
2252       param.srcDevice = cuda_mem->data + cuda_mem->offset[i];
2253       param.srcPitch = cuda_mem->stride;
2254     } else {
2255       param.srcMemoryType = CU_MEMORYTYPE_HOST;
2256       param.srcHost = GST_VIDEO_FRAME_PLANE_DATA (frame, i);
2257       param.srcPitch = GST_VIDEO_FRAME_PLANE_STRIDE (frame, i);
2258     }
2259
2260     param.dstMemoryType = CU_MEMORYTYPE_DEVICE;
2261     param.dstDevice = dst;
2262     param.dstPitch = dest_stride;
2263     param.WidthInBytes = _get_plane_width (info, i);
2264     param.Height = _get_plane_height (info, i);
2265
2266     cuda_ret = CuMemcpy2DAsync (&param, nvenc->cuda_stream);
2267     if (!gst_cuda_result (cuda_ret)) {
2268       GST_ERROR_OBJECT (nvenc, "cannot copy %dth plane, ret %d", i, cuda_ret);
2269       gst_cuda_context_pop (NULL);
2270
2271       return FALSE;
2272     }
2273
2274     dst += dest_stride * _get_plane_height (&nvenc->input_info, i);
2275   }
2276
2277   gst_cuda_result (CuStreamSynchronize (nvenc->cuda_stream));
2278   gst_cuda_context_pop (NULL);
2279
2280   return TRUE;
2281 }
2282
2283 static GstFlowReturn
2284 _acquire_input_buffer (GstNvBaseEnc * nvenc, GstNvEncFrameState ** input)
2285 {
2286   GST_LOG_OBJECT (nvenc, "acquiring input buffer..");
2287   GST_VIDEO_ENCODER_STREAM_UNLOCK (nvenc);
2288   *input = g_async_queue_pop (nvenc->available_queue);
2289   GST_VIDEO_ENCODER_STREAM_LOCK (nvenc);
2290
2291   if (*input == SHUTDOWN_COOKIE)
2292     return g_atomic_int_get (&nvenc->last_flow);
2293
2294   return GST_FLOW_OK;
2295 }
2296
2297 static GstFlowReturn
2298 _submit_input_buffer (GstNvBaseEnc * nvenc, GstVideoCodecFrame * frame,
2299     GstVideoFrame * vframe, GstNvEncFrameState * state, void *inputBufferPtr,
2300     NV_ENC_BUFFER_FORMAT bufferFormat)
2301 {
2302   GstNvBaseEncClass *nvenc_class = GST_NV_BASE_ENC_GET_CLASS (nvenc);
2303   NV_ENC_PIC_PARAMS pic_params = { 0, };
2304   NVENCSTATUS nv_ret;
2305   gpointer inputBuffer, outputBufferPtr;
2306
2307   inputBuffer = state->in_buf;
2308   outputBufferPtr = state->out_buf;
2309
2310   GST_LOG_OBJECT (nvenc, "%u: input buffer %p, output buffer %p, "
2311       "pts %" GST_TIME_FORMAT, frame->system_frame_number, inputBuffer,
2312       outputBufferPtr, GST_TIME_ARGS (frame->pts));
2313
2314   pic_params.version = gst_nvenc_get_pic_params_version ();
2315   pic_params.inputBuffer = inputBufferPtr;
2316   pic_params.bufferFmt = bufferFormat;
2317
2318   pic_params.inputWidth = GST_VIDEO_FRAME_WIDTH (vframe);
2319   pic_params.inputHeight = GST_VIDEO_FRAME_HEIGHT (vframe);
2320   pic_params.outputBitstream = outputBufferPtr;
2321   pic_params.completionEvent = NULL;
2322   if (GST_VIDEO_FRAME_IS_INTERLACED (vframe)) {
2323     if (GST_VIDEO_FRAME_IS_TFF (vframe))
2324       pic_params.pictureStruct = NV_ENC_PIC_STRUCT_FIELD_TOP_BOTTOM;
2325     else
2326       pic_params.pictureStruct = NV_ENC_PIC_STRUCT_FIELD_BOTTOM_TOP;
2327   } else {
2328     pic_params.pictureStruct = NV_ENC_PIC_STRUCT_FRAME;
2329   }
2330   pic_params.inputTimeStamp = frame->pts;
2331   pic_params.inputDuration =
2332       GST_CLOCK_TIME_IS_VALID (frame->duration) ? frame->duration : 0;
2333   pic_params.frameIdx = frame->system_frame_number;
2334
2335   if (GST_VIDEO_CODEC_FRAME_IS_FORCE_KEYFRAME (frame))
2336     pic_params.encodePicFlags = NV_ENC_PIC_FLAG_FORCEIDR;
2337   else
2338     pic_params.encodePicFlags = 0;
2339
2340   if (nvenc_class->set_pic_params
2341       && !nvenc_class->set_pic_params (nvenc, frame, &pic_params)) {
2342     GST_ERROR_OBJECT (nvenc, "Subclass failed to submit buffer");
2343     return GST_FLOW_ERROR;
2344   }
2345
2346   if (!gst_cuda_context_push (nvenc->cuda_ctx)) {
2347     GST_ELEMENT_ERROR (nvenc, LIBRARY, ENCODE, (NULL),
2348         ("Failed to push current context"));
2349     return GST_FLOW_ERROR;
2350   }
2351
2352   nv_ret = NvEncEncodePicture (nvenc->encoder, &pic_params);
2353
2354   gst_cuda_context_pop (NULL);
2355
2356   if (nv_ret == NV_ENC_SUCCESS) {
2357     GST_LOG_OBJECT (nvenc, "Encoded picture");
2358   } else if (nv_ret == NV_ENC_ERR_NEED_MORE_INPUT) {
2359     GST_DEBUG_OBJECT (nvenc, "Encoded picture (encoder needs more input)");
2360   } else {
2361     GST_ERROR_OBJECT (nvenc, "Failed to encode picture: %d", nv_ret);
2362     g_async_queue_push (nvenc->available_queue, state);
2363
2364     return GST_FLOW_ERROR;
2365   }
2366
2367   /* GstNvEncFrameState shouldn't be freed by DestroyNotify */
2368   gst_video_codec_frame_set_user_data (frame, state, NULL);
2369   g_async_queue_push (nvenc->pending_queue, state);
2370
2371   if (nv_ret == NV_ENC_SUCCESS) {
2372     GstNvEncFrameState *pending_state;
2373     gint len, i, end;
2374
2375     /* HACK: NvEncEncodePicture() with returning NV_ENC_SUCCESS means that
2376      * we can pop encoded bitstream from GPU
2377      * (via NvEncLockBitstream and copy to memory then NvEncUnlockBitstream).
2378      * But if we try to pop every buffer from GPU when the rc-lookahead
2379      * was enabled, NvEncLockBitstream returns error NV_ENC_ERR_INVALID_PARAM
2380      * randomly (seemingly it's dependent on how fast the encoding thread
2381      * dequeued the encoded picture).
2382      * So make "pending_queue" having the number of lookahead pictures always,
2383      * so that GPU should be able to reference the lookahead pictures.
2384      *
2385      * This behavior is not documented by Nvidia. The guess here is that
2386      * the lookahead pictures are still used for rate-control by Nvidia driver
2387      * and dequeuing the lookahead picture from GPU seems to be causing the
2388      * problem.
2389      */
2390     end = nvenc->rc_lookahead;
2391
2392     g_async_queue_lock (nvenc->pending_queue);
2393
2394     len = g_async_queue_length_unlocked (nvenc->pending_queue);
2395     for (i = len; i > end; i--) {
2396       pending_state = g_async_queue_pop_unlocked (nvenc->pending_queue);
2397       g_async_queue_push (nvenc->bitstream_queue, pending_state);
2398     }
2399
2400     g_async_queue_unlock (nvenc->pending_queue);
2401   }
2402
2403   return GST_FLOW_OK;
2404 }
2405
2406 static GstFlowReturn
2407 gst_nv_base_enc_handle_frame (GstVideoEncoder * enc, GstVideoCodecFrame * frame)
2408 {
2409   GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (enc);
2410   NVENCSTATUS nv_ret;
2411   GstVideoFrame vframe;
2412   GstVideoInfo *info = &nvenc->input_state->info;
2413   GstFlowReturn flow = GST_FLOW_OK;
2414   GstMapFlags in_map_flags = GST_MAP_READ;
2415   GstNvEncFrameState *state = NULL;
2416   GstNvEncInputResource *resource = NULL;
2417   gboolean use_device_memory = FALSE;
2418
2419   g_assert (nvenc->encoder != NULL);
2420
2421   /* check last flow and if it's not OK, just return the last flow,
2422    * non-OK flow means that encoding thread was terminated */
2423   flow = g_atomic_int_get (&nvenc->last_flow);
2424   if (flow != GST_FLOW_OK) {
2425     GST_DEBUG_OBJECT (nvenc, "last flow was %s", gst_flow_get_name (flow));
2426     /* just drop this frame */
2427     gst_video_encoder_finish_frame (enc, frame);
2428
2429     return flow;
2430   }
2431
2432   if (g_atomic_int_compare_and_exchange (&nvenc->reconfig, TRUE, FALSE)) {
2433     if (!gst_nv_base_enc_set_format (enc, nvenc->input_state)) {
2434       flow = GST_FLOW_NOT_NEGOTIATED;
2435       goto drop;
2436     }
2437
2438     /* reconfigured encode session should start from keyframe */
2439     GST_VIDEO_CODEC_FRAME_SET_FORCE_KEYFRAME (frame);
2440   }
2441 #if HAVE_NVCODEC_GST_GL
2442   if (nvenc->mem_type == GST_NVENC_MEM_TYPE_GL)
2443     in_map_flags |= GST_MAP_GL;
2444 #endif
2445
2446   if (nvenc->mem_type == GST_NVENC_MEM_TYPE_CUDA) {
2447     GstMemory *mem;
2448
2449     if ((mem = gst_buffer_peek_memory (frame->input_buffer, 0)) &&
2450         gst_is_cuda_memory (mem)) {
2451       GstCudaMemory *cmem = GST_CUDA_MEMORY_CAST (mem);
2452
2453       if (cmem->context == nvenc->cuda_ctx ||
2454           gst_cuda_context_get_handle (cmem->context) ==
2455           gst_cuda_context_get_handle (nvenc->cuda_ctx) ||
2456           (gst_cuda_context_can_access_peer (cmem->context, nvenc->cuda_ctx) &&
2457               gst_cuda_context_can_access_peer (nvenc->cuda_ctx,
2458                   cmem->context))) {
2459         use_device_memory = TRUE;
2460         in_map_flags |= GST_MAP_CUDA;
2461       }
2462     }
2463   }
2464
2465   if (!gst_video_frame_map (&vframe, info, frame->input_buffer, in_map_flags)) {
2466     goto drop;
2467   }
2468
2469   /* make sure our thread that waits for output to be ready is started */
2470   if (nvenc->bitstream_thread == NULL) {
2471     if (!gst_nv_base_enc_start_bitstream_thread (nvenc)) {
2472       gst_video_frame_unmap (&vframe);
2473       goto unmap_and_drop;
2474     }
2475   }
2476
2477   flow = _acquire_input_buffer (nvenc, &state);
2478   if (flow != GST_FLOW_OK || state == SHUTDOWN_COOKIE || !state)
2479     goto unmap_and_drop;
2480
2481   resource = state->in_buf;
2482
2483 #if HAVE_NVCODEC_GST_GL
2484   if (nvenc->mem_type == GST_NVENC_MEM_TYPE_GL) {
2485     GstGLMemory *gl_mem;
2486     GstNvEncGLMapData data;
2487
2488     gl_mem = (GstGLMemory *) gst_buffer_peek_memory (frame->input_buffer, 0);
2489     g_assert (gst_is_gl_memory ((GstMemory *) gl_mem));
2490
2491     data.nvenc = nvenc;
2492     data.buffer = frame->input_buffer;
2493     data.info = &vframe.info;
2494     data.resource = resource;
2495
2496     gst_gl_context_thread_add (gl_mem->mem.context,
2497         (GstGLContextThreadFunc) _map_gl_input_buffer, &data);
2498     if (!data.ret) {
2499       flow = GST_FLOW_ERROR;
2500       goto unmap_and_drop;
2501     }
2502   } else
2503 #endif
2504   if (!gst_nv_base_enc_upload_frame (nvenc,
2505           &vframe, resource, use_device_memory)) {
2506     flow = GST_FLOW_ERROR;
2507     goto unmap_and_drop;
2508   }
2509
2510   resource->nv_mapped_resource.version =
2511       gst_nvenc_get_map_input_resource_version ();
2512   resource->nv_mapped_resource.registeredResource =
2513       resource->nv_resource.registeredResource;
2514
2515   if (!gst_cuda_context_push (nvenc->cuda_ctx)) {
2516     GST_ELEMENT_ERROR (nvenc, LIBRARY, ENCODE, (NULL),
2517         ("Failed to push current context"));
2518     flow = GST_FLOW_ERROR;
2519     goto unmap_and_drop;
2520   }
2521
2522   nv_ret =
2523       NvEncMapInputResource (nvenc->encoder, &resource->nv_mapped_resource);
2524   gst_cuda_context_pop (NULL);
2525
2526   if (nv_ret != NV_ENC_SUCCESS) {
2527     GST_ERROR_OBJECT (nvenc, "Failed to map input resource %p, ret %d",
2528         resource, nv_ret);
2529     flow = GST_FLOW_ERROR;
2530     goto unmap_and_drop;
2531   }
2532
2533   resource->mapped = TRUE;
2534
2535   flow =
2536       _submit_input_buffer (nvenc, frame, &vframe, state,
2537       resource->nv_mapped_resource.mappedResource,
2538       resource->nv_mapped_resource.mappedBufferFmt);
2539
2540   if (flow != GST_FLOW_OK) {
2541     GST_DEBUG_OBJECT (nvenc, "return state to pool");
2542     g_async_queue_push (nvenc->available_queue, state);
2543     goto unmap_and_drop;
2544   }
2545
2546   flow = g_atomic_int_get (&nvenc->last_flow);
2547
2548   gst_video_frame_unmap (&vframe);
2549   /* encoder will keep frame in list internally, we'll look it up again later
2550    * in the thread where we get the output buffers and finish it there */
2551   gst_video_codec_frame_unref (frame);
2552
2553   return flow;
2554
2555 /* ERRORS */
2556 unmap_and_drop:
2557   {
2558     gst_video_frame_unmap (&vframe);
2559     goto drop;
2560   }
2561 drop:
2562   {
2563     gst_video_encoder_finish_frame (enc, frame);
2564     return flow;
2565   }
2566 }
2567
2568 static gboolean
2569 gst_nv_base_enc_drain_encoder (GstNvBaseEnc * nvenc)
2570 {
2571   NV_ENC_PIC_PARAMS pic_params = { 0, };
2572   NVENCSTATUS nv_ret;
2573   gboolean ret = TRUE;
2574
2575   GST_INFO_OBJECT (nvenc, "draining encoder");
2576
2577   if (nvenc->input_state == NULL) {
2578     GST_DEBUG_OBJECT (nvenc, "no input state, nothing to do");
2579     return TRUE;
2580   }
2581
2582   if (!nvenc->encoder) {
2583     GST_DEBUG_OBJECT (nvenc, "no configured encode session");
2584     return TRUE;
2585   }
2586
2587   pic_params.version = gst_nvenc_get_pic_params_version ();
2588   pic_params.encodePicFlags = NV_ENC_PIC_FLAG_EOS;
2589
2590   if (!gst_cuda_context_push (nvenc->cuda_ctx)) {
2591     GST_ERROR_OBJECT (nvenc, "Could not push context");
2592     return GST_FLOW_ERROR;
2593   }
2594
2595   nv_ret = NvEncEncodePicture (nvenc->encoder, &pic_params);
2596
2597   if (nv_ret != NV_ENC_SUCCESS) {
2598     GST_LOG_OBJECT (nvenc, "Failed to drain encoder, ret %d", nv_ret);
2599
2600     ret = FALSE;
2601   } else {
2602     GstNvEncFrameState *pending_state;
2603
2604     g_async_queue_lock (nvenc->pending_queue);
2605     while ((pending_state =
2606             g_async_queue_try_pop_unlocked (nvenc->pending_queue))) {
2607       g_async_queue_push (nvenc->bitstream_queue, pending_state);
2608     }
2609     g_async_queue_unlock (nvenc->pending_queue);
2610   }
2611
2612   gst_cuda_context_pop (NULL);
2613
2614   return ret;
2615 }
2616
2617 static GstFlowReturn
2618 gst_nv_base_enc_finish (GstVideoEncoder * enc)
2619 {
2620   GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (enc);
2621
2622   gst_nv_base_enc_stop_bitstream_thread (nvenc, FALSE);
2623
2624   return GST_FLOW_OK;
2625 }
2626
2627 #if 0
2628 static gboolean
2629 gst_nv_base_enc_flush (GstVideoEncoder * enc)
2630 {
2631   GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (enc);
2632   GST_INFO_OBJECT (nvenc, "done flushing encoder");
2633   return TRUE;
2634 }
2635 #endif
2636
2637 void
2638 gst_nv_base_enc_schedule_reconfig (GstNvBaseEnc * nvenc)
2639 {
2640   g_atomic_int_set (&nvenc->reconfig, TRUE);
2641 }
2642
2643 static void
2644 gst_nv_base_enc_set_property (GObject * object, guint prop_id,
2645     const GValue * value, GParamSpec * pspec)
2646 {
2647   GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (object);
2648   GstNvBaseEncClass *klass = GST_NV_BASE_ENC_GET_CLASS (nvenc);
2649   gboolean reconfig = TRUE;
2650
2651   switch (prop_id) {
2652     case PROP_PRESET:
2653       nvenc->preset_enum = g_value_get_enum (value);
2654       nvenc->selected_preset = _nv_preset_to_guid (nvenc->preset_enum);
2655       gst_nv_base_enc_schedule_reconfig (nvenc);
2656       break;
2657     case PROP_RC_MODE:
2658     {
2659       GstNvRCMode rc_mode = g_value_get_enum (value);
2660       NV_ENC_PARAMS_RC_MODE nv_rc_mode = _rc_mode_to_nv (rc_mode);
2661
2662       if ((klass->device_caps.rc_modes & nv_rc_mode) == nv_rc_mode) {
2663         nvenc->rate_control_mode = rc_mode;
2664       } else {
2665         GST_WARNING_OBJECT (nvenc,
2666             "device does not support requested rate control mode %d", rc_mode);
2667         reconfig = FALSE;
2668       }
2669       break;
2670     }
2671     case PROP_QP_MIN:
2672       nvenc->qp_min = g_value_get_int (value);
2673       break;
2674     case PROP_QP_MAX:
2675       nvenc->qp_max = g_value_get_int (value);
2676       break;
2677     case PROP_QP_CONST:
2678       nvenc->qp_const = g_value_get_int (value);
2679       break;
2680     case PROP_BITRATE:
2681       nvenc->bitrate = g_value_get_uint (value);
2682       break;
2683     case PROP_GOP_SIZE:
2684       nvenc->gop_size = g_value_get_int (value);
2685       break;
2686     case PROP_MAX_BITRATE:
2687       nvenc->max_bitrate = g_value_get_uint (value);
2688       break;
2689     case PROP_SPATIAL_AQ:
2690       nvenc->spatial_aq = g_value_get_boolean (value);
2691       break;
2692     case PROP_AQ_STRENGTH:
2693       nvenc->aq_strength = g_value_get_uint (value);
2694       break;
2695     case PROP_NON_REF_P:
2696       nvenc->non_refp = g_value_get_boolean (value);
2697       break;
2698     case PROP_ZEROLATENCY:
2699       nvenc->zerolatency = g_value_get_boolean (value);
2700       break;
2701     case PROP_STRICT_GOP:
2702       nvenc->strict_gop = g_value_get_boolean (value);
2703       break;
2704     case PROP_CONST_QUALITY:
2705       nvenc->const_quality = g_value_get_double (value);
2706       break;
2707     case PROP_I_ADAPT:
2708       nvenc->i_adapt = g_value_get_boolean (value);
2709       break;
2710     case PROP_QP_MIN_I:
2711       nvenc->qp_min_detail.qp_i = g_value_get_int (value);
2712       break;
2713     case PROP_QP_MIN_P:
2714       nvenc->qp_min_detail.qp_p = g_value_get_int (value);
2715       break;
2716     case PROP_QP_MIN_B:
2717       nvenc->qp_min_detail.qp_b = g_value_get_int (value);
2718       break;
2719     case PROP_QP_MAX_I:
2720       nvenc->qp_max_detail.qp_i = g_value_get_int (value);
2721       break;
2722     case PROP_QP_MAX_P:
2723       nvenc->qp_max_detail.qp_p = g_value_get_int (value);
2724       break;
2725     case PROP_QP_MAX_B:
2726       nvenc->qp_max_detail.qp_b = g_value_get_int (value);
2727       break;
2728     case PROP_QP_CONST_I:
2729       nvenc->qp_const_detail.qp_i = g_value_get_int (value);
2730       break;
2731     case PROP_QP_CONST_P:
2732       nvenc->qp_const_detail.qp_p = g_value_get_int (value);
2733       break;
2734     case PROP_QP_CONST_B:
2735       nvenc->qp_const_detail.qp_b = g_value_get_int (value);
2736       break;
2737     default:
2738       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2739       reconfig = FALSE;
2740       break;
2741   }
2742
2743   if (reconfig)
2744     gst_nv_base_enc_schedule_reconfig (nvenc);
2745 }
2746
2747 static void
2748 gst_nv_base_enc_get_property (GObject * object, guint prop_id, GValue * value,
2749     GParamSpec * pspec)
2750 {
2751   GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (object);
2752   GstNvBaseEncClass *nvenc_class = GST_NV_BASE_ENC_GET_CLASS (object);
2753
2754   switch (prop_id) {
2755     case PROP_DEVICE_ID:
2756       g_value_set_uint (value, nvenc_class->cuda_device_id);
2757       break;
2758     case PROP_PRESET:
2759       g_value_set_enum (value, nvenc->preset_enum);
2760       break;
2761     case PROP_RC_MODE:
2762       g_value_set_enum (value, nvenc->rate_control_mode);
2763       break;
2764     case PROP_QP_MIN:
2765       g_value_set_int (value, nvenc->qp_min);
2766       break;
2767     case PROP_QP_MAX:
2768       g_value_set_int (value, nvenc->qp_max);
2769       break;
2770     case PROP_QP_CONST:
2771       g_value_set_int (value, nvenc->qp_const);
2772       break;
2773     case PROP_BITRATE:
2774       g_value_set_uint (value, nvenc->bitrate);
2775       break;
2776     case PROP_GOP_SIZE:
2777       g_value_set_int (value, nvenc->gop_size);
2778       break;
2779     case PROP_MAX_BITRATE:
2780       g_value_set_uint (value, nvenc->max_bitrate);
2781       break;
2782     case PROP_SPATIAL_AQ:
2783       g_value_set_boolean (value, nvenc->spatial_aq);
2784       break;
2785     case PROP_AQ_STRENGTH:
2786       g_value_set_uint (value, nvenc->aq_strength);
2787       break;
2788     case PROP_NON_REF_P:
2789       g_value_set_boolean (value, nvenc->non_refp);
2790       break;
2791     case PROP_ZEROLATENCY:
2792       g_value_set_boolean (value, nvenc->zerolatency);
2793       break;
2794     case PROP_STRICT_GOP:
2795       g_value_set_boolean (value, nvenc->strict_gop);
2796       break;
2797     case PROP_CONST_QUALITY:
2798       g_value_set_double (value, nvenc->const_quality);
2799       break;
2800     case PROP_I_ADAPT:
2801       g_value_set_boolean (value, nvenc->i_adapt);
2802       break;
2803     case PROP_QP_MIN_I:
2804       g_value_set_int (value, nvenc->qp_min_detail.qp_i);
2805       break;
2806     case PROP_QP_MIN_P:
2807       g_value_set_int (value, nvenc->qp_min_detail.qp_p);
2808       break;
2809     case PROP_QP_MIN_B:
2810       g_value_set_int (value, nvenc->qp_min_detail.qp_b);
2811       break;
2812     case PROP_QP_MAX_I:
2813       g_value_set_int (value, nvenc->qp_max_detail.qp_i);
2814       break;
2815     case PROP_QP_MAX_P:
2816       g_value_set_int (value, nvenc->qp_max_detail.qp_p);
2817       break;
2818     case PROP_QP_MAX_B:
2819       g_value_set_int (value, nvenc->qp_max_detail.qp_b);
2820       break;
2821     case PROP_QP_CONST_I:
2822       g_value_set_int (value, nvenc->qp_const_detail.qp_i);
2823       break;
2824     case PROP_QP_CONST_P:
2825       g_value_set_int (value, nvenc->qp_const_detail.qp_p);
2826       break;
2827     case PROP_QP_CONST_B:
2828       g_value_set_int (value, nvenc->qp_const_detail.qp_b);
2829       break;
2830     default:
2831       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2832       break;
2833   }
2834 }
2835
2836 typedef struct
2837 {
2838   guint cuda_device_id;
2839   GstNvEncDeviceCaps device_caps;
2840 } GstNvEncClassData;
2841
2842 static void
2843 gst_nv_base_enc_subclass_init (gpointer g_class, gpointer data)
2844 {
2845   GstNvBaseEncClass *nvbaseenc_class = GST_NV_BASE_ENC_CLASS (g_class);
2846   GstNvEncClassData *cdata = (GstNvEncClassData *) data;
2847
2848   nvbaseenc_class->cuda_device_id = cdata->cuda_device_id;
2849   nvbaseenc_class->device_caps = cdata->device_caps;
2850
2851   g_free (cdata);
2852 }
2853
2854 GType
2855 gst_nv_base_enc_register (const char *codec, guint device_id,
2856     GstNvEncDeviceCaps * device_caps)
2857 {
2858   GTypeQuery type_query;
2859   GTypeInfo type_info = { 0, };
2860   GType subtype;
2861   gchar *type_name;
2862   GstNvEncClassData *cdata;
2863
2864   type_name = g_strdup_printf ("GstNvDevice%d%sEnc", device_id, codec);
2865   subtype = g_type_from_name (type_name);
2866
2867   /* has already registered nvdeviceenc class */
2868   if (subtype)
2869     goto done;
2870
2871   cdata = g_new0 (GstNvEncClassData, 1);
2872   cdata->cuda_device_id = device_id;
2873   cdata->device_caps = *device_caps;
2874
2875   g_type_query (GST_TYPE_NV_BASE_ENC, &type_query);
2876   memset (&type_info, 0, sizeof (type_info));
2877   type_info.class_size = type_query.class_size;
2878   type_info.instance_size = type_query.instance_size;
2879   type_info.class_init = (GClassInitFunc) gst_nv_base_enc_subclass_init;
2880   type_info.class_data = cdata;
2881
2882   subtype = g_type_register_static (GST_TYPE_NV_BASE_ENC,
2883       type_name, &type_info, 0);
2884
2885   gst_type_mark_as_plugin_api (subtype, 0);
2886
2887 done:
2888   g_free (type_name);
2889   return subtype;
2890 }