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