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 "gstnvh264enc.h"
25
26 #include <gst/pbutils/codec-utils.h>
27
28 #include <string.h>
29
30 typedef struct
31 {
32 GstCaps *sink_caps;
33 GstCaps *src_caps;
34 gboolean is_default;
35 } GstNvH264EncClassData;
36
37 GST_DEBUG_CATEGORY_STATIC (gst_nv_h264_enc_debug);
38 #define GST_CAT_DEFAULT gst_nv_h264_enc_debug
39
40 static GstElementClass *parent_class = NULL;
41
42 enum
43 {
44 PROP_0,
45 PROP_AUD,
46 PROP_WEIGHTED_PRED,
47 PROP_VBV_BUFFER_SIZE,
48 PROP_RC_LOOKAHEAD,
49 PROP_TEMPORAL_AQ,
50 PROP_BFRAMES,
51 PROP_B_ADAPT,
52 };
53
54 #define DEFAULT_AUD TRUE
55 #define DEFAULT_WEIGHTED_PRED FALSE
56 #define DEFAULT_VBV_BUFFER_SIZE 0
57 #define DEFAULT_RC_LOOKAHEAD 0
58 #define DEFAULT_TEMPORAL_AQ FALSE
59 #define DEFAULT_BFRAMES 0
60 #define DEFAULT_B_ADAPT FALSE
61
62 /* captured using RTX 2080 */
63 #define DOCUMENTATION_SINK_CAPS_COMM \
64 "format = (string) { NV12, YV12, I420, BGRA, RGBA, Y444, VUYA }, " \
65 "width = (int) [ 145, 4096 ], " \
66 "height = (int) [ 49, 4096 ], " \
67 "framerate = " GST_VIDEO_FPS_RANGE ", " \
68 "interlace-mode = (string) { progressive } "
69
70 #define DOCUMENTATION_SINK_CAPS \
71 "video/x-raw, " DOCUMENTATION_SINK_CAPS_COMM "; " \
72 "video/x-raw(memory:GLMemory), " DOCUMENTATION_SINK_CAPS_COMM "; " \
73 "video/x-raw(memory:CUDAMemory), " DOCUMENTATION_SINK_CAPS_COMM
74
75 #define DOCUMENTATION_SRC_CAPS \
76 "video/x-h264, " \
77 "width = (int) [ 145, 4096 ], " \
78 "height = (int) [ 49, 4096 ], " \
79 "framerate = " GST_VIDEO_FPS_RANGE ", " \
80 "stream-format = (string) byte-stream, " \
81 "alignment = (string) au, " \
82 "profile = (string) { main, high, high-4:4:4, baseline, constrained-baseline }"
83
84 static gboolean gst_nv_h264_enc_open (GstVideoEncoder * enc);
85 static gboolean gst_nv_h264_enc_close (GstVideoEncoder * enc);
86 static gboolean gst_nv_h264_enc_set_src_caps (GstNvBaseEnc * nvenc,
87 GstVideoCodecState * state);
88 static gboolean gst_nv_h264_enc_set_encoder_config (GstNvBaseEnc * nvenc,
89 GstVideoCodecState * state, NV_ENC_CONFIG * config);
90 static gboolean gst_nv_h264_enc_set_pic_params (GstNvBaseEnc * nvenc,
91 GstVideoCodecFrame * frame, NV_ENC_PIC_PARAMS * pic_params);
92 static void gst_nv_h264_enc_set_property (GObject * object, guint prop_id,
93 const GValue * value, GParamSpec * pspec);
94 static void gst_nv_h264_enc_get_property (GObject * object, guint prop_id,
95 GValue * value, GParamSpec * pspec);
96 static void gst_nv_h264_enc_finalize (GObject * obj);
97
98 static void
gst_nv_h264_enc_class_init(GstNvH264EncClass * klass,gpointer data)99 gst_nv_h264_enc_class_init (GstNvH264EncClass * klass, gpointer data)
100 {
101 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
102 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
103 GstVideoEncoderClass *videoenc_class = GST_VIDEO_ENCODER_CLASS (klass);
104 GstNvBaseEncClass *nvenc_class = GST_NV_BASE_ENC_CLASS (klass);
105 GstNvEncDeviceCaps *device_caps = &nvenc_class->device_caps;
106 GstNvH264EncClassData *cdata = (GstNvH264EncClassData *) data;
107 gchar *long_name;
108 GstPadTemplate *pad_templ;
109 GstCaps *doc_caps;
110
111 parent_class = g_type_class_peek_parent (klass);
112
113 gobject_class->set_property = gst_nv_h264_enc_set_property;
114 gobject_class->get_property = gst_nv_h264_enc_get_property;
115 gobject_class->finalize = gst_nv_h264_enc_finalize;
116
117 videoenc_class->open = GST_DEBUG_FUNCPTR (gst_nv_h264_enc_open);
118 videoenc_class->close = GST_DEBUG_FUNCPTR (gst_nv_h264_enc_close);
119
120 nvenc_class->codec_id = NV_ENC_CODEC_H264_GUID;
121 nvenc_class->set_encoder_config = gst_nv_h264_enc_set_encoder_config;
122 nvenc_class->set_src_caps = gst_nv_h264_enc_set_src_caps;
123 nvenc_class->set_pic_params = gst_nv_h264_enc_set_pic_params;
124
125 /**
126 * GstNvH264Enc:aud:
127 *
128 * Use AU (Access Unit) delimiter
129 *
130 * Since: 1.18
131 */
132 g_object_class_install_property (gobject_class, PROP_AUD,
133 g_param_spec_boolean ("aud", "AUD",
134 "Use AU (Access Unit) delimiter", DEFAULT_AUD,
135 G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
136 G_PARAM_STATIC_STRINGS));
137
138 if (device_caps->weighted_prediction) {
139 /**
140 * GstNvH264Enc:weighted-pred:
141 *
142 * Weighted Prediction
143 *
144 * Since: 1.18
145 */
146 g_object_class_install_property (gobject_class, PROP_WEIGHTED_PRED,
147 g_param_spec_boolean ("weighted-pred", "Weighted Pred",
148 "Weighted Prediction", DEFAULT_WEIGHTED_PRED,
149 G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
150 GST_PARAM_CONDITIONALLY_AVAILABLE | G_PARAM_STATIC_STRINGS));
151 }
152
153 if (device_caps->custom_vbv_bufsize) {
154 /**
155 * GstNvH264Enc:vbv-buffer-size:
156 *
157 * VBV(HRD) Buffer Size in kbits (0 = NVENC default)
158 *
159 * Since: 1.18
160 */
161 g_object_class_install_property (gobject_class,
162 PROP_VBV_BUFFER_SIZE,
163 g_param_spec_uint ("vbv-buffer-size", "VBV Buffer Size",
164 "VBV(HRD) Buffer Size in kbits (0 = NVENC default)",
165 0, G_MAXUINT, DEFAULT_VBV_BUFFER_SIZE,
166 G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY |
167 GST_PARAM_CONDITIONALLY_AVAILABLE | G_PARAM_STATIC_STRINGS));
168 }
169
170 if (device_caps->lookahead) {
171 /**
172 * GstNvH264Enc:rc-lookahead:
173 *
174 * Number of frames for frame type lookahead
175 *
176 * Since: 1.18
177 */
178 g_object_class_install_property (gobject_class, PROP_RC_LOOKAHEAD,
179 g_param_spec_uint ("rc-lookahead", "Rate Control Lookahead",
180 "Number of frames for frame type lookahead",
181 0, 32, DEFAULT_RC_LOOKAHEAD,
182 G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY |
183 GST_PARAM_CONDITIONALLY_AVAILABLE | G_PARAM_STATIC_STRINGS));
184 }
185
186 if (device_caps->temporal_aq) {
187 /**
188 * GstNvH264Enc:temporal-aq:
189 *
190 * Temporal Adaptive Quantization
191 *
192 * Since: 1.18
193 */
194 g_object_class_install_property (gobject_class, PROP_TEMPORAL_AQ,
195 g_param_spec_boolean ("temporal-aq", "Temporal AQ",
196 "Temporal Adaptive Quantization", DEFAULT_TEMPORAL_AQ,
197 G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
198 GST_PARAM_CONDITIONALLY_AVAILABLE | G_PARAM_STATIC_STRINGS));
199 }
200
201 if (device_caps->bframes > 0) {
202 /**
203 * GstNvH264Enc:bframes:
204 *
205 * Number of B-frames between I and P
206 *
207 * Since: 1.18
208 */
209 g_object_class_install_property (gobject_class, PROP_BFRAMES,
210 g_param_spec_uint ("bframes", "B-Frames",
211 "Number of B-frames between I and P", 0, device_caps->bframes,
212 DEFAULT_BFRAMES,
213 G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY |
214 GST_PARAM_CONDITIONALLY_AVAILABLE | G_PARAM_STATIC_STRINGS));
215
216 /**
217 * GstNvH264Enc:b-adapt:
218 *
219 * Enable adaptive B-frame insert when lookahead is enabled
220 *
221 * Since: 1.18
222 */
223 g_object_class_install_property (gobject_class, PROP_B_ADAPT,
224 g_param_spec_boolean ("b-adapt", "B Adapt",
225 "Enable adaptive B-frame insert when lookahead is enabled",
226 DEFAULT_B_ADAPT,
227 G_PARAM_READWRITE | GST_PARAM_MUTABLE_READY |
228 GST_PARAM_CONDITIONALLY_AVAILABLE | G_PARAM_STATIC_STRINGS));
229 }
230
231 if (cdata->is_default)
232 long_name = g_strdup ("NVENC H.264 Video Encoder");
233 else
234 long_name = g_strdup_printf ("NVENC H.264 Video Encoder with device %d",
235 nvenc_class->cuda_device_id);
236
237 gst_element_class_set_metadata (element_class, long_name,
238 "Codec/Encoder/Video/Hardware",
239 "Encode H.264 video streams using NVIDIA's hardware-accelerated NVENC encoder API",
240 "Tim-Philipp Müller <tim@centricular.com>, "
241 "Matthew Waters <matthew@centricular.com>, "
242 "Seungha Yang <seungha.yang@navercorp.com>");
243 g_free (long_name);
244
245 GST_DEBUG_CATEGORY_INIT (gst_nv_h264_enc_debug,
246 "nvh264enc", 0, "Nvidia H.264 encoder");
247
248 pad_templ = gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
249 cdata->sink_caps);
250 doc_caps = gst_caps_from_string (DOCUMENTATION_SINK_CAPS);
251 gst_pad_template_set_documentation_caps (pad_templ, doc_caps);
252 gst_caps_unref (doc_caps);
253 gst_element_class_add_pad_template (element_class, pad_templ);
254
255 pad_templ = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
256 cdata->src_caps);
257 doc_caps = gst_caps_from_string (DOCUMENTATION_SRC_CAPS);
258 gst_pad_template_set_documentation_caps (pad_templ, doc_caps);
259 gst_caps_unref (doc_caps);
260 gst_element_class_add_pad_template (element_class, pad_templ);
261
262 gst_caps_unref (cdata->sink_caps);
263 gst_caps_unref (cdata->src_caps);
264 g_free (cdata);
265 }
266
267 static void
gst_nv_h264_enc_init(GstNvH264Enc * nvenc)268 gst_nv_h264_enc_init (GstNvH264Enc * nvenc)
269 {
270 GstNvBaseEnc *baseenc = GST_NV_BASE_ENC (nvenc);
271
272 nvenc->aud = DEFAULT_AUD;
273
274 /* device capability dependent properties */
275 baseenc->weighted_pred = DEFAULT_WEIGHTED_PRED;
276 baseenc->vbv_buffersize = DEFAULT_VBV_BUFFER_SIZE;
277 baseenc->rc_lookahead = DEFAULT_RC_LOOKAHEAD;
278 baseenc->temporal_aq = DEFAULT_TEMPORAL_AQ;
279 baseenc->bframes = DEFAULT_BFRAMES;
280 baseenc->b_adapt = DEFAULT_B_ADAPT;
281 }
282
283 static void
gst_nv_h264_enc_finalize(GObject * obj)284 gst_nv_h264_enc_finalize (GObject * obj)
285 {
286 G_OBJECT_CLASS (parent_class)->finalize (obj);
287 }
288
289 static gboolean
gst_nv_h264_enc_open(GstVideoEncoder * enc)290 gst_nv_h264_enc_open (GstVideoEncoder * enc)
291 {
292 GstNvBaseEnc *base = GST_NV_BASE_ENC (enc);
293
294 if (!GST_VIDEO_ENCODER_CLASS (parent_class)->open (enc))
295 return FALSE;
296
297 /* Check if H.264 is supported */
298 {
299 uint32_t i, num = 0;
300 GUID guids[16];
301
302 NvEncGetEncodeGUIDs (base->encoder, guids, G_N_ELEMENTS (guids), &num);
303
304 for (i = 0; i < num; ++i) {
305 if (gst_nvenc_cmp_guid (guids[i], NV_ENC_CODEC_H264_GUID))
306 break;
307 }
308 GST_INFO_OBJECT (enc, "H.264 encoding %ssupported", (i == num) ? "un" : "");
309 if (i == num) {
310 gst_nv_h264_enc_close (enc);
311 return FALSE;
312 }
313 }
314
315 return TRUE;
316 }
317
318 static gboolean
gst_nv_h264_enc_close(GstVideoEncoder * enc)319 gst_nv_h264_enc_close (GstVideoEncoder * enc)
320 {
321 return GST_VIDEO_ENCODER_CLASS (parent_class)->close (enc);
322 }
323
324 static gboolean
gst_nv_h264_enc_set_profile_and_level(GstNvH264Enc * nvenc,GstCaps * caps)325 gst_nv_h264_enc_set_profile_and_level (GstNvH264Enc * nvenc, GstCaps * caps)
326 {
327 #define N_BYTES_SPS 128
328 guint8 sps[N_BYTES_SPS];
329 NV_ENC_SEQUENCE_PARAM_PAYLOAD spp = { 0, };
330 GstStructure *s;
331 const gchar *profile;
332 GstCaps *allowed_caps;
333 GstStructure *s2;
334 const gchar *allowed_profile;
335 NVENCSTATUS nv_ret;
336 guint32 seq_size;
337
338 spp.version = gst_nvenc_get_sequence_param_payload_version ();
339 spp.inBufferSize = N_BYTES_SPS;
340 spp.spsId = 0;
341 spp.ppsId = 0;
342 spp.spsppsBuffer = &sps;
343 spp.outSPSPPSPayloadSize = &seq_size;
344 nv_ret = NvEncGetSequenceParams (GST_NV_BASE_ENC (nvenc)->encoder, &spp);
345 if (nv_ret != NV_ENC_SUCCESS) {
346 GST_ELEMENT_ERROR (nvenc, STREAM, ENCODE, ("Encode header failed."),
347 ("NvEncGetSequenceParams return code=%d", nv_ret));
348 return FALSE;
349 }
350
351 if (seq_size < 8) {
352 GST_ELEMENT_ERROR (nvenc, STREAM, ENCODE, ("Encode header failed."),
353 ("NvEncGetSequenceParams returned incomplete data"));
354 return FALSE;
355 }
356
357 /* skip nal header and identifier */
358 gst_codec_utils_h264_caps_set_level_and_profile (caps, &sps[5], 3);
359
360 /* Constrained baseline is a strict subset of baseline. If downstream
361 * wanted baseline and we produced constrained baseline, we can just
362 * set the profile to baseline in the caps to make negotiation happy.
363 * Same goes for baseline as subset of main profile and main as a subset
364 * of high profile.
365 */
366 s = gst_caps_get_structure (caps, 0);
367 profile = gst_structure_get_string (s, "profile");
368
369 allowed_caps = gst_pad_get_allowed_caps (GST_VIDEO_ENCODER_SRC_PAD (nvenc));
370
371 if (allowed_caps == NULL)
372 goto no_peer;
373
374 if (!gst_caps_can_intersect (allowed_caps, caps)) {
375 allowed_caps = gst_caps_make_writable (allowed_caps);
376 allowed_caps = gst_caps_truncate (allowed_caps);
377 s2 = gst_caps_get_structure (allowed_caps, 0);
378 gst_structure_fixate_field_string (s2, "profile", profile);
379 allowed_profile = gst_structure_get_string (s2, "profile");
380 if (!strcmp (allowed_profile, "high")) {
381 if (!strcmp (profile, "constrained-baseline")
382 || !strcmp (profile, "baseline") || !strcmp (profile, "main")) {
383 gst_structure_set (s, "profile", G_TYPE_STRING, "high", NULL);
384 GST_INFO_OBJECT (nvenc, "downstream requested high profile, but "
385 "encoder will now output %s profile (which is a subset), due "
386 "to how it's been configured", profile);
387 }
388 } else if (!strcmp (allowed_profile, "main")) {
389 if (!strcmp (profile, "constrained-baseline")
390 || !strcmp (profile, "baseline")) {
391 gst_structure_set (s, "profile", G_TYPE_STRING, "main", NULL);
392 GST_INFO_OBJECT (nvenc, "downstream requested main profile, but "
393 "encoder will now output %s profile (which is a subset), due "
394 "to how it's been configured", profile);
395 }
396 } else if (!strcmp (allowed_profile, "baseline")) {
397 if (!strcmp (profile, "constrained-baseline"))
398 gst_structure_set (s, "profile", G_TYPE_STRING, "baseline", NULL);
399 }
400 }
401 gst_caps_unref (allowed_caps);
402
403 no_peer:
404
405 return TRUE;
406
407 #undef N_BYTES_SPS
408 }
409
410 static gboolean
gst_nv_h264_enc_set_src_caps(GstNvBaseEnc * nvenc,GstVideoCodecState * state)411 gst_nv_h264_enc_set_src_caps (GstNvBaseEnc * nvenc, GstVideoCodecState * state)
412 {
413 GstNvH264Enc *h264enc = (GstNvH264Enc *) nvenc;
414 GstVideoCodecState *out_state;
415 GstStructure *s;
416 GstCaps *out_caps;
417
418 out_caps = gst_caps_new_empty_simple ("video/x-h264");
419 s = gst_caps_get_structure (out_caps, 0);
420
421 /* TODO: add support for avc format as well */
422 gst_structure_set (s, "stream-format", G_TYPE_STRING, "byte-stream",
423 "alignment", G_TYPE_STRING, "au", NULL);
424
425 if (!gst_nv_h264_enc_set_profile_and_level (h264enc, out_caps)) {
426 gst_caps_unref (out_caps);
427 return FALSE;
428 }
429
430 out_state = gst_video_encoder_set_output_state (GST_VIDEO_ENCODER (nvenc),
431 out_caps, state);
432
433 GST_INFO_OBJECT (nvenc, "output caps: %" GST_PTR_FORMAT, out_state->caps);
434
435 /* encoder will keep it around for us */
436 gst_video_codec_state_unref (out_state);
437
438 /* TODO: would be nice to also send some tags with the codec name */
439 return TRUE;
440 }
441
442 static gboolean
gst_nv_h264_enc_set_encoder_config(GstNvBaseEnc * nvenc,GstVideoCodecState * state,NV_ENC_CONFIG * config)443 gst_nv_h264_enc_set_encoder_config (GstNvBaseEnc * nvenc,
444 GstVideoCodecState * state, NV_ENC_CONFIG * config)
445 {
446 GstNvH264Enc *h264enc = (GstNvH264Enc *) nvenc;
447 GstCaps *allowed_caps, *template_caps;
448 GUID selected_profile = NV_ENC_CODEC_PROFILE_AUTOSELECT_GUID;
449 int level_idc = NV_ENC_LEVEL_AUTOSELECT;
450 GstVideoInfo *info = &state->info;
451 NV_ENC_CONFIG_H264 *h264_config = &config->encodeCodecConfig.h264Config;
452 NV_ENC_CONFIG_H264_VUI_PARAMETERS *vui = &h264_config->h264VUIParameters;
453
454 template_caps =
455 gst_pad_get_pad_template_caps (GST_VIDEO_ENCODER_SRC_PAD (h264enc));
456 allowed_caps = gst_pad_get_allowed_caps (GST_VIDEO_ENCODER_SRC_PAD (h264enc));
457
458 if (template_caps == allowed_caps) {
459 GST_INFO_OBJECT (h264enc, "downstream has ANY caps");
460 } else if (allowed_caps) {
461 GstStructure *s;
462 const gchar *profile;
463 const gchar *level;
464
465 if (gst_caps_is_empty (allowed_caps)) {
466 gst_caps_unref (allowed_caps);
467 gst_caps_unref (template_caps);
468 return FALSE;
469 }
470
471 allowed_caps = gst_caps_make_writable (allowed_caps);
472 allowed_caps = gst_caps_fixate (allowed_caps);
473 s = gst_caps_get_structure (allowed_caps, 0);
474
475 profile = gst_structure_get_string (s, "profile");
476 if (profile) {
477 if (!strcmp (profile, "baseline")
478 || !strcmp (profile, "constrained-baseline")) {
479 selected_profile = NV_ENC_H264_PROFILE_BASELINE_GUID;
480 } else if (g_str_has_prefix (profile, "high-4:4:4")) {
481 selected_profile = NV_ENC_H264_PROFILE_HIGH_444_GUID;
482 } else if (g_str_has_prefix (profile, "high-10")) {
483 g_assert_not_reached ();
484 } else if (g_str_has_prefix (profile, "high-4:2:2")) {
485 g_assert_not_reached ();
486 } else if (g_str_has_prefix (profile, "high")) {
487 selected_profile = NV_ENC_H264_PROFILE_HIGH_GUID;
488 } else if (g_str_has_prefix (profile, "main")) {
489 selected_profile = NV_ENC_H264_PROFILE_MAIN_GUID;
490 } else {
491 g_assert_not_reached ();
492 }
493 }
494
495 level = gst_structure_get_string (s, "level");
496 if (level)
497 /* matches values stored in NV_ENC_LEVEL */
498 level_idc = gst_codec_utils_h264_get_level_idc (level);
499
500 gst_caps_unref (allowed_caps);
501 }
502 gst_caps_unref (template_caps);
503
504 /* override some defaults */
505 GST_LOG_OBJECT (h264enc, "setting parameters");
506 config->profileGUID = selected_profile;
507 h264_config->level = level_idc;
508 h264_config->chromaFormatIDC = 1;
509 if (GST_VIDEO_INFO_FORMAT (info) == GST_VIDEO_FORMAT_Y444 ||
510 GST_VIDEO_INFO_FORMAT (info) == GST_VIDEO_FORMAT_VUYA) {
511 GST_DEBUG_OBJECT (h264enc, "have Y444 input, setting config accordingly");
512 config->profileGUID = NV_ENC_H264_PROFILE_HIGH_444_GUID;
513 h264_config->chromaFormatIDC = 3;
514 }
515
516 h264_config->idrPeriod = config->gopLength;
517 h264_config->outputAUD = h264enc->aud;
518
519 vui->videoSignalTypePresentFlag = 1;
520 /* NOTE: vui::video_format represents the video format before
521 * being encoded such as PAL, NTSC, SECAM, and MAC. That's not much informal
522 * and can be inferred with resolution and framerate by any application.
523 */
524 /* Unspecified video format (5) */
525 vui->videoFormat = 5;
526
527 if (info->colorimetry.range == GST_VIDEO_COLOR_RANGE_0_255) {
528 vui->videoFullRangeFlag = 1;
529 } else {
530 vui->videoFullRangeFlag = 0;
531 }
532
533 vui->colourDescriptionPresentFlag = 1;
534 vui->colourMatrix = gst_video_color_matrix_to_iso (info->colorimetry.matrix);
535 vui->colourPrimaries =
536 gst_video_color_primaries_to_iso (info->colorimetry.primaries);
537 vui->transferCharacteristics =
538 gst_video_transfer_function_to_iso (info->colorimetry.transfer);
539
540 return TRUE;
541 }
542
543 static gboolean
gst_nv_h264_enc_set_pic_params(GstNvBaseEnc * enc,GstVideoCodecFrame * frame,NV_ENC_PIC_PARAMS * pic_params)544 gst_nv_h264_enc_set_pic_params (GstNvBaseEnc * enc, GstVideoCodecFrame * frame,
545 NV_ENC_PIC_PARAMS * pic_params)
546 {
547 /* encode whole picture in one single slice */
548 pic_params->codecPicParams.h264PicParams.sliceMode = 0;
549 pic_params->codecPicParams.h264PicParams.sliceModeData = 0;
550
551 return TRUE;
552 }
553
554 static void
gst_nv_h264_enc_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)555 gst_nv_h264_enc_set_property (GObject * object, guint prop_id,
556 const GValue * value, GParamSpec * pspec)
557 {
558 GstNvH264Enc *self = (GstNvH264Enc *) object;
559 GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (object);
560 GstNvBaseEncClass *klass = GST_NV_BASE_ENC_GET_CLASS (object);
561 GstNvEncDeviceCaps *device_caps = &klass->device_caps;
562 gboolean reconfig = FALSE;
563
564 switch (prop_id) {
565 case PROP_AUD:
566 {
567 gboolean aud;
568
569 aud = g_value_get_boolean (value);
570 if (aud != self->aud) {
571 self->aud = aud;
572 reconfig = TRUE;
573 }
574 break;
575 }
576 case PROP_WEIGHTED_PRED:
577 if (!device_caps->weighted_prediction) {
578 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
579 } else {
580 nvenc->weighted_pred = g_value_get_boolean (value);
581 reconfig = TRUE;
582 }
583 break;
584 case PROP_VBV_BUFFER_SIZE:
585 if (!device_caps->custom_vbv_bufsize) {
586 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
587 } else {
588 nvenc->vbv_buffersize = g_value_get_uint (value);
589 reconfig = TRUE;
590 }
591 break;
592 case PROP_RC_LOOKAHEAD:
593 if (!device_caps->lookahead) {
594 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
595 } else {
596 nvenc->rc_lookahead = g_value_get_uint (value);
597 reconfig = TRUE;
598 }
599 break;
600 case PROP_TEMPORAL_AQ:
601 if (!device_caps->temporal_aq) {
602 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
603 } else {
604 nvenc->temporal_aq = g_value_get_boolean (value);
605 reconfig = TRUE;
606 }
607 break;
608 case PROP_BFRAMES:
609 if (!device_caps->bframes) {
610 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
611 } else {
612 nvenc->bframes = g_value_get_uint (value);
613 reconfig = TRUE;
614 }
615 break;
616 case PROP_B_ADAPT:
617 if (!device_caps->bframes) {
618 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
619 } else {
620 nvenc->b_adapt = g_value_get_boolean (value);
621 }
622 break;
623 default:
624 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
625 break;
626 }
627
628 if (reconfig)
629 gst_nv_base_enc_schedule_reconfig (GST_NV_BASE_ENC (self));
630 }
631
632 static void
gst_nv_h264_enc_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)633 gst_nv_h264_enc_get_property (GObject * object, guint prop_id, GValue * value,
634 GParamSpec * pspec)
635 {
636 GstNvH264Enc *self = (GstNvH264Enc *) object;
637 GstNvBaseEnc *nvenc = GST_NV_BASE_ENC (object);
638 GstNvBaseEncClass *klass = GST_NV_BASE_ENC_GET_CLASS (object);
639 GstNvEncDeviceCaps *device_caps = &klass->device_caps;
640
641 switch (prop_id) {
642 case PROP_AUD:
643 g_value_set_boolean (value, self->aud);
644 break;
645 case PROP_WEIGHTED_PRED:
646 if (!device_caps->weighted_prediction) {
647 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
648 } else {
649 g_value_set_boolean (value, nvenc->weighted_pred);
650 }
651 break;
652 case PROP_VBV_BUFFER_SIZE:
653 if (!device_caps->custom_vbv_bufsize) {
654 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
655 } else {
656 g_value_set_uint (value, nvenc->vbv_buffersize);
657 }
658 break;
659 case PROP_RC_LOOKAHEAD:
660 if (!device_caps->lookahead) {
661 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
662 } else {
663 g_value_set_uint (value, nvenc->rc_lookahead);
664 }
665 break;
666 case PROP_TEMPORAL_AQ:
667 if (!device_caps->temporal_aq) {
668 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
669 } else {
670 g_value_set_boolean (value, nvenc->temporal_aq);
671 }
672 break;
673 case PROP_BFRAMES:
674 if (!device_caps->bframes) {
675 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
676 } else {
677 g_value_set_uint (value, nvenc->bframes);
678 }
679 break;
680 case PROP_B_ADAPT:
681 if (!device_caps->bframes) {
682 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
683 } else {
684 g_value_set_boolean (value, nvenc->b_adapt);
685 }
686 break;
687 default:
688 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
689 break;
690 }
691 }
692
693 void
gst_nv_h264_enc_register(GstPlugin * plugin,guint device_id,guint rank,GstCaps * sink_caps,GstCaps * src_caps,GstNvEncDeviceCaps * device_caps)694 gst_nv_h264_enc_register (GstPlugin * plugin, guint device_id, guint rank,
695 GstCaps * sink_caps, GstCaps * src_caps, GstNvEncDeviceCaps * device_caps)
696 {
697 GType parent_type;
698 GType type;
699 gchar *type_name;
700 gchar *feature_name;
701 GstNvH264EncClassData *cdata;
702 gboolean is_default = TRUE;
703 GTypeInfo type_info = {
704 sizeof (GstNvH264EncClass),
705 NULL,
706 NULL,
707 (GClassInitFunc) gst_nv_h264_enc_class_init,
708 NULL,
709 NULL,
710 sizeof (GstNvH264Enc),
711 0,
712 (GInstanceInitFunc) gst_nv_h264_enc_init,
713 };
714
715 parent_type = gst_nv_base_enc_register ("H264", device_id, device_caps);
716
717 cdata = g_new0 (GstNvH264EncClassData, 1);
718 cdata->sink_caps = gst_caps_ref (sink_caps);
719 cdata->src_caps = gst_caps_ref (src_caps);
720 type_info.class_data = cdata;
721 /* class data will be leaked if the element never gets instantiated */
722 GST_MINI_OBJECT_FLAG_SET (sink_caps, GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED);
723 GST_MINI_OBJECT_FLAG_SET (src_caps, GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED);
724
725 type_name = g_strdup ("GstNvH264Enc");
726 feature_name = g_strdup ("nvh264enc");
727
728 if (g_type_from_name (type_name) != 0) {
729 g_free (type_name);
730 g_free (feature_name);
731 type_name = g_strdup_printf ("GstNvH264Device%dEnc", device_id);
732 feature_name = g_strdup_printf ("nvh264device%denc", device_id);
733 is_default = FALSE;
734 }
735
736 cdata->is_default = is_default;
737 type = g_type_register_static (parent_type, type_name, &type_info, 0);
738
739 /* make lower rank than default device */
740 if (rank > 0 && !is_default)
741 rank--;
742
743 if (!gst_element_register (plugin, feature_name, rank, type))
744 GST_WARNING ("Failed to register plugin '%s'", type_name);
745
746 g_free (type_name);
747 g_free (feature_name);
748 }
749