]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - glsdk/gst-plugins-ugly0-10.git/blobdiff - gst/asfdemux/gstasfdemux.c
asfdemux: don't leak payload bufs in gst_asf_demux_free_stream
[glsdk/gst-plugins-ugly0-10.git] / gst / asfdemux / gstasfdemux.c
index f50b91f7a0f83ea08b27f107b83ba192ab97af12..46cee6dad5b69695b2f3610fdb363b9c82fc5f93 100644 (file)
@@ -1,6 +1,6 @@
 /* GStreamer ASF/WMV/WMA demuxer
  * Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
- * Copyright (C) 2006-2007 Tim-Philipp Müller <tim centricular net>
+ * Copyright (C) 2006-2009 Tim-Philipp Müller <tim centricular net>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
 #include "config.h"
 #endif
 
+/* FIXME 0.11: suppress warnings for deprecated API such as GStaticRecMutex
+ * with newer GLib versions (>= 2.31.0) */
+#define GLIB_DISABLE_DEPRECATION_WARNINGS
+
 #include <gst/gstutils.h>
+#include <gst/base/gstbytereader.h>
 #include <gst/riff/riff-media.h>
+#include <gst/tag/tag.h>
 #include <gst/gst-i18n-plugin.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
@@ -103,6 +110,8 @@ static void gst_asf_demux_activate_stream (GstASFDemux * demux,
     AsfStream * stream);
 static GstStructure *gst_asf_demux_get_metadata_for_stream (GstASFDemux * d,
     guint stream_num);
+static GstFlowReturn gst_asf_demux_push_complete_payloads (GstASFDemux * demux,
+    gboolean force);
 
 GST_BOILERPLATE (GstASFDemux, gst_asf_demux, GstElement, GST_TYPE_ELEMENT);
 
@@ -110,21 +119,17 @@ static void
 gst_asf_demux_base_init (gpointer g_class)
 {
   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
-  static GstElementDetails gst_asf_demux_details = {
-    "ASF Demuxer",
-    "Codec/Demuxer",
-    "Demultiplexes ASF Streams",
-    "Owen Fraser-Green <owen@discobabe.net>"
-  };
 
-  gst_element_class_add_pad_template (element_class,
-      gst_static_pad_template_get (&audio_src_template));
-  gst_element_class_add_pad_template (element_class,
-      gst_static_pad_template_get (&video_src_template));
-  gst_element_class_add_pad_template (element_class,
-      gst_static_pad_template_get (&gst_asf_demux_sink_template));
+  gst_element_class_add_static_pad_template (element_class,
+      &audio_src_template);
+  gst_element_class_add_static_pad_template (element_class,
+      &video_src_template);
+  gst_element_class_add_static_pad_template (element_class,
+      &gst_asf_demux_sink_template);
 
-  gst_element_class_set_details (element_class, &gst_asf_demux_details);
+  gst_element_class_set_details_simple (element_class, "ASF Demuxer",
+      "Codec/Demuxer",
+      "Demultiplexes ASF Streams", "Owen Fraser-Green <owen@discobabe.net>");
 }
 
 static void
@@ -143,8 +148,6 @@ gst_asf_demux_class_init (GstASFDemuxClass * klass)
 static void
 gst_asf_demux_free_stream (GstASFDemux * demux, AsfStream * stream)
 {
-  gst_buffer_replace (&stream->cache, NULL);
-  gst_buffer_replace (&stream->payload, NULL);
   gst_caps_replace (&stream->caps, NULL);
   if (stream->pending_tags) {
     gst_tag_list_free (stream->pending_tags);
@@ -157,7 +160,23 @@ gst_asf_demux_free_stream (GstASFDemux * demux, AsfStream * stream)
       gst_object_unref (stream->pad);
     stream->pad = NULL;
   }
+
+  while (stream->payloads->len > 0) {
+    AsfPayload *payload;
+    guint last;
+
+    last = stream->payloads->len - 1;
+    payload = &g_array_index (stream->payloads, AsfPayload, last);
+    gst_buffer_replace (&payload->buf, NULL);
+    g_array_remove_index (stream->payloads, last);
+  }
   if (stream->payloads) {
+    int i;
+    for (i = 0; i < stream->payloads->len; i++) {
+      AsfPayload *payload;
+      payload = &g_array_index (stream->payloads, AsfPayload, i);
+      gst_buffer_replace (&payload->buf, NULL);
+    }
     g_array_free (stream->payloads, TRUE);
     stream->payloads = NULL;
   }
@@ -168,13 +187,13 @@ gst_asf_demux_free_stream (GstASFDemux * demux, AsfStream * stream)
 }
 
 static void
-gst_asf_demux_reset (GstASFDemux * demux)
+gst_asf_demux_reset (GstASFDemux * demux, gboolean chain_reset)
 {
   GST_LOG_OBJECT (demux, "resetting");
 
   gst_segment_init (&demux->segment, GST_FORMAT_UNDEFINED);
   demux->segment_running = FALSE;
-  if (demux->adapter) {
+  if (demux->adapter && !chain_reset) {
     gst_adapter_clear (demux->adapter);
     g_object_unref (demux->adapter);
     demux->adapter = NULL;
@@ -187,6 +206,11 @@ gst_asf_demux_reset (GstASFDemux * demux)
     gst_caps_unref (demux->metadata);
     demux->metadata = NULL;
   }
+  if (demux->global_metadata) {
+    gst_structure_free (demux->global_metadata);
+    demux->global_metadata = NULL;
+  }
+
   demux->state = GST_ASF_DEMUX_STATE_HEADER;
   g_free (demux->objpath);
   demux->objpath = NULL;
@@ -197,16 +221,40 @@ gst_asf_demux_reset (GstASFDemux * demux)
       NULL);
   g_slist_free (demux->ext_stream_props);
   demux->ext_stream_props = NULL;
+
+  while (demux->old_num_streams > 0) {
+    gst_asf_demux_free_stream (demux,
+        &demux->old_stream[demux->old_num_streams - 1]);
+    --demux->old_num_streams;
+  }
+  memset (demux->old_stream, 0, sizeof (demux->old_stream));
+  demux->old_num_streams = 0;
+
+  /* when resetting for a new chained asf, we don't want to remove the pads
+   * before adding the new ones */
+  if (chain_reset) {
+    memcpy (demux->old_stream, demux->stream, sizeof (demux->stream));
+    demux->old_num_streams = demux->num_streams;
+    demux->num_streams = 0;
+  }
+
   while (demux->num_streams > 0) {
     gst_asf_demux_free_stream (demux, &demux->stream[demux->num_streams - 1]);
     --demux->num_streams;
   }
   memset (demux->stream, 0, sizeof (demux->stream));
-  demux->num_audio_streams = 0;
-  demux->num_video_streams = 0;
+  if (!chain_reset) {
+    /* do not remove those for not adding pads with same name */
+    demux->num_audio_streams = 0;
+    demux->num_video_streams = 0;
+  }
   demux->num_streams = 0;
   demux->activated_streams = FALSE;
   demux->first_ts = GST_CLOCK_TIME_NONE;
+  demux->segment_ts = GST_CLOCK_TIME_NONE;
+  demux->in_gap = 0;
+  if (!chain_reset)
+    gst_segment_init (&demux->in_segment, GST_FORMAT_UNDEFINED);
   demux->state = GST_ASF_DEMUX_STATE_HEADER;
   demux->seekable = FALSE;
   demux->broadcast = FALSE;
@@ -214,6 +262,23 @@ gst_asf_demux_reset (GstASFDemux * demux)
   demux->sidx_num_entries = 0;
   g_free (demux->sidx_entries);
   demux->sidx_entries = NULL;
+
+  demux->speed_packets = 1;
+
+  if (chain_reset) {
+    GST_LOG_OBJECT (demux, "Restarting");
+    gst_segment_init (&demux->segment, GST_FORMAT_TIME);
+    demux->need_newsegment = TRUE;
+    demux->segment_running = FALSE;
+    demux->accurate = FALSE;
+    demux->metadata = gst_caps_new_empty ();
+    demux->global_metadata = gst_structure_empty_new ("metadata");
+    demux->data_size = 0;
+    demux->data_offset = 0;
+    demux->index_offset = 0;
+  } else {
+    demux->base_offset = 0;
+  }
 }
 
 static void
@@ -234,7 +299,7 @@ gst_asf_demux_init (GstASFDemux * demux, GstASFDemuxClass * klass)
   gst_element_add_pad (GST_ELEMENT (demux), demux->sinkpad);
 
   /* set initial state */
-  gst_asf_demux_reset (demux);
+  gst_asf_demux_reset (demux, FALSE);
 }
 
 static gboolean
