summaryrefslogtreecommitdiff
path: root/gst-libs/gst/codecs/gstmpeg2decoder.c
diff options
context:
space:
mode:
Diffstat (limited to 'gst-libs/gst/codecs/gstmpeg2decoder.c')
-rw-r--r--gst-libs/gst/codecs/gstmpeg2decoder.c1161
1 files changed, 1161 insertions, 0 deletions
diff --git a/gst-libs/gst/codecs/gstmpeg2decoder.c b/gst-libs/gst/codecs/gstmpeg2decoder.c
new file mode 100644
index 000000000..62797aa9c
--- /dev/null
+++ b/gst-libs/gst/codecs/gstmpeg2decoder.c
@@ -0,0 +1,1161 @@
+/* GStreamer
+ * Copyright (C) 2020 Intel Corporation
+ * Author: He Junyan <junyan.he@intel.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:gstmpeg2decoder
+ * @title: GstMpeg2Decoder
+ * @short_description: Base class to implement stateless MPEG2 decoders
+ * @sources:
+ * - gstmpeg2picture.h
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "gstmpeg2decoder.h"
+
+GST_DEBUG_CATEGORY (gst_mpeg2_decoder_debug);
+#define GST_CAT_DEFAULT gst_mpeg2_decoder_debug
+
+/* ------------------------------------------------------------------------- */
+/* --- PTS Generator --- */
+/* ------------------------------------------------------------------------- */
+typedef struct _PTSGenerator PTSGenerator;
+struct _PTSGenerator
+{
+ /* The current GOP PTS */
+ GstClockTime gop_pts;
+ /* Max picture PTS */
+ GstClockTime max_pts;
+ /* Absolute GOP TSN */
+ guint gop_tsn;
+ /* Max picture TSN, relative to last GOP TSN */
+ guint max_tsn;
+ /* How many times TSN overflowed since GOP */
+ guint ovl_tsn;
+ /* Last picture TSN */
+ guint lst_tsn;
+ guint fps_n;
+ guint fps_d;
+};
+
+static void
+_pts_init (PTSGenerator * tsg)
+{
+ tsg->gop_pts = GST_CLOCK_TIME_NONE;
+ tsg->max_pts = GST_CLOCK_TIME_NONE;
+ tsg->gop_tsn = 0;
+ tsg->max_tsn = 0;
+ tsg->ovl_tsn = 0;
+ tsg->lst_tsn = 0;
+ tsg->fps_n = 0;
+ tsg->fps_d = 0;
+}
+
+static inline GstClockTime
+_pts_get_duration (PTSGenerator * tsg, guint num_frames)
+{
+ return gst_util_uint64_scale (num_frames, GST_SECOND * tsg->fps_d,
+ tsg->fps_n);
+}
+
+static inline guint
+_pts_get_poc (PTSGenerator * tsg)
+{
+ return tsg->gop_tsn + tsg->ovl_tsn * 1024 + tsg->lst_tsn;
+}
+
+static void
+_pts_set_framerate (PTSGenerator * tsg, guint fps_n, guint fps_d)
+{
+ tsg->fps_n = fps_n;
+ tsg->fps_d = fps_d;
+}
+
+static void
+_pts_sync (PTSGenerator * tsg, GstClockTime gop_pts)
+{
+ guint gop_tsn;
+
+ if (!GST_CLOCK_TIME_IS_VALID (gop_pts) ||
+ (GST_CLOCK_TIME_IS_VALID (tsg->max_pts) && tsg->max_pts >= gop_pts)) {
+ /* Invalid GOP PTS, interpolate from the last known picture PTS */
+ if (GST_CLOCK_TIME_IS_VALID (tsg->max_pts)) {
+ gop_pts = tsg->max_pts + _pts_get_duration (tsg, 1);
+ gop_tsn = tsg->gop_tsn + tsg->ovl_tsn * 1024 + tsg->max_tsn + 1;
+ } else {
+ gop_pts = 0;
+ gop_tsn = 0;
+ }
+ } else {
+ /* Interpolate GOP TSN from this valid PTS */
+ if (GST_CLOCK_TIME_IS_VALID (tsg->gop_pts))
+ gop_tsn = tsg->gop_tsn + gst_util_uint64_scale (gop_pts - tsg->gop_pts +
+ _pts_get_duration (tsg, 1) - 1, tsg->fps_n, GST_SECOND * tsg->fps_d);
+ else
+ gop_tsn = 0;
+ }
+
+ tsg->gop_pts = gop_pts;
+ tsg->gop_tsn = gop_tsn;
+ tsg->max_tsn = 0;
+ tsg->ovl_tsn = 0;
+ tsg->lst_tsn = 0;
+}
+
+static GstClockTime
+_pts_eval (PTSGenerator * tsg, GstClockTime pic_pts, guint pic_tsn)
+{
+ GstClockTime pts;
+
+ if (!GST_CLOCK_TIME_IS_VALID (tsg->gop_pts))
+ tsg->gop_pts = _pts_get_duration (tsg, pic_tsn);
+
+ pts = pic_pts;
+ if (!GST_CLOCK_TIME_IS_VALID (pts))
+ pts = tsg->gop_pts + _pts_get_duration (tsg, tsg->ovl_tsn * 1024 + pic_tsn);
+ else if (pts == tsg->gop_pts) {
+ /* The picture following the GOP header shall be an I-frame.
+ So we can compensate for the GOP start time from here */
+ tsg->gop_pts -= _pts_get_duration (tsg, pic_tsn);
+ }
+
+ if (!GST_CLOCK_TIME_IS_VALID (tsg->max_pts) || tsg->max_pts < pts)
+ tsg->max_pts = pts;
+
+ if (tsg->max_tsn < pic_tsn)
+ tsg->max_tsn = pic_tsn;
+ else if (tsg->max_tsn == 1023 && pic_tsn < tsg->lst_tsn) { /* TSN wrapped */
+ tsg->max_tsn = pic_tsn;
+ tsg->ovl_tsn++;
+ }
+ tsg->lst_tsn = pic_tsn;
+
+ return pts;
+}
+
+#define SEQ_HDR_IS_VALID(hdr) \
+ (((GstMpegVideoSequenceHdr *) hdr)->width > 0 && \
+ ((GstMpegVideoSequenceHdr *) hdr)->height > 0)
+#define SEQ_HDR_INIT(hdr) \
+ *(GstMpegVideoSequenceHdr *) hdr = (GstMpegVideoSequenceHdr) { }
+
+#define SEQ_EXT_IS_VALID(ext) \
+ (((GstMpegVideoSequenceExt *) ext)->profile >= GST_MPEG_VIDEO_PROFILE_422 && \
+ ((GstMpegVideoSequenceExt *) ext)->profile <= \
+ GST_MPEG_VIDEO_PROFILE_SIMPLE)
+#define SEQ_EXT_INIT(ext) \
+ *(GstMpegVideoSequenceExt *) ext = \
+ (GstMpegVideoSequenceExt) { .profile = 0xff, }
+
+#define SEQ_DISPLAY_EXT_IS_VALID(ext) \
+ (((GstMpegVideoSequenceDisplayExt *) ext)->video_format != 0xff)
+#define SEQ_DISPLAY_EXT_INIT(ext) \
+ *(GstMpegVideoSequenceDisplayExt *) ext = \
+ (GstMpegVideoSequenceDisplayExt) { .video_format = 0xff, }
+
+#define SEQ_SCALABLE_EXT_IS_VALID(ext) \
+ (((GstMpegVideoSequenceScalableExt *) ext)->scalable_mode != 0xff)
+#define SEQ_SCALABLE_EXT_INIT(ext) \
+ *(GstMpegVideoSequenceScalableExt *) ext = \
+ (GstMpegVideoSequenceScalableExt) { .scalable_mode = 0xff, }
+
+#define QUANT_MATRIX_EXT_IS_VALID(ext) \
+ (((GstMpegVideoQuantMatrixExt *) ext)->load_intra_quantiser_matrix != 0xff)
+#define QUANT_MATRIX_EXT_INIT(ext) \
+ *(GstMpegVideoQuantMatrixExt *) ext = \
+ (GstMpegVideoQuantMatrixExt) { .load_intra_quantiser_matrix = 0xff, }
+
+#define PIC_HDR_IS_VALID(ext) (((GstMpegVideoPictureHdr *) ext)->tsn != 0xffff)
+#define PIC_HDR_INIT(ext) \
+ *(GstMpegVideoPictureHdr *) ext = \
+ (GstMpegVideoPictureHdr) { .tsn = 0xffff, }
+
+#define PIC_HDR_EXT_IS_VALID(ext) \
+ (((GstMpegVideoPictureExt *) ext)->f_code[0][0] != 0xff)
+#define PIC_HDR_EXT_INIT(ext) \
+ *(GstMpegVideoPictureExt *) ext = \
+ (GstMpegVideoPictureExt) { .f_code[0][0] = 0xff, }
+
+typedef enum
+{
+ GST_MPEG2_DECODER_STATE_GOT_SEQ_HDR = 1 << 0,
+ GST_MPEG2_DECODER_STATE_GOT_SEQ_EXT = 1 << 1,
+ GST_MPEG2_DECODER_STATE_GOT_PIC_HDR = 1 << 2,
+ GST_MPEG2_DECODER_STATE_GOT_PIC_EXT = 1 << 3,
+ GST_MPEG2_DECODER_STATE_GOT_SLICE = 1 << 4,
+
+ GST_MPEG2_DECODER_STATE_VALID_SEQ_HEADERS =
+ (GST_MPEG2_DECODER_STATE_GOT_SEQ_HDR |
+ GST_MPEG2_DECODER_STATE_GOT_SEQ_EXT),
+ GST_MPEG2_DECODER_STATE_VALID_PIC_HEADERS =
+ (GST_MPEG2_DECODER_STATE_GOT_PIC_HDR |
+ GST_MPEG2_DECODER_STATE_GOT_PIC_EXT),
+ GST_MPEG2_DECODER_STATE_VALID_PICTURE =
+ (GST_MPEG2_DECODER_STATE_VALID_SEQ_HEADERS |
+ GST_MPEG2_DECODER_STATE_VALID_PIC_HEADERS |
+ GST_MPEG2_DECODER_STATE_GOT_SLICE)
+} GstMpeg2DecoderState;
+
+struct _GstMpeg2DecoderPrivate
+{
+ gint width;
+ gint height;
+ gint display_width;
+ gint display_height;
+ GstMpegVideoProfile profile;
+ gboolean progressive;
+
+ GstMpegVideoSequenceHdr seq_hdr;
+ GstMpegVideoSequenceExt seq_ext;
+ GstMpegVideoSequenceDisplayExt seq_display_ext;
+ GstMpegVideoSequenceScalableExt seq_scalable_ext;
+ /* some sequence info changed after last new_sequence () */
+ gboolean seq_changed;
+ GstMpegVideoGop gop;
+ GstMpegVideoQuantMatrixExt quant_matrix;
+ GstMpegVideoPictureHdr pic_hdr;
+ GstMpegVideoPictureExt pic_ext;
+
+ GstMpeg2Dpb *dpb;
+ GstMpeg2DecoderState state;
+ PTSGenerator tsg;
+ GstClockTime current_pts;
+
+ GstMpeg2Picture *current_picture;
+ GstVideoCodecFrame *current_frame;
+ GstMpeg2Picture *first_field;
+};
+
+#define parent_class gst_mpeg2_decoder_parent_class
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstMpeg2Decoder, gst_mpeg2_decoder,
+ GST_TYPE_VIDEO_DECODER,
+ G_ADD_PRIVATE (GstMpeg2Decoder);
+ GST_DEBUG_CATEGORY_INIT (gst_mpeg2_decoder_debug, "mpeg2decoder", 0,
+ "Mpeg2 Video Decoder"));
+
+static gboolean gst_mpeg2_decoder_start (GstVideoDecoder * decoder);
+static gboolean gst_mpeg2_decoder_stop (GstVideoDecoder * decoder);
+static gboolean gst_mpeg2_decoder_set_format (GstVideoDecoder * decoder,
+ GstVideoCodecState * state);
+static GstFlowReturn gst_mpeg2_decoder_finish (GstVideoDecoder * decoder);
+static gboolean gst_mpeg2_decoder_flush (GstVideoDecoder * decoder);
+static GstFlowReturn gst_mpeg2_decoder_drain (GstVideoDecoder * decoder);
+static GstFlowReturn gst_mpeg2_decoder_handle_frame (GstVideoDecoder * decoder,
+ GstVideoCodecFrame * frame);
+static gboolean gst_mpeg2_decoder_do_output_picture (GstMpeg2Decoder * self,
+ GstMpeg2Picture * picture);
+
+static void
+gst_mpeg2_decoder_class_init (GstMpeg2DecoderClass * klass)
+{
+ GstVideoDecoderClass *decoder_class = GST_VIDEO_DECODER_CLASS (klass);
+
+ decoder_class->start = GST_DEBUG_FUNCPTR (gst_mpeg2_decoder_start);
+ decoder_class->stop = GST_DEBUG_FUNCPTR (gst_mpeg2_decoder_stop);
+ decoder_class->set_format = GST_DEBUG_FUNCPTR (gst_mpeg2_decoder_set_format);
+ decoder_class->finish = GST_DEBUG_FUNCPTR (gst_mpeg2_decoder_finish);
+ decoder_class->flush = GST_DEBUG_FUNCPTR (gst_mpeg2_decoder_flush);
+ decoder_class->drain = GST_DEBUG_FUNCPTR (gst_mpeg2_decoder_drain);
+ decoder_class->handle_frame =
+ GST_DEBUG_FUNCPTR (gst_mpeg2_decoder_handle_frame);
+}
+
+static void
+gst_mpeg2_decoder_init (GstMpeg2Decoder * self)
+{
+ gst_video_decoder_set_packetized (GST_VIDEO_DECODER (self), TRUE);
+
+ self->priv = gst_mpeg2_decoder_get_instance_private (self);
+
+ SEQ_HDR_INIT (&self->priv->seq_hdr);
+ SEQ_EXT_INIT (&self->priv->seq_ext);
+ SEQ_DISPLAY_EXT_INIT (&self->priv->seq_display_ext);
+ SEQ_SCALABLE_EXT_INIT (&self->priv->seq_scalable_ext);
+ QUANT_MATRIX_EXT_INIT (&self->priv->quant_matrix);
+ PIC_HDR_INIT (&self->priv->pic_hdr);
+ PIC_HDR_EXT_INIT (&self->priv->pic_ext);
+}
+
+static gboolean
+gst_mpeg2_decoder_start (GstVideoDecoder * decoder)
+{
+ GstMpeg2Decoder *self = GST_MPEG2_DECODER (decoder);
+ GstMpeg2DecoderPrivate *priv = self->priv;
+
+ _pts_init (&priv->tsg);
+ priv->dpb = gst_mpeg2_dpb_new ();
+ priv->profile = -1;
+ priv->progressive = TRUE;
+
+ return TRUE;
+}
+
+static gboolean
+gst_mpeg2_decoder_stop (GstVideoDecoder * decoder)
+{
+ GstMpeg2Decoder *self = GST_MPEG2_DECODER (decoder);
+ GstMpeg2DecoderPrivate *priv = self->priv;
+
+ g_clear_pointer (&self->input_state, gst_video_codec_state_unref);
+ g_clear_pointer (&priv->dpb, gst_mpeg2_dpb_free);
+
+ return TRUE;
+}
+
+static gboolean
+gst_mpeg2_decoder_set_format (GstVideoDecoder * decoder,
+ GstVideoCodecState * state)
+{
+ GstMpeg2Decoder *self = GST_MPEG2_DECODER (decoder);
+ GstMpeg2DecoderPrivate *priv = self->priv;
+
+ GST_DEBUG_OBJECT (decoder, "Set format");
+
+ if (self->input_state)
+ gst_video_codec_state_unref (self->input_state);
+
+ self->input_state = gst_video_codec_state_ref (state);
+
+ priv->width = GST_VIDEO_INFO_WIDTH (&state->info);
+ priv->height = GST_VIDEO_INFO_HEIGHT (&state->info);
+
+ return TRUE;
+}
+
+static GstFlowReturn
+gst_mpeg2_decoder_drain (GstVideoDecoder * decoder)
+{
+ GstMpeg2Decoder *self = GST_MPEG2_DECODER (decoder);
+ GstMpeg2DecoderPrivate *priv = self->priv;
+ GstMpeg2Picture *picture;
+
+ while ((picture = gst_mpeg2_dpb_bump (priv->dpb)) != NULL) {
+ gst_mpeg2_decoder_do_output_picture (self, picture);
+ }
+
+ gst_mpeg2_dpb_clear (priv->dpb);
+ return GST_FLOW_OK;
+}
+
+static GstFlowReturn
+gst_mpeg2_decoder_finish (GstVideoDecoder * decoder)
+{
+ GST_DEBUG_OBJECT (decoder, "finish");
+
+ return gst_mpeg2_decoder_drain (decoder);
+}
+
+static gboolean
+gst_mpeg2_decoder_flush (GstVideoDecoder * decoder)
+{
+ GstMpeg2Decoder *self = GST_MPEG2_DECODER (decoder);
+ GstMpeg2DecoderPrivate *priv = self->priv;
+
+ gst_mpeg2_dpb_clear (priv->dpb);
+ priv->state &= GST_MPEG2_DECODER_STATE_VALID_SEQ_HEADERS;
+ PIC_HDR_INIT (&priv->pic_hdr);
+ PIC_HDR_EXT_INIT (&priv->pic_ext);
+
+ return TRUE;
+}
+
+static inline gboolean
+_is_valid_state (GstMpeg2Decoder * decoder, GstMpeg2DecoderState state)
+{
+ GstMpeg2DecoderPrivate *priv = decoder->priv;
+
+ return (priv->state & state) == state;
+}
+
+static void
+gst_mpeg2_decoder_set_latency (GstMpeg2Decoder * decoder)
+{
+ GstCaps *caps;
+ GstClockTime min, max;
+ GstStructure *structure;
+ gint fps_d = 1, fps_n = 0;
+
+ caps = gst_pad_get_current_caps (GST_VIDEO_DECODER_SRC_PAD (decoder));
+ if (!caps)
+ return;
+
+ structure = gst_caps_get_structure (caps, 0);
+ if (gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d)) {
+ if (fps_n == 0) {
+ /* variable framerate: see if we have a max-framerate */
+ gst_structure_get_fraction (structure, "max-framerate", &fps_n, &fps_d);
+ }
+ }
+ gst_caps_unref (caps);
+
+ /* if no fps or variable, then 25/1 */
+ if (fps_n == 0) {
+ fps_n = 25;
+ fps_d = 1;
+ }
+
+ max = gst_util_uint64_scale (2 * GST_SECOND, fps_d, fps_n);
+ min = gst_util_uint64_scale (1 * GST_SECOND, fps_d, fps_n);
+
+ GST_LOG_OBJECT (decoder,
+ "latency min %" G_GUINT64_FORMAT " max %" G_GUINT64_FORMAT, min, max);
+
+ gst_video_decoder_set_latency (GST_VIDEO_DECODER (decoder), min, max);
+}
+
+static gboolean
+gst_mpeg2_decoder_handle_sequence (GstMpeg2Decoder * decoder,
+ GstMpegVideoPacket * packet)
+{
+ GstMpeg2DecoderPrivate *priv = decoder->priv;
+ GstMpegVideoSequenceHdr seq_hdr = { };
+
+ if (!gst_mpeg_video_packet_parse_sequence_header (packet, &seq_hdr)) {
+ GST_ERROR_OBJECT (decoder, "failed to parse sequence header");
+ return FALSE;
+ }
+
+ if (SEQ_HDR_IS_VALID (&priv->seq_hdr) &&
+ !memcmp (&priv->seq_hdr, &seq_hdr, sizeof (GstMpegVideoSequenceHdr)))
+ return TRUE;
+
+ SEQ_EXT_INIT (&priv->seq_ext);
+ SEQ_DISPLAY_EXT_INIT (&priv->seq_display_ext);
+ SEQ_SCALABLE_EXT_INIT (&priv->seq_scalable_ext);
+ QUANT_MATRIX_EXT_INIT (&priv->quant_matrix);
+ PIC_HDR_INIT (&priv->pic_hdr);
+ PIC_HDR_EXT_INIT (&priv->pic_ext);
+
+ priv->seq_hdr = seq_hdr;
+ priv->seq_changed = TRUE;
+
+ priv->width = seq_hdr.width;
+ priv->height = seq_hdr.height;
+ priv->display_width = priv->width;
+ priv->display_height = priv->height;
+
+ _pts_set_framerate (&priv->tsg, seq_hdr.fps_n, seq_hdr.fps_d);
+
+ gst_mpeg2_decoder_set_latency (decoder);
+
+ priv->state = GST_MPEG2_DECODER_STATE_GOT_SEQ_HDR;
+
+ return TRUE;
+}
+
+static gboolean
+gst_mpeg2_decoder_handle_sequence_ext (GstMpeg2Decoder * decoder,
+ GstMpegVideoPacket * packet)
+{
+ GstMpeg2DecoderPrivate *priv = decoder->priv;
+ GstMpegVideoSequenceExt seq_ext = { };
+ guint width, height;
+
+ if (!_is_valid_state (decoder, GST_MPEG2_DECODER_STATE_GOT_SEQ_HDR)) {
+ GST_ERROR_OBJECT (decoder, "no sequence before parsing sequence-extension");
+ return FALSE;
+ }
+
+ if (!gst_mpeg_video_packet_parse_sequence_extension (packet, &seq_ext)) {
+ GST_ERROR_OBJECT (decoder, "failed to parse sequence-extension");
+ return FALSE;
+ }
+
+ if (SEQ_EXT_IS_VALID (&priv->seq_ext) &&
+ !memcmp (&priv->seq_ext, &seq_ext, sizeof (GstMpegVideoSequenceExt)))
+ return TRUE;
+
+ priv->seq_ext = seq_ext;
+ priv->seq_changed = TRUE;
+
+ priv->progressive = seq_ext.progressive;
+
+ if (seq_ext.fps_n_ext && seq_ext.fps_d_ext) {
+ guint fps_n = priv->tsg.fps_n;
+ guint fps_d = priv->tsg.fps_d;
+ fps_n *= seq_ext.fps_n_ext + 1;
+ fps_d *= seq_ext.fps_d_ext + 1;
+ _pts_set_framerate (&priv->tsg, fps_n, fps_d);
+ gst_mpeg2_decoder_set_latency (decoder);
+ }
+
+ width = (priv->width & 0x0fff) | ((guint32) seq_ext.horiz_size_ext << 12);
+ height = (priv->height & 0x0fff) | ((guint32) seq_ext.vert_size_ext << 12);
+ GST_DEBUG_OBJECT (decoder, "video resolution %ux%u", width, height);
+ priv->width = width;
+ priv->height = height;
+
+ priv->profile = seq_ext.profile;
+
+ priv->state |= GST_MPEG2_DECODER_STATE_GOT_SEQ_EXT;
+
+ return TRUE;
+}
+
+static gboolean
+gst_mpeg2_decoder_handle_sequence_display_ext (GstMpeg2Decoder * decoder,
+ GstMpegVideoPacket * packet)
+{
+ GstMpeg2DecoderPrivate *priv = decoder->priv;
+ GstMpegVideoSequenceDisplayExt seq_display_ext = { };
+
+ if (!_is_valid_state (decoder, GST_MPEG2_DECODER_STATE_GOT_SEQ_HDR)) {
+ GST_ERROR_OBJECT (decoder,
+ "no sequence before parsing sequence-display-extension");
+ return FALSE;
+ }
+
+ if (!gst_mpeg_video_packet_parse_sequence_display_extension (packet,
+ &seq_display_ext)) {
+ GST_ERROR_OBJECT (decoder, "failed to parse sequence-display-extension");
+ return FALSE;
+ }
+
+ if (SEQ_DISPLAY_EXT_IS_VALID (&priv->seq_display_ext) &&
+ !memcmp (&priv->seq_display_ext, &seq_display_ext,
+ sizeof (GstMpegVideoSequenceDisplayExt)))
+ return TRUE;
+
+ priv->seq_display_ext = seq_display_ext;
+ priv->seq_changed = TRUE;
+
+ priv->display_width = seq_display_ext.display_horizontal_size;
+ priv->display_height = seq_display_ext.display_vertical_size;
+
+ return TRUE;
+}
+
+static gboolean
+gst_mpeg2_decoder_handle_sequence_scalable_ext (GstMpeg2Decoder * decoder,
+ GstMpegVideoPacket * packet)
+{
+ GstMpeg2DecoderPrivate *priv = decoder->priv;
+ GstMpegVideoSequenceScalableExt seq_scalable_ext = { };
+
+ if (!_is_valid_state (decoder, GST_MPEG2_DECODER_STATE_GOT_SEQ_HDR)) {
+ GST_ERROR_OBJECT (decoder,
+ "no sequence before parsing sequence-scalable-extension");
+ return FALSE;
+ }
+
+ if (!gst_mpeg_video_packet_parse_sequence_scalable_extension (packet,
+ &seq_scalable_ext)) {
+ GST_ERROR_OBJECT (decoder, "failed to parse sequence-scalable-extension");
+ return FALSE;
+ }
+
+ if (SEQ_SCALABLE_EXT_IS_VALID (&priv->seq_scalable_ext) &&
+ !memcmp (&priv->seq_scalable_ext, &seq_scalable_ext,
+ sizeof (GstMpegVideoSequenceScalableExt)))
+ return TRUE;
+
+ priv->seq_scalable_ext = seq_scalable_ext;
+ priv->seq_changed = TRUE;
+
+ return TRUE;
+}
+
+static gboolean
+gst_mpeg2_decoder_handle_quant_matrix_ext (GstMpeg2Decoder * decoder,
+ GstMpegVideoPacket * packet)
+{
+ GstMpeg2DecoderPrivate *priv = decoder->priv;
+ GstMpegVideoQuantMatrixExt matrix_ext = { };
+
+ if (!gst_mpeg_video_packet_parse_quant_matrix_extension (packet, &matrix_ext)) {
+ GST_ERROR_OBJECT (decoder, "failed to parse sequence-scalable-extension");
+ return FALSE;
+ }
+
+ priv->quant_matrix = matrix_ext;
+
+ return TRUE;
+}
+
+static gboolean
+gst_mpeg2_decoder_handle_picture_ext (GstMpeg2Decoder * decoder,
+ GstMpegVideoPacket * packet)
+{
+ GstMpeg2DecoderPrivate *priv = decoder->priv;
+ GstMpegVideoPictureExt pic_ext = { };
+
+ if (!_is_valid_state (decoder,
+ GST_MPEG2_DECODER_STATE_VALID_SEQ_HEADERS |
+ GST_MPEG2_DECODER_STATE_GOT_PIC_HDR)) {
+ GST_ERROR_OBJECT (decoder,
+ "no sequence before parsing sequence-scalable-extension");
+ return FALSE;
+ }
+
+ if (!gst_mpeg_video_packet_parse_picture_extension (packet, &pic_ext)) {
+ GST_ERROR_OBJECT (decoder, "failed to parse picture-extension");
+ return FALSE;
+ }
+
+ if (priv->progressive && !pic_ext.progressive_frame) {
+ GST_WARNING_OBJECT (decoder,
+ "invalid interlaced frame in progressive sequence, fixing");
+ pic_ext.progressive_frame = 1;
+ }
+
+ if (pic_ext.picture_structure == 0 ||
+ (pic_ext.progressive_frame &&
+ pic_ext.picture_structure !=
+ GST_MPEG_VIDEO_PICTURE_STRUCTURE_FRAME)) {
+ GST_WARNING_OBJECT (decoder,
+ "invalid picture_structure %d, replacing with \"frame\"",
+ pic_ext.picture_structure);
+ pic_ext.picture_structure = GST_MPEG_VIDEO_PICTURE_STRUCTURE_FRAME;
+ }
+
+ priv->pic_ext = pic_ext;
+
+ priv->state |= GST_MPEG2_DECODER_STATE_GOT_PIC_EXT;
+
+ return TRUE;
+}
+
+static gboolean
+gst_mpeg2_decoder_handle_gop (GstMpeg2Decoder * decoder,
+ GstMpegVideoPacket * packet)
+{
+ GstMpeg2DecoderPrivate *priv = decoder->priv;
+ GstMpegVideoGop gop = { };
+
+ if (!gst_mpeg_video_packet_parse_gop (packet, &gop)) {
+ GST_ERROR_OBJECT (decoder, "failed to parse GOP");
+ return FALSE;
+ }
+
+ GST_DEBUG_OBJECT (decoder,
+ "GOP %02u:%02u:%02u:%02u (closed_gop %d, broken_link %d)", gop.hour,
+ gop.minute, gop.second, gop.frame, gop.closed_gop, gop.broken_link);
+
+ priv->gop = gop;
+
+ _pts_sync (&priv->tsg, priv->current_frame->pts);
+
+ return TRUE;
+}
+
+static gboolean
+gst_mpeg2_decoder_handle_picture (GstMpeg2Decoder * decoder,
+ GstMpegVideoPacket * packet)
+{
+ GstMpeg2DecoderPrivate *priv = decoder->priv;
+ GstMpegVideoPictureHdr pic_hdr = { };
+ GstMpeg2DecoderClass *klass = GST_MPEG2_DECODER_GET_CLASS (decoder);
+
+ if (!_is_valid_state (decoder, GST_MPEG2_DECODER_STATE_VALID_SEQ_HEADERS)) {
+ GST_ERROR_OBJECT (decoder, "no sequence before parsing picture header");
+ return FALSE;
+ }
+
+ /* 6.1.1.6: Conversely if no sequence_xxx_extension() occurs between
+ the first sequence_header() and the first picture_header() then
+ sequence_xxx_extension() shall not occur in the bitstream. */
+ if (priv->seq_changed && klass->new_sequence) {
+ g_assert (SEQ_HDR_IS_VALID (&priv->seq_hdr));
+
+ priv->seq_changed = FALSE;
+ if (!klass->new_sequence (decoder, &priv->seq_hdr,
+ SEQ_EXT_IS_VALID (&priv->seq_ext) ? &priv->seq_ext : NULL,
+ SEQ_DISPLAY_EXT_IS_VALID (&priv->seq_display_ext) ?
+ &priv->seq_display_ext : NULL,
+ SEQ_SCALABLE_EXT_IS_VALID (&priv->seq_scalable_ext) ?
+ &priv->seq_scalable_ext : NULL)) {
+ GST_ERROR_OBJECT (decoder, "new sequence error");
+ return FALSE;
+ }
+ }
+
+ priv->state &= (GST_MPEG2_DECODER_STATE_GOT_SEQ_HDR |
+ GST_MPEG2_DECODER_STATE_GOT_SEQ_EXT);
+
+ if (!gst_mpeg_video_packet_parse_picture_header (packet, &pic_hdr)) {
+ GST_ERROR_OBJECT (decoder, "failed to parse picture header");
+ return FALSE;
+ }
+
+ priv->pic_hdr = pic_hdr;
+
+ priv->state |= GST_MPEG2_DECODER_STATE_GOT_PIC_HDR;
+
+ return TRUE;
+}
+
+static gboolean
+gst_mpeg2_decoder_start_current_picture (GstMpeg2Decoder * decoder,
+ GstMpeg2Slice * slice)
+{
+ GstMpeg2DecoderPrivate *priv = decoder->priv;
+ GstMpeg2DecoderClass *klass = GST_MPEG2_DECODER_GET_CLASS (decoder);
+ GstMpeg2Picture *prev_picture_ptr, *next_picture_ptr;
+ gboolean ret;
+
+ if (!klass->start_picture)
+ return TRUE;
+
+ gst_mpeg2_dpb_get_neighbours (priv->dpb, priv->current_picture,
+ &prev_picture_ptr, &next_picture_ptr);
+
+ ret = klass->start_picture (decoder, priv->current_picture, slice,
+ prev_picture_ptr, next_picture_ptr);
+
+ if (!ret) {
+ GST_ERROR_OBJECT (decoder, "subclass does not want to start picture");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gst_mpeg2_decoder_ensure_current_picture (GstMpeg2Decoder * decoder,
+ GstMpeg2Slice * slice)
+{
+ GstMpeg2DecoderPrivate *priv = decoder->priv;
+ GstMpeg2DecoderClass *klass = GST_MPEG2_DECODER_GET_CLASS (decoder);
+ GstMpeg2Picture *picture = NULL;
+ gboolean ret = TRUE;
+
+ if (priv->current_picture) {
+ g_assert (_is_valid_state (decoder, GST_MPEG2_DECODER_STATE_GOT_SLICE));
+ return TRUE;
+ }
+
+ if (priv->progressive ||
+ priv->pic_ext.picture_structure ==
+ GST_MPEG_VIDEO_PICTURE_STRUCTURE_FRAME) {
+ g_assert (!_is_valid_state (decoder, GST_MPEG2_DECODER_STATE_GOT_SLICE));
+
+ if (priv->first_field) {
+ GST_WARNING_OBJECT (decoder, "An unmatched first field");
+ gst_mpeg2_picture_clear (&priv->first_field);
+ }
+
+ picture = gst_mpeg2_picture_new ();
+ if (klass->new_picture)
+ ret = klass->new_picture (decoder, priv->current_frame, picture);
+
+ if (!ret) {
+ GST_ERROR_OBJECT (decoder, "subclass does not want accept new picture");
+ gst_mpeg2_picture_unref (picture);
+ return FALSE;
+ }
+
+ picture->structure = GST_MPEG_VIDEO_PICTURE_STRUCTURE_FRAME;
+ } else {
+ gboolean is_first_field = (priv->pic_ext.picture_structure ==
+ GST_MPEG_VIDEO_PICTURE_STRUCTURE_TOP_FIELD) ^
+ (priv->pic_ext.top_field_first == 0);
+
+ if (is_first_field) {
+ picture = gst_mpeg2_picture_new ();
+ if (klass->new_picture)
+ ret = klass->new_picture (decoder, priv->current_frame, picture);
+
+ if (!ret) {
+ GST_ERROR_OBJECT (decoder, "subclass does not want accept new picture");
+ gst_mpeg2_picture_unref (picture);
+ return FALSE;
+ }
+ } else {
+ picture = gst_mpeg2_picture_new ();
+
+ if (priv->first_field == NULL) {
+ GST_WARNING_OBJECT (decoder, "Missing the first field");
+ if (klass->new_picture)
+ ret = klass->new_picture (decoder, priv->current_frame, picture);
+ } else {
+ if (klass->new_field_picture)
+ ret = klass->new_field_picture (decoder, priv->first_field, picture);
+
+ if (ret)
+ picture->first_field = gst_mpeg2_picture_ref (priv->first_field);
+ }
+
+ if (!ret) {
+ GST_ERROR_OBJECT (decoder,
+ "Subclass couldn't handle new field picture");
+ gst_mpeg2_picture_unref (picture);
+ return FALSE;
+ }
+ }
+
+ picture->structure = priv->pic_ext.picture_structure;
+ }
+
+ picture->needed_for_output = TRUE;
+ /* This allows accessing the frame from the picture. */
+ picture->system_frame_number = priv->current_frame->system_frame_number;
+ picture->type = priv->pic_hdr.pic_type;
+ picture->tsn = priv->pic_hdr.tsn;
+ priv->current_pts =
+ _pts_eval (&priv->tsg, priv->current_frame->pts, picture->tsn);
+ picture->pic_order_cnt = _pts_get_poc (&priv->tsg);
+
+ priv->current_picture = picture;
+ GST_LOG_OBJECT (decoder,
+ "Create new picture %p(%s), system number: %d, poc: %d,"
+ " type: 0x%d, first field %p",
+ picture,
+ (picture->structure == GST_MPEG_VIDEO_PICTURE_STRUCTURE_FRAME) ?
+ "frame" : "field",
+ picture->system_frame_number, picture->pic_order_cnt, picture->type,
+ picture->first_field);
+
+ if (!gst_mpeg2_decoder_start_current_picture (decoder, slice))
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+gst_mpeg2_decoder_finish_current_field (GstMpeg2Decoder * decoder)
+{
+ GstMpeg2DecoderPrivate *priv = decoder->priv;
+ GstMpeg2DecoderClass *klass = GST_MPEG2_DECODER_GET_CLASS (decoder);
+ gboolean ret;
+
+ if (priv->current_picture == NULL)
+ return TRUE;
+
+ ret = klass->end_picture (decoder, priv->current_picture);
+ if (!ret) {
+ GST_ERROR_OBJECT (decoder, "subclass end_picture failed");
+ return FALSE;
+ }
+
+ if (priv->current_picture->structure !=
+ GST_MPEG_VIDEO_PICTURE_STRUCTURE_FRAME &&
+ !priv->current_picture->first_field) {
+ priv->first_field = priv->current_picture;
+ priv->current_picture = NULL;
+ } else {
+ GST_WARNING_OBJECT (decoder, "The current picture %p is not %s, should not "
+ "begin another picture. Just discard this.",
+ priv->current_picture, priv->current_picture->structure ==
+ GST_MPEG_VIDEO_PICTURE_STRUCTURE_FRAME ?
+ " a field" : "the first field");
+ gst_mpeg2_picture_clear (&priv->current_picture);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gst_mpeg2_decoder_finish_current_picture (GstMpeg2Decoder * decoder)
+{
+ GstMpeg2DecoderPrivate *priv = decoder->priv;
+ GstMpeg2DecoderClass *klass = GST_MPEG2_DECODER_GET_CLASS (decoder);
+ gboolean ret;
+
+ g_assert (priv->current_picture != NULL);
+
+ ret = klass->end_picture (decoder, priv->current_picture);
+ if (!ret) {
+ GST_ERROR_OBJECT (decoder, "subclass end_picture failed");
+ return FALSE;
+ }
+
+ if (priv->current_picture->structure !=
+ GST_MPEG_VIDEO_PICTURE_STRUCTURE_FRAME &&
+ !priv->current_picture->first_field) {
+ priv->first_field = priv->current_picture;
+ priv->current_picture = NULL;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gst_mpeg2_decoder_handle_slice (GstMpeg2Decoder * decoder,
+ GstMpegVideoPacket * packet)
+{
+ GstMpeg2DecoderPrivate *priv = decoder->priv;
+ GstMpegVideoSliceHdr slice_hdr;
+ GstMpeg2DecoderClass *klass = GST_MPEG2_DECODER_GET_CLASS (decoder);
+ GstMpeg2Slice slice;
+ gboolean ret;
+
+ if (!_is_valid_state (decoder, GST_MPEG2_DECODER_STATE_VALID_PIC_HEADERS)) {
+ GST_ERROR_OBJECT (decoder,
+ "no sequence or picture header before parsing picture header");
+ return FALSE;
+ }
+
+ if (!gst_mpeg_video_packet_parse_slice_header (packet, &slice_hdr,
+ &priv->seq_hdr,
+ SEQ_SCALABLE_EXT_IS_VALID (&priv->seq_scalable_ext) ?
+ &priv->seq_scalable_ext : NULL)) {
+ GST_ERROR_OBJECT (decoder, "failed to parse slice header");
+ return FALSE;
+ }
+
+ slice.header = slice_hdr;
+ slice.packet = *packet;
+ slice.quant_matrix =
+ QUANT_MATRIX_EXT_IS_VALID (&priv->quant_matrix) ?
+ &priv->quant_matrix : NULL;
+ g_assert (PIC_HDR_IS_VALID (&priv->pic_hdr));
+ slice.pic_hdr = &priv->pic_hdr;
+ slice.pic_ext = PIC_HDR_EXT_IS_VALID (&priv->pic_ext) ? &priv->pic_ext : NULL;
+ if (!gst_mpeg2_decoder_ensure_current_picture (decoder, &slice)) {
+ GST_ERROR_OBJECT (decoder, "failed to start current picture");
+ return FALSE;
+ }
+
+ g_assert (klass->decode_slice);
+ ret = klass->decode_slice (decoder, priv->current_picture, &slice);
+ if (!ret) {
+ GST_ERROR_OBJECT (decoder,
+ "Subclass didn't want to decode picture %p (frame_num %d, poc %d)",
+ priv->current_picture, priv->current_picture->system_frame_number,
+ priv->current_picture->pic_order_cnt);
+ return FALSE;
+ }
+
+ priv->state |= GST_MPEG2_DECODER_STATE_GOT_SLICE;
+
+ return TRUE;
+}
+
+static gboolean
+gst_mpeg2_decoder_decode_packet (GstMpeg2Decoder * decoder,
+ GstMpegVideoPacket * packet)
+{
+ GstMpegVideoPacketExtensionCode ext_type;
+ gboolean ret = TRUE;
+
+ GST_LOG_OBJECT (decoder, "Parsing the packet 0x%x, size %d",
+ packet->type, packet->size);
+ switch (packet->type) {
+ case GST_MPEG_VIDEO_PACKET_PICTURE:{
+ ret = gst_mpeg2_decoder_finish_current_field (decoder);
+ if (!ret)
+ break;
+
+ ret = gst_mpeg2_decoder_handle_picture (decoder, packet);
+ break;
+ }
+ case GST_MPEG_VIDEO_PACKET_SEQUENCE:
+ ret = gst_mpeg2_decoder_handle_sequence (decoder, packet);
+ break;
+ case GST_MPEG_VIDEO_PACKET_EXTENSION:
+ ext_type = packet->data[packet->offset] >> 4;
+ GST_LOG_OBJECT (decoder, " Parsing the ext packet 0x%x", ext_type);
+ switch (ext_type) {
+ case GST_MPEG_VIDEO_PACKET_EXT_SEQUENCE:
+ ret = gst_mpeg2_decoder_handle_sequence_ext (decoder, packet);
+ break;
+ case GST_MPEG_VIDEO_PACKET_EXT_SEQUENCE_DISPLAY:
+ ret = gst_mpeg2_decoder_handle_sequence_display_ext (decoder, packet);
+ break;
+ case GST_MPEG_VIDEO_PACKET_EXT_SEQUENCE_SCALABLE:
+ ret =
+ gst_mpeg2_decoder_handle_sequence_scalable_ext (decoder, packet);
+ break;
+ case GST_MPEG_VIDEO_PACKET_EXT_QUANT_MATRIX:
+ ret = gst_mpeg2_decoder_handle_quant_matrix_ext (decoder, packet);
+ break;
+ case GST_MPEG_VIDEO_PACKET_EXT_PICTURE:
+ ret = gst_mpeg2_decoder_handle_picture_ext (decoder, packet);
+ break;
+ default:
+ /* Ignore unknown start-code extensions */
+ ret = TRUE;
+ break;
+ }
+ break;
+ case GST_MPEG_VIDEO_PACKET_SEQUENCE_END:
+ ret = TRUE;
+ break;
+ case GST_MPEG_VIDEO_PACKET_GOP:
+ ret = gst_mpeg2_decoder_handle_gop (decoder, packet);
+ break;
+ case GST_MPEG_VIDEO_PACKET_USER_DATA:
+ ret = TRUE;
+ break;
+ default:
+ if (packet->type >= GST_MPEG_VIDEO_PACKET_SLICE_MIN &&
+ packet->type <= GST_MPEG_VIDEO_PACKET_SLICE_MAX) {
+ ret = gst_mpeg2_decoder_handle_slice (decoder, packet);
+ break;
+ }
+ GST_WARNING_OBJECT (decoder, "unsupported packet type 0x%02x, ignore",
+ packet->type);
+ ret = TRUE;
+ break;
+ }
+
+ return ret;
+}
+
+static GstFlowReturn
+gst_mpeg2_decoder_do_output_picture (GstMpeg2Decoder * decoder,
+ GstMpeg2Picture * to_output)
+{
+ GstMpeg2DecoderClass *klass = GST_MPEG2_DECODER_GET_CLASS (decoder);
+ GstVideoCodecFrame *frame = NULL;
+ GstFlowReturn ret = GST_FLOW_OK;
+
+ frame =
+ gst_video_decoder_get_frame (GST_VIDEO_DECODER (decoder),
+ to_output->system_frame_number);
+ if (!frame) {
+ GST_ERROR_OBJECT (decoder,
+ "No available codec frame with frame number %d",
+ to_output->system_frame_number);
+ gst_mpeg2_picture_unref (to_output);
+ return GST_FLOW_ERROR;
+ }
+
+ g_assert (klass->output_picture);
+ GST_LOG_OBJECT (decoder,
+ "Output picture %p (frame_num %d, poc %d, pts: %" GST_TIME_FORMAT
+ "), from DPB",
+ to_output, to_output->system_frame_number, to_output->pic_order_cnt,
+ GST_TIME_ARGS (frame->pts));
+ ret = klass->output_picture (decoder, frame, to_output);
+
+ gst_video_codec_frame_unref (frame);
+
+ return ret;
+}
+
+static GstFlowReturn
+gst_mpeg2_decoder_output_current_picture (GstMpeg2Decoder * decoder)
+{
+ GstMpeg2DecoderPrivate *priv = decoder->priv;
+ GstMpeg2Picture *picture = priv->current_picture;
+ GstFlowReturn ret = GST_FLOW_OK;
+
+ if (!picture && priv->first_field) {
+ GST_WARNING_OBJECT (decoder, "Missing the second field");
+ picture = priv->first_field;
+ }
+
+ g_assert (picture);
+
+ /* Update the presentation time */
+ priv->current_frame->pts = priv->current_pts;
+
+ gst_mpeg2_dpb_add (priv->dpb, picture);
+
+ GST_LOG_OBJECT (decoder,
+ "Add picture %p (frame_num %d, poc %d, type 0x%x), into DPB", picture,
+ picture->system_frame_number, picture->pic_order_cnt, picture->type);
+
+ while (gst_mpeg2_dpb_need_bump (priv->dpb)) {
+ GstMpeg2Picture *to_output;
+
+ to_output = gst_mpeg2_dpb_bump (priv->dpb);
+ g_assert (to_output);
+ ret = gst_mpeg2_decoder_do_output_picture (decoder, to_output);
+ if (ret != GST_FLOW_OK)
+ break;
+ }
+
+ return ret;
+}
+
+static GstFlowReturn
+gst_mpeg2_decoder_handle_frame (GstVideoDecoder * decoder,
+ GstVideoCodecFrame * frame)
+{
+ GstMpeg2Decoder *self = GST_MPEG2_DECODER (decoder);
+ GstMpeg2DecoderPrivate *priv = self->priv;
+ GstBuffer *in_buf = frame->input_buffer;
+ GstMapInfo map_info;
+ GstMpegVideoPacket packet;
+ GstFlowReturn ret = GST_FLOW_OK;
+ guint offset;
+ gboolean last_one;
+
+ GST_LOG_OBJECT (self, "handle frame, PTS: %" GST_TIME_FORMAT
+ ", DTS: %" GST_TIME_FORMAT " system frame number is %d",
+ GST_TIME_ARGS (GST_BUFFER_PTS (in_buf)),
+ GST_TIME_ARGS (GST_BUFFER_DTS (in_buf)), frame->system_frame_number);
+
+ priv->state &= ~GST_MPEG2_DECODER_STATE_GOT_SLICE;
+
+ priv->current_frame = frame;
+ gst_buffer_map (in_buf, &map_info, GST_MAP_READ);
+
+ offset = 0;
+ last_one = FALSE;
+ while (gst_mpeg_video_parse (&packet, map_info.data, map_info.size, offset)) {
+ /* The packet is the last one */
+ if (packet.size == -1) {
+ if (packet.offset < map_info.size) {
+ packet.size = map_info.size - packet.offset;
+ last_one = TRUE;
+ } else {
+ GST_WARNING_OBJECT (decoder, "Get a packet with wrong size");
+ break;
+ }
+ }
+
+ if (!gst_mpeg2_decoder_decode_packet (self, &packet)) {
+ gst_buffer_unmap (in_buf, &map_info);
+ GST_ERROR_OBJECT (decoder, "failed to handle the packet type 0x%x",
+ packet.type);
+ goto failed;
+ }
+
+ if (last_one)
+ break;
+
+ offset = packet.offset;
+ }
+
+ gst_buffer_unmap (in_buf, &map_info);
+
+ if (!priv->current_picture) {
+ GST_ERROR_OBJECT (decoder, "no valid picture created");
+ goto failed;
+ }
+
+ if (!gst_mpeg2_decoder_finish_current_picture (self)) {
+ GST_ERROR_OBJECT (decoder, "failed to decode the current picture");
+ goto failed;
+ }
+
+ ret = gst_mpeg2_decoder_output_current_picture (self);
+ gst_mpeg2_picture_clear (&priv->current_picture);
+ gst_mpeg2_picture_clear (&priv->first_field);
+ priv->current_frame = NULL;
+ return ret;
+
+failed:
+ {
+ GST_VIDEO_DECODER_ERROR (decoder, 1, STREAM, DECODE,
+ ("failed to handle the frame %d", frame->system_frame_number), (NULL),
+ ret);
+ gst_video_decoder_drop_frame (decoder, frame);
+ gst_mpeg2_picture_clear (&priv->current_picture);
+ gst_mpeg2_picture_clear (&priv->first_field);
+ priv->current_frame = NULL;
+ return ret;
+ }
+}