ducatimpeg4dec: xvid support
authorRob Clark <rob@ti.com>
Sun, 4 Dec 2011 05:20:15 +0000 (23:20 -0600)
committerRob Clark <rob@ti.com>
Sun, 11 Dec 2011 23:51:04 +0000 (17:51 -0600)
Handle xvid streams with B-frames encoded in containers that don't
support B-frames natively (ie. PB-frames).  Packed frames are split
and passed to the codec in successive _process() calls, and non-coded
frames are discarded.

src/gstducatimpeg4dec.c
src/gstducatimpeg4dec.h
src/gstducatividdec.c
src/gstducatividdec.h

index 124731a2554112e9c077743523176f88464b4985..98e1fe531d54f0523ba16766a21e62abfa759d2f 100644 (file)
@@ -36,6 +36,8 @@
 
 #include "gstducatimpeg4dec.h"
 
+#include <math.h>
+
 
 #define PADX  32
 #define PADY  32
@@ -92,7 +94,7 @@ gst_ducati_mpeg4dec_allocate_params (GstDucatiVidDec * self, gint params_sz,
 
   if (ret) {
     IMPEG4VDEC_Params *params = (IMPEG4VDEC_Params *) self->params;
-    self->params->displayDelay = IVIDDEC3_DISPLAY_DELAY_1;
+    self->params->displayDelay = IVIDDEC3_DISPLAY_DELAY_AUTO;
     self->dynParams->lateAcquireArg = -1;
     params->outloopDeBlocking = TRUE;
     params->sorensonSparkStream = FALSE;
@@ -102,8 +104,188 @@ gst_ducati_mpeg4dec_allocate_params (GstDucatiVidDec * self, gint params_sz,
   return ret;
 }
 
+#define VOS_START_CODE    0xb0    /* + visual object sequence */
+#define VOS_END_CODE      0xb1
+#define UD_START_CODE     0xb2    /* user data */
+#define GVOP_START_CODE   0xb3    /* + group of VOP */
+#define VS_ERROR_CODE     0xb4
+#define VO_START_CODE     0xb5    /* visual object */
+#define VOP_START_CODE    0xb6    /* + */
+
+static const guint8 sc[] = {0x00, 0x00, 0x01};  /* start code */
+#define SC_SZ                G_N_ELEMENTS (sc)  /* start code size */
+
+static GstBitReader *
+get_bit_reader (GstDucatiMpeg4Dec *self, const guint8 * data, guint size)
+{
+  if (self->br) {
+    gst_bit_reader_init (self->br, data, size);
+  } else {
+    self->br = gst_bit_reader_new (data, size);
+  }
+  return self->br;
+}
+
+static void
+decode_vol_header (GstDucatiMpeg4Dec *self, const guint8 * data, guint size)
+{
+  GstBitReader *br = get_bit_reader (self, data, size);
+  guint32 is_oli, vc_param, vbv_param=0;
+  guint32 ar_info, vop_tir;
+
+  gst_bit_reader_skip (br, 1);         /* random_accessible_vol */
+  gst_bit_reader_skip (br, 8);         /* video_object_type_indication */
+
+  gst_bit_reader_get_bits_uint32 (br,  /* is_object_layer_identifier */
+      &is_oli, 1);
+  if (is_oli) {
+    gst_bit_reader_skip (br, 4);       /* video_object_layer_verid */
+    gst_bit_reader_skip (br, 3);       /* video_object_layer_priority */
+  }
+
+  gst_bit_reader_get_bits_uint32 (br,  /* aspect_ratio_info */
+      &ar_info, 4);
+  if (ar_info == 0xf) {
+    gst_bit_reader_skip (br, 8);       /* par_width */
+    gst_bit_reader_skip (br, 8);       /* par_height */
+  }
+
+  gst_bit_reader_get_bits_uint32 (br,  /* vol_control_parameters */
+      &vc_param, 1);
+  if (vc_param) {
+    gst_bit_reader_skip (br, 2);       /* chroma_format */
+    gst_bit_reader_skip (br, 1);       /* low_delay */
+    gst_bit_reader_get_bits_uint32 (   /* vbv_parameters */
+        br, &vbv_param, 1);
+    if (vbv_param) {
+      gst_bit_reader_skip (br, 79);    /* don't care */
+    }
+  }
+
+  gst_bit_reader_skip (br, 2);         /* video_object_layer_shape */
+  gst_bit_reader_skip (br, 1);         /* marker_bit */
+  gst_bit_reader_get_bits_uint32 (br,  /* vop_time_increment_resolution */
+      &vop_tir, 16);
+  gst_bit_reader_skip (br, 1);         /* marker_bit */
+
+  self->time_increment_bits = (guint32)log2((double)(vop_tir - 1)) + 1;
+
+  GST_DEBUG_OBJECT (self, "vop_tir=%d, time_increment_bits=%d",
+      vop_tir, self->time_increment_bits);
+
+  if (self->time_increment_bits < 1)
+    self->time_increment_bits = 1;
+
+  /* we don't care about anything beyond here */
+}
+
+static gboolean
+is_vop_coded (GstDucatiMpeg4Dec *self, const guint8 * data, guint size)
+{
+  GstBitReader *br =
+      get_bit_reader (self, data, size);
+  guint32 b = 0;
+
+  gst_bit_reader_skip (br, 2);         /* vop_coding_type */
+
+  do {                                 /* modulo_time_base */
+    gst_bit_reader_get_bits_uint32 (br, &b, 1);
+  } while (b != 0);
+
+  gst_bit_reader_skip (br, 1);         /* marker_bit */
+  gst_bit_reader_skip (br,             /* vop_time_increment */
+      self->time_increment_bits);
+  gst_bit_reader_skip (br, 1);         /* marker_bit */
+  gst_bit_reader_get_bits_uint32 (br,  /* vop_coded */
+      &b, 1);
+
+  return b;
+}
+
+static GstBuffer *
+gst_ducati_mpeg4dec_push_input (GstDucatiVidDec * vdec, GstBuffer * buf)
+{
+  GstDucatiMpeg4Dec *self = GST_DUCATIMPEG4DEC (vdec);
+  GstBuffer *remaining = NULL;
+  gint insize = GST_BUFFER_SIZE (buf);
+  const guint8 *in  = GST_BUFFER_DATA (buf);
+  gint size = 0;
+  guint8 last_start_code = 0xff;
+
+  while (insize > (SC_SZ + 1)) {
+    gint nal_size;
+    guint8 start_code = in[SC_SZ];
+    gboolean skip = FALSE;
+
+    GST_DEBUG_OBJECT (self, "start_code: %02x", start_code);
+
+    if (size > 0) {
+      /* check if we've found a potential start of frame: */
+      if ((start_code == VOS_START_CODE) ||
+          (start_code == GVOP_START_CODE) ||
+          (start_code == VOP_START_CODE) ||
+          (start_code <= 0x1f)) { /* 00->0f is video_object_start_code */
+        /* if last was a VOP, or if this is first VOP, then what follows
+         * must be the next frame:
+         */
+        if (((last_start_code == 0xff) && (start_code == VOP_START_CODE)) ||
+            (last_start_code == VOP_START_CODE)) {
+          GST_DEBUG_OBJECT (self, "found end");
+          break;
+        }
+      } else if ((0x20 <= start_code) && (start_code <= 0x2f)) {
+        decode_vol_header (self, in + SC_SZ + 1, insize - SC_SZ - 1);
+      }
+    }
+
+    last_start_code = start_code;
+
+    nal_size = SC_SZ +
+        find_start_code (sc, SC_SZ, in + SC_SZ, insize - SC_SZ);
+
+    if ((start_code == VOP_START_CODE) && (nal_size < 20)) {
+      /* suspiciously small nal..  check for !vop_coded and filter
+       * that out to avoid upsetting the decoder:
+       *
+       * XXX 20 is arbitrary value, but I want to avoid having
+       * to parse every VOP.. the non-coded VOP's I'm seeing
+       * are all 7 bytes but need to come up with some sane
+       * threshold
+       */
+      skip = ! is_vop_coded (self, in + SC_SZ + 1, insize - SC_SZ - 1);
+      if (skip)
+        GST_DEBUG_OBJECT (self, "skipping non-coded VOP");
+    }
+
+    if (!skip)
+      push_input (vdec, in, nal_size);
+
+    in += nal_size;
+    insize -= nal_size;
+    size += nal_size;
+  }
+
+  /* if there are remaining bytes, wrap those back as a buffer
+   * for the next go around:
+   */
+  if (insize > 0) {
+    remaining = gst_buffer_create_sub (buf, size, insize);
+  }
+
+  return remaining;
+}
+
 /* GObject vmethod implementations */
 
+static void
+gst_ducati_mpeg4dec_finalize (GObject * obj)
+{
+  GstDucatiMpeg4Dec *self = GST_DUCATIMPEG4DEC (obj);
+  if (self->br)
+    gst_bit_reader_free (self->br);
+  G_OBJECT_CLASS (parent_class)->finalize (obj);
+}
+
 static void
 gst_ducati_mpeg4dec_base_init (gpointer gclass)
 {
@@ -122,12 +304,19 @@ gst_ducati_mpeg4dec_base_init (gpointer gclass)
 static void
 gst_ducati_mpeg4dec_class_init (GstDucatiMpeg4DecClass * klass)
 {
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
   GstDucatiVidDecClass *bclass = GST_DUCATIVIDDEC_CLASS (klass);
+
+  gobject_class->finalize =
+      GST_DEBUG_FUNCPTR (gst_ducati_mpeg4dec_finalize);
+
   bclass->codec_name = "ivahd_mpeg4dec";
   bclass->update_buffer_size =
       GST_DEBUG_FUNCPTR (gst_ducati_mpeg4dec_update_buffer_size);
   bclass->allocate_params =
       GST_DEBUG_FUNCPTR (gst_ducati_mpeg4dec_allocate_params);
+  bclass->push_input =
+      GST_DEBUG_FUNCPTR (gst_ducati_mpeg4dec_push_input);
 }
 
 static void
index f039c08a64420ba55a2f4af48f5facb0b523d2b2..1a72b55ef2bdb0041d09683313f9fa4014ac609d 100644 (file)
@@ -22,6 +22,7 @@
 
 #include "gstducatividdec.h"
 
+#include <gst/base/gstbitreader.h>
 #include <ti/sdo/codecs/mpeg4dec/impeg4vdec.h>
 
 
@@ -39,6 +40,8 @@ typedef struct _GstDucatiMpeg4DecClass GstDucatiMpeg4DecClass;
 struct _GstDucatiMpeg4Dec
 {
   GstDucatiVidDec parent;
+  GstBitReader *br;
+  gint time_increment_bits;
 };
 
 struct _GstDucatiMpeg4DecClass
index d4fe417e78e95fcc68ed091a2471bfa46521018f..a2d919f14f67920b2af724e6811b2563157db413 100644 (file)
@@ -696,6 +696,10 @@ gst_ducati_viddec_chain (GstPad * pad, GstBuffer * buf)
     return GST_FLOW_ERROR;
   }
 
+  GST_DEBUG_OBJECT (self, "chain: %" GST_TIME_FORMAT " (%d bytes)",
+      GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
+      GST_BUFFER_SIZE (buf));
+
   decode = gst_ducati_viddec_do_qos (self, buf);
   if (!decode) {
     gst_buffer_unref (buf);
@@ -771,13 +775,6 @@ have_out_buf:
   self->inArgs->numBytes = self->in_size;
   self->inBufs->descs[0].bufSize.bytes = self->in_size;
 
-  if (buf) {
-    // XXX
-    GST_WARNING_OBJECT (self, "TODO.. can't push more than one.. need loop");
-    gst_buffer_unref (buf);
-    buf = NULL;
-  }
-
   err = codec_process (self, TRUE, FALSE);
   if (err) {
     GST_ELEMENT_ERROR (self, STREAM, DECODE, (NULL),
@@ -794,6 +791,12 @@ have_out_buf:
     self->need_out_buf = TRUE;
   }
 
+  if (buf) {
+    GST_DEBUG_OBJECT (self, "found remaining data: %d bytes",
+        GST_BUFFER_SIZE (buf));
+    goto allocate_buffer;
+  }
+
   return GST_FLOW_OK;
 }
 
index 23a06a8a8ff4a7d056b350d9b60a34f758c60c55..7f83e90eea4145eb16f8227c53c221bd32b0c8de 100644 (file)
@@ -135,13 +135,46 @@ GType gst_ducati_viddec_get_type (void);
 /* helper methods for derived classes: */
 
 static inline void
-push_input (GstDucatiVidDec * self, guint8 *in, gint sz)
+push_input (GstDucatiVidDec * self, const guint8 *in, gint sz)
 {
   GST_DEBUG_OBJECT (self, "push: %d bytes)", sz);
   memcpy (self->input + self->in_size, in, sz);
   self->in_size += sz;
 }
 
+static inline int
+check_start_code (const guint8 *sc, gint scsize,
+    const guint8 *inbuf, gint insize)
+{
+  if (insize < scsize)
+    return FALSE;
+
+  while (scsize) {
+    if (*sc != *inbuf)
+      return FALSE;
+    scsize--;
+    sc++;
+    inbuf++;
+  }
+
+  return TRUE;
+}
+
+static inline int
+find_start_code (const guint8 *sc, gint scsize,
+    const guint8 *inbuf, gint insize)
+{
+  gint size = 0;
+  while (insize) {
+    if (check_start_code (sc, scsize, inbuf, insize))
+      break;
+    insize--;
+    size++;
+    inbuf++;
+  }
+  return size;
+}
+
 G_END_DECLS
 
 #endif /* __GST_DUCATIVIDDEC_H__ */