Index: gstavidemux.c
===================================================================
RCS file: /cvs/gstreamer/gst-plugins-good/gst/avi/gstavidemux.c,v
retrieving revision 1.188
diff -u -p -u -r1.188 gstavidemux.c
--- gstavidemux.c	28 Jul 2006 08:21:27 -0000	1.188
+++ gstavidemux.c	8 Aug 2006 11:48:20 -0000
@@ -1,5 +1,6 @@
 /* GStreamer
  * Copyright (C) <1999> Erik Walthinsen <omega@temple-baptist.com>
+ * Copyright (C) <2006> Nokia Corporation (contact <stefan.kost@nokia.com>)
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -52,6 +53,7 @@
 #include "gstavidemux.h"
 #include "avi-ids.h"
 #include <gst/gst-i18n-plugin.h>
+#include <gst/base/gstadapter.h>
 
 GST_DEBUG_CATEGORY_STATIC (avidemux_debug);
 #define GST_CAT_DEFAULT avidemux_debug
@@ -67,6 +69,7 @@ static GstStaticPadTemplate sink_templ =
 static void gst_avi_demux_base_init (GstAviDemuxClass * klass);
 static void gst_avi_demux_class_init (GstAviDemuxClass * klass);
 static void gst_avi_demux_init (GstAviDemux * avi);
+static void gst_avi_demux_dispose (GObject * object);
 
 static void gst_avi_demux_reset (GstAviDemux * avi);
 
@@ -88,6 +91,9 @@ static void gst_avi_demux_loop (GstPad *
 static gboolean gst_avi_demux_sink_activate (GstPad * sinkpad);
 static gboolean gst_avi_demux_sink_activate_pull (GstPad * sinkpad,
     gboolean active);
+static gboolean gst_avi_demux_activate_push(GstPad  *pad, gboolean active);
+static GstFlowReturn gst_avi_demux_chain(GstPad *pad, GstBuffer *buf);
+
 static GstStateChangeReturn gst_avi_demux_change_state (GstElement * element,
     GstStateChange transition);
 
@@ -155,12 +161,14 @@ static void
 gst_avi_demux_class_init (GstAviDemuxClass * klass)
 {
   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
+  GObjectClass *gobject_class = (GObjectClass *) klass;
 
   GST_DEBUG_CATEGORY_INIT (avidemux_debug, "avidemux",
       0, "Demuxer for AVI streams");
 
   parent_class = g_type_class_peek_parent (klass);
 
+  gobject_class->dispose = gst_avi_demux_dispose;
   gstelement_class->change_state = gst_avi_demux_change_state;
 }
 
@@ -171,7 +179,10 @@ gst_avi_demux_init (GstAviDemux * avi)
   gst_pad_set_activate_function (avi->sinkpad, gst_avi_demux_sink_activate);
   gst_pad_set_activatepull_function (avi->sinkpad,
       gst_avi_demux_sink_activate_pull);
+  gst_pad_set_activatepush_function(avi->sinkpad,
+      gst_avi_demux_activate_push);
   gst_element_add_pad (GST_ELEMENT (avi), avi->sinkpad);
+  gst_pad_set_chain_function(avi->sinkpad, gst_avi_demux_chain);
 
   gst_avi_demux_reset (avi);
 
@@ -180,6 +191,21 @@ gst_avi_demux_init (GstAviDemux * avi)
 }
 
 static void
+gst_avi_demux_dispose (GObject * object)
+{
+  GST_DEBUG("AVI: Dispose\n");
+  GstAviDemux *avi = GST_AVI_DEMUX(object);
+
+  if ( avi->adapter ) {
+    g_object_unref(avi->adapter);
+    avi->adapter = NULL;
+  }
+
+  G_OBJECT_CLASS (parent_class)->dispose(object);
+}
+
+
+static void
 gst_avi_demux_reset (GstAviDemux * avi)
 {
   gint i;
@@ -226,6 +252,10 @@ gst_avi_demux_reset (GstAviDemux * avi)
   avi->globaltags = NULL;
 
   avi->got_tags = FALSE;
+  avi->have_eos = FALSE;
+#ifdef MEMOPT
+  avi->local_without_index = FALSE;
+#endif
 
   gst_segment_init (&avi->segment, GST_FORMAT_TIME);
 }
@@ -237,10 +267,8 @@ gst_avi_demux_index_next (GstAviDemux * 
   gst_avi_index_entry *result = NULL;
 
   for (i = start; i < avi->index_size; i++) {
-    gst_avi_index_entry *entry = &avi->index_entries[i];
-
-    if (entry->stream_nr == stream_nr) {
-      result = entry;
+    if (avi->index_entries[i].stream_nr == stream_nr) {
+      result = &avi->index_entries[i];
       break;
     }
   }
@@ -261,7 +289,7 @@ gst_avi_demux_index_entry_for_time (GstA
   do {
     entry = gst_avi_demux_index_next (avi, stream_nr, i + 1);
     if (!entry)
-      return NULL;
+      return last_entry;
 
     i = entry->index_nr;
 
@@ -269,7 +297,9 @@ gst_avi_demux_index_entry_for_time (GstA
         "looking at entry %d / ts:%" GST_TIME_FORMAT " / dur:%" GST_TIME_FORMAT
         " flags:%d", i, GST_TIME_ARGS (entry->ts), GST_TIME_ARGS (entry->dur),
         entry->flags);
-    if (entry->ts <= time && (entry->flags & flags) == flags)
+    if ( entry->ts <= time &&
+         (entry->flags & flags) == flags &&
+         stream_nr == entry->stream_nr)
       last_entry = entry;
   } while (entry->ts < time);
 
@@ -310,8 +340,9 @@ gst_avi_demux_src_convert (GstPad * pad,
   avi_stream_context *stream = gst_pad_get_element_private (pad);
 
   GST_LOG_OBJECT (avidemux,
-      "Received  src_format:%d, src_value:%lld, dest_format:%d", src_format,
-      src_value, *dest_format);
+      "Received  src_format:%s, src_value:%lld, dest_format:%s", 
+      gst_format_get_name (src_format), src_value,
+      gst_format_get_name (*dest_format));
 
   if (src_format == *dest_format) {
     *dest_value = src_value;
@@ -375,8 +406,8 @@ gst_avi_demux_src_convert (GstPad * pad,
   }
 
 done:
-  GST_LOG_OBJECT (avidemux, "Returning res:%d dest_format:%d dest_value:%lld",
-      res, *dest_format, *dest_value);
+  GST_LOG_OBJECT (avidemux, "Returning res:%d dest_format:%s dest_value:%lld",
+      res, gst_format_get_name (*dest_format), *dest_value);
   gst_object_unref (avidemux);
   return res;
 }
@@ -843,6 +874,8 @@ gst_avi_demux_parse_subindex (GstElement
   GList *entries_list = NULL;
   GstFormat format = GST_FORMAT_TIME;
 
+  *_entries_list = NULL;
+
   /* check size */
   if (buf == NULL)
     goto no_buffer;
@@ -918,7 +951,6 @@ gst_avi_demux_parse_subindex (GstElement
   if (x > 0) {
     *_entries_list = g_list_reverse (entries_list);
   } else {
-    *_entries_list = NULL;
     g_free (entries);
   }
 
@@ -936,7 +968,6 @@ too_small:
         "Not enough data to parse subindex (%d available, %d needed)",
         GST_BUFFER_SIZE (buf), 24);
     gst_buffer_unref (buf);
-    *_entries_list = NULL;
     return TRUE;                /* continue */
   }
 not_implemented:
