1 /*
2 * Copyright 2017 Rockchip Electronics Co., Ltd
3 * Author: Randy Li <randy.li@rock-chips.com>
4 *
5 * Copyright 2021 Rockchip Electronics Co., Ltd
6 * Author: Jeffy Chen <jeffy.chen@rock-chips.com>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 *
23 */
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #include "gstmppvideodec.h"
30
31 #define GST_MPP_VIDEO_DEC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
32 GST_TYPE_MPP_VIDEO_DEC, GstMppVideoDec))
33
34 #define GST_CAT_DEFAULT mpp_video_dec_debug
35 GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
36
37 struct _GstMppVideoDec
38 {
39 GstMppDec parent;
40
41 gint poll_timeout;
42 };
43
44 #define parent_class gst_mpp_video_dec_parent_class
45 G_DEFINE_TYPE (GstMppVideoDec, gst_mpp_video_dec, GST_TYPE_MPP_DEC);
46
47 /* Default output format is auto */
48 static GstVideoFormat DEFAULT_PROP_FORMAT = GST_VIDEO_FORMAT_UNKNOWN;
49 /* Disable ARM AFBC by default */
50 static GstVideoFormat DEFAULT_PROP_ARM_AFBC = FALSE;
51
52 enum
53 {
54 PROP_0,
55 PROP_FORMAT,
56 PROP_ARM_AFBC,
57 PROP_LAST,
58 };
59
60 /* GstVideoDecoder base class method */
61 static GstStaticPadTemplate gst_mpp_video_dec_sink_template =
62 GST_STATIC_PAD_TEMPLATE ("sink",
63 GST_PAD_SINK,
64 GST_PAD_ALWAYS,
65 GST_STATIC_CAPS ("video/x-h263, parsed = (boolean) true;"
66 "video/x-h264, parsed = (boolean) true;"
67 "video/x-h265, parsed = (boolean) true;"
68 "video/x-av1, parsed = (boolean) true;"
69 "video/x-vp8; video/x-vp9;"
70 "video/mpeg, parsed = (boolean) true,"
71 "mpegversion = (int) { 1, 2, 4 }, systemstream = (boolean) false;"));
72
73 static GstStaticPadTemplate gst_mpp_video_dec_src_template =
74 GST_STATIC_PAD_TEMPLATE ("src",
75 GST_PAD_SRC,
76 GST_PAD_ALWAYS,
77 GST_STATIC_CAPS (MPP_DEC_CAPS_MAKE ("{" MPP_DEC_FORMATS "}") ";"
78 MPP_DEC_CAPS_MAKE_AFBC ("{" MPP_DEC_FORMATS "}") ";")
79 );
80
81 static MppCodingType
gst_mpp_video_dec_get_mpp_type(GstStructure * s)82 gst_mpp_video_dec_get_mpp_type (GstStructure * s)
83 {
84 if (gst_structure_has_name (s, "video/x-h263"))
85 return MPP_VIDEO_CodingH263;
86
87 if (gst_structure_has_name (s, "video/x-h264"))
88 return MPP_VIDEO_CodingAVC;
89
90 if (gst_structure_has_name (s, "video/x-h265"))
91 return MPP_VIDEO_CodingHEVC;
92
93 if (gst_structure_has_name (s, "video/x-av1"))
94 return MPP_VIDEO_CodingAV1;
95
96 if (gst_structure_has_name (s, "video/mpeg")) {
97 gint mpegversion = 0;
98 if (gst_structure_get_int (s, "mpegversion", &mpegversion)) {
99 switch (mpegversion) {
100 case 1:
101 case 2:
102 return MPP_VIDEO_CodingMPEG2;
103 case 4:
104 return MPP_VIDEO_CodingMPEG4;
105 default:
106 g_assert_not_reached ();
107 return MPP_VIDEO_CodingUnused;
108 }
109 }
110 }
111
112 if (gst_structure_has_name (s, "video/x-vp8"))
113 return MPP_VIDEO_CodingVP8;
114
115 if (gst_structure_has_name (s, "video/x-vp9"))
116 return MPP_VIDEO_CodingVP9;
117
118 return MPP_VIDEO_CodingUnused;
119 }
120
121 static void
gst_mpp_video_dec_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)122 gst_mpp_video_dec_set_property (GObject * object,
123 guint prop_id, const GValue * value, GParamSpec * pspec)
124 {
125 GstVideoDecoder *decoder = GST_VIDEO_DECODER (object);
126 GstMppDec *mppdec = GST_MPP_DEC (decoder);
127
128 switch (prop_id) {
129 case PROP_FORMAT:{
130 if (mppdec->input_state)
131 GST_WARNING_OBJECT (decoder, "unable to change output format");
132 else
133 mppdec->format = g_value_get_enum (value);
134 break;
135 }
136 case PROP_ARM_AFBC:{
137 if (mppdec->input_state)
138 GST_WARNING_OBJECT (decoder, "unable to change ARM AFBC");
139 else
140 mppdec->arm_afbc = g_value_get_boolean (value);
141 break;
142 }
143 default:
144 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
145 return;
146 }
147 }
148
149 static void
gst_mpp_video_dec_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)150 gst_mpp_video_dec_get_property (GObject * object,
151 guint prop_id, GValue * value, GParamSpec * pspec)
152 {
153 GstVideoDecoder *decoder = GST_VIDEO_DECODER (object);
154 GstMppDec *mppdec = GST_MPP_DEC (decoder);
155
156 switch (prop_id) {
157 case PROP_FORMAT:
158 g_value_set_enum (value, mppdec->format);
159 break;
160 case PROP_ARM_AFBC:
161 g_value_set_boolean (value, mppdec->arm_afbc);
162 break;
163 default:
164 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
165 break;
166 }
167 }
168
169 static gboolean
gst_mpp_video_dec_set_format(GstVideoDecoder * decoder,GstVideoCodecState * state)170 gst_mpp_video_dec_set_format (GstVideoDecoder * decoder,
171 GstVideoCodecState * state)
172 {
173 GstVideoDecoderClass *pclass = GST_VIDEO_DECODER_CLASS (parent_class);
174 GstMppDec *mppdec = GST_MPP_DEC (decoder);
175 GstStructure *structure;
176 const gchar *chroma_format;
177
178 structure = gst_caps_get_structure (state->caps, 0);
179 mppdec->mpp_type = gst_mpp_video_dec_get_mpp_type (structure);
180 g_return_val_if_fail (mppdec->mpp_type != MPP_VIDEO_CodingUnused, FALSE);
181
182 /* MPP doesn't support YUV444 for h264 */
183 if (mppdec->mpp_type == MPP_VIDEO_CodingAVC) {
184 chroma_format = gst_structure_get_string (structure, "chroma-format");
185 if (g_strcmp0 (chroma_format, "4:4:4") == 0)
186 return FALSE;
187 }
188
189 return pclass->set_format (decoder, state);
190 }
191
192 static gboolean
gst_mpp_video_dec_startup(GstVideoDecoder * decoder)193 gst_mpp_video_dec_startup (GstVideoDecoder * decoder)
194 {
195 GstMppVideoDec *self = GST_MPP_VIDEO_DEC (decoder);
196 GstMppDec *mppdec = GST_MPP_DEC (decoder);
197 GstVideoCodecState *state = mppdec->input_state;
198 GstBuffer *codec_data = state->codec_data;
199 GstMapInfo mapinfo = { 0, };
200 MppPacket mpkt;
201
202 /* Send extra codec data */
203 if (codec_data) {
204 gst_buffer_ref (codec_data);
205 gst_buffer_map (codec_data, &mapinfo, GST_MAP_READ);
206 mpp_packet_init (&mpkt, mapinfo.data, mapinfo.size);
207 mpp_packet_set_extra_data (mpkt);
208
209 mppdec->mpi->decode_put_packet (mppdec->mpp_ctx, mpkt);
210
211 mpp_packet_deinit (&mpkt);
212 gst_buffer_unmap (codec_data, &mapinfo);
213 gst_buffer_unref (codec_data);
214 }
215
216 if (mppdec->arm_afbc) {
217 MppFrameFormat mpp_format = MPP_FMT_YUV420SP | MPP_FRAME_FBC_AFBC_V2;
218 mppdec->mpi->control (mppdec->mpp_ctx, MPP_DEC_SET_OUTPUT_FORMAT,
219 &mpp_format);
220 }
221
222 self->poll_timeout = 0;
223
224 return TRUE;
225 }
226
227 static MppPacket
gst_mpp_video_dec_get_mpp_packet(GstVideoDecoder * decoder UNUSED,GstMapInfo * mapinfo)228 gst_mpp_video_dec_get_mpp_packet (GstVideoDecoder * decoder UNUSED,
229 GstMapInfo * mapinfo)
230 {
231 MppPacket mpkt = NULL;
232 mpp_packet_init (&mpkt, mapinfo->data, mapinfo->size);
233 return mpkt;
234 }
235
236 static gboolean
gst_mpp_video_dec_send_mpp_packet(GstVideoDecoder * decoder,MppPacket mpkt,gint timeout_ms)237 gst_mpp_video_dec_send_mpp_packet (GstVideoDecoder * decoder,
238 MppPacket mpkt, gint timeout_ms)
239 {
240 GstMppDec *mppdec = GST_MPP_DEC (decoder);
241 gint interval_ms = 2;
242 MPP_RET ret;
243
244 do {
245 ret = mppdec->mpi->decode_put_packet (mppdec->mpp_ctx, mpkt);
246 if (!ret) {
247 mpp_packet_deinit (&mpkt);
248 return TRUE;
249 }
250
251 g_usleep (interval_ms * 1000);
252 timeout_ms -= interval_ms;
253 } while (timeout_ms > 0);
254
255 return FALSE;
256 }
257
258 static MppFrame
gst_mpp_video_dec_poll_mpp_frame(GstVideoDecoder * decoder,gint timeout_ms)259 gst_mpp_video_dec_poll_mpp_frame (GstVideoDecoder * decoder, gint timeout_ms)
260 {
261 GstMppVideoDec *self = GST_MPP_VIDEO_DEC (decoder);
262 GstMppDec *mppdec = GST_MPP_DEC (decoder);
263 MppFrame mframe = NULL;
264
265 if (self->poll_timeout != timeout_ms) {
266 self->poll_timeout = timeout_ms;
267 mppdec->mpi->control (mppdec->mpp_ctx, MPP_SET_OUTPUT_TIMEOUT, &timeout_ms);
268 }
269
270 mppdec->mpi->decode_get_frame (mppdec->mpp_ctx, &mframe);
271
272 return mframe;
273 }
274
275 static gboolean
gst_mpp_video_dec_shutdown(GstVideoDecoder * decoder,gboolean drain)276 gst_mpp_video_dec_shutdown (GstVideoDecoder * decoder, gboolean drain)
277 {
278 GstMppDec *mppdec = GST_MPP_DEC (decoder);
279 MppPacket mpkt;
280 MPP_RET ret;
281
282 /* It's safe to stop decoding immediately */
283 if (!drain) {
284 /* Interrupt the frame polling */
285 mppdec->mpi->reset (mppdec->mpp_ctx);
286 return FALSE;
287 }
288
289 mpp_packet_init (&mpkt, NULL, 0);
290 mpp_packet_set_eos (mpkt);
291
292 while (1) {
293 ret = mppdec->mpi->decode_put_packet (mppdec->mpp_ctx, mpkt);
294 if (!ret)
295 break;
296
297 g_usleep (1000);
298 }
299
300 mpp_packet_deinit (&mpkt);
301 return TRUE;
302 }
303
304 #define GST_TYPE_MPP_VIDEO_DEC_FORMAT (gst_mpp_video_dec_format_get_type ())
305 static GType
gst_mpp_video_dec_format_get_type(void)306 gst_mpp_video_dec_format_get_type (void)
307 {
308 static GType format = 0;
309
310 if (!format) {
311 static const GEnumValue formats[] = {
312 {GST_VIDEO_FORMAT_UNKNOWN, "Auto", "auto"},
313 {GST_VIDEO_FORMAT_NV12, "NV12", "NV12"},
314 {GST_VIDEO_FORMAT_NV21, "NV21", "NV21"},
315 {GST_VIDEO_FORMAT_I420, "I420", "I420"},
316 {GST_VIDEO_FORMAT_YV12, "YV12", "YV12"},
317 {GST_VIDEO_FORMAT_NV16, "NV16", "NV16"},
318 {GST_VIDEO_FORMAT_NV61, "NV61", "NV61"},
319 {GST_VIDEO_FORMAT_BGR16, "BGR565", "BGR16"},
320 {GST_VIDEO_FORMAT_RGB, "RGB", "RGB"},
321 {GST_VIDEO_FORMAT_BGR, "BGR", "BGR"},
322 {GST_VIDEO_FORMAT_RGBA, "RGBA8888", "RGBA"},
323 {GST_VIDEO_FORMAT_BGRA, "BGRA8888", "BGRA"},
324 {GST_VIDEO_FORMAT_RGBx, "RGBX8888", "RGBx"},
325 {GST_VIDEO_FORMAT_BGRx, "BGRX8888", "BGRx"},
326 {0, NULL, NULL}
327 };
328 format = g_enum_register_static ("GstMppVideoDecFormat", formats);
329 }
330 return format;
331 }
332
333 static void
gst_mpp_video_dec_init(GstMppVideoDec * self)334 gst_mpp_video_dec_init (GstMppVideoDec * self)
335 {
336 GstMppDec *mppdec = GST_MPP_DEC (self);
337 mppdec->format = DEFAULT_PROP_FORMAT;
338 mppdec->arm_afbc = DEFAULT_PROP_ARM_AFBC;
339 }
340
341 static void
gst_mpp_video_dec_setup_default_format(void)342 gst_mpp_video_dec_setup_default_format (void)
343 {
344 GEnumClass *class;
345 GEnumValue *value;
346 const gchar *env;
347
348 env = g_getenv ("GST_MPP_VIDEODEC_DEFAULT_FORMAT");
349 if (!env)
350 return;
351
352 class = g_type_class_ref (GST_TYPE_MPP_VIDEO_DEC_FORMAT);
353
354 value = g_enum_get_value_by_nick (class, env);
355 if (value)
356 DEFAULT_PROP_FORMAT = value->value;
357
358 g_type_class_unref (class);
359 }
360
361 static void
gst_mpp_video_dec_class_init(GstMppVideoDecClass * klass)362 gst_mpp_video_dec_class_init (GstMppVideoDecClass * klass)
363 {
364 GstVideoDecoderClass *decoder_class = GST_VIDEO_DECODER_CLASS (klass);
365 GstMppDecClass *pclass = GST_MPP_DEC_CLASS (klass);
366 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
367 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
368
369 GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "mppvideodec", 0,
370 "MPP video decoder");
371
372 decoder_class->set_format = GST_DEBUG_FUNCPTR (gst_mpp_video_dec_set_format);
373
374 pclass->startup = GST_DEBUG_FUNCPTR (gst_mpp_video_dec_startup);
375 pclass->get_mpp_packet = GST_DEBUG_FUNCPTR (gst_mpp_video_dec_get_mpp_packet);
376 pclass->send_mpp_packet =
377 GST_DEBUG_FUNCPTR (gst_mpp_video_dec_send_mpp_packet);
378 pclass->poll_mpp_frame = GST_DEBUG_FUNCPTR (gst_mpp_video_dec_poll_mpp_frame);
379 pclass->shutdown = GST_DEBUG_FUNCPTR (gst_mpp_video_dec_shutdown);
380
381 gobject_class->set_property =
382 GST_DEBUG_FUNCPTR (gst_mpp_video_dec_set_property);
383 gobject_class->get_property =
384 GST_DEBUG_FUNCPTR (gst_mpp_video_dec_get_property);
385
386 gst_mpp_video_dec_setup_default_format ();
387
388 #ifdef HAVE_RGA
389 g_object_class_install_property (gobject_class, PROP_FORMAT,
390 g_param_spec_enum ("format", "Prefered output format",
391 "Prefered output format",
392 GST_TYPE_MPP_VIDEO_DEC_FORMAT, DEFAULT_PROP_FORMAT,
393 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
394 #endif
395
396 if (g_getenv ("GST_MPP_VIDEODEC_DEFAULT_ARM_AFBC"))
397 DEFAULT_PROP_ARM_AFBC = TRUE;
398
399 g_object_class_install_property (gobject_class, PROP_ARM_AFBC,
400 g_param_spec_boolean ("arm-afbc", "ARM AFBC",
401 "Prefer ARM AFBC compressed format", DEFAULT_PROP_ARM_AFBC,
402 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
403
404 gst_element_class_add_pad_template (element_class,
405 gst_static_pad_template_get (&gst_mpp_video_dec_src_template));
406
407 gst_element_class_add_pad_template (element_class,
408 gst_static_pad_template_get (&gst_mpp_video_dec_sink_template));
409
410 gst_element_class_set_static_metadata (element_class,
411 "Rockchip's MPP video decoder", "Decoder/Video",
412 "Multicodec (HEVC / AVC / VP8 / VP9) hardware decoder",
413 "Randy Li <randy.li@rock-chips.com>, "
414 "Jeffy Chen <jeffy.chen@rock-chips.com>");
415 }
416
417 gboolean
gst_mpp_video_dec_register(GstPlugin * plugin,guint rank)418 gst_mpp_video_dec_register (GstPlugin * plugin, guint rank)
419 {
420 return gst_element_register (plugin, "mppvideodec", rank,
421 gst_mpp_video_dec_get_type ());
422 }
423