1 /* GStreamer NVENC plugin
2 * Copyright (C) 2015 Centricular Ltd
3 * Copyright (C) 2018 Seungha Yang <pudding8757@gmail.com>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include "gstnvh265enc.h"
26
27 #include <gst/pbutils/codec-utils.h>
28 #include <gst/base/gstbytewriter.h>
29
30 #include <string.h>
31
32 typedef struct
33 {
34 GstCaps *sink_caps;
35 GstCaps *src_caps;
36 gboolean is_default;
37 } GstNvH265EncClassData;
38
39 GST_DEBUG_CATEGORY_STATIC (gst_nv_h265_enc_debug);
40 #define GST_CAT_DEFAULT gst_nv_h265_enc_debug
41
42 static GstElementClass *parent_class = NULL;
43
44 enum
45 {
46 PROP_0,
47 PROP_AUD,
48 PROP_WEIGHTED_PRED,
49 PROP_VBV_BUFFER_SIZE,
50 PROP_RC_LOOKAHEAD,
51 PROP_TEMPORAL_AQ,
52 PROP_BFRAMES,
53 PROP_B_ADAPT,
54 };
55
56 #define DEFAULT_AUD TRUE
57 #define DEFAULT_WEIGHTED_PRED FALSE
58 #define DEFAULT_VBV_BUFFER_SIZE 0
59 #define DEFAULT_RC_LOOKAHEAD 0
60 #define DEFAULT_TEMPORAL_AQ FALSE
61 #define DEFAULT_BFRAMES 0
62 #define DEFAULT_B_ADAPT FALSE
63
64 /* captured using RTX 2080 */
65 #define DOCUMENTATION_SINK_CAPS_COMM \
66 "format = (string) { NV12, P010_10LE, P016_LE, Y444, Y444_16LE, Y444_16LE }, " \
67 "width = (int) [ 144, 8192 ], " \
68 "height = (int) [ 144, 8192 ], " \
69 "framerate = " GST_VIDEO_FPS_RANGE
70
71 #define DOCUMENTATION_SINK_CAPS \
72 "video/x-raw, " DOCUMENTATION_SINK_CAPS_COMM "; " \
73 "video/x-raw(memory:GLMemory), " DOCUMENTATION_SINK_CAPS_COMM "; " \
74 "video/x-raw(memory:CUDAMemory), " DOCUMENTATION_SINK_CAPS_COMM
75
76 #define DOCUMENTATION_SRC_CAPS \
77 "video/x-h265, " \
78 "width = (int) [ 144, 8192 ], " \
79 "height = (int) [ 144, 8192 ], " \
80 "stream-format = (string) byte-stream, " \
81 "alignment = (string) au, " \
82 "profile = (string) { main, main-10, main-12, main-444, main-444-10, main-444-12 }"
83
84 static gboolean gst_nv_h265_enc_open (GstVideoEncoder * enc);
85 static gboolean gst_nv_h265_enc_close (GstVideoEncoder * enc);
86 static gboolean gst_nv_h265_enc_stop (GstVideoEncoder * enc);
87 static gboolean gst_nv_h265_enc_set_src_caps (GstNvBaseEnc * nvenc,
88 GstVideoCodecState * state);
89 static gboolean gst_nv_h265_enc_set_encoder_config (GstNvBaseEnc * nvenc,
90 GstVideoCodecState * state, NV_ENC_CONFIG * config);
91 static gboolean gst_nv_h265_enc_set_pic_params (GstNvBaseEnc * nvenc,
92 GstVideoCodecFrame * frame, NV_ENC_PIC_PARAMS * pic_params);
93 static void gst_nv_h265_enc_set_property (GObject * object, guint prop_id,
94 const GValue * value, GParamSpec * pspec);
95 static void gst_nv_h265_enc_get_property (GObject * object, guint prop_id,
96 GValue * value, GParamSpec * pspec);
97 static void gst_nv_h265_enc_finalize (GObject * obj);
98
99 static void
gst_nv_h265_enc_class_init(GstNvH265EncClass * klass,gpointer data)100 gst_nv_h265_enc_class_init (GstNvH265EncClass * klass, gpointer data)
101 {
102 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
103 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
104 GstVideoEncoderClass *videoenc_class = GST_VIDEO_ENCODER_CLASS (klass);
105 GstNvBaseEncClass *nvenc_class = GST_NV_BASE_ENC_CLASS (klass);
106 GstNvEncDeviceCaps *device_caps = &nvenc_class->device_caps;
107 GstNvH265EncClassData *cdata = (GstNvH265EncClassData *) data;
108 gchar *long_name;
109 GstPadTemplate *pad_templ;
110 GstCaps *doc_caps;
111
112 parent_class = g_type_class_peek_parent (klass);
113
114 gobject_class->set_property = gst_nv_h265_enc_set_property;
115 gobject_class->get_property = gst_nv_h265_enc_get_property;
116 gobject_class->finalize = gst_nv_h265_enc_finalize;
117
118 videoenc_class->open = GST_DEBUG_FUNCPTR (gst_nv_h265_enc_open);
119 videoenc_class->close = GST_DEBUG_FUNCPTR (gst_nv_h265_enc_close);
120 videoenc_class->stop = GST_DEBUG_FUNCPTR (gst_nv_h265_enc_stop);
121
122 nvenc_class->codec_id = NV_ENC_CODEC_HEVC_GUID;
123 nvenc_class->set_encoder_config = gst_nv_h265_enc_set_encoder_config;
124 nvenc_class->set_src_caps = gst_nv_h265_enc_set_src_caps;
125 nvenc_class->set_pic_params = gst_nv_h265_enc_set_pic_params;
126
127 /**
128 * GstNvH265Enc:aud:
129 *
130 * Use AU (Access Unit) delimiter
131 *
132 * Since: 1.18
133 */
134 g_object_class_install_property (gobject_class, PROP_AUD,
135 g_param_spec_boolean ("aud", "AUD",
136 "Use AU (Access Unit) delimiter", DEFAULT_AUD,
137 G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
138 G_PARAM_STATIC_STRINGS));
139
140 if (device_caps->weighted_prediction) {
141 /**
142 * GstNvH265Enc:weighted-pred:
143 *
144 * Weighted Prediction
145 *
146 * Since: 1.18
147 */
148 g_object_class_install_property (gobject_class, PROP_WEIGHTED_PRED,
149 g_param_spec_boolean ("weighted-pred", "Weighted Pred",
150 "Weighted Prediction", DEFAULT_WEIGHTED_PRED,
151 G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
152 GST_PARAM_CONDITIONALLY_AVAILABLE | G_PARAM_STATIC_STRINGS));
153 }
154
155 if (device_caps->custom_vbv_bufsize) {
156 /**
157 * GstNvH265Enc:vbv-buffer-size:
158 *
159 * VBV(HRD) Buffer Size in kbits (0 = NVENC default)
160 *
161 * Since: 1.18
162 */
163 g_object_class_install_property (gobject_class,
164 PROP_VBV_BUFFER_SIZE,
165 g_param_spec_uint ("vbv-buffer-size", "VBV Buffer Size",
166 "VBV(HRD) Buffer Size in kbits (0 = NVENC default)",
167 0, G_MAXUINT, DEFAULT_VBV_BUFFER_SIZE,
168 G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY |
169 GST_PARAM_CONDITIONALLY_AVAILABLE | G_PARAM_STATIC_STRINGS));
170 }
171
172 if (device_caps->lookahead) {
173 /**
174 * GstNvH265Enc:rc-lookahead:
175 *
176 * Number of frames for frame type lookahead
177 *
178 * Since: 1.18
179 */
180 g_object_class_install_property (gobject_class, PROP_RC_LOOKAHEAD,
181 g_param_spec_uint ("rc-lookahead", "Rate Control Lookahead",
182 "Number of frames for frame type lookahead", 0, 32,
183 DEFAULT_RC_LOOKAHEAD,
184 G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY |
185 GST_PARAM_CONDITIONALLY_AVAILABLE | G_PARAM_STATIC_STRINGS));
186 }
187
188 if (device_caps->temporal_aq) {
189 /**
190 * GstNvH265Enc:temporal-aq:
191 *
192 * Temporal Adaptive Quantization
193 *
194 * Since: 1.18
195 */
196 g_object_class_install_property (gobject_class, PROP_TEMPORAL_AQ,
197 g_param_spec_boolean ("temporal-aq", "Temporal AQ",
198 "Temporal Adaptive Quantization", DEFAULT_TEMPORAL_AQ,
199 G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
200 GST_PARAM_CONDITIONALLY_AVAILABLE | G_PARAM_STATIC_STRINGS));
201 }
202
203 if (device_caps->bframes > 0) {
204 /**
205 * GstNvH265Enc:bframes:
206 *
207 * Number of B-frames between I and P
208 *
209 * Since: 1.18
210 */
211 g_object_class_install_property (gobject_class, PROP_BFRAMES,
212 g_param_spec_uint ("bframes", "B-Frames",
213 "Number of B-frames between I and P", 0, device_caps->bframes,
214 DEFAULT_BFRAMES,
215 G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY |
216 GST_PARAM_CONDITIONALLY_AVAILABLE | G_PARAM_STATIC_STRINGS));
217
218 /**
219 * GstNvH265Enc:b-adapt:
220 *
221 * Enable adaptive B-frame insert when lookahead is enabled
222 *
223 * Since: 1.18
224 */
225 g_object_class_install_property (gobject_class, PROP_B_ADAPT,
226 g_param_spec_boolean ("b-adapt", "B Adapt",
227 "Enable adaptive B-frame insert when lookahead is enabled",
228 DEFAULT_B_ADAPT,
229 G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY |
230 GST_PARAM_CONDITIONALLY_AVAILABLE | G_PARAM_STATIC_STRINGS));
231 }
232
233 if (cdata->is_default)
234 long_name = g_strdup ("NVENC HEVC Video Encoder");
235 else
236 long_name = g_strdup_printf ("NVENC HEVC Video Encoder with device %d",
237 nvenc_class->cuda_device_id);
238
239 gst_element_class_set_metadata (element_class, long_name,
240 "Codec/Encoder/Video/Hardware",
241 "Encode HEVC video streams using NVIDIA's hardware-accelerated NVENC encoder API",
242 "Tim-Philipp Müller <tim@centricular.com>, "
243 "Matthew Waters <matthew@centricular.com>, "
244 "Seungha Yang <pudding8757@gmail.com>");
245 g_free (long_name);
246
247 GST_DEBUG_CATEGORY_INIT (gst_nv_h265_enc_debug,
248 "nvh265enc", 0, "Nvidia HEVC encoder");
249
250 pad_templ = gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
251 cdata->sink_caps);
252 doc_caps = gst_caps_from_string (DOCUMENTATION_SINK_CAPS);
253 gst_pad_template_set_documentation_caps (pad_templ, doc_caps);
254 gst_caps_unref (doc_caps);
255 gst_element_class_add_pad_template (element_class, pad_templ);
256
257 pad_templ = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
258 cdata->src_caps);
259 doc_caps = gst_caps_from_string (DOCUMENTATION_SRC_CAPS);
260 gst_pad_template_set_documentation_caps (pad_templ, doc_caps);
261 gst_caps_unref (doc_caps);
262 gst_element_class_add_pad_template (element_class, pad_templ);
263
264
265 gst_caps_unref (cdata->sink_caps);
266 gst_caps_unref (cdata->src_caps);
267 g_free (cdata);
268 }
269
270 static void
gst_nv_h265_enc_init(GstNvH265Enc * nvenc)271 gst_nv_h265_enc_init (GstNvH265Enc * nvenc)
272 {
273 GstNvBaseEnc *baseenc = GST_NV_BASE_ENC (nvenc);
274
275 nvenc->aud = DEFAULT_AUD;
276
277 /* device capability dependent properties */
278 baseenc->weighted_pred = DEFAULT_WEIGHTED_PRED;
279 baseenc->vbv_buffersize = DEFAULT_VBV_BUFFER_SIZE;
280 baseenc->rc_lookahead = DEFAULT_RC_LOOKAHEAD;
281 baseenc->temporal_aq = DEFAULT_TEMPORAL_AQ;
282 baseenc->bframes = DEFAULT_BFRAMES;
283 baseenc->b_adapt = DEFAULT_B_ADAPT;
284 }
285
286 static void
gst_nv_h265_enc_finalize(GObject * obj)287 gst_nv_h265_enc_finalize (GObject * obj)
288 {
289 G_OBJECT_CLASS (parent_class)->finalize (obj);
290 }
291
292 static gboolean
gst_nv_h265_enc_open(GstVideoEncoder * enc)293 gst_nv_h265_enc_open (GstVideoEncoder * enc)
294 {
295 GstNvBaseEnc *base = GST_NV_BASE_ENC (enc);
296
297 if (!GST_VIDEO_ENCODER_CLASS (parent_class)->open (enc))
298 return FALSE;
299
300 /* Check if HEVC is supported */
301 {
302 uint32_t i, num = 0;
303 GUID guids[16];
304
305 NvEncGetEncodeGUIDs (base->encoder, guids, G_N_ELEMENTS (guids), &num);
306
307 for (i = 0; i < num; ++i) {
308 if (gst_nvenc_cmp_guid (guids[i], NV_ENC_CODEC_HEVC_GUID))
309 break;
310 }
311 GST_INFO_OBJECT (enc, "HEVC encoding %ssupported", (i == num) ? "un" : "");
312 if (i == num) {
313 gst_nv_h265_enc_close (enc);
314 return FALSE;
315 }
316 }
317
318 return TRUE;
319 }
320
321 static gboolean
gst_nv_h265_enc_close(GstVideoEncoder * enc)322 gst_nv_h265_enc_close (GstVideoEncoder * enc)
323 {
324 return GST_VIDEO_ENCODER_CLASS (parent_class)->close (enc);
325 }
326
327 static void
gst_nv_h265_enc_clear_stream_data(GstNvH265Enc * h265enc)328 gst_nv_h265_enc_clear_stream_data (GstNvH265Enc * h265enc)
329 {
330 gint i;
331
332 if (!h265enc->sei_payload)
333 return;
334
335 for (i = 0; i < h265enc->num_sei_payload; i++)
336 g_free (h265enc->sei_payload[i].payload);
337
338 g_free (h265enc->sei_payload);
339 h265enc->sei_payload = NULL;
340 h265enc->num_sei_payload = 0;
341 }
342
343 static gboolean
gst_nv_h265_enc_stop(GstVideoEncoder * enc)344 gst_nv_h265_enc_stop (GstVideoEncoder * enc)
345 {
346 GstNvH265Enc *h265enc = (GstNvH265Enc *) enc;
347
348 gst_nv_h265_enc_clear_stream_data (h265enc);
349
350 return GST_VIDEO_ENCODER_CLASS (parent_class)->stop (enc);
351 }
352
353 static gboolean
gst_nv_h265_enc_set_level_tier_and_profile(GstNvH265Enc * nvenc,GstCaps * caps)354 gst_nv_h265_enc_set_level_tier_and_profile (GstNvH265Enc * nvenc,
355 GstCaps * caps)
356 {
357 #define N_BYTES_VPS 128
358 guint8 vps[N_BYTES_VPS];
359 NV_ENC_SEQUENCE_PARAM_PAYLOAD spp = { 0, };
360 NVENCSTATUS nv_ret;
361 guint32 seq_size;
362
363 spp.version = gst_nvenc_get_sequence_param_payload_version ();
364 spp.inBufferSize = N_BYTES_VPS;
365 spp.spsId = 0;
366 spp.ppsId = 0;
367 spp.spsppsBuffer = &vps;
368 spp.outSPSPPSPayloadSize = &seq_size;
369 nv_ret = NvEncGetSequenceParams (GST_NV_BASE_ENC (nvenc)->encoder, &spp);
370 if (nv_ret != NV_ENC_SUCCESS) {
371 GST_ELEMENT_ERROR (nvenc, STREAM, ENCODE, ("Encode header failed."),
372 ("NvEncGetSequenceParams return code=%d", nv_ret));
373 return FALSE;
374 }
375
376 if (seq_size < 8) {
377 GST_ELEMENT_ERROR (nvenc, STREAM, ENCODE, ("Encode header failed."),
378 ("NvEncGetSequenceParams returned incomplete data"));
379 return FALSE;
380 }
381
382 GST_MEMDUMP ("Header", spp.spsppsBuffer, seq_size);
383
384 /* skip nal header and identifier */
385 gst_codec_utils_h265_caps_set_level_tier_and_profile (caps,
386 &vps[6], seq_size - 6);
387
388 return TRUE;
389
390 #undef N_BYTES_VPS
391 }
392
393 static gboolean
gst_nv_h265_enc_set_src_caps(GstNvBaseEnc * nvenc,GstVideoCodecState * state)394 gst_nv_h265_enc_set_src_caps (GstNvBaseEnc * nvenc, GstVideoCodecState * state)
395 {
396 GstNvH265Enc *h265enc = (GstNvH265Enc *) nvenc;
397 GstVideoCodecState *out_state;
398 GstStructure *s;
399 GstCaps *out_caps;
400
401 out_caps = gst_caps_new_empty_simple ("video/x-h265");
402 s = gst_caps_get_structure (out_caps, 0);
403
404 /* TODO: add support for hvc1,hev1 format as well */
405 gst_structure_set (s, "stream-format", G_TYPE_STRING, "byte-stream",
406 "alignment", G_TYPE_STRING, "au", NULL);
407
408 if (!gst_nv_h265_enc_set_level_tier_and_profile (h265enc, out_caps)) {
409 gst_caps_unref (out_caps);
410 return FALSE;
411 }
412
413 out_state = gst_video_encoder_set_output_state (GST_VIDEO_ENCODER (nvenc),
414 out_caps, state);
415
416 GST_INFO_OBJECT (nvenc, "output caps: %" GST_PTR_FORMAT, out_state->caps);
417
418 /* encoder will keep it around for us */
419 gst_video_codec_state_unref (out_state);
420
421 /* TODO: would be nice to also send some tags with the codec name */
422 return TRUE;
423 }
424
425 static guint8 *
gst_nv_h265_enc_create_mastering_display_sei_nal(GstNvH265Enc * h265enc,GstVideoMasteringDisplayInfo * minfo,guint * size)426 gst_nv_h265_enc_create_mastering_display_sei_nal (GstNvH265Enc * h265enc,
427 GstVideoMasteringDisplayInfo * minfo, guint * size)
428 {
429 guint sei_size;
430 gint i;
431 GstByteWriter br;
432
433 GST_LOG_OBJECT (h265enc, "Apply mastering display info: "
434 "Red(%u, %u) "
435 "Green(%u, %u) "
436 "Blue(%u, %u) "
437 "White(%u, %u) "
438 "max_luminance(%u) "
439 "min_luminance(%u) ",
440 minfo->display_primaries[0].x, minfo->display_primaries[0].y,
441 minfo->display_primaries[1].x, minfo->display_primaries[1].y,
442 minfo->display_primaries[2].x, minfo->display_primaries[2].y,
443 minfo->white_point.x, minfo->white_point.y,
444 minfo->max_display_mastering_luminance,
445 minfo->min_display_mastering_luminance);
446
447 /* x, y 16bits per RGB channel
448 * x, y 16bits white point
449 * max, min luminance 32bits
450 */
451 sei_size = (2 * 2 * 3) + (2 * 2) + (4 * 2);
452 gst_byte_writer_init_with_size (&br, sei_size, TRUE);
453
454 /* GstVideoMasteringDisplayInfo::display_primaries is rgb order but
455 * HEVC uses gbr order
456 * See spec D.3.28 display_primaries_x and display_primaries_y
457 */
458 for (i = 0; i < 3; i++) {
459 gst_byte_writer_put_uint16_be (&br,
460 minfo->display_primaries[(i + 1) % 3].x);
461 gst_byte_writer_put_uint16_be (&br,
462 minfo->display_primaries[(i + 1) % 3].y);
463 }
464
465 gst_byte_writer_put_uint16_be (&br, minfo->white_point.x);
466 gst_byte_writer_put_uint16_be (&br, minfo->white_point.y);
467
468 gst_byte_writer_put_uint32_be (&br, minfo->max_display_mastering_luminance);
469 gst_byte_writer_put_uint32_be (&br, minfo->min_display_mastering_luminance);
470
471 *size = sei_size;
472
473 return gst_byte_writer_reset_and_get_data (&br);
474 }
475
476 static guint8 *
gst_nv_h265_enc_create_content_light_level_sei_nal(GstNvH265Enc * h265enc,GstVideoContentLightLevel * linfo,guint * size)477 gst_nv_h265_enc_create_content_light_level_sei_nal (GstNvH265Enc * h265enc,
478 GstVideoContentLightLevel * linfo, guint * size)
479 {
480 guint sei_size;
481 GstByteWriter br;
482
483 GST_LOG_OBJECT (h265enc, "Apply content light level: "
484 "maxCLL:(%u), maxFALL:(%u)", linfo->max_content_light_level,
485 linfo->max_frame_average_light_level);
486
487 /* maxCLL and maxFALL per 16bits */
488 sei_size = 2 * 2;
489 gst_byte_writer_init_with_size (&br, sei_size, TRUE);
490
491 gst_byte_writer_put_uint16_be (&br, linfo->max_content_light_level);
492 gst_byte_writer_put_uint16_be (&br, linfo->max_frame_average_light_level);
493
494 *size = sei_size;
495
496 return gst_byte_writer_reset_and_get_data (&br);
497 }
498
499 static gboolean
gst_nv_h265_enc_set_encoder_config(GstNvBaseEnc * nvenc,GstVideoCodecState * state,NV_ENC_CONFIG * config)500 gst_nv_h265_enc_set_encoder_config (GstNvBaseEnc * nvenc,
501 GstVideoCodecState * state, NV_ENC_CONFIG * config)
502 {
503 GstNvH265Enc *h265enc = (GstNvH265Enc *) nvenc;
504 GstCaps *allowed_caps, *template_caps;
505 GUID selected_profile = NV_ENC_CODEC_PROFILE_AUTOSELECT_GUID;
506 int level_idc = NV_ENC_LEVEL_AUTOSELECT;
507 GstVideoInfo *info = &state->info;
508 NV_ENC_CONFIG_HEVC *hevc_config = &config->encodeCodecConfig.hevcConfig;
509 NV_ENC_CONFIG_HEVC_VUI_PARAMETERS *vui = &hevc_config->hevcVUIParameters;
510
511 template_caps =
512 gst_pad_get_pad_template_caps (GST_VIDEO_ENCODER_SRC_PAD (h265enc));
513 allowed_caps = gst_pad_get_allowed_caps (GST_VIDEO_ENCODER_SRC_PAD (h265enc));
514
515 if (template_caps == allowed_caps) {
516 GST_INFO_OBJECT (h265enc, "downstream has ANY caps");
517 } else if (allowed_caps) {
518 GstStructure *s;
519 const gchar *profile;
520 const gchar *level;
521
522 if (gst_caps_is_empty (allowed_caps)) {
523 gst_caps_unref (allowed_caps);
524 gst_caps_unref (template_caps);
525 return FALSE;
526 }
527
528 allowed_caps = gst_caps_make_writable (allowed_caps);
529 allowed_caps = gst_caps_fixate (allowed_caps);
530 s = gst_caps_get_structure (allowed_caps, 0);
531
532 profile = gst_structure_get_string (s, "profile");
533 /* FIXME: only support main profile only for now */
534 if (profile) {
535 if (!strcmp (profile, "main")) {
536 selected_profile = NV_ENC_HEVC_PROFILE_MAIN_GUID;
537 } else if (g_str_has_prefix (profile, "main-10")) {
538 selected_profile = NV_ENC_HEVC_PROFILE_MAIN10_GUID;
539 } else if (g_str_has_prefix (profile, "main-444")) {
540 selected_profile = NV_ENC_HEVC_PROFILE_FREXT_GUID;
541 } else {
542 g_assert_not_reached ();
543 }
544 }
545
546 level = gst_structure_get_string (s, "level");
547 if (level)
548 /* matches values stored in NV_ENC_LEVEL */
549 level_idc = gst_codec_utils_h265_get_level_idc (level);
550
551 gst_caps_unref (allowed_caps);
552 }
553 gst_caps_unref (template_caps);
554
555 /* override some defaults */
556 GST_LOG_OBJECT (h265enc, "setting parameters");
557 config->profileGUID = selected_profile;
558 hevc_config->level = level_idc;
559 hevc_config->idrPeriod = config->gopLength;
560
561 config->encodeCodecConfig.hevcConfig.chromaFormatIDC = 1;
562 if (GST_VIDEO_INFO_FORMAT (info) == GST_VIDEO_FORMAT_Y444 ||
563 GST_VIDEO_INFO_FORMAT (info) == GST_VIDEO_FORMAT_Y444_16LE ||
564 GST_VIDEO_INFO_FORMAT (info) == GST_VIDEO_FORMAT_Y444_16BE ||
565 GST_VIDEO_INFO_FORMAT (info) == GST_VIDEO_FORMAT_VUYA) {
566 GST_DEBUG_OBJECT (h265enc, "have Y444 input, setting config accordingly");
567 config->profileGUID = NV_ENC_HEVC_PROFILE_FREXT_GUID;
568 config->encodeCodecConfig.hevcConfig.chromaFormatIDC = 3;
569 if (GST_VIDEO_INFO_FORMAT (info) == GST_VIDEO_FORMAT_Y444_16LE ||
570 GST_VIDEO_INFO_FORMAT (info) == GST_VIDEO_FORMAT_Y444_16BE)
571 config->encodeCodecConfig.hevcConfig.pixelBitDepthMinus8 = 2;
572 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
573 } else if (GST_VIDEO_INFO_FORMAT (info) == GST_VIDEO_FORMAT_P010_10LE) {
574 #else
575 } else if (GST_VIDEO_INFO_FORMAT (info) == GST_VIDEO_FORMAT_P010_10BE) {
576 #endif
577 config->profileGUID = NV_ENC_HEVC_PROFILE_MAIN10_GUID;
578 config->encodeCodecConfig.hevcConfig.pixelBitDepthMinus8 = 2;
579 }
580
581 hevc_config->outputAUD = h265enc->aud;
582
583 vui->videoSignalTypePresentFlag = 1;
584 /* NOTE: vui::video_format represents the video format before
585 * being encoded such as PAL, NTSC, SECAM, and MAC. That's not much informal
586 * and can be inferred with resolution and framerate by any application.
587 */
588 /* Unspecified video format (5) */
589 vui->videoFormat = 5;
590
591 if (info->colorimetry.range == GST_VIDEO_COLOR_RANGE_0_255) {
592 vui->videoFullRangeFlag = 1;
593 } else {
594 vui->videoFullRangeFlag = 0;
595 }
596
597 vui->colourDescriptionPresentFlag = 1;
598 vui->colourMatrix = gst_video_color_matrix_to_iso (info->colorimetry.matrix);
599 vui->colourPrimaries =
600 gst_video_color_primaries_to_iso (info->colorimetry.primaries);
601 vui->transferCharacteristics =
602 gst_video_transfer_function_to_iso (info->colorimetry.transfer);
603
604 gst_nv_h265_enc_clear_stream_data (h265enc);
605
606 {
607 GstVideoMasteringDisplayInfo minfo;
608 GstVideoContentLightLevel linfo;
609 gboolean have_mastering;
610 gboolean have_cll;
611 guint size;
612 gint i = 0;
613
614 have_mastering =
615 gst_video_mastering_display_info_from_caps (&minfo, state->caps);
616 have_cll = gst_video_content_light_level_from_caps (&linfo, state->caps);
617
618 if (have_mastering)
619 h265enc->num_sei_payload++;
620 if (have_cll)
621 h265enc->num_sei_payload++;
622
623 h265enc->sei_payload =
624 g_new0 (NV_ENC_SEI_PAYLOAD, h265enc->num_sei_payload);
625
626 if (have_mastering) {
627 h265enc->sei_payload[i].payload =
628 gst_nv_h265_enc_create_mastering_display_sei_nal (h265enc,
629 &minfo, &size);
630 h265enc->sei_payload[i].payloadSize = size;
631 h265enc->sei_payload[i].payloadType = 137;
632 i++;
633 }
634
635 if (have_cll) {
636 h265enc->sei_payload[i].payload =
637 gst_nv_h265_enc_create_content_light_level_sei_nal (h265enc,
638 &linfo, &size);
639 h265enc->sei_payload[i].payloadSize = size;
640 h265enc->sei_payload[i].payloadType = 144;
641 }
642 }
643
644 return TRUE;
645 }
646
647 static gboolean
gst_nv_h265_enc_set_pic_params(GstNvBaseEnc * enc,GstVideoCodecFrame * frame,NV_ENC_PIC_PARAMS * pic_params)648 gst_nv_h265_enc_set_pic_params (GstNvBaseEnc * enc, GstVideoCodecFrame * frame,
649 NV_ENC_PIC_PARAMS * pic_params)
650 {
651 GstNvH265Enc *h265enc = (GstNvH265Enc *) enc;
652
653 /* encode whole picture in one single slice */
654 pic_params->codecPicParams.hevcPicParams.sliceMode = 0;
655 pic_params->codecPicParams.hevcPicParams.sliceModeData = 0;
656
657 if (h265enc->sei_payload) {
658 pic_params->codecPicParams.hevcPicParams.seiPayloadArray =
659 h265enc->sei_payload;
660 pic_params->codecPicParams.hevcPicParams.seiPayloadArrayCnt =
661 h265enc->num_sei_payload;
662 }
663
664 return TRUE;
665 }
666
667 static void
gst_nv_h265_enc_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)668 gst_nv_h265_enc_set_property (GObject * object, guint prop_id,
669 const GValue * value, GParamSpec * pspec)
670 {
671 GstNvH265Enc *self = (GstNvH265Enc *) object;
672 GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (object);
673 GstNvBaseEncClass *klass = GST_NV_BASE_ENC_GET_CLASS (object);
674 GstNvEncDeviceCaps *device_caps = &klass->device_caps;
675 gboolean reconfig = FALSE;
676
677 switch (prop_id) {
678 case PROP_AUD:
679 {
680 gboolean aud;
681
682 aud = g_value_get_boolean (value);
683 if (aud != self->aud) {
684 self->aud = aud;
685 reconfig = TRUE;
686 }
687 break;
688 }
689 case PROP_WEIGHTED_PRED:
690 if (!device_caps->weighted_prediction) {
691 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
692 } else {
693 nvenc->weighted_pred = g_value_get_boolean (value);
694 reconfig = TRUE;
695 }
696 break;
697 case PROP_VBV_BUFFER_SIZE:
698 if (!device_caps->custom_vbv_bufsize) {
699 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
700 } else {
701 nvenc->vbv_buffersize = g_value_get_uint (value);
702 reconfig = TRUE;
703 }
704 break;
705 case PROP_RC_LOOKAHEAD:
706 if (!device_caps->lookahead) {
707 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
708 } else {
709 nvenc->rc_lookahead = g_value_get_uint (value);
710 reconfig = TRUE;
711 }
712 break;
713 case PROP_TEMPORAL_AQ:
714 if (!device_caps->temporal_aq) {
715 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
716 } else {
717 nvenc->temporal_aq = g_value_get_boolean (value);
718 reconfig = TRUE;
719 }
720 break;
721 case PROP_BFRAMES:
722 if (!device_caps->bframes) {
723 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
724 } else {
725 nvenc->bframes = g_value_get_uint (value);
726 reconfig = TRUE;
727 }
728 break;
729 case PROP_B_ADAPT:
730 if (!device_caps->bframes) {
731 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
732 } else {
733 nvenc->b_adapt = g_value_get_boolean (value);
734 }
735 break;
736 default:
737 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
738 break;
739 }
740
741 if (reconfig)
742 gst_nv_base_enc_schedule_reconfig (GST_NV_BASE_ENC (self));
743 }
744
745 static void
gst_nv_h265_enc_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)746 gst_nv_h265_enc_get_property (GObject * object, guint prop_id, GValue * value,
747 GParamSpec * pspec)
748 {
749 GstNvH265Enc *self = (GstNvH265Enc *) object;
750 GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (object);
751 GstNvBaseEncClass *klass = GST_NV_BASE_ENC_GET_CLASS (object);
752 GstNvEncDeviceCaps *device_caps = &klass->device_caps;
753
754 switch (prop_id) {
755 case PROP_AUD:
756 g_value_set_boolean (value, self->aud);
757 break;
758 case PROP_WEIGHTED_PRED:
759 if (!device_caps->weighted_prediction) {
760 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
761 } else {
762 g_value_set_boolean (value, nvenc->weighted_pred);
763 }
764 break;
765 case PROP_VBV_BUFFER_SIZE:
766 if (!device_caps->custom_vbv_bufsize) {
767 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
768 } else {
769 g_value_set_uint (value, nvenc->vbv_buffersize);
770 }
771 break;
772 case PROP_RC_LOOKAHEAD:
773 if (!device_caps->lookahead) {
774 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
775 } else {
776 g_value_set_uint (value, nvenc->rc_lookahead);
777 }
778 break;
779 case PROP_TEMPORAL_AQ:
780 if (!device_caps->temporal_aq) {
781 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
782 } else {
783 g_value_set_boolean (value, nvenc->temporal_aq);
784 }
785 break;
786 case PROP_BFRAMES:
787 if (!device_caps->bframes) {
788 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
789 } else {
790 g_value_set_uint (value, nvenc->bframes);
791 }
792 break;
793 case PROP_B_ADAPT:
794 if (!device_caps->bframes) {
795 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
796 } else {
797 g_value_set_boolean (value, nvenc->b_adapt);
798 }
799 break;
800 default:
801 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
802 break;
803 }
804 }
805
806 void
gst_nv_h265_enc_register(GstPlugin * plugin,guint device_id,guint rank,GstCaps * sink_caps,GstCaps * src_caps,GstNvEncDeviceCaps * device_caps)807 gst_nv_h265_enc_register (GstPlugin * plugin, guint device_id, guint rank,
808 GstCaps * sink_caps, GstCaps * src_caps, GstNvEncDeviceCaps * device_caps)
809 {
810 GType parent_type;
811 GType type;
812 gchar *type_name;
813 gchar *feature_name;
814 GstNvH265EncClassData *cdata;
815 gboolean is_default = TRUE;
816 GTypeInfo type_info = {
817 sizeof (GstNvH265EncClass),
818 NULL,
819 NULL,
820 (GClassInitFunc) gst_nv_h265_enc_class_init,
821 NULL,
822 NULL,
823 sizeof (GstNvH265Enc),
824 0,
825 (GInstanceInitFunc) gst_nv_h265_enc_init,
826 };
827
828 parent_type = gst_nv_base_enc_register ("H265", device_id, device_caps);
829
830 cdata = g_new0 (GstNvH265EncClassData, 1);
831 cdata->sink_caps = gst_caps_ref (sink_caps);
832 cdata->src_caps = gst_caps_ref (src_caps);
833 type_info.class_data = cdata;
834 /* class data will be leaked if the element never gets instantiated */
835 GST_MINI_OBJECT_FLAG_SET (sink_caps, GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED);
836 GST_MINI_OBJECT_FLAG_SET (src_caps, GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED);
837
838 type_name = g_strdup ("GstNvH265Enc");
839 feature_name = g_strdup ("nvh265enc");
840
841 if (g_type_from_name (type_name) != 0) {
842 g_free (type_name);
843 g_free (feature_name);
844 type_name = g_strdup_printf ("GstNvH265Device%dEnc", device_id);
845 feature_name = g_strdup_printf ("nvh265device%denc", device_id);
846 is_default = FALSE;
847 }
848
849 cdata->is_default = is_default;
850 type = g_type_register_static (parent_type, type_name, &type_info, 0);
851
852 /* make lower rank than default device */
853 if (rank > 0 && !is_default)
854 rank--;
855
856 if (!gst_element_register (plugin, feature_name, rank, type))
857 GST_WARNING ("Failed to register plugin '%s'", type_name);
858
859 g_free (type_name);
860 g_free (feature_name);
861 }
862