Merge the tizen patch based on 1.12.2
[platform/upstream/gstreamer.git] / plugins / elements / gsttypefindelement.c
index 9a99d4c..0d8f092 100644 (file)
  *
  * You should have received a copy of the GNU Library General Public
  * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
  */
 /**
  * SECTION:element-typefind
+ * @title: typefind
  *
  * Determines the media-type of a stream. It applies typefind functions in the
- * order of their rank. One the type has been deteted it sets its src pad caps
+ * order of their rank. Once the type has been detected it sets its src pad caps
  * to the found media type.
  *
  * Whenever a type is found the #GstTypeFindElement::have-type signal is
@@ -39,7 +40,7 @@
  * 1) get a list of all typefind functions sorted best to worst
  * 2) if all elements have been called with all requested data goto 8
  * 3) call all functions once with all available data
- * 4) if a function returns a value >= PROP_MAXIMUM goto 8
+ * 4) if a function returns a value >= PROP_MAXIMUM goto 8 (never implemented))
  * 5) all functions with a result > PROP_MINIMUM or functions that did not get
  *    all requested data (where peek returned NULL) stay in list
  * 6) seek to requested offset of best function that still has open data
@@ -97,7 +98,14 @@ GST_STATIC_PAD_TEMPLATE ("src",
 
 /* Require at least 2kB of data before we attempt typefinding in chain-mode.
  * 128kB is massive overkill for the maximum, but doesn't do any harm */
+#ifdef TIZEN_PROFILE_TV
+/* TV demuxer needs 4*1024 as min size to match demuxer, if not
+ * sometimes can't find correct demuxer
+ */
+#define TYPE_FIND_MIN_SIZE   (4*1024)
+#else
 #define TYPE_FIND_MIN_SIZE   (2*1024)
+#endif
 #define TYPE_FIND_MAX_SIZE (128*1024)
 
 /* TypeFind signals and args */
@@ -111,7 +119,6 @@ enum
   PROP_0,
   PROP_CAPS,
   PROP_MINIMUM,
-  PROP_MAXIMUM,
   PROP_FORCE_CAPS,
   PROP_LAST
 };
@@ -136,10 +143,6 @@ static void gst_type_find_element_set_property (GObject * object,
 static void gst_type_find_element_get_property (GObject * object,
     guint prop_id, GValue * value, GParamSpec * pspec);
 
-#if 0
-static const GstEventMask *gst_type_find_element_src_event_mask (GstPad * pad);
-#endif
-
 static gboolean gst_type_find_element_src_event (GstPad * pad,
     GstObject * parent, GstEvent * event);
 static gboolean gst_type_find_handle_src_query (GstPad * pad,
@@ -165,7 +168,7 @@ static gboolean gst_type_find_element_activate_src_mode (GstPad * pad,
     GstObject * parent, GstPadMode mode, gboolean active);
 static GstFlowReturn
 gst_type_find_element_chain_do_typefinding (GstTypeFindElement * typefind,
-    gboolean check_avail);
+    gboolean check_avail, gboolean at_eos);
 static void gst_type_find_element_send_cached_events (GstTypeFindElement *
     typefind);
 
@@ -175,24 +178,72 @@ static guint gst_type_find_element_signals[LAST_SIGNAL] = { 0 };
 
 static void
 gst_type_find_element_have_type (GstTypeFindElement * typefind,
-    guint probability, const GstCaps * caps)
+    guint probability, GstCaps * caps)
 {
-  GstCaps *copy;
+  GstEvent *event;
 
   g_assert (caps != NULL);
 
   GST_INFO_OBJECT (typefind, "found caps %" GST_PTR_FORMAT ", probability=%u",
       caps, probability);
 
+  /* Do nothing if downstream is pulling from us */
+  if (GST_PAD_MODE (typefind->src) == GST_PAD_MODE_PULL)
+    return;
+
+  GST_OBJECT_LOCK (typefind);
+
+  /* Now actually send the CAPS event downstream.
+   *
+   * Try to directly send the CAPS event downstream that we created in
+   * gst_type_find_element_emit_have_type() if it is still there, instead
+   * of creating a new one. No need to create an equivalent one, replacing
+   * it in the sticky event list and possibly causing renegotiation
+   */
+  event = gst_pad_get_sticky_event (typefind->src, GST_EVENT_CAPS, 0);
+  if (event) {
+    GstCaps *event_caps;
+
+    gst_event_parse_caps (event, &event_caps);
+    if (caps != event_caps) {
+      gst_event_unref (event);
+      event = gst_event_new_caps (caps);
+    }
+  } else {
+    event = gst_event_new_caps (caps);
+  }
+
+  GST_OBJECT_UNLOCK (typefind);
+
+  gst_pad_push_event (typefind->src, event);
+}
+
+static void
+gst_type_find_element_emit_have_type (GstTypeFindElement * typefind,
+    guint probability, GstCaps * caps)
+{
+  GstEvent *event;
+
+  /* Update caps field immediatly so that caps queries and properties can be
+   * honored in all "have-type" signal handlers.
+   */
   GST_OBJECT_LOCK (typefind);
   if (typefind->caps)
     gst_caps_unref (typefind->caps);
-  typefind->caps = gst_caps_copy (caps);
-  copy = gst_caps_ref (typefind->caps);
+  typefind->caps = gst_caps_ref (caps);
   GST_OBJECT_UNLOCK (typefind);
 
-  gst_pad_push_event (typefind->src, gst_event_new_caps (copy));
-  gst_caps_unref (copy);
+  /* Only store the caps event at this point. We give signal handlers
+   * the chance to look at the caps before they are sent downstream.
+   * They are only forwarded downstream later in the default signal
+   * handler after all application signal handlers
+   */
+  event = gst_event_new_caps (caps);
+  gst_pad_store_sticky_event (typefind->src, event);
+  gst_event_unref (event);
+
+  g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE], 0,
+      probability, caps);
 }
 
 static void
@@ -214,11 +265,6 @@ gst_type_find_element_class_init (GstTypeFindElementClass * typefind_class)
           "minimum probability required to accept caps", GST_TYPE_FIND_MINIMUM,
           GST_TYPE_FIND_MAXIMUM, GST_TYPE_FIND_MINIMUM,
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-  g_object_class_install_property (gobject_class, PROP_MAXIMUM,
-      g_param_spec_uint ("maximum", _("maximum"),
-          "probability to stop typefinding (deprecated; non-functional)",
-          GST_TYPE_FIND_MINIMUM, GST_TYPE_FIND_MAXIMUM, GST_TYPE_FIND_MAXIMUM,
-          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
   g_object_class_install_property (gobject_class, PROP_FORCE_CAPS,
       g_param_spec_boxed ("force-caps", _("force caps"),
           _("force caps without doing a typefind"), GST_TYPE_CAPS,
@@ -233,7 +279,7 @@ gst_type_find_element_class_init (GstTypeFindElementClass * typefind_class)
    * been found.
    */
   gst_type_find_element_signals[HAVE_TYPE] = g_signal_new ("have-type",
-      G_TYPE_FROM_CLASS (typefind_class), G_SIGNAL_RUN_FIRST,
+      G_TYPE_FROM_CLASS (typefind_class), G_SIGNAL_RUN_LAST,
       G_STRUCT_OFFSET (GstTypeFindElementClass, have_type), NULL, NULL,
       g_cclosure_marshal_generic, G_TYPE_NONE, 2,
       G_TYPE_UINT, GST_TYPE_CAPS | G_SIGNAL_TYPE_STATIC_SCOPE);
@@ -241,15 +287,15 @@ gst_type_find_element_class_init (GstTypeFindElementClass * typefind_class)
   typefind_class->have_type =
       GST_DEBUG_FUNCPTR (gst_type_find_element_have_type);
 
-  gst_element_class_set_details_simple (gstelement_class,
+  gst_element_class_set_static_metadata (gstelement_class,
       "TypeFind",
       "Generic",
       "Finds the media type of a stream",
       "Benjamin Otte <in7y118@public.uni-hamburg.de>");
-  gst_element_class_add_pad_template (gstelement_class,
-      gst_static_pad_template_get (&type_find_element_src_template));
-  gst_element_class_add_pad_template (gstelement_class,
-      gst_static_pad_template_get (&type_find_element_sink_template));
+  gst_element_class_add_static_pad_template (gstelement_class,
+      &type_find_element_src_template);
+  gst_element_class_add_static_pad_template (gstelement_class,
+      &type_find_element_sink_template);
 
   gstelement_class->change_state =
       GST_DEBUG_FUNCPTR (gst_type_find_element_change_state);
@@ -271,6 +317,7 @@ gst_type_find_element_init (GstTypeFindElement * typefind)
       GST_DEBUG_FUNCPTR (gst_type_find_element_chain));
   gst_pad_set_event_function (typefind->sink,
       GST_DEBUG_FUNCPTR (gst_type_find_element_sink_event));
+  GST_PAD_SET_PROXY_ALLOCATION (typefind->sink);
   gst_element_add_pad (GST_ELEMENT (typefind), typefind->sink);
 
   /* srcpad */
@@ -291,7 +338,6 @@ gst_type_find_element_init (GstTypeFindElement * typefind)
   typefind->mode = MODE_TYPEFIND;
   typefind->caps = NULL;
   typefind->min_probability = 1;
-  typefind->max_probability = GST_TYPE_FIND_MAXIMUM;
 
   typefind->adapter = gst_adapter_new ();
 }
@@ -326,9 +372,6 @@ gst_type_find_element_set_property (GObject * object, guint prop_id,
     case PROP_MINIMUM:
       typefind->min_probability = g_value_get_uint (value);
       break;
-    case PROP_MAXIMUM:
-      typefind->max_probability = g_value_get_uint (value);
-      break;
     case PROP_FORCE_CAPS:
       GST_OBJECT_LOCK (typefind);
       if (typefind->force_caps)
@@ -359,9 +402,6 @@ gst_type_find_element_get_property (GObject * object, guint prop_id,
     case PROP_MINIMUM:
       g_value_set_uint (value, typefind->min_probability);
       break;
-    case PROP_MAXIMUM:
-      g_value_set_uint (value, typefind->max_probability);
-      break;
     case PROP_FORCE_CAPS:
       GST_OBJECT_LOCK (typefind);
       g_value_set_boxed (value, typefind->force_caps);
@@ -418,6 +458,8 @@ gst_type_find_handle_src_query (GstPad * pad, GstObject * parent,
       switch (format) {
         case GST_FORMAT_BYTES:
           peer_pos -= gst_adapter_available (typefind->adapter);
+          if (peer_pos < 0)     /* Clamp result to 0 */
+            peer_pos = 0;
           break;
         default:
           /* FIXME */
@@ -439,14 +481,14 @@ static gboolean
 gst_type_find_element_seek (GstTypeFindElement * typefind, GstEvent * event)
 {
   GstSeekFlags flags;
-  GstSeekType cur_type, stop_type;
+  GstSeekType start_type, stop_type;
   GstFormat format;
   gboolean flush;
   gdouble rate;
-  gint64 cur, stop;
+  gint64 start, stop;
   GstSegment seeksegment = { 0, };
 
-  gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur,
+  gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start,
       &stop_type, &stop);
 
   /* we can only seek on bytes */
@@ -461,7 +503,7 @@ gst_type_find_element_seek (GstTypeFindElement * typefind, GstEvent * event)
 
   GST_DEBUG_OBJECT (typefind, "configuring seek");
   gst_segment_do_seek (&seeksegment, rate, format, flags,
-      cur_type, cur, stop_type, stop, NULL);
+      start_type, start, stop_type, stop, NULL);
 
   flush = ! !(flags & GST_SEEK_FLAG_FLUSH);
 
@@ -494,7 +536,7 @@ gst_type_find_element_seek (GstTypeFindElement * typefind, GstEvent * event)
   typefind->offset = typefind->segment.start;
 
   /* notify start of new segment */
-  if (typefind->segment.flags & GST_SEEK_FLAG_SEGMENT) {
+  if (typefind->segment.flags & GST_SEGMENT_FLAG_SEGMENT) {
     GstMessage *msg;
 
     msg = gst_message_new_segment_start (GST_OBJECT (typefind),
@@ -507,7 +549,7 @@ gst_type_find_element_seek (GstTypeFindElement * typefind, GstEvent * event)
   /* restart our task since it might have been stopped when we did the
    * flush. */
   gst_pad_start_task (typefind->sink,
-      (GstTaskFunction) gst_type_find_element_loop, typefind->sink);
+      (GstTaskFunction) gst_type_find_element_loop, typefind->sink, NULL);
 
   /* streaming can continue now */
   GST_PAD_STREAM_UNLOCK (typefind->sink);
@@ -520,17 +562,21 @@ gst_type_find_element_src_event (GstPad * pad, GstObject * parent,
     GstEvent * event)
 {
   GstTypeFindElement *typefind = GST_TYPE_FIND_ELEMENT (parent);
+  gboolean result;
 
   if (typefind->mode != MODE_NORMAL) {
     /* need to do more? */
-    gst_mini_object_unref (GST_MINI_OBJECT_CAST (event));
+    GST_LOG_OBJECT (typefind, "Still typefinding. Not passing event upstream");
+    gst_event_unref (event);
     return FALSE;
   }
 
   /* Only handle seeks here if driving the pipeline */
   if (typefind->segment.format != GST_FORMAT_UNDEFINED &&
       GST_EVENT_TYPE (event) == GST_EVENT_SEEK) {
-    return gst_type_find_element_seek (typefind, event);
+    result = gst_type_find_element_seek (typefind, event);
+    gst_event_unref (event);
+    return result;
   } else {
     return gst_pad_push_event (typefind->sink, event);
   }
@@ -544,6 +590,7 @@ start_typefinding (GstTypeFindElement * typefind)
   GST_OBJECT_LOCK (typefind);
   if (typefind->caps)
     gst_caps_replace (&typefind->caps, NULL);
+  typefind->initial_offset = GST_BUFFER_OFFSET_NONE;
   GST_OBJECT_UNLOCK (typefind);
 
   typefind->mode = MODE_TYPEFIND;
@@ -556,20 +603,30 @@ stop_typefinding (GstTypeFindElement * typefind)
   gboolean push_cached_buffers;
   gsize avail;
   GstBuffer *buffer;
+  GstClockTime pts, dts;
 
   gst_element_get_state (GST_ELEMENT (typefind), &state, NULL, 0);
 
-  push_cached_buffers = (state >= GST_STATE_PAUSED);
+  push_cached_buffers = (state >= GST_STATE_PAUSED && typefind->caps);
 
   GST_DEBUG_OBJECT (typefind, "stopping typefinding%s",
-      push_cached_buffers ? " and pushing cached buffers" : "");
+      push_cached_buffers ? " and pushing cached events and buffers" : "");
+
+  typefind->mode = MODE_NORMAL;
+  if (push_cached_buffers)
+    gst_type_find_element_send_cached_events (typefind);
 
   GST_OBJECT_LOCK (typefind);
   avail = gst_adapter_available (typefind->adapter);
   if (avail == 0)
     goto no_data;
 
+  pts = gst_adapter_prev_pts (typefind->adapter, NULL);
+  dts = gst_adapter_prev_dts (typefind->adapter, NULL);
   buffer = gst_adapter_take_buffer (typefind->adapter, avail);
+  GST_BUFFER_PTS (buffer) = pts;
+  GST_BUFFER_DTS (buffer) = dts;
+  GST_BUFFER_OFFSET (buffer) = typefind->initial_offset;
   GST_OBJECT_UNLOCK (typefind);
 
   if (!push_cached_buffers) {
@@ -577,8 +634,6 @@ stop_typefinding (GstTypeFindElement * typefind)
   } else {
     GstPad *peer = gst_pad_get_peer (typefind->src);
 
-    typefind->mode = MODE_NORMAL;
-
     /* make sure the user gets a meaningful error message in this case,
      * which is not a core bug or bug of any kind (as the default error
      * message emitted by gstpad.c otherwise would make you think) */
@@ -594,7 +649,6 @@ stop_typefinding (GstTypeFindElement * typefind)
       typefind->mode = MODE_ERROR;      /* make the chain function error out */
       gst_buffer_unref (buffer);
     } else {
-      gst_type_find_element_send_cached_events (typefind);
       gst_pad_push (typefind->src, buffer);
     }
     if (peer)
@@ -628,44 +682,75 @@ gst_type_find_element_sink_event (GstPad * pad, GstObject * parent,
         {
           GstCaps *caps;
 
-          /* first pass the caps event downstream */
-          res = gst_pad_push_event (typefind->src, gst_event_ref (event));
-
-          /* then parse and push out our data */
+          /* Parse and push out our caps and data */
           gst_event_parse_caps (event, &caps);
           res = gst_type_find_element_setcaps (typefind, caps);
 
           gst_event_unref (event);
           break;
         }
+        case GST_EVENT_GAP:
+        {
+          GST_FIXME_OBJECT (typefind,
+              "GAP events during typefinding not handled properly");
+
+          /* FIXME: These would need to be inserted in the stream at
+           * the right position between buffers, but we combine all
+           * buffers with a GstAdapter. Drop the GAP event for now,
+           * which will only cause an implicit GAP between buffers.
+           */
+          gst_event_unref (event);
+          res = TRUE;
+          break;
+        }
         case GST_EVENT_EOS:
         {
           GST_INFO_OBJECT (typefind, "Got EOS and no type found yet");
-          gst_type_find_element_chain_do_typefinding (typefind, FALSE);
+          gst_type_find_element_chain_do_typefinding (typefind, FALSE, TRUE);
 
           res = gst_pad_push_event (typefind->src, event);
           break;
         }
-        case GST_EVENT_FLUSH_STOP:
+        case GST_EVENT_FLUSH_STOP:{
+          GList *l;
+
           GST_OBJECT_LOCK (typefind);
-          g_list_foreach (typefind->cached_events,
-              (GFunc) gst_mini_object_unref, NULL);
+
+          for (l = typefind->cached_events; l; l = l->next) {
+            if (GST_EVENT_IS_STICKY (l->data) &&
+                GST_EVENT_TYPE (l->data) != GST_EVENT_SEGMENT &&
+                GST_EVENT_TYPE (l->data) != GST_EVENT_EOS) {
+              gst_pad_store_sticky_event (typefind->src, l->data);
+            }
+            gst_event_unref (l->data);
+          }
+
           g_list_free (typefind->cached_events);
           typefind->cached_events = NULL;
           gst_adapter_clear (typefind->adapter);
           GST_OBJECT_UNLOCK (typefind);
           /* fall through */
+        }
         case GST_EVENT_FLUSH_START:
           res = gst_pad_push_event (typefind->src, event);
           break;
         default:
-          GST_DEBUG_OBJECT (typefind, "Saving %s event to send later",
-              GST_EVENT_TYPE_NAME (event));
-          GST_OBJECT_LOCK (typefind);
-          typefind->cached_events =
-              g_list_append (typefind->cached_events, event);
-          GST_OBJECT_UNLOCK (typefind);
-          res = TRUE;
+          /* Forward events that would happen before the caps event
+           * directly instead of storing them. There's no reason not
+           * to send them directly and we should only store events
+           * for later sending that would need to come after the caps
+           * event */
+          if (GST_EVENT_TYPE (event) < GST_EVENT_CAPS) {
+            res = gst_pad_push_event (typefind->src, event);
+          } else {
+            GST_DEBUG_OBJECT (typefind, "Saving %s event to send later",
+                GST_EVENT_TYPE_NAME (event));
+            GST_OBJECT_LOCK (typefind);
+            typefind->cached_events =
+                g_list_append (typefind->cached_events, event);
+            GST_OBJECT_UNLOCK (typefind);
+            res = TRUE;
+          }
           break;
       }
       break;
@@ -707,39 +792,17 @@ gst_type_find_element_setcaps (GstTypeFindElement * typefind, GstCaps * caps)
   if (gst_caps_is_any (caps))
     return TRUE;
 
-  g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE], 0,
-      GST_TYPE_FIND_MAXIMUM, caps);
+  /* Set to MODE_NORMAL before emitting have-type, in case it triggers a seek */
+  typefind->mode = MODE_NORMAL;
+  gst_type_find_element_emit_have_type (typefind, GST_TYPE_FIND_MAXIMUM, caps);
 
   /* Shortcircuit typefinding if we get caps */
-  if (typefind->mode == MODE_TYPEFIND) {
-    GstBuffer *buffer;
-    gsize avail;
-
-    GST_DEBUG_OBJECT (typefind, "Skipping typefinding, using caps from "
-        "upstream buffer: %" GST_PTR_FORMAT, caps);
-    typefind->mode = MODE_NORMAL;
+  GST_DEBUG_OBJECT (typefind, "Skipping typefinding, using caps from "
+      "upstream: %" GST_PTR_FORMAT, caps);
 
-    gst_type_find_element_send_cached_events (typefind);
-    GST_OBJECT_LOCK (typefind);
-    avail = gst_adapter_available (typefind->adapter);
-    if (avail == 0)
-      goto no_data;
-
-    buffer = gst_adapter_take_buffer (typefind->adapter, avail);
-    GST_OBJECT_UNLOCK (typefind);
-
-    GST_DEBUG_OBJECT (typefind, "Pushing buffer: %" G_GSIZE_FORMAT, avail);
-    gst_pad_push (typefind->src, buffer);
-  }
+  stop_typefinding (typefind);
 
   return TRUE;
-
-no_data:
-  {
-    GST_DEBUG_OBJECT (typefind, "no data to push");
-    GST_OBJECT_UNLOCK (typefind);
-    return TRUE;
-  }
 }
 
 static gchar *
@@ -845,10 +908,12 @@ gst_type_find_element_chain (GstPad * pad, GstObject * parent,
     case MODE_TYPEFIND:
     {
       GST_OBJECT_LOCK (typefind);
+      if (typefind->initial_offset == GST_BUFFER_OFFSET_NONE)
+        typefind->initial_offset = GST_BUFFER_OFFSET (buffer);
       gst_adapter_push (typefind->adapter, buffer);
       GST_OBJECT_UNLOCK (typefind);
 
-      res = gst_type_find_element_chain_do_typefinding (typefind, TRUE);
+      res = gst_type_find_element_chain_do_typefinding (typefind, TRUE, FALSE);
 
       if (typefind->mode == MODE_ERROR)
         res = GST_FLOW_ERROR;
@@ -865,48 +930,57 @@ gst_type_find_element_chain (GstPad * pad, GstObject * parent,
 
 static GstFlowReturn
 gst_type_find_element_chain_do_typefinding (GstTypeFindElement * typefind,
-    gboolean check_avail)
+    gboolean check_avail, gboolean at_eos)
 {
   GstTypeFindProbability probability;
-  GstCaps *caps;
+  GstCaps *caps = NULL;
   gsize avail;
   const guint8 *data;
   gboolean have_min, have_max;
 
   GST_OBJECT_LOCK (typefind);
-  avail = gst_adapter_available (typefind->adapter);
-
-  if (check_avail) {
-    have_min = avail >= TYPE_FIND_MIN_SIZE;
-    have_max = avail >= TYPE_FIND_MAX_SIZE;
-  } else {
-    have_min = TRUE;
-    have_max = TRUE;
+  if (typefind->force_caps) {
+    caps = gst_caps_ref (typefind->force_caps);
+    probability = GST_TYPE_FIND_MAXIMUM;
   }
 
-  if (!have_min)
-    goto not_enough_data;
+  if (!caps) {
+    avail = gst_adapter_available (typefind->adapter);
 
-  /* map all available data */
-  data = gst_adapter_map (typefind->adapter, avail);
-  caps = gst_type_find_helper_for_data (GST_OBJECT (typefind),
-      data, avail, &probability);
-  gst_adapter_unmap (typefind->adapter);
+    if (check_avail) {
+      have_min = avail >= TYPE_FIND_MIN_SIZE;
+      have_max = avail >= TYPE_FIND_MAX_SIZE;
+    } else {
+      have_min = avail > 0;
+      have_max = TRUE;
+    }
+
+    if (!have_min)
+      goto not_enough_data;
 
-  if (caps == NULL && have_max)
-    goto no_type_found;
-  else if (caps == NULL)
-    goto wait_for_data;
+    /* map all available data */
+    data = gst_adapter_map (typefind->adapter, avail);
+    caps = gst_type_find_helper_for_data (GST_OBJECT (typefind),
+        data, avail, &probability);
+    gst_adapter_unmap (typefind->adapter);
+
+    if (caps == NULL && have_max)
+      goto no_type_found;
+    else if (caps == NULL)
+      goto wait_for_data;
+
+    /* found a type */
+    if (probability < typefind->min_probability)
+      goto low_probability;
+  }
 
-  /* found a type */
-  if (probability < typefind->min_probability)
-    goto low_probability;
   GST_OBJECT_UNLOCK (typefind);
 
   /* probability is good enough too, so let's make it known ... emiting this
    * signal calls our object handler which sets the caps. */
-  g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE], 0,
-      probability, caps);
+  /* Set to MODE_NORMAL before emitting have-type, in case it triggers a seek */
+  typefind->mode = MODE_NORMAL;
+  gst_type_find_element_emit_have_type (typefind, probability, caps);
 
   /* .. and send out the accumulated data */
   stop_typefinding (typefind);
@@ -916,10 +990,18 @@ gst_type_find_element_chain_do_typefinding (GstTypeFindElement * typefind,
 
 not_enough_data:
   {
-    GST_DEBUG_OBJECT (typefind, "not enough data for typefinding yet "
-        "(%" G_GSIZE_FORMAT " bytes)", avail);
     GST_OBJECT_UNLOCK (typefind);
-    return GST_FLOW_OK;
+
+    if (at_eos) {
+      GST_ELEMENT_ERROR (typefind, STREAM, TYPE_NOT_FOUND,
+          (_("Stream doesn't contain enough data.")),
+          ("Can't typefind stream"));
+      return GST_FLOW_ERROR;
+    } else {
+      GST_DEBUG_OBJECT (typefind, "not enough data for typefinding yet "
+          "(%" G_GSIZE_FORMAT " bytes)", avail);
+      return GST_FLOW_OK;
+    }
   }
 no_type_found:
   {
@@ -930,11 +1012,19 @@ no_type_found:
   }
 wait_for_data:
   {
-    GST_DEBUG_OBJECT (typefind,
-        "no caps found with %" G_GSIZE_FORMAT " bytes of data, "
-        "waiting for more data", avail);
     GST_OBJECT_UNLOCK (typefind);
-    return GST_FLOW_OK;
+
+    if (at_eos) {
+      GST_ELEMENT_ERROR (typefind, STREAM, TYPE_NOT_FOUND,
+          (_("Stream doesn't contain enough data.")),
+          ("Can't typefind stream"));
+      return GST_FLOW_ERROR;
+    } else {
+      GST_DEBUG_OBJECT (typefind,
+          "no caps found with %" G_GSIZE_FORMAT " bytes of data, "
+          "waiting for more data", avail);
+      return GST_FLOW_OK;
+    }
   }
 low_probability:
   {
@@ -982,14 +1072,6 @@ gst_type_find_element_activate_src_mode (GstPad * pad, GstObject * parent,
        * activation might happen from the streaming thread. */
       gst_pad_pause_task (typefind->sink);
       res = gst_pad_activate_mode (typefind->sink, mode, active);
-      if (typefind->caps) {
-        GstCaps *caps;
-        GST_OBJECT_LOCK (typefind);
-        caps = gst_caps_ref (typefind->caps);
-        GST_OBJECT_UNLOCK (typefind);
-        gst_pad_push_event (typefind->src, gst_event_new_caps (caps));
-        gst_caps_unref (caps);
-      }
       break;
     default:
       res = TRUE;
@@ -1006,51 +1088,77 @@ gst_type_find_element_loop (GstPad * pad)
 
   typefind = GST_TYPE_FIND_ELEMENT (GST_PAD_PARENT (pad));
 
+  if (typefind->need_stream_start) {
+    gchar *stream_id;
+    GstEvent *event;
+
+    stream_id = gst_pad_create_stream_id (typefind->src,
+        GST_ELEMENT_CAST (typefind), NULL);
+
+    GST_DEBUG_OBJECT (typefind, "Pushing STREAM_START");
+    event = gst_event_new_stream_start (stream_id);
+    gst_event_set_group_id (event, gst_util_group_id_next ());
+    gst_pad_push_event (typefind->src, event);
+
+    typefind->need_stream_start = FALSE;
+    g_free (stream_id);
+  }
+
   if (typefind->mode == MODE_TYPEFIND) {
-    GstPad *peer;
+    GstPad *peer = NULL;
     GstCaps *found_caps = NULL;
     GstTypeFindProbability probability = GST_TYPE_FIND_NONE;
 
     GST_DEBUG_OBJECT (typefind, "find type in pull mode");
 
-    peer = gst_pad_get_peer (pad);
-    if (peer) {
-      gint64 size;
-      gchar *ext;
+    GST_OBJECT_LOCK (typefind);
+    if (typefind->force_caps) {
+      found_caps = gst_caps_ref (typefind->force_caps);
+      probability = GST_TYPE_FIND_MAXIMUM;
+    }
+    GST_OBJECT_UNLOCK (typefind);
+
+    if (!found_caps) {
+      peer = gst_pad_get_peer (pad);
+      if (peer) {
+        gint64 size;
+        gchar *ext;
 
-      if (!gst_pad_query_duration (peer, GST_FORMAT_BYTES, &size)) {
-        GST_WARNING_OBJECT (typefind, "Could not query upstream length!");
-        gst_object_unref (peer);
+        if (!gst_pad_query_duration (peer, GST_FORMAT_BYTES, &size)) {
+          GST_WARNING_OBJECT (typefind, "Could not query upstream length!");
+          gst_object_unref (peer);
 
-        ret = GST_FLOW_ERROR;
-        goto pause;
-      }
+          ret = GST_FLOW_ERROR;
+          goto pause;
+        }
 
-      /* the size if 0, we cannot continue */
-      if (size == 0) {
-        /* keep message in sync with message in sink event handler */
-        GST_ELEMENT_ERROR (typefind, STREAM, TYPE_NOT_FOUND,
-            (_("Stream contains no data.")), ("Can't typefind empty stream"));
-        gst_object_unref (peer);
-        ret = GST_FLOW_ERROR;
-        goto pause;
-      }
-      ext = gst_type_find_get_extension (typefind, pad);
+        /* the size if 0, we cannot continue */
+        if (size == 0) {
+          /* keep message in sync with message in sink event handler */
+          GST_ELEMENT_ERROR (typefind, STREAM, TYPE_NOT_FOUND,
+              (_("Stream contains no data.")), ("Can't typefind empty stream"));
+          gst_object_unref (peer);
+          ret = GST_FLOW_ERROR;
+          goto pause;
+        }
+        ext = gst_type_find_get_extension (typefind, pad);
 
-      found_caps =
-          gst_type_find_helper_get_range (GST_OBJECT_CAST (peer),
-          GST_OBJECT_PARENT (peer),
-          (GstTypeFindHelperGetRangeFunction) (GST_PAD_GETRANGEFUNC (peer)),
-          (guint64) size, ext, &probability);
-      g_free (ext);
+        found_caps =
+            gst_type_find_helper_get_range (GST_OBJECT_CAST (peer),
+            GST_OBJECT_PARENT (peer),
+            (GstTypeFindHelperGetRangeFunction) (GST_PAD_GETRANGEFUNC (peer)),
+            (guint64) size, ext, &probability);
+        g_free (ext);
 
-      GST_DEBUG ("Found caps %" GST_PTR_FORMAT, found_caps);
+        GST_DEBUG ("Found caps %" GST_PTR_FORMAT, found_caps);
 
-      gst_object_unref (peer);
+        gst_object_unref (peer);
+      }
     }
 
     if (!found_caps || probability < typefind->min_probability) {
       GST_DEBUG ("Trying to guess using extension");
+      gst_caps_replace (&found_caps, NULL);
       found_caps =
           gst_type_find_guess_by_extension (typefind, pad, &probability);
     }
@@ -1063,11 +1171,12 @@ gst_type_find_element_loop (GstPad * pad)
     }
 
     GST_DEBUG ("Emiting found caps %" GST_PTR_FORMAT, found_caps);
-    g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE],
-        0, probability, found_caps);
+    /* Set to MODE_NORMAL before emitting have-type, in case it triggers a seek */
     typefind->mode = MODE_NORMAL;
+    gst_type_find_element_emit_have_type (typefind, probability, found_caps);
+    gst_caps_unref (found_caps);
   } else if (typefind->mode == MODE_NORMAL) {
-    GstBuffer *outbuf;
+    GstBuffer *outbuf = NULL;
 
     if (typefind->need_segment) {
       typefind->need_segment = FALSE;
@@ -1080,7 +1189,7 @@ gst_type_find_element_loop (GstPad * pad)
     if (ret != GST_FLOW_OK)
       goto pause;
 
-    typefind->offset += 4096;
+    typefind->offset += gst_buffer_get_size (outbuf);
 
     ret = gst_pad_push (typefind->src, outbuf);
     if (ret != GST_FLOW_OK)
@@ -1104,7 +1213,7 @@ pause:
     if (ret == GST_FLOW_EOS) {
       /* perform EOS logic */
 
-      if (typefind->segment.flags & GST_SEEK_FLAG_SEGMENT) {
+      if (typefind->segment.flags & GST_SEGMENT_FLAG_SEGMENT) {
         gint64 stop;
 
         /* for segment playback we need to post when (in stream time)
@@ -1116,13 +1225,14 @@ pause:
         gst_element_post_message (GST_ELEMENT (typefind),
             gst_message_new_segment_done (GST_OBJECT (typefind),
                 GST_FORMAT_BYTES, stop));
+        gst_pad_push_event (typefind->src,
+            gst_event_new_segment_done (GST_FORMAT_BYTES, stop));
       } else {
         push_eos = TRUE;
       }
     } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) {
       /* for fatal errors we post an error message */
-      GST_ELEMENT_ERROR (typefind, STREAM, FAILED, (NULL),
-          ("stream stopped, reason %s", reason));
+      GST_ELEMENT_FLOW_ERROR (typefind, ret);
       push_eos = TRUE;
     }
     if (push_eos) {
@@ -1148,18 +1258,22 @@ gst_type_find_element_activate_sink_mode (GstPad * pad, GstObject * parent,
       if (active) {
         gst_segment_init (&typefind->segment, GST_FORMAT_BYTES);
         typefind->need_segment = TRUE;
+        typefind->need_stream_start = TRUE;
         typefind->offset = 0;
         res = TRUE;
       } else {
         res = gst_pad_stop_task (pad);
+        gst_segment_init (&typefind->segment, GST_FORMAT_UNDEFINED);
       }
       break;
     case GST_PAD_MODE_PUSH:
-      if (active)
+      if (active) {
+        gst_segment_init (&typefind->segment, GST_FORMAT_UNDEFINED);
         start_typefinding (typefind);
-      else
+      } else {
         stop_typefinding (typefind);
-
+        gst_segment_init (&typefind->segment, GST_FORMAT_UNDEFINED);
+      }
       res = TRUE;
       break;
     default:
@@ -1172,28 +1286,8 @@ gst_type_find_element_activate_sink_mode (GstPad * pad, GstObject * parent,
 static gboolean
 gst_type_find_element_activate_sink (GstPad * pad, GstObject * parent)
 {
-  GstTypeFindElement *typefind;
   GstQuery *query;
   gboolean pull_mode;
-  GstCaps *found_caps = NULL;
-  GstTypeFindProbability probability = GST_TYPE_FIND_NONE;
-
-  typefind = GST_TYPE_FIND_ELEMENT (parent);
-
-  /* if we have force caps, use those */
-  GST_OBJECT_LOCK (typefind);
-  if (typefind->force_caps) {
-    found_caps = gst_caps_ref (typefind->force_caps);
-    probability = GST_TYPE_FIND_MAXIMUM;
-    GST_OBJECT_UNLOCK (typefind);
-
-    GST_DEBUG ("Emiting found caps %" GST_PTR_FORMAT, found_caps);
-    g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE],
-        0, probability, found_caps);
-    typefind->mode = MODE_NORMAL;
-    goto typefind_push;
-  }
-  GST_OBJECT_UNLOCK (typefind);
 
   query = gst_query_new_scheduling ();
 
@@ -1202,7 +1296,9 @@ gst_type_find_element_activate_sink (GstPad * pad, GstObject * parent)
     goto typefind_push;
   }
 
-  pull_mode = gst_query_has_scheduling_mode (query, GST_PAD_MODE_PULL);
+  pull_mode = gst_query_has_scheduling_mode_with_flags (query,
+      GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE);
+
   gst_query_unref (query);
 
   if (!pull_mode)
@@ -1213,7 +1309,7 @@ gst_type_find_element_activate_sink (GstPad * pad, GstObject * parent)
 
   /* only start our task if we ourselves decide to start in pull mode */
   return gst_pad_start_task (pad, (GstTaskFunction) gst_type_find_element_loop,
-      pad);
+      pad, NULL);
 
 typefind_push:
   {