/* GStreamer * Copyright (C) 2020 Intel Corporation * Author: He Junyan * * 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 #endif #include #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; } static inline gboolean _seq_hdr_is_valid (GstMpegVideoSequenceHdr * hdr) { return hdr->width > 0 && hdr->height > 0; } #define SEQ_HDR_INIT (GstMpegVideoSequenceHdr) { 0, } static inline gboolean _seq_ext_is_valid (GstMpegVideoSequenceExt * ext) { return ext->profile >= GST_MPEG_VIDEO_PROFILE_422 && ext->profile <= GST_MPEG_VIDEO_PROFILE_SIMPLE; } #define SEQ_EXT_INIT (GstMpegVideoSequenceExt) { 0xff, 0, } static inline gboolean _seq_display_ext_is_valid (GstMpegVideoSequenceDisplayExt * ext) { return ext->video_format != 0xff; } #define SEQ_DISPLAY_EXT_INIT (GstMpegVideoSequenceDisplayExt) { 0xff, 0, } static inline gboolean _seq_scalable_ext_is_valid (GstMpegVideoSequenceScalableExt * ext) { return ext->scalable_mode != 0xff; } #define SEQ_SCALABLE_EXT_INIT (GstMpegVideoSequenceScalableExt) { 0xff, 0, } static inline gboolean _quant_matrix_ext_is_valid (GstMpegVideoQuantMatrixExt * ext) { return ext->load_intra_quantiser_matrix != 0xff; } #define QUANT_MATRIX_EXT_INIT (GstMpegVideoQuantMatrixExt) { 0xff, { 0, } } static inline gboolean _pic_hdr_is_valid (GstMpegVideoPictureHdr * hdr) { return hdr->tsn != 0xffff; } #define PIC_HDR_INIT (GstMpegVideoPictureHdr) { 0xffff, 0, } static inline gboolean _pic_hdr_ext_is_valid (GstMpegVideoPictureExt * ext) { return ext->f_code[0][0] != 0xff; } #define PIC_HDR_EXT_INIT \ (GstMpegVideoPictureExt) { { { 0xff, 0, }, { 0, } }, 0, } 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; /* whether we need to drain before new_sequence () */ gboolean need_to_drain; 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; guint preferred_output_delay; /* for delayed output */ GstQueueArray *output_queue; /* used for low-latency vs. high throughput mode decision */ gboolean is_live; gboolean input_state_changed; }; #define UPDATE_FLOW_RETURN(ret,new_ret) G_STMT_START { \ if (*(ret) == GST_FLOW_OK) \ *(ret) = new_ret; \ } G_STMT_END typedef struct { GstVideoCodecFrame *frame; GstMpeg2Picture *picture; GstMpeg2Decoder *self; } GstMpeg2DecoderOutputFrame; #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 gboolean gst_mpeg2_decoder_negotiate (GstVideoDecoder * decoder); 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 void gst_mpeg2_decoder_do_output_picture (GstMpeg2Decoder * self, GstMpeg2Picture * picture, GstFlowReturn * ret); static void gst_mpeg2_decoder_clear_output_frame (GstMpeg2DecoderOutputFrame * output_frame); static void gst_mpeg2_decoder_drain_output_queue (GstMpeg2Decoder * self, guint num, GstFlowReturn * ret); 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->negotiate = GST_DEBUG_FUNCPTR (gst_mpeg2_decoder_negotiate); 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); self->priv->seq_hdr = SEQ_HDR_INIT; self->priv->seq_ext = SEQ_EXT_INIT; self->priv->seq_display_ext = SEQ_DISPLAY_EXT_INIT; self->priv->seq_scalable_ext = SEQ_SCALABLE_EXT_INIT; self->priv->quant_matrix = QUANT_MATRIX_EXT_INIT; self->priv->pic_hdr = PIC_HDR_INIT; self->priv->pic_ext = PIC_HDR_EXT_INIT; } 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; priv->output_queue = gst_queue_array_new_for_struct (sizeof (GstMpeg2DecoderOutputFrame), 1); gst_queue_array_set_clear_func (priv->output_queue, (GDestroyNotify) gst_mpeg2_decoder_clear_output_frame); 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); gst_queue_array_free (priv->output_queue); return TRUE; } static gboolean gst_mpeg2_decoder_set_format (GstVideoDecoder * decoder, GstVideoCodecState * state) { GstMpeg2Decoder *self = GST_MPEG2_DECODER (decoder); GstMpeg2DecoderPrivate *priv = self->priv; GstQuery *query; GST_DEBUG_OBJECT (decoder, "Set format"); priv->input_state_changed = TRUE; 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); query = gst_query_new_latency (); if (gst_pad_peer_query (GST_VIDEO_DECODER_SINK_PAD (self), query)) gst_query_parse_latency (query, &priv->is_live, NULL, NULL); gst_query_unref (query); return TRUE; } static gboolean gst_mpeg2_decoder_negotiate (GstVideoDecoder * decoder) { GstMpeg2Decoder *self = GST_MPEG2_DECODER (decoder); /* output state must be updated by subclass using new input state already */ self->priv->input_state_changed = FALSE; return GST_VIDEO_DECODER_CLASS (parent_class)->negotiate (decoder); } static GstFlowReturn gst_mpeg2_decoder_drain (GstVideoDecoder * decoder) { GstMpeg2Decoder *self = GST_MPEG2_DECODER (decoder); GstMpeg2DecoderPrivate *priv = self->priv; GstMpeg2Picture *picture; GstFlowReturn ret = GST_FLOW_OK; while ((picture = gst_mpeg2_dpb_bump (priv->dpb)) != NULL) { gst_mpeg2_decoder_do_output_picture (self, picture, &ret); } gst_mpeg2_decoder_drain_output_queue (self, 0, &ret); gst_queue_array_clear (priv->output_queue); gst_mpeg2_dpb_clear (priv->dpb); return ret; } static GstFlowReturn gst_mpeg2_decoder_finish (GstVideoDecoder * decoder) { 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); gst_queue_array_clear (priv->output_queue); priv->state &= GST_MPEG2_DECODER_STATE_VALID_SEQ_HEADERS; priv->pic_hdr = PIC_HDR_INIT; priv->pic_ext = PIC_HDR_EXT_INIT; 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; GstMpeg2DecoderPrivate *priv = decoder->priv; GstStructure *structure; gint fps_d = 1, fps_n = 0; if (priv->tsg.fps_d > 0 && priv->tsg.fps_n > 0) { fps_n = priv->tsg.fps_n; fps_d = priv->tsg.fps_d; } else { caps = gst_pad_get_current_caps (GST_VIDEO_DECODER_SINK_PAD (decoder)); if (caps) { 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 GstFlowReturn gst_mpeg2_decoder_handle_sequence (GstMpeg2Decoder * decoder, GstMpegVideoPacket * packet) { GstMpeg2DecoderPrivate *priv = decoder->priv; GstMpegVideoSequenceHdr seq_hdr = { 0, }; if (!gst_mpeg_video_packet_parse_sequence_header (packet, &seq_hdr)) { GST_ERROR_OBJECT (decoder, "failed to parse sequence header"); return GST_FLOW_ERROR; } /* 6.1.1.6 Sequence header The quantisation matrices may be redefined each time that a sequence header occurs in the bitstream */ priv->quant_matrix = QUANT_MATRIX_EXT_INIT; if (_seq_hdr_is_valid (&priv->seq_hdr) && memcmp (&priv->seq_hdr, &seq_hdr, sizeof (seq_hdr)) == 0) return GST_FLOW_OK; priv->seq_ext = SEQ_EXT_INIT; priv->seq_display_ext = SEQ_DISPLAY_EXT_INIT; priv->seq_scalable_ext = SEQ_SCALABLE_EXT_INIT; priv->pic_ext = PIC_HDR_EXT_INIT; priv->seq_hdr = seq_hdr; priv->seq_changed = TRUE; if (priv->width != seq_hdr.width || priv->height != seq_hdr.height) { priv->need_to_drain = 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 GST_FLOW_OK; } static GstFlowReturn gst_mpeg2_decoder_handle_sequence_ext (GstMpeg2Decoder * decoder, GstMpegVideoPacket * packet) { GstMpeg2DecoderPrivate *priv = decoder->priv; GstMpegVideoSequenceExt seq_ext = { 0, }; 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 GST_FLOW_ERROR; } if (!gst_mpeg_video_packet_parse_sequence_extension (packet, &seq_ext)) { GST_ERROR_OBJECT (decoder, "failed to parse sequence-extension"); return GST_FLOW_ERROR; } if (_seq_ext_is_valid (&priv->seq_ext) && memcmp (&priv->seq_ext, &seq_ext, sizeof (seq_ext)) == 0) return GST_FLOW_OK; priv->seq_ext = seq_ext; priv->seq_changed = TRUE; 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); if (priv->width != width || priv->height != height || priv->profile != seq_ext.profile || priv->progressive != seq_ext.progressive) { priv->need_to_drain = TRUE; priv->width = width; priv->height = height; priv->profile = seq_ext.profile; priv->progressive = seq_ext.progressive; GST_DEBUG_OBJECT (decoder, "video resolution %ux%u, profile %d," " progressive %d", priv->width, priv->height, priv->profile, priv->progressive); } priv->state |= GST_MPEG2_DECODER_STATE_GOT_SEQ_EXT; return GST_FLOW_OK; } static GstFlowReturn gst_mpeg2_decoder_handle_sequence_display_ext (GstMpeg2Decoder * decoder, GstMpegVideoPacket * packet) { GstMpeg2DecoderPrivate *priv = decoder->priv; GstMpegVideoSequenceDisplayExt seq_display_ext = { 0, }; if (!_is_valid_state (decoder, GST_MPEG2_DECODER_STATE_GOT_SEQ_HDR)) { GST_ERROR_OBJECT (decoder, "no sequence before parsing sequence-display-extension"); return GST_FLOW_ERROR; } if (!gst_mpeg_video_packet_parse_sequence_display_extension (packet, &seq_display_ext)) { GST_ERROR_OBJECT (decoder, "failed to parse sequence-display-extension"); return GST_FLOW_ERROR; } if (_seq_display_ext_is_valid (&priv->seq_display_ext) && memcmp (&priv->seq_display_ext, &seq_display_ext, sizeof (seq_display_ext)) == 0) return GST_FLOW_OK; 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 GST_FLOW_OK; } static GstFlowReturn gst_mpeg2_decoder_handle_sequence_scalable_ext (GstMpeg2Decoder * decoder, GstMpegVideoPacket * packet) { GstMpeg2DecoderPrivate *priv = decoder->priv; GstMpegVideoSequenceScalableExt seq_scalable_ext = { 0, }; if (!_is_valid_state (decoder, GST_MPEG2_DECODER_STATE_GOT_SEQ_HDR)) { GST_ERROR_OBJECT (decoder, "no sequence before parsing sequence-scalable-extension"); return GST_FLOW_ERROR; } if (!gst_mpeg_video_packet_parse_sequence_scalable_extension (packet, &seq_scalable_ext)) { GST_ERROR_OBJECT (decoder, "failed to parse sequence-scalable-extension"); return GST_FLOW_ERROR; } if (_seq_scalable_ext_is_valid (&priv->seq_scalable_ext) && memcmp (&priv->seq_scalable_ext, &seq_scalable_ext, sizeof (seq_scalable_ext)) == 0) return GST_FLOW_OK; priv->seq_scalable_ext = seq_scalable_ext; priv->seq_changed = TRUE; return GST_FLOW_OK; } static GstFlowReturn gst_mpeg2_decoder_handle_quant_matrix_ext (GstMpeg2Decoder * decoder, GstMpegVideoPacket * packet) { GstMpeg2DecoderPrivate *priv = decoder->priv; GstMpegVideoQuantMatrixExt matrix_ext = { 0, }; if (!gst_mpeg_video_packet_parse_quant_matrix_extension (packet, &matrix_ext)) { GST_ERROR_OBJECT (decoder, "failed to parse sequence-scalable-extension"); return GST_FLOW_ERROR; } priv->quant_matrix = matrix_ext; return GST_FLOW_OK; } static GstFlowReturn gst_mpeg2_decoder_handle_picture_ext (GstMpeg2Decoder * decoder, GstMpegVideoPacket * packet) { GstMpeg2DecoderPrivate *priv = decoder->priv; GstMpegVideoPictureExt pic_ext = { {{0,},}, }; 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 GST_FLOW_ERROR; } if (!gst_mpeg_video_packet_parse_picture_extension (packet, &pic_ext)) { GST_ERROR_OBJECT (decoder, "failed to parse picture-extension"); return GST_FLOW_ERROR; } 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 GST_FLOW_OK; } static GstFlowReturn gst_mpeg2_decoder_handle_gop (GstMpeg2Decoder * decoder, GstMpegVideoPacket * packet) { GstMpeg2DecoderPrivate *priv = decoder->priv; GstMpegVideoGop gop = { 0, }; if (!gst_mpeg_video_packet_parse_gop (packet, &gop)) { GST_ERROR_OBJECT (decoder, "failed to parse GOP"); return GST_FLOW_ERROR; } 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 GST_FLOW_OK; } static GstFlowReturn gst_mpeg2_decoder_handle_picture (GstMpeg2Decoder * decoder, GstMpegVideoPacket * packet) { GstMpeg2DecoderPrivate *priv = decoder->priv; GstMpegVideoPictureHdr pic_hdr = { 0, }; GstMpeg2DecoderClass *klass = GST_MPEG2_DECODER_GET_CLASS (decoder); g_assert (klass->new_sequence); if (!_is_valid_state (decoder, GST_MPEG2_DECODER_STATE_VALID_SEQ_HEADERS)) { GST_ERROR_OBJECT (decoder, "no sequence before parsing picture header"); return GST_FLOW_ERROR; } /* If need_to_drain, we must have sequence changed. */ g_assert (priv->need_to_drain ? priv->seq_changed : TRUE); /* 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) { GstFlowReturn ret; /* There are a lot of info in the mpeg2's sequence(also including ext display_ext and scalable_ext). We need to notify the subclass about its change, but not all the changes should trigger a drain(), which may change the output picture order. */ if (priv->need_to_drain) { ret = gst_mpeg2_decoder_drain (GST_VIDEO_DECODER (decoder)); if (ret != GST_FLOW_OK) return ret; priv->need_to_drain = FALSE; } if (klass->get_preferred_output_delay) { priv->preferred_output_delay = klass->get_preferred_output_delay (decoder, priv->is_live); } else { priv->preferred_output_delay = 0; } priv->seq_changed = FALSE; ret = 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, /* previous/next 2 pictures */ 2 + priv->preferred_output_delay); if (ret != GST_FLOW_OK) { GST_WARNING_OBJECT (decoder, "new sequence error"); return ret; } } 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 GST_FLOW_ERROR; } priv->pic_hdr = pic_hdr; priv->state |= GST_MPEG2_DECODER_STATE_GOT_PIC_HDR; return GST_FLOW_OK; } static GstFlowReturn 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, *next_picture; GstFlowReturn ret; /* If subclass didn't update output state at this point, * marking this picture as a discont and stores current input state */ if (priv->input_state_changed) { priv->current_picture->discont_state = gst_video_codec_state_ref (decoder->input_state); priv->input_state_changed = FALSE; } if (!klass->start_picture) return GST_FLOW_OK; gst_mpeg2_dpb_get_neighbours (priv->dpb, priv->current_picture, &prev_picture, &next_picture); if (priv->current_picture->type == GST_MPEG_VIDEO_PICTURE_TYPE_B && !prev_picture && !priv->gop.closed_gop) { GST_VIDEO_CODEC_FRAME_FLAG_SET (priv->current_frame, GST_VIDEO_CODEC_FRAME_FLAG_DECODE_ONLY); } ret = klass->start_picture (decoder, priv->current_picture, slice, prev_picture, next_picture); if (ret != GST_FLOW_OK) { GST_WARNING_OBJECT (decoder, "subclass does not want to start picture"); return ret; } return GST_FLOW_OK; } static GstFlowReturn 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; GstFlowReturn ret = GST_FLOW_OK; if (priv->current_picture) { g_assert (_is_valid_state (decoder, GST_MPEG2_DECODER_STATE_GOT_SLICE)); return GST_FLOW_OK; } 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_clear_mpeg2_picture (&priv->first_field); } picture = gst_mpeg2_picture_new (); if (klass->new_picture) ret = klass->new_picture (decoder, priv->current_frame, picture); if (ret != GST_FLOW_OK) { GST_WARNING_OBJECT (decoder, "subclass does not want accept new picture"); gst_mpeg2_picture_unref (picture); return ret; } picture->structure = GST_MPEG_VIDEO_PICTURE_STRUCTURE_FRAME; } else { if (!priv->first_field) { picture = gst_mpeg2_picture_new (); if (klass->new_picture) ret = klass->new_picture (decoder, priv->current_frame, picture); if (ret != GST_FLOW_OK) { GST_WARNING_OBJECT (decoder, "subclass does not want accept new picture"); gst_mpeg2_picture_unref (picture); return ret; } } else { picture = gst_mpeg2_picture_new (); if (klass->new_field_picture) ret = klass->new_field_picture (decoder, priv->first_field, picture); if (ret != GST_FLOW_OK) { GST_WARNING_OBJECT (decoder, "Subclass couldn't handle new field picture"); gst_mpeg2_picture_unref (picture); return ret; } picture->first_field = gst_mpeg2_picture_ref (priv->first_field); /* At this moment, this picture should be interlaced */ picture->buffer_flags |= GST_VIDEO_BUFFER_FLAG_INTERLACED; if (priv->pic_ext.top_field_first) picture->buffer_flags |= GST_VIDEO_BUFFER_FLAG_TFF; } 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); return gst_mpeg2_decoder_start_current_picture (decoder, slice); } static GstFlowReturn gst_mpeg2_decoder_finish_current_field (GstMpeg2Decoder * decoder) { GstMpeg2DecoderPrivate *priv = decoder->priv; GstMpeg2DecoderClass *klass = GST_MPEG2_DECODER_GET_CLASS (decoder); GstFlowReturn ret; if (priv->current_picture == NULL) return GST_FLOW_OK; ret = klass->end_picture (decoder, priv->current_picture); if (ret != GST_FLOW_OK) { GST_WARNING_OBJECT (decoder, "subclass end_picture failed"); return ret; } 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_clear_mpeg2_picture (&priv->current_picture); } return GST_FLOW_OK; } static GstFlowReturn gst_mpeg2_decoder_finish_current_picture (GstMpeg2Decoder * decoder) { GstMpeg2DecoderPrivate *priv = decoder->priv; GstMpeg2DecoderClass *klass = GST_MPEG2_DECODER_GET_CLASS (decoder); GstFlowReturn ret; g_assert (priv->current_picture != NULL); ret = klass->end_picture (decoder, priv->current_picture); if (ret != GST_FLOW_OK) { GST_WARNING_OBJECT (decoder, "subclass end_picture failed"); return ret; } 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 GST_FLOW_OK; } static GstFlowReturn 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; GstFlowReturn 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 GST_FLOW_ERROR; } 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 GST_FLOW_ERROR; } 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; slice.sc_offset = slice.packet.offset - 4; slice.size = slice.packet.size + 4; ret = gst_mpeg2_decoder_ensure_current_picture (decoder, &slice); if (ret != GST_FLOW_OK) { GST_WARNING_OBJECT (decoder, "failed to start current picture"); return ret; } g_assert (klass->decode_slice); ret = klass->decode_slice (decoder, priv->current_picture, &slice); if (ret != GST_FLOW_OK) { GST_WARNING_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 ret; } priv->state |= GST_MPEG2_DECODER_STATE_GOT_SLICE; return GST_FLOW_OK; } static GstFlowReturn gst_mpeg2_decoder_decode_packet (GstMpeg2Decoder * decoder, GstMpegVideoPacket * packet) { GstMpegVideoPacketExtensionCode ext_type; GstFlowReturn ret = GST_FLOW_OK; 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 != GST_FLOW_OK) 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 */ break; } break; case GST_MPEG_VIDEO_PACKET_SEQUENCE_END: break; case GST_MPEG_VIDEO_PACKET_GOP: ret = gst_mpeg2_decoder_handle_gop (decoder, packet); break; case GST_MPEG_VIDEO_PACKET_USER_DATA: 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); break; } return ret; } static void gst_mpeg2_decoder_do_output_picture (GstMpeg2Decoder * decoder, GstMpeg2Picture * to_output, GstFlowReturn * ret) { GstVideoCodecFrame *frame = NULL; GstMpeg2DecoderPrivate *priv = decoder->priv; GstMpeg2DecoderOutputFrame output_frame; g_assert (ret != NULL); 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); UPDATE_FLOW_RETURN (ret, GST_FLOW_ERROR); gst_mpeg2_picture_unref (to_output); return; } output_frame.frame = frame; output_frame.picture = to_output; output_frame.self = decoder; gst_queue_array_push_tail_struct (priv->output_queue, &output_frame); gst_mpeg2_decoder_drain_output_queue (decoder, priv->preferred_output_delay, 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); gst_mpeg2_decoder_do_output_picture (decoder, to_output, &ret); if (ret != GST_FLOW_OK) break; } return ret; } static void gst_mpeg2_decoder_clear_output_frame (GstMpeg2DecoderOutputFrame * output_frame) { if (!output_frame) return; if (output_frame->frame) { gst_video_decoder_release_frame (GST_VIDEO_DECODER (output_frame->self), output_frame->frame); output_frame->frame = NULL; } gst_clear_mpeg2_picture (&output_frame->picture); } 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; } } ret = gst_mpeg2_decoder_decode_packet (self, &packet); if (ret != GST_FLOW_OK) { gst_buffer_unmap (in_buf, &map_info); GST_WARNING_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; } ret = gst_mpeg2_decoder_finish_current_picture (self); if (ret != GST_FLOW_OK) { GST_ERROR_OBJECT (decoder, "failed to decode the current picture"); goto failed; } ret = gst_mpeg2_decoder_output_current_picture (self); gst_clear_mpeg2_picture (&priv->current_picture); gst_clear_mpeg2_picture (&priv->first_field); gst_video_codec_frame_unref (priv->current_frame); priv->current_frame = NULL; return ret; failed: { if (ret == GST_FLOW_ERROR) { 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_clear_mpeg2_picture (&priv->current_picture); gst_clear_mpeg2_picture (&priv->first_field); priv->current_frame = NULL; return ret; } } static void gst_mpeg2_decoder_drain_output_queue (GstMpeg2Decoder * self, guint num, GstFlowReturn * ret) { GstMpeg2DecoderPrivate *priv = self->priv; GstMpeg2DecoderClass *klass = GST_MPEG2_DECODER_GET_CLASS (self); GstFlowReturn flow_ret; g_assert (klass->output_picture); while (gst_queue_array_get_length (priv->output_queue) > num) { GstMpeg2DecoderOutputFrame *output_frame = (GstMpeg2DecoderOutputFrame *) gst_queue_array_pop_head_struct (priv->output_queue); GST_LOG_OBJECT (self, "Output picture %p (frame_num %d, poc %d, pts: %" GST_TIME_FORMAT "), from DPB", output_frame->picture, output_frame->picture->system_frame_number, output_frame->picture->pic_order_cnt, GST_TIME_ARGS (output_frame->frame->pts)); flow_ret = klass->output_picture (self, output_frame->frame, output_frame->picture); UPDATE_FLOW_RETURN (ret, flow_ret); } }