decoder: mpeg2: respect any input PTS provided for a frame.
[platform/upstream/gstreamer-vaapi.git] / gst-libs / gst / vaapi / gstvaapidecoder_mpeg2.c
index b320248..c3ab7a6 100644 (file)
@@ -2,6 +2,7 @@
  *  gstvaapidecoder_mpeg2.c - MPEG-2 decoder
  *
  *  Copyright (C) 2011-2013 Intel Corporation
+ *    Author: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
  *
  *  This library is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU Lesser General Public License
 #include "gstvaapidebug.h"
 
 /* ------------------------------------------------------------------------- */
-/* --- VLC Reader                                                        --- */
-/* ------------------------------------------------------------------------- */
-
-#define READ_UINT8(br, val, nbits) G_STMT_START {  \
-  if (!gst_bit_reader_get_bits_uint8 (br, &val, nbits)) { \
-    GST_WARNING ("failed to read uint8, nbits: %d", nbits); \
-    goto failed; \
-  } \
-} G_STMT_END
-
-#define SKIP(reader, nbits) G_STMT_START { \
-  if (!gst_bit_reader_skip (reader, nbits)) { \
-    GST_WARNING ("failed to skip nbits: %d", nbits); \
-    goto failed; \
-  } \
-} G_STMT_END
-
-/* VLC decoder from gst-plugins-bad */
-typedef struct _VLCTable VLCTable;
-struct _VLCTable {
-    gint  value;
-    guint cword;
-    guint cbits;
-};
-
-static gboolean
-decode_vlc(GstBitReader *br, gint *res, const VLCTable *table, guint length)
-{
-    guint8 i;
-    guint cbits = 0;
-    guint32 value = 0;
-
-    for (i = 0; i < length; i++) {
-        if (cbits != table[i].cbits) {
-            cbits = table[i].cbits;
-            if (!gst_bit_reader_peek_bits_uint32(br, &value, cbits)) {
-                goto failed;
-            }
-        }
-
-        if (value == table[i].cword) {
-            SKIP(br, cbits);
-            if (res)
-                *res = table[i].value;
-            return TRUE;
-        }
-    }
-    GST_DEBUG("failed to find VLC code");
-
-failed:
-    GST_WARNING("failed to decode VLC, returning");
-    return FALSE;
-}
-
-enum {
-    GST_MPEG_VIDEO_MACROBLOCK_ESCAPE = -1,
-};
-
-/* Table B-1: Variable length codes for macroblock_address_increment */
-static const VLCTable mpeg2_mbaddr_vlc_table[] = {
-    {  1, 0x01,  1 },
-    {  2, 0x03,  3 },
-    {  3, 0x02,  3 },
-    {  4, 0x03,  4 },
-    {  5, 0x02,  4 },
-    {  6, 0x03,  5 },
-    {  7, 0x02,  5 },
-    {  8, 0x07,  7 },
-    {  9, 0x06,  7 },
-    { 10, 0x0b,  8 },
-    { 11, 0x0a,  8 },
-    { 12, 0x09,  8 },
-    { 13, 0x08,  8 },
-    { 14, 0x07,  8 },
-    { 15, 0x06,  8 },
-    { 16, 0x17, 10 },
-    { 17, 0x16, 10 },
-    { 18, 0x15, 10 },
-    { 19, 0x14, 10 },
-    { 20, 0x13, 10 },
-    { 21, 0x12, 10 },
-    { 22, 0x23, 11 },
-    { 23, 0x22, 11 },
-    { 24, 0x21, 11 },
-    { 25, 0x20, 11 },
-    { 26, 0x1f, 11 },
-    { 27, 0x1e, 11 },
-    { 28, 0x1d, 11 },
-    { 29, 0x1c, 11 },
-    { 30, 0x1b, 11 },
-    { 31, 0x1a, 11 },
-    { 32, 0x19, 11 },
-    { 33, 0x18, 11 },
-    { GST_MPEG_VIDEO_MACROBLOCK_ESCAPE, 0x08, 11 }
-};
-
-/* ------------------------------------------------------------------------- */
 /* --- PTS Generator                                                     --- */
 /* ------------------------------------------------------------------------- */
 
@@ -226,7 +130,9 @@ pts_eval(PTSGenerator *tsg, GstClockTime pic_pts, guint pic_tsn)
     if (!GST_CLOCK_TIME_IS_VALID(tsg->gop_pts))
         tsg->gop_pts = 0;
 
-    pts = tsg->gop_pts + pts_get_duration(tsg, tsg->ovl_tsn * 1024 + 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);
 
     if (!GST_CLOCK_TIME_IS_VALID(tsg->max_pts) || tsg->max_pts < pts)
         tsg->max_pts = pts;
@@ -238,6 +144,7 @@ pts_eval(PTSGenerator *tsg, GstClockTime pic_pts, guint pic_tsn)
         tsg->ovl_tsn++;
     }
     tsg->lst_tsn = pic_tsn;
+
     return pts;
 }
 
@@ -245,25 +152,16 @@ pts_eval(PTSGenerator *tsg, GstClockTime pic_pts, guint pic_tsn)
 /* --- MPEG-2 Parser Info                                                --- */
 /* ------------------------------------------------------------------------- */
 
