avidemux: Also extract IDIT tags present too early
[platform/upstream/gst-plugins-good.git] / gst / avi / gstavidemux.c
index 6d8fff7..6bcdffd 100644 (file)
@@ -1,6 +1,7 @@
 /* GStreamer
  * Copyright (C) <1999> Erik Walthinsen <omega@temple-baptist.com>
  * Copyright (C) <2006> Nokia Corporation (contact <stefan.kost@nokia.com>)
+ * Copyright (C) <2009-2010> STEricsson <benjamin.gaignard@stericsson.com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -45,6 +46,7 @@
 #endif
 
 #include <string.h>
+#include <stdio.h>
 
 #include "gst/riff/riff-media.h"
 #include "gstavidemux.h"
 
 #define DIV_ROUND_UP(s,v) (((s) + ((v)-1)) / (v))
 
+#define GST_AVI_KEYFRAME 1
+#define ENTRY_IS_KEYFRAME(e) ((e)->flags == GST_AVI_KEYFRAME)
+#define ENTRY_SET_KEYFRAME(e) ((e)->flags = GST_AVI_KEYFRAME)
+#define ENTRY_UNSET_KEYFRAME(e) ((e)->flags = 0)
+
+
 GST_DEBUG_CATEGORY_STATIC (avidemux_debug);
 #define GST_CAT_DEFAULT avidemux_debug
 
@@ -90,6 +98,8 @@ static gboolean gst_avi_demux_src_convert (GstPad * pad, GstFormat src_format,
 static gboolean gst_avi_demux_do_seek (GstAviDemux * avi, GstSegment * segment);
 static gboolean gst_avi_demux_handle_seek (GstAviDemux * avi, GstPad * pad,
     GstEvent * event);
+static gboolean gst_avi_demux_handle_seek_push (GstAviDemux * avi, GstPad * pad,
+    GstEvent * event);
 static void gst_avi_demux_loop (GstPad * pad);
 static gboolean gst_avi_demux_sink_activate (GstPad * sinkpad);
 static gboolean gst_avi_demux_sink_activate_pull (GstPad * sinkpad,
@@ -97,8 +107,16 @@ static gboolean gst_avi_demux_sink_activate_pull (GstPad * sinkpad,
 static gboolean gst_avi_demux_activate_push (GstPad * pad, gboolean active);
 static GstFlowReturn gst_avi_demux_chain (GstPad * pad, GstBuffer * buf);
 
+static void gst_avi_demux_set_index (GstElement * element, GstIndex * index);
+static GstIndex *gst_avi_demux_get_index (GstElement * element);
 static GstStateChangeReturn gst_avi_demux_change_state (GstElement * element,
     GstStateChange transition);
+static void gst_avi_demux_calculate_durations_from_index (GstAviDemux * avi);
+static void gst_avi_demux_get_buffer_info (GstAviDemux * avi,
+    GstAviStream * stream, guint entry_n, GstClockTime * timestamp,
+    GstClockTime * ts_end, guint64 * offset, guint64 * offset_end);
+
+static void gst_avi_demux_parse_idit (GstAviDemux * avi, GstBuffer * buf);
 
 static GstElementClass *parent_class = NULL;
 
@@ -133,13 +151,6 @@ gst_avi_demux_get_type (void)
 static void
 gst_avi_demux_base_init (GstAviDemuxClass * klass)
 {
-  static const GstElementDetails gst_avi_demux_details =
-      GST_ELEMENT_DETAILS ("Avi demuxer",
-      "Codec/Demuxer",
-      "Demultiplex an avi file into audio and video",
-      "Erik Walthinsen <omega@cse.ogi.edu>\n"
-      "Wim Taymans <wim.taymans@chello.be>\n"
-      "Thijs Vermeir <thijsvermeir@gmail.com>");
   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
   GstPadTemplate *videosrctempl, *audiosrctempl, *subsrctempl;
   GstCaps *audcaps, *vidcaps, *subcaps;
@@ -163,7 +174,12 @@ gst_avi_demux_base_init (GstAviDemuxClass * klass)
   gst_element_class_add_pad_template (element_class, subsrctempl);
   gst_element_class_add_pad_template (element_class,
       gst_static_pad_template_get (&sink_templ));
-  gst_element_class_set_details (element_class, &gst_avi_demux_details);
+  gst_element_class_set_details_simple (element_class, "Avi demuxer",
+      "Codec/Demuxer",
+      "Demultiplex an avi file into audio and video",
+      "Erik Walthinsen <omega@cse.ogi.edu>, "
+      "Wim Taymans <wim.taymans@chello.be>, "
+      "Thijs Vermeir <thijsvermeir@gmail.com>");
 }
 
 static void
@@ -180,6 +196,9 @@ gst_avi_demux_class_init (GstAviDemuxClass * klass)
   gobject_class->finalize = gst_avi_demux_finalize;
   gstelement_class->change_state =
       GST_DEBUG_FUNCPTR (gst_avi_demux_change_state);
+
+  gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_avi_demux_set_index);
+  gstelement_class->get_index = GST_DEBUG_FUNCPTR (gst_avi_demux_get_index);
 }
 
 static void
@@ -196,7 +215,7 @@ gst_avi_demux_init (GstAviDemux * avi)
       GST_DEBUG_FUNCPTR (gst_avi_demux_chain));
   gst_pad_set_event_function (avi->sinkpad,
       GST_DEBUG_FUNCPTR (gst_avi_demux_handle_sink_event));
-  gst_element_add_pad (GST_ELEMENT (avi), avi->sinkpad);
+  gst_element_add_pad (GST_ELEMENT_CAST (avi), avi->sinkpad);
 
   avi->adapter = gst_adapter_new ();
 
@@ -228,8 +247,11 @@ gst_avi_demux_reset_stream (GstAviDemux * avi, GstAviStream * stream)
   if (stream->extradata)
     gst_buffer_unref (stream->extradata);
   if (stream->pad) {
-    gst_pad_set_active (stream->pad, FALSE);
-    gst_element_remove_pad (GST_ELEMENT (avi), stream->pad);
+    if (stream->exposed) {
+      gst_pad_set_active (stream->pad, FALSE);
+      gst_element_remove_pad (GST_ELEMENT_CAST (avi), stream->pad);
+    } else
+      gst_object_unref (stream->pad);
   }
   if (stream->taglist) {
     gst_tag_list_free (stream->taglist);
@@ -253,14 +275,28 @@ gst_avi_demux_reset (GstAviDemux * avi)
   avi->num_v_streams = 0;
   avi->num_a_streams = 0;
   avi->num_t_streams = 0;
+  avi->main_stream = -1;
 
   avi->state = GST_AVI_DEMUX_START;
   avi->offset = 0;
+  avi->building_index = FALSE;
 
   avi->index_offset = 0;
   g_free (avi->avih);
   avi->avih = NULL;
 
+  if (avi->element_index)
+    gst_object_unref (avi->element_index);
+  avi->element_index = NULL;
+
+  if (avi->close_seg_event) {
+    gst_event_unref (avi->close_seg_event);
+    avi->close_seg_event = NULL;
+  }
+  if (avi->seg_event) {
+    gst_event_unref (avi->seg_event);
+    avi->seg_event = NULL;
+  }
   if (avi->seek_event) {
     gst_event_unref (avi->seek_event);
     avi->seek_event = NULL;
@@ -272,6 +308,7 @@ gst_avi_demux_reset (GstAviDemux * avi)
 
   avi->got_tags = TRUE;         /* we always want to push global tags */
   avi->have_eos = FALSE;
+  avi->seekable = TRUE;
 
   gst_adapter_clear (avi->adapter);
 
@@ -505,18 +542,24 @@ gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query)
     case GST_QUERY_DURATION:
     {
       GstFormat fmt;
+      GstClockTime duration;
 
+      /* only act on audio or video streams */
       if (stream->strh->type != GST_RIFF_FCC_auds &&
           stream->strh->type != GST_RIFF_FCC_vids) {
         res = FALSE;
         break;
       }
 
+      /* take stream duration, fall back to avih duration */
+      if ((duration = stream->duration) == -1)
+        duration = avi->duration;
+
       gst_query_parse_duration (query, &fmt, NULL);
 
       switch (fmt) {
         case GST_FORMAT_TIME:
-          gst_query_set_duration (query, fmt, stream->duration);
+          gst_query_set_duration (query, fmt, duration);
           break;
         case GST_FORMAT_DEFAULT:
         {
@@ -527,7 +570,7 @@ gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query)
           if (stream->idx_n >= 0)
             gst_query_set_duration (query, fmt, stream->idx_n);
           else if (gst_pad_query_convert (pad, GST_FORMAT_TIME,
-                  stream->duration, &fmt, &dur))
+                  duration, &fmt, &dur))
             gst_query_set_duration (query, fmt, dur);
           break;
         }
@@ -545,7 +588,7 @@ gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query)
         gboolean seekable = TRUE;
 
         if (avi->streaming) {
-          seekable = FALSE;
+          seekable = avi->seekable;
         }
 
         gst_query_set_seeking (query, GST_FORMAT_TIME, seekable,
@@ -588,6 +631,119 @@ gst_avi_demux_get_event_mask (GstPad * pad)
 }
 #endif
 
