1 /*
2 * Copyright 2021 Rockchip Electronics Co., Ltd
3 * Author: Jeffy Chen <jeffy.chen@rock-chips.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
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include <string.h>
27
28 #include "gstmpph265enc.h"
29
30 /* FIXME: Not all chips support NV24 and Y444. */
31 #define MPP_H265_ENC_FORMATS MPP_ENC_FORMATS ", NV24, Y444"
32
33 #define GST_MPP_H265_ENC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
34 GST_TYPE_MPP_H265_ENC, GstMppH265Enc))
35
36 #define GST_CAT_DEFAULT mpp_h265_enc_debug
37 GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
38
39 struct _GstMppH265Enc
40 {
41 GstMppEnc parent;
42
43 guint qp_init;
44 guint qp_min;
45 guint qp_max;
46 gint qp_max_step;
47 };
48
49 #define parent_class gst_mpp_h265_enc_parent_class
50 G_DEFINE_TYPE (GstMppH265Enc, gst_mpp_h265_enc, GST_TYPE_MPP_ENC);
51
52 #define DEFAULT_PROP_QP_INIT 26
53 #define DEFAULT_PROP_QP_MIN 0 /* Auto */
54 #define DEFAULT_PROP_QP_MAX 0 /* Auto */
55 #define DEFAULT_PROP_QP_MAX_STEP -1 /* Auto */
56
57 enum
58 {
59 PROP_0,
60 PROP_QP_INIT,
61 PROP_QP_MIN,
62 PROP_QP_MAX,
63 PROP_QP_MAX_STEP,
64 PROP_LAST,
65 };
66
67 #define GST_MPP_H265_ENC_SIZE_CAPS \
68 "width = (int) [ 96, MAX ], height = (int) [ 64, MAX ]"
69
70 static GstStaticPadTemplate gst_mpp_h265_enc_src_template =
71 GST_STATIC_PAD_TEMPLATE ("src",
72 GST_PAD_SRC,
73 GST_PAD_ALWAYS,
74 GST_STATIC_CAPS ("video/x-h265, "
75 GST_MPP_H265_ENC_SIZE_CAPS ","
76 "stream-format = (string) { byte-stream }, "
77 "alignment = (string) { au }")
78 );
79
80 static GstStaticPadTemplate gst_mpp_h265_enc_sink_template =
81 GST_STATIC_PAD_TEMPLATE ("sink",
82 GST_PAD_SINK,
83 GST_PAD_ALWAYS,
84 GST_STATIC_CAPS ("video/x-raw,"
85 "format = (string) { " MPP_H265_ENC_FORMATS " }, "
86 GST_MPP_H265_ENC_SIZE_CAPS ";"));
87
88 static void
gst_mpp_h265_enc_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)89 gst_mpp_h265_enc_set_property (GObject * object,
90 guint prop_id, const GValue * value, GParamSpec * pspec)
91 {
92 GstVideoEncoder *encoder = GST_VIDEO_ENCODER (object);
93 GstMppH265Enc *self = GST_MPP_H265_ENC (encoder);
94 GstMppEnc *mppenc = GST_MPP_ENC (encoder);
95
96 switch (prop_id) {
97 case PROP_QP_INIT:{
98 guint qp_init = g_value_get_uint (value);
99 if (self->qp_init == qp_init)
100 return;
101
102 self->qp_init = qp_init;
103 break;
104 }
105 case PROP_QP_MIN:{
106 guint qp_min = g_value_get_uint (value);
107 if (self->qp_min == qp_min)
108 return;
109
110 self->qp_min = qp_min;
111 break;
112 }
113 case PROP_QP_MAX:{
114 guint qp_max = g_value_get_uint (value);
115 if (self->qp_max == qp_max)
116 return;
117
118 self->qp_max = qp_max;
119 break;
120 }
121 case PROP_QP_MAX_STEP:{
122 gint qp_max_step = g_value_get_int (value);
123 if (self->qp_max_step == qp_max_step)
124 return;
125
126 self->qp_max_step = qp_max_step;
127 break;
128 }
129 default:
130 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
131 return;
132 }
133
134 mppenc->prop_dirty = TRUE;
135 }
136
137 static void
gst_mpp_h265_enc_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)138 gst_mpp_h265_enc_get_property (GObject * object,
139 guint prop_id, GValue * value, GParamSpec * pspec)
140 {
141 GstVideoEncoder *encoder = GST_VIDEO_ENCODER (object);
142 GstMppH265Enc *self = GST_MPP_H265_ENC (encoder);
143
144 switch (prop_id) {
145 case PROP_QP_INIT:
146 g_value_set_uint (value, self->qp_init);
147 break;
148 case PROP_QP_MIN:
149 g_value_set_uint (value, self->qp_min);
150 break;
151 case PROP_QP_MAX:
152 g_value_set_uint (value, self->qp_max);
153 break;
154 case PROP_QP_MAX_STEP:
155 g_value_set_int (value, self->qp_max_step);
156 break;
157 default:
158 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
159 break;
160 }
161 }
162
163 static gboolean
gst_mpp_h265_enc_set_src_caps(GstVideoEncoder * encoder)164 gst_mpp_h265_enc_set_src_caps (GstVideoEncoder * encoder)
165 {
166 GstStructure *structure;
167 GstCaps *caps;
168
169 caps = gst_caps_new_empty_simple ("video/x-h265");
170
171 structure = gst_caps_get_structure (caps, 0);
172 gst_structure_set (structure, "stream-format",
173 G_TYPE_STRING, "byte-stream", NULL);
174 gst_structure_set (structure, "alignment", G_TYPE_STRING, "au", NULL);
175
176 return gst_mpp_enc_set_src_caps (encoder, caps);
177 }
178
179 static gboolean
gst_mpp_h265_enc_apply_properties(GstVideoEncoder * encoder)180 gst_mpp_h265_enc_apply_properties (GstVideoEncoder * encoder)
181 {
182 GstMppH265Enc *self = GST_MPP_H265_ENC (encoder);
183 GstMppEnc *mppenc = GST_MPP_ENC (encoder);
184
185 if (G_LIKELY (!mppenc->prop_dirty))
186 return TRUE;
187
188 mpp_enc_cfg_set_s32 (mppenc->mpp_cfg, "h265:qp_init", self->qp_init);
189
190 if (mppenc->rc_mode == MPP_ENC_RC_MODE_FIXQP) {
191 mpp_enc_cfg_set_s32 (mppenc->mpp_cfg, "h265:qp_max", self->qp_init);
192 mpp_enc_cfg_set_s32 (mppenc->mpp_cfg, "h265:qp_min", self->qp_init);
193 mpp_enc_cfg_set_s32 (mppenc->mpp_cfg, "h265:qp_step", 0);
194 } else if (mppenc->rc_mode == MPP_ENC_RC_MODE_CBR) {
195 /* NOTE: These settings have been tuned for better quality */
196 mpp_enc_cfg_set_s32 (mppenc->mpp_cfg, "h265:qp_max",
197 self->qp_max ? self->qp_max : 28);
198 mpp_enc_cfg_set_s32 (mppenc->mpp_cfg, "h265:qp_min",
199 self->qp_min ? self->qp_min : 4);
200 mpp_enc_cfg_set_s32 (mppenc->mpp_cfg, "h265:qp_step",
201 self->qp_max_step >= 0 ? self->qp_max_step : 8);
202 } else if (mppenc->rc_mode == MPP_ENC_RC_MODE_VBR) {
203 mpp_enc_cfg_set_s32 (mppenc->mpp_cfg, "h265:qp_max",
204 self->qp_max ? self->qp_max : 40);
205 mpp_enc_cfg_set_s32 (mppenc->mpp_cfg, "h265:qp_min",
206 self->qp_min ? self->qp_min : 12);
207 mpp_enc_cfg_set_s32 (mppenc->mpp_cfg, "h265:qp_step",
208 self->qp_max_step >= 0 ? self->qp_max_step : 8);
209 }
210
211 if (!gst_mpp_enc_apply_properties (encoder))
212 return FALSE;
213
214 return gst_mpp_h265_enc_set_src_caps (encoder);
215 }
216
217 static gboolean
gst_mpp_h265_enc_set_format(GstVideoEncoder * encoder,GstVideoCodecState * state)218 gst_mpp_h265_enc_set_format (GstVideoEncoder * encoder,
219 GstVideoCodecState * state)
220 {
221 GstVideoEncoderClass *pclass = GST_VIDEO_ENCODER_CLASS (parent_class);
222
223 if (!pclass->set_format (encoder, state))
224 return FALSE;
225
226 return gst_mpp_h265_enc_apply_properties (encoder);
227 }
228
229 static GstFlowReturn
gst_mpp_h265_enc_handle_frame(GstVideoEncoder * encoder,GstVideoCodecFrame * frame)230 gst_mpp_h265_enc_handle_frame (GstVideoEncoder * encoder,
231 GstVideoCodecFrame * frame)
232 {
233 GstVideoEncoderClass *pclass = GST_VIDEO_ENCODER_CLASS (parent_class);
234
235 if (G_UNLIKELY (!gst_mpp_h265_enc_apply_properties (encoder))) {
236 gst_video_codec_frame_unref (frame);
237 return GST_FLOW_NOT_NEGOTIATED;
238 }
239
240 return pclass->handle_frame (encoder, frame);
241 }
242
243 static void
gst_mpp_h265_enc_init(GstMppH265Enc * self)244 gst_mpp_h265_enc_init (GstMppH265Enc * self)
245 {
246 self->parent.mpp_type = MPP_VIDEO_CodingHEVC;
247
248 self->qp_init = DEFAULT_PROP_QP_INIT;
249 self->qp_min = DEFAULT_PROP_QP_MIN;
250 self->qp_max = DEFAULT_PROP_QP_MAX;
251 self->qp_max_step = DEFAULT_PROP_QP_MAX_STEP;
252 }
253
254 static void
gst_mpp_h265_enc_class_init(GstMppH265EncClass * klass)255 gst_mpp_h265_enc_class_init (GstMppH265EncClass * klass)
256 {
257 GstVideoEncoderClass *encoder_class = GST_VIDEO_ENCODER_CLASS (klass);
258 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
259 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
260
261 GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "mpph265enc", 0,
262 "MPP H265 encoder");
263
264 encoder_class->set_format = GST_DEBUG_FUNCPTR (gst_mpp_h265_enc_set_format);
265 encoder_class->handle_frame =
266 GST_DEBUG_FUNCPTR (gst_mpp_h265_enc_handle_frame);
267
268 gobject_class->set_property =
269 GST_DEBUG_FUNCPTR (gst_mpp_h265_enc_set_property);
270 gobject_class->get_property =
271 GST_DEBUG_FUNCPTR (gst_mpp_h265_enc_get_property);
272
273 g_object_class_install_property (gobject_class, PROP_QP_INIT,
274 g_param_spec_uint ("qp-init", "Initial QP",
275 "Initial QP (lower value means higher quality)",
276 0, 51, DEFAULT_PROP_QP_INIT,
277 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
278
279 g_object_class_install_property (gobject_class, PROP_QP_MIN,
280 g_param_spec_uint ("qp-min", "Min QP",
281 "Min QP (0 = default)", 0, 51, DEFAULT_PROP_QP_MIN,
282 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
283
284 g_object_class_install_property (gobject_class, PROP_QP_MAX,
285 g_param_spec_uint ("qp-max", "Max QP",
286 "Max QP (0 = default)", 0, 51, DEFAULT_PROP_QP_MAX,
287 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
288
289 g_object_class_install_property (gobject_class, PROP_QP_MAX_STEP,
290 g_param_spec_int ("qp-max-step", "Max QP step",
291 "Max delta QP step between two frames (-1 = default)", -1, 51,
292 DEFAULT_PROP_QP_MAX_STEP,
293 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
294
295 gst_element_class_add_pad_template (element_class,
296 gst_static_pad_template_get (&gst_mpp_h265_enc_src_template));
297
298 gst_element_class_add_pad_template (element_class,
299 gst_static_pad_template_get (&gst_mpp_h265_enc_sink_template));
300
301 gst_element_class_set_static_metadata (element_class,
302 "Rockchip Mpp H265 Encoder", "Codec/Encoder/Video",
303 "Encode video streams via Rockchip Mpp",
304 "Jeffy Chen <jeffy.chen@rock-chips.com>");
305 }
306
307 gboolean
gst_mpp_h265_enc_register(GstPlugin * plugin,guint rank)308 gst_mpp_h265_enc_register (GstPlugin * plugin, guint rank)
309 {
310 if (!gst_mpp_enc_supported (MPP_VIDEO_CodingHEVC))
311 return FALSE;
312
313 return gst_element_register (plugin, "mpph265enc", rank,
314 gst_mpp_h265_enc_get_type ());
315 }
316