-typedef struct _GstMpegVideoSliceHdr GstMpegVideoSliceHdr;
-struct _GstMpegVideoSliceHdr {
-    guint16 slice_horizontal_position;
-    guint16 slice_vertical_position;
-    guint8 quantiser_scale_code;
-    guint8 intra_slice;
-
-    /* Size of the slice() header in bits */
-    guint header_size;
-};
-
 typedef struct _GstVaapiParserInfoMpeg2 GstVaapiParserInfoMpeg2;
 struct _GstVaapiParserInfoMpeg2 {
+    GstVaapiMiniObject  parent_instance;
     GstMpegVideoPacket  packet;
     guint8              extension_type; /* for Extension packets */
     union {
         GstMpegVideoSequenceHdr         seq_hdr;
         GstMpegVideoSequenceExt         seq_ext;
         GstMpegVideoSequenceDisplayExt  seq_display_ext;
+        GstMpegVideoSequenceScalableExt seq_scalable_ext;
         GstMpegVideoGop                 gop;
         GstMpegVideoQuantMatrixExt      quant_matrix;
         GstMpegVideoPictureHdr          pic_hdr;
@@ -315,17 +213,11 @@ gst_vaapi_parser_info_mpeg2_ensure(GstVaapiParserInfoMpeg2 **pi_ptr)
 /* --- MPEG-2 Decoder                                                    --- */
 /* ------------------------------------------------------------------------- */
 
-G_DEFINE_TYPE(GstVaapiDecoderMpeg2,
-              gst_vaapi_decoder_mpeg2,
-              GST_VAAPI_TYPE_DECODER)
-
 #define GST_VAAPI_DECODER_MPEG2_CAST(decoder) \
     ((GstVaapiDecoderMpeg2 *)(decoder))
 
-#define GST_VAAPI_DECODER_MPEG2_GET_PRIVATE(obj)                \
-    (G_TYPE_INSTANCE_GET_PRIVATE((obj),                         \
-                                 GST_VAAPI_TYPE_DECODER_MPEG2,  \
-                                 GstVaapiDecoderMpeg2Private))
+typedef struct _GstVaapiDecoderMpeg2Private     GstVaapiDecoderMpeg2Private;
+typedef struct _GstVaapiDecoderMpeg2Class       GstVaapiDecoderMpeg2Class;
 
 typedef enum {
     GST_MPEG_VIDEO_STATE_GOT_SEQ_HDR    = 1 << 0,
@@ -354,6 +246,7 @@ struct _GstVaapiDecoderMpeg2Private {
     guint                       fps_n;
     guint                       fps_d;
     guint                       state;
+    GstVaapiRectangle           crop_rect;
     GstVaapiParserInfoMpeg2    *seq_hdr;
     GstVaapiParserInfoMpeg2    *seq_ext;
     GstVaapiParserInfoMpeg2    *seq_display_ext;
@@ -361,12 +254,12 @@ struct _GstVaapiDecoderMpeg2Private {
     GstVaapiParserInfoMpeg2    *gop;
     GstVaapiParserInfoMpeg2    *pic_hdr;
     GstVaapiParserInfoMpeg2    *pic_ext;
+    GstVaapiParserInfoMpeg2    *pic_display_ext;
     GstVaapiParserInfoMpeg2    *quant_matrix;
     GstVaapiParserInfoMpeg2    *slice_hdr;
     GstVaapiPicture            *current_picture;
     GstVaapiDpb                *dpb;
     PTSGenerator                tsg;
-    guint                       is_constructed          : 1;
     guint                       is_opened               : 1;
     guint                       size_changed            : 1;
     guint                       profile_changed         : 1;
@@ -376,10 +269,31 @@ struct _GstVaapiDecoderMpeg2Private {
     guint                       broken_link             : 1;
 };
 
+/**
+ * GstVaapiDecoderMpeg2:
+ *
+ * A decoder based on Mpeg2.
+ */
+struct _GstVaapiDecoderMpeg2 {
+    /*< private >*/
+    GstVaapiDecoder             parent_instance;
+    GstVaapiDecoderMpeg2Private priv;
+};
+
+/**
+ * GstVaapiDecoderMpeg2Class:
+ *
+ * A decoder class based on Mpeg2.
+ */
+struct _GstVaapiDecoderMpeg2Class {
+    /*< private >*/
+    GstVaapiDecoderClass parent_class;
+};
+
 static void
 gst_vaapi_decoder_mpeg2_close(GstVaapiDecoderMpeg2 *decoder)
 {
-    GstVaapiDecoderMpeg2Private * const priv = decoder->priv;
+    GstVaapiDecoderMpeg2Private * const priv = &decoder->priv;
 
     gst_vaapi_picture_replace(&priv->current_picture, NULL);
 
@@ -390,6 +304,7 @@ gst_vaapi_decoder_mpeg2_close(GstVaapiDecoderMpeg2 *decoder)
     gst_vaapi_parser_info_mpeg2_replace(&priv->gop, NULL);
     gst_vaapi_parser_info_mpeg2_replace(&priv->pic_hdr, NULL);
     gst_vaapi_parser_info_mpeg2_replace(&priv->pic_ext, NULL);
+    gst_vaapi_parser_info_mpeg2_replace(&priv->pic_display_ext, NULL);
     gst_vaapi_parser_info_mpeg2_replace(&priv->quant_matrix, NULL);
     gst_vaapi_parser_info_mpeg2_replace(&priv->slice_hdr, NULL);
 
@@ -399,7 +314,7 @@ gst_vaapi_decoder_mpeg2_close(GstVaapiDecoderMpeg2 *decoder)
 static gboolean
 gst_vaapi_decoder_mpeg2_open(GstVaapiDecoderMpeg2 *decoder)
 {
-    GstVaapiDecoderMpeg2Private * const priv = decoder->priv;
+    GstVaapiDecoderMpeg2Private * const priv = &decoder->priv;
 
     gst_vaapi_decoder_mpeg2_close(decoder);
 
@@ -412,16 +327,24 @@ gst_vaapi_decoder_mpeg2_open(GstVaapiDecoderMpeg2 *decoder)
 }
 
 static void
-gst_vaapi_decoder_mpeg2_destroy(GstVaapiDecoderMpeg2 *decoder)
+gst_vaapi_decoder_mpeg2_destroy(GstVaapiDecoder *base_decoder)
 {
+    GstVaapiDecoderMpeg2 * const decoder =
+        GST_VAAPI_DECODER_MPEG2_CAST(base_decoder);
+
     gst_vaapi_decoder_mpeg2_close(decoder);
 }
 
 static gboolean
-gst_vaapi_decoder_mpeg2_create(GstVaapiDecoderMpeg2 *decoder)
+gst_vaapi_decoder_mpeg2_create(GstVaapiDecoder *base_decoder)
 {
-    if (!GST_VAAPI_DECODER_CODEC(decoder))
-        return FALSE;
+    GstVaapiDecoderMpeg2 * const decoder =
+        GST_VAAPI_DECODER_MPEG2_CAST(base_decoder);
+    GstVaapiDecoderMpeg2Private * const priv = &decoder->priv;
+
+    priv->hw_profile            = GST_VAAPI_PROFILE_UNKNOWN;
+    priv->profile               = GST_VAAPI_PROFILE_MPEG2_SIMPLE;
+    priv->profile_changed       = TRUE; /* Allow fallbacks to work */
     return TRUE;
 }
 
@@ -449,7 +372,7 @@ static GstVaapiProfile
 get_profile(GstVaapiDecoderMpeg2 *decoder, GstVaapiEntrypoint entrypoint)
 {
     GstVaapiDisplay * const va_display = GST_VAAPI_DECODER_DISPLAY(decoder);
-    GstVaapiDecoderMpeg2Private * const priv = decoder->priv;
+    GstVaapiDecoderMpeg2Private * const priv = &decoder->priv;
     GstVaapiProfile profile = priv->profile;
 
     do {
@@ -490,7 +413,7 @@ get_profile(GstVaapiDecoderMpeg2 *decoder, GstVaapiEntrypoint entrypoint)
 static GstVaapiDecoderStatus
 ensure_context(GstVaapiDecoderMpeg2 *decoder)
 {
-    GstVaapiDecoderMpeg2Private * const priv = decoder->priv;
+    GstVaapiDecoderMpeg2Private * const priv = &decoder->priv;
     GstVaapiEntrypoint entrypoint = GST_VAAPI_ENTRYPOINT_VLD;
     gboolean reset_context = FALSE;
 
@@ -515,6 +438,7 @@ ensure_context(GstVaapiDecoderMpeg2 *decoder)
 
         info.profile    = priv->hw_profile;
         info.entrypoint = entrypoint;
+        info.chroma_type = GST_VAAPI_CHROMA_TYPE_YUV420;
         info.width      = priv->width;
         info.height     = priv->height;
         info.ref_frames = 2;
@@ -531,7 +455,7 @@ ensure_context(GstVaapiDecoderMpeg2 *decoder)
 static GstVaapiDecoderStatus
 ensure_quant_matrix(GstVaapiDecoderMpeg2 *decoder, GstVaapiPicture *picture)
 {
-    GstVaapiDecoderMpeg2Private * const priv = decoder->priv;
+    GstVaapiDecoderMpeg2Private * const priv = &decoder->priv;
     GstMpegVideoSequenceHdr * const seq_hdr = &priv->seq_hdr->data.seq_hdr;
     VAIQMatrixBufferMPEG2 *iq_matrix;
     guint8 *intra_quant_matrix = NULL;
@@ -592,7 +516,7 @@ ensure_quant_matrix(GstVaapiDecoderMpeg2 *decoder, GstVaapiPicture *picture)
 static inline gboolean
 is_valid_state(GstVaapiDecoderMpeg2 *decoder, guint state)
 {
-    GstVaapiDecoderMpeg2Private * const priv = decoder->priv;
+    GstVaapiDecoderMpeg2Private * const priv = &decoder->priv;
 
     return (priv->state & state) == state;
 }
@@ -600,7 +524,7 @@ is_valid_state(GstVaapiDecoderMpeg2 *decoder, guint state)
 static GstVaapiDecoderStatus
 decode_current_picture(GstVaapiDecoderMpeg2 *decoder)
 {
-    GstVaapiDecoderMpeg2Private * const priv = decoder->priv;
+    GstVaapiDecoderMpeg2Private * const priv = &decoder->priv;
     GstVaapiPicture * const picture = priv->current_picture;
 
     if (!is_valid_state(decoder, GST_MPEG_VIDEO_STATE_VALID_PICTURE))
@@ -631,9 +555,9 @@ drop_frame:
 
 static GstVaapiDecoderStatus
 parse_sequence(GstVaapiDecoderMpeg2 *decoder,
-    GstVaapiDecoderUnit *unit, GstMpegVideoPacket *packet)
+    GstVaapiDecoderUnit *unit, const GstMpegVideoPacket *packet)
 {
-    GstVaapiDecoderMpeg2Private * const priv = decoder->priv;
+    GstVaapiDecoderMpeg2Private * const priv = &decoder->priv;
     GstMpegVideoSequenceHdr *seq_hdr;
 
     priv->state = 0;
@@ -645,8 +569,7 @@ parse_sequence(GstVaapiDecoderMpeg2 *decoder,
 
     seq_hdr = &priv->seq_hdr->data.seq_hdr;
 
-    if (!gst_mpeg_video_parse_sequence_header(seq_hdr,
-            packet->data, packet->size, packet->offset)) {
+    if (!gst_mpeg_video_packet_parse_sequence_header(packet, seq_hdr)) {
         GST_ERROR("failed to parse sequence header");
         return GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER;
     }
@@ -659,11 +582,14 @@ static GstVaapiDecoderStatus
 decode_sequence(GstVaapiDecoderMpeg2 *decoder, GstVaapiDecoderUnit *unit)
 {
     GstVaapiDecoder * const base_decoder = GST_VAAPI_DECODER_CAST(decoder);
-    GstVaapiDecoderMpeg2Private * const priv = decoder->priv;
+    GstVaapiDecoderMpeg2Private * const priv = &decoder->priv;
     GstMpegVideoSequenceHdr * const seq_hdr = unit->parsed_info;
 
     gst_vaapi_parser_info_mpeg2_replace(&priv->seq_ext, NULL);
     gst_vaapi_parser_info_mpeg2_replace(&priv->seq_display_ext, NULL);
+    gst_vaapi_parser_info_mpeg2_replace(&priv->seq_scalable_ext, NULL);
+    gst_vaapi_parser_info_mpeg2_replace(&priv->quant_matrix, NULL);
+    gst_vaapi_parser_info_mpeg2_replace(&priv->pic_display_ext, NULL);
 
     priv->fps_n = seq_hdr->fps_n;
     priv->fps_d = seq_hdr->fps_d;
@@ -682,9 +608,9 @@ decode_sequence(GstVaapiDecoderMpeg2 *decoder, GstVaapiDecoderUnit *unit)
 
 static GstVaapiDecoderStatus
 parse_sequence_ext(GstVaapiDecoderMpeg2 *decoder,
-    GstVaapiDecoderUnit *unit, GstMpegVideoPacket *packet)
+    GstVaapiDecoderUnit *unit, const GstMpegVideoPacket *packet)
 {
-    GstVaapiDecoderMpeg2Private * const priv = decoder->priv;
+    GstVaapiDecoderMpeg2Private * const priv = &decoder->priv;
     GstMpegVideoSequenceExt *seq_ext;
 
     priv->state &= GST_MPEG_VIDEO_STATE_GOT_SEQ_HDR;
@@ -696,8 +622,7 @@ parse_sequence_ext(GstVaapiDecoderMpeg2 *decoder,
 
     seq_ext = &priv->seq_ext->data.seq_ext;
 
-    if (!gst_mpeg_video_parse_sequence_extension(seq_ext,
-            packet->data, packet->size, packet->offset)) {
+    if (!gst_mpeg_video_packet_parse_sequence_extension(packet, seq_ext)) {
         GST_ERROR("failed to parse sequence-extension");
         return GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER;
     }
@@ -710,7 +635,7 @@ static GstVaapiDecoderStatus
 decode_sequence_ext(GstVaapiDecoderMpeg2 *decoder, GstVaapiDecoderUnit *unit)
 {
     GstVaapiDecoder * const base_decoder = GST_VAAPI_DECODER_CAST(decoder);
-    GstVaapiDecoderMpeg2Private * const priv = decoder->priv;
+    GstVaapiDecoderMpeg2Private * const priv = &decoder->priv;
     GstMpegVideoSequenceExt * const seq_ext = unit->parsed_info;
     GstVaapiProfile profile;
     guint width, height;
@@ -767,9 +692,9 @@ decode_sequence_ext(GstVaapiDecoderMpeg2 *decoder, GstVaapiDecoderUnit *unit)
 
 static GstVaapiDecoderStatus
 parse_sequence_display_ext(GstVaapiDecoderMpeg2 *decoder,
-    GstVaapiDecoderUnit *unit, GstMpegVideoPacket *packet)
+    GstVaapiDecoderUnit *unit, const GstMpegVideoPacket *packet)
 {
-    GstVaapiDecoderMpeg2Private * const priv = decoder->priv;
+    GstVaapiDecoderMpeg2Private * const priv = &decoder->priv;
     GstMpegVideoSequenceDisplayExt *seq_display_ext;
 
     if (!gst_vaapi_parser_info_mpeg2_ensure(&priv->seq_display_ext)) {
@@ -779,8 +704,8 @@ parse_sequence_display_ext(GstVaapiDecoderMpeg2 *decoder,
 
     seq_display_ext = &priv->seq_display_ext->data.seq_display_ext;
 
-    if (!gst_mpeg_video_parse_sequence_display_extension(seq_display_ext,
-            packet->data, packet->size, packet->offset)) {
+    if (!gst_mpeg_video_packet_parse_sequence_display_extension(packet,
+            seq_display_ext)) {
         GST_ERROR("failed to parse sequence-display-extension");
         return GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER;
     }
@@ -793,14 +718,61 @@ static GstVaapiDecoderStatus
 decode_sequence_display_ext(GstVaapiDecoderMpeg2 *decoder,
     GstVaapiDecoderUnit *unit)
 {
-    /* XXX: handle color primaries and cropping */
+    GstVaapiDecoderMpeg2Private * const priv = &decoder->priv;
+    GstMpegVideoSequenceDisplayExt *seq_display_ext;
+
+    seq_display_ext = priv->seq_display_ext ?
+        &priv->seq_display_ext->data.seq_display_ext : NULL;
+
+    /* Update cropping rectangle */
+    if (seq_display_ext) {
+        GstVaapiRectangle * const crop_rect = &priv->crop_rect;
+        crop_rect->x = 0;
+        crop_rect->y = 0;
+        crop_rect->width = seq_display_ext->display_horizontal_size;
+        crop_rect->height = seq_display_ext->display_vertical_size;
+    }
+
+    /* XXX: handle color primaries */
+    return GST_VAAPI_DECODER_STATUS_SUCCESS;
+}
+
+static GstVaapiDecoderStatus
+parse_sequence_scalable_ext(GstVaapiDecoderMpeg2 *decoder,
+    GstVaapiDecoderUnit *unit, const GstMpegVideoPacket *packet)
+{
+    GstVaapiDecoderMpeg2Private * const priv = &decoder->priv;
+    GstMpegVideoSequenceScalableExt *seq_scalable_ext;
+
+    if (!gst_vaapi_parser_info_mpeg2_ensure(&priv->seq_scalable_ext)) {
+        GST_ERROR("failed to allocate parser info for sequence scalable extension");
+        return GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED;
+    }
+
+    seq_scalable_ext = &priv->seq_scalable_ext->data.seq_scalable_ext;
+
+    if (!gst_mpeg_video_packet_parse_sequence_scalable_extension(packet,
+            seq_scalable_ext)) {
+        GST_ERROR("failed to parse sequence-scalable-extension");
+        return GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER;
+    }
+
+    gst_vaapi_decoder_unit_set_parsed_info(unit, seq_scalable_ext, NULL);
+    return GST_VAAPI_DECODER_STATUS_SUCCESS;
+}
+
+static GstVaapiDecoderStatus
+decode_sequence_scalable_ext(GstVaapiDecoderMpeg2 *decoder,
+    GstVaapiDecoderUnit *unit)
+{
+    /* XXX: unsupported header -- ignore */
     return GST_VAAPI_DECODER_STATUS_SUCCESS;
 }
 
 static GstVaapiDecoderStatus
 decode_sequence_end(GstVaapiDecoderMpeg2 *decoder)
 {
-    GstVaapiDecoderMpeg2Private * const priv = decoder->priv;
+    GstVaapiDecoderMpeg2Private * const priv = &decoder->priv;
 
     gst_vaapi_dpb_flush(priv->dpb);
     return GST_VAAPI_DECODER_STATUS_SUCCESS;
@@ -808,9 +780,9 @@ decode_sequence_end(GstVaapiDecoderMpeg2 *decoder)
 
 static GstVaapiDecoderStatus
 parse_quant_matrix_ext(GstVaapiDecoderMpeg2 *decoder,
-    GstVaapiDecoderUnit *unit, GstMpegVideoPacket *packet)
+    GstVaapiDecoderUnit *unit, const GstMpegVideoPacket *packet)
 {
-    GstVaapiDecoderMpeg2Private * const priv = decoder->priv;
+    GstVaapiDecoderMpeg2Private * const priv = &decoder->priv;
     GstMpegVideoQuantMatrixExt *quant_matrix;
 
     if (!gst_vaapi_parser_info_mpeg2_ensure(&priv->quant_matrix)) {
@@ -820,8 +792,8 @@ parse_quant_matrix_ext(GstVaapiDecoderMpeg2 *decoder,
 
     quant_matrix = &priv->quant_matrix->data.quant_matrix;
 
-    if (!gst_mpeg_video_parse_quant_matrix_extension(quant_matrix,
-            packet->data, packet->size, packet->offset)) {
+    if (!gst_mpeg_video_packet_parse_quant_matrix_extension(packet,
+            quant_matrix)) {
         GST_ERROR("failed to parse quant-matrix-extension");
         return GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER;
     }
@@ -834,7 +806,7 @@ static GstVaapiDecoderStatus
 decode_quant_matrix_ext(GstVaapiDecoderMpeg2 *decoder,
     GstVaapiDecoderUnit *unit)
 {
-    GstVaapiDecoderMpeg2Private * const priv = decoder->priv;
+    GstVaapiDecoderMpeg2Private * const priv = &decoder->priv;
 
     priv->quant_matrix_changed = TRUE;
     return GST_VAAPI_DECODER_STATUS_SUCCESS;
@@ -842,9 +814,9 @@ decode_quant_matrix_ext(GstVaapiDecoderMpeg2 *decoder,
 
 static GstVaapiDecoderStatus
 parse_gop(GstVaapiDecoderMpeg2 *decoder,
-    GstVaapiDecoderUnit *unit, GstMpegVideoPacket *packet)
+    GstVaapiDecoderUnit *unit, const GstMpegVideoPacket *packet)
 {
-    GstVaapiDecoderMpeg2Private * const priv = decoder->priv;
+    GstVaapiDecoderMpeg2Private * const priv = &decoder->priv;
     GstMpegVideoGop *gop;
 
     if (!gst_vaapi_parser_info_mpeg2_ensure(&priv->gop)) {
@@ -854,8 +826,7 @@ parse_gop(GstVaapiDecoderMpeg2 *decoder,
 
     gop = &priv->gop->data.gop;
 
-    if (!gst_mpeg_video_parse_gop(gop,
-            packet->data, packet->size, packet->offset)) {
+    if (!gst_mpeg_video_packet_parse_gop(packet, gop)) {
         GST_ERROR("failed to parse GOP");
         return GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER;
     }
@@ -867,7 +838,7 @@ parse_gop(GstVaapiDecoderMpeg2 *decoder,
 static GstVaapiDecoderStatus
 decode_gop(GstVaapiDecoderMpeg2 *decoder, GstVaapiDecoderUnit *unit)
 {
-    GstVaapiDecoderMpeg2Private * const priv = decoder->priv;
+    GstVaapiDecoderMpeg2Private * const priv = &decoder->priv;
     GstMpegVideoGop * const gop = unit->parsed_info;
 
     priv->closed_gop  = gop->closed_gop;
@@ -883,9 +854,9 @@ decode_gop(GstVaapiDecoderMpeg2 *decoder, GstVaapiDecoderUnit *unit)
 
 static GstVaapiDecoderStatus
 parse_picture(GstVaapiDecoderMpeg2 *decoder,
-    GstVaapiDecoderUnit *unit, GstMpegVideoPacket *packet)
+    GstVaapiDecoderUnit *unit, const GstMpegVideoPacket *packet)
 {
-    GstVaapiDecoderMpeg2Private * const priv = decoder->priv;
+    GstVaapiDecoderMpeg2Private * const priv = &decoder->priv;
     GstMpegVideoPictureHdr *pic_hdr;
 
     priv->state &= (GST_MPEG_VIDEO_STATE_GOT_SEQ_HDR|
@@ -898,8 +869,7 @@ parse_picture(GstVaapiDecoderMpeg2 *decoder,
 
     pic_hdr = &priv->pic_hdr->data.pic_hdr;
 
-    if (!gst_mpeg_video_parse_picture_header(pic_hdr,
-            packet->data, packet->size, packet->offset)) {
+    if (!gst_mpeg_video_packet_parse_picture_header(packet, pic_hdr)) {
         GST_ERROR("failed to parse picture header");
         return GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER;
     }
@@ -911,7 +881,7 @@ parse_picture(GstVaapiDecoderMpeg2 *decoder,
 static GstVaapiDecoderStatus
 decode_picture(GstVaapiDecoderMpeg2 *decoder, GstVaapiDecoderUnit *unit)
 {
-    GstVaapiDecoderMpeg2Private * const priv = decoder->priv;
+    GstVaapiDecoderMpeg2Private * const priv = &decoder->priv;
 
     if (!is_valid_state(decoder, GST_MPEG_VIDEO_STATE_VALID_SEQ_HEADERS))
         return GST_VAAPI_DECODER_STATUS_SUCCESS;
@@ -924,9 +894,9 @@ decode_picture(GstVaapiDecoderMpeg2 *decoder, GstVaapiDecoderUnit *unit)
 
 static GstVaapiDecoderStatus
 parse_picture_ext(GstVaapiDecoderMpeg2 *decoder,
-    GstVaapiDecoderUnit *unit, GstMpegVideoPacket *packet)
+    GstVaapiDecoderUnit *unit, const GstMpegVideoPacket *packet)
 {
-    GstVaapiDecoderMpeg2Private * const priv = decoder->priv;
+    GstVaapiDecoderMpeg2Private * const priv = &decoder->priv;
     GstMpegVideoPictureExt *pic_ext;
 
     priv->state &= (GST_MPEG_VIDEO_STATE_GOT_SEQ_HDR|
@@ -940,8 +910,7 @@ parse_picture_ext(GstVaapiDecoderMpeg2 *decoder,
 
     pic_ext = &priv->pic_ext->data.pic_ext;
 
-    if (!gst_mpeg_video_parse_picture_extension(pic_ext,
-            packet->data, packet->size, packet->offset)) {
+    if (!gst_mpeg_video_packet_parse_picture_extension(packet, pic_ext)) {
         GST_ERROR("failed to parse picture-extension");
         return GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER;
     }
@@ -953,7 +922,7 @@ parse_picture_ext(GstVaapiDecoderMpeg2 *decoder,
 static GstVaapiDecoderStatus
 decode_picture_ext(GstVaapiDecoderMpeg2 *decoder, GstVaapiDecoderUnit *unit)
 {
-    GstVaapiDecoderMpeg2Private * const priv = decoder->priv;
+    GstVaapiDecoderMpeg2Private * const priv = &decoder->priv;
     GstMpegVideoPictureExt * const pic_ext = unit->parsed_info;
 
     if (!is_valid_state(decoder, GST_MPEG_VIDEO_STATE_GOT_PIC_HDR))
@@ -988,7 +957,7 @@ pack_f_code(guint8 f_code[2][2])
 static GstVaapiDecoderStatus
 init_picture(GstVaapiDecoderMpeg2 *decoder, GstVaapiPicture *picture)
 {
-    GstVaapiDecoderMpeg2Private * const priv = decoder->priv;
+    GstVaapiDecoderMpeg2Private * const priv = &decoder->priv;
     GstMpegVideoPictureHdr * const pic_hdr = &priv->pic_hdr->data.pic_hdr;
     GstMpegVideoPictureExt * const pic_ext = &priv->pic_ext->data.pic_ext;
 
@@ -1048,6 +1017,7 @@ init_picture(GstVaapiDecoderMpeg2 *decoder, GstVaapiPicture *picture)
         GST_VAAPI_PICTURE_FLAG_SET(
             dummy_picture,
             (GST_VAAPI_PICTURE_FLAG_SKIPPED |
+             GST_VAAPI_PICTURE_FLAG_OUTPUT  |
              GST_VAAPI_PICTURE_FLAG_REFERENCE)
         );
 
@@ -1070,7 +1040,7 @@ init_picture(GstVaapiDecoderMpeg2 *decoder, GstVaapiPicture *picture)
 static void
 fill_picture(GstVaapiDecoderMpeg2 *decoder, GstVaapiPicture *picture)
 {
-    GstVaapiDecoderMpeg2Private * const priv = decoder->priv;
+    GstVaapiDecoderMpeg2Private * const priv = &decoder->priv;
     VAPictureParameterBufferMPEG2 * const pic_param = picture->param;
     GstMpegVideoPictureHdr * const pic_hdr = &priv->pic_hdr->data.pic_hdr;
     GstMpegVideoPictureExt * const pic_ext = &priv->pic_ext->data.pic_ext;
@@ -1121,14 +1091,12 @@ fill_picture(GstVaapiDecoderMpeg2 *decoder, GstVaapiPicture *picture)
 
 static GstVaapiDecoderStatus
 parse_slice(GstVaapiDecoderMpeg2 *decoder,
-    GstVaapiDecoderUnit *unit, GstMpegVideoPacket *packet)
+    GstVaapiDecoderUnit *unit, const GstMpegVideoPacket *packet)
 {
-    GstVaapiDecoderMpeg2Private * const priv = decoder->priv;
+    GstVaapiDecoderMpeg2Private * const priv = &decoder->priv;
     GstMpegVideoSliceHdr *slice_hdr;
-    GstBitReader br;
-    gint mb_x, mb_y, mb_inc;
-    guint8 slice_vertical_position_extension = 0;
-    guint8 extra_bit_slice, junk8;
+    GstMpegVideoSequenceHdr *seq_hdr;
+    GstMpegVideoSequenceScalableExt *seq_scalable_ext;
 
     priv->state &= (GST_MPEG_VIDEO_STATE_GOT_SEQ_HDR|
                     GST_MPEG_VIDEO_STATE_GOT_SEQ_EXT|
@@ -1141,55 +1109,24 @@ parse_slice(GstVaapiDecoderMpeg2 *decoder,
     }
 
     slice_hdr = &priv->slice_hdr->data.slice_hdr;
+    seq_hdr = &priv->seq_hdr->data.seq_hdr;
+    seq_scalable_ext = priv->seq_scalable_ext ?
+        &priv->seq_scalable_ext->data.seq_scalable_ext : NULL;
 
-    gst_bit_reader_init(&br, packet->data + packet->offset, packet->size);
-    if (priv->height > 2800)
-        READ_UINT8(&br, slice_vertical_position_extension, 3);
-    if (priv->seq_scalable_ext) {
-        GST_ERROR("failed to parse slice with sequence_scalable_extension()");
+    if (!gst_mpeg_video_packet_parse_slice_header(packet, slice_hdr,
+            seq_hdr, seq_scalable_ext)) {
+        GST_ERROR("failed to parse slice header");
         return GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER;
     }
-    READ_UINT8(&br, slice_hdr->quantiser_scale_code, 5);
-    READ_UINT8(&br, extra_bit_slice, 1);
-    if (!extra_bit_slice)
-        slice_hdr->intra_slice = 0;
-    else {
-        READ_UINT8(&br, slice_hdr->intra_slice, 1);
-        READ_UINT8(&br, junk8, 7);
-        READ_UINT8(&br, extra_bit_slice, 1);
-        while (extra_bit_slice) {
-            READ_UINT8(&br, junk8, 8);
-            READ_UINT8(&br, extra_bit_slice, 1);
-        }
-    }
-    slice_hdr->header_size = 32 + gst_bit_reader_get_pos(&br);
-
-    mb_y = ((guint)slice_vertical_position_extension << 7) +
-        packet->type - GST_MPEG_VIDEO_PACKET_SLICE_MIN;
-    mb_x = -1;
-    do {
-        if (!decode_vlc(&br, &mb_inc, mpeg2_mbaddr_vlc_table,
-                G_N_ELEMENTS(mpeg2_mbaddr_vlc_table))) {
-            GST_WARNING("failed to decode first macroblock_address_increment");
-            goto failed;
-        }
-        mb_x += mb_inc == GST_MPEG_VIDEO_MACROBLOCK_ESCAPE ? 33 : mb_inc;
-    } while (mb_inc == GST_MPEG_VIDEO_MACROBLOCK_ESCAPE);
-
-    slice_hdr->slice_horizontal_position = mb_x;
-    slice_hdr->slice_vertical_position   = mb_y;
 
     gst_vaapi_decoder_unit_set_parsed_info(unit, slice_hdr, NULL);
     return GST_VAAPI_DECODER_STATUS_SUCCESS;
-
-failed:
-    return GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER;
 }
 
 static GstVaapiDecoderStatus
 decode_slice(GstVaapiDecoderMpeg2 *decoder, GstVaapiDecoderUnit *unit)
 {
-    GstVaapiDecoderMpeg2Private * const priv = decoder->priv;
+    GstVaapiDecoderMpeg2Private * const priv = &decoder->priv;
     GstVaapiPicture * const picture = priv->current_picture;
     GstVaapiSlice *slice;
     VASliceParameterBufferMPEG2 *slice_param;
@@ -1198,8 +1135,7 @@ decode_slice(GstVaapiDecoderMpeg2 *decoder, GstVaapiDecoderUnit *unit)
         GST_VAAPI_DECODER_CODEC_FRAME(decoder)->input_buffer;
     GstMapInfo map_info;
 
-    GST_DEBUG("slice %d (%u bytes)", slice_hdr->slice_vertical_position,
-              unit->size);
+    GST_DEBUG("slice %d (%u bytes)", slice_hdr->mb_row, unit->size);
 
     if (!is_valid_state(decoder, GST_MPEG_VIDEO_STATE_VALID_PIC_HEADERS))
         return GST_VAAPI_DECODER_STATUS_SUCCESS;
@@ -1220,9 +1156,9 @@ decode_slice(GstVaapiDecoderMpeg2 *decoder, GstVaapiDecoderUnit *unit)
 
     /* Fill in VASliceParameterBufferMPEG2 */
     slice_param                            = slice->param;
-    slice_param->macroblock_offset         = slice_hdr->header_size;
-    slice_param->slice_horizontal_position = slice_hdr->slice_horizontal_position;
-    slice_param->slice_vertical_position   = slice_hdr->slice_vertical_position;
+    slice_param->macroblock_offset         = slice_hdr->header_size + 32;
+    slice_param->slice_horizontal_position = slice_hdr->mb_column;
+    slice_param->slice_vertical_position   = slice_hdr->mb_row;
     slice_param->quantiser_scale_code      = slice_hdr->quantiser_scale_code;
     slice_param->intra_slice_flag          = slice_hdr->intra_slice;
 
@@ -1280,6 +1216,9 @@ parse_unit(GstVaapiDecoderMpeg2 *decoder, GstVaapiDecoderUnit *unit,
         case GST_MPEG_VIDEO_PACKET_EXT_SEQUENCE_DISPLAY:
             status = parse_sequence_display_ext(decoder, unit, packet);
             break;
+        case GST_MPEG_VIDEO_PACKET_EXT_SEQUENCE_SCALABLE:
+            status = parse_sequence_scalable_ext(decoder, unit, packet);
+            break;
         case GST_MPEG_VIDEO_PACKET_EXT_QUANT_MATRIX:
             status = parse_quant_matrix_ext(decoder, unit, packet);
             break;
@@ -1331,6 +1270,9 @@ decode_unit(GstVaapiDecoderMpeg2 *decoder, GstVaapiDecoderUnit *unit,
         case GST_MPEG_VIDEO_PACKET_EXT_SEQUENCE_DISPLAY:
             status = decode_sequence_display_ext(decoder, unit);
             break;
+        case GST_MPEG_VIDEO_PACKET_EXT_SEQUENCE_SCALABLE:
+            status = decode_sequence_scalable_ext(decoder, unit);
+            break;
         case GST_MPEG_VIDEO_PACKET_EXT_QUANT_MATRIX:
             status = decode_quant_matrix_ext(decoder, unit);
             break;
@@ -1366,10 +1308,7 @@ decode_unit(GstVaapiDecoderMpeg2 *decoder, GstVaapiDecoderUnit *unit,
 static GstVaapiDecoderStatus
 ensure_decoder(GstVaapiDecoderMpeg2 *decoder)
 {
-    GstVaapiDecoderMpeg2Private * const priv = decoder->priv;
-
-    g_return_val_if_fail(priv->is_constructed,
-                         GST_VAAPI_DECODER_STATUS_ERROR_INIT_FAILED);
+    GstVaapiDecoderMpeg2Private * const priv = &decoder->priv;
 
     if (!priv->is_opened) {
         priv->is_opened = gst_vaapi_decoder_mpeg2_open(decoder);
@@ -1512,7 +1451,7 @@ gst_vaapi_decoder_mpeg2_start_frame(GstVaapiDecoder *base_decoder,
 {
     GstVaapiDecoderMpeg2 * const decoder =
         GST_VAAPI_DECODER_MPEG2_CAST(base_decoder);
-    GstVaapiDecoderMpeg2Private * const priv = decoder->priv;
+    GstVaapiDecoderMpeg2Private * const priv = &decoder->priv;
     GstMpegVideoSequenceHdr *seq_hdr;
     GstMpegVideoSequenceExt *seq_ext;
     GstMpegVideoSequenceDisplayExt *seq_display_ext;
@@ -1557,6 +1496,15 @@ gst_vaapi_decoder_mpeg2_start_frame(GstVaapiDecoder *base_decoder,
     gst_vaapi_picture_replace(&priv->current_picture, picture);
     gst_vaapi_picture_unref(picture);
 
+    /* Update cropping rectangle */
+    /* XXX: handle picture_display_extension() */
+    if (seq_display_ext && priv->pic_display_ext) {
+        GstVaapiRectangle * const crop_rect = &priv->crop_rect;
+        if (crop_rect->x + crop_rect->width <= priv->width &&
+            crop_rect->y + crop_rect->height <= priv->height)
+            gst_vaapi_picture_set_crop_rect(picture, crop_rect);
+    }
+
     status = ensure_quant_matrix(decoder, picture);
     if (status != GST_VAAPI_DECODER_STATUS_SUCCESS) {
         GST_ERROR("failed to reset quantizer matrix");
@@ -1587,47 +1535,24 @@ gst_vaapi_decoder_mpeg2_flush(GstVaapiDecoder *base_decoder)
 {
     GstVaapiDecoderMpeg2 * const decoder =
         GST_VAAPI_DECODER_MPEG2_CAST(base_decoder);
-    GstVaapiDecoderMpeg2Private * const priv = decoder->priv;
+    GstVaapiDecoderMpeg2Private * const priv = &decoder->priv;
 
     gst_vaapi_dpb_flush(priv->dpb);
     return GST_VAAPI_DECODER_STATUS_SUCCESS;
 }
 
 static void
-gst_vaapi_decoder_mpeg2_finalize(GObject *object)
-{
-    GstVaapiDecoderMpeg2 * const decoder = GST_VAAPI_DECODER_MPEG2_CAST(object);
-
-    gst_vaapi_decoder_mpeg2_destroy(decoder);
-
-    G_OBJECT_CLASS(gst_vaapi_decoder_mpeg2_parent_class)->finalize(object);
-}
-
-static void
-gst_vaapi_decoder_mpeg2_constructed(GObject *object)
-{
-    GstVaapiDecoderMpeg2 * const decoder = GST_VAAPI_DECODER_MPEG2_CAST(object);
-    GstVaapiDecoderMpeg2Private * const priv = decoder->priv;
-    GObjectClass *parent_class;
-
-    parent_class = G_OBJECT_CLASS(gst_vaapi_decoder_mpeg2_parent_class);
-    if (parent_class->constructed)
-        parent_class->constructed(object);
-
-    priv->is_constructed = gst_vaapi_decoder_mpeg2_create(decoder);
-}
-
-static void
 gst_vaapi_decoder_mpeg2_class_init(GstVaapiDecoderMpeg2Class *klass)
 {
-    GObjectClass * const object_class = G_OBJECT_CLASS(klass);
+    GstVaapiMiniObjectClass * const object_class =
+        GST_VAAPI_MINI_OBJECT_CLASS(klass);
     GstVaapiDecoderClass * const decoder_class = GST_VAAPI_DECODER_CLASS(klass);
 
-    g_type_class_add_private(klass, sizeof(GstVaapiDecoderMpeg2Private));
-
-    object_class->finalize      = gst_vaapi_decoder_mpeg2_finalize;
-    object_class->constructed   = gst_vaapi_decoder_mpeg2_constructed;
+    object_class->size          = sizeof(GstVaapiDecoderMpeg2);
+    object_class->finalize      = (GDestroyNotify)gst_vaapi_decoder_finalize;
 
+    decoder_class->create       = gst_vaapi_decoder_mpeg2_create;
+    decoder_class->destroy      = gst_vaapi_decoder_mpeg2_destroy;
     decoder_class->parse        = gst_vaapi_decoder_mpeg2_parse;
     decoder_class->decode       = gst_vaapi_decoder_mpeg2_decode;
     decoder_class->start_frame  = gst_vaapi_decoder_mpeg2_start_frame;
@@ -1635,16 +1560,17 @@ gst_vaapi_decoder_mpeg2_class_init(GstVaapiDecoderMpeg2Class *klass)
     decoder_class->flush        = gst_vaapi_decoder_mpeg2_flush;
 }
 
-static void
-gst_vaapi_decoder_mpeg2_init(GstVaapiDecoderMpeg2 *decoder)
+static inline const GstVaapiDecoderClass *
+gst_vaapi_decoder_mpeg2_class(void)
 {
-    GstVaapiDecoderMpeg2Private *priv;
+    static GstVaapiDecoderMpeg2Class g_class;
+    static gsize g_class_init = FALSE;
 
-    priv                        = GST_VAAPI_DECODER_MPEG2_GET_PRIVATE(decoder);
-    decoder->priv               = priv;
-    priv->hw_profile            = GST_VAAPI_PROFILE_UNKNOWN;
-    priv->profile               = GST_VAAPI_PROFILE_MPEG2_SIMPLE;
-    priv->profile_changed       = TRUE; /* Allow fallbacks to work */
+    if (g_once_init_enter(&g_class_init)) {
+        gst_vaapi_decoder_mpeg2_class_init(&g_class);
+        g_once_init_leave(&g_class_init, TRUE);
+    }
+    return GST_VAAPI_DECODER_CLASS(&g_class);
 }
 
 /**
@@ -1660,20 +1586,6 @@ gst_vaapi_decoder_mpeg2_init(GstVaapiDecoderMpeg2 *decoder)
 GstVaapiDecoder *
 gst_vaapi_decoder_mpeg2_new(GstVaapiDisplay *display, GstCaps *caps)
 {
-    GstVaapiDecoderMpeg2 *decoder;
-
-    g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), NULL);
-    g_return_val_if_fail(GST_IS_CAPS(caps), NULL);
-
-    decoder = g_object_new(
-        GST_VAAPI_TYPE_DECODER_MPEG2,
-        "display",      display,
-        "caps",         caps,
-        NULL
-    );
-    if (!decoder->priv->is_constructed) {
-        g_object_unref(decoder);
-        return NULL;
-    }
-    return GST_VAAPI_DECODER_CAST(decoder);
+    return gst_vaapi_decoder_new(gst_vaapi_decoder_mpeg2_class(),
+        display, caps);
 }