@@ -952,7 +983,7 @@ static void
 gst_avi_demux_read_subindexes (GstAviDemux * avi,
     GList ** index, GList ** alloc_list)
 {
-  GList *list;
+  GList *list=NULL;
   guint32 tag;
   GstBuffer *buf;
   gint i, n;
@@ -968,8 +999,7 @@ gst_avi_demux_read_subindexes (GstAviDem
         continue;
       else if (tag != GST_MAKE_FOURCC ('i', 'x', '0' + stream->num / 10,
               '0' + stream->num % 10)) {
-        GST_WARNING_OBJECT (GST_ELEMENT (avi),
-            "Not an ix## chunk (%" GST_FOURCC_FORMAT ")",
+        GST_WARNING_OBJECT (avi, "Not an ix## chunk (%" GST_FOURCC_FORMAT ")",
             GST_FOURCC_ARGS (tag));
         gst_buffer_unref (buf);
         continue;
@@ -978,6 +1008,7 @@ gst_avi_demux_read_subindexes (GstAviDem
       if (!gst_avi_demux_parse_subindex (GST_ELEMENT (avi), buf, stream, &list))
         continue;
       if (list) {
+        GST_DEBUG_OBJECT (avi, "  adding %d entries",g_list_length(list));
         *alloc_list = g_list_append (*alloc_list, list->data);
         *index = g_list_concat (*index, list);
       }
@@ -986,6 +1017,7 @@ gst_avi_demux_read_subindexes (GstAviDem
     g_free (stream->indexes);
     stream->indexes = NULL;
   }
+  GST_DEBUG_OBJECT (avi, "index %s", ((*index) ? "!= 0" : "== 0"));
 }
 
 /**
@@ -1181,10 +1213,14 @@ gst_avi_demux_parse_stream (GstElement *
   gst_pad_use_fixed_caps (pad);
 #if 0
   gst_pad_set_formats_function (pad, gst_avi_demux_get_src_formats);
+  gst_pad_set_event_mask_function (pad, gst_avi_demux_get_event_mask);
 #endif
   gst_pad_set_event_function (pad, gst_avi_demux_handle_src_event);
   gst_pad_set_query_type_function (pad, gst_avi_demux_get_src_query_types);
   gst_pad_set_query_function (pad, gst_avi_demux_handle_src_query);
+#if 0
+  gst_pad_set_convert_function (pad, gst_avi_demux_src_convert);
+#endif
 
   stream->num = avi->num_streams;
   stream->total_bytes = 0;
@@ -1300,6 +1336,17 @@ gst_avi_demux_parse_odml (GstElement * e
     gst_buffer_unref (buf);
 }
 
+static gint
+sort (gst_avi_index_entry * a, gst_avi_index_entry * b)
+{
+  if (a->ts > b->ts)
+    return 1;
+  else if (a->ts < b->ts)
+    return -1;
+  else
+    return a->stream_nr - b->stream_nr;
+}
+
 /**
  * gst_avi_demux_parse_index:
  * @element: calling element (used for debugging/errors).
@@ -1312,6 +1359,302 @@ gst_avi_demux_parse_odml (GstElement * e
  * Read index entries from the provided buffer.
  */
 
+#ifdef MEMOPT
+static void
+gst_avi_demux_parse_index (GstElement * element,
+    GstBuffer * buf, GList ** _entries_list)
+{
+  GstAviDemux *avi = GST_AVI_DEMUX (element);
+  guint64 pos_before = avi->offset;
+  guint8 *data;
+  guint i, num, first = 0;
+  gst_riff_index_entry entry, *_entry = NULL;
+  avi_stream_context *stream;
+  gint stream_nr;
+  gst_avi_index_entry t,*target=&t;
+  gst_avi_index_entry *last_t=NULL;
+  GstFormat format;
+  gint64 next_ts;
+  gint video_key_frames = 0;
+  GList *entries_list = NULL;
+
+  if (!buf) {
+    *_entries_list = NULL;
+    return;
+  }
+
+  /* Instead of parsing all the index entries, just parse video keyframes
+  and corresponding audio frame and put those to list */
+
+  data = GST_BUFFER_DATA (buf);
+  num = GST_BUFFER_SIZE (buf) / sizeof (gst_riff_index_entry);
+
+  GST_DEBUG("Parsing index (optimized), %d entries\n\n",num);
+
+  for (i = 0; i < num; i++) {
+    _entry = &((gst_riff_index_entry *) data)[i];
+    entry.id = GUINT32_FROM_LE (_entry->id);
+    entry.offset = GUINT32_FROM_LE (_entry->offset);
+    entry.flags = GUINT32_FROM_LE (_entry->flags);
+    entry.size = GUINT32_FROM_LE (_entry->size);
+    /* the entry we just inspect and that not yet has been added */
+    target=&t;
+
+    if (entry.id == GST_RIFF_rec || entry.id == 0 ||
+        (entry.offset == 0 && first > 0))
+      continue;
+
+    stream_nr = CHUNKID_TO_STREAMNR (entry.id);
+    if (stream_nr >= avi->num_streams || stream_nr < 0) {
+      GST_WARNING_OBJECT (element,
+          "Index entry %d has invalid stream nr %d", i, stream_nr);
+      continue;
+    }
+    target->stream_nr = stream_nr;
+    stream = &avi->stream[stream_nr];
+
+    target->index_nr = i;
+    target->flags = entry.flags;
+    target->size = entry.size;
+    /* FIXME: which one is correct ? */
+    //target->offset = entry.offset + avi->index_offset;
+    target->offset = entry.offset + 8;
+
+    /* figure out if the index is 0 based or relative to the MOVI start */
+    if (first == 0) {
+      avi->current_stream = stream_nr;
+      if (target->offset < pos_before)
+        avi->index_offset = pos_before + 8;
+      else
+        avi->index_offset = 0;
+    }
+
+    /* only video */
+    if (stream->strh->type == GST_RIFF_FCC_vids) {
+      format = GST_FORMAT_TIME;
+
+      /* timestamps */
+      target->ts = stream->total_time;
+      if (stream->is_vbr) {
+        /* VBR stream next timestamp */
+        gst_avi_demux_src_convert (stream->pad, GST_FORMAT_DEFAULT,
+            stream->total_frames + 1, &format, &next_ts);
+      } else {
+        /* constant rate stream */
+        gst_avi_demux_src_convert (stream->pad, GST_FORMAT_BYTES,
+            stream->total_bytes + target->size, &format, &next_ts);
+      }
+      /* duration is next - current */
+      target->dur = next_ts - target->ts;
+
+      /* Pick video keyframes first */
+      if ( target->flags & GST_RIFF_IF_KEYFRAME ) {
+        /* stream position */
+        target->bytes_before = stream->total_bytes;
+        target->frames_before = stream->total_frames;
+
+        if ( video_key_frames == 0 ) {
+          stream->current_pos = entry.offset + avi->index_offset;
+          //target->offset = entry.offset + avi->index_offset;
+          GST_DEBUG("Start pos for video: current_pos = %" G_GUINT64_FORMAT, stream->current_pos);
+        }
+
+        /* add to list */
+        target = g_malloc(sizeof (gst_avi_index_entry));
+        memcpy(target, &t, sizeof (gst_avi_index_entry));
+        entries_list = g_list_prepend (entries_list, target);
+        video_key_frames++;
+        /* keep to fixup entry size + duration */
+        last_t = target;
+
+        GST_DEBUG_OBJECT (avi,
+          "Adding video index entry %d (%d), flags %08x, stream %d, size %u "
+          ", offset %" G_GUINT64_FORMAT ", time %" GST_TIME_FORMAT ", dur %"
+          GST_TIME_FORMAT,
+          target->index_nr, stream->total_frames - 1, target->flags,
+          target->stream_nr, target->size, target->offset,
+          GST_TIME_ARGS (target->ts), GST_TIME_ARGS (target->dur));
+      }
+      else if(last_t) {
+        /* join with last entry */
+        last_t->size += target->size;
+        last_t->dur += target->dur;
+        //last_t->dur = next_ts - last_t->ts;
+        GST_DEBUG (" expanded video entry %d by %u to %u, dur %" GST_TIME_FORMAT,
+            last_t->index_nr, entry.size, last_t->size, GST_TIME_ARGS (last_t->dur));
+      }
+      stream->total_bytes += target->size;
+      stream->total_time = next_ts;
+      stream->total_frames++;
+
+      GST_DEBUG("video  total_bytes = %" G_GUINT64_FORMAT ", total_time = %" GST_TIME_FORMAT,
+        stream->total_bytes, GST_TIME_ARGS (stream->total_time));
+    }
+    first++;
+  }
+
+  entries_list = g_list_sort( entries_list, (GCompareFunc) sort);
+  
+  GST_DEBUG("Got %d video keyframes out of %d index entries",video_key_frames,num);
+
+  if ( avi->num_a_streams == 0 ) {
+    GST_DEBUG("Got video keyframes, no audio streams, index done");
+    goto end;
+  }
+
+  /* Second round. Find closest audio frames for videokeyframes */
+  GST_DEBUG("Trying to find closest audio frames for video keyframes\n\n");
+  if(video_key_frames) {
+    guint current_frame = 0;
+    GList *l = g_list_nth(entries_list, current_frame);
+    gst_avi_index_entry *idx = (gst_avi_index_entry*) l->data;
+    guint64 video_ts = idx->ts;
+    guint64 video_dur = idx->dur;
+    gint audio_key_frames = 0;
+    gboolean done = FALSE;
+
+    last_t=NULL;
+    for (i = 0; i < num; i++) {
+      _entry = &((gst_riff_index_entry *) data)[i];
+      entry.id = GUINT32_FROM_LE (_entry->id);
+      entry.offset = GUINT32_FROM_LE (_entry->offset);
+      entry.flags = GUINT32_FROM_LE (_entry->flags);
+      entry.size = GUINT32_FROM_LE (_entry->size);
+      /* the entry we just inspect and that not yet has been added */
+      target=&t;
+
+      if (entry.id == GST_RIFF_rec || entry.id == 0 ||
+          (entry.offset == 0 && i > 0))
+        continue;
+
+      stream_nr = CHUNKID_TO_STREAMNR (entry.id);
+      if (stream_nr >= avi->num_streams || stream_nr < 0) {
+        GST_WARNING ("Index entry %d has invalid stream nr %d", i, stream_nr);
+        target->stream_nr = -1;
+        continue;
+      }
+      target->stream_nr = stream_nr;
+      stream = &avi->stream[stream_nr];
+
+      target->index_nr = i;
+      target->flags = entry.flags;
+      target->size = entry.size;
+      /* FIXME: which one is correct ? */
+      //target->offset = entry.offset + avi->index_offset;
+      target->offset = entry.offset + 8;
+
+      /* only audio */
+      if ( stream->strh->type == GST_RIFF_FCC_auds ) {
+        /* all audio frames are keyframes */
+        target->flags |= GST_RIFF_IF_KEYFRAME;
+        
+        /* timestamps */
+        target->ts = stream->total_time;
+        if (stream->is_vbr) {
+          /* VBR stream next timestamp */
+          gst_avi_demux_src_convert (stream->pad, GST_FORMAT_DEFAULT,
+              stream->total_frames + 1, &format, &next_ts);
+        } else {
+          /* constant rate stream */
+          gst_avi_demux_src_convert (stream->pad, GST_FORMAT_BYTES,
+              stream->total_bytes + target->size, &format, &next_ts);
+        }
+        /* duration is next - current */
+        target->dur = next_ts - target->ts;
+
+        GST_DEBUG_OBJECT (avi,
+          "comparing %d audio %" GST_TIME_FORMAT " >= video %" GST_TIME_FORMAT " ?",
+          i, GST_TIME_ARGS (target->ts), GST_TIME_ARGS (video_ts));
+
+        // We have corresponding audio ts
+        if (target->ts >= video_ts) {
+          /* stream position */
+          target->bytes_before = stream->total_bytes;
+          target->frames_before = stream->total_frames;
+
+          /* add to list */
+          target = g_malloc(sizeof (gst_avi_index_entry));
+          memcpy(target, &t, sizeof (gst_avi_index_entry));
+          entries_list = g_list_insert (entries_list, target, current_frame + 1);
+          audio_key_frames++;
+          /* keep to fixup entry size + duration */
+          last_t = target;
+
+          GST_DEBUG_OBJECT (avi,
+              "Adding audio index entry %d (%d), flags %08x, stream %d, size %u "
+              ", offset %" G_GUINT64_FORMAT ", time %" GST_TIME_FORMAT ", dur %"
+              GST_TIME_FORMAT,
+              target->index_nr, stream->total_frames - 1, target->flags,
+              target->stream_nr, target->size, target->offset,
+              GST_TIME_ARGS (target->ts), GST_TIME_ARGS (target->dur));
+
+          if ( current_frame == 0 ) {
+            stream->current_pos = entry.offset + avi->index_offset;
+            GST_DEBUG("Start pos for audio: current_pos = %" G_GUINT64_FORMAT, stream->current_pos);
+          }
+
+          /* Jump over freshly added audio index */
+          current_frame += 2;
+          /* Do not abort if we are beyond (video_key_frames + audio_key_frames)
+             as this would truncate the audio-index length
+          if ( current_frame >= video_key_frames * 2)
+            break;
+          */
+          if ( current_frame < video_key_frames * 2) {
+            l = g_list_nth(entries_list, current_frame);
+            idx = (gst_avi_index_entry*) l->data;
+            video_ts = idx->ts;
+            video_dur = idx->dur;
+            GST_DEBUG (" next video key: stream-nr %d  ts %" GST_TIME_FORMAT " + dur %" GST_TIME_FORMAT,
+               idx->stream_nr, GST_TIME_ARGS (video_ts), GST_TIME_ARGS (video_dur));
+          }
+          else {
+            if(!done) {
+              /* just iterate untile we match end of video stream */
+              GST_DEBUG (" set end-ts: ts %" GST_TIME_FORMAT " + dur %" GST_TIME_FORMAT " = %" GST_TIME_FORMAT,
+                GST_TIME_ARGS (video_ts), GST_TIME_ARGS (video_dur), GST_TIME_ARGS (video_ts+video_dur));
+              video_ts += video_dur;
+              done = TRUE;
+            }
+            else {
+              GST_DEBUG (" dropping remaining entries, >%d",i);
+              break;
+            }
+          }
+        }
+        else if(last_t) {
+          /* join with last entry */
+          last_t->size += target->size;
+          last_t->dur += target->dur;
+          //last_t->dur = next_ts - last_t->ts;
+          GST_DEBUG (" expanded audio entry %d by %u to %u, dur %" GST_TIME_FORMAT,
+            last_t->index_nr, entry.size, last_t->size, GST_TIME_ARGS (last_t->dur));
+          //GST_DEBUG (" flags: %lx %lx", last_t->flags, target->flags);
+        }
+        stream->total_bytes += target->size;
+        stream->total_time = next_ts;
+        stream->total_frames++;
+        
+        GST_DEBUG("audio  current_frame = %ld, total_bytes = %" G_GUINT64_FORMAT ", total_time = %" GST_TIME_FORMAT,
+          current_frame, stream->total_bytes, GST_TIME_ARGS (stream->total_time));
+      }
+    }
+    GST_DEBUG("Got %d audio keyframes out of %d index entries\n\n",audio_key_frames,num);
+  }
+
+end:
+  *_entries_list = entries_list;
+  gst_buffer_unref (buf);
+
+  GST_DEBUG("Parsing done\n");
+  GST_DEBUG("Whole-index mem usage: %d bytes\n",
+    num * sizeof(gst_avi_index_entry));
+  GST_DEBUG("Keyframe only index mem usage: %d bytes\n",
+    g_list_length(entries_list) * sizeof(gst_avi_index_entry));
+}
+
+#else
+
 static void
 gst_avi_demux_parse_index (GstElement * element,
     GstBuffer * buf, GList ** _entries_list)
@@ -1332,6 +1675,8 @@ gst_avi_demux_parse_index (GstElement * 
   num = GST_BUFFER_SIZE (buf) / sizeof (gst_riff_index_entry);
   entries = g_new (gst_avi_index_entry, num);
 
+  GST_DEBUG("Parsing index, %d entries",num);
+
   for (i = 0, n = 0; i < num; i++) {
     gst_riff_index_entry entry, *_entry;
     avi_stream_context *stream;
@@ -1417,6 +1762,8 @@ gst_avi_demux_parse_index (GstElement * 
   gst_buffer_unref (buf);
 }
 
+#endif
+
 /**
  * gst_avi_demux_stream_index:
  * @avi: avi demuxer object.
@@ -1435,6 +1782,8 @@ gst_avi_demux_stream_index (GstAviDemux 
   guint32 tag;
   gint i;
 
+  GST_DEBUG("Demux stream index");
+
   *alloc_list = NULL;
   *index = NULL;
 
@@ -1641,10 +1990,15 @@ gst_avi_demux_peek_tag (GstAviDemux * av
   GstFlowReturn res = GST_FLOW_OK;
   GstBuffer *buf = NULL;
 
-  if ((res = gst_pad_pull_range (avi->sinkpad, offset, 8, &buf)) != GST_FLOW_OK)
+  if ((res = gst_pad_pull_range (avi->sinkpad, offset, 8, &buf)) != GST_FLOW_OK) {
+    GST_DEBUG_OBJECT (avi, "pull_ranged returned %s", gst_flow_get_name (res));
     goto beach;
-  if (GST_BUFFER_SIZE (buf) != 8)
+  }
+
+  if (!buf || GST_BUFFER_SIZE (buf) != 8) {
+    GST_DEBUG_OBJECT (avi, "got %d bytes which is <> 8 bytes",(buf ? GST_BUFFER_SIZE (buf) : 0));
     return GST_FLOW_ERROR;
+  }
   *tag = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf));
   *size = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf) + 4);
   gst_buffer_unref (buf);
@@ -1694,6 +2048,8 @@ gst_avi_demux_next_data_buffer (GstAviDe
  * pull-range based
  */
 
+#ifndef MEMOPT
+
 static gboolean
 gst_avi_demux_stream_scan (GstAviDemux * avi,
     GList ** index, GList ** alloc_list)
@@ -1874,6 +2230,9 @@ gst_avi_demux_stream_scan (GstAviDemux *
   return TRUE;
 }
 
+#endif /* MEMOPT */
+
+
 /*
  * Massage index.
  * We're going to go over each entry in the index and finetune
@@ -1884,17 +2243,6 @@ gst_avi_demux_stream_scan (GstAviDemux *
  * order. The end result should be a smoother playing AVI.
  */
 
-static gint
-sort (gst_avi_index_entry * a, gst_avi_index_entry * b)
-{
-  if (a->ts > b->ts)
-    return 1;
-  else if (a->ts < b->ts)
-    return -1;
-  else
-    return a->stream_nr - b->stream_nr;
-}
-
 static void
 gst_avi_demux_massage_index (GstAviDemux * avi,
     GList * list, GList * alloc_list)
@@ -1938,6 +2286,8 @@ gst_avi_demux_massage_index (GstAviDemux
     }
   }
 
+#ifndef MEMOPT
+
   GST_LOG_OBJECT (avi, "I'm now going to cut large chunks into smaller pieces");
 
   /* cut chunks in small (seekable) pieces */
@@ -2022,6 +2372,8 @@ gst_avi_demux_massage_index (GstAviDemux
   /* re-order for time */
   list = g_list_sort (list, (GCompareFunc) sort);
 
+#endif /* #ifndef MEMOPT */
+
   GST_LOG_OBJECT (avi, "Filling in index array");
 
   avi->index_size = g_list_length (list);
@@ -2030,10 +2382,11 @@ gst_avi_demux_massage_index (GstAviDemux
     entry = one->data;
     memcpy (&avi->index_entries[i], entry, sizeof (gst_avi_index_entry));
     avi->index_entries[i].index_nr = i;
-    GST_DEBUG ("Adding index entry %d for stream %d of size %u "
-        "at offset %" G_GUINT64_FORMAT " and time %" GST_TIME_FORMAT,
-        avi->index_entries[i].index_nr, entry->stream_nr, entry->size,
-        entry->offset, GST_TIME_ARGS (entry->ts));
+    GST_DEBUG("Adding index entry %3d for stream %d of size %6u"
+      " at offset %7" G_GUINT64_FORMAT ", time %" GST_TIME_FORMAT
+      " dur %" GST_TIME_FORMAT,
+      avi->index_entries[i].index_nr, entry->stream_nr, entry->size,
+      entry->offset, GST_TIME_ARGS (entry->ts), GST_TIME_ARGS (entry->dur));
   }
 
   GST_LOG_OBJECT (avi, "Freeing original index list");
@@ -2043,7 +2396,7 @@ gst_avi_demux_massage_index (GstAviDemux
   g_list_free (list);
 
   for (i = 0; i < avi->num_streams; i++) {
-    GST_LOG_OBJECT (avi, "Stream %d, %d frames, %" G_GUINT64_FORMAT " bytes, %"
+    GST_LOG_OBJECT (avi, "Stream %d, %d frames, %8" G_GUINT64_FORMAT " bytes, %"
         GST_TIME_FORMAT " time", i,
         avi->stream[i].total_frames, avi->stream[i].total_bytes,
         GST_TIME_ARGS (avi->stream[i].total_time));
@@ -2092,6 +2445,310 @@ gst_avi_demux_push_event (GstAviDemux * 
   return result;
 }
 
+
+/*
+ * Read AVI file tag when streaming
+ */
+static GstFlowReturn
+gst_avi_demux_parse_stream_init(GstAviDemux * avi) //, GstBuffer * buf)
+{
+  if ( gst_adapter_available(avi->adapter) >= 12 ) {
+    GstBuffer *tmp = gst_buffer_new();
+    /* _take flushes the data */
+    GST_BUFFER_DATA(tmp) = gst_adapter_take(avi->adapter, 12);
+    GST_BUFFER_SIZE(tmp) = 12;
+
+    GST_DEBUG("Parsing avi header\n");
+    if(!gst_avi_demux_parse_file_header (GST_ELEMENT (avi), tmp)) {
+      return GST_FLOW_ERROR;
+    }
+    GST_DEBUG("header ok\n");
+    avi->offset += 12;
+    /* Go to next state */
+    avi->state = GST_AVI_DEMUX_HEADER;
+  }
+  return GST_FLOW_OK;
+}
+
+
+/**
+ *
+ * Peek next chunk info (tag and size)
+ *
+ * @avi Avi object
+ * @tag holder for tag
+ * @size holder for tag size
+ *
+ * returns: TRUE when one chunk info has been got
+ */
+static gboolean
+gst_avi_demux_peek_chunk_info( GstAviDemux * avi, guint32 * tag, guint32 * size )
+{
+  const guint8 * data = NULL;
+
+  if ( gst_adapter_available(avi->adapter) < 8 ) {
+    return FALSE;
+  }
+
+  data = gst_adapter_peek(avi->adapter, 8);
+  *tag = GST_READ_UINT32_LE (data);
+  *size = GST_READ_UINT32_LE (data + 4);
+
+  return TRUE;
+}
+
+
+/**
+ *
+ * Peek enough data for one full chunk
+ *
+ * @avi Avi object
+ * @tag holder for tag
+ * @size holder for tag size
+ *
+ * returns: TRUE when one chunk has been got
+ */
+static gboolean
+gst_avi_demux_peek_chunk( GstAviDemux * avi, guint32 * tag, guint32 * size )
+{
+  const guint8 * data = NULL;
+  guint32 peek_size = 0;
+
+  if ( gst_adapter_available(avi->adapter) < 8 ) {
+    return FALSE;
+  }
+
+  data = gst_adapter_peek(avi->adapter, 8);
+  *tag = GST_READ_UINT32_LE (data);
+  *size = GST_READ_UINT32_LE (data + 4);
+  GST_DEBUG("Need to peek chunk of %d bytes\n", *size);
+  peek_size = (*size + 1) & ~1;
+
+  if ( gst_adapter_available(avi->adapter) >= (8 + peek_size) ) {
+    return TRUE;
+  } else {
+    return FALSE;
+  }
+}
+
+
+/*
+ * Read AVI headers when streaming
+ */
+
+static GstFlowReturn
+gst_avi_demux_parse_stream_header (GstAviDemux * avi) //, GstBuffer * buf)
+{
+  GstFlowReturn ret = GST_FLOW_OK;
+  guint32 tag = 0;
+  guint32 ltag = 0;
+  guint32 size = 0;
+  const guint8 * data;
+  GstBuffer *buf = NULL;
+  GstBuffer *sub = NULL;
+  guint offset = 4;
+
+  GST_DEBUG("Reading and parsing avi headers\n");
+  switch (avi->header_state) {
+    case GST_AVI_DEMUX_HEADER_TAG_LIST:
+      if ( gst_avi_demux_peek_chunk(avi, &tag, &size) ) {
+        avi->offset += 8 + ((size + 1) & ~1);
+        if (tag != GST_RIFF_TAG_LIST) {
+          GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
+                             ("Invalid AVI header (no LIST at start): %"
+                                 GST_FOURCC_FORMAT, GST_FOURCC_ARGS (tag)));
+          return GST_FLOW_ERROR;
+        }
+        gst_adapter_flush(avi->adapter, 8);
+        GST_DEBUG("Reading %d bytes\n", size);
+        buf = gst_buffer_new();
+        GST_BUFFER_DATA(buf) = gst_adapter_take(avi->adapter, size);
+        GST_BUFFER_SIZE(buf) = size;
+        if ( GST_READ_UINT32_LE (GST_BUFFER_DATA (buf)) != GST_RIFF_LIST_hdrl ) {
+          GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
+                             ("Invalid AVI header (no hdrl at start): %"
+                                 GST_FOURCC_FORMAT, GST_FOURCC_ARGS (tag)));
+          gst_buffer_unref (buf);
+          return GST_FLOW_ERROR;
+        }
+        GST_DEBUG("hdrl tag found. Parsing next chunk\n");
+
+        if (!gst_riff_parse_chunk (GST_ELEMENT (avi), buf, &offset, &tag, &sub)
+            || tag != GST_RIFF_TAG_avih) {
+          GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
+                             ("Invalid AVI header (no avih at start): %"
+                                 GST_FOURCC_FORMAT, GST_FOURCC_ARGS (tag)));
+          if (sub) {
+            gst_buffer_unref (sub);
+            sub = NULL;
+          }
+          gst_buffer_unref (buf);
+          return GST_FLOW_ERROR;
+        } else if (!gst_avi_demux_parse_avih (GST_ELEMENT (avi), sub, &avi->avih)) {
+          gst_buffer_unref (buf);
+          return GST_FLOW_ERROR;
+        }
+
+        GST_DEBUG("AVI header ok, parsing elements next\n");
+
+        /* now, read the elements from the header until the end */
+        while (gst_riff_parse_chunk (GST_ELEMENT (avi), buf, &offset, &tag, &sub)) {
+          switch (tag) {
+            case GST_RIFF_TAG_LIST:
+              if (!sub || GST_BUFFER_SIZE (sub) < 4) {
+                if (sub) {
+                  gst_buffer_unref (sub);
+                  sub = NULL;
+                }
+                break;
+              }
+
+              switch (GST_READ_UINT32_LE (GST_BUFFER_DATA (sub))) {
+                case GST_RIFF_LIST_strl:
+                  if (!(gst_avi_demux_parse_stream (GST_ELEMENT (avi), sub)))
+                    return GST_FLOW_ERROR;
+                  gst_buffer_unref (sub);
+                  break;
+                case GST_RIFF_LIST_odml:
+                  gst_avi_demux_parse_odml (GST_ELEMENT (avi), sub);
+                  break;
+                default:
+                  GST_WARNING_OBJECT (avi,
+                      "Unknown list %" GST_FOURCC_FORMAT " in AVI header",
+                      GST_FOURCC_ARGS (GST_READ_UINT32_LE (GST_BUFFER_DATA (sub))));
+                  /* fall-through */
+                case GST_RIFF_TAG_JUNK:
+                  if (sub) {
+                    gst_buffer_unref (sub);
+                    sub = NULL;
+                  }
+                  break;
+              }
+              break;
+            default:
+              GST_WARNING_OBJECT (avi,
+                  "Unknown off %d tag %" GST_FOURCC_FORMAT " in AVI header",
+                  offset, GST_FOURCC_ARGS (tag));
+              /* fall-through */
+            case GST_RIFF_TAG_JUNK:
+              if (sub) {
+                gst_buffer_unref (sub);
+                sub = NULL;
+              }
+              break;
+          }
+        }
+        gst_buffer_unref (buf);
+        GST_DEBUG("Elements parsed\n");
+        /* check parsed streams */
+        if (avi->num_streams == 0) {
+          GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), ("No streams found"));
+          return GST_FLOW_ERROR;
+        } else if (avi->num_streams != avi->avih->streams) {
+          GST_WARNING_OBJECT (avi,
+                              "Stream header mentioned %d streams, but %d available",
+                              avi->avih->streams, avi->num_streams);
+        }
+        GST_DEBUG("Get junk and info next\n");
+        avi->header_state = GST_AVI_DEMUX_HEADER_INFO;
+      } else {
+        /* Need more data */
+        return ret;
+      }
+      /* fall-though */
+    case GST_AVI_DEMUX_HEADER_INFO:
+
+iterate:
+      if ( gst_adapter_available(avi->adapter) < 12 ) {
+        return GST_FLOW_OK;
+      }
+
+      data = gst_adapter_peek(avi->adapter, 12);
+      tag = GST_READ_UINT32_LE (data);
+      size = GST_READ_UINT32_LE (data + 4);
+      ltag = GST_READ_UINT32_LE (data + 8);
+
+      if (tag == GST_RIFF_TAG_LIST) {
+        switch (ltag) {
+          case GST_RIFF_LIST_movi:
+            /* done */
+            GST_DEBUG("Found movi chunk. Starting to stream data\n");
+
+            gst_adapter_flush(avi->adapter, 12);
+            avi->offset += 12;
+            avi->state = GST_AVI_DEMUX_MOVI;
+
+            /* send initial discont */
+            avi->segment.start = 0;
+            avi->segment.stop = (gint64) (((gfloat) avi->stream[0].strh->scale) *
+                avi->stream[0].strh->length / avi->stream[0].strh->rate) * GST_SECOND;
+            avi->seek_event = gst_event_new_new_segment
+                (FALSE, avi->segment.rate, GST_FORMAT_TIME,
+                 avi->segment.start, avi->segment.stop, avi->segment.start);
+
+            /* at this point we know all the streams and we can signal the no more
+              * pads signal */
+            GST_DEBUG("Signaling no more pads\n");
+            GST_DEBUG_OBJECT (avi, "signaling no more pads");
+            gst_element_no_more_pads (GST_ELEMENT (avi));
+
+            return GST_FLOW_OK;
+          case GST_RIFF_LIST_INFO:
+            GST_DEBUG("Found INFO chunk\n");
+            if ( gst_avi_demux_peek_chunk(avi, &tag, &size ) ) {
+              avi->offset += 12;
+              gst_adapter_flush(avi->adapter, 12);
+              buf = gst_buffer_new();
+              GST_BUFFER_DATA(buf) = gst_adapter_take(avi->adapter, size-4);
+              GST_BUFFER_SIZE(buf) = size-4;
+
+              GstTagList *t;
+
+              gst_riff_parse_info (GST_ELEMENT (avi), buf, &t);
+              if (t) {
+                GST_DEBUG("Tags found, signaling\n");
+                gst_element_found_tags (GST_ELEMENT (avi), t);
+              }
+              gst_buffer_unref (buf);
+//              gst_adapter_flush(avi->adapter, ((size + 1) & ~1) - 4);
+              avi->offset += ((size + 1) & ~1) - 4;
+              goto iterate;
+            } else {
+              /* Need more data */
+              return GST_FLOW_OK;
+            }
+
+            /* gst_riff_read_chunk() has already advanced avi->offset */
+            break;
+          default:
+            if ( gst_avi_demux_peek_chunk(avi, &tag, &size ) ) {
+              avi->offset += 8 + ((size + 1) & ~1);
+              gst_adapter_flush(avi->adapter, 8 + ((size + 1) & ~1));
+            } else {
+              /* Need more data */
+              return GST_FLOW_OK;
+            }
+            break;
+        }
+      } else {
+        if ( gst_avi_demux_peek_chunk(avi, &tag, &size ) ) {
+          avi->offset += 8 + ((size + 1) & ~1);
+          gst_adapter_flush(avi->adapter, 8 + ((size + 1) & ~1));
+          goto iterate;
+        } else {
+          /* Need more data */
+          return GST_FLOW_OK;
+        }
+      }
+      break;
+    default:
+      break;
+  }
+
+  return ret;
+
+}
+
 /*
  * Read full AVI headers.
  */
@@ -2114,6 +2771,8 @@ gst_avi_demux_stream_header (GstAviDemux
   else if (GST_BUFFER_SIZE (buf) < 4)
     goto no_header;
 
+  GST_DEBUG_OBJECT (avi, "parsing headers");
+
   /* Find the 'hdrl' LIST tag */
   while (GST_READ_UINT32_LE (GST_BUFFER_DATA (buf)) != GST_RIFF_LIST_hdrl) {
     GST_LOG_OBJECT (avi, "buffer contains %" GST_FOURCC_FORMAT,
@@ -2137,6 +2796,8 @@ gst_avi_demux_stream_header (GstAviDemux
   else if (!gst_avi_demux_parse_avih (GST_ELEMENT (avi), sub, &avi->avih))
     goto invalid_avih;
 
+  GST_DEBUG_OBJECT (avi, "reading elemnts from header");
+
   /* now, read the elements from the header until the end */
   while (gst_riff_parse_chunk (GST_ELEMENT (avi), buf, &offset, &tag, &sub)) {
     /* sub can be NULL on empty tags */
@@ -2153,8 +2814,10 @@ gst_avi_demux_stream_header (GstAviDemux
 
         switch (GST_READ_UINT32_LE (GST_BUFFER_DATA (sub))) {
           case GST_RIFF_LIST_strl:
-            if (!(gst_avi_demux_parse_stream (GST_ELEMENT (avi), sub)))
+            if (!(gst_avi_demux_parse_stream (GST_ELEMENT (avi), sub))) {
+              GST_DEBUG_OBJECT (avi, "avi_demux_parse_stream failed");
               return GST_FLOW_ERROR;
+            }
             gst_buffer_unref (sub);
             break;
           case GST_RIFF_LIST_odml:
@@ -2201,10 +2864,14 @@ gst_avi_demux_stream_header (GstAviDemux
     guint32 tag, ltag;
 
     if ((res = gst_pad_pull_range (avi->sinkpad, avi->offset,
-                12, &buf)) != GST_FLOW_OK)
+                12, &buf)) != GST_FLOW_OK) {
+      GST_DEBUG_OBJECT (avi, "pull_ranged returned %s", gst_flow_get_name (res));
       return res;
-    else if (!buf || GST_BUFFER_SIZE (buf) < 12)
+    }
+    else if (!buf || GST_BUFFER_SIZE (buf) < 12) {
+      GST_DEBUG_OBJECT (avi, "got %d bytes which is less than 12 bytes",(buf ? GST_BUFFER_SIZE (buf) : 0));
       return GST_FLOW_ERROR;
+    }
 
     tag = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf));
     size = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf) + 4);
@@ -2214,11 +2881,13 @@ gst_avi_demux_stream_header (GstAviDemux
     if (tag == GST_RIFF_TAG_LIST) {
       switch (ltag) {
         case GST_RIFF_LIST_movi:
-          goto done;
+          goto skipping_done;
         case GST_RIFF_LIST_INFO:
           if ((res = gst_riff_read_chunk (GST_ELEMENT (avi), avi->sinkpad,
-                      &avi->offset, &tag, &buf)) != GST_FLOW_OK)
+                      &avi->offset, &tag, &buf)) != GST_FLOW_OK) {
+            GST_DEBUG_OBJECT (avi, "read_chunk returned %s", gst_flow_get_name (res));
             return res;
+          }
           else {
             sub = gst_buffer_create_sub (buf, 4, GST_BUFFER_SIZE (buf) - 4);
             gst_riff_parse_info (GST_ELEMENT (avi), sub, &avi->globaltags);
@@ -2238,24 +2907,64 @@ gst_avi_demux_stream_header (GstAviDemux
       avi->offset += 8 + ((size + 1) & ~1);
     }
   } while (1);
-done:
+skipping_done:
+
+  GST_DEBUG_OBJECT (avi, "skipping done ... (streams=%d, stream[0].indexes=%p)",
+        avi->num_streams, avi->stream[0].indexes);
 
   /* create or read stream index (for seeking) */
   if (avi->stream[0].indexes != NULL) {
     gst_avi_demux_read_subindexes (avi, &index, &alloc);
   }
-  if (!index) {
+  /* FIXME: why was this there? 
+  if (!index) { */
     if (avi->avih->flags & GST_RIFF_AVIH_HASINDEX) {
       gst_avi_demux_stream_index (avi, &index, &alloc);
     }
     /* some indexes are incomplete, continue streaming from there */
+#ifndef MEMOPT
     if (!index)
       gst_avi_demux_stream_scan (avi, &index, &alloc);
-  }
+#endif
+  /*}*/
 
   /* this is a fatal error */
-  if (!index)
+  if (!index) {
+    GST_WARNING ("file without index");
+#ifdef MEMOPT
+    guint64 pos = avi->offset;
+    guint64 pos_first = 0;
+    guint32 tag;
+    guint size, stream_nr;
+    avi_stream_context *stream = NULL;
+    guint i, count = 0;
+
+    avi->local_without_index = TRUE;
+
+    for(i=0; count < avi->num_streams; i++) {
+      if(gst_avi_demux_next_data_buffer (avi, &pos, &tag, &size) != GST_FLOW_OK) {
+        return GST_FLOW_ERROR;
+      }
+
+      stream_nr = CHUNKID_TO_STREAMNR (tag);
+
+      if(i == 0) {
+        pos_first = pos;
+        avi->current_stream = stream_nr;
+      }
+      stream = &avi->stream[stream_nr];
+      if(!stream->current_pos) {
+        stream->current_pos = pos - 8;
+        GST_DEBUG("stream[%d]->current_pos = %d",stream_nr,stream->current_pos);
+        count++;
+      }
+      pos += size;
+    }
+    avi->offset = pos_first - 8;
+#else
     goto no_index;
+#endif
+  }
 
   gst_avi_demux_massage_index (avi, index, alloc);
   gst_avi_demux_calculate_durations_from_index (avi);
@@ -2268,6 +2977,11 @@ done:
 
   GST_DEBUG_OBJECT (avi, "segment stop %" G_GINT64_FORMAT, avi->segment.stop);
 
+  if(!avi->segment.stop) {
+    GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), ("Corrupted stream header"));
+    return GST_FLOW_ERROR;
+  }
+
   avi->seek_event = gst_event_new_new_segment
       (FALSE, avi->segment.rate, GST_FORMAT_TIME,
       avi->segment.start, avi->segment.stop, avi->segment.start);
@@ -2324,6 +3038,7 @@ no_streams:
     GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), ("No streams found"));
     return GST_FLOW_ERROR;
   }
