ext/mpeg2dec/gstmpeg2dec.*: Fix padtemplate as we can now do fractional framerates.
authorWim Taymans <wim.taymans@gmail.com>
Wed, 7 Jun 2006 16:15:42 +0000 (16:15 +0000)
committerWim Taymans <wim.taymans@gmail.com>
Wed, 7 Jun 2006 16:15:42 +0000 (16:15 +0000)
Original commit message from CVS:
* ext/mpeg2dec/gstmpeg2dec.c: (gst_mpeg2dec_base_init),
(gst_mpeg2dec_class_init), (gst_mpeg2dec_init),
(gst_mpeg2dec_finalize), (gst_mpeg2dec_reset),
(gst_mpeg2dec_qos_reset), (gst_mpeg2dec_alloc_buffer),
(gst_mpeg2dec_negotiate_format), (init_dummybuf),
(handle_sequence), (handle_picture), (handle_slice),
(gst_mpeg2dec_chain), (gst_mpeg2dec_sink_event),
(gst_mpeg2dec_src_event), (gst_mpeg2dec_change_state):
* ext/mpeg2dec/gstmpeg2dec.h:
Fix padtemplate as we can now do fractional framerates.
Small cleanups.
Use GstSegment.
Add simple frame dropping QoS.
Precalc buffer output sizes and UV offsets.
Always give libmpeg2 a valid fbuf when it wants one.
don't trust libmpeg to discard our buffers but manage it
ourselves.
Fixes #343627, #327350, #335288

ChangeLog
common
ext/mpeg2dec/gstmpeg2dec.c
ext/mpeg2dec/gstmpeg2dec.h

index 626c3b1..948871b 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,24 @@
+2006-06-07  Wim Taymans  <wim@fluendo.com>
+
+       * ext/mpeg2dec/gstmpeg2dec.c: (gst_mpeg2dec_base_init),
+       (gst_mpeg2dec_class_init), (gst_mpeg2dec_init),
+       (gst_mpeg2dec_finalize), (gst_mpeg2dec_reset),
+       (gst_mpeg2dec_qos_reset), (gst_mpeg2dec_alloc_buffer),
+       (gst_mpeg2dec_negotiate_format), (init_dummybuf),
+       (handle_sequence), (handle_picture), (handle_slice),
+       (gst_mpeg2dec_chain), (gst_mpeg2dec_sink_event),
+       (gst_mpeg2dec_src_event), (gst_mpeg2dec_change_state):
+       * ext/mpeg2dec/gstmpeg2dec.h:
+       Fix padtemplate as we can now do fractional framerates.
+       Small cleanups.
+       Use GstSegment.
+       Add simple frame dropping QoS.
+       Precalc buffer output sizes and UV offsets.
+       Always give libmpeg2 a valid fbuf when it wants one.
+       don't trust libmpeg to discard our buffers but manage it
+       ourselves.
+       Fixes #343627, #327350, #335288
+
 2006-06-05  Sebastien Moutte  <sebastien@moutte.net>
 
        * win32/MANIFEST:
diff --git a/common b/common
index 4282dfa..5d58e76 160000 (submodule)
--- a/common
+++ b/common
@@ -1 +1 @@
-Subproject commit 4282dfa89d50e80f59d4e1837af95fbf74e75d18
+Subproject commit 5d58e7652bf38a637dceca167d6e47e7794b2b52
index e95054f..5bfe9e8 100644 (file)
@@ -66,61 +66,6 @@ GST_ELEMENT_DETAILS ("mpeg1 and mpeg2 video decoder",
  */
 #define MAX_ERROR_COUNT (5)
 
-/*
- * We can't use fractions in static pad templates, so
- * we do something manual...
- * FIXME: This is no longer true. We could use a normal pad template
- * now
- */
-static GstPadTemplate *
-src_templ (void)
-{
-  static GstPadTemplate *templ = NULL;
-
-  if (!templ) {
-    GstCaps *caps;
-    GstStructure *structure;
-    GValue list = { 0 }
-    , fps = {
-    0}
-    , fmt = {
-    0};
-    char *fmts[] = { "YV12", "I420", "Y42B", NULL };
-    guint n;
-
-    caps = gst_caps_new_simple ("video/x-raw-yuv",
-        "format", GST_TYPE_FOURCC,
-        GST_MAKE_FOURCC ('I', '4', '2', '0'),
-        "width", GST_TYPE_INT_RANGE, 16, 4096,
-        "height", GST_TYPE_INT_RANGE, 16, 4096, NULL);
-
-    structure = gst_caps_get_structure (caps, 0);
-
-    g_value_init (&list, GST_TYPE_LIST);
-    g_value_init (&fps, GST_TYPE_FRACTION);
-    for (n = 0; fpss[n][0] != 0; n++) {
-      gst_value_set_fraction (&fps, fpss[n][0], fpss[n][1]);
-      gst_value_list_append_value (&list, &fps);
-    }
-    gst_structure_set_value (structure, "framerate", &list);
-    g_value_unset (&list);
-    g_value_unset (&fps);
-
-    g_value_init (&list, GST_TYPE_LIST);
-    g_value_init (&fmt, GST_TYPE_FOURCC);
-    for (n = 0; fmts[n] != NULL; n++) {
-      gst_value_set_fourcc (&fmt, GST_STR_FOURCC (fmts[n]));
-      gst_value_list_append_value (&list, &fmt);
-    }
-    gst_structure_set_value (structure, "format", &list);
-    g_value_unset (&list);
-    g_value_unset (&fmt);
-
-    templ = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, caps);
-  }
-  return templ;
-}
-
 #ifdef enable_user_data
 static GstStaticPadTemplate user_data_template_factory =
 GST_STATIC_PAD_TEMPLATE ("user_data",
@@ -137,11 +82,22 @@ GST_STATIC_PAD_TEMPLATE ("sink",
         "mpegversion = (int) [ 1, 2 ], " "systemstream = (boolean) false")
     );
 
