xref: /OK3568_Linux_fs/external/gstreamer-rockchip/gst/rockchipmpp/gstmppdec.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 /*
2  * Copyright 2018 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 "gstmppallocator.h"
30 #include "gstmppdec.h"
31 
32 #define GST_CAT_DEFAULT mpp_dec_debug
33 GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
34 
35 #define parent_class gst_mpp_dec_parent_class
36 G_DEFINE_ABSTRACT_TYPE (GstMppDec, gst_mpp_dec, GST_TYPE_VIDEO_DECODER);
37 
38 #define GST_MPP_DEC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \
39     GST_TYPE_MPP_DEC, GstMppDecClass))
40 
41 #define MPP_OUTPUT_TIMEOUT_MS 200       /* Block timeout for MPP output queue */
42 #define MPP_INPUT_TIMEOUT_MS 2000       /* Block timeout for MPP input queue */
43 
44 #define MPP_TO_GST_PTS(pts) ((pts) * GST_MSECOND)
45 
46 #define GST_MPP_DEC_TASK_STARTED(decoder) \
47     (gst_pad_get_task_state ((decoder)->srcpad) == GST_TASK_STARTED)
48 
49 #define GST_MPP_DEC_MUTEX(decoder) (&GST_MPP_DEC (decoder)->mutex)
50 
51 #define GST_MPP_DEC_LOCK(decoder) \
52   GST_VIDEO_DECODER_STREAM_UNLOCK (decoder); \
53   g_mutex_lock (GST_MPP_DEC_MUTEX (decoder)); \
54   GST_VIDEO_DECODER_STREAM_LOCK (decoder);
55 
56 #define GST_MPP_DEC_UNLOCK(decoder) \
57   g_mutex_unlock (GST_MPP_DEC_MUTEX (decoder));
58 
59 #define DEFAULT_PROP_ROTATION 0
60 #define DEFAULT_PROP_WIDTH 0    /* Original */
61 #define DEFAULT_PROP_HEIGHT 0   /* Original */
62 
63 static gboolean DEFAULT_PROP_IGNORE_ERROR = TRUE;
64 static gboolean DEFAULT_PROP_FAST_MODE = TRUE;
65 static gboolean DEFAULT_PROP_DMA_FEATURE = FALSE;
66 
67 enum
68 {
69   PROP_0,
70   PROP_ROTATION,
71   PROP_WIDTH,
72   PROP_HEIGHT,
73   PROP_CROP_RECTANGLE,
74   PROP_IGNORE_ERROR,
75   PROP_FAST_MODE,
76   PROP_DMA_FEATURE,
77   PROP_LAST,
78 };
79 
80 static void
gst_mpp_dec_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)81 gst_mpp_dec_set_property (GObject * object,
82     guint prop_id, const GValue * value, GParamSpec * pspec)
83 {
84   GstVideoDecoder *decoder = GST_VIDEO_DECODER (object);
85   GstMppDec *self = GST_MPP_DEC (decoder);
86 
87   switch (prop_id) {
88     case PROP_ROTATION:{
89       if (self->input_state)
90         GST_WARNING_OBJECT (decoder, "unable to change rotation");
91       else
92         self->rotation = g_value_get_enum (value);
93       break;
94     }
95 
96     case PROP_WIDTH:{
97       if (self->input_state)
98         GST_WARNING_OBJECT (decoder, "unable to change width");
99       else
100         self->width = g_value_get_uint (value);
101       break;
102     }
103     case PROP_HEIGHT:{
104       if (self->input_state)
105         GST_WARNING_OBJECT (decoder, "unable to change height");
106       else
107         self->height = g_value_get_uint (value);
108       break;
109     }
110     case PROP_CROP_RECTANGLE:{
111       const GValue *v;
112       gint rect[4], i;
113 
114       if (gst_value_array_get_size (value) != 4) {
115         GST_WARNING_OBJECT (decoder, "too less values for crop-rectangle");
116         break;
117       }
118 
119       for (i = 0; i < 4; i++) {
120         v = gst_value_array_get_value (value, i);
121         if (!G_VALUE_HOLDS_INT (v)) {
122           GST_WARNING_OBJECT (decoder, "crop-rectangle needs int values");
123           break;
124         }
125 
126         rect[i] = g_value_get_int (v);
127       }
128 
129       self->crop_x = rect[0];
130       self->crop_y = rect[1];
131       self->crop_w = rect[2];
132       self->crop_h = rect[3];
133 
134       break;
135     }
136     case PROP_IGNORE_ERROR:{
137       if (self->input_state)
138         GST_WARNING_OBJECT (decoder, "unable to change error mode");
139       else
140         self->ignore_error = g_value_get_boolean (value);
141       break;
142     }
143     case PROP_FAST_MODE:{
144       if (self->input_state)
145         GST_WARNING_OBJECT (decoder, "unable to change fast mode");
146       else
147         self->fast_mode = g_value_get_boolean (value);
148       break;
149     }
150     case PROP_DMA_FEATURE:{
151       self->dma_feature = g_value_get_boolean (value);
152       break;
153     }
154     default:
155       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
156       break;
157   }
158 }
159 
160 static void
gst_mpp_dec_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)161 gst_mpp_dec_get_property (GObject * object,
162     guint prop_id, GValue * value, GParamSpec * pspec)
163 {
164   GstVideoDecoder *decoder = GST_VIDEO_DECODER (object);
165   GstMppDec *self = GST_MPP_DEC (decoder);
166 
167   switch (prop_id) {
168     case PROP_ROTATION:
169       g_value_set_enum (value, self->rotation);
170       break;
171     case PROP_WIDTH:
172       g_value_set_uint (value, self->width);
173       break;
174     case PROP_HEIGHT:
175       g_value_set_uint (value, self->height);
176       break;
177     case PROP_IGNORE_ERROR:
178       g_value_set_boolean (value, self->ignore_error);
179       break;
180     case PROP_FAST_MODE:
181       g_value_set_boolean (value, self->fast_mode);
182       break;
183     case PROP_DMA_FEATURE:
184       g_value_set_boolean (value, self->dma_feature);
185       break;
186     default:
187       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
188       return;
189   }
190 }
191 
192 static void
gst_mpp_dec_stop_task(GstVideoDecoder * decoder,gboolean drain)193 gst_mpp_dec_stop_task (GstVideoDecoder * decoder, gboolean drain)
194 {
195   GstMppDecClass *klass = GST_MPP_DEC_GET_CLASS (decoder);
196 
197   if (!GST_MPP_DEC_TASK_STARTED (decoder))
198     return;
199 
200   GST_DEBUG_OBJECT (decoder, "stopping decoding thread");
201 
202   GST_VIDEO_DECODER_STREAM_UNLOCK (decoder);
203   if (klass->shutdown && klass->shutdown (decoder, drain)) {
204     /* Wait for task thread to pause */
205     GstTask *task = decoder->srcpad->task;
206     if (task) {
207       GST_OBJECT_LOCK (task);
208       while (GST_TASK_STATE (task) == GST_TASK_STARTED)
209         GST_TASK_WAIT (task);
210       GST_OBJECT_UNLOCK (task);
211     }
212   }
213 
214   gst_pad_stop_task (decoder->srcpad);
215   GST_VIDEO_DECODER_STREAM_LOCK (decoder);
216 }
217 
218 static void
gst_mpp_dec_reset(GstVideoDecoder * decoder,gboolean drain,gboolean final)219 gst_mpp_dec_reset (GstVideoDecoder * decoder, gboolean drain, gboolean final)
220 {
221   GstMppDec *self = GST_MPP_DEC (decoder);
222 
223   GST_MPP_DEC_LOCK (decoder);
224 
225   GST_DEBUG_OBJECT (self, "resetting");
226 
227   self->flushing = TRUE;
228   self->draining = drain;
229 
230   gst_mpp_dec_stop_task (decoder, drain);
231 
232   self->flushing = final;
233   self->draining = FALSE;
234 
235   self->mpi->reset (self->mpp_ctx);
236   self->task_ret = GST_FLOW_OK;
237   self->decoded_frames = 0;
238 
239   GST_MPP_DEC_UNLOCK (decoder);
240 }
241 
242 static gboolean
gst_mpp_dec_start(GstVideoDecoder * decoder)243 gst_mpp_dec_start (GstVideoDecoder * decoder)
244 {
245   GstMppDec *self = GST_MPP_DEC (decoder);
246 
247   GST_DEBUG_OBJECT (self, "starting");
248 
249   gst_video_info_init (&self->info);
250 
251   if (mpp_create (&self->mpp_ctx, &self->mpi))
252     return FALSE;
253 
254   self->interlace_mode = GST_VIDEO_INTERLACE_MODE_PROGRESSIVE;
255   self->mpp_type = MPP_VIDEO_CodingUnused;
256   self->seen_valid_pts = FALSE;
257   self->convert = FALSE;
258 
259   self->input_state = NULL;
260 
261   self->task_ret = GST_FLOW_OK;
262   self->decoded_frames = 0;
263   self->flushing = FALSE;
264 
265   /* Prefer using MPP PTS */
266   self->use_mpp_pts = TRUE;
267   self->mpp_delta_pts = 0;
268 
269   g_mutex_init (&self->mutex);
270 
271   GST_DEBUG_OBJECT (self, "started");
272 
273   return TRUE;
274 }
275 
276 static void
gst_mpp_dec_clear_allocator(GstVideoDecoder * decoder)277 gst_mpp_dec_clear_allocator (GstVideoDecoder * decoder)
278 {
279   GstMppDec *self = GST_MPP_DEC (decoder);
280   if (self->allocator) {
281     gst_mpp_allocator_set_cacheable (self->allocator, FALSE);
282     gst_object_unref (self->allocator);
283     self->allocator = NULL;
284     self->mpi->control (self->mpp_ctx, MPP_DEC_SET_EXT_BUF_GROUP, NULL);
285   }
286 }
287 
288 static gboolean
gst_mpp_dec_stop(GstVideoDecoder * decoder)289 gst_mpp_dec_stop (GstVideoDecoder * decoder)
290 {
291   GstMppDec *self = GST_MPP_DEC (decoder);
292 
293   GST_DEBUG_OBJECT (self, "stopping");
294 
295   GST_VIDEO_DECODER_STREAM_LOCK (decoder);
296   gst_mpp_dec_reset (decoder, FALSE, TRUE);
297   GST_VIDEO_DECODER_STREAM_UNLOCK (decoder);
298 
299   g_mutex_clear (&self->mutex);
300 
301   mpp_destroy (self->mpp_ctx);
302 
303   if (self->input_state) {
304     gst_video_codec_state_unref (self->input_state);
305     self->input_state = NULL;
306   }
307 
308   gst_mpp_dec_clear_allocator (decoder);
309 
310   GST_DEBUG_OBJECT (self, "stopped");
311 
312   return TRUE;
313 }
314 
315 static gboolean
gst_mpp_dec_flush(GstVideoDecoder * decoder)316 gst_mpp_dec_flush (GstVideoDecoder * decoder)
317 {
318   GST_DEBUG_OBJECT (decoder, "flushing");
319   gst_mpp_dec_reset (decoder, FALSE, FALSE);
320   return TRUE;
321 }
322 
323 static GstFlowReturn
gst_mpp_dec_drain(GstVideoDecoder * decoder)324 gst_mpp_dec_drain (GstVideoDecoder * decoder)
325 {
326   GST_DEBUG_OBJECT (decoder, "draining");
327   gst_mpp_dec_reset (decoder, TRUE, FALSE);
328   return GST_FLOW_OK;
329 }
330 
331 static GstFlowReturn
gst_mpp_dec_finish(GstVideoDecoder * decoder)332 gst_mpp_dec_finish (GstVideoDecoder * decoder)
333 {
334   GST_DEBUG_OBJECT (decoder, "finishing");
335   gst_mpp_dec_reset (decoder, TRUE, FALSE);
336 
337   /* No need to caching buffers after finished */
338   gst_mpp_dec_clear_allocator (decoder);
339 
340   return GST_FLOW_OK;
341 }
342 
343 static gboolean
gst_mpp_dec_set_format(GstVideoDecoder * decoder,GstVideoCodecState * state)344 gst_mpp_dec_set_format (GstVideoDecoder * decoder, GstVideoCodecState * state)
345 {
346   GstMppDec *self = GST_MPP_DEC (decoder);
347 
348   GST_DEBUG_OBJECT (self, "setting format: %" GST_PTR_FORMAT, state->caps);
349 
350   /* The MPP m2vd's PTS is buggy */
351   if (self->mpp_type == MPP_VIDEO_CodingMPEG2)
352     self->use_mpp_pts = FALSE;
353 
354   if (self->input_state) {
355     if (gst_caps_is_strictly_equal (self->input_state->caps, state->caps))
356       return TRUE;
357 
358     gst_mpp_dec_reset (decoder, TRUE, FALSE);
359 
360     /* Clear cached buffers when format info changed */
361     gst_mpp_dec_clear_allocator (decoder);
362 
363     gst_video_codec_state_unref (self->input_state);
364     self->input_state = NULL;
365   } else {
366     /* NOTE: MPP fast mode must be applied before mpp_init() */
367     self->mpi->control (self->mpp_ctx, MPP_DEC_SET_PARSER_FAST_MODE,
368         &self->fast_mode);
369 
370     if (mpp_init (self->mpp_ctx, MPP_CTX_DEC, self->mpp_type)) {
371       GST_ERROR_OBJECT (self, "failed to init mpp ctx");
372       return FALSE;
373     }
374   }
375 
376   if (self->ignore_error)
377     self->mpi->control (self->mpp_ctx, MPP_DEC_SET_DISABLE_ERROR, NULL);
378 
379   self->input_state = gst_video_codec_state_ref (state);
380   return TRUE;
381 }
382 
383 static gboolean
gst_mpp_dec_update_video_info(GstVideoDecoder * decoder,GstVideoFormat format,guint width,guint height,gint hstride,gint vstride,guint align,gboolean afbc)384 gst_mpp_dec_update_video_info (GstVideoDecoder * decoder, GstVideoFormat format,
385     guint width, guint height, gint hstride, gint vstride, guint align,
386     gboolean afbc)
387 {
388   GstMppDec *self = GST_MPP_DEC (decoder);
389   GstVideoInfo *info = &self->info;
390   GstVideoCodecState *output_state;
391 
392   g_return_val_if_fail (format != GST_VIDEO_FORMAT_UNKNOWN, FALSE);
393 
394   /* Sinks like kmssink require width and height align to 2 */
395   output_state = gst_video_decoder_set_output_state (decoder, format,
396       GST_ROUND_UP_2 (width), GST_ROUND_UP_2 (height), self->input_state);
397   output_state->caps = gst_video_info_to_caps (&output_state->info);
398 
399   if (afbc) {
400     if (!self->arm_afbc) {
401       GST_ERROR_OBJECT (self, "AFBC not supported");
402       return FALSE;
403     }
404 
405     gst_caps_set_simple (output_state->caps,
406         MPP_DEC_FEATURE_ARM_AFBC, G_TYPE_INT, afbc, NULL);
407 
408     GST_VIDEO_INFO_SET_AFBC (&output_state->info);
409   } else {
410     GST_VIDEO_INFO_UNSET_AFBC (&output_state->info);
411   }
412 
413   if (self->dma_feature) {
414     GstCaps *tmp_caps = gst_caps_copy (output_state->caps);
415     gst_caps_set_features (tmp_caps, 0,
416         gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_DMABUF, NULL));
417 
418     /* HACK: Expose dmabuf feature when the subset check is hacked */
419     if (gst_caps_is_subset (tmp_caps, output_state->caps))
420       gst_caps_replace (&output_state->caps, tmp_caps);
421 
422     gst_caps_unref (tmp_caps);
423   }
424 
425   *info = output_state->info;
426   gst_video_codec_state_unref (output_state);
427 
428   if (!gst_video_decoder_negotiate (decoder))
429     return FALSE;
430 
431   align = align ? : 2;
432 
433   hstride = hstride ? : GST_MPP_VIDEO_INFO_HSTRIDE (info);
434   hstride = GST_ROUND_UP_N (hstride, align);
435 
436   vstride = vstride ? : GST_MPP_VIDEO_INFO_VSTRIDE (info);
437   vstride = GST_ROUND_UP_N (vstride, align);
438 
439   return gst_mpp_video_info_align (info, hstride, vstride);
440 }
441 
442 gboolean
gst_mpp_dec_update_simple_video_info(GstVideoDecoder * decoder,GstVideoFormat format,guint width,guint height,guint align)443 gst_mpp_dec_update_simple_video_info (GstVideoDecoder * decoder,
444     GstVideoFormat format, guint width, guint height, guint align)
445 {
446   return gst_mpp_dec_update_video_info (decoder, format, width, height, 0, 0,
447       align, FALSE);
448 }
449 
450 void
gst_mpp_dec_fixup_video_info(GstVideoDecoder * decoder,GstVideoFormat format,gint width,gint height)451 gst_mpp_dec_fixup_video_info (GstVideoDecoder * decoder, GstVideoFormat format,
452     gint width, gint height)
453 {
454   GstMppDec *self = GST_MPP_DEC (decoder);
455   GstVideoInfo *info = &self->info;
456 
457   if (self->rotation % 180)
458     SWAP (width, height);
459 
460   /* Figure out output format */
461   if (self->format != GST_VIDEO_FORMAT_UNKNOWN)
462     /* Use specified format */
463     format = self->format;
464   if (format == GST_VIDEO_FORMAT_UNKNOWN)
465     /* Fallback to NV12 */
466     format = GST_VIDEO_FORMAT_NV12;
467 
468 #ifdef HAVE_NV12_10LE40
469   if (format == GST_VIDEO_FORMAT_NV12_10LE40 &&
470       g_getenv ("GST_MPP_DEC_DISABLE_NV12_10"))
471     format = GST_VIDEO_FORMAT_NV12;
472 #endif
473 
474 #ifdef HAVE_NV16_10LE40
475   if (format == GST_VIDEO_FORMAT_NV16_10LE40 &&
476       g_getenv ("GST_MPP_DEC_DISABLE_NV16_10"))
477     format = GST_VIDEO_FORMAT_NV12;
478 #endif
479 
480   gst_mpp_video_info_update_format (info, format,
481       self->width ? : width, self->height ? : height);
482 }
483 
484 static GstFlowReturn
gst_mpp_dec_apply_info_change(GstVideoDecoder * decoder,MppFrame mframe)485 gst_mpp_dec_apply_info_change (GstVideoDecoder * decoder, MppFrame mframe)
486 {
487   GstMppDec *self = GST_MPP_DEC (decoder);
488   GstVideoFormat dst_format, src_format;
489   GstVideoInfo *info = &self->info;
490   MppFrameFormat mpp_format;
491   gint width = mpp_frame_get_width (mframe);
492   gint height = mpp_frame_get_height (mframe);
493   gint hstride = mpp_frame_get_hor_stride (mframe);
494   gint vstride = mpp_frame_get_ver_stride (mframe);
495   gint offset_x = mpp_frame_get_offset_x (mframe);
496   gint offset_y = mpp_frame_get_offset_y (mframe);
497   gint dst_width, dst_height;
498   gboolean afbc;
499 
500   if (hstride % 2 || vstride % 2)
501     return GST_FLOW_NOT_NEGOTIATED;
502 
503   mpp_format = mpp_frame_get_fmt (mframe);
504   afbc = !!MPP_FRAME_FMT_IS_FBC (mpp_format);
505   src_format = gst_mpp_mpp_format_to_gst_format (mpp_format);
506 
507   GST_INFO_OBJECT (self, "applying %s%s %dx%d (%dx%d)",
508       gst_mpp_video_format_to_string (src_format), afbc ? "(AFBC)" : "",
509       width, height, hstride, vstride);
510 
511   /* Figure out final output info */
512   gst_mpp_dec_fixup_video_info (decoder, src_format, width, height);
513   dst_format = GST_VIDEO_INFO_FORMAT (info);
514   dst_width = GST_VIDEO_INFO_WIDTH (info);
515   dst_height = GST_VIDEO_INFO_HEIGHT (info);
516 
517   if (self->rotation || dst_format != src_format ||
518       dst_width != width || dst_height != height) {
519     if (afbc || offset_x || offset_y) {
520       GST_ERROR_OBJECT (self, "unable to convert with AFBC or offsets (%d, %d)",
521           offset_x, offset_y);
522       return GST_FLOW_NOT_NEGOTIATED;
523     }
524 
525     /* Conversion required */
526     GST_INFO_OBJECT (self, "convert from %s (%dx%d) to %s (%dx%d)",
527         gst_mpp_video_format_to_string (src_format), width, height,
528         gst_mpp_video_format_to_string (dst_format), dst_width, dst_height);
529 
530     self->convert = TRUE;
531 
532     hstride = 0;
533     vstride = 0;
534   }
535 
536   if (afbc) {
537     /* HACK: MPP would align width to 64 for AFBC */
538     dst_width = GST_ROUND_UP_64 (dst_width);
539 
540     /* HACK: MPP might have extra offsets for AFBC */
541     dst_height += offset_y;
542     vstride = dst_height;
543 
544     /* HACK: Fake hstride for Rockchip VOP driver */
545     hstride = 0;
546   }
547 
548   if (!gst_mpp_dec_update_video_info (decoder, dst_format,
549           dst_width, dst_height, hstride, vstride, 0, afbc))
550     return GST_FLOW_NOT_NEGOTIATED;
551 
552   return GST_FLOW_OK;
553 }
554 
555 static GstVideoCodecFrame *
gst_mpp_dec_get_frame(GstVideoDecoder * decoder,GstClockTime pts)556 gst_mpp_dec_get_frame (GstVideoDecoder * decoder, GstClockTime pts)
557 {
558   GstMppDec *self = GST_MPP_DEC (decoder);
559   GstVideoCodecFrame *frame;
560   GList *frames, *l;
561   gboolean is_first_frame = !self->decoded_frames;
562   gint i;
563 
564   self->decoded_frames++;
565 
566   frames = gst_video_decoder_get_frames (decoder);
567   if (!frames) {
568     GST_DEBUG_OBJECT (self, "missing frame");
569     return NULL;
570   }
571 
572   /* Choose PTS source when getting the first frame */
573   if (is_first_frame) {
574     frame = frames->data;
575 
576     if (self->use_mpp_pts) {
577       if (!GST_CLOCK_TIME_IS_VALID (pts)) {
578         GST_WARNING_OBJECT (self, "MPP is not able to generate pts");
579         self->use_mpp_pts = FALSE;
580       } else {
581         if (GST_CLOCK_TIME_IS_VALID (frame->pts))
582           self->mpp_delta_pts = frame->pts - MPP_TO_GST_PTS (pts);
583 
584         pts = GST_CLOCK_TIME_NONE;
585 
586         GST_DEBUG_OBJECT (self, "MPP delta pts=%" GST_TIME_FORMAT,
587             GST_TIME_ARGS (self->mpp_delta_pts));
588       }
589     }
590 
591     if (self->use_mpp_pts)
592       GST_DEBUG_OBJECT (self, "using MPP pts");
593     else
594       GST_DEBUG_OBJECT (self, "using original pts");
595 
596     GST_DEBUG_OBJECT (self, "using first frame (#%d)",
597         frame->system_frame_number);
598     goto out;
599   }
600 
601   if (!pts)
602     pts = GST_CLOCK_TIME_NONE;
603 
604   /* Fixup MPP PTS */
605   if (self->use_mpp_pts && GST_CLOCK_TIME_IS_VALID (pts)) {
606     pts = MPP_TO_GST_PTS (pts);
607 
608     /* Apply delta PTS for frame matching */
609     pts += self->mpp_delta_pts;
610   }
611 
612   GST_DEBUG_OBJECT (self, "receiving pts=%" GST_TIME_FORMAT,
613       GST_TIME_ARGS (pts));
614 
615   if (!self->seen_valid_pts) {
616     /* No frame with valid PTS, choose the oldest one */
617     frame = frames->data;
618 
619     GST_DEBUG_OBJECT (self, "using oldest frame (#%d)",
620         frame->system_frame_number);
621     goto out;
622   }
623 
624   /* MPP outputs frames in display order, so let's find the earliest one */
625   for (frame = NULL, l = frames, i = 0; l != NULL; l = l->next, i++) {
626     GstVideoCodecFrame *f = l->data;
627 
628     if (GST_CLOCK_TIME_IS_VALID (f->pts)) {
629       /* Prefer frame with close PTS */
630       if (abs ((gint) f->pts - (gint) pts) < 3 * GST_MSECOND) {
631         frame = f;
632 
633         GST_DEBUG_OBJECT (self, "using matched frame (#%d)",
634             frame->system_frame_number);
635         goto out;
636       }
637 
638       /* Filter out future frames */
639       if (GST_CLOCK_TIME_IS_VALID (pts) && f->pts > pts)
640         continue;
641     } else if (self->interlace_mode == GST_VIDEO_INTERLACE_MODE_MIXED) {
642       /* Consider frames with invalid PTS are decode-only when deinterlaced */
643 
644       /* Delay discarding frames for some broken videos */
645       if (i >= 16) {
646         GST_WARNING_OBJECT (self, "discarding decode-only frame (#%d)",
647             f->system_frame_number);
648 
649         gst_video_codec_frame_ref (f);
650         gst_video_decoder_release_frame (decoder, f);
651         continue;
652       }
653     }
654 
655     /* Find the frame with earliest PTS (including invalid PTS) */
656     if (!frame || frame->pts > f->pts)
657       frame = f;
658   }
659 
660   if (frame)
661     GST_DEBUG_OBJECT (self, "using guested frame (#%d)",
662         frame->system_frame_number);
663 
664 out:
665   if (frame) {
666     gst_video_codec_frame_ref (frame);
667 
668     /* Prefer using MPP PTS */
669     if (self->use_mpp_pts)
670       frame->pts = pts;
671   }
672 
673   g_list_free_full (frames, (GDestroyNotify) gst_video_codec_frame_unref);
674   return frame;
675 }
676 
677 #ifdef HAVE_RGA
678 static gboolean
gst_mpp_dec_rga_convert(GstVideoDecoder * decoder,MppFrame mframe,GstBuffer * buffer)679 gst_mpp_dec_rga_convert (GstVideoDecoder * decoder, MppFrame mframe,
680     GstBuffer * buffer)
681 {
682   GstMppDec *self = GST_MPP_DEC (decoder);
683   GstVideoInfo *info = &self->info;
684   GstMemory *mem;
685   gboolean ret;
686 
687   GST_VIDEO_DECODER_STREAM_UNLOCK (decoder);
688 
689   mem = gst_allocator_alloc (self->allocator, GST_VIDEO_INFO_SIZE (info), NULL);
690   g_return_val_if_fail (mem, FALSE);
691 
692   if (!gst_mpp_rga_convert_from_mpp_frame (mframe, mem, info, self->rotation)) {
693     GST_WARNING_OBJECT (self, "failed to convert");
694     gst_memory_unref (mem);
695     ret = FALSE;
696   } else {
697     gst_buffer_replace_all_memory (buffer, mem);
698     ret = TRUE;
699   }
700 
701   GST_VIDEO_DECODER_STREAM_LOCK (decoder);
702   return ret;
703 }
704 #endif
705 
706 static GstBuffer *
gst_mpp_dec_get_gst_buffer(GstVideoDecoder * decoder,MppFrame mframe)707 gst_mpp_dec_get_gst_buffer (GstVideoDecoder * decoder, MppFrame mframe)
708 {
709   GstMppDec *self = GST_MPP_DEC (decoder);
710   GstVideoInfo *info = &self->info;
711   GstBuffer *buffer;
712   GstMemory *mem;
713   MppBuffer mbuf;
714   gint offset_x = mpp_frame_get_offset_x (mframe);
715   gint offset_y = mpp_frame_get_offset_y (mframe);
716   gint crop_x = self->crop_x;
717   gint crop_y = self->crop_y;
718   guint crop_w = self->crop_w;
719   guint crop_h = self->crop_h;
720   gboolean afbc = !!MPP_FRAME_FMT_IS_FBC (mpp_frame_get_fmt (mframe));
721 
722   if (!self->allocator)
723     return NULL;
724 
725   mbuf = mpp_frame_get_buffer (mframe);
726   if (!mbuf)
727     return NULL;
728 
729   /* Allocated from this MPP group in MPP */
730   mpp_buffer_set_index (mbuf, gst_mpp_allocator_get_index (self->allocator));
731 
732   mem = gst_mpp_allocator_import_mppbuf (self->allocator, mbuf);
733   if (!mem)
734     return NULL;
735 
736   buffer = gst_buffer_new ();
737   if (!buffer) {
738     gst_memory_unref (mem);
739     return NULL;
740   }
741 
742   if (afbc || offset_x || offset_y || crop_x || crop_y || crop_w || crop_h) {
743     GstVideoCropMeta *cmeta = gst_buffer_add_video_crop_meta (buffer);
744     gint width = mpp_frame_get_width (mframe);
745     gint height = mpp_frame_get_height (mframe);
746 
747     cmeta->x = CLAMP (crop_x, 0, width - 1);
748     cmeta->y = CLAMP (crop_y, 0, height - 1);
749 
750     cmeta->width = width - cmeta->x;
751     cmeta->height = height - cmeta->y;
752 
753     if (crop_w && crop_w < cmeta->width)
754       cmeta->width = crop_w;
755 
756     if (crop_h && crop_h < cmeta->height)
757       cmeta->height = crop_h;
758 
759     /* Apply MPP offsets */
760     cmeta->x += offset_x;
761     cmeta->y += offset_y;
762 
763     GST_DEBUG_OBJECT (self, "cropping <%d,%d,%d,%d> within <%d,%d,%d,%d>",
764         cmeta->x, cmeta->y, cmeta->width, cmeta->height, offset_x, offset_y,
765         GST_VIDEO_INFO_WIDTH (info), GST_VIDEO_INFO_HEIGHT (info));
766   }
767 
768   gst_buffer_append_memory (buffer, mem);
769 
770   gst_buffer_add_video_meta_full (buffer, GST_VIDEO_FRAME_FLAG_NONE,
771       GST_VIDEO_INFO_FORMAT (info),
772       GST_VIDEO_INFO_WIDTH (info), GST_VIDEO_INFO_HEIGHT (info),
773       GST_VIDEO_INFO_N_PLANES (info), info->offset, info->stride);
774 
775   if (!self->convert)
776     return buffer;
777 
778 #ifdef HAVE_RGA
779   if (gst_mpp_use_rga ()) {
780     if (!GST_VIDEO_INFO_IS_AFBC (info) && !offset_x && !offset_y &&
781         gst_mpp_dec_rga_convert (decoder, mframe, buffer))
782       return buffer;
783   }
784 #endif
785 
786   GST_WARNING_OBJECT (self, "unable to convert frame");
787 
788   gst_buffer_unref (buffer);
789   return NULL;
790 }
791 
792 static void
gst_mpp_dec_update_interlace_mode(GstVideoDecoder * decoder,GstBuffer * buffer,guint mode)793 gst_mpp_dec_update_interlace_mode (GstVideoDecoder * decoder,
794     GstBuffer * buffer, guint mode)
795 {
796   GstMppDec *self = GST_MPP_DEC (decoder);
797   GstVideoInterlaceMode interlace_mode;
798   GstVideoCodecState *output_state;
799 
800   interlace_mode = GST_VIDEO_INTERLACE_MODE_PROGRESSIVE;
801 
802   switch (mode & MPP_FRAME_FLAG_FIELD_ORDER_MASK) {
803     case MPP_FRAME_FLAG_BOT_FIRST:
804       GST_BUFFER_FLAG_SET (buffer, GST_VIDEO_BUFFER_FLAG_INTERLACED);
805       GST_BUFFER_FLAG_UNSET (buffer, GST_VIDEO_BUFFER_FLAG_TFF);
806       interlace_mode = GST_VIDEO_INTERLACE_MODE_INTERLEAVED;
807       break;
808     case MPP_FRAME_FLAG_TOP_FIRST:
809       GST_BUFFER_FLAG_SET (buffer, GST_VIDEO_BUFFER_FLAG_INTERLACED);
810       GST_BUFFER_FLAG_SET (buffer, GST_VIDEO_BUFFER_FLAG_TFF);
811       interlace_mode = GST_VIDEO_INTERLACE_MODE_INTERLEAVED;
812       break;
813     case MPP_FRAME_FLAG_DEINTERLACED:
814       interlace_mode = GST_VIDEO_INTERLACE_MODE_MIXED;
815       /* fall-through */
816     default:
817       GST_BUFFER_FLAG_UNSET (buffer, GST_VIDEO_BUFFER_FLAG_INTERLACED);
818       GST_BUFFER_FLAG_UNSET (buffer, GST_VIDEO_BUFFER_FLAG_TFF);
819       break;
820   }
821 
822   if (self->interlace_mode != interlace_mode) {
823     output_state = gst_video_decoder_get_output_state (decoder);
824     if (output_state) {
825       GstCaps *caps = gst_caps_copy (output_state->caps);
826       gst_caps_set_simple (caps, "interlace-mode", G_TYPE_STRING,
827           gst_video_interlace_mode_to_string (interlace_mode), NULL);
828       gst_caps_replace (&output_state->caps, caps);
829       gst_caps_unref (caps);
830 
831       GST_VIDEO_INFO_INTERLACE_MODE (&output_state->info) = interlace_mode;
832       self->interlace_mode = interlace_mode;
833       gst_video_codec_state_unref (output_state);
834     }
835   }
836 }
837 
838 static void
gst_mpp_dec_loop(GstVideoDecoder * decoder)839 gst_mpp_dec_loop (GstVideoDecoder * decoder)
840 {
841   GstMppDecClass *klass = GST_MPP_DEC_GET_CLASS (decoder);
842   GstMppDec *self = GST_MPP_DEC (decoder);
843   GstVideoCodecFrame *frame;
844   GstBuffer *buffer;
845   MppFrame mframe;
846   int timeout, mode;
847 
848   timeout = self->flushing ? MPP_TIMEOUT_NON_BLOCK : MPP_OUTPUT_TIMEOUT_MS;
849 
850   mframe = klass->poll_mpp_frame (decoder, timeout);
851   /* Likely due to timeout */
852   if (!mframe)
853     return;
854 
855   GST_VIDEO_DECODER_STREAM_LOCK (decoder);
856 
857   if (mpp_frame_get_info_change (mframe)) {
858     self->mpi->control (self->mpp_ctx, MPP_DEC_SET_INFO_CHANGE_READY, NULL);
859     self->task_ret = gst_mpp_dec_apply_info_change (decoder, mframe);
860     goto info_change;
861   }
862 
863   if (!mpp_frame_get_buffer (mframe))
864     goto out;
865 
866   /* Apply info change when video info not unavaliable (no info-change event) */
867   if (!self->info.size)
868     self->task_ret = gst_mpp_dec_apply_info_change (decoder, mframe);
869 
870   mode = mpp_frame_get_mode (mframe);
871 #ifdef MPP_FRAME_FLAG_IEP_DEI_MASK
872   /* IEP deinterlaced */
873   if (mode & MPP_FRAME_FLAG_IEP_DEI_MASK) {
874 #ifdef MPP_FRAME_FLAG_IEP_DEI_I4O2
875     if (mode & MPP_FRAME_FLAG_IEP_DEI_I4O2) {
876       /* 1 input frame generates 2 deinterlaced MPP frames */
877       static int mpp_i4o2_frames = 0;
878       if (mpp_i4o2_frames++ % 2) {
879         GST_DEBUG_OBJECT (self, "ignore extra MPP frame");
880         goto out;
881       }
882     }
883 #endif
884     mode = MPP_FRAME_FLAG_DEINTERLACED;
885   }
886 #endif
887 
888   frame = gst_mpp_dec_get_frame (decoder, mpp_frame_get_pts (mframe));
889   if (!frame)
890     goto no_frame;
891 
892   if (mpp_frame_get_discard (mframe) || mpp_frame_get_errinfo (mframe))
893     goto error;
894 
895   buffer = gst_mpp_dec_get_gst_buffer (decoder, mframe);
896   if (!buffer)
897     goto error;
898 
899   gst_buffer_resize (buffer, 0, GST_VIDEO_INFO_SIZE (&self->info));
900 
901   gst_mpp_dec_update_interlace_mode (decoder, buffer, mode);
902 
903   /* HACK: Mark lockable to avoid copying in make_writable() while shared */
904   GST_MINI_OBJECT_FLAG_SET (buffer, GST_MINI_OBJECT_FLAG_LOCKABLE);
905 
906   frame->output_buffer = buffer;
907 
908   if (self->flushing && !self->draining)
909     goto drop;
910 
911   GST_DEBUG_OBJECT (self, "finish frame ts=%" GST_TIME_FORMAT,
912       GST_TIME_ARGS (frame->pts));
913 
914   gst_video_decoder_finish_frame (decoder, frame);
915 
916 out:
917   if (mpp_frame_get_eos (mframe)) {
918     GST_INFO_OBJECT (self, "got eos");
919     self->task_ret = GST_FLOW_EOS;
920   }
921 
922   mpp_frame_deinit (&mframe);
923 
924   if (self->task_ret != GST_FLOW_OK) {
925     GST_DEBUG_OBJECT (self, "leaving output thread: %s",
926         gst_flow_get_name (self->task_ret));
927 
928     gst_pad_pause_task (decoder->srcpad);
929   }
930 
931   GST_VIDEO_DECODER_STREAM_UNLOCK (decoder);
932   return;
933 info_change:
934   GST_INFO_OBJECT (self, "video info changed");
935   goto out;
936 no_frame:
937   GST_WARNING_OBJECT (self, "no matched frame");
938   goto out;
939 error:
940   GST_WARNING_OBJECT (self, "can't process this frame");
941   goto drop;
942 drop:
943   GST_DEBUG_OBJECT (self, "drop frame");
944   gst_video_decoder_release_frame (decoder, frame);
945   goto out;
946 }
947 
948 static GstFlowReturn
gst_mpp_dec_handle_frame(GstVideoDecoder * decoder,GstVideoCodecFrame * frame)949 gst_mpp_dec_handle_frame (GstVideoDecoder * decoder, GstVideoCodecFrame * frame)
950 {
951   GstMppDecClass *klass = GST_MPP_DEC_GET_CLASS (decoder);
952   GstMppDec *self = GST_MPP_DEC (decoder);
953   GstMapInfo mapinfo = { 0, };
954   GstBuffer *tmp;
955   GstFlowReturn ret;
956   gint timeout_ms = MPP_INPUT_TIMEOUT_MS;
957   gint interval_ms = 5;
958   MppPacket mpkt = NULL;
959 
960   GST_MPP_DEC_LOCK (decoder);
961 
962   GST_DEBUG_OBJECT (self, "handling frame %d", frame->system_frame_number);
963 
964   if (G_UNLIKELY (self->flushing))
965     goto flushing;
966 
967   if (!self->allocator) {
968     MppBufferGroup group;
969 
970     self->allocator = gst_mpp_allocator_new ();
971     if (!self->allocator)
972       goto no_allocator;
973 
974     group = gst_mpp_allocator_get_mpp_group (self->allocator);
975     self->mpi->control (self->mpp_ctx, MPP_DEC_SET_EXT_BUF_GROUP, group);
976   }
977 
978   if (G_UNLIKELY (!GST_MPP_DEC_TASK_STARTED (decoder))) {
979     if (klass->startup && !klass->startup (decoder))
980       goto not_negotiated;
981 
982     GST_DEBUG_OBJECT (self, "starting decoding thread");
983 
984     gst_pad_start_task (decoder->srcpad,
985         (GstTaskFunction) gst_mpp_dec_loop, decoder, NULL);
986   }
987 
988   GST_VIDEO_DECODER_STREAM_UNLOCK (decoder);
989   gst_buffer_map (frame->input_buffer, &mapinfo, GST_MAP_READ);
990   mpkt = klass->get_mpp_packet (decoder, &mapinfo);
991   GST_VIDEO_DECODER_STREAM_LOCK (decoder);
992   if (!mpkt)
993     goto no_packet;
994 
995   mpp_packet_set_pts (mpkt, self->use_mpp_pts ? -1 : (gint64) frame->pts);
996 
997   if (GST_CLOCK_TIME_IS_VALID (frame->pts))
998     self->seen_valid_pts = TRUE;
999 
1000   while (1) {
1001     GST_VIDEO_DECODER_STREAM_UNLOCK (decoder);
1002     if (klass->send_mpp_packet (decoder, mpkt, interval_ms)) {
1003       GST_VIDEO_DECODER_STREAM_LOCK (decoder);
1004       break;
1005     }
1006     GST_VIDEO_DECODER_STREAM_LOCK (decoder);
1007 
1008     timeout_ms -= interval_ms;
1009     if (timeout_ms <= 0)
1010       goto send_error;
1011   }
1012 
1013   /* NOTE: Sub-class takes over the MPP packet when success */
1014   mpkt = NULL;
1015   gst_buffer_unmap (frame->input_buffer, &mapinfo);
1016 
1017   /* No need to keep input arround */
1018   tmp = frame->input_buffer;
1019   frame->input_buffer = gst_buffer_new ();
1020   gst_buffer_copy_into (frame->input_buffer, tmp,
1021       GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS |
1022       GST_BUFFER_COPY_META, 0, 0);
1023   gst_buffer_unref (tmp);
1024 
1025   gst_video_codec_frame_unref (frame);
1026 
1027   GST_MPP_DEC_UNLOCK (decoder);
1028 
1029   return self->task_ret;
1030 
1031 flushing:
1032   GST_WARNING_OBJECT (self, "flushing");
1033   ret = GST_FLOW_FLUSHING;
1034   goto drop;
1035 no_allocator:
1036   GST_ERROR_OBJECT (self, "failed to create mpp allocator");
1037   ret = GST_FLOW_ERROR;
1038   goto drop;
1039 not_negotiated:
1040   GST_ERROR_OBJECT (self, "not negotiated");
1041   ret = GST_FLOW_NOT_NEGOTIATED;
1042   goto drop;
1043 no_packet:
1044   GST_ERROR_OBJECT (self, "failed to get packet");
1045   ret = GST_FLOW_ERROR;
1046   goto drop;
1047 send_error:
1048   GST_ERROR_OBJECT (self, "failed to send packet");
1049   ret = GST_FLOW_ERROR;
1050   goto drop;
1051 drop:
1052   GST_WARNING_OBJECT (self, "can't handle this frame");
1053 
1054   if (mpkt)
1055     mpp_packet_deinit (&mpkt);
1056 
1057   gst_buffer_unmap (frame->input_buffer, &mapinfo);
1058   gst_video_decoder_release_frame (decoder, frame);
1059 
1060   GST_MPP_DEC_UNLOCK (decoder);
1061 
1062   return ret;
1063 }
1064 
1065 static GstStateChangeReturn
gst_mpp_dec_change_state(GstElement * element,GstStateChange transition)1066 gst_mpp_dec_change_state (GstElement * element, GstStateChange transition)
1067 {
1068   GstVideoDecoder *decoder = GST_VIDEO_DECODER (element);
1069 
1070   if (transition == GST_STATE_CHANGE_PAUSED_TO_READY) {
1071     GST_VIDEO_DECODER_STREAM_LOCK (decoder);
1072     gst_mpp_dec_reset (decoder, FALSE, TRUE);
1073     GST_VIDEO_DECODER_STREAM_UNLOCK (decoder);
1074   }
1075 
1076   return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1077 }
1078 
1079 static void
gst_mpp_dec_init(GstMppDec * self)1080 gst_mpp_dec_init (GstMppDec * self)
1081 {
1082   GstVideoDecoder *decoder = GST_VIDEO_DECODER (self);
1083 
1084   self->ignore_error = DEFAULT_PROP_IGNORE_ERROR;
1085   self->fast_mode = DEFAULT_PROP_FAST_MODE;
1086   self->dma_feature = DEFAULT_PROP_DMA_FEATURE;
1087 
1088   gst_video_decoder_set_packetized (decoder, TRUE);
1089 }
1090 
1091 #ifdef HAVE_RGA
1092 #define GST_TYPE_MPP_DEC_ROTATION (gst_mpp_dec_rotation_get_type ())
1093 static GType
gst_mpp_dec_rotation_get_type(void)1094 gst_mpp_dec_rotation_get_type (void)
1095 {
1096   static GType rotation = 0;
1097 
1098   if (!rotation) {
1099     static const GEnumValue rotations[] = {
1100       {0, "Rotate 0", "0"},
1101       {90, "Rotate 90", "90"},
1102       {180, "Rotate 180", "180"},
1103       {270, "Rotate 270", "270"},
1104       {0, NULL, NULL}
1105     };
1106     rotation = g_enum_register_static ("GstMppDecRotation", rotations);
1107   }
1108   return rotation;
1109 }
1110 #endif
1111 
1112 static void
gst_mpp_dec_class_init(GstMppDecClass * klass)1113 gst_mpp_dec_class_init (GstMppDecClass * klass)
1114 {
1115   GstVideoDecoderClass *decoder_class = GST_VIDEO_DECODER_CLASS (klass);
1116   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1117   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
1118   const gchar *env;
1119 
1120   GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "mppdec", 0, "MPP decoder");
1121 
1122   decoder_class->start = GST_DEBUG_FUNCPTR (gst_mpp_dec_start);
1123   decoder_class->stop = GST_DEBUG_FUNCPTR (gst_mpp_dec_stop);
1124   decoder_class->flush = GST_DEBUG_FUNCPTR (gst_mpp_dec_flush);
1125   decoder_class->drain = GST_DEBUG_FUNCPTR (gst_mpp_dec_drain);
1126   decoder_class->finish = GST_DEBUG_FUNCPTR (gst_mpp_dec_finish);
1127   decoder_class->set_format = GST_DEBUG_FUNCPTR (gst_mpp_dec_set_format);
1128   decoder_class->handle_frame = GST_DEBUG_FUNCPTR (gst_mpp_dec_handle_frame);
1129 
1130   gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_mpp_dec_set_property);
1131   gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_mpp_dec_get_property);
1132 
1133 #ifdef HAVE_RGA
1134   if (!gst_mpp_use_rga ())
1135     goto no_rga;
1136 
1137   g_object_class_install_property (gobject_class, PROP_ROTATION,
1138       g_param_spec_enum ("rotation", "Rotation",
1139           "Rotation",
1140           GST_TYPE_MPP_DEC_ROTATION, DEFAULT_PROP_ROTATION,
1141           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1142 
1143   g_object_class_install_property (gobject_class, PROP_WIDTH,
1144       g_param_spec_uint ("width", "Width",
1145           "Width (0 = original)",
1146           0, G_MAXINT, DEFAULT_PROP_WIDTH,
1147           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1148 
1149   g_object_class_install_property (gobject_class, PROP_HEIGHT,
1150       g_param_spec_uint ("height", "Height",
1151           "Height (0 = original)",
1152           0, G_MAXINT, DEFAULT_PROP_HEIGHT,
1153           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1154 
1155 no_rga:
1156 #endif
1157 
1158   g_object_class_install_property (gobject_class, PROP_CROP_RECTANGLE,
1159       gst_param_spec_array ("crop-rectangle", "Crop Rectangle",
1160           "The crop rectangle ('<x, y, width, height>')",
1161           g_param_spec_int ("rect-value", "Rectangle Value",
1162               "One of x, y, width or height value.", 0, G_MAXINT, 0,
1163               G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS),
1164           G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
1165 
1166   env = g_getenv ("GST_MPP_DEC_DEFAULT_IGNORE_ERROR");
1167   if (env && !strcmp (env, "0"))
1168     DEFAULT_PROP_IGNORE_ERROR = FALSE;
1169 
1170   g_object_class_install_property (gobject_class, PROP_IGNORE_ERROR,
1171       g_param_spec_boolean ("ignore-error", "Ignore error",
1172           "Ignore MPP decode errors", DEFAULT_PROP_IGNORE_ERROR,
1173           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1174 
1175   env = g_getenv ("GST_MPP_DEC_DEFAULT_FAST_MODE");
1176   if (env && !strcmp (env, "0"))
1177     DEFAULT_PROP_FAST_MODE = FALSE;
1178 
1179   g_object_class_install_property (gobject_class, PROP_FAST_MODE,
1180       g_param_spec_boolean ("fast-mode", "Fast mode",
1181           "Enable MPP fast decode mode", DEFAULT_PROP_FAST_MODE,
1182           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1183 
1184   env = g_getenv ("GST_MPP_DEC_DMA_FEATURE");
1185   if (env && !strcmp (env, "1"))
1186     DEFAULT_PROP_DMA_FEATURE = TRUE;
1187 
1188   g_object_class_install_property (gobject_class, PROP_DMA_FEATURE,
1189       g_param_spec_boolean ("dma-feature", "DMA feature",
1190           "Enable GST DMA feature", DEFAULT_PROP_DMA_FEATURE,
1191           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1192 
1193   element_class->change_state = GST_DEBUG_FUNCPTR (gst_mpp_dec_change_state);
1194 }
1195