+static guint64
+gst_avi_demux_seek_streams (GstAviDemux * avi, guint64 offset, gboolean before)
+{
+  GstAviStream *stream;
+  GstIndexEntry *entry;
+  gint i;
+  gint64 val, min = offset;
+
+  for (i = 0; i < avi->num_streams; i++) {
+    stream = &avi->stream[i];
+
+    entry = gst_index_get_assoc_entry (avi->element_index, stream->index_id,
+        before ? GST_INDEX_LOOKUP_BEFORE : GST_INDEX_LOOKUP_AFTER,
+        GST_ASSOCIATION_FLAG_NONE, GST_FORMAT_BYTES, offset);
+
+    if (before) {
+      if (entry) {
+        gst_index_entry_assoc_map (entry, GST_FORMAT_BYTES, &val);
+        GST_DEBUG_OBJECT (avi, "stream %d, previous entry at %"
+            G_GUINT64_FORMAT, i, val);
+        if (val < min)
+          min = val;
+      }
+      continue;
+    }
+
+    if (!entry) {
+      GST_DEBUG_OBJECT (avi, "no position for stream %d, assuming at start", i);
+      stream->current_entry = 0;
+      stream->current_total = 0;
+      continue;
+    }
+
+    gst_index_entry_assoc_map (entry, GST_FORMAT_BYTES, &val);
+    GST_DEBUG_OBJECT (avi, "stream %d, next entry at %" G_GUINT64_FORMAT,
+        i, val);
+
+    gst_index_entry_assoc_map (entry, GST_FORMAT_TIME, &val);
+    stream->current_total = val;
+    gst_index_entry_assoc_map (entry, GST_FORMAT_DEFAULT, &val);
+    stream->current_entry = val;
+  }
+
+  return min;
+}
+
+static guint
+gst_avi_demux_index_entry_offset_search (GstAviIndexEntry * entry,
+    guint64 * offset)
+{
+  if (entry->offset < *offset)
+    return -1;
+  else if (entry->offset > *offset)
+    return 1;
+  return 0;
+}
+
+static guint64
+gst_avi_demux_seek_streams_index (GstAviDemux * avi, guint64 offset,
+    gboolean before)
+{
+  GstAviStream *stream;
+  GstAviIndexEntry *entry;
+  gint i;
+  gint64 val, min = offset;
+  guint index;
+
+  for (i = 0; i < avi->num_streams; i++) {
+    stream = &avi->stream[i];
+
+    /* compensate for chunk header */
+    offset += 8;
+    entry =
+        gst_util_array_binary_search (stream->index, stream->idx_n,
+        sizeof (GstAviIndexEntry),
+        (GCompareDataFunc) gst_avi_demux_index_entry_offset_search,
+        before ? GST_SEARCH_MODE_BEFORE : GST_SEARCH_MODE_AFTER, &offset, NULL);
+    offset -= 8;
+
+    if (entry)
+      index = entry - stream->index;
+
+    if (before) {
+      if (entry) {
+        val = stream->index[index].offset;
+        GST_DEBUG_OBJECT (avi,
+            "stream %d, previous entry at %" G_GUINT64_FORMAT, i, val);
+        if (val < min)
+          min = val;
+      }
+      continue;
+    }
+
+    if (!entry) {
+      GST_DEBUG_OBJECT (avi, "no position for stream %d, assuming at start", i);
+      stream->current_entry = 0;
+      stream->current_total = 0;
+      continue;
+    }
+
+    val = stream->index[index].offset - 8;
+    GST_DEBUG_OBJECT (avi, "stream %d, next entry at %" G_GUINT64_FORMAT, i,
+        val);
+
+    stream->current_total = stream->index[index].total;
+    stream->current_entry = index;
+  }
+
+  return min;
+}
+
+#define GST_AVI_SEEK_PUSH_DISPLACE     (4 * GST_SECOND)
+
 static gboolean
 gst_avi_demux_handle_sink_event (GstPad * pad, GstEvent * event)
 {
@@ -599,9 +755,134 @@ gst_avi_demux_handle_sink_event (GstPad * pad, GstEvent * event)
 
   switch (GST_EVENT_TYPE (event)) {
     case GST_EVENT_NEWSEGMENT:
-      /* Drop NEWSEGMENT events, new ones are generated later */
+    {
+      GstFormat format;
+      gdouble rate, arate;
+      gint64 start, stop, time, offset = 0;
+      gboolean update;
+      GstSegment segment;
+
+      /* some debug output */
+      gst_segment_init (&segment, GST_FORMAT_UNDEFINED);
+      gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
+          &start, &stop, &time);
+      gst_segment_set_newsegment_full (&segment, update, rate, arate, format,
+          start, stop, time);
+      GST_DEBUG_OBJECT (avi,
+          "received format %d newsegment %" GST_SEGMENT_FORMAT, format,
+          &segment);
+
+      /* chain will send initial newsegment after pads have been added */
+      if (avi->state != GST_AVI_DEMUX_MOVI) {
+        GST_DEBUG_OBJECT (avi, "still starting, eating event");
+        goto exit;
+      }
+
+      /* we only expect a BYTE segment, e.g. following a seek */
+      if (format != GST_FORMAT_BYTES) {
+        GST_DEBUG_OBJECT (avi, "unsupported segment format, ignoring");
+        goto exit;
+      }
+
+      if (avi->have_index) {
+        GstAviIndexEntry *entry;
+        guint i = 0, index = 0, k = 0;
+        GstAviStream *stream;
+
+        /* compensate chunk header, stored index offset points after header */
+        start += 8;
+        /* find which stream we're on */
+        do {
+          stream = &avi->stream[i];
+
+          /* find the index for start bytes offset */
+          entry = gst_util_array_binary_search (stream->index,
+              stream->idx_n, sizeof (GstAviIndexEntry),
+              (GCompareDataFunc) gst_avi_demux_index_entry_offset_search,
+              GST_SEARCH_MODE_AFTER, &start, NULL);
+
+          if (entry == NULL)
+            continue;
+          index = entry - stream->index;
+
+          /* we are on the stream with a chunk start offset closest to start */
+          if (!offset || stream->index[index].offset < offset) {
+            offset = stream->index[index].offset;
+            k = i;
+          }
+          /* exact match needs no further searching */
+          if (stream->index[index].offset == start)
+            break;
+        } while (++i < avi->num_streams);
+        start -= 8;
+        offset -= 8;
+        stream = &avi->stream[k];
+
+        /* so we have no idea what is to come, or where we are */
+        if (!offset) {
+          GST_WARNING_OBJECT (avi, "insufficient index data, forcing EOS");
+          goto eos;
+        }
+
+        /* get the ts corresponding to start offset bytes for the stream */
+        gst_avi_demux_get_buffer_info (avi, stream, index,
+            (GstClockTime *) & time, NULL, NULL, NULL);
+      } else if (avi->element_index) {
+        GstIndexEntry *entry;
+
+        /* Let's check if we have an index entry for this position */
+        entry = gst_index_get_assoc_entry (avi->element_index, avi->index_id,
+            GST_INDEX_LOOKUP_AFTER, GST_ASSOCIATION_FLAG_NONE,
+            GST_FORMAT_BYTES, start);
+
+        /* we can not go where we have not yet been before ... */
+        if (!entry) {
+          GST_WARNING_OBJECT (avi, "insufficient index data, forcing EOS");
+          goto eos;
+        }
+
+        gst_index_entry_assoc_map (entry, GST_FORMAT_TIME, &time);
+        gst_index_entry_assoc_map (entry, GST_FORMAT_BYTES, &offset);
+      } else {
+        GST_WARNING_OBJECT (avi, "no index data, forcing EOS");
+        goto eos;
+      }
+
+      stop = GST_CLOCK_TIME_NONE;
+
+      /* set up segment and send downstream */
+      gst_segment_set_newsegment_full (&avi->segment, update, rate, arate,
+          GST_FORMAT_TIME, time, stop, time);
+      GST_DEBUG_OBJECT (avi, "Pushing newseg update %d, rate %g, "
+          "applied rate %g, format %d, start %" G_GINT64_FORMAT ", "
+          "stop %" G_GINT64_FORMAT, update, rate, arate, GST_FORMAT_TIME,
+          time, stop);
+      gst_avi_demux_push_event (avi,
+          gst_event_new_new_segment_full (update, rate, arate, GST_FORMAT_TIME,
+              time, stop, time));
+
+      GST_DEBUG_OBJECT (avi, "next chunk expected at %" G_GINT64_FORMAT, start);
+
+      /* adjust state for streaming thread accordingly */
+      if (avi->have_index)
+        gst_avi_demux_seek_streams_index (avi, offset, FALSE);
+      else
+        gst_avi_demux_seek_streams (avi, offset, FALSE);
+
+      /* set up streaming thread */
+      g_assert (offset >= start);
+      avi->offset = start;
+      avi->todrop = offset - start;
+
+    exit:
       gst_event_unref (event);
+      res = TRUE;
       break;
+    eos:
+      /* set up for EOS */
+      avi->have_eos = TRUE;
+      goto exit;
+    }
     case GST_EVENT_EOS:
     {
       if (avi->state != GST_AVI_DEMUX_MOVI) {
@@ -614,6 +895,18 @@ gst_avi_demux_handle_sink_event (GstPad * pad, GstEvent * event)
       }
       break;
     }
+    case GST_EVENT_FLUSH_STOP:
+    {
+      gint i;
+
+      gst_adapter_clear (avi->adapter);
+      avi->have_eos = FALSE;
+      for (i = 0; i < avi->num_streams; i++) {
+        avi->stream[i].last_flow = GST_FLOW_OK;
+        avi->stream[i].discont = TRUE;
+      }
+      /* fall through to default case so that the event gets passed downstream */
+    }
     default:
       res = gst_pad_event_default (pad, event);
       break;
@@ -635,13 +928,12 @@ gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event)
 
   switch (GST_EVENT_TYPE (event)) {
     case GST_EVENT_SEEK:
-      /* handle seeking only in pull mode */
       if (!avi->streaming) {
         res = gst_avi_demux_handle_seek (avi, pad, event);
-        gst_event_unref (event);
       } else {
-        res = gst_pad_event_default (pad, event);
+        res = gst_avi_demux_handle_seek_push (avi, pad, event);
       }
+      gst_event_unref (event);
       break;
     case GST_EVENT_QOS:
     case GST_EVENT_NAVIGATION:
@@ -775,7 +1067,7 @@ gst_avi_demux_parse_file_header (GstElement * element, GstBuffer * buf)
     goto not_avi;
 
   stamp = gst_util_get_timestamp () - stamp;
-  GST_DEBUG_OBJECT (element, "parsing header %" GST_TIME_FORMAT,
+  GST_DEBUG_OBJECT (element, "header parsing took %" GST_TIME_FORMAT,
       GST_TIME_ARGS (stamp));
 
   return TRUE;
@@ -802,7 +1094,7 @@ gst_avi_demux_stream_init_push (GstAviDemux * avi)
     tmp = gst_adapter_take_buffer (avi->adapter, 12);
 
     GST_DEBUG ("Parsing avi header");
-    if (!gst_avi_demux_parse_file_header (GST_ELEMENT (avi), tmp)) {
+    if (!gst_avi_demux_parse_file_header (GST_ELEMENT_CAST (avi), tmp)) {
       return GST_FLOW_ERROR;
     }
     GST_DEBUG ("header ok");
@@ -841,10 +1133,9 @@ wrong_header:
 }
 
 /* AVI header handling */
-
 /*
  * gst_avi_demux_parse_avih:
- * @element: caller element (used for errors/debug).
+ * @avi: caller element (used for errors/debug).
  * @buf: input data to be used for parsing.
  * @avih: pointer to structure (filled in by function) containing
  *        stream information (such as flags, number of streams, etc.).
@@ -856,7 +1147,7 @@ wrong_header:
  *          (fatal).
  */
 static gboolean
-gst_avi_demux_parse_avih (GstElement * element,
+gst_avi_demux_parse_avih (GstAviDemux * avi,
     GstBuffer * buf, gst_riff_avih ** _avih)
 {
   gst_riff_avih *avih;
@@ -887,36 +1178,45 @@ gst_avi_demux_parse_avih (GstElement * element,
 #endif
 
   /* debug stuff */
-  GST_INFO_OBJECT (element, "avih tag found:");
-  GST_INFO_OBJECT (element, " us_frame    %u", avih->us_frame);
-  GST_INFO_OBJECT (element, " max_bps     %u", avih->max_bps);
-  GST_INFO_OBJECT (element, " pad_gran    %u", avih->pad_gran);
-  GST_INFO_OBJECT (element, " flags       0x%08x", avih->flags);
-  GST_INFO_OBJECT (element, " tot_frames  %u", avih->tot_frames);
-  GST_INFO_OBJECT (element, " init_frames %u", avih->init_frames);
-  GST_INFO_OBJECT (element, " streams     %u", avih->streams);
-  GST_INFO_OBJECT (element, " bufsize     %u", avih->bufsize);
-  GST_INFO_OBJECT (element, " width       %u", avih->width);
-  GST_INFO_OBJECT (element, " height      %u", avih->height);
-  GST_INFO_OBJECT (element, " scale       %u", avih->scale);
-  GST_INFO_OBJECT (element, " rate        %u", avih->rate);
-  GST_INFO_OBJECT (element, " start       %u", avih->start);
-  GST_INFO_OBJECT (element, " length      %u", avih->length);
+  GST_INFO_OBJECT (avi, "avih tag found:");
+  GST_INFO_OBJECT (avi, " us_frame    %u", avih->us_frame);
+  GST_INFO_OBJECT (avi, " max_bps     %u", avih->max_bps);
+  GST_INFO_OBJECT (avi, " pad_gran    %u", avih->pad_gran);
+  GST_INFO_OBJECT (avi, " flags       0x%08x", avih->flags);
+  GST_INFO_OBJECT (avi, " tot_frames  %u", avih->tot_frames);
+  GST_INFO_OBJECT (avi, " init_frames %u", avih->init_frames);
+  GST_INFO_OBJECT (avi, " streams     %u", avih->streams);
+  GST_INFO_OBJECT (avi, " bufsize     %u", avih->bufsize);
+  GST_INFO_OBJECT (avi, " width       %u", avih->width);
+  GST_INFO_OBJECT (avi, " height      %u", avih->height);
+  GST_INFO_OBJECT (avi, " scale       %u", avih->scale);
+  GST_INFO_OBJECT (avi, " rate        %u", avih->rate);
+  GST_INFO_OBJECT (avi, " start       %u", avih->start);
+  GST_INFO_OBJECT (avi, " length      %u", avih->length);
 
   *_avih = avih;
   gst_buffer_unref (buf);
 
+  if (avih->us_frame != 0 && avih->tot_frames != 0)
+    avi->duration =
+        (guint64) avih->us_frame * (guint64) avih->tot_frames * 1000;
+  else
+    avi->duration = GST_CLOCK_TIME_NONE;
+
+  GST_INFO_OBJECT (avi, " header duration  %" GST_TIME_FORMAT,
+      GST_TIME_ARGS (avi->duration));
+
   return TRUE;
 
   /* ERRORS */
 no_buffer:
   {
-    GST_ELEMENT_ERROR (element, STREAM, DEMUX, (NULL), ("No buffer"));
+    GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), ("No buffer"));
     return FALSE;
   }
 avih_too_small:
   {
-    GST_ELEMENT_ERROR (element, STREAM, DEMUX, (NULL),
+    GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
         ("Too small avih (%d available, %d needed)",
             GST_BUFFER_SIZE (buf), (int) sizeof (gst_riff_avih)));
     gst_buffer_unref (buf);
@@ -968,11 +1268,14 @@ gst_avi_demux_parse_superindex (GstAviDemux * avi,
   }
   num = GST_READ_UINT32_LE (&data[4]);
 
+  GST_DEBUG_OBJECT (avi, "got %d indexes", num);
+
   indexes = g_new (guint64, num + 1);
   for (i = 0; i < num; i++) {
     if (size < 24 + bpe * (i + 1))
       break;
     indexes[i] = GST_READ_UINT64_LE (&data[24 + bpe * i]);
+    GST_DEBUG_OBJECT (avi, "index %d at %" G_GUINT64_FORMAT, i, indexes[i]);
   }
   indexes[i] = GST_BUFFER_OFFSET_NONE;
   *_indexes = indexes;
@@ -1088,7 +1391,7 @@ gst_avi_demux_get_buffer_info (GstAviDemux * avi, GstAviStream * stream,
         *ts_end = avi_stream_convert_frames_to_time_unchecked (stream,
             entry_n + 1);
     }
-  } else {
+  } else if (stream->strh->type == GST_RIFF_FCC_auds) {
     /* constant rate stream */
     if (timestamp)
       *timestamp =
@@ -1114,13 +1417,16 @@ gst_avi_demux_get_buffer_info (GstAviDemux * avi, GstAviStream * stream,
 
 /* collect and debug stats about the indexes for all streams.
  * This method is also responsible for filling in the stream duration
- * as measured by the amount of index entries. */
-static void
+ * as measured by the amount of index entries.
+ *
+ * Returns TRUE if the index is not empty, else FALSE */
+static gboolean
 gst_avi_demux_do_index_stats (GstAviDemux * avi)
 {
+  guint total_idx = 0;
   guint i;
 #ifndef GST_DISABLE_GST_DEBUG
-  guint total_idx = 0, total_max = 0;
+  guint total_max = 0;
 #endif
 
   /* get stream stats now */
@@ -1139,8 +1445,8 @@ gst_avi_demux_do_index_stats (GstAviDemux * avi)
     gst_avi_demux_get_buffer_info (avi, stream, stream->idx_n - 1,
         NULL, &stream->idx_duration, NULL, NULL);
 
-#ifndef GST_DISABLE_GST_DEBUG
     total_idx += stream->idx_n;
+#ifndef GST_DISABLE_GST_DEBUG
     total_max += stream->idx_max;
 #endif
     GST_INFO_OBJECT (avi, "Stream %d, dur %" GST_TIME_FORMAT ", %6u entries, "
@@ -1150,12 +1456,18 @@ gst_avi_demux_do_index_stats (GstAviDemux * avi)
         (guint) (stream->idx_n * sizeof (GstAviIndexEntry)),
         (guint) (stream->idx_max * sizeof (GstAviIndexEntry)));
   }
-#ifndef GST_DISABLE_GST_DEBUG
   total_idx *= sizeof (GstAviIndexEntry);
+#ifndef GST_DISABLE_GST_DEBUG
   total_max *= sizeof (GstAviIndexEntry);
 #endif
   GST_INFO_OBJECT (avi, "%u bytes for index vs %u ideally, %u wasted",
       total_max, total_idx, total_max - total_idx);
+
+  if (total_idx == 0) {
+    GST_WARNING_OBJECT (avi, "Index is empty !");
+    return FALSE;
+  }
+  return TRUE;
 }
 
 /*
@@ -1281,53 +1593,90 @@ out_of_mem:
   }
 }
 
-#if 0
+/*
+ * Create and push a flushing seek event upstream
+ */
+static gboolean
+perform_seek_to_offset (GstAviDemux * demux, guint64 offset)
+{
+  GstEvent *event;
+  gboolean res = 0;
+
+  GST_DEBUG_OBJECT (demux, "Seeking to %" G_GUINT64_FORMAT, offset);
+
+  event =
+      gst_event_new_seek (1.0, GST_FORMAT_BYTES,
+      GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET, offset,
+      GST_SEEK_TYPE_NONE, -1);
+
+  res = gst_pad_push_event (demux->sinkpad, event);
+
+  if (res)
+    demux->offset = offset;
+  return res;
+}
+
 /*
  * Read AVI index when streaming
  */
-static void
+static gboolean
 gst_avi_demux_read_subindexes_push (GstAviDemux * avi)
 {
   guint32 tag = 0, size;
   GstBuffer *buf = NULL;
-  gint i, n;
+  guint odml_stream;
 
   GST_DEBUG_OBJECT (avi, "read subindexes for %d streams", avi->num_streams);
 
-  for (n = 0; n < avi->num_streams; n++) {
-    GstAviStream *stream = &avi->stream[n];
+  if (avi->odml_subidxs[avi->odml_subidx] != avi->offset)
+    return FALSE;
 
-    for (i = 0; stream->indexes[i] != GST_BUFFER_OFFSET_NONE; i++) {
-      if (!gst_avi_demux_peek_chunk (avi, &tag, &size))
-        continue;
-      else if ((tag != GST_MAKE_FOURCC ('i', 'x', '0' + stream->num / 10,
-                  '0' + stream->num % 10)) &&
-          (tag != GST_MAKE_FOURCC ('0' + stream->num / 10,
-                  '0' + stream->num % 10, 'i', 'x'))) {
-        GST_WARNING_OBJECT (avi, "Not an ix## chunk (%" GST_FOURCC_FORMAT ")",
-            GST_FOURCC_ARGS (tag));
-        continue;
-      }
+  if (!gst_avi_demux_peek_chunk (avi, &tag, &size))
+    return TRUE;
 
-      avi->offset += 8 + GST_ROUND_UP_2 (size);
+  /* this is the ODML chunk we expect */
+  odml_stream = avi->odml_stream;
 
-      buf = gst_buffer_new ();
-      GST_BUFFER_DATA (buf) = gst_adapter_take (avi->adapter, size);
-      GST_BUFFER_SIZE (buf) = size;
+  if ((tag != GST_MAKE_FOURCC ('i', 'x', '0' + odml_stream / 10,
+              '0' + odml_stream % 10)) &&
+      (tag != GST_MAKE_FOURCC ('0' + odml_stream / 10,
+              '0' + odml_stream % 10, 'i', 'x'))) {
+    GST_WARNING_OBJECT (avi, "Not an ix## chunk (%" GST_FOURCC_FORMAT ")",
+        GST_FOURCC_ARGS (tag));
+    return FALSE;
+  }
 
-      if (!gst_avi_demux_parse_subindex (avi, stream, buf))
-        continue;
-    }
+  avi->offset += 8 + GST_ROUND_UP_2 (size);
+  /* flush chunk header so we get just the 'size' payload data */
+  gst_adapter_flush (avi->adapter, 8);
+  buf = gst_adapter_take_buffer (avi->adapter, size);
 
-    g_free (stream->indexes);
-    stream->indexes = NULL;
+  if (!gst_avi_demux_parse_subindex (avi, &avi->stream[odml_stream], buf))
+    return FALSE;
+
+  /* we parsed the index, go to next subindex */
+  avi->odml_subidx++;
+
+  if (avi->odml_subidxs[avi->odml_subidx] == GST_BUFFER_OFFSET_NONE) {
+    /* we reached the end of the indexes for this stream, move to the next
+     * stream to handle the first index */
+    avi->odml_stream++;
+    avi->odml_subidx = 0;
+
+    if (avi->odml_stream < avi->num_streams) {
+      /* there are more indexes */
+      avi->odml_subidxs = avi->stream[avi->odml_stream].indexes;
+    } else {
+      /* we're done, get stream stats now */
+      avi->have_index = gst_avi_demux_do_index_stats (avi);
+
+      return TRUE;
+    }
   }
-  /* get stream stats now */
-  gst_avi_demux_do_index_stats (avi);
 
-  avi->have_index = TRUE;
+  /* seek to next index */
+  return perform_seek_to_offset (avi, avi->odml_subidxs[avi->odml_subidx]);
 }
-#endif
 
 /*
  * Read AVI index
@@ -1344,6 +1693,9 @@ gst_avi_demux_read_subindexes_pull (GstAviDemux * avi)
   for (n = 0; n < avi->num_streams; n++) {
     GstAviStream *stream = &avi->stream[n];
 
+    if (stream->indexes == NULL)
+      continue;
+
     for (i = 0; stream->indexes[i] != GST_BUFFER_OFFSET_NONE; i++) {
       if (gst_riff_read_chunk (GST_ELEMENT_CAST (avi), avi->sinkpad,
               &stream->indexes[i], &tag, &buf) != GST_FLOW_OK)
@@ -1368,9 +1720,7 @@ gst_avi_demux_read_subindexes_pull (GstAviDemux * avi)
     stream->indexes = NULL;
   }
   /* get stream stats now */
-  gst_avi_demux_do_index_stats (avi);
-
-  avi->have_index = TRUE;
+  avi->have_index = gst_avi_demux_do_index_stats (avi);
 }
 
 /*
@@ -1494,6 +1844,31 @@ too_small:
   }
 }
 
+static void
+gst_avi_demux_expose_streams (GstAviDemux * avi, gboolean force)
+{
+  guint i;
+
+  GST_DEBUG_OBJECT (avi, "force : %d", force);
+
+  for (i = 0; i < avi->num_streams; i++) {
+    GstAviStream *stream = &avi->stream[i];
+
+    if (force || stream->idx_n != 0) {
+      GST_LOG_OBJECT (avi, "Added pad %s with caps %" GST_PTR_FORMAT,
+          GST_PAD_NAME (stream->pad), GST_PAD_CAPS (stream->pad));
+      gst_element_add_pad ((GstElement *) avi, stream->pad);
+      stream->exposed = TRUE;
+      if (avi->main_stream == -1)
+        avi->main_stream = i;
+    } else {
+      GST_WARNING_OBJECT (avi, "Stream #%d doesn't have any entry, removing it",
+          i);
+      gst_avi_demux_reset_stream (avi, stream);
+    }
+  }
+}
+
 /*
  * gst_avi_demux_parse_stream:
  * @avi: calling element (used for debugging/errors).
@@ -1617,14 +1992,36 @@ gst_avi_demux_parse_stream (GstAviDemux * avi, GstBuffer * buf)
             GST_DEBUG_OBJECT (element, "marking video as VBR, res %d", res);
             break;
           case GST_RIFF_FCC_auds:
-            stream->is_vbr = (stream->strh->samplesize == 0)
-                && stream->strh->scale > 1;
             res =
                 gst_riff_parse_strf_auds (element, sub, &stream->strf.auds,
                 &stream->extradata);
+            stream->is_vbr = (stream->strh->samplesize == 0)
+                && stream->strh->scale > 1
+                && stream->strf.auds->blockalign != 1;
             sub = NULL;
             GST_DEBUG_OBJECT (element, "marking audio as VBR:%d, res %d",
                 stream->is_vbr, res);
+            /* we need these or we have no way to come up with timestamps */
+            if ((!stream->is_vbr && !stream->strf.auds->av_bps) ||
+                (stream->is_vbr && (!stream->strh->scale ||
+                        !stream->strh->rate))) {
+              GST_WARNING_OBJECT (element,
+                  "invalid audio header, ignoring stream");
+              goto fail;
+            }
+            /* some more sanity checks */
+            if (stream->is_vbr) {
+              if (stream->strf.auds->blockalign <= 4) {
+                /* that would mean (too) many frames per chunk,
+                 * so not likely set as expected */
+                GST_DEBUG_OBJECT (element,
+                    "suspicious blockalign %d for VBR audio; "
+                    "overriding to 1 frame per chunk",
+                    stream->strf.auds->blockalign);
+                /* this should top any likely value */
+                stream->strf.auds->blockalign = (1 << 12);
+              }
+            }
             break;
           case GST_RIFF_FCC_iavs:
             stream->is_vbr = TRUE;
@@ -1698,6 +2095,9 @@ gst_avi_demux_parse_stream (GstAviDemux * avi, GstBuffer * buf)
         }
         GST_DEBUG_OBJECT (avi, "stream name: %s", stream->name);
         break;
+      case GST_RIFF_IDIT:
+        gst_avi_demux_parse_idit (avi, sub);
+        break;
       default:
         if (tag == GST_MAKE_FOURCC ('i', 'n', 'd', 'x') ||
             tag == GST_MAKE_FOURCC ('i', 'x', '0' + avi->num_streams / 10,
@@ -1712,6 +2112,7 @@ gst_avi_demux_parse_stream (GstAviDemux * avi, GstBuffer * buf)
             "Unknown stream header tag %" GST_FOURCC_FORMAT ", ignoring",
             GST_FOURCC_ARGS (tag));
         /* fall-through */
+      case GST_RIFF_TAG_JUNQ:
       case GST_RIFF_TAG_JUNK:
         break;
     }
@@ -1844,6 +2245,10 @@ gst_avi_demux_parse_stream (GstAviDemux * avi, GstBuffer * buf)
       GST_DEBUG_FUNCPTR (gst_avi_demux_src_convert));
 #endif
 
+  if (avi->element_index)
+    gst_index_get_writer_id (avi->element_index, GST_OBJECT_CAST (stream->pad),
+        &stream->index_id);
+
   stream->num = avi->num_streams;
 
   stream->start_entry = 0;
@@ -1868,10 +2273,6 @@ gst_avi_demux_parse_stream (GstAviDemux * avi, GstBuffer * buf)
 
   gst_pad_set_caps (pad, caps);
   gst_pad_set_active (pad, TRUE);
-  gst_element_add_pad (GST_ELEMENT (avi), pad);
-
-  GST_LOG_OBJECT (element, "Added pad %s with caps %" GST_PTR_FORMAT,
-      GST_PAD_NAME (pad), caps);
   gst_caps_unref (caps);
 
   /* make tags */
@@ -1953,6 +2354,7 @@ gst_avi_demux_parse_odml (GstAviDemux * avi, GstBuffer * buf)
             "Unknown tag %" GST_FOURCC_FORMAT " in ODML header",
             GST_FOURCC_ARGS (tag));
         /* fall-through */
+      case GST_RIFF_TAG_JUNQ:
       case GST_RIFF_TAG_JUNK:
       next:
         /* skip and move to next chunk */
@@ -1971,7 +2373,7 @@ gst_avi_demux_parse_odml (GstAviDemux * avi, GstBuffer * buf)
 static guint
 gst_avi_demux_index_last (GstAviDemux * avi, GstAviStream * stream)
 {
-  return stream->idx_n - 1;
+  return stream->idx_n;
 }
 
 /* find a previous entry in the index with the given flags */
@@ -2195,15 +2597,12 @@ gst_avi_demux_parse_index (GstAviDemux * avi, GstBuffer * buf)
   gst_buffer_unref (buf);
 
   /* get stream stats now */
-  gst_avi_demux_do_index_stats (avi);
+  avi->have_index = gst_avi_demux_do_index_stats (avi);
 
   stamp = gst_util_get_timestamp () - stamp;
-  GST_DEBUG_OBJECT (avi, "parsing index %" GST_TIME_FORMAT,
+  GST_DEBUG_OBJECT (avi, "index parsing took %" GST_TIME_FORMAT,
       GST_TIME_ARGS (stamp));
 
-  /* we have an index now */
-  avi->have_index = TRUE;
-
   return TRUE;
 
   /* ERRORS */
@@ -2330,46 +2729,129 @@ zero_index:
 }
 
 /*
- * gst_avi_demux_peek_tag:
+ * gst_avi_demux_stream_index_push:
+ * @avi: avi demuxer object.
  *
- * Returns the tag and size of the next chunk
+ * Read index.
  */
-static GstFlowReturn
-gst_avi_demux_peek_tag (GstAviDemux * avi, guint64 offset, guint32 * tag,
-    guint * size)
+static void
+gst_avi_demux_stream_index_push (GstAviDemux * avi)
 {
-  GstFlowReturn res = GST_FLOW_OK;
-  GstBuffer *buf = NULL;
-  guint bufsize;
-  guint8 *bufdata;
+  guint64 offset = avi->idx1_offset;
+  GstBuffer *buf;
+  guint32 tag;
+  guint32 size;
 
-  res = gst_pad_pull_range (avi->sinkpad, offset, 8, &buf);
-  if (res != GST_FLOW_OK)
-    goto pull_failed;
+  GST_DEBUG ("demux stream index at offset %" G_GUINT64_FORMAT, offset);
 
-  bufsize = GST_BUFFER_SIZE (buf);
-  if (bufsize != 8)
-    goto wrong_size;
+  /* get chunk information */
+  if (!gst_avi_demux_peek_chunk (avi, &tag, &size))
+    return;
 
-  bufdata = GST_BUFFER_DATA (buf);
+  /* check tag first before blindly trying to read 'size' bytes */
+  if (tag == GST_RIFF_TAG_LIST) {
+    /* this is the movi tag */
+    GST_DEBUG_OBJECT (avi, "skip LIST chunk, size %" G_GUINT32_FORMAT,
+        (8 + GST_ROUND_UP_2 (size)));
+    avi->idx1_offset = offset + 8 + GST_ROUND_UP_2 (size);
+    /* issue seek to allow chain function to handle it and return! */
+    perform_seek_to_offset (avi, avi->idx1_offset);
+    return;
+  }
 
-  *tag = GST_READ_UINT32_LE (bufdata);
-  *size = GST_READ_UINT32_LE (bufdata + 4);
+  if (tag != GST_RIFF_TAG_idx1)
+    goto no_index;
 
-  GST_LOG_OBJECT (avi, "Tag[%" GST_FOURCC_FORMAT "] (size:%d) %"
-      G_GINT64_FORMAT " -- %" G_GINT64_FORMAT, GST_FOURCC_ARGS (*tag),
-      *size, offset + 8, offset + 8 + (gint64) * size);
+  GST_DEBUG ("index found at offset %" G_GUINT64_FORMAT, offset);
 
-done:
-  gst_buffer_unref (buf);
+  /* flush chunk header */
+  gst_adapter_flush (avi->adapter, 8);
+  /* read chunk payload */
+  buf = gst_adapter_take_buffer (avi->adapter, size);
+  if (!buf)
+    goto pull_failed;
+  /* advance offset */
+  offset += 8 + GST_ROUND_UP_2 (size);
 
-  return res;
+  GST_DEBUG ("will parse index chunk size %u for tag %"
+      GST_FOURCC_FORMAT, GST_BUFFER_SIZE (buf), GST_FOURCC_ARGS (tag));
 
-  /* ERRORS */
-pull_failed:
+  avi->offset = avi->first_movi_offset;
+  gst_avi_demux_parse_index (avi, buf);
+
+#ifndef GST_DISABLE_GST_DEBUG
+  /* debug our indexes */
   {
-    GST_DEBUG_OBJECT (avi, "pull_ranged returned %s", gst_flow_get_name (res));
-    return res;
+    gint i;
+    GstAviStream *stream;
+
+    for (i = 0; i < avi->num_streams; i++) {
+      stream = &avi->stream[i];
+      GST_DEBUG_OBJECT (avi, "stream %u: %u frames, %" G_GINT64_FORMAT " bytes",
+          i, stream->idx_n, stream->total_bytes);
+    }
+  }
+#endif
+  return;
+
+  /* ERRORS */
+pull_failed:
+  {
+    GST_DEBUG_OBJECT (avi,
+        "taking data from adapter failed: pos=%" G_GUINT64_FORMAT " size=%u",
+        offset, size);
+    return;
+  }
+no_index:
+  {
+    GST_WARNING_OBJECT (avi,
+        "No index data (idx1) after movi chunk, but %" GST_FOURCC_FORMAT,
+        GST_FOURCC_ARGS (tag));
+    return;
+  }
+}
+
+/*
+ * gst_avi_demux_peek_tag:
+ *
+ * Returns the tag and size of the next chunk
+ */
+static GstFlowReturn
+gst_avi_demux_peek_tag (GstAviDemux * avi, guint64 offset, guint32 * tag,
+    guint * size)
+{
+  GstFlowReturn res = GST_FLOW_OK;
+  GstBuffer *buf = NULL;
+  guint bufsize;
+  guint8 *bufdata;
+
+  res = gst_pad_pull_range (avi->sinkpad, offset, 8, &buf);
+  if (res != GST_FLOW_OK)
+    goto pull_failed;
+
+  bufsize = GST_BUFFER_SIZE (buf);
+  if (bufsize != 8)
+    goto wrong_size;
+
+  bufdata = GST_BUFFER_DATA (buf);
+
+  *tag = GST_READ_UINT32_LE (bufdata);
+  *size = GST_READ_UINT32_LE (bufdata + 4);
+
+  GST_LOG_OBJECT (avi, "Tag[%" GST_FOURCC_FORMAT "] (size:%d) %"
+      G_GINT64_FORMAT " -- %" G_GINT64_FORMAT, GST_FOURCC_ARGS (*tag),
+      *size, offset + 8, offset + 8 + (gint64) * size);
+
+done:
+  gst_buffer_unref (buf);
+
+  return res;
+
+  /* ERRORS */
+pull_failed:
+  {
+    GST_DEBUG_OBJECT (avi, "pull_ranged returned %s", gst_flow_get_name (res));
+    return res;
   }
 wrong_size:
   {
@@ -2474,11 +2956,9 @@ gst_avi_demux_stream_scan (GstAviDemux * avi)
       break;
     }
   }
-  /* collect stats */
-  gst_avi_demux_do_index_stats (avi);
 
-  /* we have an index now */
-  avi->have_index = TRUE;
+  /* collect stats */
+  avi->have_index = gst_avi_demux_do_index_stats (avi);
 
   return TRUE;
 
@@ -2508,7 +2988,7 @@ gst_avi_demux_calculate_durations_from_index (GstAviDemux * avi)
     gst_riff_strh *strh;
 
     stream = &avi->stream[i];
-    if (G_UNLIKELY (!stream || !(strh = stream->strh)))
+    if (G_UNLIKELY (!stream || !stream->idx_n || !(strh = stream->strh)))
       continue;
 
     /* get header duration for the stream */
@@ -2525,11 +3005,14 @@ gst_avi_demux_calculate_durations_from_index (GstAviDemux * avi)
       /* fall back to header info to calculate a duration */
       duration = hduration;
     }