+static GstStaticPadTemplate src_template_factory =
+GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("video/x-raw-yuv, "
+        "format = (fourcc) { YV12, I420, Y42B }, "
+        "width = (int) [ 16, 4096 ], "
+        "height = (int) [ 16, 4096 ], "
+        "framerate = (fraction) { 24000/1001, 24/1, 25/1, 30000/1001, 30/1, 50/1, 60000/1001, 60/1 }")
+    );
+
 static void gst_mpeg2dec_base_init (gpointer g_class);
 static void gst_mpeg2dec_class_init (GstMpeg2decClass * klass);
 static void gst_mpeg2dec_init (GstMpeg2dec * mpeg2dec);
 
-static void gst_mpeg2dec_dispose (GObject * object);
+static void gst_mpeg2dec_finalize (GObject * object);
 static void gst_mpeg2dec_reset (GstMpeg2dec * mpeg2dec);
 
 static void gst_mpeg2dec_set_property (GObject * object, guint prop_id,
@@ -168,7 +124,6 @@ static gboolean gst_mpeg2dec_sink_event (GstPad * pad, GstEvent * event);
 static GstFlowReturn gst_mpeg2dec_chain (GstPad * pad, GstBuffer * buf);
 
 //static gboolean gst_mpeg2dec_sink_query (GstPad * pad, GstQuery * query);
-static GstCaps *gst_mpeg2dec_src_getcaps (GstPad * pad);
 
 #if 0
 static const GstFormat *gst_mpeg2dec_get_formats (GstPad * pad);
@@ -218,7 +173,8 @@ gst_mpeg2dec_base_init (gpointer g_class)
 {
   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
 
-  gst_element_class_add_pad_template (element_class, src_templ ());
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&src_template_factory));
   gst_element_class_add_pad_template (element_class,
       gst_static_pad_template_get (&sink_template_factory));
 #ifdef enable_user_data
@@ -241,7 +197,7 @@ gst_mpeg2dec_class_init (GstMpeg2decClass * klass)
 
   gobject_class->set_property = gst_mpeg2dec_set_property;
   gobject_class->get_property = gst_mpeg2dec_get_property;
-  gobject_class->dispose = gst_mpeg2dec_dispose;
+  gobject_class->finalize = gst_mpeg2dec_finalize;
 
   gstelement_class->change_state = gst_mpeg2dec_change_state;
   gstelement_class->set_index = gst_mpeg2dec_set_index;
@@ -255,24 +211,27 @@ gst_mpeg2dec_init (GstMpeg2dec * mpeg2dec)
   mpeg2dec->sinkpad =
       gst_pad_new_from_template (gst_static_pad_template_get
       (&sink_template_factory), "sink");
-  gst_element_add_pad (GST_ELEMENT (mpeg2dec), mpeg2dec->sinkpad);
   gst_pad_set_chain_function (mpeg2dec->sinkpad,
       GST_DEBUG_FUNCPTR (gst_mpeg2dec_chain));
-  //gst_pad_set_query_function (mpeg2dec->sinkpad,
-  //                            GST_DEBUG_FUNCPTR (gst_mpeg2dec_get_sink_query));
+#if 0
+  gst_pad_set_query_function (mpeg2dec->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_mpeg2dec_get_sink_query));
+#endif
   gst_pad_set_event_function (mpeg2dec->sinkpad,
       GST_DEBUG_FUNCPTR (gst_mpeg2dec_sink_event));
+  gst_element_add_pad (GST_ELEMENT (mpeg2dec), mpeg2dec->sinkpad);
 
-  mpeg2dec->srcpad = gst_pad_new_from_template (src_templ (), "src");
-  gst_element_add_pad (GST_ELEMENT (mpeg2dec), mpeg2dec->srcpad);
-  gst_pad_set_getcaps_function (mpeg2dec->srcpad,
-      GST_DEBUG_FUNCPTR (gst_mpeg2dec_src_getcaps));
+  mpeg2dec->srcpad =
+      gst_pad_new_from_template (gst_static_pad_template_get
+      (&src_template_factory), "src");
   gst_pad_set_event_function (mpeg2dec->srcpad,
       GST_DEBUG_FUNCPTR (gst_mpeg2dec_src_event));
   gst_pad_set_query_type_function (mpeg2dec->srcpad,
       GST_DEBUG_FUNCPTR (gst_mpeg2dec_get_src_query_types));
   gst_pad_set_query_function (mpeg2dec->srcpad,
       GST_DEBUG_FUNCPTR (gst_mpeg2dec_src_query));
+  gst_pad_use_fixed_caps (mpeg2dec->srcpad);
+  gst_element_add_pad (GST_ELEMENT (mpeg2dec), mpeg2dec->srcpad);
 
 #ifdef enable_user_data
   mpeg2dec->userdatapad =
@@ -287,7 +246,7 @@ gst_mpeg2dec_init (GstMpeg2dec * mpeg2dec)
 }
 
 static void
-gst_mpeg2dec_dispose (GObject * object)
+gst_mpeg2dec_finalize (GObject * object)
 {
   GstMpeg2dec *mpeg2dec = GST_MPEG2DEC (object);
 
@@ -296,8 +255,10 @@ gst_mpeg2dec_dispose (GObject * object)
     mpeg2_close (mpeg2dec->decoder);
     mpeg2dec->decoder = NULL;
   }
+  g_free (mpeg2dec->dummybuf[0]);
+  mpeg2dec->dummybuf[0] = NULL;
 
-  G_OBJECT_CLASS (parent_class)->dispose (object);
+  G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
 static void
@@ -307,8 +268,7 @@ gst_mpeg2dec_reset (GstMpeg2dec * mpeg2dec)
   mpeg2dec->format = MPEG2DEC_FORMAT_NONE;
   mpeg2dec->width = -1;
   mpeg2dec->height = -1;
-  mpeg2dec->segment_start = 0;
-  mpeg2dec->segment_end = -1;
+  gst_segment_init (&mpeg2dec->segment, GST_FORMAT_UNDEFINED);
   mpeg2dec->discont_state = MPEG2DEC_DISC_NEW_PICTURE;
   mpeg2dec->frame_period = 0;
   mpeg2dec->need_sequence = TRUE;
@@ -319,6 +279,15 @@ gst_mpeg2dec_reset (GstMpeg2dec * mpeg2dec)
 }
 
 static void
+gst_mpeg2dec_qos_reset (GstMpeg2dec * mpeg2dec)
+{
+  GST_OBJECT_LOCK (mpeg2dec);
+  mpeg2dec->proportion = 1.0;
+  mpeg2dec->earliest_time = -1;
+  GST_OBJECT_UNLOCK (mpeg2dec);
+}
+
+static void
 gst_mpeg2dec_set_index (GstElement * element, GstIndex * index)
 {
   GstMpeg2dec *mpeg2dec = GST_MPEG2DEC (element);
@@ -492,48 +461,19 @@ gst_mpeg2dec_alloc_buffer (GstMpeg2dec * mpeg2dec, gint64 offset,
     GstBuffer ** obuf)
 {
   GstBuffer *outbuf = NULL;
-  gint size;
-  guint8 *buf[3], *out = NULL;
+  guint8 *buf[3];
   GstFlowReturn ret = GST_FLOW_OK;
 
-  if (mpeg2dec->format == MPEG2DEC_FORMAT_I422) {
-    size = mpeg2dec->decoded_width * mpeg2dec->decoded_height;
-
-    ret = gst_mpeg2dec_alloc_sized_buf (mpeg2dec, size * 2, &outbuf);
-    if (ret != GST_FLOW_OK)
-      goto no_buffer;
+  ret = gst_mpeg2dec_alloc_sized_buf (mpeg2dec, mpeg2dec->size, &outbuf);
+  if (ret != GST_FLOW_OK)
+    goto no_buffer;
 
-    out = GST_BUFFER_DATA (outbuf);
+  buf[0] = GST_BUFFER_DATA (outbuf);
+  buf[1] = buf[0] + mpeg2dec->u_offs;
+  buf[2] = buf[0] + mpeg2dec->v_offs;
 
-    buf[0] = out;
-    buf[1] = buf[0] + size;
-    buf[2] = buf[1] + size / 2;
-
-  } else {
-    size = I420_SIZE (mpeg2dec->decoded_width, mpeg2dec->decoded_height);
-
-    ret = gst_mpeg2dec_alloc_sized_buf (mpeg2dec, size, &outbuf);
-    if (ret != GST_FLOW_OK)
-      goto no_buffer;
-
-    out = GST_BUFFER_DATA (outbuf);
-
-    if (mpeg2dec->format == MPEG2DEC_FORMAT_I420) {
-      buf[0] = out +
-          I420_Y_OFFSET (mpeg2dec->decoded_width, mpeg2dec->decoded_height);
-      buf[1] = out +
-          I420_U_OFFSET (mpeg2dec->decoded_width, mpeg2dec->decoded_height);
-      buf[2] = out +
-          I420_V_OFFSET (mpeg2dec->decoded_width, mpeg2dec->decoded_height);
-    } else {
-      buf[0] = out +
-          I420_Y_OFFSET (mpeg2dec->decoded_width, mpeg2dec->decoded_height);
-      buf[1] = out +
-          I420_V_OFFSET (mpeg2dec->decoded_width, mpeg2dec->decoded_height);
-      buf[2] = out +
-          I420_U_OFFSET (mpeg2dec->decoded_width, mpeg2dec->decoded_height);
-    }
-  }
+  GST_DEBUG_OBJECT (mpeg2dec, "set_buf: %p %p %p, outbuf %p",
+      buf[0], buf[1], buf[2], outbuf);
 
   mpeg2_set_buf (mpeg2dec->decoder, buf, outbuf);
 
@@ -552,6 +492,9 @@ no_buffer:
           ("Failed to allocate memory for buffer, reason %s",
               gst_flow_get_name (ret)));
     }
+    GST_DEBUG_OBJECT (mpeg2dec, "no output buffer, reason %s",
+        gst_flow_get_name (ret));
+    mpeg2_set_buf (mpeg2dec->decoder, mpeg2dec->dummybuf, NULL);
     *obuf = NULL;
     return ret;
   }
@@ -570,12 +513,27 @@ gst_mpeg2dec_negotiate_format (GstMpeg2dec * mpeg2dec)
 
   if (sequence->width != sequence->chroma_width &&
       sequence->height != sequence->chroma_height) {
+
     fourcc = GST_STR_FOURCC ("I420");
     mpeg2dec->format = MPEG2DEC_FORMAT_I420;
+    mpeg2dec->size =
+        I420_SIZE (mpeg2dec->decoded_width, mpeg2dec->decoded_height);
+
+    mpeg2dec->u_offs =
+        I420_U_OFFSET (mpeg2dec->decoded_width, mpeg2dec->decoded_height);
+    mpeg2dec->v_offs =
+        I420_V_OFFSET (mpeg2dec->decoded_width, mpeg2dec->decoded_height);
+
   } else if (sequence->width == sequence->chroma_width ||
       sequence->height == sequence->chroma_height) {
+    gint halfsize;
+
     fourcc = GST_STR_FOURCC ("Y42B");
     mpeg2dec->format = MPEG2DEC_FORMAT_I422;
+    halfsize = mpeg2dec->decoded_width * mpeg2dec->decoded_height;
+    mpeg2dec->size = halfsize * 2;
+    mpeg2dec->u_offs = halfsize;
+    mpeg2dec->v_offs = halfsize + (halfsize / 2);
   } else {
     g_warning ("mpeg2dec: 4:4:4 format not yet supported");
     return (FALSE);
@@ -595,13 +553,21 @@ gst_mpeg2dec_negotiate_format (GstMpeg2dec * mpeg2dec)
   return TRUE;
 }
 
+static void
+init_dummybuf (GstMpeg2dec * mpeg2dec)
+{
+  g_free (mpeg2dec->dummybuf[0]);
+
+  mpeg2dec->dummybuf[0] = g_malloc (mpeg2dec->size);
+  mpeg2dec->dummybuf[1] = mpeg2dec->dummybuf[0] + mpeg2dec->u_offs;
+  mpeg2dec->dummybuf[2] = mpeg2dec->dummybuf[1] + mpeg2dec->v_offs;
+}
+
 static GstFlowReturn
 handle_sequence (GstMpeg2dec * mpeg2dec, const mpeg2_info_t * info)
 {
   gint i;
-  GstBuffer *buf;
-  GstFlowReturn ret;
-  guint8 *dummybuf[3] = { NULL, NULL, NULL };
+  GstFlowReturn ret = GST_FLOW_OK;
 
   mpeg2dec->width = info->sequence->picture_width;
   mpeg2dec->height = info->sequence->picture_height;
@@ -635,18 +601,14 @@ handle_sequence (GstMpeg2dec * mpeg2dec, const mpeg2_info_t * info)
     goto negotiate_failed;
 
   mpeg2_custom_fbuf (mpeg2dec->decoder, 1);
-  /* Pump in some null buffers, because otherwise libmpeg2 doesn't 
-   * initialise the discard_fbuf->id */
-  mpeg2_set_buf (mpeg2dec->decoder, dummybuf, NULL);
-  mpeg2_set_buf (mpeg2dec->decoder, dummybuf, NULL);
-  mpeg2_set_buf (mpeg2dec->decoder, dummybuf, NULL);
 
-  ret = gst_mpeg2dec_alloc_buffer (mpeg2dec, mpeg2dec->offset, &buf);
-  if (ret != GST_FLOW_OK)
-    goto done;
+  init_dummybuf (mpeg2dec);
 
-  /* libmpeg2 discards first buffer twice for some reason. */
-  gst_buffer_ref (buf);
+  /* Pump in some null buffers, because otherwise libmpeg2 doesn't 
+   * initialise the discard_fbuf->id */
+  mpeg2_set_buf (mpeg2dec->decoder, mpeg2dec->dummybuf, NULL);
+  mpeg2_set_buf (mpeg2dec->decoder, mpeg2dec->dummybuf, NULL);
+  mpeg2_set_buf (mpeg2dec->decoder, mpeg2dec->dummybuf, NULL);
 
   mpeg2dec->need_sequence = FALSE;
 
@@ -665,32 +627,67 @@ static GstFlowReturn
 handle_picture (GstMpeg2dec * mpeg2dec, const mpeg2_info_t * info)
 {
   gboolean key_frame = FALSE;
-  GstBuffer *outbuf;
+  GstBuffer *outbuf, **bufpen;
+  GstMpeg2Buf *mpbuf;
   GstFlowReturn ret;
+  gint type;
 
-  GST_DEBUG_OBJECT (mpeg2dec, "handle picture");
+  ret = gst_mpeg2dec_alloc_buffer (mpeg2dec, mpeg2dec->offset, &outbuf);
+  if (ret != GST_FLOW_OK)
+    goto no_buffer;
 
   if (info->current_picture) {
-    key_frame =
-        (info->current_picture->flags & PIC_MASK_CODING_TYPE) ==
-        PIC_FLAG_CODING_TYPE_I;
+    type = info->current_picture->flags & PIC_MASK_CODING_TYPE;
+  } else {
+    type = 0;
+  }
+
+  GST_DEBUG_OBJECT (mpeg2dec, "handle picture type %d", type);
+
+  key_frame = type == PIC_FLAG_CODING_TYPE_I;
+
+  switch (type) {
+    case PIC_FLAG_CODING_TYPE_I:
+      mpeg2_skip (mpeg2dec->decoder, 0);
+    case PIC_FLAG_CODING_TYPE_P:
+      mpbuf = &mpeg2dec->ip_buffers[mpeg2dec->ip_bufpos];
+      bufpen = &mpbuf->buffer;
+      mpbuf->type = type;
+
+      GST_DEBUG_OBJECT (mpeg2dec, "I/P unref %p, ref %p", *bufpen, outbuf);
+      if (*bufpen)
+        gst_buffer_unref (*bufpen);
+      *bufpen = outbuf;
+      mpeg2dec->ip_bufpos = (mpeg2dec->ip_bufpos + 1) & 3;
+      break;
+    case PIC_FLAG_CODING_TYPE_B:
+      mpbuf = &mpeg2dec->b_buffer;
+      mpbuf->type = type;
+      bufpen = &mpbuf->buffer;
+      GST_DEBUG_OBJECT (mpeg2dec, "B unref %p, ref %p", *bufpen, outbuf);
+      if (*bufpen)
+        gst_buffer_unref (*bufpen);
+      *bufpen = outbuf;
+      break;
+    default:
+      break;
   }
-  ret = gst_mpeg2dec_alloc_buffer (mpeg2dec, mpeg2dec->offset, &outbuf);
-  if (ret != GST_FLOW_OK)
-    goto done;
 
   GST_DEBUG_OBJECT (mpeg2dec, "picture %s, outbuf %p, offset %"
       G_GINT64_FORMAT,
       key_frame ? ", kf," : "    ", outbuf, GST_BUFFER_OFFSET (outbuf)
       );
 
-  if (mpeg2dec->discont_state == MPEG2DEC_DISC_NEW_PICTURE && key_frame)
+  if (mpeg2dec->discont_state == MPEG2DEC_DISC_NEW_PICTURE && key_frame) {
     mpeg2dec->discont_state = MPEG2DEC_DISC_NEW_KEYFRAME;
+  }
 
-  mpeg2_skip (mpeg2dec->decoder, 0);
-
-done:
   return ret;
+
+no_buffer:
+  {
+    return ret;
+  }
 }
 
 static GstFlowReturn
@@ -698,119 +695,153 @@ handle_slice (GstMpeg2dec * mpeg2dec, const mpeg2_info_t * info)
 {
   GstBuffer *outbuf = NULL;
   GstFlowReturn ret = GST_FLOW_OK;
+  const mpeg2_picture_t *picture;
+  gboolean key_frame = FALSE;
+  GstClockTime time;
 
   GST_DEBUG_OBJECT (mpeg2dec, "picture slice/end %p %p %p %p",
       info->display_fbuf,
       info->display_picture, info->current_picture,
       (info->display_fbuf ? info->display_fbuf->id : NULL));
 
-  if (info->display_fbuf && info->display_fbuf->id) {
-    const mpeg2_picture_t *picture;
-    gboolean key_frame = FALSE;
-    GstClockTime time;
+  if (!info->display_fbuf || !info->display_fbuf->id)
+    goto no_display;
 
-    outbuf = GST_BUFFER (info->display_fbuf->id);
+  outbuf = GST_BUFFER (info->display_fbuf->id);
 
-    picture = info->display_picture;
+  picture = info->display_picture;
 
-    key_frame =
-        (picture->flags & PIC_MASK_CODING_TYPE) == PIC_FLAG_CODING_TYPE_I;
+  key_frame = (picture->flags & PIC_MASK_CODING_TYPE) == PIC_FLAG_CODING_TYPE_I;
 
-    GST_DEBUG_OBJECT (mpeg2dec, "picture keyframe %d", key_frame);
+  GST_DEBUG_OBJECT (mpeg2dec, "picture flags: %d, type: %d, keyframe: %d",
+      picture->flags, picture->flags & PIC_MASK_CODING_TYPE, key_frame);
 
-    if (!key_frame)
-      GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT);
-    else
-      GST_BUFFER_FLAG_UNSET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT);
+  if (key_frame) {
+    GST_BUFFER_FLAG_UNSET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT);
+    mpeg2_skip (mpeg2dec->decoder, 0);
+  } else {
+    GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT);
+  }
 
-    if (mpeg2dec->discont_state == MPEG2DEC_DISC_NEW_KEYFRAME && key_frame)
-      mpeg2dec->discont_state = MPEG2DEC_DISC_NONE;
+  if (mpeg2dec->discont_state == MPEG2DEC_DISC_NEW_KEYFRAME && key_frame)
+    mpeg2dec->discont_state = MPEG2DEC_DISC_NONE;
 
-    time = GST_CLOCK_TIME_NONE;
+  time = GST_CLOCK_TIME_NONE;
 
 #if MPEG2_RELEASE < MPEG2_VERSION(0,4,0)
-    if (picture->flags & PIC_FLAG_PTS)
-      time = MPEG_TIME_TO_GST_TIME (picture->pts);
+  if (picture->flags & PIC_FLAG_PTS)
+    time = MPEG_TIME_TO_GST_TIME (picture->pts);
 #else
-    if (picture->flags & PIC_FLAG_TAGS)
-      time = MPEG_TIME_TO_GST_TIME ((GstClockTime) (picture->
-              tag2) << 32 | picture->tag);
+  if (picture->flags & PIC_FLAG_TAGS)
+    time = MPEG_TIME_TO_GST_TIME ((GstClockTime) (picture->
+            tag2) << 32 | picture->tag);
 #endif
 
-    if (time == GST_CLOCK_TIME_NONE) {
-      time = mpeg2dec->next_time;
-      GST_DEBUG_OBJECT (mpeg2dec, "picture didn't have pts");
-    } else {
-      GST_DEBUG_OBJECT (mpeg2dec,
-          "picture had pts %" GST_TIME_FORMAT ", we had %"
-          GST_TIME_FORMAT, GST_TIME_ARGS (time),
-          GST_TIME_ARGS (mpeg2dec->next_time));
-      mpeg2dec->next_time = time;
-    }
-    GST_BUFFER_TIMESTAMP (outbuf) = time;
-
-    /* TODO set correct offset here based on frame number */
-    if (info->display_picture_2nd) {
-      GST_BUFFER_DURATION (outbuf) = (picture->nb_fields +
-          info->display_picture_2nd->nb_fields) * mpeg2dec->frame_period / 2;
-    } else {
-      GST_BUFFER_DURATION (outbuf) =
-          picture->nb_fields * mpeg2dec->frame_period / 2;
-    }
-    mpeg2dec->next_time += GST_BUFFER_DURATION (outbuf);
-
+  if (time == GST_CLOCK_TIME_NONE) {
+    time = mpeg2dec->next_time;
+    GST_DEBUG_OBJECT (mpeg2dec, "picture didn't have pts");
+  } else {
     GST_DEBUG_OBJECT (mpeg2dec,
-        "picture: %s %s fields:%d off:%" G_GINT64_FORMAT " ts:%"
-        GST_TIME_FORMAT,
-        (picture->flags & PIC_FLAG_TOP_FIELD_FIRST ? "tff " : "    "),
-        (picture->flags & PIC_FLAG_PROGRESSIVE_FRAME ? "prog" : "    "),
-        picture->nb_fields, GST_BUFFER_OFFSET (outbuf),
-        GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)));
-
-    if (mpeg2dec->index) {
-      gst_index_add_association (mpeg2dec->index, mpeg2dec->index_id,
-          (key_frame ? GST_ASSOCIATION_FLAG_KEY_UNIT : 0),
-          GST_FORMAT_BYTES, GST_BUFFER_OFFSET (outbuf),
-          GST_FORMAT_TIME, GST_BUFFER_TIMESTAMP (outbuf), 0);
-    }
+        "picture had pts %" GST_TIME_FORMAT ", we had %"
+        GST_TIME_FORMAT, GST_TIME_ARGS (time),
+        GST_TIME_ARGS (mpeg2dec->next_time));
+    mpeg2dec->next_time = time;
+  }
+  GST_BUFFER_TIMESTAMP (outbuf) = time;
 
-    if (picture->flags & PIC_FLAG_SKIP) {
-      GST_DEBUG_OBJECT (mpeg2dec, "dropping buffer because of skip flag");
-    } else if (mpeg2dec->discont_state != MPEG2DEC_DISC_NONE) {
-      GST_DEBUG_OBJECT (mpeg2dec, "dropping buffer, discont state %d",
-          mpeg2dec->discont_state);
-    } else if (mpeg2dec->next_time < mpeg2dec->segment_start) {
-      GST_DEBUG_OBJECT (mpeg2dec, "dropping buffer, next_time %"
-          GST_TIME_FORMAT " <  segment_start %" GST_TIME_FORMAT,
-          GST_TIME_ARGS (mpeg2dec->next_time),
-          GST_TIME_ARGS (mpeg2dec->segment_start));
-    } else {
-      gboolean cropped_outbuf_different_from_outbuf = FALSE;
-
-      GST_LOG_OBJECT (mpeg2dec, "pushing buffer, timestamp %"
-          GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT,
-          GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
-          GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)));
-
-      if ((mpeg2dec->decoded_height > mpeg2dec->height) ||
-          (mpeg2dec->decoded_width > mpeg2dec->width)) {
-        cropped_outbuf_different_from_outbuf = crop_buffer (mpeg2dec, &outbuf);
-      }
+  /* TODO set correct offset here based on frame number */
+  if (info->display_picture_2nd) {
+    GST_BUFFER_DURATION (outbuf) = (picture->nb_fields +
+        info->display_picture_2nd->nb_fields) * mpeg2dec->frame_period / 2;
+  } else {
+    GST_BUFFER_DURATION (outbuf) =
+        picture->nb_fields * mpeg2dec->frame_period / 2;
+  }
+  mpeg2dec->next_time += GST_BUFFER_DURATION (outbuf);
 
-      gst_buffer_ref (outbuf);
-      ret = gst_pad_push (mpeg2dec->srcpad, outbuf);
-      if (cropped_outbuf_different_from_outbuf)
-        gst_buffer_unref (outbuf);
-    }
+  GST_DEBUG_OBJECT (mpeg2dec,
+      "picture: %s %s fields:%d off:%" G_GINT64_FORMAT " ts:%"
+      GST_TIME_FORMAT,
+      (picture->flags & PIC_FLAG_TOP_FIELD_FIRST ? "tff " : "    "),
+      (picture->flags & PIC_FLAG_PROGRESSIVE_FRAME ? "prog" : "    "),
+      picture->nb_fields, GST_BUFFER_OFFSET (outbuf),
+      GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)));
+
+  if (mpeg2dec->index) {
+    gst_index_add_association (mpeg2dec->index, mpeg2dec->index_id,
+        (key_frame ? GST_ASSOCIATION_FLAG_KEY_UNIT : 0),
+        GST_FORMAT_BYTES, GST_BUFFER_OFFSET (outbuf),
+        GST_FORMAT_TIME, GST_BUFFER_TIMESTAMP (outbuf), 0);
+  }
+
+  if (picture->flags & PIC_FLAG_SKIP)
+    goto skip;
+
+  if (mpeg2dec->discont_state != MPEG2DEC_DISC_NONE)
+    goto drop;
+
+  if (GST_CLOCK_TIME_IS_VALID (time)) {
+    gboolean need_skip;
+    GstClockTime qostime;
+
+    /* qos needs to be done on running time */
+    qostime = gst_segment_to_running_time (&mpeg2dec->segment, GST_FORMAT_TIME,
+        time);
+
+    GST_OBJECT_LOCK (mpeg2dec);
+    /* check for QoS, don't perform the last steps of getting and
+     * pushing the buffers that are known to be late. */
+    /* FIXME, we can also entirely skip decoding if the next valid buffer is
+     * known to be after a keyframe (using the granule_shift) */
+    need_skip = mpeg2dec->earliest_time != -1
+        && qostime <= mpeg2dec->earliest_time;
+    GST_OBJECT_UNLOCK (mpeg2dec);
+
+    if (need_skip)
+      goto dropping_qos;
   }
 
-  if (info->discard_fbuf && info->discard_fbuf->id) {
-    GstBuffer *discard = GST_BUFFER (info->discard_fbuf->id);
+  GST_LOG_OBJECT (mpeg2dec, "pushing buffer %p, timestamp %"
+      GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT,
+      outbuf,
+      GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
+      GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)));
 
-    gst_buffer_unref (discard);
-    GST_DEBUG_OBJECT (mpeg2dec, "Discarded buffer %p", discard);
+  if ((mpeg2dec->decoded_height > mpeg2dec->height) ||
+      (mpeg2dec->decoded_width > mpeg2dec->width)) {
+    crop_buffer (mpeg2dec, &outbuf);
   }
+
+  /* ref before pushing it out, so we still have the ref in our
+   * array of buffers */
+  gst_buffer_ref (outbuf);
+  ret = gst_pad_push (mpeg2dec->srcpad, outbuf);
+  GST_DEBUG_OBJECT (mpeg2dec, "pushed with result %s", gst_flow_get_name (ret));
+
   return ret;
+
+  /* special cases */
+no_display:
+  {
+    GST_DEBUG_OBJECT (mpeg2dec, "no picture to display");
+    return GST_FLOW_OK;
+  }
+skip:
+  {
+    GST_DEBUG_OBJECT (mpeg2dec, "dropping buffer because of skip flag");
+    return GST_FLOW_OK;
+  }
+drop:
+  {
+    GST_DEBUG_OBJECT (mpeg2dec, "dropping buffer, discont state %d",
+        mpeg2dec->discont_state);
+    return GST_FLOW_OK;
+  }
+dropping_qos:
+  {
+    GST_DEBUG_OBJECT (mpeg2dec, "dropping buffer because of QoS");
+    return GST_FLOW_OK;
+  }
 }
 
 #if 0
@@ -871,6 +902,27 @@ gst_mpeg2dec_chain (GstPad * pad, GstBuffer * buf)
 
   mpeg2dec->offset = GST_BUFFER_OFFSET (buf);
 