+#ifndef MEMOPT
 no_index:
   {
     g_list_free (index);
@@ -2334,6 +3049,7 @@ no_index:
         ("Could not get/create index"));
     return GST_FLOW_ERROR;
   }
+#endif
 }
 
 /*
@@ -2347,6 +3063,9 @@ gst_avi_demux_handle_seek (GstAviDemux *
   gboolean flush, keyframe;
   gst_avi_index_entry *entry;
   guint i;
+#ifdef MEMOPT
+  avi_stream_context *str = NULL;
+#endif
 
   /* FIXME: if we seek in an openDML file, we will have multiple
    * primary levels. Seeking in between those will cause havoc. */
@@ -2373,6 +3092,27 @@ gst_avi_demux_handle_seek (GstAviDemux *
           entry->stream_nr, GST_TIME_ARGS (entry->ts),
           GST_TIME_ARGS (entry->dur));
       avi->current_entry = entry->index_nr;
+#ifdef MEMOPT
+      /* Update video stream pos */
+      GST_DEBUG("AVI: seek, update video pos\n");
+      str = &avi->stream[entry->stream_nr];
+      str->current_pos = entry->offset;
+      str->current_frame = entry->frames_before;
+      str->current_byte = entry->bytes_before;
+      avi->current_stream = entry->stream_nr;
+      GST_DEBUG("Video pos updated to: current_pos = %lld\n", str->current_pos);
+      GST_DEBUG("Video time %"GST_TIME_FORMAT" \n", GST_TIME_ARGS (entry->ts));
+      /* And audio stream, if present */
+      if  ( avi->num_a_streams > 0 ) {
+        gst_avi_index_entry *_entry = &avi->index_entries[entry->index_nr + 1];
+        str = &avi->stream[entry->stream_nr + 1];
+        str->current_pos = _entry->offset;
+        str->current_frame = _entry->frames_before;
+        str->current_byte = _entry->bytes_before;
+        GST_DEBUG("Audio pos updated to: current_pos =  %lld\n", str->current_pos);
+        GST_DEBUG("Audio time %"GST_TIME_FORMAT" \n", GST_TIME_ARGS (_entry->ts));
+      }
+#endif
     } else {
       GST_WARNING_OBJECT (avi,
           "Couldn't find AviIndexEntry for time:%" GST_TIME_FORMAT,
@@ -2496,6 +3236,7 @@ gst_avi_demux_aggregated_flow (GstAviDem
   return res;
 }
 
+#ifndef MEMOPT
 static GstFlowReturn
 gst_avi_demux_process_next_entry (GstAviDemux * avi)
 {
@@ -2507,6 +3248,13 @@ gst_avi_demux_process_next_entry (GstAvi
     if (avi->current_entry >= avi->index_size) {
       GST_LOG_OBJECT (avi, "Handled last index entry, setting EOS (%d > %d)",
           avi->current_entry, avi->index_size);
+      if (avi->segment.flags & GST_SEEK_FLAG_SEGMENT)
+        gst_element_post_message
+            (GST_ELEMENT (avi),
+            gst_message_new_segment_done (GST_OBJECT (avi), GST_FORMAT_TIME,
+                avi->segment.stop));
+      else
+        gst_avi_demux_push_event (avi, gst_event_new_eos ());
       avi->stream[0].last_flow = GST_FLOW_UNEXPECTED;
       goto eos;
     } else {
@@ -2591,6 +3339,186 @@ eos:
     return GST_FLOW_UNEXPECTED;
   }
 }
+#endif
+
+#ifdef MEMOPT
+static gboolean
+gst_avi_demux_check_eos (guint32 tag)
+{
+  // Do: Better detection for stream end
+  if ( tag == GST_RIFF_TAG_idx1 ) {
+    GST_DEBUG("Found index tag, stream done\n");
+    return TRUE;
+  }
+  return FALSE;
+}
+
+
+/*
+ * Read one frame
+ *
+ */
+static GstFlowReturn
+gst_avi_demux_read_frame (GstAviDemux * avi)
+{
+  guint32 tag = 0;
+  guint32 size = 0;
+  gint stream_nr = 0;
+  GstFlowReturn res = GST_FLOW_OK;
+  avi_stream_context *stream = NULL;
+  GstBuffer * buf = NULL;
+
+  stream = &avi->stream[avi->current_stream];
+  GST_DEBUG("AVI: Reading one frame for stream %d at current pos %lld\n",
+          avi->current_stream, stream->current_pos);
+
+  if ((res = gst_pad_pull_range (avi->sinkpad, stream->current_pos,
+       8, &buf)) != GST_FLOW_OK) {
+    GST_DEBUG_OBJECT (avi, "pull_range returned %s", gst_flow_get_name (res));
+    return res;
+  }
+
+  tag = GST_READ_UINT32_LE ( GST_BUFFER_DATA(buf) );
+  size = GST_READ_UINT32_LE ( GST_BUFFER_DATA(buf) + 4);
+  gst_buffer_unref(buf);
+
+  if ( gst_avi_demux_check_eos(tag) ) {
+    gst_avi_demux_push_event (avi, gst_event_new_eos ());
+    return GST_FLOW_WRONG_STATE;
+  }
+
+  stream->current_pos += 8;
+  stream_nr = CHUNKID_TO_STREAMNR (tag);
+
+  if (stream_nr < 0 || stream_nr >= avi->num_streams) {
+    /* recoverable */
+    GST_WARNING ("Invalid stream ID %d (%" GST_FOURCC_FORMAT ") size = %d",
+                 stream_nr, GST_FOURCC_ARGS (tag), size);
+    stream->current_pos += (size + 1) & ~1;
+
+    /* skip */
+    return GST_FLOW_OK;
+    /* this isn't defined anywhere
+    if (!gst_riff_read_skip (riff)) return FALSE;
+    */
+  } else {
+    gint s = stream_nr;
+    guint32 t_tag = 0;
+    guint32 t_size = size;
+
+    if ( s != avi->current_stream ) {
+      GST_DEBUG("Switching stream");
+      while ( s != avi->current_stream ) {
+        GstPad *peer;
+        gint64 total_length;
+
+        /* Skip over chunk */
+        stream->current_pos += (t_size + 1) & ~1;
+
+        // FIXME: Optimize. Not need to ask this again every time
+        peer = gst_pad_get_peer(avi->sinkpad);
+        if(peer) {
+          GstFormat fmt = GST_FORMAT_BYTES;
+          gst_pad_query_duration (peer, &fmt, &total_length);
+          gst_object_unref(peer); // _get_peer() ref'ed it
+        }
+        if(stream->current_pos >= total_length) {
+          gst_avi_demux_push_event (avi, gst_event_new_eos ());
+          return GST_FLOW_WRONG_STATE;
+        }
+
+        if ((res = gst_pad_pull_range (avi->sinkpad, stream->current_pos,
+             8, &buf)) != GST_FLOW_OK) {
+          GST_DEBUG_OBJECT (avi, "pull_range returned %s", gst_flow_get_name (res));
+          return res;
+        }
+
+        t_tag = GST_READ_UINT32_LE ( GST_BUFFER_DATA(buf) );
+        t_size = GST_READ_UINT32_LE ( GST_BUFFER_DATA(buf) + 4);
+        s = CHUNKID_TO_STREAMNR (t_tag);
+        stream->current_pos += 8;
+        gst_buffer_unref(buf);
+        if ( gst_avi_demux_check_eos(t_tag) ) {
+          gst_avi_demux_push_event (avi, gst_event_new_eos ());
+          return GST_FLOW_WRONG_STATE;
+        }
+      }
+      /* Save pos */
+      stream->current_pos -= 8;
+      avi->current_stream = stream_nr;
+      stream = &avi->stream[avi->current_stream];
+      if ((res = gst_pad_pull_range (avi->sinkpad, stream->current_pos,
+           8, &buf)) != GST_FLOW_OK) {
+        GST_DEBUG_OBJECT (avi, "pull_range returned %s", gst_flow_get_name (res));
+        return res;
+      }
+
+      tag = GST_READ_UINT32_LE ( GST_BUFFER_DATA(buf) );
+      size = GST_READ_UINT32_LE ( GST_BUFFER_DATA(buf) + 4);
+
+      gst_buffer_unref(buf);
+      stream->current_pos += 8;
+    }
+
+    stream = &avi->stream[avi->current_stream];
+    if ( size == 0 )
+      return GST_FLOW_OK;
+
+    /* Read the buffer */
+    if ((res = gst_pad_pull_range (avi->sinkpad, stream->current_pos,
+         size, &buf)) != GST_FLOW_OK) {
+      GST_DEBUG_OBJECT (avi, "pull_range returned %s", gst_flow_get_name (res));
+      return res;
+    }
+
+    GST_DEBUG("Read buffer of %d bytes, offset %lld",
+      size, stream->current_pos);
+    stream->current_pos += (size + 1) & ~1;
+    GstClockTime next_ts = 0;
+    GstFormat format;
+
+    /* get time of this buffer */
+    //stream = &avi->stream[stream_nr];
+    format = GST_FORMAT_TIME;
+    gst_pad_query_position(stream->pad, &format, (gint64 *) &next_ts);
+
+    /* set delay (if any) */
+//     if (stream->strh->init_frames == stream->current_frame &&
+//         stream->delay == 0)
+//       stream->delay = next_ts;
+
+    stream->current_frame++;
+    stream->current_byte += GST_BUFFER_SIZE (buf);
+
+    /* should we skip this data? */
+/*    if (stream->skip) {
+    stream->skip--;
+    gst_buffer_unref (buf);
+  } else {*/
+    if (!stream->pad || ! gst_pad_is_linked (stream->pad)) {
+      gst_buffer_unref (buf);
+    } else {
+      GstClockTime dur_ts = 0;
+
+      if (stream->strh->fcc_handler == GST_MAKE_FOURCC ('D', 'I', 'B', ' ')) {
+        buf = gst_avi_demux_invert (stream, buf);
+      }
+
+      GST_BUFFER_TIMESTAMP (buf) = next_ts;
+      gst_pad_query_position(stream->pad, &format, (gint64 *) &dur_ts);
+
+      GST_BUFFER_DURATION (buf) = dur_ts - next_ts;
+      gst_buffer_set_caps (buf, GST_PAD_CAPS (stream->pad));
+      GST_DEBUG_OBJECT (avi,
+                        "Pushing buffer with time=%" GST_TIME_FORMAT " over pad %s",
+                        GST_TIME_ARGS (next_ts), GST_PAD_NAME (stream->pad));
+      res = gst_pad_push (stream->pad, buf);
+    }
+  }
+  return res;
+}
+
+#endif
 
 static void
 push_tag_lists (GstAviDemux * avi)
@@ -2613,6 +3541,7 @@ push_tag_lists (GstAviDemux * avi)
     avi->globaltags = NULL;
   }
   avi->got_tags = FALSE;
+  GST_DEBUG_OBJECT (avi, "Pushed tag lists");
 }
 
 /*
@@ -2622,72 +3551,134 @@ push_tag_lists (GstAviDemux * avi)
 static GstFlowReturn
 gst_avi_demux_stream_data (GstAviDemux * avi)
 {
+  guint32 tag = 0;
+  guint32 size = 0;
+  gint stream_nr = 0;
+  const guint8 * data = NULL;
+  GstFlowReturn res = GST_FLOW_OK;
+
   /* if we have a avi->index_entries[], we don't want to read
    * the stream linearly, but seek to the next ts/index_entry. */
-  //if (avi->index_entries != NULL) {
-  return gst_avi_demux_process_next_entry (avi);
-  //}
-#if 0
-  if (!gst_avi_demux_sync (avi, &tag, FALSE))
-    return FALSE;
-  stream_nr = CHUNKID_TO_STREAMNR (tag);
+#ifdef MEMOPT
+  if (avi->index_entries != NULL || avi->local_without_index)
+    return gst_avi_demux_read_frame (avi);
+#else
+  if (avi->index_entries != NULL)
+    return gst_avi_demux_process_next_entry (avi);
+#endif
 
-  if (stream_nr < 0 || stream_nr >= avi->num_streams) {
-    /* recoverable */
-    GST_WARNING ("Invalid stream ID %d (%" GST_FOURCC_FORMAT ")",
-        stream_nr, GST_FOURCC_ARGS (tag));
-    if (!gst_riff_read_skip (riff))
-      return FALSE;
-  } else {
-    avi_stream_context *stream;
-    GstClockTime next_ts;
-    GstFormat format;
-    GstBuffer *buf;
+  if ( avi->have_eos ) {
+    /* Clean adapter, we're done */
+    gst_adapter_clear(avi->adapter);
+    return res;
+  }
 
-    /* get buffer */
-    if (!gst_riff_read_data (riff, &tag, &buf))
-      return FALSE;
+/*  if (!gst_avi_demux_sync (avi, &tag, FALSE))
+    return FALSE;*/
 
-    /* get time of this buffer */
-    stream = &avi->stream[stream_nr];
-    format = GST_FORMAT_TIME;
-    gst_pad_query (stream->pad, GST_QUERY_POSITION, &format, &next_ts);
+  /* Iterate until need more data, so adapter won't grow too much */
+  while (1) {
+    if ( ! gst_avi_demux_peek_chunk_info(avi, &tag, &size) ) {
+      return res;
+    }
 
-    /* set delay (if any) */
-    if (stream->strh->init_frames == stream->current_frame &&
-        stream->delay == 0)
-      stream->delay = next_ts;
+    if ( ( tag & 0xff) >= '0' &&
+         ( tag & 0xff) <= '9' &&
+         ( (tag >> 8) & 0xff) >= '0' &&
+         ( (tag >> 8) & 0xff) <= '9' ) {
+      GST_DEBUG("Chunk ok (%" GST_FOURCC_FORMAT "), size %d\n", GST_FOURCC_ARGS (tag), size);
+    } else if ( tag == GST_RIFF_TAG_idx1 ) {
+      GST_DEBUG("Found index tag, stream done\n");
+      gst_avi_demux_push_event (avi, gst_event_new_eos ());
+      avi->have_eos = TRUE;
+      return res;
+    } else {
+      GST_DEBUG("No more stream chunks, send EOS\n");
+      gst_avi_demux_push_event (avi, gst_event_new_eos ());
+      avi->have_eos = TRUE;
+      return res;
+    }
 
-    stream->current_frame++;
-    stream->current_byte += GST_BUFFER_SIZE (buf);
+    if ( ! gst_avi_demux_peek_chunk(avi, &tag, &size) ) {
+      return res;
+    }
 
-    /* should we skip this data? */
-    if (stream->skip) {
-      stream->skip--;
-      gst_buffer_unref (buf);
-    } else {
-      if (!stream->pad || !GST_PAD_IS_USABLE (stream->pad)) {
-        gst_buffer_unref (buf);
+    if ( size > 0 ) {
+      stream_nr = CHUNKID_TO_STREAMNR (tag);
+
+      if (stream_nr < 0 || stream_nr >= avi->num_streams) {
+        /* recoverable */
+        GST_WARNING ("Invalid stream ID %d (%" GST_FOURCC_FORMAT ")",
+            stream_nr, GST_FOURCC_ARGS (tag));
+    /*    if (!gst_riff_read_skip (riff))
+          return FALSE;*/
       } else {
-        GstClockTime dur_ts;
+        avi_stream_context *stream;
+        GstClockTime next_ts = 0;
+        GstFormat format;
+        GstBuffer *buf = gst_buffer_new_and_alloc(size);
+        gst_adapter_flush(avi->adapter, 8);
+
+        /* get buffer */
+        data = gst_adapter_peek(avi->adapter, ((size + 1) & ~1));
+        memcpy( GST_BUFFER_DATA(buf), data, size);
+        GST_BUFFER_SIZE(buf) = size;
+
+        avi->offset += 8 + ((size + 1) & ~1);
+        gst_adapter_flush(avi->adapter, ((size + 1) & ~1));
+
+        /* get time of this buffer */
+        stream = &avi->stream[stream_nr];
+        format = GST_FORMAT_TIME;
+        gst_pad_query_position(stream->pad, &format, (gint64 *) &next_ts);
+
+        /* set delay (if any) */
+    //     if (stream->strh->init_frames == stream->current_frame &&
+    //         stream->delay == 0)
+    //       stream->delay = next_ts;
+
+        stream->current_frame++;
+        stream->current_byte += GST_BUFFER_SIZE (buf);
+
+        /* should we skip this data? */
+    /*    if (stream->skip) {
+          stream->skip--;
+          gst_buffer_unref (buf);
+        } else {*/
+          if (!stream->pad || ! gst_pad_is_linked (stream->pad)) {
+            gst_buffer_unref (buf);
+          } else {
+            GstClockTime dur_ts = 0;
 
-        if (stream->strh->fcc_handler == GST_MAKE_FOURCC ('D', 'I', 'B', ' ')) {
-          buf = gst_avi_demux_invert (stream, buf);
-        }
+            if (stream->strh->fcc_handler == GST_MAKE_FOURCC ('D', 'I', 'B', ' ')) {
+              buf = gst_avi_demux_invert (stream, buf);
+            }
 
-        GST_BUFFER_TIMESTAMP (buf) = next_ts;
-        gst_pad_query (stream->pad, GST_QUERY_POSITION, &format, &dur_ts);
-        GST_BUFFER_DURATION (buf) = dur_ts - next_ts;
-        GST_DEBUG_OBJECT (avi,
-            "Pushing buffer with time=%" GST_TIME_FORMAT " over pad %s",
-            GST_TIME_ARGS (next_ts), GST_PAD_NAME (stream->pad));
-        gst_pad_push (stream->pad, GST_DATA (buf));
+            GST_BUFFER_TIMESTAMP (buf) = next_ts;
+            gst_pad_query_position(stream->pad, &format, (gint64 *) &dur_ts);
+
+            GST_BUFFER_DURATION (buf) = dur_ts - next_ts;
+            gst_buffer_set_caps (buf, GST_PAD_CAPS (stream->pad));
+            GST_DEBUG_OBJECT (avi,
+                "Pushing buffer with time=%" GST_TIME_FORMAT " over pad %s",
+                GST_TIME_ARGS (next_ts), GST_PAD_NAME (stream->pad));
+            res = gst_pad_push (stream->pad, buf);
+            if ( res == GST_FLOW_OK ) {
+              GST_DEBUG("Push ok!\n");
+            } else {
+              return res;
+            }
+          }
       }
+        //}
+    } else {
+      GST_DEBUG("Chunk with size 0. Skip it\n");
+      gst_adapter_flush(avi->adapter, 8);
     }
   }
 
-  return TRUE;
-#endif
+  return res;
+
 }
 
 static void
@@ -2759,13 +3750,80 @@ pause:
   }
 }
 
+
+static GstFlowReturn
+gst_avi_demux_chain (GstPad *pad, GstBuffer *buf) {
+
+  GstFlowReturn res = GST_FLOW_OK;
+  GstAviDemux *avi = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
+
+  GST_DEBUG("avi _chain\n");
+
+  gst_adapter_push(avi->adapter, buf);
+
+  switch (avi->state) {
+    case GST_AVI_DEMUX_START:
+      if ((res = gst_avi_demux_parse_stream_init (avi)) != GST_FLOW_OK)
+        goto pause;
+      /* fall-through */
+    case GST_AVI_DEMUX_HEADER:
+      if ((res = gst_avi_demux_parse_stream_header (avi)) != GST_FLOW_OK) {
+        GST_DEBUG("gst flow not ok, going to pause\n");
+        goto pause;
+      }
+      break;
+    case GST_AVI_DEMUX_MOVI:
+      if (avi->seek_event) {
+        gst_avi_demux_push_event (avi, avi->seek_event);
+        avi->seek_event = NULL;
+      }
+      if ((res = gst_avi_demux_stream_data (avi)) != GST_FLOW_OK) {
+        GST_DEBUG("stream_data flow: %s", gst_flow_get_name (res));
+        goto pause;
+      }
+      break;
+    default:
+      g_assert_not_reached ();
+  }
+
+  return res;
+
+pause:
+  GST_DEBUG("Pausing task\n");
+  GST_LOG_OBJECT (avi, "pausing task");
+  gst_pad_pause_task (avi->sinkpad);
+  if (GST_FLOW_IS_FATAL (res)) {
+    guint stream = avi->num_streams;
+
+    /* for fatal errors we post an error message */
+    GST_ELEMENT_ERROR (avi, STREAM, FAILED,
+                       (_("Internal data stream error.")),
+                       ("streaming stopped, reason %s", gst_flow_get_name (res)));
+    while (stream--) {
+      if (avi->stream[stream].pad)
+        gst_pad_push_event (avi->stream[stream].pad, gst_event_new_eos ());
+    }
+  }
+  return res;
+}
+
+
+
 static gboolean
 gst_avi_demux_sink_activate (GstPad * sinkpad)
 {
-  if (gst_pad_check_pull_range (sinkpad))
-    return gst_pad_activate_pull (sinkpad, TRUE);
+  GstAviDemux *avidemux = GST_AVI_DEMUX (gst_pad_get_parent (sinkpad));
 
-  return FALSE;
+  if (gst_pad_check_pull_range (sinkpad)) {
+    avidemux->adapter = NULL;
+    gst_object_unref(avidemux);
+    return gst_pad_activate_pull (sinkpad, TRUE);
+  } else {
+    GST_DEBUG("going to push (streaming) mode\n");
+    avidemux->adapter = gst_adapter_new();
+    gst_object_unref(avidemux);
+    return gst_pad_activate_push (sinkpad, TRUE);
+  }
 }
 
 static gboolean
@@ -2781,6 +3839,20 @@ gst_avi_demux_sink_activate_pull (GstPad
   return TRUE;
 }
 
+static gboolean
+gst_avi_demux_activate_push(GstPad  *pad, gboolean active)
+{
+
+  if (active) {
+    GST_DEBUG("avi: activating push/chain function");
+  } else {
+    GST_DEBUG("avi: deactivating push/chain function");
+  }
+
+  return TRUE;
+}
+
+
 static GstStateChangeReturn
 gst_avi_demux_change_state (GstElement * element, GstStateChange transition)
 {
@@ -2802,6 +3874,9 @@ gst_avi_demux_change_state (GstElement *
   switch (transition) {
     case GST_STATE_CHANGE_PAUSED_TO_READY:
       gst_avi_demux_reset (avi);
+      if ( avi->adapter ) {
+        gst_adapter_clear(avi->adapter);
+      }
       break;
     default:
       break;
Index: gstavidemux.h
===================================================================
RCS file: /cvs/gstreamer/gst-plugins-good/gst/avi/gstavidemux.h,v
retrieving revision 1.37
diff -u -p -u -r1.37 gstavidemux.h
--- gstavidemux.h	28 Jul 2006 08:21:27 -0000	1.37
+++ gstavidemux.h	8 Aug 2006 11:48:20 -0000
@@ -1,5 +1,6 @@
 /* GStreamer
  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ * Copyright (C) <2006> Nokia Corporation (contact <stefan.kost@nokia.com>)
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -25,6 +26,7 @@
 #include "avi-ids.h"
 #include "gst/riff/riff-ids.h"
 #include "gst/riff/riff-read.h"
+#include <gst/base/gstadapter.h>
 
 G_BEGIN_DECLS
 
@@ -41,6 +43,17 @@ G_BEGIN_DECLS
 
 #define GST_AVI_DEMUX_MAX_STREAMS       16
 
+/*
+ * Saves memory because it does not interpolate between index frames and
+ * create seeking entries "in-between" the index frames.
+ * This way the index structure in the memory will consist only of the
+ * actual index frames found in the AVI header.
+ * The memory saving is quite notable with big AVI clips (from several Megs
+ * to some tens of kilobytes).
+ * FIXME: make this a gobject property if accepted
+ */
+#define MEMOPT 1
+
 #define CHUNKID_TO_STREAMNR(chunkid) \
   ((((chunkid) & 0xff) - '0') * 10 + \
    (((chunkid) >> 8) & 0xff) - '0')
@@ -77,6 +90,9 @@ typedef struct {
   guint          current_frame;
   guint64        current_byte;
   gint           current_entry;
+#ifdef MEMOPT
+  guint64        current_pos;
+#endif
 
   /* stream length */
   guint64        total_bytes;
@@ -100,6 +116,15 @@ typedef enum {
   GST_AVI_DEMUX_MOVI,
 } GstAviDemuxState;
 
+typedef enum {
+  GST_AVI_DEMUX_HEADER_TAG_LIST,
+  GST_AVI_DEMUX_HEADER_AVIH,
+  GST_AVI_DEMUX_HEADER_ELEMENTS,
+  GST_AVI_DEMUX_HEADER_INFO,
+  GST_AVI_DEMUX_HEADER_JUNK,
+  GST_AVI_DEMUX_HEADER_DATA
+} GstAviDemuxHeaderState;
+
 typedef struct _GstAviDemux {
   GstElement     parent;
 
@@ -108,6 +133,7 @@ typedef struct _GstAviDemux {
 
   /* AVI decoding state */
   GstAviDemuxState state;
+  GstAviDemuxHeaderState header_state;
   guint64        offset;
 
   /* index */
@@ -120,8 +146,18 @@ typedef struct _GstAviDemux {
   guint          num_streams;
   guint          num_v_streams;
   guint          num_a_streams;
+#ifdef MEMOPT
+  guint          current_stream;
+  gboolean       local_without_index;
+#endif
+
   avi_stream_context stream[GST_AVI_DEMUX_MAX_STREAMS];
 
+  /* for streaming mode */
+  gboolean      streaming;
+  gboolean      have_eos;
+  GstAdapter    *adapter;
+
   /* some stream info for length */
   gst_riff_avih *avih;
 
