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
gst_nv_preset_get_type(void)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
_nv_preset_to_guid(GstNvPreset preset)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
gst_nv_rc_mode_get_type(void)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
_rc_mode_to_nv(GstNvRCMode 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
gst_nv_base_enc_class_init(GstNvBaseEncClass * klass)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
gst_nv_base_enc_open_encode_session(GstNvBaseEnc * nvenc)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 (¶ms, &nvenc->encoder);
464
465 return nv_ret == NV_ENC_SUCCESS;
466 }
467
468 static gboolean
gst_nv_base_enc_open(GstVideoEncoder * enc)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
gst_nv_base_enc_set_context(GstElement * element,GstContext * context)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
gst_nv_base_enc_sink_query(GstVideoEncoder * enc,GstQuery * query)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
gst_nv_base_enc_ensure_gl_context(GstNvBaseEnc * nvenc)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
gst_nv_base_enc_propose_allocation(GstVideoEncoder * enc,GstQuery * query)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, 0);
671
672 gst_query_add_allocation_pool (query, pool, GST_VIDEO_INFO_SIZE (&info),
673 nvenc->items->len, 0);
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
gst_nv_base_enc_sink_event(GstVideoEncoder * enc,GstEvent * event)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
gst_nv_base_enc_start(GstVideoEncoder * enc)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
gst_nv_base_enc_stop(GstVideoEncoder * enc)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
check_formats(const gchar * str,guint * max_chroma,guint * max_bit_minus8)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
gst_nv_base_enc_set_filtered_input_formats(GstNvBaseEnc * nvenc,GstCaps * caps,const GValue * input_formats,guint max_chroma,guint max_bit_minus8)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 *
gst_nv_base_enc_getcaps(GstVideoEncoder * enc,GstCaps * filter)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
gst_nv_base_enc_close(GstVideoEncoder * enc)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
gst_nv_base_enc_init(GstNvBaseEnc * nvenc)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
gst_nv_base_enc_finalize(GObject * obj)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 *
_find_frame_with_output_buffer(GstNvBaseEnc * nvenc,NV_ENC_OUTPUT_PTR out_buf)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 *
picture_type_to_string(NV_ENC_PIC_TYPE type)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
gst_nv_base_enc_bitstream_thread(gpointer user_data)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
gst_nv_base_enc_start_bitstream_thread(GstNvBaseEnc * nvenc)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
gst_nv_base_enc_stop_bitstream_thread(GstNvBaseEnc * nvenc,gboolean force)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
gst_nv_base_enc_reset_queues(GstNvBaseEnc * nvenc)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
gst_nv_base_enc_free_buffers(GstNvBaseEnc * nvenc)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
_get_plane_width(GstVideoInfo * info,guint plane)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
_get_plane_height(GstVideoInfo * info,guint plane)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
_get_frame_data_height(GstVideoInfo * info)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
qp_has_values(const GstNvEncQP * qp)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
gst_nv_base_enc_setup_rate_control(GstNvBaseEnc * nvenc,NV_ENC_RC_PARAMS * rc_params)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
gst_nv_base_enc_calculate_num_prealloc_buffers(GstNvBaseEnc * enc,NV_ENC_CONFIG * config)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
gst_nv_base_enc_set_format(GstVideoEncoder * enc,GstVideoCodecState * state)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, ¶ms->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
_get_cuda_device_stride(GstVideoInfo * info,guint plane,gsize cuda_stride)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
register_cuda_resource(GstGLContext * context,GstNvEncRegisterResourceData * data)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 *
ensure_cuda_graphics_resource(GstMemory * mem,GstNvBaseEnc * nvenc)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
_map_gl_input_buffer(GstGLContext * context,GstNvEncGLMapData * data)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 (¶m, 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
gst_nv_base_enc_upload_frame(GstNvBaseEnc * nvenc,GstVideoFrame * frame,GstNvEncInputResource * resource,gboolean use_device_memory)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 (¶m, 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
_acquire_input_buffer(GstNvBaseEnc * nvenc,GstNvEncFrameState ** input)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
_submit_input_buffer(GstNvBaseEnc * nvenc,GstVideoCodecFrame * frame,GstVideoFrame * vframe,GstNvEncFrameState * state,void * inputBufferPtr,NV_ENC_BUFFER_FORMAT bufferFormat)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
gst_nv_base_enc_handle_frame(GstVideoEncoder * enc,GstVideoCodecFrame * frame)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
gst_nv_base_enc_drain_encoder(GstNvBaseEnc * nvenc)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
gst_nv_base_enc_finish(GstVideoEncoder * enc)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
gst_nv_base_enc_schedule_reconfig(GstNvBaseEnc * nvenc)2638 gst_nv_base_enc_schedule_reconfig (GstNvBaseEnc * nvenc)
2639 {
2640 g_atomic_int_set (&nvenc->reconfig, TRUE);
2641 }
2642
2643 static void
gst_nv_base_enc_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)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
gst_nv_base_enc_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)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
gst_nv_base_enc_subclass_init(gpointer g_class,gpointer data)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
gst_nv_base_enc_register(const char * codec,guint device_id,GstNvEncDeviceCaps * device_caps)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 }
2891