• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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