+    GST_INFO ("Setting duration of stream #%d to %" GST_TIME_FORMAT,
+        i, GST_TIME_ARGS (duration));
     /* set duration for the stream */
     stream->duration = duration;
 
     /* find total duration */
-    if (total == GST_CLOCK_TIME_NONE || duration > total)
+    if (total == GST_CLOCK_TIME_NONE ||
+        (GST_CLOCK_TIME_IS_VALID (duration) && duration > total))
       total = duration;
   }
 
@@ -2579,6 +3062,44 @@ gst_avi_demux_push_event (GstAviDemux * avi, GstEvent * event)
   return result;
 }
 
+static void
+gst_avi_demux_check_seekability (GstAviDemux * avi)
+{
+  GstQuery *query;
+  gboolean seekable = FALSE;
+  gint64 start = -1, stop = -1;
+
+  query = gst_query_new_seeking (GST_FORMAT_BYTES);
+  if (!gst_pad_peer_query (avi->sinkpad, query)) {
+    GST_DEBUG_OBJECT (avi, "seeking query failed");
+    goto done;
+  }
+
+  gst_query_parse_seeking (query, NULL, &seekable, &start, &stop);
+
+  /* try harder to query upstream size if we didn't get it the first time */
+  if (seekable && stop == -1) {
+    GstFormat fmt = GST_FORMAT_BYTES;
+
+    GST_DEBUG_OBJECT (avi, "doing duration query to fix up unset stop");
+    gst_pad_query_peer_duration (avi->sinkpad, &fmt, &stop);
+  }
+
+  /* if upstream doesn't know the size, it's likely that it's not seekable in
+   * practice even if it technically may be seekable */
+  if (seekable && (start != 0 || stop <= start)) {
+    GST_DEBUG_OBJECT (avi, "seekable but unknown start/stop -> disable");
+    seekable = FALSE;
+  }
+
+done:
+  GST_INFO_OBJECT (avi, "seekable: %d (%" G_GUINT64_FORMAT " - %"
+      G_GUINT64_FORMAT ")", seekable, start, stop);
+  avi->seekable = seekable;
+
+  gst_query_unref (query);
+}
+
 /*
  * Read AVI headers when streaming
  */