+  if (pts != GST_CLOCK_TIME_NONE) {
+    gint64 mpeg_pts = GST_TIME_TO_MPEG_TIME (pts);
+
+    GST_DEBUG_OBJECT (mpeg2dec,
+        "have pts: %" G_GINT64_FORMAT " (%" GST_TIME_FORMAT ")",
+        mpeg_pts, GST_TIME_ARGS (MPEG_TIME_TO_GST_TIME (mpeg_pts)));
+
+#if MPEG2_RELEASE >= MPEG2_VERSION(0,4,0)
+    mpeg2_tag_picture (mpeg2dec->decoder, mpeg_pts & 0xffffffff,
+        mpeg_pts >> 32);
+#else
+    mpeg2_pts (mpeg2dec->decoder, mpeg_pts);
+#endif
+  } else {
+    GST_LOG ("no pts");
+  }
+
+  GST_LOG_OBJECT (mpeg2dec, "calling mpeg2_buffer");
+  mpeg2_buffer (mpeg2dec->decoder, data, end);
+  GST_LOG_OBJECT (mpeg2dec, "calling mpeg2_buffer done");
+
   while (!done) {
     GST_LOG_OBJECT (mpeg2dec, "calling parse");
     state = mpeg2_parse (mpeg2dec->decoder);
@@ -884,6 +936,7 @@ gst_mpeg2dec_chain (GstPad * pad, GstBuffer * buf)
         GST_DEBUG_OBJECT (mpeg2dec, "sequence repeated");
         break;
       case STATE_GOP:
+        GST_DEBUG_OBJECT (mpeg2dec, "gop");
         break;
       case STATE_PICTURE:
         ret = handle_picture (mpeg2dec, info);
@@ -897,39 +950,16 @@ gst_mpeg2dec_chain (GstPad * pad, GstBuffer * buf)
         break;
 #if MPEG2_RELEASE >= MPEG2_VERSION (0, 4, 0)
       case STATE_INVALID_END:
+        GST_DEBUG_OBJECT (mpeg2dec, "invalid end");
 #endif
       case STATE_END:
+        GST_DEBUG_OBJECT (mpeg2dec, "end");
         mpeg2dec->need_sequence = TRUE;
       case STATE_SLICE:
         ret = handle_slice (mpeg2dec, info);
         break;
       case STATE_BUFFER:
-        if (data == NULL) {
-          done = TRUE;
-        } else {
-          if (pts != GST_CLOCK_TIME_NONE) {
-            gint64 mpeg_pts = GST_TIME_TO_MPEG_TIME (pts);
-
-            GST_DEBUG_OBJECT (mpeg2dec,
-                "have pts: %" G_GINT64_FORMAT " (%" GST_TIME_FORMAT ")",
-                mpeg_pts, GST_TIME_ARGS (MPEG_TIME_TO_GST_TIME (mpeg_pts)));
-
-#if MPEG2_RELEASE >= MPEG2_VERSION(0,4,0)
-            mpeg2_tag_picture (mpeg2dec->decoder, mpeg_pts & 0xffffffff,
-                mpeg_pts >> 32);
-#else
-            mpeg2_pts (mpeg2dec->decoder, mpeg_pts);
-#endif
-          } else {
-            GST_LOG ("no pts");
-          }
-
-          GST_LOG_OBJECT (mpeg2dec, "calling mpeg2_buffer");
-          mpeg2_buffer (mpeg2dec->decoder, data, end);
-          GST_LOG_OBJECT (mpeg2dec, "calling mpeg2_buffer done");
-
-          data = NULL;
-        }
+        done = TRUE;
         break;
         /* error */
       case STATE_INVALID:
@@ -962,76 +992,107 @@ gst_mpeg2dec_chain (GstPad * pad, GstBuffer * buf)
 #endif
 
     if (ret != GST_FLOW_OK) {
-      mpeg2_reset (mpeg2dec->decoder, 0);
+      GST_DEBUG_OBJECT (mpeg2dec, "exit loop, reason %s",
+          gst_flow_get_name (ret));
       break;
     }
   }
+done:
   gst_buffer_unref (buf);
   return ret;
 
+  /* errors */
 exit:
-  gst_buffer_unref (buf);
-  return GST_FLOW_OK;
-
+  {
+    ret = GST_FLOW_OK;
+    goto done;
+  }
 exit_error:
-  GST_ELEMENT_ERROR (mpeg2dec, STREAM, DECODE, (NULL), (NULL));
-  gst_buffer_unref (buf);
-  return GST_FLOW_ERROR;
+  {
+    GST_ELEMENT_ERROR (mpeg2dec, STREAM, DECODE, (NULL), (NULL));
+    ret = GST_FLOW_ERROR;
+    goto done;
+  }
 }
 
 static gboolean
 gst_mpeg2dec_sink_event (GstPad * pad, GstEvent * event)
 {
-
-  GstMpeg2dec *mpeg2dec = GST_MPEG2DEC (GST_PAD_PARENT (pad));
+  GstMpeg2dec *mpeg2dec;
   gboolean ret = TRUE;
 
+  mpeg2dec = GST_MPEG2DEC (gst_pad_get_parent (pad));
+
   GST_DEBUG_OBJECT (mpeg2dec, "Got %s event on sink pad",
       GST_EVENT_TYPE_NAME (event));
 
   switch (GST_EVENT_TYPE (event)) {
     case GST_EVENT_NEWSEGMENT:
     {
-      mpeg2dec->next_time = -1;;
-      ret = gst_pad_event_default (pad, event);
+      gboolean update;
+      GstFormat format;
+      gdouble rate, arate;
+      gint64 start, stop, time;
+
+      gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
+          &start, &stop, &time);
+
+      /* we need TIME and a positive rate */
+      if (format != GST_FORMAT_TIME)
+        goto newseg_wrong_format;
+
+      if (rate <= 0.0)
+        goto newseg_wrong_rate;
+
+      /* now configure the values */
+      gst_segment_set_newsegment_full (&mpeg2dec->segment, update,
+          rate, arate, format, start, stop, time);
+
+      ret = gst_pad_push_event (mpeg2dec->srcpad, event);
       break;
     }
     case GST_EVENT_FLUSH_START:
-      ret = gst_pad_event_default (pad, event);
+      ret = gst_pad_push_event (mpeg2dec->srcpad, event);
       break;
     case GST_EVENT_FLUSH_STOP:
+    {
       mpeg2dec->discont_state = MPEG2DEC_DISC_NEW_PICTURE;
       mpeg2dec->next_time = -1;;
+      gst_mpeg2dec_qos_reset (mpeg2dec);
       mpeg2_reset (mpeg2dec->decoder, 0);
-      ret = gst_pad_event_default (pad, event);
+      mpeg2_skip (mpeg2dec->decoder, 1);
+      ret = gst_pad_push_event (mpeg2dec->srcpad, event);
       break;
+    }
     case GST_EVENT_EOS:
       if (mpeg2dec->index && mpeg2dec->closed) {
         gst_index_commit (mpeg2dec->index, mpeg2dec->index_id);
       }
-      ret = gst_pad_event_default (pad, event);
+      ret = gst_pad_push_event (mpeg2dec->srcpad, event);
       break;
     default:
-      ret = gst_pad_event_default (pad, event);
+      ret = gst_pad_push_event (mpeg2dec->srcpad, event);
       break;
   }
 
-  return ret;
-
-}
-
-static GstCaps *
-gst_mpeg2dec_src_getcaps (GstPad * pad)
-{
-  GstCaps *caps;
+done:
+  gst_object_unref (mpeg2dec);
 
-  GST_OBJECT_LOCK (pad);
-  if (!(caps = GST_PAD_CAPS (pad)))
-    caps = (GstCaps *) gst_pad_get_pad_template_caps (pad);
-  caps = gst_caps_ref (caps);
-  GST_OBJECT_UNLOCK (pad);
+  return ret;
 
-  return caps;
+  /* ERRORS */
+newseg_wrong_format:
+  {
+    GST_DEBUG_OBJECT (mpeg2dec, "received non TIME newsegment");
+    gst_event_unref (event);
+    goto done;
+  }
+newseg_wrong_rate:
+  {
+    GST_DEBUG_OBJECT (mpeg2dec, "negative rates not supported yet");
+    gst_event_unref (event);
+    goto done;
+  }
 }
 
 static gboolean
@@ -1420,7 +1481,7 @@ gst_mpeg2dec_src_event (GstPad * pad, GstEvent * event)
       /* the all-formats seek logic */
     case GST_EVENT_SEEK:{
       gst_event_ref (event);
-      if (!(res = gst_pad_event_default (pad, event))) {
+      if (!(res = gst_pad_push_event (mpeg2dec->sinkpad, event))) {
         if (mpeg2dec->index)
           res = index_seek (pad, event);
         else
@@ -1429,6 +1490,26 @@ gst_mpeg2dec_src_event (GstPad * pad, GstEvent * event)
       gst_event_unref (event);
       break;
     }
+    case GST_EVENT_QOS:
+    {
+      gdouble proportion;
+      GstClockTimeDiff diff;
+      GstClockTime timestamp;
+
+      gst_event_parse_qos (event, &proportion, &diff, &timestamp);
+
+      GST_OBJECT_LOCK (mpeg2dec);
+      mpeg2dec->proportion = proportion;
+      mpeg2dec->earliest_time = timestamp + diff;
+      GST_OBJECT_UNLOCK (mpeg2dec);
+
+      GST_DEBUG_OBJECT (mpeg2dec,
+          "got QoS %" GST_TIME_FORMAT ", %" G_GINT64_FORMAT,
+          GST_TIME_ARGS (timestamp), diff);
+
+      res = gst_pad_push_event (mpeg2dec->sinkpad, event);
+      break;
+    }
     case GST_EVENT_NAVIGATION:
       /* Forward a navigation event unchanged */
     default:
@@ -1460,6 +1541,7 @@ gst_mpeg2dec_change_state (GstElement * element, GstStateChange transition)
       break;
     case GST_STATE_CHANGE_READY_TO_PAUSED:
       gst_mpeg2dec_reset (mpeg2dec);
+      gst_mpeg2dec_qos_reset (mpeg2dec);
       break;
     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
     default:
@@ -1472,6 +1554,7 @@ gst_mpeg2dec_change_state (GstElement * element, GstStateChange transition)
     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
       break;
     case GST_STATE_CHANGE_PAUSED_TO_READY:
+      gst_mpeg2dec_qos_reset (mpeg2dec);
       break;
     case GST_STATE_CHANGE_READY_TO_NULL:
       if (mpeg2dec->decoder) {
index 0fdc8c2..8c44b2f 100644 (file)
@@ -41,8 +41,6 @@ G_BEGIN_DECLS
 #define MPEG_TIME_TO_GST_TIME(time) (((time) * (GST_MSECOND/10)) / 9LL)
 #define GST_TIME_TO_MPEG_TIME(time) (((time) * 9LL) / (GST_MSECOND/10))
 
-#define GST_MPEG2DEC_NUM_BUFS 6
-
 typedef struct _GstMpeg2dec GstMpeg2dec;
 typedef struct _GstMpeg2decClass GstMpeg2decClass;
 
@@ -61,6 +59,11 @@ typedef enum
   MPEG2DEC_DISC_NEW_KEYFRAME
 } DiscontState;
 
+typedef struct {
+  gint type;
+  GstBuffer *buffer;
+} GstMpeg2Buf;
+
 struct _GstMpeg2dec {
   GstElement     element;
 
@@ -75,14 +78,16 @@ struct _GstMpeg2dec {
   gboolean       closed;
   gboolean       have_fbuf;
 
-  GstBuffer     *buffers[GST_MPEG2DEC_NUM_BUFS];
+  /* buffer management */
+  guint          ip_bufpos;
+  GstMpeg2Buf    ip_buffers[4];
+  GstMpeg2Buf    b_buffer;
 
   DiscontState   discont_state;
 
   /* the timestamp of the next frame */
   GstClockTime   next_time;
-  gint64         segment_start;
-  gint64         segment_end;
+  GstSegment     segment;
 
   /* video state */
   Mpeg2decFormat format;
@@ -95,18 +100,26 @@ struct _GstMpeg2dec {
   gint           frame_rate_code;
   gint64         total_frames;
   gint64         frame_period;
+
+  gint           size;
+  gint           u_offs;
+  gint           v_offs;
+  guint8        *dummybuf[3];
+
   
   guint64        offset;
   gint           fps_n;
   gint           fps_d;
   gboolean       need_sequence;
 
-  GstEvent      *pending_event;
-
   GstIndex      *index;
   gint           index_id;
   
   gint           error_count;
+
+  /* QoS stuff */ /* with LOCK*/
+  gdouble        proportion;
+  GstClockTime   earliest_time;
 };
 
 struct _GstMpeg2decClass {