From 7e8050728a26b02317d85afcc9456b1e6630bf32 Mon Sep 17 00:00:00 2001 From: Nicolas Dufresne Date: Tue, 10 Mar 2015 16:45:01 +0000 Subject: mpeg2dec: Re-implement pool handling This is a rewrite of the pool negotiation and configuration. Direct to output decoding is now achieved by configuring the pool using video-alignment. This removes copies when dealing with any elements that supports VideoAlignment, and enable usage of generic video buffer pool, XVImagePool and GLPool. It drops the crop meta implementation for now. https://bugzilla.gnome.org/show_bug.cgi?id=735379 --- ext/mpeg2dec/gstmpeg2dec.c | 384 ++++++++++++++++++++++++--------------------- ext/mpeg2dec/gstmpeg2dec.h | 5 +- 2 files changed, 207 insertions(+), 182 deletions(-) diff --git a/ext/mpeg2dec/gstmpeg2dec.c b/ext/mpeg2dec/gstmpeg2dec.c index 28616c56..73908759 100644 --- a/ext/mpeg2dec/gstmpeg2dec.c +++ b/ext/mpeg2dec/gstmpeg2dec.c @@ -222,6 +222,11 @@ gst_mpeg2dec_stop (GstVideoDecoder * decoder) gst_video_codec_state_unref (mpeg2dec->input_state); mpeg2dec->input_state = NULL; + if (mpeg2dec->downstream_pool) { + gst_buffer_pool_set_active (mpeg2dec->downstream_pool, FALSE); + gst_object_unref (mpeg2dec->downstream_pool); + } + return TRUE; } @@ -237,6 +242,9 @@ gst_mpeg2dec_flush (GstVideoDecoder * decoder) gst_mpeg2dec_clear_buffers (mpeg2dec); + if (mpeg2dec->downstream_pool) + gst_buffer_pool_set_active (mpeg2dec->downstream_pool, FALSE); + return TRUE; } @@ -250,13 +258,24 @@ static gboolean gst_mpeg2dec_decide_allocation (GstVideoDecoder * decoder, GstQuery * query) { GstMpeg2dec *dec = GST_MPEG2DEC (decoder); - GstVideoCodecState *state; GstBufferPool *pool; guint size, min, max; - GstStructure *config; + GstStructure *config, *down_config = NULL; GstAllocator *allocator; GstAllocationParams params; gboolean update_allocator; + gboolean has_videometa = FALSE; + GstCaps *caps; + + /* Get rid of ancient pool */ + if (dec->downstream_pool) { + gst_buffer_pool_set_active (dec->downstream_pool, FALSE); + gst_object_unref (dec->downstream_pool); + dec->downstream_pool = NULL; + } + + /* Get negotiated allocation caps */ + gst_query_parse_allocation (query, &caps, NULL); /* Set allocation parameters to guarantee 16-byte aligned output buffers */ if (gst_query_get_n_allocation_params (query) > 0) { @@ -274,47 +293,133 @@ gst_mpeg2dec_decide_allocation (GstVideoDecoder * decoder, GstQuery * query) gst_query_set_nth_allocation_param (query, 0, allocator, ¶ms); else gst_query_add_allocation_param (query, allocator, ¶ms); - if (allocator) - gst_object_unref (allocator); /* Now chain up to the parent class to guarantee that we can * get a buffer pool from the query */ if (!GST_VIDEO_DECODER_CLASS (parent_class)->decide_allocation (decoder, - query)) + query)) { + if (allocator) + gst_object_unref (allocator); return FALSE; - - state = gst_video_decoder_get_output_state (decoder); + } gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max); - dec->has_cropping = FALSE; config = gst_buffer_pool_get_config (pool); if (gst_query_find_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL)) { gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META); - dec->has_cropping = - gst_query_find_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, - NULL); + has_videometa = TRUE; } - if (dec->has_cropping) { - GstCaps *caps; + if (dec->need_alignment) { + /* If downstream does not support video meta, we will have to copy, keep + * the downstream pool to avoid double copying */ + if (!has_videometa) { + dec->downstream_pool = pool; + pool = NULL; + down_config = gst_structure_copy (config); + config = NULL; + min = 2; + max = 0; + } + + /* In case downstream support video meta, but the downstream pool does not + * have alignment support, discard downstream pool and use video pool */ + else if (!gst_buffer_pool_has_option (pool, + GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT)) { + gst_object_unref (pool); + pool = NULL; + gst_structure_free (config); + config = NULL; + } + + if (!pool) { + pool = gst_video_buffer_pool_new (); + config = gst_buffer_pool_get_config (pool); + gst_buffer_pool_config_set_allocator (config, allocator, ¶ms); + gst_buffer_pool_config_set_params (config, caps, size, min, max); + gst_buffer_pool_config_add_option (config, + GST_BUFFER_POOL_OPTION_VIDEO_META); + } - /* Calculate uncropped size */ - size = MAX (size, dec->decoded_info.size); - caps = gst_video_info_to_caps (&dec->decoded_info); - gst_buffer_pool_config_set_params (config, caps, size, min, max); - gst_caps_unref (caps); + gst_buffer_pool_config_add_option (config, + GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT); + gst_buffer_pool_config_set_video_alignment (config, &dec->valign); } - gst_buffer_pool_set_config (pool, config); + if (allocator) + gst_object_unref (allocator); - gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max); + /* If we are copying out, we'll need to setup and active the other pool */ + if (dec->downstream_pool) { + if (!gst_buffer_pool_set_config (dec->downstream_pool, down_config)) { + down_config = gst_buffer_pool_get_config (dec->downstream_pool); + if (!gst_buffer_pool_config_validate_params (down_config, caps, size, min, + max)) { + gst_structure_free (down_config); + goto config_failed; + } + + if (!gst_buffer_pool_set_config (dec->downstream_pool, down_config)) + goto config_failed; + } + + if (!gst_buffer_pool_set_active (dec->downstream_pool, TRUE)) + goto pool_activation_failed; + } + + /* Now configure the pool, if the pool had made some changes, it will + * return FALSE. Validate the changes ...*/ + if (!gst_buffer_pool_set_config (pool, config)) { + config = gst_buffer_pool_get_config (pool); + + /* Check basic params */ + if (!gst_buffer_pool_config_validate_params (config, caps, size, min, max)) { + gst_structure_free (config); + goto config_failed; + } + + /* If needed, check that resulting alignment is still valid */ + if (dec->need_alignment) { + GstVideoAlignment valign; + + if (!gst_buffer_pool_config_get_video_alignment (config, &valign)) { + gst_structure_free (config); + goto config_failed; + } + + if (valign.padding_left != 0 || valign.padding_top != 0 + || valign.padding_right < dec->valign.padding_right + || valign.padding_bottom < dec->valign.padding_bottom) { + gst_structure_free (config); + goto config_failed; + } + } + + if (!gst_buffer_pool_set_config (pool, config)) + goto config_failed; + /* FIXME Activate the pool, and check that strides are uniform */ + } + + gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max); gst_object_unref (pool); - gst_video_codec_state_unref (state); return TRUE; + +config_failed: + gst_object_unref (pool); + GST_ELEMENT_ERROR (dec, RESOURCE, SETTINGS, + ("Failed to configure buffer pool"), + ("Configuration is most likely invalid, please report this issue.")); + return FALSE; + +pool_activation_failed: + gst_object_unref (pool); + GST_ELEMENT_ERROR (dec, RESOURCE, SETTINGS, + ("Failed to activate downstream buffer pool"), (NULL)); + return FALSE; } static GstFlowReturn @@ -324,9 +429,9 @@ gst_mpeg2dec_crop_buffer (GstMpeg2dec * dec, GstVideoCodecFrame * in_frame, GstVideoCodecState *state; GstVideoInfo *info; GstVideoInfo *dinfo; - guint c, n_planes; GstVideoFrame output_frame; GstFlowReturn ret; + GstBuffer *buffer; state = gst_video_decoder_get_output_state (GST_VIDEO_DECODER (dec)); info = &state->info; @@ -337,39 +442,17 @@ gst_mpeg2dec_crop_buffer (GstMpeg2dec * dec, GstVideoCodecFrame * in_frame, "%ux%u (%" G_GSIZE_FORMAT ")", dinfo->width, dinfo->height, dinfo->size, info->width, info->height, info->size); - ret = - gst_video_decoder_allocate_output_frame (GST_VIDEO_DECODER (dec), - in_frame); + ret = gst_buffer_pool_acquire_buffer (dec->downstream_pool, &buffer, NULL); if (ret != GST_FLOW_OK) goto beach; - if (!gst_video_frame_map (&output_frame, info, in_frame->output_buffer, - GST_MAP_WRITE)) + if (!gst_video_frame_map (&output_frame, info, buffer, GST_MAP_WRITE)) goto map_fail; - n_planes = GST_VIDEO_FRAME_N_PLANES (&output_frame); - for (c = 0; c < n_planes; c++) { - guint w, h, j; - guint8 *sp, *dp; - gint ss, ds; + gst_buffer_replace (&in_frame->output_buffer, buffer); - sp = GST_VIDEO_FRAME_PLANE_DATA (input_vframe, c); - dp = GST_VIDEO_FRAME_PLANE_DATA (&output_frame, c); - - ss = GST_VIDEO_FRAME_PLANE_STRIDE (input_vframe, c); - ds = GST_VIDEO_FRAME_PLANE_STRIDE (&output_frame, c); - - w = MIN (ABS (ss), ABS (ds)); - h = GST_VIDEO_FRAME_COMP_HEIGHT (&output_frame, c); - - GST_CAT_DEBUG (GST_CAT_PERFORMANCE, "copy plane %u, w:%u h:%u ", c, w, h); - - for (j = 0; j < h; j++) { - memcpy (dp, sp, w); - dp += ds; - sp += ss; - } - } + if (!gst_video_frame_copy (&output_frame, input_vframe)) + goto copy_failed; gst_video_frame_unmap (&output_frame); @@ -387,48 +470,13 @@ map_fail: gst_video_codec_state_unref (state); return GST_FLOW_ERROR; } -} -static void -frame_user_data_destroy_notify (GstBuffer * buf) -{ - GST_DEBUG ("Releasing buffer %p", buf); - if (buf) - gst_buffer_unref (buf); -} - -static GstFlowReturn -gst_mpeg2dec_alloc_sized_buf (GstMpeg2dec * mpeg2dec, guint size, - GstVideoCodecFrame * frame, GstBuffer ** buffer) -{ - GstFlowReturn ret = GST_FLOW_OK; - GstVideoCodecState *state; - - state = gst_video_decoder_get_output_state (GST_VIDEO_DECODER (mpeg2dec)); - - if (!mpeg2dec->need_cropping || mpeg2dec->has_cropping) { - /* need parsed input, but that might be slightly bogus, - * so avoid giving up altogether and mark it as error */ - if (frame->output_buffer) { - gst_buffer_replace (&frame->output_buffer, NULL); - GST_VIDEO_DECODER_ERROR (mpeg2dec, 1, STREAM, DECODE, - ("decoding error"), ("Input not correctly parsed"), ret); - } - ret = - gst_video_decoder_allocate_output_frame (GST_VIDEO_DECODER (mpeg2dec), - frame); - *buffer = frame->output_buffer; - } else { - GstAllocationParams params = { 0, 15, 0, 0 }; - - *buffer = gst_buffer_new_allocate (NULL, size, ¶ms); - gst_video_codec_frame_set_user_data (frame, *buffer, - (GDestroyNotify) frame_user_data_destroy_notify); +copy_failed: + { + GST_ERROR_OBJECT (dec, "Failed to copy output frame"); + gst_video_codec_state_unref (state); + return GST_FLOW_ERROR; } - - gst_video_codec_state_unref (state); - - return ret; } typedef struct @@ -504,67 +552,6 @@ gst_mpeg2dec_get_buffer (GstMpeg2dec * mpeg2dec, gint id) return NULL; } -static GstFlowReturn -gst_mpeg2dec_alloc_buffer (GstMpeg2dec * mpeg2dec, GstVideoCodecFrame * frame, - GstBuffer ** buffer) -{ - GstFlowReturn ret; - GstVideoFrame vframe; - guint8 *buf[3]; - - ret = - gst_mpeg2dec_alloc_sized_buf (mpeg2dec, mpeg2dec->decoded_info.size, - frame, buffer); - if (G_UNLIKELY (ret != GST_FLOW_OK)) - goto beach; - - if (mpeg2dec->need_cropping && mpeg2dec->has_cropping) { - GstVideoCropMeta *crop; - GstVideoCodecState *state; - GstVideoInfo *vinfo; - - state = gst_video_decoder_get_output_state (GST_VIDEO_DECODER (mpeg2dec)); - vinfo = &state->info; - - crop = gst_buffer_add_video_crop_meta (frame->output_buffer); - /* we can do things slightly more efficient when we know that - * downstream understands clipping */ - crop->x = 0; - crop->y = 0; - crop->width = vinfo->width; - crop->height = vinfo->height; - - gst_video_codec_state_unref (state); - } - - if (!gst_video_frame_map (&vframe, &mpeg2dec->decoded_info, *buffer, - GST_MAP_READ | GST_MAP_WRITE)) - goto map_fail; - - buf[0] = GST_VIDEO_FRAME_PLANE_DATA (&vframe, 0); - buf[1] = GST_VIDEO_FRAME_PLANE_DATA (&vframe, 1); - buf[2] = GST_VIDEO_FRAME_PLANE_DATA (&vframe, 2); - - GST_DEBUG_OBJECT (mpeg2dec, "set_buf: %p %p %p, frame %i", - buf[0], buf[1], buf[2], frame->system_frame_number); - - /* Note: We use a non-null 'id' value to make the distinction - * between the dummy buffers (which have an id of NULL) and the - * ones we did */ - mpeg2_set_buf (mpeg2dec->decoder, buf, - GINT_TO_POINTER (frame->system_frame_number + 1)); - gst_mpeg2dec_save_buffer (mpeg2dec, frame->system_frame_number, &vframe); - -beach: - return ret; - -map_fail: - { - GST_ERROR_OBJECT (mpeg2dec, "Failed to map frame"); - return GST_FLOW_ERROR; - } -} - static void init_dummybuf (GstMpeg2dec * mpeg2dec) { @@ -588,9 +575,7 @@ handle_sequence (GstMpeg2dec * mpeg2dec, const mpeg2_info_t * info) GstClockTime latency; const mpeg2_sequence_t *sequence; GstVideoCodecState *state; - GstVideoInfo *dinfo = &mpeg2dec->decoded_info; GstVideoInfo *vinfo; - GstVideoInfo pre_crop_info; GstVideoFormat format; sequence = info->sequence; @@ -609,13 +594,21 @@ handle_sequence (GstMpeg2dec * mpeg2dec, const mpeg2_info_t * info) sequence->picture_width, sequence->picture_height, sequence->width, sequence->height); - if (sequence->picture_width != sequence->width || - sequence->picture_height != sequence->height) { + gst_video_alignment_reset (&mpeg2dec->valign); + + if (sequence->picture_width < sequence->width || + sequence->picture_height < sequence->height) { GST_DEBUG_OBJECT (mpeg2dec, "we need to crop"); - mpeg2dec->need_cropping = TRUE; - } else { + mpeg2dec->valign.padding_right = sequence->width - sequence->picture_width; + mpeg2dec->valign.padding_bottom = + sequence->height - sequence->picture_height; + mpeg2dec->need_alignment = TRUE; + } else if (sequence->picture_width == sequence->width || + sequence->picture_height == sequence->height) { GST_DEBUG_OBJECT (mpeg2dec, "no cropping needed"); - mpeg2dec->need_cropping = FALSE; + mpeg2dec->need_alignment = FALSE; + } else { + goto invalid_picture; } /* get subsampling */ @@ -759,15 +752,9 @@ handle_sequence (GstMpeg2dec * mpeg2dec, const mpeg2_info_t * info) sequence->flags & SEQ_FLAG_LOW_DELAY, sequence->flags & SEQ_FLAG_COLOUR_DESCRIPTION); - /* we store the codec size before cropping */ - *dinfo = *vinfo; - gst_video_info_set_format (&pre_crop_info, format, sequence->width, - sequence->height); - dinfo->width = sequence->width; - dinfo->height = sequence->height; - dinfo->size = pre_crop_info.size; - memcpy (dinfo->stride, pre_crop_info.stride, sizeof (pre_crop_info.stride)); - memcpy (dinfo->offset, pre_crop_info.offset, sizeof (pre_crop_info.offset)); + /* Save the padded video information */ + mpeg2dec->decoded_info = *vinfo; + gst_video_info_align (&mpeg2dec->decoded_info, &mpeg2dec->valign); /* Mpeg2dec has 2 frame latency to produce a picture and 1 frame latency in * it's parser */ @@ -805,6 +792,15 @@ invalid_size: return GST_FLOW_ERROR; } +invalid_picture: + { + GST_ERROR_OBJECT (mpeg2dec, "Picture dimension bigger then frame: " + "%d x %d is bigger then %d x %d", sequence->picture_width, + sequence->picture_height, sequence->width, sequence->height); + return GST_FLOW_ERROR; + } + + negotiation_fail: { GST_WARNING_OBJECT (mpeg2dec, "Failed to negotiate with downstream"); @@ -817,14 +813,16 @@ static GstFlowReturn handle_picture (GstMpeg2dec * mpeg2dec, const mpeg2_info_t * info, GstVideoCodecFrame * frame) { + GstVideoDecoder *decoder = (GstVideoDecoder *) mpeg2dec; GstFlowReturn ret; gint type; const gchar *type_str = NULL; gboolean key_frame = FALSE; const mpeg2_picture_t *picture = info->current_picture; - GstBuffer *buffer; + GstVideoFrame vframe; + guint8 *buf[3]; - ret = gst_mpeg2dec_alloc_buffer (mpeg2dec, frame, &buffer); + ret = gst_video_decoder_allocate_output_frame (decoder, frame); if (ret != GST_FLOW_OK) return ret; @@ -856,15 +854,16 @@ handle_picture (GstMpeg2dec * mpeg2dec, const mpeg2_info_t * info, if (GST_VIDEO_INFO_IS_INTERLACED (&mpeg2dec->decoded_info)) { /* This implies SEQ_FLAG_PROGRESSIVE_SEQUENCE is not set */ if (picture->flags & PIC_FLAG_TOP_FIELD_FIRST) { - GST_BUFFER_FLAG_SET (buffer, GST_VIDEO_BUFFER_FLAG_TFF); + GST_BUFFER_FLAG_SET (frame->output_buffer, GST_VIDEO_BUFFER_FLAG_TFF); } if (!(picture->flags & PIC_FLAG_PROGRESSIVE_FRAME)) { - GST_BUFFER_FLAG_SET (buffer, GST_VIDEO_BUFFER_FLAG_INTERLACED); + GST_BUFFER_FLAG_SET (frame->output_buffer, + GST_VIDEO_BUFFER_FLAG_INTERLACED); } #if MPEG2_RELEASE >= MPEG2_VERSION(0,5,0) /* repeat field introduced in 0.5.0 */ if (picture->flags & PIC_FLAG_REPEAT_FIRST_FIELD) { - GST_BUFFER_FLAG_SET (buffer, GST_VIDEO_BUFFER_FLAG_RFF); + GST_BUFFER_FLAG_SET (frame->output_buffer, GST_VIDEO_BUFFER_FLAG_RFF); } #endif } @@ -887,7 +886,32 @@ handle_picture (GstMpeg2dec * mpeg2dec, const mpeg2_info_t * info, (picture->flags & PIC_FLAG_COMPOSITE_DISPLAY ? "composite" : " "), picture->nb_fields, GST_TIME_ARGS (frame->pts)); + if (!gst_video_frame_map (&vframe, &mpeg2dec->decoded_info, + frame->output_buffer, GST_MAP_READ | GST_MAP_WRITE)) + goto map_fail; + + buf[0] = GST_VIDEO_FRAME_PLANE_DATA (&vframe, 0); + buf[1] = GST_VIDEO_FRAME_PLANE_DATA (&vframe, 1); + buf[2] = GST_VIDEO_FRAME_PLANE_DATA (&vframe, 2); + + GST_DEBUG_OBJECT (mpeg2dec, "set_buf: %p %p %p, frame %i", + buf[0], buf[1], buf[2], frame->system_frame_number); + + /* Note: We use a non-null 'id' value to make the distinction + * between the dummy buffers (which have an id of NULL) and the + * ones we did */ + mpeg2_set_buf (mpeg2dec->decoder, buf, + GINT_TO_POINTER (frame->system_frame_number + 1)); + gst_mpeg2dec_save_buffer (mpeg2dec, frame->system_frame_number, &vframe); + return ret; + +map_fail: + { + GST_ELEMENT_ERROR (mpeg2dec, RESOURCE, WRITE, ("Failed to map frame"), + (NULL)); + return GST_FLOW_ERROR; + } } static GstFlowReturn @@ -897,7 +921,6 @@ handle_slice (GstMpeg2dec * mpeg2dec, const mpeg2_info_t * info) GstVideoCodecFrame *frame; const mpeg2_picture_t *picture; gboolean key_frame = FALSE; - GstVideoCodecState *state; GST_DEBUG_OBJECT (mpeg2dec, "fbuf:%p display_picture:%p current_picture:%p fbuf->id:%d", @@ -937,29 +960,30 @@ handle_slice (GstMpeg2dec * mpeg2dec, const mpeg2_info_t * info) return ret; } - state = gst_video_decoder_get_output_state (GST_VIDEO_DECODER (mpeg2dec)); - /* do cropping if the target region is smaller than the input one */ - if (mpeg2dec->need_cropping && !mpeg2dec->has_cropping) { + if (mpeg2dec->downstream_pool) { GstVideoFrame *vframe; if (gst_video_decoder_get_max_decode_time (GST_VIDEO_DECODER (mpeg2dec), frame) < 0) { GST_DEBUG_OBJECT (mpeg2dec, "dropping buffer crop, too late"); - ret = gst_video_decoder_drop_frame (GST_VIDEO_DECODER (mpeg2dec), frame); - goto beach; + return gst_video_decoder_drop_frame (GST_VIDEO_DECODER (mpeg2dec), frame); } - GST_DEBUG_OBJECT (mpeg2dec, "cropping buffer"); + GST_DEBUG_OBJECT (mpeg2dec, "Doing a crop copy of the decoded buffer"); + vframe = gst_mpeg2dec_get_buffer (mpeg2dec, frame->system_frame_number); g_assert (vframe != NULL); ret = gst_mpeg2dec_crop_buffer (mpeg2dec, frame, vframe); + + if (ret != GST_FLOW_OK) { + gst_video_decoder_drop_frame (GST_VIDEO_DECODER (mpeg2dec), frame); + return ret; + } } ret = gst_video_decoder_finish_frame (GST_VIDEO_DECODER (mpeg2dec), frame); -beach: - gst_video_codec_state_unref (state); return ret; no_frame: diff --git a/ext/mpeg2dec/gstmpeg2dec.h b/ext/mpeg2dec/gstmpeg2dec.h index 9623b722..f88459e4 100644 --- a/ext/mpeg2dec/gstmpeg2dec.h +++ b/ext/mpeg2dec/gstmpeg2dec.h @@ -71,8 +71,9 @@ struct _GstMpeg2dec { /* video state */ GstVideoCodecState *input_state; GstVideoInfo decoded_info; - gboolean need_cropping; - gboolean has_cropping; + GstVideoAlignment valign; + GstBufferPool * downstream_pool; + gboolean need_alignment; guint8 *dummybuf[4]; }; -- cgit v1.2.3