@@ -2593,6 +3114,8 @@ gst_avi_demux_stream_header_push (GstAviDemux * avi)
   GstBuffer *buf = NULL, *sub = NULL;
   guint offset = 4;
   gint64 stop;
+  gint i;
+  GstTagList *tags = NULL;
 
   GST_DEBUG ("Reading and parsing avi headers: %d", avi->header_state);
 
@@ -2618,19 +3141,20 @@ gst_avi_demux_stream_header_push (GstAviDemux * avi)
         GST_DEBUG ("'hdrl' LIST tag found. Parsing next chunk");
 
         /* the hdrl starts with a 'avih' header */
-        if (!gst_riff_parse_chunk (GST_ELEMENT (avi), buf, &offset, &tag, &sub))
+        if (!gst_riff_parse_chunk (GST_ELEMENT_CAST (avi), buf, &offset, &tag,
+                &sub))
           goto header_no_avih;
 
         if (tag != GST_RIFF_TAG_avih)
           goto header_no_avih;
 
-        if (!gst_avi_demux_parse_avih (GST_ELEMENT (avi), sub, &avi->avih))
+        if (!gst_avi_demux_parse_avih (avi, sub, &avi->avih))
           goto header_wrong_avih;
 
         GST_DEBUG_OBJECT (avi, "AVI header ok, 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,
+        while (gst_riff_parse_chunk (GST_ELEMENT_CAST (avi), buf, &offset, &tag,
                 &sub)) {
           /* sub can be NULL on empty tags */
           if (!sub)
@@ -2661,15 +3185,20 @@ gst_avi_demux_stream_header_push (GstAviDemux * avi)
                       GST_FOURCC_ARGS (GST_READ_UINT32_LE (GST_BUFFER_DATA
                               (sub))));
                   /* fall-through */
+                case GST_RIFF_TAG_JUNQ:
                 case GST_RIFF_TAG_JUNK:
                   goto next;
               }
               break;
+            case GST_RIFF_IDIT:
+              gst_avi_demux_parse_idit (avi, sub);
+              goto next;
             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_JUNQ:
             case GST_RIFF_TAG_JUNK:
             next:
               /* move to next chunk */
