android: Make it ready for androgenizer
[platform/upstream/gst-plugins-good.git] / gst / flx / gstflxdec.c
index bb86916..627aad1 100644 (file)
  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  * Boston, MA 02111-1307, USA.
  */
+/**
+ * SECTION:element-flxdec
+ *
+ * This element decodes fli/flc/flx-video into raw video
+ */
+/*
+ * http://www.coolutils.com/Formats/FLI
+ * http://woodshole.er.usgs.gov/operations/modeling/flc.html
+ */
 
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 GST_DEBUG_CATEGORY_STATIC (flxdec_debug);
 #define GST_CAT_DEFAULT flxdec_debug
 
-/* flx element information */
-static GstElementDetails flxdec_details = {
-  "FLX Decoder",
-  "Codec/Decoder/Audio",
-  "FLX decoder",
-  "Sepp Wijnands <mrrazz@garbage-coderz.net>, Zeeshan Ali <zeenix@gmail.com>"
-};
-
-/* Flx signals and args */
-enum
-{
-  /* FILL ME */
-  LAST_SIGNAL
-};
-
-enum
-{
-  ARG_0
-};
-
 /* input */
 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
     GST_PAD_SINK,
@@ -69,27 +58,23 @@ static GstStaticPadTemplate src_video_factory = GST_STATIC_PAD_TEMPLATE ("src",
 static void gst_flxdec_class_init (GstFlxDecClass * klass);
 static void gst_flxdec_base_init (GstFlxDecClass * klass);
 static void gst_flxdec_init (GstFlxDec * flxdec);
+static void gst_flxdec_dispose (GstFlxDec * flxdec);
 
 static GstFlowReturn gst_flxdec_chain (GstPad * pad, GstBuffer * buf);
 
 static GstStateChangeReturn gst_flxdec_change_state (GstElement * element,
     GstStateChange transition);
 
+static gboolean gst_flxdec_src_query_handler (GstPad * pad, GstQuery * query);
 static gboolean gst_flxdec_src_event_handler (GstPad * pad, GstEvent * event);
 static gboolean gst_flxdec_sink_event_handler (GstPad * pad, GstEvent * event);
 
-static void gst_flxdec_set_property (GObject * object, guint prop_id,
-    const GValue * value, GParamSpec * pspec);
-static void gst_flxdec_get_property (GObject * object, guint prop_id,
-    GValue * value, GParamSpec * pspec);
-
-
 static void flx_decode_color (GstFlxDec *, guchar *, guchar *, gint);
 static void flx_decode_brun (GstFlxDec *, guchar *, guchar *);
 static void flx_decode_delta_fli (GstFlxDec *, guchar *, guchar *);
 static void flx_decode_delta_flc (GstFlxDec *, guchar *, guchar *);
 
-#define rndalign(off) ((off) + ((off) % 2))
+#define rndalign(off) ((off) + ((off) & 1))
 
 static GstElementClass *parent_class = NULL;
 
@@ -122,7 +107,10 @@ gst_flxdec_base_init (GstFlxDecClass * klass)
 {
   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
 
-  gst_element_class_set_details (gstelement_class, &flxdec_details);
+  gst_element_class_set_details_simple (gstelement_class, "FLX video decoder",
+      "Codec/Decoder/Video",
+      "FLC/FLI/FLX video decoder",
+      "Sepp Wijnands <mrrazz@garbage-coderz.net>, Zeeshan Ali <zeenix@gmail.com>");
   gst_element_class_add_pad_template (gstelement_class,
       gst_static_pad_template_get (&sink_factory));
   gst_element_class_add_pad_template (gstelement_class,
@@ -138,12 +126,11 @@ gst_flxdec_class_init (GstFlxDecClass * klass)
   gobject_class = (GObjectClass *) klass;
   gstelement_class = (GstElementClass *) klass;
 
-  parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
+  parent_class = g_type_class_peek_parent (klass);
 
-  GST_DEBUG_CATEGORY_INIT (flxdec_debug, "flxdec", 0, "FLX video decoder");
+  gobject_class->dispose = (GObjectFinalizeFunc) gst_flxdec_dispose;
 
-  gobject_class->set_property = gst_flxdec_set_property;
-  gobject_class->get_property = gst_flxdec_get_property;
+  GST_DEBUG_CATEGORY_INIT (flxdec_debug, "flxdec", 0, "FLX video decoder");
 
   gstelement_class->change_state = gst_flxdec_change_state;
 }
@@ -151,17 +138,14 @@ gst_flxdec_class_init (GstFlxDecClass * klass)
 static void
 gst_flxdec_init (GstFlxDec * flxdec)
 {
-  flxdec->sinkpad =
-      gst_pad_new_from_template (gst_static_pad_template_get (&sink_factory),
-      "sink");
+  flxdec->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink");
   gst_element_add_pad (GST_ELEMENT (flxdec), flxdec->sinkpad);
   gst_pad_set_chain_function (flxdec->sinkpad, gst_flxdec_chain);
   gst_pad_set_event_function (flxdec->sinkpad, gst_flxdec_sink_event_handler);
 
-  flxdec->srcpad =
-      gst_pad_new_from_template (gst_static_pad_template_get
-      (&src_video_factory), "src");
+  flxdec->srcpad = gst_pad_new_from_static_template (&src_video_factory, "src");
   gst_element_add_pad (GST_ELEMENT (flxdec), flxdec->srcpad);
+  gst_pad_set_query_function (flxdec->srcpad, gst_flxdec_src_query_handler);
   gst_pad_set_event_function (flxdec->srcpad, gst_flxdec_src_event_handler);
 
   gst_pad_use_fixed_caps (flxdec->srcpad);
@@ -172,36 +156,73 @@ gst_flxdec_init (GstFlxDec * flxdec)
   flxdec->adapter = gst_adapter_new ();
 }
 
+static void
+gst_flxdec_dispose (GstFlxDec * flxdec)
+{
+  if (flxdec->adapter) {
+    g_object_unref (flxdec->adapter);
+    flxdec->adapter = NULL;
+  }
+
+  G_OBJECT_CLASS (parent_class)->dispose ((GObject *) flxdec);
+}
+
 static gboolean
-gst_flxdec_src_event_handler (GstPad * pad, GstEvent * event)
+gst_flxdec_src_query_handler (GstPad * pad, GstQuery * query)
 {
   GstFlxDec *flxdec = (GstFlxDec *) gst_pad_get_parent (pad);
+  gboolean ret = FALSE;
 
-  g_return_val_if_fail (GST_IS_EVENT (event), FALSE);
+  switch (GST_QUERY_TYPE (query)) {
+    case GST_QUERY_DURATION:
+    {
+      GstFormat format;
 
-  /* TODO: implement the seek and other event handling */
+      gst_query_parse_duration (query, &format, NULL);
 
-  return gst_pad_push_event (flxdec->sinkpad, event);
+      if (format != GST_FORMAT_TIME)
+        goto done;
+
+      gst_query_set_duration (query, format, flxdec->duration);
+
+      ret = TRUE;
+    }
+    default:
+      break;
+  }
+done:
+  gst_object_unref (flxdec);
+
+  return ret;
 }
 
 static gboolean
-gst_flxdec_sink_event_handler (GstPad * pad, GstEvent * event)
+gst_flxdec_src_event_handler (GstPad * pad, GstEvent * event)
 {
   GstFlxDec *flxdec = (GstFlxDec *) gst_pad_get_parent (pad);
+  gboolean ret;
+
+  /* TODO: implement the seek and other event handling */
+
+  ret = gst_pad_push_event (flxdec->sinkpad, event);
 
-  g_return_val_if_fail (GST_IS_EVENT (event), FALSE);
+  gst_object_unref (flxdec);
 
-  if (GST_EVENT_TYPE (event) == GST_EVENT_EOS ||
-      GST_EVENT_TYPE (event) == GST_EVENT_NEWSEGMENT)
-    GST_STREAM_LOCK (flxdec->srcpad);
+  return ret;
+}
+
+static gboolean
+gst_flxdec_sink_event_handler (GstPad * pad, GstEvent * event)
+{
+  GstFlxDec *flxdec;
+  gboolean ret;
 
-  gst_pad_push_event (flxdec->srcpad, gst_event_ref (event));
+  flxdec = GST_FLXDEC (gst_pad_get_parent (pad));
 
-  if (GST_EVENT_TYPE (event) == GST_EVENT_EOS ||
-      GST_EVENT_TYPE (event) == GST_EVENT_NEWSEGMENT)
-    GST_STREAM_UNLOCK (flxdec->srcpad);
+  ret = gst_pad_push_event (flxdec->srcpad, event);
 
-  return TRUE;
+  gst_object_unref (flxdec);
+  return ret;
 }
 
 static void
@@ -214,6 +235,7 @@ flx_decode_chunks (GstFlxDec * flxdec, gulong count, guchar * data,
 
   while (count--) {
     hdr = (FlxFrameChunk *) data;
+    FLX_FRAME_CHUNK_FIX_ENDIANNESS (hdr);
     data += FlxFrameChunkSize;
 
     switch (hdr->id) {
@@ -282,7 +304,7 @@ flx_decode_color (GstFlxDec * flxdec, guchar * data, guchar * dest, gint scale)
     if (count == 0)
       count = 256;
 
-    GST_LOG ("GstFlxDec: cmap count: %d (indx: %d)\n", count, indx);
+    GST_LOG ("GstFlxDec: cmap count: %d (indx: %d)", count, indx);
     flx_set_palette_vector (flxdec->converter, indx, count, data, scale);
 
     data += (count * 3);
@@ -333,7 +355,7 @@ flx_decode_brun (GstFlxDec * flxdec, guchar * data, guchar * dest)
 static void
 flx_decode_delta_fli (GstFlxDec * flxdec, guchar * data, guchar * dest)
 {
-  gulong count, packets, lines, start_line, start_l;
+  gulong count, packets, lines, start_line;
   guchar *start_p, x;
 
   g_return_if_fail (flxdec != NULL);
@@ -350,7 +372,6 @@ flx_decode_delta_fli (GstFlxDec * flxdec, guchar * data, guchar * dest)
   /* start position of delta */
   dest += (flxdec->hdr.width * start_line);
   start_p = dest;
-  start_l = lines;
 
   while (lines--) {
     /* packet count */
@@ -469,40 +490,43 @@ gst_flxdec_chain (GstPad * pad, GstBuffer * buf)
       const guint8 *data = gst_adapter_peek (flxdec->adapter, FlxHeaderSize);
 
       memcpy ((gchar *) & flxdec->hdr, data, FlxHeaderSize);
+      FLX_HDR_FIX_ENDIANNESS (&(flxdec->hdr));
       gst_adapter_flush (flxdec->adapter, FlxHeaderSize);
 
       flxh = &flxdec->hdr;
 
       /* check header */
       if (flxh->type != FLX_MAGICHDR_FLI &&
-          flxh->type != FLX_MAGICHDR_FLC && flxh->type != FLX_MAGICHDR_FLX) {
-        GST_ELEMENT_ERROR (flxdec, STREAM, WRONG_TYPE, (NULL),
-            ("not a flx file (type %d)\n", flxh->type));
-        return GST_FLOW_ERROR;
-      }
+          flxh->type != FLX_MAGICHDR_FLC && flxh->type != FLX_MAGICHDR_FLX)
+        goto wrong_type;
 
-
-      GST_LOG ("size      :  %d\n", flxh->size);
-      GST_LOG ("frames    :  %d\n", flxh->frames);
-      GST_LOG ("width     :  %d\n", flxh->width);
-      GST_LOG ("height    :  %d\n", flxh->height);
-      GST_LOG ("depth     :  %d\n", flxh->depth);
-      GST_LOG ("speed     :  %d\n", flxh->speed);
+      GST_LOG ("size      :  %d", flxh->size);
+      GST_LOG ("frames    :  %d", flxh->frames);
+      GST_LOG ("width     :  %d", flxh->width);
+      GST_LOG ("height    :  %d", flxh->height);
+      GST_LOG ("depth     :  %d", flxh->depth);
+      GST_LOG ("speed     :  %d", flxh->speed);
 
       flxdec->next_time = 0;
 
       if (flxh->type == FLX_MAGICHDR_FLI) {
         flxdec->frame_time = JIFFIE * flxh->speed;
+      } else if (flxh->speed == 0) {
+        flxdec->frame_time = GST_SECOND / 70;
       } else {
         flxdec->frame_time = flxh->speed * GST_MSECOND;
       }
 
+      flxdec->duration = flxh->frames * flxdec->frame_time;
+      GST_LOG ("duration   :  %" GST_TIME_FORMAT,
+          GST_TIME_ARGS (flxdec->duration));
+
       caps = gst_caps_from_string (GST_VIDEO_CAPS_xRGB_HOST_ENDIAN);
       gst_caps_set_simple (caps,
           "width", G_TYPE_INT, flxh->width,
           "height", G_TYPE_INT, flxh->height,
-          "framerate", G_TYPE_DOUBLE,
-          (gdouble) (GST_SECOND / flxdec->frame_time), NULL);
+          "framerate", GST_TYPE_FRACTION, (gint) GST_MSECOND,
+          (gint) flxdec->frame_time / 1000, NULL);
 
       gst_pad_set_caps (flxdec->srcpad, caps);
       gst_caps_unref (caps);
@@ -512,10 +536,10 @@ gst_flxdec_chain (GstPad * pad, GstBuffer * buf)
             flx_colorspace_converter_new (flxh->width, flxh->height);
 
       if (flxh->type == FLX_MAGICHDR_FLC || flxh->type == FLX_MAGICHDR_FLX) {
-        GST_LOG ("(FLC) aspect_dx :  %d\n", flxh->aspect_dx);
-        GST_LOG ("(FLC) aspect_dy :  %d\n", flxh->aspect_dy);
-        GST_LOG ("(FLC) oframe1   :  0x%08x\n", flxh->oframe1);
-        GST_LOG ("(FLC) oframe2   :  0x%08x\n", flxh->oframe2);
+        GST_LOG ("(FLC) aspect_dx :  %d", flxh->aspect_dx);
+        GST_LOG ("(FLC) aspect_dy :  %d", flxh->aspect_dy);
+        GST_LOG ("(FLC) oframe1   :  0x%08x", flxh->oframe1);
+        GST_LOG ("(FLC) oframe2   :  0x%08x", flxh->oframe2);
       }
 
       flxdec->size = (flxh->width * flxh->height);
@@ -524,8 +548,10 @@ gst_flxdec_chain (GstPad * pad, GstBuffer * buf)
       flxdec->frame = gst_buffer_new ();
       flxdec->delta = gst_buffer_new ();
       GST_BUFFER_DATA (flxdec->frame) = g_malloc (flxdec->size);
+      GST_BUFFER_MALLOCDATA (flxdec->frame) = GST_BUFFER_DATA (flxdec->frame);
       GST_BUFFER_SIZE (flxdec->frame) = flxdec->size;
       GST_BUFFER_DATA (flxdec->delta) = g_malloc (flxdec->size);
+      GST_BUFFER_MALLOCDATA (flxdec->delta) = GST_BUFFER_DATA (flxdec->delta);
       GST_BUFFER_SIZE (flxdec->delta) = flxdec->size;
 
       flxdec->state = GST_FLXDEC_PLAYING;
@@ -533,34 +559,36 @@ gst_flxdec_chain (GstPad * pad, GstBuffer * buf)
   } else if (flxdec->state == GST_FLXDEC_PLAYING) {
     GstBuffer *out;
 
-    if (avail >= FlxFrameChunkSize) {
+    /* while we have enough data in the adapter */
+    while (avail >= FlxFrameChunkSize) {
       FlxFrameChunk flxfh;
-      guchar *chunk = NULL;
-      guint to_flush = 0;
-      const guint8 *data =
-          gst_adapter_peek (flxdec->adapter, FlxFrameChunkSize);
+      guchar *chunk;
+      const guint8 *data;
+
+      chunk = NULL;
+      data = gst_adapter_peek (flxdec->adapter, FlxFrameChunkSize);
       memcpy (&flxfh, data, FlxFrameChunkSize);
+      FLX_FRAME_CHUNK_FIX_ENDIANNESS (&flxfh);
 
       switch (flxfh.id) {
         case FLX_FRAME_TYPE:
-          if (avail < flxfh.size) {
-            break;
-          }
+          /* check if we have the complete frame */
+          if (avail < flxfh.size)
+            goto need_more_data;
 
+          /* flush header */
           gst_adapter_flush (flxdec->adapter, FlxFrameChunkSize);
-          data = gst_adapter_peek (flxdec->adapter,
-              flxfh.size - FlxFrameChunkSize);
-          chunk = g_memdup (data, flxfh.size - FlxFrameChunkSize);
-          to_flush = flxfh.size - FlxFrameChunkSize;
 
+          chunk = gst_adapter_take (flxdec->adapter,
+              flxfh.size - FlxFrameChunkSize);
+          FLX_FRAME_TYPE_FIX_ENDIANNESS ((FlxFrameType *) chunk);
           if (((FlxFrameType *) chunk)->chunks == 0)
             break;
 
           /* create 32 bits output frame */
-          res = gst_pad_alloc_buffer (flxdec->srcpad,
+          res = gst_pad_alloc_buffer_and_set_caps (flxdec->srcpad,
               GST_BUFFER_OFFSET_NONE,
               flxdec->size * 4, GST_PAD_CAPS (flxdec->srcpad), &out);
-
           if (res != GST_FLOW_OK)
             break;
 
@@ -584,20 +612,31 @@ gst_flxdec_chain (GstPad * pad, GstBuffer * buf)
           break;
       }
 
-      gst_adapter_flush (flxdec->adapter, to_flush);
-
       if (chunk)
         g_free (chunk);
+
+      avail = gst_adapter_available (flxdec->adapter);
     }
   }
-
+need_more_data:
+  gst_object_unref (flxdec);
   return res;
+
+  /* ERRORS */
+wrong_type:
+  {
+    GST_ELEMENT_ERROR (flxdec, STREAM, WRONG_TYPE, (NULL),
+        ("not a flx file (type %x)", flxh->type));
+    gst_object_unref (flxdec);
+    return GST_FLOW_ERROR;
+  }
 }
 
 static GstStateChangeReturn
 gst_flxdec_change_state (GstElement * element, GstStateChange transition)
 {
   GstFlxDec *flxdec;
+  GstStateChangeReturn ret;
 
   flxdec = GST_FLXDEC (element);
 
@@ -610,52 +649,31 @@ gst_flxdec_change_state (GstElement * element, GstStateChange transition)
       break;
     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
       break;
+    default:
+      break;
+  }
+
+  ret = parent_class->change_state (element, transition);
+
+  switch (transition) {
     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
       break;
     case GST_STATE_CHANGE_PAUSED_TO_READY:
-      gst_buffer_unref (flxdec->frame);
-      flxdec->frame = NULL;
-      gst_buffer_unref (flxdec->delta);
-      flxdec->delta = NULL;
+      if (flxdec->frame) {
+        gst_buffer_unref (flxdec->frame);
+        flxdec->frame = NULL;
+      }
+      if (flxdec->delta) {
+        gst_buffer_unref (flxdec->delta);
+        flxdec->delta = NULL;
+      }
       break;
     case GST_STATE_CHANGE_READY_TO_NULL:
       break;
-  }
-
-  return parent_class->change_state (element, transition);
-
-  //return GST_STATE_CHANGE_SUCCESS;
-}
-
-static void
-gst_flxdec_set_property (GObject * object, guint prop_id, const GValue * value,
-    GParamSpec * pspec)
-{
-  GstFlxDec *flxdec;
-
-  g_return_if_fail (GST_IS_FLXDEC (object));
-  flxdec = GST_FLXDEC (object);
-
-  switch (prop_id) {
-    default:
-      break;
-  }
-}
-
-static void
-gst_flxdec_get_property (GObject * object, guint prop_id, GValue * value,
-    GParamSpec * pspec)
-{
-  GstFlxDec *flxdec;
-
-  g_return_if_fail (GST_IS_FLXDEC (object));
-  flxdec = GST_FLXDEC (object);
-
-  switch (prop_id) {
     default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
   }
+  return ret;
 }
 
 static gboolean
@@ -668,5 +686,5 @@ plugin_init (GstPlugin * plugin)
 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
     GST_VERSION_MINOR,
     "flxdec",
-    "FLX video decoder",
-    plugin_init, VERSION, GST_LICENSE, GST_PACKAGE, GST_ORIGIN)
+    "FLC/FLI/FLX video decoder",
+    plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)