demux: Push STREAM_START event when needed
[platform/upstream/gstreamer.git] / gst / flv / gstflvdemux.c
index bc3413a..3913602 100644 (file)
 #include <gst/base/gstbytereader.h>
 #include <gst/pbutils/descriptions.h>
 #include <gst/pbutils/pbutils.h>
+#include <gst/audio/audio.h>
+
+/* FIXME: don't rely on own GstIndex */
+#include "gstindex.c"
+#include "gstmemindex.c"
+#define GST_ASSOCIATION_FLAG_NONE GST_INDEX_ASSOCIATION_FLAG_NONE
+#define GST_ASSOCIATION_FLAG_KEY_UNIT GST_INDEX_ASSOCIATION_FLAG_KEY_UNIT
+#define GST_ASSOCIATION_FLAG_DELTA_UNIT GST_INDEX_ASSOCIATION_FLAG_DELTA_UNIT
 
 static GstStaticPadTemplate flv_sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
     GST_PAD_SINK,
@@ -55,10 +63,9 @@ static GstStaticPadTemplate audio_src_template =
     GST_STATIC_CAPS
     ("audio/x-adpcm, layout = (string) swf, channels = (int) { 1, 2 }, rate = (int) { 5512, 11025, 22050, 44100 }; "
         "audio/mpeg, mpegversion = (int) 1, layer = (int) 3, channels = (int) { 1, 2 }, rate = (int) { 5512, 8000, 11025, 22050, 44100 }, parsed = (boolean) TRUE; "
-        "audio/mpeg, mpegversion = (int) 4, framed = (boolean) TRUE; "
+        "audio/mpeg, mpegversion = (int) 4, stream-format = (string) raw, framed = (boolean) TRUE; "
         "audio/x-nellymoser, channels = (int) { 1, 2 }, rate = (int) { 5512, 8000, 11025, 16000, 22050, 44100 }; "
-        "audio/x-raw-int, endianness = (int) LITTLE_ENDIAN, channels = (int) { 1, 2 }, width = (int) 8, depth = (int) 8, rate = (int) { 5512, 11025, 22050, 44100 }, signed = (boolean) FALSE; "
-        "audio/x-raw-int, endianness = (int) LITTLE_ENDIAN, channels = (int) { 1, 2 }, width = (int) 16, depth = (int) 16, rate = (int) { 5512, 11025, 22050, 44100 }, signed = (boolean) TRUE; "
+        "audio/x-raw, format = (string) { U8, S16LE }, layout = (string) interleaved, channels = (int) { 1, 2 }, rate = (int) { 5512, 11025, 22050, 44100 }; "
         "audio/x-alaw, channels = (int) { 1, 2 }, rate = (int) { 5512, 11025, 22050, 44100 }; "
         "audio/x-mulaw, channels = (int) { 1, 2 }, rate = (int) { 5512, 11025, 22050, 44100 }; "
         "audio/x-speex, channels = (int) { 1, 2 }, rate = (int) { 5512, 11025, 22050, 44100 };")
@@ -85,21 +92,28 @@ G_DEFINE_TYPE (GstFlvDemux, gst_flv_demux, GST_TYPE_ELEMENT);
 /* 1 byte of tag type + 3 bytes of tag data size */
 #define FLV_TAG_TYPE_SIZE 4
 
+/* two seconds - consider pts are resynced to another base if this different */
+#define RESYNC_THRESHOLD 2000
+
 static gboolean flv_demux_handle_seek_push (GstFlvDemux * demux,
     GstEvent * event);
 static gboolean gst_flv_demux_handle_seek_pull (GstFlvDemux * demux,
     GstEvent * event, gboolean seeking);
 
-static gboolean gst_flv_demux_query (GstPad * pad, GstQuery * query);
-static gboolean gst_flv_demux_src_event (GstPad * pad, GstEvent * event);
+static gboolean gst_flv_demux_query (GstPad * pad, GstObject * parent,
+    GstQuery * query);
+static gboolean gst_flv_demux_src_event (GstPad * pad, GstObject * parent,
+    GstEvent * event);
 
+static GstIndex *gst_flv_demux_get_index (GstElement * element);
 
 static void
 gst_flv_demux_parse_and_add_index_entry (GstFlvDemux * demux, GstClockTime ts,
     guint64 pos, gboolean keyframe)
 {
-  static GstIndexAssociation associations[2];
-  static GstIndexEntry *entry;
+  GstIndexAssociation associations[2];
+  GstIndex *index;
+  GstIndexEntry *entry;
 
   GST_LOG_OBJECT (demux,
       "adding key=%d association %" GST_TIME_FORMAT "-> %" G_GUINT64_FORMAT,
@@ -109,13 +123,18 @@ gst_flv_demux_parse_and_add_index_entry (GstFlvDemux * demux, GstClockTime ts,
   if (!demux->upstream_seekable)
     return;
 
+  index = gst_flv_demux_get_index (GST_ELEMENT (demux));
+
+  if (!index)
+    return;
+
   /* entry may already have been added before, avoid adding indefinitely */
-  entry = gst_index_get_assoc_entry (demux->index, demux->index_id,
+  entry = gst_index_get_assoc_entry (index, demux->index_id,
       GST_INDEX_LOOKUP_EXACT, GST_ASSOCIATION_FLAG_NONE, GST_FORMAT_BYTES, pos);
 
   if (entry) {
 #ifndef GST_DISABLE_GST_DEBUG
-    gint64 time;
+    gint64 time = 0;
     gboolean key;
 
     gst_index_entry_assoc_map (entry, GST_FORMAT_TIME, &time);
@@ -126,6 +145,7 @@ gst_flv_demux_parse_and_add_index_entry (GstFlvDemux * demux, GstClockTime ts,
     if (time != ts || key != ! !keyframe)
       GST_DEBUG_OBJECT (demux, "metadata mismatch");
 #endif
+    gst_object_unref (index);
     return;
   }
 
@@ -134,7 +154,7 @@ gst_flv_demux_parse_and_add_index_entry (GstFlvDemux * demux, GstClockTime ts,
   associations[1].format = GST_FORMAT_BYTES;
   associations[1].value = pos;
 
-  gst_index_add_associationv (demux->index, demux->index_id,
+  gst_index_add_associationv (index, demux->index_id,
       (keyframe) ? GST_ASSOCIATION_FLAG_KEY_UNIT :
       GST_ASSOCIATION_FLAG_DELTA_UNIT, 2,
       (const GstIndexAssociation *) &associations);
@@ -143,6 +163,8 @@ gst_flv_demux_parse_and_add_index_entry (GstFlvDemux * demux, GstClockTime ts,
     demux->index_max_pos = pos;
   if (ts > demux->index_max_time)
     demux->index_max_time = ts;
+
+  gst_object_unref (index);
 }
 
 static gchar *
@@ -179,19 +201,6 @@ FLV_GET_STRING (GstByteReader * reader)
   return string;
 }
 
-static const GstQueryType *
-gst_flv_demux_query_types (GstPad * pad)
-{
-  static const GstQueryType query_types[] = {
-    GST_QUERY_DURATION,
-    GST_QUERY_POSITION,
-    GST_QUERY_SEEKING,
-    0
-  };
-
-  return query_types;
-}
-
 static void
 gst_flv_demux_check_seekability (GstFlvDemux * demux)
 {
@@ -215,7 +224,7 @@ gst_flv_demux_check_seekability (GstFlvDemux * demux)
   /* try harder to query upstream size if we didn't get it the first time */
   if (demux->upstream_seekable && stop == -1) {
     GST_DEBUG_OBJECT (demux, "doing duration query to fix up unset stop");
-    gst_pad_query_peer_duration (demux->sinkpad, GST_FORMAT_BYTES, &stop);
+    gst_pad_peer_query_duration (demux->sinkpad, GST_FORMAT_BYTES, &stop);
   }
 
   /* if upstream doesn't know the size, it's likely that it's not seekable in
@@ -528,15 +537,14 @@ gst_flv_demux_parse_tag_script (GstFlvDemux * demux, GstBuffer * buffer)
   GstFlowReturn ret = GST_FLOW_OK;
   GstByteReader reader;
   guint8 type = 0;
-  guint8 *data;
-  gsize size;
+  GstMapInfo map;
 
   g_return_val_if_fail (gst_buffer_get_size (buffer) >= 7, GST_FLOW_ERROR);
 
-  data = gst_buffer_map (buffer, &size, NULL, GST_MAP_READ);
-  gst_byte_reader_init (&reader, data, size);
+  gst_buffer_map (buffer, &map, GST_MAP_READ);
+  gst_byte_reader_init (&reader, map.data, map.size);
 
-  gst_byte_reader_skip (&reader, 7);
+  gst_byte_reader_skip_unchecked (&reader, 7);
 
   GST_LOG_OBJECT (demux, "parsing a script tag");
 
@@ -603,7 +611,7 @@ gst_flv_demux_parse_tag_script (GstFlvDemux * demux, GstBuffer * buffer)
 
     g_free (function_name);
 
-    if (demux->index && demux->times && demux->filepositions) {
+    if (demux->times && demux->filepositions) {
       guint num;
 
       /* If an index was found, insert associations */
@@ -621,7 +629,7 @@ gst_flv_demux_parse_tag_script (GstFlvDemux * demux, GstBuffer * buffer)
   }
 
 cleanup:
-  gst_buffer_unmap (buffer, data, -1);
+  gst_buffer_unmap (buffer, &map);
 
   return ret;
 }
@@ -648,58 +656,64 @@ gst_flv_demux_audio_negotiate (GstFlvDemux * demux, guint32 codec_tag,
       break;
     case 0:
     case 3:
+    {
+      GstAudioFormat format;
+
       /* Assuming little endian for 0 (aka endianness of the
        * system on which the file was created) as most people
        * are probably using little endian machines */
-      caps = gst_caps_new_simple ("audio/x-raw-int",
-          "endianness", G_TYPE_INT, G_LITTLE_ENDIAN,
-          "signed", G_TYPE_BOOLEAN, (width == 8) ? FALSE : TRUE,
-          "width", G_TYPE_INT, width, "depth", G_TYPE_INT, width, NULL);
+      format = gst_audio_format_build_integer ((width == 8) ? FALSE : TRUE,
+          G_LITTLE_ENDIAN, width, width);
+
+      caps = gst_caps_new_simple ("audio/x-raw",
+          "format", G_TYPE_STRING, gst_audio_format_to_string (format),
+          "layout", G_TYPE_STRING, "interleaved", NULL);
       break;
+    }
     case 4:
     case 5:
     case 6:
-      caps = gst_caps_new_simple ("audio/x-nellymoser", NULL);
+      caps = gst_caps_new_empty_simple ("audio/x-nellymoser");
       break;
     case 10:
     {
-      guint8 *data = NULL;
-      gsize size;
-
-      if (demux->audio_codec_data)
-        data = gst_buffer_map (demux->audio_codec_data, &size, NULL,
-            GST_MAP_READ);
-      /* use codec-data to extract and verify samplerate */
-      if (demux->audio_codec_data && size >= 2) {
-        gint freq_index;
-
-        freq_index = GST_READ_UINT16_BE (data);
-        freq_index = (freq_index & 0x0780) >> 7;
-        adjusted_rate =
-            gst_codec_utils_aac_get_sample_rate_from_index (freq_index);
-
-        if (adjusted_rate && (rate != adjusted_rate)) {
-          GST_LOG_OBJECT (demux, "Ajusting AAC sample rate %d -> %d", rate,
-              adjusted_rate);
-        } else {
-          adjusted_rate = rate;
+      if (demux->audio_codec_data) {
+        GstMapInfo map;
+
+        gst_buffer_map (demux->audio_codec_data, &map, GST_MAP_READ);
+
+        /* use codec-data to extract and verify samplerate */
+        if (map.size >= 2) {
+          gint freq_index;
+
+          freq_index = GST_READ_UINT16_BE (map.data);
+          freq_index = (freq_index & 0x0780) >> 7;
+          adjusted_rate =
+              gst_codec_utils_aac_get_sample_rate_from_index (freq_index);
+
+          if (adjusted_rate && (rate != adjusted_rate)) {
+            GST_LOG_OBJECT (demux, "Ajusting AAC sample rate %d -> %d", rate,
+                adjusted_rate);
+          } else {
+            adjusted_rate = rate;
+          }
         }
+        gst_buffer_unmap (demux->audio_codec_data, &map);
       }
-      if (data)
-        gst_buffer_unmap (demux->audio_codec_data, data, -1);
+
       caps = gst_caps_new_simple ("audio/mpeg",
           "mpegversion", G_TYPE_INT, 4, "framed", G_TYPE_BOOLEAN, TRUE,
           "stream-format", G_TYPE_STRING, "raw", NULL);
       break;
     }
     case 7:
-      caps = gst_caps_new_simple ("audio/x-alaw", NULL);
+      caps = gst_caps_new_empty_simple ("audio/x-alaw");
       break;
     case 8:
-      caps = gst_caps_new_simple ("audio/x-mulaw", NULL);
+      caps = gst_caps_new_empty_simple ("audio/x-mulaw");
       break;
     case 11:
-      caps = gst_caps_new_simple ("audio/x-speex", NULL);
+      caps = gst_caps_new_empty_simple ("audio/x-speex");
       break;
     default:
       GST_WARNING_OBJECT (demux, "unsupported audio codec tag %u", codec_tag);
@@ -718,6 +732,7 @@ gst_flv_demux_audio_negotiate (GstFlvDemux * demux, guint32 codec_tag,
         demux->audio_codec_data, NULL);
   }
 
+  gst_pad_push_event (demux->audio_pad, gst_event_new_stream_start ());
   ret = gst_pad_set_caps (demux->audio_pad, caps);
 
   if (G_LIKELY (ret)) {
@@ -731,7 +746,7 @@ gst_flv_demux_audio_negotiate (GstFlvDemux * demux, guint32 codec_tag,
 
     if (codec_name) {
       if (demux->taglist == NULL)
-        demux->taglist = gst_tag_list_new ();
+        demux->taglist = gst_tag_list_new_empty ();
       gst_tag_list_add (demux->taglist, GST_TAG_MERGE_REPLACE,
           GST_TAG_AUDIO_CODEC, codec_name, NULL);
       g_free (codec_name);
@@ -750,6 +765,22 @@ beach:
   return ret;
 }
 
+static gboolean
+gst_flv_demux_push_src_event (GstFlvDemux * demux, GstEvent * event)
+{
+  gboolean ret = TRUE;
+
+  if (demux->audio_pad)
+    ret |= gst_pad_push_event (demux->audio_pad, gst_event_ref (event));
+
+  if (demux->video_pad)
+    ret |= gst_pad_push_event (demux->video_pad, gst_event_ref (event));
+
+  gst_event_unref (event);
+
+  return ret;
+}
+
 static void
 gst_flv_demux_push_tags (GstFlvDemux * demux)
 {
@@ -766,12 +797,30 @@ gst_flv_demux_push_tags (GstFlvDemux * demux)
   if (demux->taglist) {
     GST_DEBUG_OBJECT (demux, "pushing tags out %" GST_PTR_FORMAT,
         demux->taglist);
-    gst_element_found_tags (GST_ELEMENT (demux), demux->taglist);
-    demux->taglist = gst_tag_list_new ();
+    gst_flv_demux_push_src_event (demux, gst_event_new_tag ("GstDemuxer",
+            demux->taglist));
+    demux->taglist = gst_tag_list_new_empty ();
     demux->push_tags = FALSE;
   }
 }
 
+static void
+gst_flv_demux_update_resync (GstFlvDemux * demux, guint32 pts, gboolean discont,
+    guint32 * last, GstClockTime * offset)
+{
+  gint32 dpts = pts - *last;
+  if (!discont && ABS (dpts) >= RESYNC_THRESHOLD) {
+    /* Theoretically, we should use substract the duration of the last buffer,
+       but this demuxer sends no durations on buffers, not sure if it cannot
+       know, or just does not care to calculate. */
+    *offset -= dpts * GST_MSECOND;
+    GST_WARNING_OBJECT (demux,
+        "Large pts gap (%" G_GINT32_FORMAT " ms), assuming resync, offset now %"
+        GST_TIME_FORMAT "", dpts, GST_TIME_ARGS (*offset));
+  }
+  *last = pts;
+}
+
 static GstFlowReturn
 gst_flv_demux_parse_tag_audio (GstFlvDemux * demux, GstBuffer * buffer)
 {
@@ -779,9 +828,9 @@ gst_flv_demux_parse_tag_audio (GstFlvDemux * demux, GstBuffer * buffer)
   guint32 pts = 0, codec_tag = 0, rate = 5512, width = 8, channels = 1;
   guint32 codec_data = 0, pts_ext = 0;
   guint8 flags = 0;
-  guint8 *data;
+  GstMapInfo map;
   GstBuffer *outbuf;
-  gsize size;
+  guint8 *data;
 
   GST_LOG_OBJECT (demux, "parsing an audio tag");
 
@@ -796,12 +845,13 @@ gst_flv_demux_parse_tag_audio (GstFlvDemux * demux, GstBuffer * buffer)
 
   /* Error out on tags with too small headers */
   if (gst_buffer_get_size (buffer) < 11) {
-    GST_ERROR_OBJECT (demux, "Too small tag size (%d)",
+    GST_ERROR_OBJECT (demux, "Too small tag size (%" G_GSIZE_FORMAT ")",
         gst_buffer_get_size (buffer));
     return GST_FLOW_ERROR;
   }
 
-  data = gst_buffer_map (buffer, &size, NULL, GST_MAP_READ);
+  gst_buffer_map (buffer, &map, GST_MAP_READ);
+  data = map.data;
 
   /* Grab information about audio tag */
   pts = GST_READ_UINT24_BE (data);
@@ -817,7 +867,7 @@ gst_flv_demux_parse_tag_audio (GstFlvDemux * demux, GstBuffer * buffer)
   flags = GST_READ_UINT8 (data + 7);
 
   /* Silently skip buffers with no data */
-  if (size == 11)
+  if (map.size == 11)
     goto beach;
 
   /* Channels */
@@ -866,6 +916,17 @@ gst_flv_demux_parse_tag_audio (GstFlvDemux * demux, GstBuffer * buffer)
       goto beach;
     }
 
+    /* Set functions on the pad */
+    gst_pad_set_query_function (demux->audio_pad,
+        GST_DEBUG_FUNCPTR (gst_flv_demux_query));
+    gst_pad_set_event_function (demux->audio_pad,
+        GST_DEBUG_FUNCPTR (gst_flv_demux_src_event));
+
+    gst_pad_use_fixed_caps (demux->audio_pad);
+
+    /* Make it active */
+    gst_pad_set_active (demux->audio_pad, TRUE);
+
     /* Negotiate caps */
     if (!gst_flv_demux_audio_negotiate (demux, codec_tag, rate, channels,
             width)) {
@@ -886,19 +947,6 @@ gst_flv_demux_parse_tag_audio (GstFlvDemux * demux, GstBuffer * buffer)
     }
 #endif
 
-    /* Set functions on the pad */
-    gst_pad_set_query_type_function (demux->audio_pad,
-        GST_DEBUG_FUNCPTR (gst_flv_demux_query_types));
-    gst_pad_set_query_function (demux->audio_pad,
-        GST_DEBUG_FUNCPTR (gst_flv_demux_query));
-    gst_pad_set_event_function (demux->audio_pad,
-        GST_DEBUG_FUNCPTR (gst_flv_demux_src_event));
-
-    gst_pad_use_fixed_caps (demux->audio_pad);
-
-    /* Make it active */
-    gst_pad_set_active (demux->audio_pad, TRUE);
-
     /* We need to set caps before adding */
     gst_element_add_pad (GST_ELEMENT (demux),
         gst_object_ref (demux->audio_pad));
@@ -968,8 +1016,12 @@ gst_flv_demux_parse_tag_audio (GstFlvDemux * demux, GstBuffer * buffer)
     }
   }
 
+  /* detect (and deem to be resyncs)  large pts gaps */
+  gst_flv_demux_update_resync (demux, pts, demux->audio_need_discont,
+      &demux->last_audio_pts, &demux->audio_time_offset);
+
   /* Fill buffer with data */
-  GST_BUFFER_TIMESTAMP (outbuf) = pts * GST_MSECOND;
+  GST_BUFFER_TIMESTAMP (outbuf) = pts * GST_MSECOND + demux->audio_time_offset;
   GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE;
   GST_BUFFER_OFFSET (outbuf) = demux->audio_offset++;
   GST_BUFFER_OFFSET_END (outbuf) = demux->audio_offset;
@@ -980,7 +1032,7 @@ gst_flv_demux_parse_tag_audio (GstFlvDemux * demux, GstBuffer * buffer)
 
   /* Only add audio frames to the index if we have no video,
    * and if the index is not yet complete */
-  if (!demux->has_video && demux->index && !demux->indexed) {
+  if (!demux->has_video && !demux->indexed) {
     gst_flv_demux_parse_and_add_index_entry (demux,
         GST_BUFFER_TIMESTAMP (outbuf), demux->cur_tag_offset, TRUE);
   }
@@ -994,7 +1046,6 @@ gst_flv_demux_parse_tag_audio (GstFlvDemux * demux, GstBuffer * buffer)
 
   /* Do we need a newsegment event ? */
   if (G_UNLIKELY (demux->audio_need_segment)) {
-    /* FIXME need one segment sent for all stream to maintain a/v sync */
     if (!demux->new_seg_event) {
       GST_DEBUG_OBJECT (demux, "pushing newsegment from %"
           GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
@@ -1011,7 +1062,8 @@ gst_flv_demux_parse_tag_audio (GstFlvDemux * demux, GstBuffer * buffer)
     demux->audio_need_segment = FALSE;
   }
 
-  GST_LOG_OBJECT (demux, "pushing %d bytes buffer at pts %" GST_TIME_FORMAT
+  GST_LOG_OBJECT (demux,
+      "pushing %" G_GSIZE_FORMAT " bytes buffer at pts %" GST_TIME_FORMAT
       " with duration %" GST_TIME_FORMAT ", offset %" G_GUINT64_FORMAT,
       gst_buffer_get_size (outbuf),
       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
@@ -1038,9 +1090,9 @@ gst_flv_demux_parse_tag_audio (GstFlvDemux * demux, GstBuffer * buffer)
   /* Push downstream */
   ret = gst_pad_push (demux->audio_pad, outbuf);
   if (G_UNLIKELY (ret != GST_FLOW_OK)) {
-    if (demux->segment.rate < 0.0 && ret == GST_FLOW_UNEXPECTED &&
+    if (demux->segment.rate < 0.0 && ret == GST_FLOW_EOS &&
         demux->segment.position > demux->segment.stop) {
-      /* In reverse playback we can get a GST_FLOW_UNEXPECTED when
+      /* In reverse playback we can get a GST_FLOW_EOS when
        * we are at the end of the segment, so we just need to jump
        * back to the previous section. */
       GST_DEBUG_OBJECT (demux, "downstream has reached end of segment");
@@ -1060,7 +1112,7 @@ gst_flv_demux_parse_tag_audio (GstFlvDemux * demux, GstBuffer * buffer)
   demux->audio_linked = TRUE;
 
 beach:
-  gst_buffer_unmap (buffer, data, -1);
+  gst_buffer_unmap (buffer, &map);
 
   return ret;
 }
@@ -1075,16 +1127,16 @@ gst_flv_demux_video_negotiate (GstFlvDemux * demux, guint32 codec_tag)
   /* Generate caps for that pad */
   switch (codec_tag) {
     case 2:
-      caps = gst_caps_new_simple ("video/x-flash-video", NULL);
+      caps = gst_caps_new_empty_simple ("video/x-flash-video");
       break;
     case 3:
-      caps = gst_caps_new_simple ("video/x-flash-screen", NULL);
+      caps = gst_caps_new_empty_simple ("video/x-flash-screen");
       break;
     case 4:
-      caps = gst_caps_new_simple ("video/x-vp6-flash", NULL);
+      caps = gst_caps_new_empty_simple ("video/x-vp6-flash");
       break;
     case 5:
-      caps = gst_caps_new_simple ("video/x-vp6-alpha", NULL);
+      caps = gst_caps_new_empty_simple ("video/x-vp6-alpha");
       break;
     case 7:
       caps =
@@ -1127,6 +1179,7 @@ gst_flv_demux_video_negotiate (GstFlvDemux * demux, guint32 codec_tag)
         demux->video_codec_data, NULL);
   }
 
+  gst_pad_push_event (demux->video_pad, gst_event_new_stream_start ());
   ret = gst_pad_set_caps (demux->video_pad, caps);
 
   if (G_LIKELY (ret)) {
@@ -1137,7 +1190,7 @@ gst_flv_demux_video_negotiate (GstFlvDemux * demux, guint32 codec_tag)
 
     if (codec_name) {
       if (demux->taglist == NULL)
-        demux->taglist = gst_tag_list_new ();
+        demux->taglist = gst_tag_list_new_empty ();
       gst_tag_list_add (demux->taglist, GST_TAG_MERGE_REPLACE,
           GST_TAG_VIDEO_CODEC, codec_name, NULL);
       g_free (codec_name);
@@ -1163,9 +1216,9 @@ gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer)
   guint32 pts = 0, codec_data = 1, pts_ext = 0;
   gboolean keyframe = FALSE;
   guint8 flags = 0, codec_tag = 0;
-  guint8 *data;
   GstBuffer *outbuf;
-  gsize size;
+  GstMapInfo map;
+  guint8 *data;
 
   g_return_val_if_fail (gst_buffer_get_size (buffer) == demux->tag_size,
       GST_FLOW_ERROR);
@@ -1183,7 +1236,8 @@ gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer)
     return GST_FLOW_ERROR;
   }
 
-  data = gst_buffer_map (buffer, &size, NULL, GST_MAP_READ);
+  gst_buffer_map (buffer, &map, GST_MAP_READ);
+  data = map.data;
 
   /* Grab information about video tag */
   pts = GST_READ_UINT24_BE (data);
@@ -1216,7 +1270,9 @@ gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer)
 
     GST_LOG_OBJECT (demux, "got cts %d", cts);
 
-    pts = pts + cts;
+    /* avoid negative overflow */
+    if (cts >= 0 || pts >= -cts)
+      pts += cts;
   }
 
   GST_LOG_OBJECT (demux, "video tag with codec tag %u, keyframe (%d) "
@@ -1233,6 +1289,18 @@ gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer)
       goto beach;
     }
 
+    /* Set functions on the pad */
+    gst_pad_set_query_function (demux->video_pad,
+        GST_DEBUG_FUNCPTR (gst_flv_demux_query));
+    gst_pad_set_event_function (demux->video_pad,
+        GST_DEBUG_FUNCPTR (gst_flv_demux_src_event));
+
+    gst_pad_use_fixed_caps (demux->video_pad);
+
+    /* Make it active */
+    gst_pad_set_active (demux->video_pad, TRUE);
+
+    /* Needs to be active before setting caps */
     if (!gst_flv_demux_video_negotiate (demux, codec_tag)) {
       gst_object_unref (demux->video_pad);
       demux->video_pad = NULL;
@@ -1256,19 +1324,6 @@ gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer)
     }
 #endif
 
-    /* Set functions on the pad */
-    gst_pad_set_query_type_function (demux->video_pad,
-        GST_DEBUG_FUNCPTR (gst_flv_demux_query_types));
-    gst_pad_set_query_function (demux->video_pad,
-        GST_DEBUG_FUNCPTR (gst_flv_demux_query));
-    gst_pad_set_event_function (demux->video_pad,
-        GST_DEBUG_FUNCPTR (gst_flv_demux_src_event));
-
-    gst_pad_use_fixed_caps (demux->video_pad);
-
-    /* Make it active */
-    gst_pad_set_active (demux->video_pad, TRUE);
-
     /* We need to set caps before adding */
     gst_element_add_pad (GST_ELEMENT (demux),
         gst_object_ref (demux->video_pad));
@@ -1340,8 +1395,12 @@ gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer)
     }
   }
 
+  /* detect (and deem to be resyncs)  large pts gaps */
+  gst_flv_demux_update_resync (demux, pts, demux->video_need_discont,
+      &demux->last_video_pts, &demux->video_time_offset);
+
   /* Fill buffer with data */
-  GST_BUFFER_TIMESTAMP (outbuf) = pts * GST_MSECOND;
+  GST_BUFFER_TIMESTAMP (outbuf) = pts * GST_MSECOND + demux->video_time_offset;
   GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE;
   GST_BUFFER_OFFSET (outbuf) = demux->video_offset++;
   GST_BUFFER_OFFSET_END (outbuf) = demux->video_offset;
@@ -1353,7 +1412,7 @@ gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer)
   if (!keyframe)
     GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT);
 
-  if (!demux->indexed && demux->index) {
+  if (!demux->indexed) {
     gst_flv_demux_parse_and_add_index_entry (demux,
         GST_BUFFER_TIMESTAMP (outbuf), demux->cur_tag_offset, keyframe);
   }
@@ -1367,7 +1426,6 @@ gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer)
 
   /* Do we need a newsegment event ? */
   if (G_UNLIKELY (demux->video_need_segment)) {
-    /* FIXME need one segment sent for all stream to maintain a/v sync */
     if (!demux->new_seg_event) {
       GST_DEBUG_OBJECT (demux, "pushing newsegment from %"
           GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
@@ -1384,7 +1442,8 @@ gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer)
     demux->video_need_segment = FALSE;
   }
 
-  GST_LOG_OBJECT (demux, "pushing %d bytes buffer at pts %" GST_TIME_FORMAT
+  GST_LOG_OBJECT (demux,
+      "pushing %" G_GSIZE_FORMAT " bytes buffer at pts %" GST_TIME_FORMAT
       " with duration %" GST_TIME_FORMAT ", offset %" G_GUINT64_FORMAT
       ", keyframe (%d)", gst_buffer_get_size (outbuf),
       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
@@ -1413,9 +1472,9 @@ gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer)
   ret = gst_pad_push (demux->video_pad, outbuf);
 
   if (G_UNLIKELY (ret != GST_FLOW_OK)) {
-    if (demux->segment.rate < 0.0 && ret == GST_FLOW_UNEXPECTED &&
+    if (demux->segment.rate < 0.0 && ret == GST_FLOW_EOS &&
         demux->segment.position > demux->segment.stop) {
-      /* In reverse playback we can get a GST_FLOW_UNEXPECTED when
+      /* In reverse playback we can get a GST_FLOW_EOS when
        * we are at the end of the segment, so we just need to jump
        * back to the previous section. */
       GST_DEBUG_OBJECT (demux, "downstream has reached end of segment");
@@ -1435,7 +1494,7 @@ gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer)
   demux->video_linked = TRUE;
 
 beach:
-  gst_buffer_unmap (buffer, data, -1);
+  gst_buffer_unmap (buffer, &map);
   return ret;
 }
 
@@ -1448,13 +1507,16 @@ gst_flv_demux_parse_tag_timestamp (GstFlvDemux * demux, gboolean index,
   guint8 type;
   gboolean keyframe = TRUE;
   GstClockTime ret = GST_CLOCK_TIME_NONE;
-  guint8 *data, *bdata;
+  GstMapInfo map;
+  guint8 *data;
   gsize size;
 
   g_return_val_if_fail (gst_buffer_get_size (buffer) >= 12,
       GST_CLOCK_TIME_NONE);
 
-  data = bdata = gst_buffer_map (buffer, &size, NULL, GST_MAP_READ);
+  gst_buffer_map (buffer, &map, GST_MAP_READ);
+  data = map.data;
+  size = map.size;
 
   type = data[0];
 
@@ -1501,7 +1563,7 @@ gst_flv_demux_parse_tag_timestamp (GstFlvDemux * demux, gboolean index,
   ret = pts * GST_MSECOND;
   GST_LOG_OBJECT (demux, "pts: %" GST_TIME_FORMAT, GST_TIME_ARGS (ret));
 
-  if (index && demux->index && !demux->indexed && (type == 9 || (type == 8
+  if (index && !demux->indexed && (type == 9 || (type == 8
               && !demux->has_video))) {
     gst_flv_demux_parse_and_add_index_entry (demux, ret, demux->offset,
         keyframe);
@@ -1511,7 +1573,7 @@ gst_flv_demux_parse_tag_timestamp (GstFlvDemux * demux, gboolean index,
     demux->duration = ret;
 
 exit:
-  gst_buffer_unmap (buffer, bdata, -1);
+  gst_buffer_unmap (buffer, &map);
   return ret;
 }
 
@@ -1520,13 +1582,13 @@ gst_flv_demux_parse_tag_type (GstFlvDemux * demux, GstBuffer * buffer)
 {
   GstFlowReturn ret = GST_FLOW_OK;
   guint8 tag_type = 0;
-  guint8 *data;
+  GstMapInfo map;
 
   g_return_val_if_fail (gst_buffer_get_size (buffer) >= 4, GST_FLOW_ERROR);
 
-  data = gst_buffer_map (buffer, NULL, NULL, GST_MAP_READ);
+  gst_buffer_map (buffer, &map, GST_MAP_READ);
 
-  tag_type = data[0];
+  tag_type = map.data[0];
 
   switch (tag_type) {
     case 9:
@@ -1546,13 +1608,13 @@ gst_flv_demux_parse_tag_type (GstFlvDemux * demux, GstBuffer * buffer)
 
   /* Tag size is 1 byte of type + 3 bytes of size + 7 bytes + tag data size +
    * 4 bytes of previous tag size */
-  demux->tag_data_size = GST_READ_UINT24_BE (data + 1);
+  demux->tag_data_size = GST_READ_UINT24_BE (map.data + 1);
   demux->tag_size = demux->tag_data_size + 11;
 
   GST_LOG_OBJECT (demux, "tag data size is %" G_GUINT64_FORMAT,
       demux->tag_data_size);
 
-  gst_buffer_unmap (buffer, data, -1);
+  gst_buffer_unmap (buffer, &map);
 
   return ret;
 }
@@ -1561,26 +1623,26 @@ static GstFlowReturn
 gst_flv_demux_parse_header (GstFlvDemux * demux, GstBuffer * buffer)
 {
   GstFlowReturn ret = GST_FLOW_OK;
-  guint8 *data;
+  GstMapInfo map;
 
   g_return_val_if_fail (gst_buffer_get_size (buffer) >= 9, GST_FLOW_ERROR);
 
-  data = gst_buffer_map (buffer, NULL, NULL, GST_MAP_READ);
+  gst_buffer_map (buffer, &map, GST_MAP_READ);
 
   /* Check for the FLV tag */
-  if (data[0] == 'F' && data[1] == 'L' && data[2] == 'V') {
+  if (map.data[0] == 'F' && map.data[1] == 'L' && map.data[2] == 'V') {
     GST_DEBUG_OBJECT (demux, "FLV header detected");
   } else {
     if (G_UNLIKELY (demux->strict)) {
       GST_WARNING_OBJECT (demux, "invalid header tag detected");
-      ret = GST_FLOW_UNEXPECTED;
+      ret = GST_FLOW_EOS;
       goto beach;
     }
   }
 
   /* Now look at audio/video flags */
   {
-    guint8 flags = data[4];
+    guint8 flags = map.data[4];
 
     demux->has_video = demux->has_audio = FALSE;
 
@@ -1601,7 +1663,7 @@ gst_flv_demux_parse_header (GstFlvDemux * demux, GstBuffer * buffer)
   demux->need_header = FALSE;
 
 beach:
-  gst_buffer_unmap (buffer, data, -1);
+  gst_buffer_unmap (buffer, &map);
   return ret;
 }
 
@@ -1658,6 +1720,8 @@ gst_flv_demux_cleanup (GstFlvDemux * demux)
   demux->index_max_time = 0;
 
   demux->audio_start = demux->video_start = GST_CLOCK_TIME_NONE;
+  demux->last_audio_pts = demux->last_video_pts = 0;
+  demux->audio_time_offset = demux->video_time_offset = 0;
 
   demux->no_more_pads = FALSE;
 
@@ -1736,14 +1800,15 @@ flv_demux_seek_to_offset (GstFlvDemux * demux, guint64 offset)
 }
 
 static GstFlowReturn
-gst_flv_demux_chain (GstPad * pad, GstBuffer * buffer)
+gst_flv_demux_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
 {
   GstFlowReturn ret = GST_FLOW_OK;
   GstFlvDemux *demux = NULL;
 
-  demux = GST_FLV_DEMUX (gst_pad_get_parent (pad));
+  demux = GST_FLV_DEMUX (parent);
 
-  GST_LOG_OBJECT (demux, "received buffer of %d bytes at offset %"
+  GST_LOG_OBJECT (demux,
+      "received buffer of %" G_GSIZE_FORMAT " bytes at offset %"
       G_GUINT64_FORMAT, gst_buffer_get_size (buffer),
       GST_BUFFER_OFFSET (buffer));
 
@@ -1785,7 +1850,7 @@ parse:
 
   if (G_UNLIKELY (demux->flushing)) {
     GST_DEBUG_OBJECT (demux, "we are now flushing, exiting parser loop");
-    ret = GST_FLOW_WRONG_STATE;
+    ret = GST_FLOW_FLUSHING;
     goto beach;
   }
 
@@ -1951,8 +2016,6 @@ beach:
     }
   }
 
-  gst_object_unref (demux);
-
   return ret;
 
 /* ERRORS */
@@ -1993,10 +2056,10 @@ gst_flv_demux_pull_range (GstFlvDemux * demux, GstPad * pad, guint64 offset,
 
   if (G_UNLIKELY (*buffer && gst_buffer_get_size (*buffer) != size)) {
     GST_WARNING_OBJECT (demux,
-        "partial pull got %d when expecting %d from offset %" G_GUINT64_FORMAT,
-        gst_buffer_get_size (*buffer), size, offset);
+        "partial pull got %" G_GSIZE_FORMAT " when expecting %d from offset %"
+        G_GUINT64_FORMAT, gst_buffer_get_size (*buffer), size, offset);
     gst_buffer_unref (*buffer);
-    ret = GST_FLOW_UNEXPECTED;
+    ret = GST_FLOW_EOS;
     *buffer = NULL;
     return ret;
   }
@@ -2030,6 +2093,7 @@ gst_flv_demux_pull_tag (GstPad * pad, GstFlvDemux * demux)
   demux->offset += FLV_TAG_TYPE_SIZE;
 
   /* Pull the whole tag */
+  buffer = NULL;
   if (G_UNLIKELY ((ret = gst_flv_demux_pull_range (demux, pad, demux->offset,
                   demux->tag_size, &buffer)) != GST_FLOW_OK))
     goto beach;
@@ -2127,7 +2191,8 @@ gst_flv_demux_move_to_offset (GstFlvDemux * demux, gint64 offset,
 static GstFlowReturn
 gst_flv_demux_seek_to_prev_keyframe (GstFlvDemux * demux)
 {
-  GstFlowReturn ret = GST_FLOW_UNEXPECTED;
+  GstFlowReturn ret = GST_FLOW_EOS;
+  GstIndex *index;
   GstIndexEntry *entry = NULL;
 
   GST_DEBUG_OBJECT (demux,
@@ -2146,45 +2211,35 @@ gst_flv_demux_seek_to_prev_keyframe (GstFlvDemux * demux)
 
   GST_DEBUG_OBJECT (demux, "locating previous position");
 
+  index = gst_flv_demux_get_index (GST_ELEMENT (demux));
+
   /* locate index entry before previous start position */
-  if (demux->index)
-    entry = gst_index_get_assoc_entry (demux->index, demux->index_id,
+  if (index) {
+    entry = gst_index_get_assoc_entry (index, demux->index_id,
         GST_INDEX_LOOKUP_BEFORE, GST_ASSOCIATION_FLAG_KEY_UNIT,
         GST_FORMAT_BYTES, demux->from_offset - 1);
 
-  if (entry) {
-    gint64 bytes, time;
-
-    gst_index_entry_assoc_map (entry, GST_FORMAT_BYTES, &bytes);
-    gst_index_entry_assoc_map (entry, GST_FORMAT_TIME, &time);
-
-    GST_DEBUG_OBJECT (demux, "found index entry for %" G_GINT64_FORMAT
-        " at %" GST_TIME_FORMAT ", seeking to %" G_GINT64_FORMAT,
-        demux->offset - 1, GST_TIME_ARGS (time), bytes);
-
-    /* setup for next section */
-    demux->to_offset = demux->from_offset;
-    gst_flv_demux_move_to_offset (demux, bytes, FALSE);
-    ret = GST_FLOW_OK;
-  }
+    if (entry) {
+      gint64 bytes = 0, time = 0;
 
-done:
-  return ret;
-}
+      gst_index_entry_assoc_map (entry, GST_FORMAT_BYTES, &bytes);
+      gst_index_entry_assoc_map (entry, GST_FORMAT_TIME, &time);
 
-static gboolean
-gst_flv_demux_push_src_event (GstFlvDemux * demux, GstEvent * event)
-{
-  gboolean ret = TRUE;
+      GST_DEBUG_OBJECT (demux, "found index entry for %" G_GINT64_FORMAT
+          " at %" GST_TIME_FORMAT ", seeking to %" G_GINT64_FORMAT,
+          demux->offset - 1, GST_TIME_ARGS (time), bytes);
 
-  if (demux->audio_pad)
-    ret |= gst_pad_push_event (demux->audio_pad, gst_event_ref (event));
+      /* setup for next section */
+      demux->to_offset = demux->from_offset;
+      gst_flv_demux_move_to_offset (demux, bytes, FALSE);
+      ret = GST_FLOW_OK;
+    }
 
-  if (demux->video_pad)
-    ret |= gst_pad_push_event (demux->video_pad, gst_event_ref (event));
+    gst_object_unref (index);
+  }
 
-  gst_event_unref (event);
 
+done:
   return ret;
 }
 
@@ -2198,7 +2253,7 @@ gst_flv_demux_create_index (GstFlvDemux * demux, gint64 pos, GstClockTime ts)
   GstClockTime tag_time;
   GstFlowReturn ret = GST_FLOW_OK;
 
-  if (!gst_pad_query_peer_duration (demux->sinkpad, GST_FORMAT_BYTES, &size))
+  if (!gst_pad_peer_query_duration (demux->sinkpad, GST_FORMAT_BYTES, &size))
     return GST_FLOW_OK;
 
   GST_DEBUG_OBJECT (demux, "building index at %" G_GINT64_FORMAT
@@ -2207,12 +2262,14 @@ gst_flv_demux_create_index (GstFlvDemux * demux, gint64 pos, GstClockTime ts)
   old_offset = demux->offset;
   demux->offset = pos;
 
+  buffer = NULL;
   while ((ret = gst_flv_demux_pull_range (demux, demux->sinkpad, demux->offset,
               12, &buffer)) == GST_FLOW_OK) {
     tag_time =
         gst_flv_demux_parse_tag_timestamp (demux, TRUE, buffer, &tag_size);
 
     gst_buffer_unref (buffer);
+    buffer = NULL;
 
     if (G_UNLIKELY (tag_time == GST_CLOCK_TIME_NONE || tag_time > ts))
       goto exit;
@@ -2220,7 +2277,7 @@ gst_flv_demux_create_index (GstFlvDemux * demux, gint64 pos, GstClockTime ts)
     demux->offset += tag_size;
   }
 
-  if (ret == GST_FLOW_UNEXPECTED) {
+  if (ret == GST_FLOW_EOS) {
     /* file ran out, so mark we have complete index */
     demux->indexed = TRUE;
     ret = GST_FLOW_OK;
@@ -2238,9 +2295,9 @@ gst_flv_demux_get_metadata (GstFlvDemux * demux)
   gint64 ret = 0, offset;
   size_t tag_size, size;
   GstBuffer *buffer = NULL;
-  guint8 *data;
+  GstMapInfo map;
 
-  if (!gst_pad_query_peer_duration (demux->sinkpad, GST_FORMAT_BYTES, &offset))
+  if (!gst_pad_peer_query_duration (demux->sinkpad, GST_FORMAT_BYTES, &offset))
     goto exit;
 
   ret = offset;
@@ -2253,9 +2310,9 @@ gst_flv_demux_get_metadata (GstFlvDemux * demux)
           4, &buffer))
     goto exit;
 
-  data = gst_buffer_map (buffer, NULL, NULL, GST_MAP_READ);
-  tag_size = GST_READ_UINT32_BE (data);
-  gst_buffer_unmap (buffer, data, -1);
+  gst_buffer_map (buffer, &map, GST_MAP_READ);
+  tag_size = GST_READ_UINT32_BE (map.data);
+  gst_buffer_unmap (buffer, &map);
   GST_DEBUG_OBJECT (demux, "last tag size: %" G_GSIZE_FORMAT, tag_size);
   gst_buffer_unref (buffer);
   buffer = NULL;
@@ -2266,10 +2323,10 @@ gst_flv_demux_get_metadata (GstFlvDemux * demux)
     goto exit;
 
   /* a consistency check */
-  data = gst_buffer_map (buffer, NULL, NULL, GST_MAP_READ);
-  size = GST_READ_UINT24_BE (data + 1);
+  gst_buffer_map (buffer, &map, GST_MAP_READ);
+  size = GST_READ_UINT24_BE (map.data + 1);
   if (size != tag_size - 11) {
-    gst_buffer_unmap (buffer, data, -1);
+    gst_buffer_unmap (buffer, &map);
     GST_DEBUG_OBJECT (demux,
         "tag size %" G_GSIZE_FORMAT ", expected %" G_GSIZE_FORMAT
         ", corrupt or truncated file", size, tag_size - 11);
@@ -2280,8 +2337,8 @@ gst_flv_demux_get_metadata (GstFlvDemux * demux)
   gst_flv_demux_parse_tag_timestamp (demux, FALSE, buffer, &size);
 
   /* maybe get some more metadata */
-  if (data[0] == 18) {
-    gst_buffer_unmap (buffer, data, -1);
+  if (map.data[0] == 18) {
+    gst_buffer_unmap (buffer, &map);
     gst_buffer_unref (buffer);
     buffer = NULL;
     GST_DEBUG_OBJECT (demux, "script tag, pulling it to parse");
@@ -2290,7 +2347,7 @@ gst_flv_demux_get_metadata (GstFlvDemux * demux)
             tag_size, &buffer))
       gst_flv_demux_parse_tag_script (demux, buffer);
   } else {
-    gst_buffer_unmap (buffer, data, -1);
+    gst_buffer_unmap (buffer, &map);
   }
 
 exit:
@@ -2322,7 +2379,7 @@ gst_flv_demux_loop (GstPad * pad)
         demux->file_size = gst_flv_demux_get_metadata (demux);
       break;
     case FLV_STATE_DONE:
-      ret = GST_FLOW_UNEXPECTED;
+      ret = GST_FLOW_EOS;
       break;
     case FLV_STATE_SEEK:
       /* seek issued with insufficient index;
@@ -2355,7 +2412,7 @@ gst_flv_demux_loop (GstPad * pad)
     /* check EOS condition */
     if ((demux->segment.stop != -1) &&
         (demux->segment.position >= demux->segment.stop)) {
-      ret = GST_FLOW_UNEXPECTED;
+      ret = GST_FLOW_EOS;
     }
   }
 
@@ -2374,7 +2431,7 @@ pause:
     GST_LOG_OBJECT (demux, "pausing task, reason %s", reason);
     gst_pad_pause_task (pad);
 
-    if (ret == GST_FLOW_UNEXPECTED) {
+    if (ret == GST_FLOW_EOS) {
       /* handle end-of-stream/segment */
       /* so align our position with the end of it, if there is one
        * this ensures a subsequent will arrive at correct base/acc time */
@@ -2403,12 +2460,17 @@ pause:
           gst_element_post_message (GST_ELEMENT_CAST (demux),
               gst_message_new_segment_done (GST_OBJECT_CAST (demux),
                   GST_FORMAT_TIME, stop));
+          gst_flv_demux_push_src_event (demux,
+              gst_event_new_segment_done (GST_FORMAT_TIME, stop));
         } else {                /* Reverse playback */
           GST_LOG_OBJECT (demux, "Sending segment done, at beginning of "
               "segment");
           gst_element_post_message (GST_ELEMENT_CAST (demux),
               gst_message_new_segment_done (GST_OBJECT_CAST (demux),
                   GST_FORMAT_TIME, demux->segment.start));
+          gst_flv_demux_push_src_event (demux,
+              gst_event_new_segment_done (GST_FORMAT_TIME,
+                  demux->segment.start));
         }
       } else {
         /* normal playback, send EOS to all linked pads */
@@ -2418,10 +2480,13 @@ pause:
         }
 
         GST_LOG_OBJECT (demux, "Sending EOS, at end of stream");
-        if (!gst_flv_demux_push_src_event (demux, gst_event_new_eos ()))
+        if (!demux->audio_pad && !demux->video_pad)
+          GST_ELEMENT_ERROR (demux, STREAM, FAILED,
+              ("Internal data stream error."), ("Got EOS before any data"));
+        else if (!gst_flv_demux_push_src_event (demux, gst_event_new_eos ()))
           GST_WARNING_OBJECT (demux, "failed pushing EOS on streams");
       }
-    } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_UNEXPECTED) {
+    } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) {
       GST_ELEMENT_ERROR (demux, STREAM, FAILED,
           ("Internal data stream error."),
           ("stream stopped, reason %s", reason));
@@ -2437,15 +2502,18 @@ gst_flv_demux_find_offset (GstFlvDemux * demux, GstSegment * segment)
 {
   gint64 bytes = 0;
   gint64 time = 0;
+  GstIndex *index;
   GstIndexEntry *entry;
 
   g_return_val_if_fail (segment != NULL, 0);
 
   time = segment->position;
 
-  if (demux->index) {
+  index = gst_flv_demux_get_index (GST_ELEMENT (demux));
+
+  if (index) {
     /* Let's check if we have an index entry for that seek time */
-    entry = gst_index_get_assoc_entry (demux->index, demux->index_id,
+    entry = gst_index_get_assoc_entry (index, demux->index_id,
         GST_INDEX_LOOKUP_BEFORE, GST_ASSOCIATION_FLAG_KEY_UNIT,
         GST_FORMAT_TIME, time);
 
@@ -2469,6 +2537,8 @@ gst_flv_demux_find_offset (GstFlvDemux * demux, GstSegment * segment)
       GST_DEBUG_OBJECT (demux, "no index entry found for %" GST_TIME_FORMAT,
           GST_TIME_ARGS (segment->start));
     }
+
+    gst_object_unref (index);
   }
 
   return bytes;
@@ -2597,7 +2667,7 @@ gst_flv_demux_handle_seek_push (GstFlvDemux * demux, GstEvent * event)
     if (!building_index) {
       demux->building_index = TRUE;
       if (!demux->file_size
-          && !gst_pad_query_peer_duration (demux->sinkpad, GST_FORMAT_BYTES,
+          && !gst_pad_peer_query_duration (demux->sinkpad, GST_FORMAT_BYTES,
               &demux->file_size)) {
         GST_WARNING_OBJECT (demux, "Failed to query upstream file size");
         GST_OBJECT_UNLOCK (demux);
@@ -2765,7 +2835,7 @@ exit:
     gst_pad_pause_task (demux->sinkpad);
   } else {
     gst_pad_start_task (demux->sinkpad,
-        (GstTaskFunction) gst_flv_demux_loop, demux->sinkpad);
+        (GstTaskFunction) gst_flv_demux_loop, demux->sinkpad, NULL);
   }
 
   GST_PAD_STREAM_UNLOCK (demux->sinkpad);
@@ -2784,7 +2854,7 @@ wrong_format:
 
 /* If we can pull that's prefered */
 static gboolean
-gst_flv_demux_sink_activate (GstPad * sinkpad)
+gst_flv_demux_sink_activate (GstPad * sinkpad, GstObject * parent)
 {
   GstQuery *query;
   gboolean pull_mode;
@@ -2796,67 +2866,60 @@ gst_flv_demux_sink_activate (GstPad * sinkpad)
     goto activate_push;
   }
 
-  gst_query_parse_scheduling (query, &pull_mode, NULL, NULL, NULL, NULL, NULL);
+  pull_mode = gst_query_has_scheduling_mode (query, GST_PAD_MODE_PULL);
   gst_query_unref (query);
 
   if (!pull_mode)
     goto activate_push;
 
   GST_DEBUG_OBJECT (sinkpad, "activating pull");
-  return gst_pad_activate_pull (sinkpad, TRUE);
+  return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE);
 
 activate_push:
   {
     GST_DEBUG_OBJECT (sinkpad, "activating push");
-    return gst_pad_activate_push (sinkpad, TRUE);
+    return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE);
   }
 }
 
-/* This function gets called when we activate ourselves in push mode.
- * We cannot seek (ourselves) in the stream */
 static gboolean
-gst_flv_demux_sink_activate_push (GstPad * sinkpad, gboolean active)
+gst_flv_demux_sink_activate_mode (GstPad * sinkpad, GstObject * parent,
+    GstPadMode mode, gboolean active)
 {
+  gboolean res;
   GstFlvDemux *demux;
 
-  demux = GST_FLV_DEMUX (gst_pad_get_parent (sinkpad));
-
-  demux->random_access = FALSE;
-
-  gst_object_unref (demux);
-
-  return TRUE;
-}
+  demux = GST_FLV_DEMUX (parent);
 
-/* this function gets called when we activate ourselves in pull mode.
- * We can perform  random access to the resource and we start a task
- * to start reading */
-static gboolean
-gst_flv_demux_sink_activate_pull (GstPad * sinkpad, gboolean active)
-{
-  GstFlvDemux *demux;
-
-  demux = GST_FLV_DEMUX (gst_pad_get_parent (sinkpad));
-
-  if (active) {
-    demux->random_access = TRUE;
-    gst_object_unref (demux);
-    return gst_pad_start_task (sinkpad, (GstTaskFunction) gst_flv_demux_loop,
-        sinkpad);
-  } else {
-    demux->random_access = FALSE;
-    gst_object_unref (demux);
-    return gst_pad_stop_task (sinkpad);
+  switch (mode) {
+    case GST_PAD_MODE_PUSH:
+      demux->random_access = FALSE;
+      res = TRUE;
+      break;
+    case GST_PAD_MODE_PULL:
+      if (active) {
+        demux->random_access = TRUE;
+        res = gst_pad_start_task (sinkpad, (GstTaskFunction) gst_flv_demux_loop,
+            sinkpad, NULL);
+      } else {
+        demux->random_access = FALSE;
+        res = gst_pad_stop_task (sinkpad);
+      }
+      break;
+    default:
+      res = FALSE;
+      break;
   }
+  return res;
 }
 
 static gboolean
-gst_flv_demux_sink_event (GstPad * pad, GstEvent * event)
+gst_flv_demux_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
 {
   GstFlvDemux *demux;
   gboolean ret = FALSE;
 
-  demux = GST_FLV_DEMUX (gst_pad_get_parent (pad));
+  demux = GST_FLV_DEMUX (parent);
 
   GST_DEBUG_OBJECT (demux, "handling event %s", GST_EVENT_TYPE_NAME (event));
 
@@ -2872,20 +2935,34 @@ gst_flv_demux_sink_event (GstPad * pad, GstEvent * event)
       ret = gst_flv_demux_push_src_event (demux, event);
       break;
     case GST_EVENT_EOS:
+    {
+      GstIndex *index;
+
       GST_DEBUG_OBJECT (demux, "received EOS");
-      if (demux->index) {
+
+      index = gst_flv_demux_get_index (GST_ELEMENT (demux));
+
+      if (index) {
         GST_DEBUG_OBJECT (demux, "committing index");
-        gst_index_commit (demux->index, demux->index_id);
-      }
-      if (!demux->no_more_pads) {
-        gst_element_no_more_pads (GST_ELEMENT (demux));
-        demux->no_more_pads = TRUE;
+        gst_index_commit (index, demux->index_id);
+        gst_object_unref (index);
       }
 
-      if (!gst_flv_demux_push_src_event (demux, event))
-        GST_WARNING_OBJECT (demux, "failed pushing EOS on streams");
+      if (!demux->audio_pad && !demux->video_pad)
+        GST_ELEMENT_ERROR (demux, STREAM, FAILED,
+            ("Internal data stream error."), ("Got EOS before any data"));
+      else {
+        if (!demux->no_more_pads) {
+          gst_element_no_more_pads (GST_ELEMENT (demux));
+          demux->no_more_pads = TRUE;
+        }
+
+        if (!gst_flv_demux_push_src_event (demux, event))
+          GST_WARNING_OBJECT (demux, "failed pushing EOS on streams");
+      }
       ret = TRUE;
       break;
+    }
     case GST_EVENT_SEGMENT:
     {
       GstSegment in_segment;
@@ -2917,18 +2994,16 @@ gst_flv_demux_sink_event (GstPad * pad, GstEvent * event)
       break;
   }
 
-  gst_object_unref (demux);
-
   return ret;
 }
 
 static gboolean
-gst_flv_demux_src_event (GstPad * pad, GstEvent * event)
+gst_flv_demux_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
 {
   GstFlvDemux *demux;
   gboolean ret = FALSE;
 
-  demux = GST_FLV_DEMUX (gst_pad_get_parent (pad));
+  demux = GST_FLV_DEMUX (parent);
 
   GST_DEBUG_OBJECT (demux, "handling event %s", GST_EVENT_TYPE_NAME (event));
 
@@ -2945,18 +3020,16 @@ gst_flv_demux_src_event (GstPad * pad, GstEvent * event)
       break;
   }
 
-  gst_object_unref (demux);
-
   return ret;
 }
 
 static gboolean
-gst_flv_demux_query (GstPad * pad, GstQuery * query)
+gst_flv_demux_query (GstPad * pad, GstObject * parent, GstQuery * query)
 {
   gboolean res = TRUE;
   GstFlvDemux *demux;
 
-  demux = GST_FLV_DEMUX (gst_pad_get_parent (pad));
+  demux = GST_FLV_DEMUX (parent);
 
   switch (GST_QUERY_TYPE (query)) {
     case GST_QUERY_DURATION:
@@ -3018,6 +3091,7 @@ gst_flv_demux_query (GstPad * pad, GstQuery * query)
         }
       }
       res = TRUE;
+      /* FIXME, check index this way is not thread safe */
       if (fmt != GST_FORMAT_TIME || !demux->index) {
         gst_query_set_seeking (query, fmt, FALSE, -1, -1);
       } else if (demux->random_access) {
@@ -3041,23 +3115,11 @@ gst_flv_demux_query (GstPad * pad, GstQuery * query)
     }
     case GST_QUERY_LATENCY:
     default:
-    {
-      GstPad *peer;
-
-      if ((peer = gst_pad_get_peer (demux->sinkpad))) {
-        /* query latency on peer pad */
-        res = gst_pad_query (peer, query);
-        gst_object_unref (peer);
-      } else {
-        /* no peer, we don't know */
-        res = FALSE;
-      }
+      res = gst_pad_query_default (pad, parent, query);
       break;
-    }
   }
 
 beach:
-  gst_object_unref (demux);
 
   return res;
 }
@@ -3084,7 +3146,7 @@ gst_flv_demux_change_state (GstElement * element, GstStateChange transition)
       if (G_UNLIKELY (!demux->index)) {
         GST_DEBUG_OBJECT (demux, "no index provided creating our own");
 
-        demux->index = gst_index_factory_make ("memindex");
+        demux->index = g_object_new (gst_mem_index_get_type (), NULL);
 
         gst_index_get_writer_id (demux->index, GST_OBJECT (demux),
             &demux->index_id);
@@ -3111,27 +3173,39 @@ gst_flv_demux_change_state (GstElement * element, GstStateChange transition)
   return ret;
 }
 
+#if 0
 static void
 gst_flv_demux_set_index (GstElement * element, GstIndex * index)
 {
   GstFlvDemux *demux = GST_FLV_DEMUX (element);
+  GstIndex *old_index;
 
   GST_OBJECT_LOCK (demux);
-  if (demux->index)
-    gst_object_unref (demux->index);
+
+  old_index = demux->index;
+
   if (index) {
     demux->index = gst_object_ref (index);
     demux->own_index = FALSE;
   } else
     demux->index = NULL;
 
+  if (old_index)
+    gst_object_unref (demux->index);
+
+  gst_object_ref (index);
+
   GST_OBJECT_UNLOCK (demux);
+
   /* object lock might be taken again */
   if (index)
     gst_index_get_writer_id (index, GST_OBJECT (element), &demux->index_id);
+
   GST_DEBUG_OBJECT (demux, "Set index %" GST_PTR_FORMAT, demux->index);
 
+  gst_object_unref (index);
 }
+#endif
 
 static GstIndex *
 gst_flv_demux_get_index (GstElement * element)
@@ -3219,8 +3293,11 @@ gst_flv_demux_class_init (GstFlvDemuxClass * klass)
 
   gstelement_class->change_state =
       GST_DEBUG_FUNCPTR (gst_flv_demux_change_state);
+
+#if 0
   gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_flv_demux_set_index);
   gstelement_class->get_index = GST_DEBUG_FUNCPTR (gst_flv_demux_get_index);
+#endif
 
   gst_element_class_add_pad_template (gstelement_class,
       gst_static_pad_template_get (&flv_sink_template));
@@ -3228,7 +3305,7 @@ gst_flv_demux_class_init (GstFlvDemuxClass * klass)
       gst_static_pad_template_get (&audio_src_template));
   gst_element_class_add_pad_template (gstelement_class,
       gst_static_pad_template_get (&video_src_template));
-  gst_element_class_set_details_simple (gstelement_class, "FLV Demuxer",
+  gst_element_class_set_static_metadata (gstelement_class, "FLV Demuxer",
       "Codec/Demuxer",
       "Demux FLV feeds into digital streams",
       "Julien Moutte <julien@moutte.net>");
@@ -3246,19 +3323,19 @@ gst_flv_demux_init (GstFlvDemux * demux)
       GST_DEBUG_FUNCPTR (gst_flv_demux_chain));
   gst_pad_set_activate_function (demux->sinkpad,
       GST_DEBUG_FUNCPTR (gst_flv_demux_sink_activate));
-  gst_pad_set_activatepull_function (demux->sinkpad,
-      GST_DEBUG_FUNCPTR (gst_flv_demux_sink_activate_pull));
-  gst_pad_set_activatepush_function (demux->sinkpad,
-      GST_DEBUG_FUNCPTR (gst_flv_demux_sink_activate_push));
+  gst_pad_set_activatemode_function (demux->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_flv_demux_sink_activate_mode));
 
   gst_element_add_pad (GST_ELEMENT (demux), demux->sinkpad);
 
   demux->adapter = gst_adapter_new ();
-  demux->taglist = gst_tag_list_new ();
+  demux->taglist = gst_tag_list_new_empty ();
   gst_segment_init (&demux->segment, GST_FORMAT_TIME);
 
   demux->own_index = FALSE;
 
+  GST_OBJECT_FLAG_SET (demux, GST_ELEMENT_FLAG_INDEXABLE);
+
   gst_flv_demux_cleanup (demux);
 }
 
@@ -3277,5 +3354,5 @@ plugin_init (GstPlugin * plugin)
 }
 
 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR,
-    "flv", "FLV muxing and demuxing plugin",
+    flv, "FLV muxing and demuxing plugin",
     plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)