@@ -2712,7 +3241,10 @@ gst_avi_demux_stream_header_push (GstAviDemux * avi)
           switch (ltag) {
             case GST_RIFF_LIST_movi:
               gst_adapter_flush (avi->adapter, 12);
+              if (!avi->first_movi_offset)
+                avi->first_movi_offset = avi->offset;
               avi->offset += 12;
+              avi->idx1_offset = avi->offset + size - 4;
               goto skipping_done;
             case GST_RIFF_LIST_INFO:
               GST_DEBUG ("Found INFO chunk");
@@ -2725,8 +3257,16 @@ gst_avi_demux_stream_header_push (GstAviDemux * avi)
                   /* mind padding */
                   if (size & 1)
                     gst_adapter_flush (avi->adapter, 1);
-                  gst_riff_parse_info (GST_ELEMENT (avi), buf,
-                      &avi->globaltags);
+                  gst_riff_parse_info (GST_ELEMENT_CAST (avi), buf, &tags);
+                  if (tags) {
+                    if (avi->globaltags) {
+                      gst_tag_list_insert (avi->globaltags, tags,
+                          GST_TAG_MERGE_REPLACE);
+                    } else {
+                      avi->globaltags = tags;
+                    }
+                  }
+                  tags = NULL;
                   gst_buffer_unref (buf);
 
                   avi->offset += GST_ROUND_UP_2 (size) - 4;
@@ -2773,22 +3313,33 @@ skipping_done:
   GST_DEBUG ("Found movi chunk. Starting to stream data");
   avi->state = GST_AVI_DEMUX_MOVI;
 
+  /* no indexes in push mode, but it still sets some variables */
+  gst_avi_demux_calculate_durations_from_index (avi);
+
+  gst_avi_demux_expose_streams (avi, TRUE);
+
+  /* prepare all streams for index 0 */
+  for (i = 0; i < avi->num_streams; i++)
+    avi->stream[i].current_entry = 0;
+
   /* create initial NEWSEGMENT event */
   if ((stop = avi->segment.stop) == GST_CLOCK_TIME_NONE)
     stop = avi->segment.duration;
 
   GST_DEBUG_OBJECT (avi, "segment stop %" G_GINT64_FORMAT, stop);
 
-  if (avi->seek_event)
-    gst_event_unref (avi->seek_event);
-  avi->seek_event = gst_event_new_new_segment
-      (FALSE, avi->segment.rate, GST_FORMAT_TIME,
+  if (avi->seg_event)
+    gst_event_unref (avi->seg_event);
+  avi->seg_event = gst_event_new_new_segment_full
+      (FALSE, avi->segment.rate, avi->segment.applied_rate, GST_FORMAT_TIME,
       avi->segment.start, stop, avi->segment.time);
 
+  gst_avi_demux_check_seekability (avi);
+
   /* at this point we know all the streams and we can signal the no more
    * pads signal */
   GST_DEBUG_OBJECT (avi, "signaling no more pads");
-  gst_element_no_more_pads (GST_ELEMENT (avi));
+  gst_element_no_more_pads (GST_ELEMENT_CAST (avi));
 
   return GST_FLOW_OK;
 
@@ -2831,6 +3382,150 @@ header_wrong_avih:
   }
 }
 
+static void
+gst_avi_demux_add_date_tag (GstAviDemux * avi, gint y, gint m, gint d,
+    gint h, gint min, gint s)
+{
+  GDate *date;
+  GstDateTime *dt;
+
+  date = g_date_new_dmy (d, m, y);
+  if (!g_date_valid (date)) {
+    /* bogus date */
+    GST_WARNING_OBJECT (avi, "Refusing to add invalid date %d-%d-%d", y, m, d);
+    g_date_free (date);
+    return;
+  }
+
+  dt = gst_date_time_new_local_time (y, m, d, h, min, s);
+
+  if (avi->globaltags == NULL)
+    avi->globaltags = gst_tag_list_new ();
+
+  gst_tag_list_add (avi->globaltags, GST_TAG_MERGE_REPLACE, GST_TAG_DATE, date,
+      NULL);
+  g_date_free (date);
+  if (dt) {
+    gst_tag_list_add (avi->globaltags, GST_TAG_MERGE_REPLACE, GST_TAG_DATE_TIME,
+        dt, NULL);
+    gst_date_time_unref (dt);
+  }
+}
+
+static void
+gst_avi_demux_parse_idit_nums_only (GstAviDemux * avi, gchar * data)
+{
+  gint y, m, d;
+  gint ret;
+
+  ret = sscanf (data, "%d:%d:%d", &y, &m, &d);
+  if (ret != 3) {
+    GST_WARNING_OBJECT (avi, "Failed to parse IDIT tag");
+    return;
+  }
+  gst_avi_demux_add_date_tag (avi, y, m, d, 0, 0, 0);
+}
+
+static gint
+get_month_num (gchar * data, guint size)
+{
+  if (g_ascii_strncasecmp (data, "jan", 3) == 0) {
+    return 1;
+  } else if (g_ascii_strncasecmp (data, "feb", 3) == 0) {
+    return 2;
+  } else if (g_ascii_strncasecmp (data, "mar", 3) == 0) {
+    return 3;
+  } else if (g_ascii_strncasecmp (data, "apr", 3) == 0) {
+    return 4;
+  } else if (g_ascii_strncasecmp (data, "may", 3) == 0) {
+    return 5;
+  } else if (g_ascii_strncasecmp (data, "jun", 3) == 0) {
+    return 6;
+  } else if (g_ascii_strncasecmp (data, "jul", 3) == 0) {
+    return 7;
+  } else if (g_ascii_strncasecmp (data, "aug", 3) == 0) {
+    return 8;
+  } else if (g_ascii_strncasecmp (data, "sep", 3) == 0) {
+    return 9;
+  } else if (g_ascii_strncasecmp (data, "oct", 3) == 0) {
+    return 10;
+  } else if (g_ascii_strncasecmp (data, "nov", 3) == 0) {
+    return 11;
+  } else if (g_ascii_strncasecmp (data, "dec", 3) == 0) {
+    return 12;
+  }
+
+  return 0;
+}
+
+static void
+gst_avi_demux_parse_idit_text (GstAviDemux * avi, gchar * data)
+{
+  gint year, month, day;
+  gint hour, min, sec;
+  gint ret;
+  gchar weekday[4];
+  gchar monthstr[4];
+
+  ret = sscanf (data, "%3s %3s %d %d:%d:%d %d", weekday, monthstr, &day, &hour,
+      &min, &sec, &year);
+  if (ret != 7) {
+    GST_WARNING_OBJECT (avi, "Failed to parse IDIT tag");
+    return;
+  }
+  month = get_month_num (monthstr, strlen (monthstr));
+  gst_avi_demux_add_date_tag (avi, year, month, day, hour, min, sec);
+}
+
+static void
+gst_avi_demux_parse_idit (GstAviDemux * avi, GstBuffer * buf)
+{
+  gchar *data = (gchar *) GST_BUFFER_DATA (buf);
+  guint size = GST_BUFFER_SIZE (buf);
+  gchar *safedata = NULL;
+
+  /*
+   * According to:
+   * http://www.eden-foundation.org/products/code/film_date_stamp/index.html
+   *
+   * This tag could be in one of the below formats
+   * 2005:08:17 11:42:43
+   * THU OCT 26 16:46:04 2006
+   * Mon Mar  3 09:44:56 2008
+   *
+   * FIXME: Our date tag doesn't include hours
+   */
+
+  /* skip eventual initial whitespace */
+  while (size > 0 && g_ascii_isspace (data[0])) {
+    data++;
+    size--;
+  }
+
+  if (size == 0) {
+    goto non_parsable;
+  }
+
+  /* make a safe copy to add a \0 to the end of the string */
+  safedata = g_strndup (data, size);
+
+  /* test if the first char is a alpha or a number */
+  if (g_ascii_isdigit (data[0])) {
+    gst_avi_demux_parse_idit_nums_only (avi, safedata);
+    g_free (safedata);
+    return;
+  } else if (g_ascii_isalpha (data[0])) {
+    gst_avi_demux_parse_idit_text (avi, safedata);
+    g_free (safedata);
+    return;
+  }
+
+  g_free (safedata);
+
+non_parsable:
+  GST_WARNING_OBJECT (avi, "IDIT tag has no parsable info");
+}
+
 /*
  * Read full AVI headers.
  */
@@ -2844,6 +3539,7 @@ gst_avi_demux_stream_header_pull (GstAviDemux * avi)
   gint64 stop;
   GstElement *element = GST_ELEMENT_CAST (avi);
   GstClockTime stamp;
+  GstTagList *tags = NULL;
 
   stamp = gst_util_get_timestamp ();
 
@@ -2883,7 +3579,7 @@ gst_avi_demux_stream_header_pull (GstAviDemux * avi)
     goto no_avih;
   else if (tag != GST_RIFF_TAG_avih)
     goto no_avih;
-  else if (!gst_avi_demux_parse_avih (element, sub, &avi->avih))
+  else if (!gst_avi_demux_parse_avih (avi, sub, &avi->avih))
     goto invalid_avih;
 
   GST_DEBUG_OBJECT (avi, "AVI header ok, reading elements from header");
@@ -2919,6 +3615,20 @@ gst_avi_demux_stream_header_pull (GstAviDemux * avi)
             gst_avi_demux_parse_odml (avi, sub);
             sub = NULL;
             break;
+          case GST_RIFF_LIST_INFO:
+            GST_BUFFER_DATA (sub) = data + 4;
+            GST_BUFFER_SIZE (sub) -= 4;
+            gst_riff_parse_info (element, sub, &tags);
+            if (tags) {
+              if (avi->globaltags) {
+                gst_tag_list_insert (avi->globaltags, tags,
+                    GST_TAG_MERGE_REPLACE);
+              } else {
+                avi->globaltags = tags;
+              }
+            }
+            tags = NULL;
+            break;
           default:
             GST_WARNING_OBJECT (avi,
                 "Unknown list %" GST_FOURCC_FORMAT " in AVI header",
@@ -2926,11 +3636,15 @@ gst_avi_demux_stream_header_pull (GstAviDemux * avi)
             GST_MEMDUMP_OBJECT (avi, "Unknown list", GST_BUFFER_DATA (sub),
                 GST_BUFFER_SIZE (sub));
             /* fall-through */
+          case GST_RIFF_TAG_JUNQ:
           case GST_RIFF_TAG_JUNK:
             goto next;
         }
         break;
       }
+      case GST_RIFF_IDIT:
+        gst_avi_demux_parse_idit (avi, sub);
+        goto next;
       default:
         GST_WARNING_OBJECT (avi,
             "Unknown tag %" GST_FOURCC_FORMAT " in AVI header at off %d",
@@ -2938,6 +3652,7 @@ gst_avi_demux_stream_header_pull (GstAviDemux * avi)
         GST_MEMDUMP_OBJECT (avi, "Unknown tag", GST_BUFFER_DATA (sub),
             GST_BUFFER_SIZE (sub));
         /* fall-through */
+      case GST_RIFF_TAG_JUNQ:
       case GST_RIFF_TAG_JUNK:
       next:
         if (sub)
@@ -3013,7 +3728,16 @@ gst_avi_demux_stream_header_pull (GstAviDemux * avi)
             }
 
             sub = gst_buffer_create_sub (buf, 4, GST_BUFFER_SIZE (buf) - 4);