@@ -291,10 +356,12 @@ gst_asf_demux_sink_event (GstPad * pad, GstEvent * event)
   switch (GST_EVENT_TYPE (event)) {
     case GST_EVENT_NEWSEGMENT:{
       GstFormat newsegment_format;
-      gint64 newsegment_start;
+      gint64 newsegment_start, stop, time;
+      gdouble rate, arate;
+      gboolean update;
 
-      gst_event_parse_new_segment (event, NULL, NULL, &newsegment_format,
-          &newsegment_start, NULL, NULL);
+      gst_event_parse_new_segment_full (event, &update, &rate, &arate,
+          &newsegment_format, &newsegment_start, &stop, &time);
 
       if (newsegment_format == GST_FORMAT_BYTES) {
         if (demux->packet_size && newsegment_start > demux->data_offset)
@@ -306,14 +373,21 @@ gst_asf_demux_sink_event (GstPad * pad, GstEvent * event)
         /* do not know packet position, not really a problem */
         demux->packet = -1;
       } else {
-        GST_WARNING_OBJECT (demux, "unsupported newsegment format , ignoring");
+        GST_WARNING_OBJECT (demux, "unsupported newsegment format, ignoring");
         gst_event_unref (event);
         break;
       }
 
+      /* record upstream segment for interpolation */
+      if (newsegment_format != demux->in_segment.format)
+        gst_segment_init (&demux->in_segment, GST_FORMAT_UNDEFINED);
+      gst_segment_set_newsegment_full (&demux->in_segment, update, rate, arate,
+          newsegment_format, newsegment_start, stop, time);
+
       /* in either case, clear some state and generate newsegment later on */
       GST_OBJECT_LOCK (demux);
-      demux->first_ts = GST_CLOCK_TIME_NONE;
+      demux->segment_ts = GST_CLOCK_TIME_NONE;
+      demux->in_gap = GST_CLOCK_TIME_NONE;
       demux->need_newsegment = TRUE;
       gst_asf_demux_reset_stream_state_after_discont (demux);
       GST_OBJECT_UNLOCK (demux);
@@ -322,12 +396,22 @@ gst_asf_demux_sink_event (GstPad * pad, GstEvent * event)
       break;
     }
     case GST_EVENT_EOS:{
+      GstFlowReturn flow;
+
       if (demux->state == GST_ASF_DEMUX_STATE_HEADER) {
         GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
             (_("This stream contains no data.")),
             ("got eos and didn't receive a complete header object"));
         break;
       }
+      flow = gst_asf_demux_push_complete_payloads (demux, TRUE);
+      if (flow < GST_FLOW_UNEXPECTED || flow == GST_FLOW_NOT_LINKED) {
+        GST_ELEMENT_ERROR (demux, STREAM, FAILED,
+            (_("Internal data stream error.")),
+            ("streaming stopped, reason %s", gst_flow_get_name (flow)));
+        break;
+      }
+
       GST_OBJECT_LOCK (demux);
       gst_adapter_clear (demux->adapter);
       GST_OBJECT_UNLOCK (demux);
@@ -340,6 +424,9 @@ gst_asf_demux_sink_event (GstPad * pad, GstEvent * event)
       gst_asf_demux_reset_stream_state_after_discont (demux);
       GST_OBJECT_UNLOCK (demux);
       gst_asf_demux_send_event_unlocked (demux, event);
+      /* upon activation, latency is no longer introduced, e.g. after seek */
+      if (demux->activated_streams)
+        demux->latency = 0;
       break;
 
     default:
@@ -353,22 +440,24 @@ gst_asf_demux_sink_event (GstPad * pad, GstEvent * event)
 
 static gboolean
 gst_asf_demux_seek_index_lookup (GstASFDemux * demux, guint * packet,
-    GstClockTime seek_time, GstClockTime * p_idx_time)
+    GstClockTime seek_time, GstClockTime * p_idx_time, guint * speed)
 {
   GstClockTime idx_time;
   guint idx;
 
-  if (demux->sidx_num_entries == 0 || demux->sidx_interval == 0)
+  if (G_UNLIKELY (demux->sidx_num_entries == 0 || demux->sidx_interval == 0))
     return FALSE;
 
-  idx = (guint) (seek_time / demux->sidx_interval);
+  idx = (guint) ((seek_time + demux->preroll) / demux->sidx_interval);
 
   /* FIXME: seek beyond end of file should result in immediate EOS from
    * streaming thread instead of a failed seek */
-  if (idx >= demux->sidx_num_entries)
+  if (G_UNLIKELY (idx >= demux->sidx_num_entries))
     return FALSE;
 
-  *packet = demux->sidx_entries[idx];
+  *packet = demux->sidx_entries[idx].packet;
+  if (speed)
+    *speed = demux->sidx_entries[idx].count;
 
   /* so we get closer to the actual time of the packet ... actually, let's not
    * do this, since we throw away superfluous payloads before the seek position
@@ -380,12 +469,14 @@ gst_asf_demux_seek_index_lookup (GstASFDemux * demux, guint * packet,
    */
 
   idx_time = demux->sidx_interval * idx;
+  if (G_LIKELY (idx_time >= demux->preroll))
+    idx_time -= demux->preroll;
 
   GST_DEBUG_OBJECT (demux, "%" GST_TIME_FORMAT " => packet %u at %"
       GST_TIME_FORMAT, GST_TIME_ARGS (seek_time), *packet,
       GST_TIME_ARGS (idx_time));
 
-  if (p_idx_time)
+  if (G_LIKELY (p_idx_time))
     *p_idx_time = idx_time;
 
   return TRUE;
@@ -396,16 +487,11 @@ gst_asf_demux_reset_stream_state_after_discont (GstASFDemux * demux)
 {
   guint n;
 
-  demux->pts = 0;               //why?
   gst_adapter_clear (demux->adapter);
 
+  GST_DEBUG_OBJECT (demux, "reset stream state");
+
   for (n = 0; n < demux->num_streams; n++) {
-    gst_buffer_replace (&demux->stream[n].payload, NULL);
-    gst_buffer_replace (&demux->stream[n].cache, NULL);
-    demux->stream[n].frag_offset = 0;
-    demux->stream[n].last_pts = GST_CLOCK_TIME_NONE;
-    demux->stream[n].last_buffer_timestamp = GST_CLOCK_TIME_NONE;
-    demux->stream[n].sequence = 0;
     demux->stream[n].discont = TRUE;
     demux->stream[n].last_flow = GST_FLOW_OK;
 
@@ -421,6 +507,17 @@ gst_asf_demux_reset_stream_state_after_discont (GstASFDemux * demux)
   }
 }
 
+static void
+gst_asf_demux_mark_discont (GstASFDemux * demux)
+{
+  guint n;
+
+  GST_DEBUG_OBJECT (demux, "Mark stream discont");
+
+  for (n = 0; n < demux->num_streams; n++)
+    demux->stream[n].discont = TRUE;
+}
+
 /* do a seek in push based mode */
 static gboolean
 gst_asf_demux_handle_seek_push (GstASFDemux * demux, GstEvent * event)
@@ -442,7 +539,7 @@ gst_asf_demux_handle_seek_push (GstASFDemux * demux, GstEvent * event)
   GST_DEBUG_OBJECT (demux, "seeking to %" GST_TIME_FORMAT, GST_TIME_ARGS (cur));
 
   /* determine packet, by index or by estimation */
-  if (!gst_asf_demux_seek_index_lookup (demux, &packet, cur, NULL)) {
+  if (!gst_asf_demux_seek_index_lookup (demux, &packet, cur, NULL, NULL)) {
     packet = (guint) gst_util_uint64_scale (demux->num_packets,
         cur, demux->play_time);
   }
@@ -477,20 +574,19 @@ gst_asf_demux_handle_seek_event (GstASFDemux * demux, GstEvent * event)
   GstFormat format;
   gboolean only_need_update;
   gboolean keyunit_sync;
-  gboolean accurate;
   gboolean flush;
   gdouble rate;
   gint64 cur, stop;
   gint64 seek_time;
-  guint packet;
+  guint packet, speed_count = 1;
 
-  if (demux->seekable == FALSE || demux->packet_size == 0 ||
-      demux->num_packets == 0 || demux->play_time == 0) {
+  if (G_UNLIKELY (demux->seekable == FALSE || demux->packet_size == 0 ||
+          demux->num_packets == 0 || demux->play_time == 0)) {
     GST_LOG_OBJECT (demux, "stream is not seekable");
     return FALSE;
   }
 
-  if (!demux->activated_streams) {
+  if (G_UNLIKELY (!demux->activated_streams)) {
     GST_LOG_OBJECT (demux, "streams not yet activated, ignoring seek");
     return FALSE;
   }
@@ -498,21 +594,22 @@ gst_asf_demux_handle_seek_event (GstASFDemux * demux, GstEvent * event)
   gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur,
       &stop_type, &stop);
 
-  if (format != GST_FORMAT_TIME) {
+  if (G_UNLIKELY (format != GST_FORMAT_TIME)) {
     GST_LOG_OBJECT (demux, "seeking is only supported in TIME format");
     return FALSE;
   }
 
-  if (rate <= 0.0) {
+  if (G_UNLIKELY (rate <= 0.0)) {
     GST_LOG_OBJECT (demux, "backward playback is not supported yet");
     return FALSE;
   }
 
   flush = ((flags & GST_SEEK_FLAG_FLUSH) == GST_SEEK_FLAG_FLUSH);
-  accurate = ((flags & GST_SEEK_FLAG_ACCURATE) == GST_SEEK_FLAG_ACCURATE);
+  demux->accurate =
+      ((flags & GST_SEEK_FLAG_ACCURATE) == GST_SEEK_FLAG_ACCURATE);
   keyunit_sync = ((flags & GST_SEEK_FLAG_KEY_UNIT) == GST_SEEK_FLAG_KEY_UNIT);
 
-  if (demux->streaming) {
+  if (G_UNLIKELY (demux->streaming)) {
     /* support it safely needs more segment handling, e.g. closing etc */
     if (!flush) {
       GST_LOG_OBJECT (demux, "streaming; non-flushing seek not supported");
@@ -533,7 +630,7 @@ gst_asf_demux_handle_seek_event (GstASFDemux * demux, GstEvent * event)
   }
 
   /* unlock the streaming thread */
-  if (flush) {
+  if (G_LIKELY (flush)) {
     gst_pad_push_event (demux->sinkpad, gst_event_new_flush_start ());
     gst_asf_demux_send_event_unlocked (demux, gst_event_new_flush_start ());
   } else {
@@ -548,13 +645,13 @@ gst_asf_demux_handle_seek_event (GstASFDemux * demux, GstEvent * event)
   /* we now can stop flushing, since we have the stream lock now */
   gst_pad_push_event (demux->sinkpad, gst_event_new_flush_stop ());
 
-  if (flush)
+  if (G_LIKELY (flush))
     gst_asf_demux_send_event_unlocked (demux, gst_event_new_flush_stop ());
 
   /* operating on copy of segment until we know the seek worked */
   segment = demux->segment;
 
-  if (demux->segment_running && !flush) {
+  if (G_UNLIKELY (demux->segment_running && !flush)) {
     GstEvent *newseg;
 
     /* create the segment event to close the current segment */
@@ -574,7 +671,8 @@ gst_asf_demux_handle_seek_event (GstASFDemux * demux, GstEvent * event)
 
   /* FIXME: should check the KEY_UNIT flag; need to adjust last_stop to
    * real start of data and segment_start to indexed time for key unit seek*/
-  if (!gst_asf_demux_seek_index_lookup (demux, &packet, seek_time, &idx_time)) {
+  if (G_UNLIKELY (!gst_asf_demux_seek_index_lookup (demux, &packet, seek_time,
+              &idx_time, &speed_count))) {
     /* First try to query our source to see if it can convert for us. This is
        the case when our source is an mms stream, notice that in this case
        gstmms will do a time based seek to get the byte offset, this is not a
@@ -586,13 +684,17 @@ gst_asf_demux_handle_seek_event (GstASFDemux * demux, GstEvent * event)
             &dest_format, &offset) && dest_format == GST_FORMAT_BYTES) {
       packet = (offset - demux->data_offset) / demux->packet_size;
       GST_LOG_OBJECT (demux, "convert %" GST_TIME_FORMAT
-          " to bytes query result: %lld, data_ofset: %llu, packet_size: %u,"
-          " resulting packet: %u\n", GST_TIME_ARGS (seek_time), offset,
-          demux->data_offset, demux->packet_size, packet);
+          " to bytes query result: %" G_GINT64_FORMAT ", data_ofset: %"
+          G_GINT64_FORMAT ", packet_size: %u," " resulting packet: %u\n",
+          GST_TIME_ARGS (seek_time), offset, demux->data_offset,
+          demux->packet_size, packet);
     } else {
-      /* Hackety hack, this sucks. We just seek to an earlier position
-       *  and let the sinks throw away the stuff before the segment start */
-      if (flush && (accurate || keyunit_sync)) {
+      /* FIXME: For streams containing video, seek to an earlier position in
+       * the hope of hitting a keyframe and let the sinks throw away the stuff
+       * before the segment start. For audio-only this is unnecessary as every
+       * frame is 'key'. */
+      if (flush && (demux->accurate || keyunit_sync)
+          && demux->num_video_streams > 0) {
         seek_time -= 5 * GST_SECOND;
         if (seek_time < 0)
           seek_time = 0;
@@ -605,7 +707,7 @@ gst_asf_demux_handle_seek_event (GstASFDemux * demux, GstEvent * event)
         packet = demux->num_packets;
     }
   } else {
-    if (keyunit_sync) {
+    if (G_LIKELY (keyunit_sync)) {
       GST_DEBUG_OBJECT (demux, "key unit seek, adjust seek_time = %"
           GST_TIME_FORMAT " to index_time = %" GST_TIME_FORMAT,
           GST_TIME_ARGS (seek_time), GST_TIME_ARGS (idx_time));
@@ -615,12 +717,13 @@ gst_asf_demux_handle_seek_event (GstASFDemux * demux, GstEvent * event)
     }
   }
 
-  GST_DEBUG_OBJECT (demux, "seeking to packet %u", packet);
+  GST_DEBUG_OBJECT (demux, "seeking to packet %u (%d)", packet, speed_count);
 
   GST_OBJECT_LOCK (demux);
   demux->segment = segment;
   demux->packet = packet;
   demux->need_newsegment = TRUE;
+  demux->speed_packets = speed_count;
   gst_asf_demux_reset_stream_state_after_discont (demux);
   GST_OBJECT_UNLOCK (demux);
 
@@ -684,9 +787,14 @@ typedef struct
   guint64 size;
 } AsfObject;
 
+
+/* expect is true when the user is expeting an object,
+ * when false, it will give no warnings if the object
+ * is not identified
+ */
 static gboolean
 asf_demux_peek_object (GstASFDemux * demux, const guint8 * data,
-    guint data_len, AsfObject * object)
+    guint data_len, AsfObject * object, gboolean expect)
 {
   ASFGuid guid;
 
@@ -702,7 +810,7 @@ asf_demux_peek_object (GstASFDemux * demux, const guint8 * data,
 
   /* FIXME: make asf_demux_identify_object_guid() */
   object->id = gst_asf_demux_identify_guid (asf_object_guids, &guid);
-  if (object->id == ASF_OBJ_UNDEFINED) {
+  if (object->id == ASF_OBJ_UNDEFINED && expect) {
     GST_WARNING_OBJECT (demux, "Unknown object %08x-%08x-%08x-%08x",
         guid.v1, guid.v2, guid.v3, guid.v4);
   }
@@ -710,19 +818,36 @@ asf_demux_peek_object (GstASFDemux * demux, const guint8 * data,
   return TRUE;
 }
 
+static void
+gst_asf_demux_release_old_pads (GstASFDemux * demux)
+{
+  GST_DEBUG_OBJECT (demux, "Releasing old pads");
+
+  while (demux->old_num_streams > 0) {
+    gst_pad_push_event (demux->old_stream[demux->old_num_streams - 1].pad,
+        gst_event_new_eos ());
+    gst_asf_demux_free_stream (demux,
+        &demux->old_stream[demux->old_num_streams - 1]);
+    --demux->old_num_streams;
+  }
+  memset (demux->old_stream, 0, sizeof (demux->old_stream));
+  demux->old_num_streams = 0;
+}
+
 static GstFlowReturn
 gst_asf_demux_chain_headers (GstASFDemux * demux)
 {
   GstFlowReturn flow;
   AsfObject obj;
   guint8 *header_data, *data = NULL;
+  const guint8 *cdata = NULL;
   guint64 header_size;
 
-  data = (guint8 *) gst_adapter_peek (demux->adapter, ASF_OBJECT_HEADER_SIZE);
-  if (data == NULL)
+  cdata = (guint8 *) gst_adapter_peek (demux->adapter, ASF_OBJECT_HEADER_SIZE);
+  if (cdata == NULL)
     goto need_more_data;
 
-  asf_demux_peek_object (demux, data, ASF_OBJECT_HEADER_SIZE, &obj);
+  asf_demux_peek_object (demux, cdata, ASF_OBJECT_HEADER_SIZE, &obj, TRUE);
   if (obj.id != ASF_OBJ_HEADER)
     goto wrong_type;
 
@@ -780,23 +905,33 @@ parse_failed:
 }
 
 static GstFlowReturn
-gst_asf_demux_aggregate_flow_return (GstASFDemux * demux)
+gst_asf_demux_aggregate_flow_return (GstASFDemux * demux, AsfStream * stream,
+    GstFlowReturn flow)
 {
   int i;
+
   GST_DEBUG_OBJECT (demux, "Aggregating");
 
+  /* Store the value */
+  stream->last_flow = flow;
+
+  /* any other error that is not not-linked can be returned right away */
+  if (flow != GST_FLOW_NOT_LINKED)
+    goto done;
+
   for (i = 0; i < demux->num_streams; i++) {
     if (demux->stream[i].active) {
-      GstFlowReturn flowret = demux->stream[i].last_flow;
+      flow = demux->stream[i].last_flow;
       GST_DEBUG_OBJECT (demux, "Aggregating: flow %i return %s", i,
-          gst_flow_get_name (flowret));
-      if (flowret != GST_FLOW_NOT_LINKED)
-        return flowret;
+          gst_flow_get_name (flow));
+      if (flow != GST_FLOW_NOT_LINKED)
+        goto done;
     }
   }
 
   /* If we got here, then all our active streams are not linked */
-  return GST_FLOW_NOT_LINKED;
+done:
+  return flow;
 }
 
 static gboolean
@@ -810,10 +945,10 @@ gst_asf_demux_pull_data (GstASFDemux * demux, guint64 offset, guint size,
 
   flow = gst_pad_pull_range (demux->sinkpad, offset, size, p_buf);
 
-  if (p_flow)
+  if (G_LIKELY (p_flow))
     *p_flow = flow;
 
-  if (flow != GST_FLOW_OK) {
+  if (G_UNLIKELY (flow != GST_FLOW_OK)) {
     GST_DEBUG_OBJECT (demux, "flow %s pulling buffer at %" G_GUINT64_FORMAT
         "+%u", gst_flow_get_name (flow), offset, size);
     *p_buf = NULL;
@@ -822,11 +957,11 @@ gst_asf_demux_pull_data (GstASFDemux * demux, guint64 offset, guint size,
 
   g_assert (*p_buf != NULL);
 
-  if (GST_BUFFER_SIZE (*p_buf) < size) {
+  if (G_UNLIKELY (GST_BUFFER_SIZE (*p_buf) < size)) {
     GST_DEBUG_OBJECT (demux, "short read pulling buffer at %" G_GUINT64_FORMAT
         "+%u (got only %u bytes)", offset, size, GST_BUFFER_SIZE (*p_buf));
     gst_buffer_unref (*p_buf);
-    if (p_flow)
+    if (G_LIKELY (p_flow))
       *p_flow = GST_FLOW_UNEXPECTED;
     *p_buf = NULL;
     return FALSE;
@@ -844,7 +979,7 @@ gst_asf_demux_pull_indices (GstASFDemux * demux)
 
   offset = demux->index_offset;
 
-  if (offset == 0) {
+  if (G_UNLIKELY (offset == 0)) {
     GST_DEBUG_OBJECT (demux, "can't read indices, don't know index offset");
     return;
   }
@@ -853,16 +988,17 @@ gst_asf_demux_pull_indices (GstASFDemux * demux)
     GstFlowReturn flow;
     AsfObject obj;
 
-    asf_demux_peek_object (demux, GST_BUFFER_DATA (buf), 16 + 8, &obj);
+    asf_demux_peek_object (demux, GST_BUFFER_DATA (buf), 16 + 8, &obj, TRUE);
     gst_buffer_replace (&buf, NULL);
 
     /* check for sanity */
-    if (obj.size > (5 * 1024 * 1024)) {
+    if (G_UNLIKELY (obj.size > (5 * 1024 * 1024))) {
       GST_DEBUG_OBJECT (demux, "implausible index object size, bailing out");
       break;
     }
 
-    if (!gst_asf_demux_pull_data (demux, offset, obj.size, &buf, NULL))
+    if (G_UNLIKELY (!gst_asf_demux_pull_data (demux, offset, obj.size, &buf,
+                NULL)))
       break;
 
     GST_LOG_OBJECT (demux, "index object at offset 0x%" G_GINT64_MODIFIER "X"
@@ -873,7 +1009,7 @@ gst_asf_demux_pull_indices (GstASFDemux * demux)
     flow = gst_asf_demux_process_object (demux, &buf->data, &obj.size);
     gst_buffer_replace (&buf, NULL);
 
-    if (flow != GST_FLOW_OK)
+    if (G_UNLIKELY (flow != GST_FLOW_OK))
       break;
 
     ++num_read;
@@ -886,7 +1022,7 @@ gst_asf_demux_parse_data_object_start (GstASFDemux * demux, guint8 * data)
 {
   AsfObject obj;
 
-  asf_demux_peek_object (demux, data, 50, &obj);
+  asf_demux_peek_object (demux, data, 50, &obj, TRUE);
   if (obj.id != ASF_OBJ_DATA) {
     GST_WARNING_OBJECT (demux, "headers not followed by a DATA object");
     return FALSE;
@@ -943,10 +1079,10 @@ gst_asf_demux_pull_headers (GstASFDemux * demux)
   GST_LOG_OBJECT (demux, "reading headers");
 
   /* pull HEADER object header, so we know its size */
-  if (!gst_asf_demux_pull_data (demux, 0, 16 + 8, &buf, NULL))
+  if (!gst_asf_demux_pull_data (demux, demux->base_offset, 16 + 8, &buf, NULL))
     goto read_failed;
 
-  asf_demux_peek_object (demux, GST_BUFFER_DATA (buf), 16 + 8, &obj);
+  asf_demux_peek_object (demux, GST_BUFFER_DATA (buf), 16 + 8, &obj, TRUE);
   gst_buffer_replace (&buf, NULL);
 
   if (obj.id != ASF_OBJ_HEADER)
@@ -955,7 +1091,8 @@ gst_asf_demux_pull_headers (GstASFDemux * demux)
   GST_LOG_OBJECT (demux, "header size = %u", (guint) obj.size);
 
   /* pull HEADER object */
-  if (!gst_asf_demux_pull_data (demux, 0, obj.size, &buf, NULL))
+  if (!gst_asf_demux_pull_data (demux, demux->base_offset, obj.size, &buf,
+          NULL))
     goto read_failed;
 
   size = obj.size;              /* don't want obj.size changed */
@@ -968,10 +1105,11 @@ gst_asf_demux_pull_headers (GstASFDemux * demux)
   }
 
   /* calculate where the packet data starts */
-  demux->data_offset = obj.size + 50;
+  demux->data_offset = demux->base_offset + obj.size + 50;
 
   /* now pull beginning of DATA object before packet data */
-  if (!gst_asf_demux_pull_data (demux, obj.size, 50, &buf, NULL))
+  if (!gst_asf_demux_pull_data (demux, demux->base_offset + obj.size, 50, &buf,
+          NULL))
     goto read_failed;
 
   if (!gst_asf_demux_parse_data_object_start (demux, GST_BUFFER_DATA (buf)))
@@ -1006,7 +1144,8 @@ all_streams_prerolled (GstASFDemux * demux)
   GstClockTime preroll_time;
   guint i, num_no_data = 0;
 
-  preroll_time = demux->preroll;
+  /* Allow at least 500ms of preroll_time  */
+  preroll_time = MAX (demux->preroll, 500 * GST_MSECOND);
 
   /* returns TRUE as long as there isn't a stream which (a) has data queued
    * and (b) the timestamp of last piece of data queued is < demux->preroll
@@ -1017,7 +1156,7 @@ all_streams_prerolled (GstASFDemux * demux)
     guint last_idx;
 
     stream = &demux->stream[i];
-    if (stream->payloads->len == 0) {
+    if (G_UNLIKELY (stream->payloads->len == 0)) {
       ++num_no_data;
       GST_LOG_OBJECT (stream->pad, "no data queued");
       continue;
@@ -1029,13 +1168,13 @@ all_streams_prerolled (GstASFDemux * demux)
     GST_LOG_OBJECT (stream->pad, "checking if %" GST_TIME_FORMAT " > %"
         GST_TIME_FORMAT, GST_TIME_ARGS (last_payload->ts),
         GST_TIME_ARGS (preroll_time));
-    if (last_payload->ts <= preroll_time) {
+    if (G_UNLIKELY (last_payload->ts <= preroll_time)) {
       GST_LOG_OBJECT (stream->pad, "not beyond preroll point yet");
       return FALSE;
     }
   }
 
-  if (num_no_data == demux->num_streams)
+  if (G_UNLIKELY (num_no_data == demux->num_streams))
     return FALSE;
 
   return TRUE;
@@ -1106,6 +1245,8 @@ gst_asf_demux_check_activate_streams (GstASFDemux * demux, gboolean force)
     }
   }
 
+  gst_asf_demux_release_old_pads (demux);
+
   demux->activated_streams = TRUE;
   GST_LOG_OBJECT (demux, "signalling no more pads");
   gst_element_no_more_pads (GST_ELEMENT (demux));
@@ -1138,19 +1279,24 @@ gst_asf_demux_find_stream_with_complete_payload (GstASFDemux * demux)
 
       last_idx = stream->payloads->len - 1;
       payload = &g_array_index (stream->payloads, AsfPayload, last_idx);
-      if (GST_CLOCK_TIME_IS_VALID (payload->ts) &&
-          payload->ts < demux->segment.start) {
-        GST_DEBUG_OBJECT (stream->pad, "Last queued payload has timestamp %"
-            GST_TIME_FORMAT " which is before our segment start %"
-            GST_TIME_FORMAT ", not pushing yet", GST_TIME_ARGS (payload->ts),
-            GST_TIME_ARGS (demux->segment.start));
-        continue;
+      if (G_UNLIKELY (GST_CLOCK_TIME_IS_VALID (payload->ts) &&
+              (payload->ts < demux->segment.start))) {
+        if (G_UNLIKELY ((!demux->accurate) && payload->keyframe)) {
+          GST_DEBUG_OBJECT (stream->pad,
+              "Found keyframe, updating segment start to %" GST_TIME_FORMAT,
+              GST_TIME_ARGS (payload->ts));
+          demux->segment.start = payload->ts;
+          demux->segment.time = payload->ts;
+        } else {
+          GST_DEBUG_OBJECT (stream->pad, "Last queued payload has timestamp %"
+              GST_TIME_FORMAT " which is before our segment start %"
+              GST_TIME_FORMAT ", not pushing yet", GST_TIME_ARGS (payload->ts),
+              GST_TIME_ARGS (demux->segment.start));
+          continue;
+        }
       }
-    }
 
-    /* Now see if there's a complete payload queued for this stream */
-    if (stream->payloads->len > 0) {
-      AsfPayload *payload;
+      /* Now see if there's a complete payload queued for this stream */
 
       payload = &g_array_index (stream->payloads, AsfPayload, 0);
       if (!gst_asf_payload_is_complete (payload))
@@ -1171,6 +1317,7 @@ static GstFlowReturn
 gst_asf_demux_push_complete_payloads (GstASFDemux * demux, gboolean force)
 {
   AsfStream *stream;
+  GstFlowReturn ret = GST_FLOW_OK;
 
   if (G_UNLIKELY (!demux->activated_streams)) {
     if (!gst_asf_demux_check_activate_streams (demux, force))
@@ -1178,38 +1325,64 @@ gst_asf_demux_push_complete_payloads (GstASFDemux * demux, gboolean force)
     /* streams are now activated */
   }
 
-  /* do we need to send a newsegment event */
-  if (demux->need_newsegment) {
+  /* wait until we had a chance to "lock on" some payload's timestamp */
+  if (G_UNLIKELY (demux->need_newsegment
+          && !GST_CLOCK_TIME_IS_VALID (demux->segment_ts)))
+    return GST_FLOW_OK;
 
-    /* wait until we had a chance to "lock on" some payload's timestamp */
-    if (!GST_CLOCK_TIME_IS_VALID (demux->first_ts))
-      return GST_FLOW_OK;
+  while ((stream = gst_asf_demux_find_stream_with_complete_payload (demux))) {
+    AsfPayload *payload;
 
-    if (demux->segment.stop == GST_CLOCK_TIME_NONE &&
-        demux->segment.duration > 0) {
-      demux->segment.stop = demux->segment.duration;
-    }
+    payload = &g_array_index (stream->payloads, AsfPayload, 0);
 
-    GST_DEBUG_OBJECT (demux, "sending new-segment event %" GST_SEGMENT_FORMAT,
-        &demux->segment);
+    /* do we need to send a newsegment event */
+    if ((G_UNLIKELY (demux->need_newsegment))) {
 
-    /* note: we fix up all timestamps to start from 0, so this should be ok */
-    gst_asf_demux_send_event_unlocked (demux,
-        gst_event_new_new_segment (FALSE, demux->segment.rate,
-            GST_FORMAT_TIME, demux->segment.start, demux->segment.stop,
-            demux->segment.start));
+      /* safe default if insufficient upstream info */
+      if (!GST_CLOCK_TIME_IS_VALID (demux->in_gap))
+        demux->in_gap = 0;
 
-    demux->need_newsegment = FALSE;
-    demux->segment_running = TRUE;
-  }
+      if (demux->segment.stop == GST_CLOCK_TIME_NONE &&
+          demux->segment.duration > 0) {
+        /* slight HACK; prevent clipping of last bit */
+        demux->segment.stop = demux->segment.duration + demux->in_gap;
+      }
 
-  while ((stream = gst_asf_demux_find_stream_with_complete_payload (demux))) {
-    AsfPayload *payload;
+      /* FIXME : only if ACCURATE ! */
+      if (G_LIKELY (!demux->accurate
+              && (GST_CLOCK_TIME_IS_VALID (payload->ts)))) {
+        GST_DEBUG ("Adjusting newsegment start to %" GST_TIME_FORMAT,
+            GST_TIME_ARGS (payload->ts));
+        demux->segment.start = payload->ts;
+        demux->segment.time = payload->ts;
+      }
 
-    payload = &g_array_index (stream->payloads, AsfPayload, 0);
+      GST_DEBUG_OBJECT (demux, "sending new-segment event %" GST_SEGMENT_FORMAT,
+          &demux->segment);
+
+      /* note: we fix up all timestamps to start from 0, so this should be ok */
+      gst_asf_demux_send_event_unlocked (demux,
+          gst_event_new_new_segment (FALSE, demux->segment.rate,
+              GST_FORMAT_TIME, demux->segment.start, demux->segment.stop,
+              demux->segment.start));
+
+      /* now post any global tags we may have found */
+      if (demux->taglist == NULL)
+        demux->taglist = gst_tag_list_new ();
+
+      gst_tag_list_add (demux->taglist, GST_TAG_MERGE_REPLACE,
+          GST_TAG_CONTAINER_FORMAT, "ASF", NULL);
+
+      GST_DEBUG_OBJECT (demux, "global tags: %" GST_PTR_FORMAT, demux->taglist);
+      gst_element_found_tags (GST_ELEMENT (demux), demux->taglist);
+      demux->taglist = NULL;
+
+      demux->need_newsegment = FALSE;
+      demux->segment_running = TRUE;
+    }
 
     /* Do we have tags pending for this stream? */
-    if (stream->pending_tags) {
+    if (G_UNLIKELY (stream->pending_tags)) {
       GST_LOG_OBJECT (stream->pad, "%" GST_PTR_FORMAT, stream->pending_tags);
       gst_element_found_tags_for_pad (GST_ELEMENT (demux), stream->pad,
           stream->pending_tags);
@@ -1219,25 +1392,55 @@ gst_asf_demux_push_complete_payloads (GstASFDemux * demux, gboolean force)
     /* We have the whole packet now so we should push the packet to
      * the src pad now. First though we should check if we need to do
      * descrambling */
-    if (demux->span > 1) {
+    if (G_UNLIKELY (demux->span > 1)) {
       gst_asf_demux_descramble_buffer (demux, stream, &payload->buf);
     }
 
     payload->buf = gst_buffer_make_metadata_writable (payload->buf);
 
-    if (!payload->keyframe) {
+    if (G_LIKELY (!payload->keyframe)) {
       GST_BUFFER_FLAG_SET (payload->buf, GST_BUFFER_FLAG_DELTA_UNIT);
     }
 
-    if (stream->discont) {
+    if (G_UNLIKELY (stream->discont)) {
+      GST_DEBUG_OBJECT (stream->pad, "marking DISCONT on stream");
       GST_BUFFER_FLAG_SET (payload->buf, GST_BUFFER_FLAG_DISCONT);
       stream->discont = FALSE;
     }
 
+    if (G_UNLIKELY (stream->is_video && payload->par_x && payload->par_y &&
+            (payload->par_x != stream->par_x) &&
+            (payload->par_y != stream->par_y))) {
+      GST_DEBUG ("Updating PAR (%d/%d => %d/%d)",
+          stream->par_x, stream->par_y, payload->par_x, payload->par_y);
+      stream->par_x = payload->par_x;
+      stream->par_y = payload->par_y;
+      gst_caps_set_simple (stream->caps, "pixel-aspect-ratio",
+          GST_TYPE_FRACTION, stream->par_x, stream->par_y, NULL);
+      gst_pad_set_caps (stream->pad, stream->caps);
+    }
+
+    if (G_UNLIKELY (stream->interlaced != payload->interlaced)) {
+      GST_DEBUG ("Updating interlaced status (%d => %d)", stream->interlaced,
+          payload->interlaced);
+      stream->interlaced = payload->interlaced;
+      gst_caps_set_simple (stream->caps, "interlaced", G_TYPE_BOOLEAN,
+          stream->interlaced, NULL);
+    }
+
     gst_buffer_set_caps (payload->buf, stream->caps);
 
-    GST_BUFFER_TIMESTAMP (payload->buf) = payload->ts;
-    GST_BUFFER_DURATION (payload->buf) = payload->duration;
+    /* (sort of) interpolate timestamps using upstream "frame of reference",
+     * typically useful for live src, but might (unavoidably) mess with
+     * position reporting if a live src is playing not so live content
+     * (e.g. rtspsrc taking some time to fall back to tcp) */
+    GST_BUFFER_TIMESTAMP (payload->buf) = payload->ts + demux->in_gap;
+    if (payload->duration == GST_CLOCK_TIME_NONE
+        && stream->ext_props.avg_time_per_frame != 0)
+      GST_BUFFER_DURATION (payload->buf) =
+          stream->ext_props.avg_time_per_frame * 100;
+    else
+      GST_BUFFER_DURATION (payload->buf) = payload->duration;
 
     /* FIXME: we should really set durations on buffers if we can */
 
@@ -1247,12 +1450,63 @@ gst_asf_demux_push_complete_payloads (GstASFDemux * demux, gboolean force)
         GST_TIME_ARGS (GST_BUFFER_DURATION (payload->buf)),
         GST_BUFFER_SIZE (payload->buf));
 
-    stream->last_flow = gst_pad_push (stream->pad, payload->buf);
+    ret = gst_pad_push (stream->pad, payload->buf);
+    ret = gst_asf_demux_aggregate_flow_return (demux, stream, ret);
     payload->buf = NULL;
     g_array_remove_index (stream->payloads, 0);
+
+    /* Break out as soon as we have an issue */
+    if (G_UNLIKELY (ret != GST_FLOW_OK))
+      break;
   }
 
-  return gst_asf_demux_aggregate_flow_return (demux);
+  return ret;
+}
+
+static gboolean
+gst_asf_demux_check_buffer_is_header (GstASFDemux * demux, GstBuffer * buf)
+{
+  AsfObject obj;
+  g_assert (buf != NULL);
+
+  GST_LOG_OBJECT (demux, "Checking if buffer is a header");
+
+  /* we return false on buffer too small */
+  if (GST_BUFFER_SIZE (buf) < ASF_OBJECT_HEADER_SIZE)
+    return FALSE;
+
+  /* check if it is a header */
+  asf_demux_peek_object (demux, GST_BUFFER_DATA (buf),
+      ASF_OBJECT_HEADER_SIZE, &obj, TRUE);
+  if (obj.id == ASF_OBJ_HEADER) {
+    return TRUE;
+  }
+  return FALSE;
+}
+
+static gboolean
+gst_asf_demux_check_chained_asf (GstASFDemux * demux)
+{
+  guint64 off = demux->data_offset + (demux->packet * demux->packet_size);
+  GstFlowReturn ret = GST_FLOW_OK;
+  GstBuffer *buf = NULL;
+  gboolean header = FALSE;
+
+  /* TODO maybe we should skip index objects after the data and look
+   * further for a new header */
+  if (gst_asf_demux_pull_data (demux, off, ASF_OBJECT_HEADER_SIZE, &buf, &ret)) {
+    g_assert (buf != NULL);
+    /* check if it is a header */
+    if (gst_asf_demux_check_buffer_is_header (demux, buf)) {
+      GST_DEBUG_OBJECT (demux, "new base offset: %" G_GUINT64_FORMAT, off);
+      demux->base_offset = off;
+      header = TRUE;
+    }
+
+    gst_buffer_unref (buf);
+  }
+
+  return header;
 }
 
 static void
@@ -1261,8 +1515,9 @@ gst_asf_demux_loop (GstASFDemux * demux)
   GstFlowReturn flow = GST_FLOW_OK;
   GstBuffer *buf = NULL;
   guint64 off;
+  gboolean sent_eos = FALSE;
 
-  if (demux->state == GST_ASF_DEMUX_STATE_HEADER) {
+  if (G_UNLIKELY (demux->state == GST_ASF_DEMUX_STATE_HEADER)) {
     if (!gst_asf_demux_pull_headers (demux)) {
       flow = GST_FLOW_ERROR;
       goto pause;
@@ -1273,7 +1528,8 @@ gst_asf_demux_loop (GstASFDemux * demux)
 
   g_assert (demux->state == GST_ASF_DEMUX_STATE_DATA);
 
-  if (demux->num_packets != 0 && demux->packet >= demux->num_packets)
+  if (G_UNLIKELY (demux->num_packets != 0
+          && demux->packet >= demux->num_packets))
     goto eos;
 
   GST_LOG_OBJECT (demux, "packet %u/%u", (guint) demux->packet + 1,
@@ -1281,34 +1537,87 @@ gst_asf_demux_loop (GstASFDemux * demux)
 
   off = demux->data_offset + (demux->packet * demux->packet_size);
 
-  if (!gst_asf_demux_pull_data (demux, off, demux->packet_size, &buf, &flow)) {
+  if (G_UNLIKELY (!gst_asf_demux_pull_data (demux, off,
+              demux->packet_size * demux->speed_packets, &buf, &flow))) {
     GST_DEBUG_OBJECT (demux, "got flow %s", gst_flow_get_name (flow));
     if (flow == GST_FLOW_UNEXPECTED)
       goto eos;
-    else if (!GST_FLOW_IS_FATAL (flow)) {
+    else if (flow == GST_FLOW_WRONG_STATE) {
       GST_DEBUG_OBJECT (demux, "Not fatal");
       goto pause;
     } else
       goto read_failed;
   }
 
-  /* FIXME: maybe we should just skip broken packets and error out only
-   * after a few broken packets in a row? */
-  if (!gst_asf_demux_parse_packet (demux, buf))
-    goto parse_error;
+  if (G_LIKELY (demux->speed_packets == 1)) {
+    /* FIXME: maybe we should just skip broken packets and error out only
+     * after a few broken packets in a row? */
+    if (G_UNLIKELY (!gst_asf_demux_parse_packet (demux, buf))) {
+      /* when we don't know when the data object ends, we should check
+       * for a chained asf */
+      if (demux->num_packets == 0) {
+        if (gst_asf_demux_check_buffer_is_header (demux, buf)) {
+          GST_INFO_OBJECT (demux, "Chained asf found");
+          demux->base_offset = off;
+          gst_asf_demux_reset (demux, TRUE);
+          gst_buffer_unref (buf);
+          return;
+        }
+      }
+      goto parse_error;
+    }
 
-  gst_buffer_unref (buf);
+    flow = gst_asf_demux_push_complete_payloads (demux, FALSE);
 
-  flow = gst_asf_demux_push_complete_payloads (demux, FALSE);
+    ++demux->packet;
 
-  ++demux->packet;
+  } else {
+    guint n;
+    for (n = 0; n < demux->speed_packets; n++) {
+      GstBuffer *sub;
+
+      sub =
+          gst_buffer_create_sub (buf, n * demux->packet_size,
+          demux->packet_size);
+      /* FIXME: maybe we should just skip broken packets and error out only
+       * after a few broken packets in a row? */
+      if (G_UNLIKELY (!gst_asf_demux_parse_packet (demux, sub))) {
+        /* when we don't know when the data object ends, we should check
+         * for a chained asf */
+        if (demux->num_packets == 0) {
+          if (gst_asf_demux_check_buffer_is_header (demux, sub)) {
+            GST_INFO_OBJECT (demux, "Chained asf found");
+            demux->base_offset = off + n * demux->packet_size;
+            gst_asf_demux_reset (demux, TRUE);
+            gst_buffer_unref (sub);
+            gst_buffer_unref (buf);
+            return;
+          }
+        }
+        goto parse_error;
+      }
 
-  if (demux->num_packets > 0 && demux->packet >= demux->num_packets) {
+      gst_buffer_unref (sub);
+
+      flow = gst_asf_demux_push_complete_payloads (demux, FALSE);
+
+      ++demux->packet;
+
+    }
+
+    /* reset speed pull */
+    demux->speed_packets = 1;
+  }
+
+  gst_buffer_unref (buf);
+
+  if (G_UNLIKELY (demux->num_packets > 0
+          && demux->packet >= demux->num_packets)) {
     GST_LOG_OBJECT (demux, "reached EOS");
     goto eos;
   }
 
-  if (flow != GST_FLOW_OK) {
+  if (G_UNLIKELY (flow != GST_FLOW_OK)) {
     GST_DEBUG_OBJECT (demux, "pushing complete payloads failed");
     goto pause;
   }
@@ -1324,13 +1633,9 @@ eos:
      * less data queued than required for preroll; force stream activation and
      * send any pending payloads before sending EOS */
     if (!demux->activated_streams)
-      flow = gst_asf_demux_push_complete_payloads (demux, TRUE);
-
-    if (GST_FLOW_IS_FATAL (flow) || flow == GST_FLOW_NOT_LINKED) {
-      GST_DEBUG_OBJECT (demux, "pushing complete payloads failed");
-      goto pause;
-    }
+      gst_asf_demux_push_complete_payloads (demux, TRUE);
 
+    /* we want to push an eos or post a segment-done in any case */
     if (demux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
       gint64 stop;
 
@@ -1343,26 +1648,37 @@ eos:
       gst_element_post_message (GST_ELEMENT_CAST (demux),
           gst_message_new_segment_done (GST_OBJECT (demux), GST_FORMAT_TIME,
               stop));
-    } else {
-      /* normal playback, send EOS to all linked pads */
-      GST_INFO_OBJECT (demux, "Sending EOS, at end of stream");
-      gst_asf_demux_send_event_unlocked (demux, gst_event_new_eos ());
+    } else if (flow != GST_FLOW_UNEXPECTED) {
+      /* check if we have a chained asf, in case, we don't eos yet */
+      if (gst_asf_demux_check_chained_asf (demux)) {
+        GST_INFO_OBJECT (demux, "Chained ASF starting");
+        gst_asf_demux_reset (demux, TRUE);
+        return;
+      }
     }
+    /* normal playback, send EOS to all linked pads */
+    GST_INFO_OBJECT (demux, "Sending EOS, at end of stream");
+    gst_asf_demux_send_event_unlocked (demux, gst_event_new_eos ());
+    sent_eos = TRUE;
     /* ... and fall through to pause */
-    GST_DEBUG_OBJECT (demux, "EOSing");
   }
 pause:
   {
-    GST_DEBUG_OBJECT (demux, "pausing task");
+    GST_DEBUG_OBJECT (demux, "pausing task, flow return: %s",
+        gst_flow_get_name (flow));
     demux->segment_running = FALSE;
     gst_pad_pause_task (demux->sinkpad);
 
     /* For the error cases (not EOS) */
-    if (GST_FLOW_IS_FATAL (flow) || flow == GST_FLOW_NOT_LINKED) {
-      /* Post an error. Hopefully something else already has, but if not... */
-      GST_ELEMENT_ERROR (demux, STREAM, FAILED,
-          (_("Internal data stream error.")),
-          ("streaming stopped, reason %s", gst_flow_get_name (flow)));
+    if (!sent_eos) {
+      if (flow == GST_FLOW_UNEXPECTED)
+        gst_asf_demux_send_event_unlocked (demux, gst_event_new_eos ());
+      else if (flow < GST_FLOW_UNEXPECTED || flow == GST_FLOW_NOT_LINKED) {
+        /* Post an error. Hopefully something else already has, but if not... */
+        GST_ELEMENT_ERROR (demux, STREAM, FAILED,
+            (_("Internal data stream error.")),
+            ("streaming stopped, reason %s", gst_flow_get_name (flow)));
+      }
     }
     return;
   }
@@ -1386,6 +1702,27 @@ parse_error:
   }
 }
 
+#define GST_ASF_DEMUX_CHECK_HEADER_YES       0
+#define GST_ASF_DEMUX_CHECK_HEADER_NO        1
+#define GST_ASF_DEMUX_CHECK_HEADER_NEED_DATA 2
+
+static gint
+gst_asf_demux_check_header (GstASFDemux * demux)
+{
+  AsfObject obj;
+  guint8 *cdata = (guint8 *) gst_adapter_peek (demux->adapter,
+      ASF_OBJECT_HEADER_SIZE);
+  if (cdata == NULL)            /* need more data */
+    return GST_ASF_DEMUX_CHECK_HEADER_NEED_DATA;
+
+  asf_demux_peek_object (demux, cdata, ASF_OBJECT_HEADER_SIZE, &obj, FALSE);
+  if (obj.id != ASF_OBJ_HEADER) {
+    return GST_ASF_DEMUX_CHECK_HEADER_NO;
+  } else {
+    return GST_ASF_DEMUX_CHECK_HEADER_YES;
+  }
+}
+
 static GstFlowReturn
 gst_asf_demux_chain (GstPad * pad, GstBuffer * buf)
 {
@@ -1394,15 +1731,44 @@ gst_asf_demux_chain (GstPad * pad, GstBuffer * buf)
 
   demux = GST_ASF_DEMUX (GST_PAD_PARENT (pad));
 
-  GST_LOG_OBJECT (demux, "buffer: size=%u, offset=%" G_GINT64_FORMAT,
-      GST_BUFFER_SIZE (buf), GST_BUFFER_OFFSET (buf));
+  GST_LOG_OBJECT (demux, "buffer: size=%u, offset=%" G_GINT64_FORMAT ", time=%"
+      GST_TIME_FORMAT, GST_BUFFER_SIZE (buf), GST_BUFFER_OFFSET (buf),
+      GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
 
-  if (GST_BUFFER_IS_DISCONT (buf))
-    gst_asf_demux_reset_stream_state_after_discont (demux);
+  if (G_UNLIKELY (GST_BUFFER_IS_DISCONT (buf))) {
+    GST_DEBUG_OBJECT (demux, "received DISCONT");
+    gst_asf_demux_mark_discont (demux);
+  }
+
+  if (G_UNLIKELY ((!GST_CLOCK_TIME_IS_VALID (demux->in_gap) &&
+              GST_BUFFER_TIMESTAMP_IS_VALID (buf)))) {
+    demux->in_gap = GST_BUFFER_TIMESTAMP (buf) - demux->in_segment.start;
+    GST_DEBUG_OBJECT (demux, "upstream segment start %" GST_TIME_FORMAT
+        ", interpolation gap: %" GST_TIME_FORMAT,
+        GST_TIME_ARGS (demux->in_segment.start), GST_TIME_ARGS (demux->in_gap));
+  }
 
   gst_adapter_push (demux->adapter, buf);
 
   switch (demux->state) {
+    case GST_ASF_DEMUX_STATE_INDEX:{
+      gint result = gst_asf_demux_check_header (demux);
+      if (result == GST_ASF_DEMUX_CHECK_HEADER_NEED_DATA)       /* need more data */
+        break;
+
+      if (result == GST_ASF_DEMUX_CHECK_HEADER_NO) {
+        /* we don't care about this, probably an index */
+        /* TODO maybe would be smarter to skip all the indices
+         * until we got a new header or EOS to decide */
+        GST_LOG_OBJECT (demux, "Received index object, its EOS");
+        goto eos;
+      } else {
+        GST_INFO_OBJECT (demux, "Chained asf starting");
+        /* cleanup and get ready for a chained asf */
+        gst_asf_demux_reset (demux, TRUE);
+        /* fall through */
+      }
+    }
     case GST_ASF_DEMUX_STATE_HEADER:{
       ret = gst_asf_demux_chain_headers (demux);
       if (demux->state != GST_ASF_DEMUX_STATE_DATA)
@@ -1418,16 +1784,28 @@ gst_asf_demux_chain (GstPad * pad, GstBuffer * buf)
       while (gst_adapter_available (demux->adapter) >= data_size) {
         GstBuffer *buf;
 
-        /* do not overshoot data section when streaming */
-        if (demux->num_packets != 0 && demux->packet >= 0
-            && demux->packet >= demux->num_packets)
-          goto eos;
+        /* we don't know the length of the stream
+         * check for a chained asf everytime */
+        if (demux->num_packets == 0) {
+          gint result = gst_asf_demux_check_header (demux);
+
+          if (result == GST_ASF_DEMUX_CHECK_HEADER_YES) {
+            GST_INFO_OBJECT (demux, "Chained asf starting");
+            /* cleanup and get ready for a chained asf */
+            gst_asf_demux_reset (demux, TRUE);
+            break;
+          }
+        } else if (G_UNLIKELY (demux->num_packets != 0 && demux->packet >= 0
+                && demux->packet >= demux->num_packets)) {
+          /* do not overshoot data section when streaming */
+          break;
+        }
 
         buf = gst_adapter_take_buffer (demux->adapter, data_size);
 
         /* FIXME: maybe we should just skip broken packets and error out only
          * after a few broken packets in a row? */
-        if (!gst_asf_demux_parse_packet (demux, buf)) {
+        if (G_UNLIKELY (!gst_asf_demux_parse_packet (demux, buf))) {
           GST_WARNING_OBJECT (demux, "Parse error");
         }
 
@@ -1438,6 +1816,10 @@ gst_asf_demux_chain (GstPad * pad, GstBuffer * buf)
         if (demux->packet >= 0)
           ++demux->packet;
       }
+      if (G_UNLIKELY (demux->num_packets != 0 && demux->packet >= 0
+              && demux->packet >= demux->num_packets)) {
+        demux->state = GST_ASF_DEMUX_STATE_INDEX;
+      }
       break;
     }
     default:
@@ -1537,10 +1919,10 @@ gst_asf_demux_get_var_length (guint8 type, guint8 ** p_data, guint64 * p_size)
       return gst_asf_demux_get_uint32 (p_data, p_size);
 
     default:
+      g_assert_not_reached ();
       break;
   }
-
-  g_assert_not_reached ();
+  return 0;
 }
 
 static gboolean
@@ -1713,16 +2095,21 @@ gst_asf_demux_setup_pad (GstASFDemux * demux, GstPad * src_pad,
   stream->caps = caps;
   stream->pad = src_pad;
   stream->id = id;
-  stream->frag_offset = 0;
-  stream->sequence = 0;
-  stream->delay = 0;
-  stream->first_pts = GST_CLOCK_TIME_NONE;
-  stream->last_pts = GST_CLOCK_TIME_NONE;
-  stream->last_buffer_timestamp = GST_CLOCK_TIME_NONE;
   stream->fps_known = !is_video;        /* bit hacky for audio */
   stream->is_video = is_video;
   stream->pending_tags = tags;
   stream->discont = TRUE;
+  if (is_video) {
+    GstStructure *st;
+    gint par_x, par_y;
+    st = gst_caps_get_structure (caps, 0);
+    if (gst_structure_get_fraction (st, "pixel-aspect-ratio", &par_x, &par_y) &&
+        par_x > 0 && par_y > 0) {
+      GST_DEBUG ("PAR %d/%d", par_x, par_y);
+      stream->par_x = par_x;
+      stream->par_y = par_y;
+    }
+  }
 
   stream->payloads = g_array_new (FALSE, FALSE, sizeof (AsfPayload));
 
@@ -1834,14 +2221,24 @@ gst_asf_demux_add_video_stream (GstASFDemux * demux,
 
     s = gst_asf_demux_get_metadata_for_stream (demux, id);
     if (gst_structure_get_int (s, "AspectRatioX", &ax) &&
-        gst_structure_get_int (s, "AspectRatioY", &ay)) {
-      /* only copy sane values */
-      if (ax > 0 && ay > 0) {
-        gst_caps_set_simple (caps, "pixel-aspect-ratio", GST_TYPE_FRACTION,
-            ax, ay, NULL);
+        gst_structure_get_int (s, "AspectRatioY", &ay) && (ax > 0 && ay > 0)) {
+      gst_caps_set_simple (caps, "pixel-aspect-ratio", GST_TYPE_FRACTION,
+          ax, ay, NULL);
+
+    } else {
+      guint ax, ay;
+      /* retry with the global metadata */
+      GST_DEBUG ("Retrying with global metadata %" GST_PTR_FORMAT,
+          demux->global_metadata);
+      s = demux->global_metadata;
+      if (gst_structure_get_uint (s, "AspectRatioX", &ax) &&
+          gst_structure_get_uint (s, "AspectRatioY", &ay)) {
+        GST_DEBUG ("ax:%d, ay:%d", ax, ay);
+        if (ax > 0 && ay > 0)
+          gst_caps_set_simple (caps, "pixel-aspect-ratio", GST_TYPE_FRACTION,
+              ax, ay, NULL);
       }
     }
-    /* remove the framerate we will guess and add it later */
     s = gst_caps_get_structure (caps, 0);
     gst_structure_remove_field (s, "framerate");
   }
@@ -1887,13 +2284,13 @@ gst_asf_demux_parse_stream_object (GstASFDemux * demux, guint8 * data,
   AsfCorrectionType correction_type;
   AsfStreamType stream_type;
   GstClockTime time_offset;
-  gboolean is_encrypted;
+  gboolean is_encrypted G_GNUC_UNUSED;
   guint16 stream_id;
   guint16 flags;
   ASFGuid guid;
   guint stream_specific_size;
-  guint type_specific_size;
-  guint unknown;
+  guint type_specific_size G_GNUC_UNUSED;
+  guint unknown G_GNUC_UNUSED;
 
   /* Get the rest of the header's header */
   if (size < (16 + 16 + 8 + 4 + 4 + 2 + 4))
@@ -1912,7 +2309,7 @@ gst_asf_demux_parse_stream_object (GstASFDemux * demux, guint8 * data,
 
   flags = gst_asf_demux_get_uint16 (&data, &size);
   stream_id = flags & 0x7f;
-  is_encrypted = !!((flags & 0x8000) << 15);
+  is_encrypted = ! !((flags & 0x8000) << 15);
   unknown = gst_asf_demux_get_uint32 (&data, &size);
 
   GST_DEBUG_OBJECT (demux, "Found stream %u, time_offset=%" GST_TIME_FORMAT,
@@ -2037,8 +2434,7 @@ not_enough_data:
 }
 
 static const gchar *
-gst_asf_demux_get_gst_tag_from_tag_name (const gchar * name_utf16le,
-    gsize name_len)
+gst_asf_demux_get_gst_tag_from_tag_name (const gchar * name_utf8)
 {
   const struct
   {
@@ -2049,60 +2445,95 @@ gst_asf_demux_get_gst_tag_from_tag_name (const gchar * name_utf16le,
     "WM/Genre", GST_TAG_GENRE}, {
     "WM/AlbumTitle", GST_TAG_ALBUM}, {
     "WM/AlbumArtist", GST_TAG_ARTIST}, {
+    "WM/Picture", GST_TAG_IMAGE}, {
     "WM/Track", GST_TAG_TRACK_NUMBER}, {
+    "WM/TrackNumber", GST_TAG_TRACK_NUMBER}, {
     "WM/Year", GST_TAG_DATE}
     /* { "WM/Composer", GST_TAG_COMPOSER } */
   };
-  gchar *name_utf8;
-  gsize in, out;
+  gsize out;
   guint i;
 
-  /* convert name to UTF-8 */
-  name_utf8 = g_convert (name_utf16le, name_len, "UTF-8", "UTF-16LE", &in,
-      &out, NULL);
-
   if (name_utf8 == NULL) {
     GST_WARNING ("Failed to convert name to UTF8, skipping");
     return NULL;
   }
 
+  out = strlen (name_utf8);
+
   for (i = 0; i < G_N_ELEMENTS (tags); ++i) {
     if (strncmp (tags[i].asf_name, name_utf8, out) == 0) {
       GST_LOG ("map tagname '%s' -> '%s'", name_utf8, tags[i].gst_name);
-      g_free (name_utf8);
       return tags[i].gst_name;
     }
   }
 
-  GST_LOG ("unhandled tagname '%s'", name_utf8);
-  g_free (name_utf8);
   return NULL;
 }
 
-/* gst_asf_demux_commit_taglist() takes ownership of taglist! */
+/* gst_asf_demux_add_global_tags() takes ownership of taglist! */
 static void
-gst_asf_demux_commit_taglist (GstASFDemux * demux, GstTagList * taglist)
+gst_asf_demux_add_global_tags (GstASFDemux * demux, GstTagList * taglist)
 {
-  GST_DEBUG ("Committing tags: %" GST_PTR_FORMAT, taglist);
+  GstTagList *t;
 
-  gst_element_found_tags (GST_ELEMENT (demux), gst_tag_list_copy (taglist));
+  GST_DEBUG_OBJECT (demux, "adding global tags: %" GST_PTR_FORMAT, taglist);
 
-  /* save internally */
-  if (!demux->taglist)
-    demux->taglist = taglist;
-  else {
-    GstTagList *t;
+  if (taglist == NULL)
+    return;
 
-    t = gst_tag_list_merge (demux->taglist, taglist, GST_TAG_MERGE_APPEND);
-    gst_tag_list_free (demux->taglist);
+  if (gst_tag_list_is_empty (taglist)) {
     gst_tag_list_free (taglist);
-    demux->taglist = t;
+    return;
   }
+
+  t = gst_tag_list_merge (demux->taglist, taglist, GST_TAG_MERGE_APPEND);
+  if (demux->taglist)
+    gst_tag_list_free (demux->taglist);
+  gst_tag_list_free (taglist);
+  demux->taglist = t;
+  GST_LOG_OBJECT (demux, "global tags now: %" GST_PTR_FORMAT, demux->taglist);
 }
 
 #define ASF_DEMUX_DATA_TYPE_UTF16LE_STRING  0
+#define ASF_DEMUX_DATA_TYPE_BYTE_ARRAY      1
 #define ASF_DEMUX_DATA_TYPE_DWORD           3
 
+static void
+asf_demux_parse_picture_tag (GstTagList * tags, const guint8 * tag_data,
+    guint tag_data_len)
+{
+  GstByteReader r;
+  const guint8 *img_data = NULL;
+  guint32 img_data_len = 0;
+  guint8 pic_type = 0;
+
+  gst_byte_reader_init (&r, tag_data, tag_data_len);
+
+  /* skip mime type string (we don't trust it and do our own typefinding),
+   * and also skip the description string, since we don't use it */
+  if (!gst_byte_reader_get_uint8 (&r, &pic_type) ||
+      !gst_byte_reader_get_uint32_le (&r, &img_data_len) ||
+      !gst_byte_reader_skip_string_utf16 (&r) ||
+      !gst_byte_reader_skip_string_utf16 (&r) ||
+      !gst_byte_reader_get_data (&r, img_data_len, &img_data)) {
+    goto not_enough_data;
+  }
+
+
+  if (!gst_tag_list_add_id3_image (tags, img_data, img_data_len, pic_type))
+    GST_DEBUG ("failed to add image extracted from WM/Picture tag to taglist");
+
+  return;
+
+not_enough_data:
+  {
+    GST_DEBUG ("Failed to read WM/Picture tag: not enough data");
+    GST_MEMDUMP ("WM/Picture data", tag_data, tag_data_len);
+    return;
+  }
+}
+
 /* Extended Content Description Object */
 static GstFlowReturn
 gst_asf_demux_process_ext_content_desc (GstASFDemux * demux, guint8 * data,
@@ -2153,6 +2584,7 @@ gst_asf_demux_process_ext_content_desc (GstASFDemux * demux, guint8 * data,
     GValue tag_value = { 0, };
     gsize in, out;
     gchar *name;
+    gchar *name_utf8 = NULL;
     gchar *value;
 
     /* Descriptor */
@@ -2172,8 +2604,15 @@ gst_asf_demux_process_ext_content_desc (GstASFDemux * demux, guint8 * data,
       goto not_enough_data;
     }
 
-    gst_tag_name = gst_asf_demux_get_gst_tag_from_tag_name (name, name_len);
-    if (gst_tag_name != NULL) {
+    name_utf8 =
+        g_convert (name, name_len, "UTF-8", "UTF-16LE", &in, &out, NULL);
+
+    if (name_utf8 != NULL) {
+      GST_DEBUG ("Found tag/metadata %s", name_utf8);
+
+      gst_tag_name = gst_asf_demux_get_gst_tag_from_tag_name (name_utf8);
+      GST_DEBUG ("gst_tag_name %s", GST_STR_NULL (gst_tag_name));
+
       switch (datatype) {
         case ASF_DEMUX_DATA_TYPE_UTF16LE_STRING:{
           gchar *value_utf8;
@@ -2183,50 +2622,90 @@ gst_asf_demux_process_ext_content_desc (GstASFDemux * demux, guint8 * data,
 
           /* get rid of tags with empty value */
           if (value_utf8 != NULL && *value_utf8 != '\0') {
+            GST_DEBUG ("string value %s", value_utf8);
+
             value_utf8[out] = '\0';
 
-            if (strcmp (gst_tag_name, GST_TAG_DATE) == 0) {
-              guint year = atoi (value_utf8);
+            if (gst_tag_name != NULL) {
+              if (strcmp (gst_tag_name, GST_TAG_DATE) == 0) {
+                guint year = atoi (value_utf8);
 
-              if (year > 0) {
-                GDate *date = g_date_new_dmy (1, 1, year);
+                if (year > 0) {
+                  GDate *date = g_date_new_dmy (1, 1, year);
 
-                g_value_init (&tag_value, GST_TYPE_DATE);
-                gst_value_set_date (&tag_value, date);
-                g_date_free (date);
-              }
-            } else {
-              GType tag_type;
-
-              /* convert tag from string to other type if required */
-              tag_type = gst_tag_get_type (gst_tag_name);
-              g_value_init (&tag_value, tag_type);
-              if (!gst_value_deserialize (&tag_value, value_utf8)) {
-                GValue from_val = { 0, };
-
-                g_value_init (&from_val, G_TYPE_STRING);
-                g_value_set_string (&from_val, value_utf8);
-                if (!g_value_transform (&from_val, &tag_value)) {
-                  GST_WARNING_OBJECT (demux,
-                      "Could not transform string tag to " "%s tag type %s",
-                      gst_tag_name, g_type_name (tag_type));
-                  g_value_unset (&tag_value);
+                  g_value_init (&tag_value, GST_TYPE_DATE);
+                  gst_value_set_date (&tag_value, date);
+                  g_date_free (date);
+                }
+              } else if (strcmp (gst_tag_name, GST_TAG_GENRE) == 0) {
+                guint id3v1_genre_id;
+                const gchar *genre_str;
+
+                if (sscanf (value_utf8, "(%u)", &id3v1_genre_id) == 1 &&
+                    ((genre_str = gst_tag_id3_genre_get (id3v1_genre_id)))) {
+                  GST_DEBUG ("Genre: %s -> %s", value_utf8, genre_str);
+                  g_free (value_utf8);
+                  value_utf8 = g_strdup (genre_str);
+                }
+              } else {
+                GType tag_type;
+
+                /* convert tag from string to other type if required */
+                tag_type = gst_tag_get_type (gst_tag_name);
+                g_value_init (&tag_value, tag_type);
+                if (!gst_value_deserialize (&tag_value, value_utf8)) {
+                  GValue from_val = { 0, };
+
+                  g_value_init (&from_val, G_TYPE_STRING);
+                  g_value_set_string (&from_val, value_utf8);
+                  if (!g_value_transform (&from_val, &tag_value)) {
+                    GST_WARNING_OBJECT (demux,
+                        "Could not transform string tag to " "%s tag type %s",
+                        gst_tag_name, g_type_name (tag_type));
+                    g_value_unset (&tag_value);
+                  }
+                  g_value_unset (&from_val);
                 }
-                g_value_unset (&from_val);
               }
+            } else {
+              /* metadata ! */
+              GST_DEBUG ("Setting metadata");
+              g_value_init (&tag_value, G_TYPE_STRING);
+              g_value_set_string (&tag_value, value_utf8);
             }
           } else if (value_utf8 == NULL) {
             GST_WARNING ("Failed to convert string value to UTF8, skipping");
           } else {
-            GST_DEBUG ("Skipping empty string value for %s", gst_tag_name);
+            GST_DEBUG ("Skipping empty string value for %s",
+                GST_STR_NULL (gst_tag_name));
           }
           g_free (value_utf8);
           break;
         }
+        case ASF_DEMUX_DATA_TYPE_BYTE_ARRAY:{
+          if (gst_tag_name) {
+            if (!g_str_equal (gst_tag_name, GST_TAG_IMAGE)) {
+              GST_FIXME ("Unhandled byte array tag %s",
+                  GST_STR_NULL (gst_tag_name));
+              break;
+            } else {
+              asf_demux_parse_picture_tag (taglist, (guint8 *) value,
+                  value_len);
+            }
+          }
+          break;
+        }
         case ASF_DEMUX_DATA_TYPE_DWORD:{
+          guint uint_val = GST_READ_UINT32_LE (value);
+
           /* this is the track number */
           g_value_init (&tag_value, G_TYPE_UINT);
-          g_value_set_uint (&tag_value, (guint) GST_READ_UINT32_LE (value));
+
+          /* WM/Track counts from 0 */
+          if (!strcmp (name_utf8, "WM/Track"))
+            ++uint_val;
+
+          g_value_set_uint (&tag_value, uint_val);
           break;
         }
         default:{
@@ -2236,8 +2715,24 @@ gst_asf_demux_process_ext_content_desc (GstASFDemux * demux, guint8 * data,
       }
 
       if (G_IS_VALUE (&tag_value)) {
-        gst_tag_list_add_values (taglist, GST_TAG_MERGE_APPEND,
-            gst_tag_name, &tag_value, NULL);
+        if (gst_tag_name) {
+          GstTagMergeMode merge_mode = GST_TAG_MERGE_APPEND;
+
+          /* WM/TrackNumber is more reliable than WM/Track, since the latter
+           * is supposed to have a 0 base but is often wrongly written to start
+           * from 1 as well, so prefer WM/TrackNumber when we have it: either
+           * replace the value added earlier from WM/Track or put it first in
+           * the list, so that it will get picked up by _get_uint() */
+          if (strcmp (name_utf8, "WM/TrackNumber") == 0)
+            merge_mode = GST_TAG_MERGE_REPLACE;
+
+          gst_tag_list_add_values (taglist, merge_mode, gst_tag_name,
+              &tag_value, NULL);
+        } else {
+          GST_DEBUG ("Setting global metadata %s", name_utf8);
+          gst_structure_set_value (demux->global_metadata, name_utf8,
+              &tag_value);
+        }
 
         g_value_unset (&tag_value);
       }
@@ -2245,13 +2740,10 @@ gst_asf_demux_process_ext_content_desc (GstASFDemux * demux, guint8 * data,
 
     g_free (name);
     g_free (value);
+    g_free (name_utf8);
   }
 
-  if (gst_structure_n_fields (GST_STRUCTURE (taglist)) > 0) {
-    gst_asf_demux_commit_taglist (demux, taglist);
-  } else {
-    gst_tag_list_free (taglist);
-  }
+  gst_asf_demux_add_global_tags (demux, taglist);
 
   return GST_FLOW_OK;
 
@@ -2304,7 +2796,7 @@ gst_asf_demux_process_metadata (GstASFDemux * demux, guint8 * data,
 
   for (i = 0; i < blockcount; ++i) {
     GstStructure *s;
-    guint16 lang_idx, stream_num, name_len, data_type;
+    guint16 stream_num, name_len, data_type, lang_idx G_GNUC_UNUSED;
     guint32 data_len, ival;
     gchar *name_utf8;
 
@@ -2369,7 +2861,7 @@ gst_asf_demux_process_header (GstASFDemux * demux, guint8 * data, guint64 size)
 {
   GstFlowReturn ret = GST_FLOW_OK;
   guint32 i, num_objects;
-  guint8 unknown;
+  guint8 unknown G_GNUC_UNUSED;
 
   /* Get the rest of the header's header */
   if (size < (4 + 1 + 1))
@@ -2404,9 +2896,11 @@ not_enough_data:
 static GstFlowReturn
 gst_asf_demux_process_file (GstASFDemux * demux, guint8 * data, guint64 size)
 {
-  guint64 file_size, creation_time, packets_count;
-  guint64 play_time, send_time, preroll;
-  guint32 flags, min_pktsize, max_pktsize, min_bitrate;
+  guint64 creation_time G_GNUC_UNUSED;
+  guint64 file_size G_GNUC_UNUSED;
+  guint64 send_time G_GNUC_UNUSED;
+  guint64 packets_count, play_time, preroll;
+  guint32 flags, min_pktsize, max_pktsize, min_bitrate G_GNUC_UNUSED;
 
   if (size < (16 + 8 + 8 + 8 + 8 + 8 + 8 + 4 + 4 + 4 + 4))
     goto not_enough_data;
@@ -2423,8 +2917,8 @@ gst_asf_demux_process_file (GstASFDemux * demux, guint8 * data, guint64 size)
   max_pktsize = gst_asf_demux_get_uint32 (&data, &size);
   min_bitrate = gst_asf_demux_get_uint32 (&data, &size);
 
-  demux->broadcast = !!(flags & 0x01);
-  demux->seekable = !!(flags & 0x02);
+  demux->broadcast = ! !(flags & 0x01);
+  demux->seekable = ! !(flags & 0x02);
 
   GST_DEBUG_OBJECT (demux, "min_pktsize = %u", min_pktsize);
   GST_DEBUG_OBJECT (demux, "flags::broadcast = %d", demux->broadcast);
@@ -2548,11 +3042,7 @@ gst_asf_demux_process_comment (GstASFDemux * demux, guint8 * data, guint64 size)
   }
   g_value_unset (&value);
 
-  if (gst_structure_n_fields (GST_STRUCTURE (taglist)) > 0) {
-    gst_asf_demux_commit_taglist (demux, taglist);
-  } else {
-    gst_tag_list_free (taglist);
-  }
+  gst_asf_demux_add_global_tags (demux, taglist);
 
   for (i = 0; i < G_N_ELEMENTS (tags); ++i)
     g_free (tags[i].val_utf8);
@@ -2574,6 +3064,7 @@ gst_asf_demux_process_bitrate_props_object (GstASFDemux * demux, guint8 * data,
     guint64 size)
 {
   guint16 num_streams, i;
+  AsfStream *stream;
 
   if (size < 2)
     goto not_enough_data;
@@ -2594,8 +3085,16 @@ gst_asf_demux_process_bitrate_props_object (GstASFDemux * demux, guint8 * data,
     bitrate = gst_asf_demux_get_uint32 (&data, &size);
 
     if (stream_id < GST_ASF_DEMUX_NUM_STREAM_IDS) {
-      demux->bitrate[stream_id] = bitrate;
-      GST_DEBUG ("bitrate[%u] = %u", stream_id, bitrate);
+      GST_DEBUG_OBJECT (demux, "bitrate of stream %u = %u", stream_id, bitrate);
+      stream = gst_asf_demux_get_stream (demux, stream_id);
+      if (stream) {
+        if (stream->pending_tags == NULL)
+          stream->pending_tags = gst_tag_list_new ();
+        gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE,
+            GST_TAG_BITRATE, bitrate, NULL);
+      } else {
+        GST_WARNING_OBJECT (demux, "Stream id %u wasn't found", stream_id);
+      }
     } else {
       GST_WARNING ("stream id %u is too large", stream_id);
     }
@@ -2707,7 +3206,7 @@ gst_asf_demux_process_simple_index (GstASFDemux * demux, guint8 * data,
     guint64 size)
 {
   GstClockTime interval;
-  guint32 x, count, i;
+  guint32 count, i;
 
   if (size < (16 + 8 + 4 + 4))
     goto not_enough_data;
@@ -2715,19 +3214,22 @@ gst_asf_demux_process_simple_index (GstASFDemux * demux, guint8 * data,
   /* skip file id */
   gst_asf_demux_skip_bytes (16, &data, &size);
   interval = gst_asf_demux_get_uint64 (&data, &size) * (GstClockTime) 100;
-  x = gst_asf_demux_get_uint32 (&data, &size);
+  gst_asf_demux_skip_bytes (4, &data, &size);
   count = gst_asf_demux_get_uint32 (&data, &size);
   if (count > 0) {
     demux->sidx_interval = interval;
     demux->sidx_num_entries = count;
     g_free (demux->sidx_entries);
-    demux->sidx_entries = g_new0 (guint32, count);
+    demux->sidx_entries = g_new0 (AsfSimpleIndexEntry, count);
 
-    for (i = 0; i < count && size > (4 + 2); ++i) {
-      demux->sidx_entries[i] = gst_asf_demux_get_uint32 (&data, &size);
-      x = (guint32) gst_asf_demux_get_uint16 (&data, &size);
-      GST_LOG_OBJECT (demux, "%" GST_TIME_FORMAT " = packet %4u",
-          GST_TIME_ARGS (i * interval), demux->sidx_entries[i]);
+    for (i = 0; i < count; ++i) {
+      if (G_UNLIKELY (size <= 6))
+        break;
+      demux->sidx_entries[i].packet = gst_asf_demux_get_uint32 (&data, &size);
+      demux->sidx_entries[i].count = gst_asf_demux_get_uint16 (&data, &size);
+      GST_LOG_OBJECT (demux, "%" GST_TIME_FORMAT " = packet %4u  count : %2d",
+          GST_TIME_ARGS (i * interval), demux->sidx_entries[i].packet,
+          demux->sidx_entries[i].count);
     }
   } else {
     GST_DEBUG_OBJECT (demux, "simple index object with 0 entries");
@@ -2839,7 +3341,7 @@ gst_asf_demux_process_ext_stream_props (GstASFDemux * demux, guint8 * data,
 
   /* read stream names */
   for (i = 0; i < stream_name_count; ++i) {
-    guint16 stream_lang_idx;
+    guint16 stream_lang_idx G_GNUC_UNUSED;
     gchar *stream_name = NULL;
 
     if (size < 2)
@@ -2890,7 +3392,7 @@ gst_asf_demux_process_ext_stream_props (GstASFDemux * demux, guint8 * data,
   }
 
   /* get size of the stream object */
-  if (!asf_demux_peek_object (demux, data, size, &stream_obj))
+  if (!asf_demux_peek_object (demux, data, size, &stream_obj, TRUE))
     goto not_enough_data;
 
   if (stream_obj.id != ASF_OBJ_STREAM)
@@ -3101,7 +3603,7 @@ gst_asf_demux_process_object (GstASFDemux * demux, guint8 ** p_data,
   if (*p_size < ASF_OBJECT_HEADER_SIZE)
     return ASF_FLOW_NEED_MORE_DATA;
 
-  asf_demux_peek_object (demux, *p_data, ASF_OBJECT_HEADER_SIZE, &obj);
+  asf_demux_peek_object (demux, *p_data, ASF_OBJECT_HEADER_SIZE, &obj, TRUE);
   gst_asf_demux_skip_bytes (ASF_OBJECT_HEADER_SIZE, p_data, p_size);
 
   obj_data_size = obj.size - ASF_OBJECT_HEADER_SIZE;
@@ -3114,15 +3616,10 @@ gst_asf_demux_process_object (GstASFDemux * demux, guint8 ** p_data,
   GST_INFO ("%s: size %" G_GUINT64_FORMAT, demux->objpath, obj.size);
 
   switch (obj.id) {
-    case ASF_OBJ_STREAM:{
-      AsfStream *stream;
-
-      stream =
-          gst_asf_demux_parse_stream_object (demux, *p_data, obj_data_size);
-
+    case ASF_OBJ_STREAM:
+      gst_asf_demux_parse_stream_object (demux, *p_data, obj_data_size);
       ret = GST_FLOW_OK;
       break;
-    }
     case ASF_OBJ_FILE:
       ret = gst_asf_demux_process_file (demux, *p_data, obj_data_size);
       break;
@@ -3173,6 +3670,7 @@ gst_asf_demux_process_object (GstASFDemux * demux, guint8 ** p_data,
     case ASF_OBJ_CONTENT_ENCRYPTION:
     case ASF_OBJ_EXT_CONTENT_ENCRYPTION:
     case ASF_OBJ_DIGITAL_SIGNATURE_OBJECT:
+    case ASF_OBJ_UNKNOWN_ENCRYPTION_OBJECT:
       goto error_encrypted;
     case ASF_OBJ_CONCEAL_NONE:
     case ASF_OBJ_HEAD2:
@@ -3429,6 +3927,35 @@ gst_asf_demux_handle_src_query (GstPad * pad, GstQuery * query)
       break;
     }
 
+    case GST_QUERY_LATENCY:
+    {
+      gboolean live;
+      GstClockTime min, max;
+
+      /* preroll delay does not matter in non-live pipeline,
+       * but we might end up in a live (rtsp) one ... */
+
+      /* first forward */
+      res = gst_pad_query_default (pad, query);
+      if (!res)
+        break;
+
+      gst_query_parse_latency (query, &live, &min, &max);
+
+      GST_DEBUG_OBJECT (demux, "Peer latency: live %d, min %"
+          GST_TIME_FORMAT " max %" GST_TIME_FORMAT, live,
+          GST_TIME_ARGS (min), GST_TIME_ARGS (max));
+
+      GST_OBJECT_LOCK (demux);
+      if (min != -1)
+        min += demux->latency;
+      if (max != -1)
+        max += demux->latency;
+      GST_OBJECT_UNLOCK (demux);
+
+      gst_query_set_latency (query, live, min, max);
+      break;
+    }
     default:
       res = gst_pad_query_default (pad, query);
       break;
@@ -3449,11 +3976,14 @@ gst_asf_demux_change_state (GstElement * element, GstStateChange transition)
       gst_segment_init (&demux->segment, GST_FORMAT_TIME);
       demux->need_newsegment = TRUE;
       demux->segment_running = FALSE;
+      demux->accurate = FALSE;
       demux->adapter = gst_adapter_new ();
       demux->metadata = gst_caps_new_empty ();
+      demux->global_metadata = gst_structure_empty_new ("metadata");
       demux->data_size = 0;
       demux->data_offset = 0;
       demux->index_offset = 0;
+      demux->base_offset = 0;
       break;
     }
     default:
@@ -3467,7 +3997,7 @@ gst_asf_demux_change_state (GstElement * element, GstStateChange transition)
   switch (transition) {
     case GST_STATE_CHANGE_PAUSED_TO_READY:
     case GST_STATE_CHANGE_READY_TO_NULL:
-      gst_asf_demux_reset (demux);
+      gst_asf_demux_reset (demux, FALSE);
       break;
     default:
       break;