-            gst_riff_parse_info (element, sub, &avi->globaltags);
+            gst_riff_parse_info (element, sub, &tags);
+            if (tags) {
+              if (avi->globaltags) {
+                gst_tag_list_insert (avi->globaltags, tags,
+                    GST_TAG_MERGE_REPLACE);
+              } else {
+                avi->globaltags = tags;
+              }
+            }
+            tags = NULL;
             if (sub) {
               gst_buffer_unref (sub);
               sub = NULL;
@@ -3036,6 +3760,16 @@ gst_avi_demux_stream_header_pull (GstAviDemux * avi)
         /* Fall-through */
       case GST_MAKE_FOURCC ('J', 'U', 'N', 'Q'):
       case GST_MAKE_FOURCC ('J', 'U', 'N', 'K'):
+        /* Only get buffer for debugging if the memdump is needed  */
+        if (gst_debug_category_get_threshold (GST_CAT_DEFAULT) >= 9) {
+          res = gst_pad_pull_range (avi->sinkpad, avi->offset, size, &buf);
+          if (res != GST_FLOW_OK) {
+            GST_DEBUG_OBJECT (avi, "couldn't read INFO chunk");
+            goto pull_range_failed;
+          }
+          GST_MEMDUMP ("Junk", GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
+          gst_buffer_unref (buf);
+        }
         avi->offset += 8 + GST_ROUND_UP_2 (size);
         break;
     }
@@ -3068,6 +3802,8 @@ skipping_done:
   /* use the indexes now to construct nice durations */
   gst_avi_demux_calculate_durations_from_index (avi);
 
+  gst_avi_demux_expose_streams (avi, FALSE);
+
   /* create initial NEWSEGMENT event */
   if ((stop = avi->segment.stop) == GST_CLOCK_TIME_NONE)
     stop = avi->segment.duration;
@@ -3076,15 +3812,16 @@ skipping_done:
 
   /* do initial seek to the default segment values */
   gst_avi_demux_do_seek (avi, &avi->segment);
+
   /* prepare initial segment */
-  if (avi->seek_event)
-    gst_event_unref (avi->seek_event);
-  avi->seek_event = gst_event_new_new_segment
-      (FALSE, avi->segment.rate, GST_FORMAT_TIME,
+  if (avi->seg_event)
+    gst_event_unref (avi->seg_event);
+  avi->seg_event = gst_event_new_new_segment_full
+      (FALSE, avi->segment.rate, avi->segment.applied_rate, GST_FORMAT_TIME,
       avi->segment.start, stop, avi->segment.time);
 
   stamp = gst_util_get_timestamp () - stamp;
-  GST_DEBUG_OBJECT (avi, "pulling header %" GST_TIME_FORMAT,
+  GST_DEBUG_OBJECT (avi, "pulling header took %" GST_TIME_FORMAT,
       GST_TIME_ARGS (stamp));
 
   /* at this point we know all the streams and we can signal the no more
@@ -3195,6 +3932,9 @@ gst_avi_demux_move_stream (GstAviDemux * avi, GstAviStream * stream,
       GST_TIME_ARGS (stream->current_timestamp),
       GST_TIME_ARGS (stream->current_ts_end), stream->current_offset,
       stream->current_offset_end);
+
+  GST_DEBUG_OBJECT (avi, "Seeking to offset %" G_GUINT64_FORMAT,
+      stream->index[index].offset);
 }
 
 /*
@@ -3209,14 +3949,14 @@ gst_avi_demux_do_seek (GstAviDemux * avi, GstSegment * segment)
   GstAviStream *stream;
 
   seek_time = segment->last_stop;
-  keyframe = !!(segment->flags & GST_SEEK_FLAG_KEY_UNIT);
+  keyframe = ! !(segment->flags & GST_SEEK_FLAG_KEY_UNIT);
 
   GST_DEBUG_OBJECT (avi, "seek to: %" GST_TIME_FORMAT
       " keyframe seeking:%d", GST_TIME_ARGS (seek_time), keyframe);
 
   /* FIXME, this code assumes the main stream with keyframes is stream 0,
    * which is mostly correct... */
-  stream = &avi->stream[0];
+  stream = &avi->stream[avi->main_stream];
 
   /* get the entry index for the requested position */
   index = gst_avi_demux_index_for_time (avi, stream, seek_time);
@@ -3253,7 +3993,7 @@ gst_avi_demux_do_seek (GstAviDemux * avi, GstSegment * segment)
     GstAviStream *ostream;
 
     ostream = &avi->stream[i];
-    if (ostream == stream)
+    if ((ostream == stream) || (ostream->index == NULL))
       continue;
 
     /* get the entry index for the requested position */
@@ -3272,7 +4012,7 @@ gst_avi_demux_do_seek (GstAviDemux * avi, GstSegment * segment)
 }
 
 /*
- * Handle seek event.
+ * Handle seek event in pull mode.
  */
 static gboolean
 gst_avi_demux_handle_seek (GstAviDemux * avi, GstPad * pad, GstEvent * event)
@@ -3354,29 +4094,21 @@ gst_avi_demux_handle_seek (GstAviDemux * avi, GstPad * pad, GstEvent * event)
    * actually never fails. */
   gst_avi_demux_do_seek (avi, &seeksegment);
 
+  gst_event_replace (&avi->close_seg_event, NULL);
   if (flush) {
     GstEvent *fevent = gst_event_new_flush_stop ();
 
     GST_DEBUG_OBJECT (avi, "sending flush stop");
     gst_avi_demux_push_event (avi, gst_event_ref (fevent));
     gst_pad_push_event (avi->sinkpad, fevent);
-
-    /* reset the last flow and mark discont, FLUSH is always DISCONT */
-    for (i = 0; i < avi->num_streams; i++) {
-      avi->stream[i].last_flow = GST_FLOW_OK;
-      avi->stream[i].discont = TRUE;
-    }
   } else if (avi->segment_running) {
-    GstEvent *seg;
-
     /* we are running the current segment and doing a non-flushing seek,
      * close the segment first based on the last_stop. */
     GST_DEBUG_OBJECT (avi, "closing running segment %" G_GINT64_FORMAT
         " to %" G_GINT64_FORMAT, avi->segment.start, avi->segment.last_stop);
-    seg = gst_event_new_new_segment (TRUE,
-        avi->segment.rate, avi->segment.format,
+    avi->close_seg_event = gst_event_new_new_segment_full (TRUE,
+        avi->segment.rate, avi->segment.applied_rate, avi->segment.format,
         avi->segment.start, avi->segment.last_stop, avi->segment.time);
-    gst_avi_demux_push_event (avi, seg);
   }
 
   /* now update the real segment info */
@@ -3384,8 +4116,8 @@ gst_avi_demux_handle_seek (GstAviDemux * avi, GstPad * pad, GstEvent * event)
 
   /* post the SEGMENT_START message when we do segmented playback */
   if (avi->segment.flags & GST_SEEK_FLAG_SEGMENT) {
-    gst_element_post_message (GST_ELEMENT (avi),
-        gst_message_new_segment_start (GST_OBJECT (avi),
+    gst_element_post_message (GST_ELEMENT_CAST (avi),
+        gst_message_new_segment_start (GST_OBJECT_CAST (avi),
             avi->segment.format, avi->segment.last_stop));
   }
 
@@ -3394,17 +4126,17 @@ gst_avi_demux_handle_seek (GstAviDemux * avi, GstPad * pad, GstEvent * event)
     stop = avi->segment.duration;
 
   /* queue the segment event for the streaming thread. */
-  if (avi->seek_event)
-    gst_event_unref (avi->seek_event);
+  if (avi->seg_event)
+    gst_event_unref (avi->seg_event);
   if (avi->segment.rate > 0.0) {
     /* forwards goes from last_stop to stop */
-    avi->seek_event = gst_event_new_new_segment (FALSE,
-        avi->segment.rate, avi->segment.format,
+    avi->seg_event = gst_event_new_new_segment_full (FALSE,
+        avi->segment.rate, avi->segment.applied_rate, avi->segment.format,
         avi->segment.last_stop, stop, avi->segment.time);
   } else {
     /* reverse goes from start to last_stop */
-    avi->seek_event = gst_event_new_new_segment (FALSE,
-        avi->segment.rate, avi->segment.format,
+    avi->seg_event = gst_event_new_new_segment_full (FALSE,
+        avi->segment.rate, avi->segment.applied_rate, avi->segment.format,
         avi->segment.start, avi->segment.last_stop, avi->segment.time);
   }
 
@@ -3413,6 +4145,12 @@ gst_avi_demux_handle_seek (GstAviDemux * avi, GstPad * pad, GstEvent * event)
     gst_pad_start_task (avi->sinkpad, (GstTaskFunction) gst_avi_demux_loop,
         avi->sinkpad);
   }
+  /* reset the last flow and mark discont, seek is always DISCONT */
+  for (i = 0; i < avi->num_streams; i++) {
+    GST_DEBUG_OBJECT (avi, "marking DISCONT");
+    avi->stream[i].last_flow = GST_FLOW_OK;
+    avi->stream[i].discont = TRUE;
+  }
   GST_PAD_STREAM_UNLOCK (avi->sinkpad);
 
   return TRUE;
@@ -3426,6 +4164,216 @@ no_format:
 }
 
 /*
+ * Handle seek event in push mode.
+ */
+static gboolean
+avi_demux_handle_seek_push (GstAviDemux * avi, GstPad * pad, GstEvent * event)
+{
+  gdouble rate;
+  GstFormat format;
+  GstSeekFlags flags;
+  GstSeekType cur_type = GST_SEEK_TYPE_NONE, stop_type;
+  gint64 cur, stop;
+  gboolean keyframe;
+  GstAviStream *stream;
+  guint index;
+  guint n, str_num;
+  guint64 min_offset;
+  GstSegment seeksegment;
+  gboolean update;
+
+  /* check we have the index */
+  if (!avi->have_index) {
+    GST_DEBUG_OBJECT (avi, "no seek index built, seek aborted.");
+    return FALSE;
+  } else {
+    GST_DEBUG_OBJECT (avi, "doing push-based seek with event");
+  }
+
+  gst_event_parse_seek (event, &rate, &format, &flags,
+      &cur_type, &cur, &stop_type, &stop);
+
+  if (format != GST_FORMAT_TIME) {
+    GstFormat fmt = GST_FORMAT_TIME;
+    gboolean res = TRUE;
+
+    if (cur_type != GST_SEEK_TYPE_NONE)
+      res = gst_pad_query_convert (pad, format, cur, &fmt, &cur);
+    if (res && stop_type != GST_SEEK_TYPE_NONE)
+      res = gst_pad_query_convert (pad, format, stop, &fmt, &stop);
+    if (!res) {
+      GST_DEBUG_OBJECT (avi, "unsupported format given, seek aborted.");
+      return FALSE;
+    }
+
+    format = fmt;
+  }
+
+  /* let gst_segment handle any tricky stuff */
+  GST_DEBUG_OBJECT (avi, "configuring seek");
+  memcpy (&seeksegment, &avi->segment, sizeof (GstSegment));
+  gst_segment_set_seek (&seeksegment, rate, format, flags,
+      cur_type, cur, stop_type, stop, &update);
+
+  keyframe = ! !(flags & GST_SEEK_FLAG_KEY_UNIT);
+  cur = seeksegment.last_stop;
+
+  GST_DEBUG_OBJECT (avi,
+      "Seek requested: ts %" GST_TIME_FORMAT " stop %" GST_TIME_FORMAT
+      ", kf %u, rate %lf", GST_TIME_ARGS (cur), GST_TIME_ARGS (stop), keyframe,
+      rate);
+
+  if (rate < 0) {
+    GST_DEBUG_OBJECT (avi, "negative rate seek not supported in push mode");
+    return FALSE;
+  }
+
+  /* FIXME, this code assumes the main stream with keyframes is stream 0,
+   * which is mostly correct... */
+  str_num = avi->main_stream;
+  stream = &avi->stream[str_num];
+
+  /* get the entry index for the requested position */
+  index = gst_avi_demux_index_for_time (avi, stream, cur);
+  GST_DEBUG_OBJECT (avi, "str %u: Found entry %u for %" GST_TIME_FORMAT,
+      str_num, index, GST_TIME_ARGS (cur));
+
+  /* check if we are already on a keyframe */
+  if (!ENTRY_IS_KEYFRAME (&stream->index[index])) {
+    GST_DEBUG_OBJECT (avi, "Entry is not a keyframe - searching back");
+    /* now go to the previous keyframe, this is where we should start
+     * decoding from. */
+    index = gst_avi_demux_index_prev (avi, stream, index, TRUE);
+    GST_DEBUG_OBJECT (avi, "Found previous keyframe at %u", index);
+  }
+
+  gst_avi_demux_get_buffer_info (avi, stream, index,
+      &stream->current_timestamp, &stream->current_ts_end,
+      &stream->current_offset, &stream->current_offset_end);
+
+  /* re-use cur to be the timestamp of the seek as it _will_ be */
+  cur = stream->current_timestamp;
+
+  min_offset = stream->index[index].offset;
+  avi->seek_kf_offset = min_offset - 8;
+
+  GST_DEBUG_OBJECT (avi,
+      "Seek to: ts %" GST_TIME_FORMAT " (on str %u, idx %u, offset %"
+      G_GUINT64_FORMAT ")", GST_TIME_ARGS (stream->current_timestamp), str_num,
+      index, min_offset);
+
+  for (n = 0; n < avi->num_streams; n++) {
+    GstAviStream *str = &avi->stream[n];
+    guint idx;
+
+    if (n == avi->main_stream)
+      continue;
+
+    /* get the entry index for the requested position */
+    idx = gst_avi_demux_index_for_time (avi, str, cur);
+    GST_DEBUG_OBJECT (avi, "str %u: Found entry %u for %" GST_TIME_FORMAT, n,
+        idx, GST_TIME_ARGS (cur));
+
+    /* check if we are already on a keyframe */
+    if (!ENTRY_IS_KEYFRAME (&str->index[idx])) {
+      GST_DEBUG_OBJECT (avi, "Entry is not a keyframe - searching back");
+      /* now go to the previous keyframe, this is where we should start
+       * decoding from. */
+      idx = gst_avi_demux_index_prev (avi, str, idx, TRUE);
+      GST_DEBUG_OBJECT (avi, "Found previous keyframe at %u", idx);
+    }
+
+    gst_avi_demux_get_buffer_info (avi, str, idx,
+        &str->current_timestamp, &str->current_ts_end,
+        &str->current_offset, &str->current_offset_end);
+
+    if (str->index[idx].offset < min_offset) {
+      min_offset = str->index[idx].offset;
+      GST_DEBUG_OBJECT (avi,
+          "Found an earlier offset at %" G_GUINT64_FORMAT ", str %u",
+          min_offset, n);
+      str_num = n;
+      stream = str;
+      index = idx;
+    }
+  }
+
+  GST_DEBUG_OBJECT (avi,
+      "Seek performed: str %u, offset %" G_GUINT64_FORMAT ", idx %u, ts %"
+      GST_TIME_FORMAT ", ts_end %" GST_TIME_FORMAT ", off %" G_GUINT64_FORMAT
+      ", off_end %" G_GUINT64_FORMAT, str_num, min_offset, index,
+      GST_TIME_ARGS (stream->current_timestamp),
+      GST_TIME_ARGS (stream->current_ts_end), stream->current_offset,
+      stream->current_offset_end);
+
+  /* index data refers to data, not chunk header (for pull mode convenience) */
+  min_offset -= 8;
+  GST_DEBUG_OBJECT (avi, "seeking to chunk at offset %" G_GUINT64_FORMAT,
+      min_offset);
+
+  if (!perform_seek_to_offset (avi, min_offset)) {
+    GST_DEBUG_OBJECT (avi, "seek event failed!");
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+/*
+ * Handle whether we can perform the seek event or if we have to let the chain
+ * function handle seeks to build the seek indexes first.
+ */
+static gboolean
+gst_avi_demux_handle_seek_push (GstAviDemux * avi, GstPad * pad,
+    GstEvent * event)
+{
+  /* check for having parsed index already */
+  if (!avi->have_index) {
+    guint64 offset = 0;
+    gboolean building_index;
+
+    GST_OBJECT_LOCK (avi);
+    /* handle the seek event in the chain function */
+    avi->state = GST_AVI_DEMUX_SEEK;
+
+    /* copy the event */
+    if (avi->seek_event)
+      gst_event_unref (avi->seek_event);
+    avi->seek_event = gst_event_ref (event);
+
+    /* set the building_index flag so that only one thread can setup the
+     * structures for index seeking. */
+    building_index = avi->building_index;
+    if (!building_index) {
+      avi->building_index = TRUE;
+      if (avi->stream[0].indexes) {
+        avi->odml_stream = 0;
+        avi->odml_subidxs = avi->stream[avi->odml_stream].indexes;
+        offset = avi->odml_subidxs[0];
+      } else {
+        offset = avi->idx1_offset;
+      }
+    }
+    GST_OBJECT_UNLOCK (avi);
+
+    if (!building_index) {
+      /* seek to the first subindex or legacy index */
+      GST_INFO_OBJECT (avi,
+          "Seeking to legacy index/first subindex at %" G_GUINT64_FORMAT,
+          offset);
+      return perform_seek_to_offset (avi, offset);
+    }
+
+    /* FIXME: we have to always return true so that we don't block the seek
+     * thread.
+     * Note: maybe it is OK to return true if we're still building the index */
+    return TRUE;
+  }
+
+  return avi_demux_handle_seek_push (avi, pad, event);
+}
+
+/*
  * Helper for gst_avi_demux_invert()
  */
 static inline void
@@ -3496,6 +4444,25 @@ gst_avi_demux_invert (GstAviStream * stream, GstBuffer * buf)
   return buf;
 }
 
+static void
+gst_avi_demux_add_assoc (GstAviDemux * avi, GstAviStream * stream,
+    GstClockTime timestamp, guint64 offset, gboolean keyframe)
+{
+  /* do not add indefinitely for open-ended streaming */
+  if (G_UNLIKELY (avi->element_index && avi->seekable)) {
+    GST_LOG_OBJECT (avi, "adding association %" GST_TIME_FORMAT "-> %"
+        G_GUINT64_FORMAT, GST_TIME_ARGS (timestamp), offset);
+    gst_index_add_association (avi->element_index, avi->index_id,
+        keyframe ? GST_ASSOCIATION_FLAG_KEY_UNIT : GST_ASSOCIATION_FLAG_NONE,
+        GST_FORMAT_TIME, timestamp, GST_FORMAT_BYTES, offset, NULL);
+    /* well, current_total determines TIME and entry DEFAULT (frame #) ... */
+    gst_index_add_association (avi->element_index, stream->index_id,
+        GST_ASSOCIATION_FLAG_NONE,
+        GST_FORMAT_TIME, stream->current_total, GST_FORMAT_BYTES, offset,
+        GST_FORMAT_DEFAULT, stream->current_entry, NULL);
+  }
+}
+
 /*
  * Returns the aggregated GstFlowReturn.
  */
@@ -3504,12 +4471,13 @@ gst_avi_demux_combine_flows (GstAviDemux * avi, GstAviStream * stream,
     GstFlowReturn ret)
 {
   guint i;
+  gboolean unexpected = FALSE, not_linked = TRUE;
 
   /* store the value */
   stream->last_flow = ret;
 
-  /* any other error that is not-linked can be returned right away */
-  if (G_UNLIKELY (ret != GST_FLOW_NOT_LINKED))
+  /* any other error that is not-linked or eos can be returned right away */
+  if (G_LIKELY (ret != GST_FLOW_UNEXPECTED && ret != GST_FLOW_NOT_LINKED))
     goto done;
 
   /* only return NOT_LINKED if all other pads returned NOT_LINKED */
@@ -3517,13 +4485,19 @@ gst_avi_demux_combine_flows (GstAviDemux * avi, GstAviStream * stream,
     GstAviStream *ostream = &avi->stream[i];
 
     ret = ostream->last_flow;
-    /* some other return value (must be SUCCESS but we can return
-     * other values as well) */
-    if (G_UNLIKELY (ret != GST_FLOW_NOT_LINKED))
+    /* no unexpected or unlinked, return */
+    if (G_LIKELY (ret != GST_FLOW_UNEXPECTED && ret != GST_FLOW_NOT_LINKED))
       goto done;
+
+    /* we check to see if we have at least 1 unexpected or all unlinked */
+    unexpected |= (ret == GST_FLOW_UNEXPECTED);
+    not_linked &= (ret == GST_FLOW_NOT_LINKED);
   }
-  /* if we get here, all other pads were unlinked and we return
-   * NOT_LINKED then */
+  /* when we get here, we all have unlinked or unexpected */
+  if (not_linked)
+    ret = GST_FLOW_NOT_LINKED;
+  else if (unexpected)
+    ret = GST_FLOW_UNEXPECTED;
 done:
   GST_LOG_OBJECT (avi, "combined %s to return %s",
       gst_flow_get_name (stream->last_flow), gst_flow_get_name (ret));
@@ -3625,6 +4599,11 @@ gst_avi_demux_find_next (GstAviDemux * avi, gfloat rate)
     GstAviStream *stream;
 
     stream = &avi->stream[i];
+
+    /* ignore streams that finished */
+    if (stream->last_flow == GST_FLOW_UNEXPECTED)
+      continue;
+
     position = stream->current_timestamp;
 
     /* position of -1 is EOS */
@@ -3732,10 +4711,13 @@ gst_avi_demux_loop_data (GstAviDemux * avi)
 
     /* mark discont when pending */
     if (stream->discont) {
+      GST_DEBUG_OBJECT (avi, "setting DISCONT flag");
       GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
       stream->discont = FALSE;
     }
 
+    gst_avi_demux_add_assoc (avi, stream, timestamp, offset, keyframe);
+
     gst_buffer_set_caps (buf, GST_PAD_CAPS (stream->pad));
 
     /* update current position in the segment */
@@ -3786,7 +4768,8 @@ eos_stop:
         " setting EOS (%" GST_TIME_FORMAT " > %" GST_TIME_FORMAT ")",
         GST_TIME_ARGS (timestamp), GST_TIME_ARGS (avi->segment.stop));
     ret = GST_FLOW_UNEXPECTED;
-    goto beach;
+    /* move to next stream */
+    goto next;
   }
 pull_failed:
   {
@@ -3821,7 +4804,20 @@ gst_avi_demux_stream_data (GstAviDemux * avi)
   if (G_UNLIKELY (avi->have_eos)) {
     /* Clean adapter, we're done */
     gst_adapter_clear (avi->adapter);
-    return res;
+    return GST_FLOW_UNEXPECTED;
+  }
+
+  if (G_UNLIKELY (avi->todrop)) {
+    guint drop;
+
+    if ((drop = gst_adapter_available (avi->adapter))) {
+      if (drop > avi->todrop)
+        drop = avi->todrop;
+      GST_DEBUG_OBJECT (avi, "Dropping %d bytes", drop);
+      gst_adapter_flush (avi->adapter, drop);
+      avi->todrop -= drop;
+      avi->offset += drop;
+    }
   }
 
   /* Iterate until need more data, so adapter won't grow too much */
@@ -3845,10 +4841,23 @@ gst_avi_demux_stream_data (GstAviDemux * avi)
         gst_adapter_flush (avi->adapter, 8 + GST_ROUND_UP_2 (size));
       }
       return GST_FLOW_OK;
+    } else if (tag == GST_RIFF_TAG_RIFF) {
+      /* RIFF tags can appear in ODML files, just jump over them */
+      if (gst_adapter_available (avi->adapter) >= 12) {
+        GST_DEBUG ("Found RIFF tag, skipping RIFF header");
+        gst_adapter_flush (avi->adapter, 12);
+        continue;
+      }
+      return GST_FLOW_OK;
     } else if (tag == GST_RIFF_TAG_idx1) {
-      GST_DEBUG ("Found index tag, stream done");
-      avi->have_eos = TRUE;
-      return GST_FLOW_UNEXPECTED;
+      GST_DEBUG ("Found index tag");
+      if (gst_avi_demux_peek_chunk (avi, &tag, &size) || size == 0) {
+        /* accept 0 size buffer here */
+        avi->abort_buffering = FALSE;
+        GST_DEBUG ("  skipping %d bytes for now", size);
+        gst_adapter_flush (avi->adapter, 8 + GST_ROUND_UP_2 (size));
+      }
+      return GST_FLOW_OK;
     } else if (tag == GST_RIFF_TAG_LIST) {
       /* movi chunks might be grouped in rec list */
       if (gst_adapter_available (avi->adapter) >= 12) {
@@ -3857,7 +4866,7 @@ gst_avi_demux_stream_data (GstAviDemux * avi)
         continue;
       }
       return GST_FLOW_OK;
-    } else if (tag == GST_RIFF_TAG_JUNK) {
+    } else if (tag == GST_RIFF_TAG_JUNK || tag == GST_RIFF_TAG_JUNQ) {
       /* rec list might contain JUNK chunks */
       GST_DEBUG ("Found JUNK tag");
       if (gst_avi_demux_peek_chunk (avi, &tag, &size) || size == 0) {
@@ -3879,9 +4888,13 @@ gst_avi_demux_stream_data (GstAviDemux * avi)
        * through the whole file */
       if (avi->abort_buffering) {
         avi->abort_buffering = FALSE;
-        gst_adapter_flush (avi->adapter, 8);
+        if (size) {
+          gst_adapter_flush (avi->adapter, 8);
+          return GST_FLOW_OK;
+        }
+      } else {
+        return GST_FLOW_OK;
       }
-      return GST_FLOW_OK;
     }
     GST_DEBUG ("chunk ID %" GST_FOURCC_FORMAT ", size %u",
         GST_FOURCC_ARGS (tag), size);
@@ -3897,14 +4910,33 @@ gst_avi_demux_stream_data (GstAviDemux * avi)
     } else {
       GstAviStream *stream;
       GstClockTime next_ts = 0;
-      GstBuffer *buf;
+      GstBuffer *buf = NULL;
+      guint64 offset;
+      gboolean saw_desired_kf = stream_nr != avi->main_stream
+          || avi->offset >= avi->seek_kf_offset;
+
+      if (stream_nr == avi->main_stream && avi->offset == avi->seek_kf_offset) {
+        GST_DEBUG_OBJECT (avi, "Desired keyframe reached");
+        avi->seek_kf_offset = 0;
+      }
 
-      gst_adapter_flush (avi->adapter, 8);
+      if (saw_desired_kf) {
+        gst_adapter_flush (avi->adapter, 8);
+        /* get buffer */
+        if (size) {
+          buf = gst_adapter_take_buffer (avi->adapter, GST_ROUND_UP_2 (size));
+          /* patch the size */
+          GST_BUFFER_SIZE (buf) = size;
+        } else {
+          buf = NULL;
+        }
+      } else {
+        GST_DEBUG_OBJECT (avi,
+            "Desired keyframe not yet reached, flushing chunk");
+        gst_adapter_flush (avi->adapter, 8 + GST_ROUND_UP_2 (size));
+      }
 
-      /* get buffer */
-      buf = gst_adapter_take_buffer (avi->adapter, GST_ROUND_UP_2 (size));
-      /* patch the size */
-      GST_BUFFER_SIZE (buf) = size;
+      offset = avi->offset;
       avi->offset += 8 + GST_ROUND_UP_2 (size);
 
       stream = &avi->stream[stream_nr];
@@ -3919,59 +4951,66 @@ gst_avi_demux_stream_data (GstAviDemux * avi)
       if (G_UNLIKELY (!stream->pad)) {
         GST_WARNING_OBJECT (avi, "no pad for stream ID %" GST_FOURCC_FORMAT,
             GST_FOURCC_ARGS (tag));
-        gst_buffer_unref (buf);
+        if (buf)
+          gst_buffer_unref (buf);
       } else {
-        GstClockTime dur_ts = 0;
-
         /* get time of this buffer */
         gst_pad_query_position (stream->pad, &format, (gint64 *) & next_ts);
         if (G_UNLIKELY (format != GST_FORMAT_TIME))
           goto wrong_format;
 
+        gst_avi_demux_add_assoc (avi, stream, next_ts, offset, FALSE);
+
         /* increment our positions */
         stream->current_entry++;
         stream->current_total += size;
 
-        /* invert the picture if needed */
-        buf = gst_avi_demux_invert (stream, buf);
+        /* update current position in the segment */
+        gst_segment_set_last_stop (&avi->segment, GST_FORMAT_TIME, next_ts);
 
-        gst_pad_query_position (stream->pad, &format, (gint64 *) & dur_ts);
-        if (G_UNLIKELY (format != GST_FORMAT_TIME))
-          goto wrong_format;
+        if (saw_desired_kf && buf) {
+          GstClockTime dur_ts = 0;
 
-        GST_BUFFER_TIMESTAMP (buf) = next_ts;
-        GST_BUFFER_DURATION (buf) = dur_ts - next_ts;
-        if (stream->strh->type == GST_RIFF_FCC_vids) {
-          GST_BUFFER_OFFSET (buf) = stream->current_entry - 1;
-          GST_BUFFER_OFFSET_END (buf) = stream->current_entry;
-        } else {
-          GST_BUFFER_OFFSET (buf) = GST_BUFFER_OFFSET_NONE;
-          GST_BUFFER_OFFSET_END (buf) = GST_BUFFER_OFFSET_NONE;
-        }
+          /* invert the picture if needed */
+          buf = gst_avi_demux_invert (stream, buf);
 
-        gst_buffer_set_caps (buf, GST_PAD_CAPS (stream->pad));
-        GST_DEBUG_OBJECT (avi,
-            "Pushing buffer with time=%" GST_TIME_FORMAT ", duration %"
-            GST_TIME_FORMAT ", offset %" G_GUINT64_FORMAT
-            " and size %d over pad %s", GST_TIME_ARGS (next_ts),
-            GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), GST_BUFFER_OFFSET (buf),
-            size, GST_PAD_NAME (stream->pad));
+          gst_pad_query_position (stream->pad, &format, (gint64 *) & dur_ts);
+          if (G_UNLIKELY (format != GST_FORMAT_TIME))
+            goto wrong_format;
 
-        /* update current position in the segment */
-        gst_segment_set_last_stop (&avi->segment, GST_FORMAT_TIME, next_ts);
-
-        /* mark discont when pending */
-        if (G_UNLIKELY (stream->discont)) {
-          GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
-          stream->discont = FALSE;
-        }
-        res = gst_pad_push (stream->pad, buf);
+          GST_BUFFER_TIMESTAMP (buf) = next_ts;
+          GST_BUFFER_DURATION (buf) = dur_ts - next_ts;
+          if (stream->strh->type == GST_RIFF_FCC_vids) {
+            GST_BUFFER_OFFSET (buf) = stream->current_entry - 1;
+            GST_BUFFER_OFFSET_END (buf) = stream->current_entry;
+          } else {
+            GST_BUFFER_OFFSET (buf) = GST_BUFFER_OFFSET_NONE;
+            GST_BUFFER_OFFSET_END (buf) = GST_BUFFER_OFFSET_NONE;
+          }
 
-        /* combine flows */
-        res = gst_avi_demux_combine_flows (avi, stream, res);
-        if (G_UNLIKELY (res != GST_FLOW_OK)) {
-          GST_DEBUG ("Push failed; %s", gst_flow_get_name (res));
-          return res;
+          gst_buffer_set_caps (buf, GST_PAD_CAPS (stream->pad));
+          GST_DEBUG_OBJECT (avi,
+              "Pushing buffer with time=%" GST_TIME_FORMAT ", duration %"
+              GST_TIME_FORMAT ", offset %" G_GUINT64_FORMAT
+              " and size %d over pad %s", GST_TIME_ARGS (next_ts),
+              GST_TIME_ARGS (GST_BUFFER_DURATION (buf)),
+              GST_BUFFER_OFFSET (buf), size, GST_PAD_NAME (stream->pad));
+
+          /* mark discont when pending */
+          if (G_UNLIKELY (stream->discont)) {
+            GST_DEBUG_OBJECT (avi, "Setting DISCONT");
+            GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
+            stream->discont = FALSE;
+          }
+          res = gst_pad_push (stream->pad, buf);
+          buf = NULL;
+
+          /* combine flows */
+          res = gst_avi_demux_combine_flows (avi, stream, res);
+          if (G_UNLIKELY (res != GST_FLOW_OK)) {
+            GST_DEBUG ("Push failed; %s", gst_flow_get_name (res));
+            return res;
+          }
         }
       }
     }
@@ -4054,9 +5093,13 @@ gst_avi_demux_loop (GstPad * pad)
       avi->state = GST_AVI_DEMUX_MOVI;
       break;
     case GST_AVI_DEMUX_MOVI:
-      if (G_UNLIKELY (avi->seek_event)) {
-        gst_avi_demux_push_event (avi, avi->seek_event);
-        avi->seek_event = NULL;
+      if (G_UNLIKELY (avi->close_seg_event)) {
+        gst_avi_demux_push_event (avi, avi->close_seg_event);
+        avi->close_seg_event = NULL;
+      }
+      if (G_UNLIKELY (avi->seg_event)) {
+        gst_avi_demux_push_event (avi, avi->seg_event);
+        avi->seg_event = NULL;
       }
       if (G_UNLIKELY (avi->got_tags)) {
         push_tag_lists (avi);
@@ -4079,13 +5122,13 @@ gst_avi_demux_loop (GstPad * pad)
   return;
 
   /* ERRORS */
-pause:
-  GST_LOG_OBJECT (avi, "pausing task, reason %s", gst_flow_get_name (res));
-  avi->segment_running = FALSE;
-  gst_pad_pause_task (avi->sinkpad);
+pause:{
+
+    gboolean push_eos = FALSE;
+    GST_LOG_OBJECT (avi, "pausing task, reason %s", gst_flow_get_name (res));
+    avi->segment_running = FALSE;
+    gst_pad_pause_task (avi->sinkpad);
 
-  if (GST_FLOW_IS_FATAL (res) || (res == GST_FLOW_NOT_LINKED)) {
-    gboolean push_eos = TRUE;
 
     if (res == GST_FLOW_UNEXPECTED) {
       /* handle end-of-stream/segment */
@@ -4098,16 +5141,20 @@ pause:
         GST_INFO_OBJECT (avi, "sending segment_done");
 
         gst_element_post_message
-            (GST_ELEMENT (avi),
-            gst_message_new_segment_done (GST_OBJECT (avi), GST_FORMAT_TIME,
-                stop));
-        push_eos = FALSE;
+            (GST_ELEMENT_CAST (avi),
+            gst_message_new_segment_done (GST_OBJECT_CAST (avi),
+                GST_FORMAT_TIME, stop));
+      } else {
+        push_eos = TRUE;
       }
-    } else {
-      /* for fatal errors we post an error message */
+    } else if (res == GST_FLOW_NOT_LINKED || res < GST_FLOW_UNEXPECTED) {
+      /* for fatal errors we post an error message, wrong-state is
+       * not fatal because it happens due to flushes and only means
+       * that we should stop now. */
       GST_ELEMENT_ERROR (avi, STREAM, FAILED,
           (_("Internal data stream error.")),
           ("streaming stopped, reason %s", gst_flow_get_name (res)));
+      push_eos = TRUE;
     }
     if (push_eos) {
       GST_INFO_OBJECT (avi, "sending eos");
@@ -4126,6 +5173,15 @@ gst_avi_demux_chain (GstPad * pad, GstBuffer * buf)
 {
   GstFlowReturn res;
   GstAviDemux *avi = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
+  gint i;
+
+  if (GST_BUFFER_IS_DISCONT (buf)) {
+    GST_DEBUG_OBJECT (avi, "got DISCONT");
+    gst_adapter_clear (avi->adapter);
+    /* mark all streams DISCONT */
+    for (i = 0; i < avi->num_streams; i++)
+      avi->stream[i].discont = TRUE;
+  }
 
   GST_DEBUG ("Store %d bytes in adapter", GST_BUFFER_SIZE (buf));
   gst_adapter_push (avi->adapter, buf);
@@ -4144,15 +5200,55 @@ gst_avi_demux_chain (GstPad * pad, GstBuffer * buf)
       }
       break;
     case GST_AVI_DEMUX_MOVI:
-      if (G_UNLIKELY (avi->seek_event)) {
-        gst_avi_demux_push_event (avi, avi->seek_event);
-        avi->seek_event = NULL;
+      if (G_UNLIKELY (avi->close_seg_event)) {
+        gst_avi_demux_push_event (avi, avi->close_seg_event);
+        avi->close_seg_event = NULL;
+      }
+      if (G_UNLIKELY (avi->seg_event)) {
+        gst_avi_demux_push_event (avi, avi->seg_event);
+        avi->seg_event = NULL;
       }
       if (G_UNLIKELY (avi->got_tags)) {
         push_tag_lists (avi);
       }
       res = gst_avi_demux_stream_data (avi);
       break;
+    case GST_AVI_DEMUX_SEEK:
+    {
+      GstEvent *event;
+
+      res = GST_FLOW_OK;
+
+      /* obtain and parse indexes */
+      if (avi->stream[0].indexes && !gst_avi_demux_read_subindexes_push (avi))
+        /* seek in subindex read function failed */
+        goto index_failed;
+
+      if (!avi->stream[0].indexes && !avi->have_index
+          && avi->avih->flags & GST_RIFF_AVIH_HASINDEX)
+        gst_avi_demux_stream_index_push (avi);
+
+      if (avi->have_index) {
+        /* use the indexes now to construct nice durations */
+        gst_avi_demux_calculate_durations_from_index (avi);
+      } else {
+        /* still parsing indexes */
+        break;
+      }
+
+      GST_OBJECT_LOCK (avi);
+      event = avi->seek_event;
+      avi->seek_event = NULL;
+      GST_OBJECT_UNLOCK (avi);
+
+      /* calculate and perform seek */
+      if (!avi_demux_handle_seek_push (avi, avi->sinkpad, event))
+        goto seek_failed;
+
+      gst_event_unref (event);
+      avi->state = GST_AVI_DEMUX_MOVI;
+      break;
+    }
     default:
       GST_ELEMENT_ERROR (avi, STREAM, FAILED, (NULL),
           ("Illegal internal state"));
@@ -4163,13 +5259,28 @@ gst_avi_demux_chain (GstPad * pad, GstBuffer * buf)
   GST_DEBUG_OBJECT (avi, "state: %d res:%s", avi->state,
       gst_flow_get_name (res));
 
-  if (G_UNLIKELY (avi->abort_buffering)) {
+  if (G_UNLIKELY (avi->abort_buffering))
+    goto abort_buffering;
+
+  return res;
+
+  /* ERRORS */
+index_failed:
+  {
+    GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), ("failed to read indexes"));
+    return GST_FLOW_ERROR;
+  }
+seek_failed:
+  {
+    GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), ("push mode seek failed"));
+    return GST_FLOW_ERROR;
+  }
+abort_buffering:
+  {
     avi->abort_buffering = FALSE;
-    res = GST_FLOW_ERROR;
     GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), ("unhandled buffer size"));
+    return GST_FLOW_ERROR;
   }
-
-  return res;
 }
 
 static gboolean
@@ -4208,6 +5319,18 @@ gst_avi_demux_activate_push (GstPad * pad, gboolean active)
   if (active) {
     GST_DEBUG ("avi: activating push/chain function");
     avi->streaming = TRUE;
+#if 0
+    /* create index for some push based seeking if not provided */
+    GST_OBJECT_LOCK (avi);
+    if (!avi->element_index) {
+      GST_DEBUG_OBJECT (avi, "creating index");
+      avi->element_index = gst_index_factory_make ("memindex");
+    }
+    GST_OBJECT_UNLOCK (avi);
+    /* object lock might be taken again */
+    gst_index_get_writer_id (avi->element_index, GST_OBJECT_CAST (avi),
+        &avi->index_id);
+#endif
   } else {
     GST_DEBUG ("avi: deactivating push/chain function");
   }
@@ -4215,6 +5338,42 @@ gst_avi_demux_activate_push (GstPad * pad, gboolean active)
   return TRUE;
 }
 
+static void
+gst_avi_demux_set_index (GstElement * element, GstIndex * index)
+{
+  GstAviDemux *avi = GST_AVI_DEMUX (element);
+
+  GST_OBJECT_LOCK (avi);
+  if (avi->element_index)
+    gst_object_unref (avi->element_index);
+  if (index) {
+    avi->element_index = gst_object_ref (index);
+  } else {
+    avi->element_index = NULL;
+  }
+  GST_OBJECT_UNLOCK (avi);
+  /* object lock might be taken again */
+  if (index)
+    gst_index_get_writer_id (index, GST_OBJECT_CAST (element), &avi->index_id);
+  GST_DEBUG_OBJECT (avi, "Set index %" GST_PTR_FORMAT, avi->element_index);
+}
+
+static GstIndex *
+gst_avi_demux_get_index (GstElement * element)
+{
+  GstIndex *result = NULL;
+  GstAviDemux *avi = GST_AVI_DEMUX (element);
+
+  GST_OBJECT_LOCK (avi);
+  if (avi->element_index)
+    result = gst_object_ref (avi->element_index);
+  GST_OBJECT_UNLOCK (avi);
+
+  GST_DEBUG_OBJECT (avi, "Returning index %" GST_PTR_FORMAT, result);
+
+  return result;
+}
+
 static GstStateChangeReturn
 gst_avi_demux_change_state (GstElement * element, GstStateChange transition)
 {
@@ -4236,6 +5395,7 @@ gst_avi_demux_change_state (GstElement * element, GstStateChange transition)
 
   switch (transition) {
     case GST_STATE_CHANGE_PAUSED_TO_READY:
+      avi->have_index = FALSE;
       gst_avi_demux_reset (avi);
       break;
     default: