Index: docs/gst/gstreamer-sections.txt
===================================================================
RCS file: /cvs/gstreamer/gstreamer/docs/gst/gstreamer-sections.txt,v
retrieving revision 1.280
diff -u -B -b -p -r1.280 gstreamer-sections.txt
--- docs/gst/gstreamer-sections.txt	8 Jan 2007 20:30:11 -0000	1.280
+++ docs/gst/gstreamer-sections.txt	10 Jan 2007 13:59:06 -0000
@@ -1003,6 +1003,8 @@ gst_message_new_buffering
 gst_message_new_warning
 gst_message_new_duration
 gst_message_new_state_dirty
+gst_message_new_lost_preroll
+gst_message_new_prerolled
 gst_message_parse_clock_lost
 gst_message_parse_clock_provide
 gst_message_parse_error
@@ -1014,6 +1016,8 @@ gst_message_parse_tag
 gst_message_parse_buffering
 gst_message_parse_warning
 gst_message_parse_duration
+gst_message_parse_lost_preroll
+gst_message_parse_prerolled
 gst_message_ref
 gst_message_unref
 <SUBSECTION Standard>
@@ -1602,6 +1606,10 @@ gst_query_new_duration
 gst_query_set_duration
 gst_query_parse_duration
 
+gst_query_new_latency
+gst_query_parse_latency
+gst_query_set_latency
+
 gst_query_new_seeking
 gst_query_set_seeking
 gst_query_parse_seeking
Index: gst/gstbin.c
===================================================================
RCS file: /cvs/gstreamer/gstreamer/gst/gstbin.c,v
retrieving revision 1.342
diff -u -B -b -p -r1.342 gstbin.c
--- gst/gstbin.c	7 Dec 2006 10:02:19 -0000	1.342
+++ gst/gstbin.c	10 Jan 2007 13:59:07 -0000
@@ -1276,6 +1276,86 @@ gst_bin_iterate_sources (GstBin * bin)
   return result;
 }
 
+static GstMessage *
+gst_bin_message_new_state (GstObject *src, gboolean new_base, GstState pending)
+{
+  GstMessage *message;
+
+  message = gst_message_new_element (src, 
+     gst_structure_new ("GstBinState", 
+	"prerolled", G_TYPE_BOOLEAN, FALSE, 
+	"new-base-time", G_TYPE_BOOLEAN, new_base,
+	"pending", GST_TYPE_STATE, pending, NULL));
+
+  return message;
+}
+
+static gboolean
+gst_bin_message_set_state_prerolled (GstMessage *message,
+	gboolean live, gboolean upstream_live, gint64 min_latency, gint64 max_latency)
+{
+  const GstStructure *s;
+  
+  if (GST_MESSAGE_TYPE (message) != GST_MESSAGE_ELEMENT) 
+    return FALSE;
+
+  s = gst_message_get_structure (message);
+  gst_structure_set ((GstStructure *)s, 
+      "prerolled", G_TYPE_BOOLEAN, TRUE, 
+      "live", G_TYPE_BOOLEAN, live,
+      "upstream-live", G_TYPE_BOOLEAN, upstream_live,
+      "min-latency", G_TYPE_INT64, min_latency,
+      "max-latency", G_TYPE_INT64, max_latency,
+      NULL);
+
+  return TRUE;
+}
+
+static gboolean
+gst_bin_message_parse_state_prerolled (GstMessage *message, gboolean *prerolled,
+	gboolean *live, gboolean *upstream_live, gint64 *min_latency, gint64 *max_latency)
+{
+  const GstStructure *s;
+
+  if (GST_MESSAGE_TYPE (message) != GST_MESSAGE_ELEMENT) 
+    return FALSE;
+
+  s = gst_message_get_structure (message);
+  if (prerolled) {
+    gst_structure_get_boolean (s, "prerolled", prerolled);
+    if (*prerolled) {
+      if (live)
+        gst_structure_get_boolean (s, "live", live);
+      if (upstream_live)
+        gst_structure_get_boolean (s, "upstream-live", upstream_live);
+      if (min_latency)
+        *min_latency = g_value_get_int64 (gst_structure_get_value (s, "min-latency"));
+      if (max_latency)
+        *max_latency = g_value_get_int64 (gst_structure_get_value (s, "max-latency"));
+    }
+  }
+  return TRUE;
+}
+
+static gboolean
+gst_bin_message_parse_state (GstMessage *message, gboolean *prerolled, gboolean *new_base, GstState *pending)
+{
+  const GstStructure *s;
+
+  if (GST_MESSAGE_TYPE (message) != GST_MESSAGE_ELEMENT) 
+    return FALSE;
+
+  s = gst_message_get_structure (message);
+  if (prerolled)
+    gst_structure_get_boolean (s, "prerolled", prerolled);
+  if (new_base)
+    gst_structure_get_boolean (s, "new-base-time", new_base);
+  if (pending)
+    gst_structure_get_enum (s, "pending", GST_TYPE_STATE, (gint *)pending);
+    	
+  return TRUE;
+}
+
 /*
  * MT safe
  */
@@ -1296,6 +1376,89 @@ gst_bin_get_state_func (GstElement * ele
   return ret;
 }
 
+static GstStateChangeReturn
+gst_bin_handle_prerolled (GstBin * bin)
+{
+  GList *walk, *to_change = NULL, *next;
+  gboolean complete = TRUE;
+  GstClockTime new_base_time;
+  GstClock *clock;
+
+  GST_OBJECT_LOCK (bin);
+  /* inspect the element messages */
+  for (walk = bin->messages; walk; walk = next) {
+    GstMessage *message = GST_MESSAGE_CAST (walk->data);
+    next = g_list_next (walk);
+
+    if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ELEMENT) {
+      gboolean prerolled;
+
+      GST_DEBUG_OBJECT (bin, "checking message %p", message);
+
+      gst_bin_message_parse_state (message, &prerolled, NULL, NULL);
+      if (prerolled) {
+        GST_DEBUG_OBJECT (bin, "need to change %p", message);
+        to_change = g_list_prepend (to_change, message);
+        bin->messages = g_list_delete_link (bin->messages, walk);
+      }
+      else {
+        GST_DEBUG_OBJECT (bin, "don't yet need to change %p", message);
+        complete = FALSE;
+	break;
+      }
+    }
+  }
+  if (!complete)
+    goto not_complete;
+
+  /* get clock in case we need to adjust a base time */
+  clock = GST_ELEMENT_CLOCK (bin);
+  if (clock) {
+    gst_object_ref (clock);
+    new_base_time = GST_CLOCK_TIME_NONE;
+  }
+  else {
+    new_base_time = 0;
+  }
+  GST_OBJECT_UNLOCK (bin);
+
+
+  for (walk = to_change; walk; walk = g_list_next (walk)) {
+    GstMessage *message = GST_MESSAGE_CAST (walk->data);
+    GstElement *element = GST_ELEMENT_CAST (GST_MESSAGE_SRC (message));
+    gboolean new_base;
+    GstState pending;
+
+    GST_DEBUG_OBJECT (bin, "we have element %s", GST_ELEMENT_NAME (element));
+
+    gst_bin_message_parse_state (message, NULL, &new_base, &pending);
+
+    if (new_base) {
+      if (new_base_time == GST_CLOCK_TIME_NONE) 
+	new_base_time = gst_clock_get_time (clock);
+
+      GST_DEBUG_OBJECT (bin, "setting new base time to on %"GST_TIME_FORMAT, 
+		      GST_TIME_ARGS (new_base_time));
+
+      gst_element_set_base_time (element, new_base_time);
+    }
+    GST_DEBUG_OBJECT (bin, "setting state to %s", gst_element_state_get_name (pending));
+    gst_element_set_state (element, pending);
+
+    gst_message_unref (message);
+  }
+  g_list_free (to_change);
+
+  return TRUE;
+
+not_complete:
+  {
+    bin->messages = g_list_concat (to_change, bin->messages);
+    GST_OBJECT_UNLOCK (bin);
+    return TRUE;
+  }
+}
+
 static void
 gst_bin_recalc_state (GstBin * bin, gboolean force)
 {
@@ -1412,6 +1575,7 @@ done:
   switch (ret) {
     case GST_STATE_CHANGE_SUCCESS:
     case GST_STATE_CHANGE_NO_PREROLL:
+      gst_bin_handle_prerolled (bin);
       ret = gst_element_continue_state (GST_ELEMENT_CAST (bin), ret);
       break;
     case GST_STATE_CHANGE_ASYNC:
@@ -1748,26 +1912,39 @@ static GstStateChangeReturn
 gst_bin_element_set_state (GstBin * bin, GstElement * element, GstState pending)
 {
   GstStateChangeReturn ret;
+  GstState next;
   gboolean locked;
 
   /* peel off the locked flag */
   GST_OBJECT_LOCK (element);
   locked = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_LOCKED_STATE);
+  next = GST_STATE_NEXT (element);
   GST_OBJECT_UNLOCK (element);
 
   /* skip locked elements */
-  if (G_UNLIKELY (locked)) {
-    GST_DEBUG_OBJECT (element,
-        "element is locked, pretending state change succeeded");
-    ret = GST_STATE_CHANGE_SUCCESS;
-    goto done;
-  }
+  if (G_UNLIKELY (locked))
+    goto locked;
+
+  if (next != GST_STATE_VOID_PENDING && pending > GST_STATE_READY)
+    goto was_busy;
 
   /* change state */
   ret = gst_element_set_state (element, pending);
 
-done:
   return ret;
+
+locked:
+  {
+    GST_DEBUG_OBJECT (element,
+        "element is locked, pretending state change succeeded");
+    return GST_STATE_CHANGE_SUCCESS;
+  }
+was_busy:
+  {
+    GST_DEBUG_OBJECT (element,
+        "element is was busy delaying state change");
+    return GST_STATE_CHANGE_ASYNC;
+  }
 }
 
 /* gst_iterator_fold functions for pads_activate
@@ -1938,11 +2115,23 @@ restart:
                 gst_element_state_get_name (next));
             break;
           case GST_STATE_CHANGE_ASYNC:
+	  {
+	    GstMessage *message;
+
             GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
                 "child '%s' is changing state asynchronously",
                 GST_ELEMENT_NAME (child));
             have_async = TRUE;
+
+	    /* create message */
+	    message = gst_bin_message_new_state (GST_OBJECT_CAST (child), 
+			FALSE, next);
+
+            GST_OBJECT_LOCK (bin);
+            bin_replace_message (bin, message, GST_MESSAGE_ELEMENT);
+            GST_OBJECT_UNLOCK (bin);
             break;
+	  }
           case GST_STATE_CHANGE_FAILURE:
             GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
                 "child '%s' failed to go to state %d(%s)",
@@ -1978,7 +2167,7 @@ restart:
   }
 
   ret = parent_class->change_state (element, transition);
-  if (ret == GST_STATE_CHANGE_FAILURE)
+  if (G_UNLIKELY (ret == GST_STATE_CHANGE_FAILURE))
     goto done;
 
   if (have_no_preroll) {
@@ -1998,10 +2187,13 @@ done:
 
   return ret;
 
+  /* ERRORS */
 activate_failure:
+  {
   GST_CAT_WARNING_OBJECT (GST_CAT_STATES, element,
       "failure (de)activating src pads");
   return GST_STATE_CHANGE_FAILURE;
+  }
 }
 
 /*
@@ -2136,6 +2327,10 @@ bin_bus_handler (GstBus * bus, GstMessag
  *     This message is never sent to the application but is forwarded to
  *     the parent.
  *
+ * GST_MESSAGE_LOST_PREROLL:
+ *
+ * GST_MESSAGE_PREROLLED:
+ *
  * OTHER: post upwards.
  */
 static void
@@ -2308,6 +2503,82 @@ gst_bin_handle_message_func (GstBin * bi
       gst_message_unref (message);
       break;
     }
+    case GST_MESSAGE_LOST_PREROLL:
+    {
+      GstMessage *msg;
+      GstState pending;
+
+      gst_message_parse_lost_preroll (message, &pending);
+
+      /* create message */
+      msg = gst_bin_message_new_state (GST_MESSAGE_SRC (message), 
+			TRUE, pending);
+
+      GST_OBJECT_LOCK (bin);
+      bin_replace_message (bin, msg, GST_MESSAGE_ELEMENT);
+      GST_OBJECT_UNLOCK (bin);
+
+      goto forward;
+    }
+    case GST_MESSAGE_PREROLLED:
+    {
+      gboolean post = TRUE;
+      GList *walk;
+      gboolean live, upstream_live;
+      gint64 min_latency, max_latency;
+
+      gst_message_parse_prerolled (message, &live, &upstream_live,
+		      &min_latency, &max_latency);
+
+      GST_OBJECT_LOCK (bin);
+      /* tag the element messages of this element */
+      for (walk = bin->messages; walk; walk = g_list_next (walk)) {
+	GstMessage *find = GST_MESSAGE_CAST (walk->data);
+
+        if (GST_MESSAGE_TYPE (find) == GST_MESSAGE_ELEMENT) {
+          if (GST_MESSAGE_SRC (find) == GST_MESSAGE_SRC (message)) {
+            gst_bin_message_set_state_prerolled (find,
+			    live, upstream_live, min_latency, max_latency);
+	  }
+	  else {
+            gboolean l = FALSE, ul = FALSE;
+            gint64 minl = 0, maxl = -1;
+	    gboolean prerolled = FALSE;
+
+            gst_bin_message_parse_state_prerolled (find, &prerolled, &l, &ul, &minl, &maxl);
+
+	    if (!prerolled) {
+              post = FALSE;
+	    }
+	    else if (post) {
+	      if (l)
+		live = TRUE;
+	      if (ul)
+		upstream_live = TRUE;
+	      if (minl != -1)
+		min_latency = MAX (min_latency, minl);
+	      if (maxl != -1) {
+		if (max_latency == -1) 
+		  max_latency = minl;
+	        else
+		  max_latency = MIN (max_latency, minl);
+	      }
+	    }
+	  }
+	}
+      }
+      GST_OBJECT_UNLOCK (bin);
+
+      if (post) {
+        /* post our combined prerolled. */
+        gst_element_post_message (GST_ELEMENT_CAST (bin), 
+		gst_message_new_prerolled (GST_OBJECT_CAST (bin), live, upstream_live, 
+			min_latency, max_latency));
+      }
+      /* free message */
+      gst_message_unref (message);
+      break;
+    }
     default:
       goto forward;
   }
Index: gst/gstelement.c
===================================================================
RCS file: /cvs/gstreamer/gstreamer/gst/gstelement.c,v
retrieving revision 1.445
diff -u -B -b -p -r1.445 gstelement.c
--- gst/gstelement.c	15 Dec 2006 16:01:58 -0000	1.445
+++ gst/gstelement.c	10 Jan 2007 13:59:07 -0000
@@ -82,6 +82,7 @@
 #include <gobject/gvaluecollector.h>
 
 #include "gstelement.h"
+#include "gstenumtypes.h"
 #include "gstbus.h"
 #include "gstmarshal.h"
 #include "gsterror.h"
@@ -1645,7 +1646,7 @@ gst_element_is_locked_state (GstElement 
  *
  * MT safe.
  *
- * Returns: TRUE if the state was changed, FALSE if bad parameterss were given
+ * Returns: TRUE if the state was changed, FALSE if bad parameters were given
  * or the elements state-locking needed no change.
  */
 gboolean
@@ -1675,11 +1676,13 @@ gst_element_set_locked_state (GstElement
   return TRUE;
 
 was_ok:
+  {
   GST_CAT_DEBUG (GST_CAT_STATES, "elements %s was in locked state %d",
       GST_ELEMENT_NAME (element), old);
   GST_OBJECT_UNLOCK (element);
 
   return FALSE;
+  }
 }
 
 /**
@@ -2074,7 +2077,7 @@ complete:
 void
 gst_element_lost_state (GstElement * element)
 {
-  GstState current_state;
+  GstState old_state, new_state;
   GstMessage *message;
 
   g_return_if_fail (GST_IS_ELEMENT (element));
@@ -2084,18 +2087,27 @@ gst_element_lost_state (GstElement * ele
       GST_STATE_RETURN (element) == GST_STATE_CHANGE_FAILURE)
     goto nothing_lost;
 
-  current_state = GST_STATE (element);
+  old_state = GST_STATE (element);
+
+  if (old_state > GST_STATE_PAUSED)
+    new_state = GST_STATE_PAUSED;
+  else
+    new_state = old_state;
 
   GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
-      "lost state of %s", gst_element_state_get_name (current_state));
+      "lost state of %s", gst_element_state_get_name (old_state));
 
-  GST_STATE_NEXT (element) = current_state;
-  GST_STATE_PENDING (element) = current_state;
+  GST_STATE_NEXT (element) = new_state;
+  GST_STATE_PENDING (element) = new_state;
   GST_STATE_RETURN (element) = GST_STATE_CHANGE_ASYNC;
   GST_OBJECT_UNLOCK (element);
 
   message = gst_message_new_state_changed (GST_OBJECT_CAST (element),
-      current_state, current_state, current_state);
+      new_state, new_state, new_state);
+  gst_element_post_message (element, message);
+
+  message = gst_message_new_lost_preroll (GST_OBJECT_CAST (element), 
+	old_state);
   gst_element_post_message (element, message);
 
   /* and mark us dirty */
Index: gst/gstmessage.c
===================================================================
RCS file: /cvs/gstreamer/gstreamer/gst/gstmessage.c,v
retrieving revision 1.56
diff -u -B -b -p -r1.56 gstmessage.c
--- gst/gstmessage.c	22 Sep 2006 15:29:23 -0000	1.56
+++ gst/gstmessage.c	10 Jan 2007 13:59:07 -0000
@@ -108,6 +108,8 @@ static GstMessageQuarks message_quarks[]
   {GST_MESSAGE_SEGMENT_START, "segment-start", 0},
   {GST_MESSAGE_SEGMENT_DONE, "segment-done", 0},
   {GST_MESSAGE_DURATION, "duration", 0},
+  {GST_MESSAGE_LOST_PREROLL, "lost-preroll", 0},
+  {GST_MESSAGE_PREROLLED, "prerolled", 0},
   {0, NULL, 0}
 };
 
@@ -697,6 +699,59 @@ gst_message_new_duration (GstObject * sr
 }
 
 /**
+ * gst_message_new_lost_preroll:
+ * @src: The object originating the message.
+ * @pending: The state of @src when it lost preroll
+ *
+ * Returns: The new lost_preroll message.
+ *
+ * MT safe.
+ */
+GstMessage *
+gst_message_new_lost_preroll (GstObject * src, GstState pending)
+{
+  GstMessage *message;
+
+  message = gst_message_new_custom (GST_MESSAGE_LOST_PREROLL, src,
+      gst_structure_new ("GstMessageLostPreroll",
+          "pending", GST_TYPE_STATE, pending, NULL));
+
+  return message;
+}
+
+/**
+ * gst_message_new_prerolled:
+ * @src: The object originating the message.
+ * @live: if the object is synchronized against the clock
+ * @upstream_live: if an upstream object is synchronized against the clock
+ * @min_latency: the minimal latency 
+ * @max_latency: the maximal latency 
+ *
+ * This message is posted by sinks when they preroll. The parameters to this
+ * functions are mostly obtained from the latency query performed by the sink.
+ *
+ * Returns: The new prerolled message. 
+ *
+ * MT safe.
+ */
+GstMessage *
+gst_message_new_prerolled (GstObject * src, gboolean live, gboolean upstream_live,
+    gint64 min_latency, gint64 max_latency)
+{
+  GstMessage *message;
+
+  message = gst_message_new_custom (GST_MESSAGE_PREROLLED, src,
+      gst_structure_new ("GstMessagePrerolled",
+          "live", G_TYPE_BOOLEAN, live,
+          "upstream-live", G_TYPE_BOOLEAN, upstream_live,
+          "min-latency", G_TYPE_INT64, min_latency,
+          "max-latency", G_TYPE_INT64, max_latency, NULL));
+
+  return message;
+}
+
+
+/**
  * gst_message_get_structure:
  * @message: The #GstMessage.
  *
@@ -749,7 +804,7 @@ gst_message_parse_tag (GstMessage * mess
  * MT safe.
  */
 void
-gst_message_parse_buffering (GstMessage * message, gint * percent)
+gst_message_parse_buffering (GstMessage * message, gint *percent)
 {
   g_return_if_fail (GST_IS_MESSAGE (message));
   g_return_if_fail (GST_MESSAGE_TYPE (message) == GST_MESSAGE_BUFFERING);
@@ -1021,3 +1076,59 @@ gst_message_parse_duration (GstMessage *
     *duration =
         g_value_get_int64 (gst_structure_get_value (structure, "duration"));
 }
+
+/**
+ * gst_message_parse_lost_preroll:
+ * @message: A valid #GstMessage of type GST_MESSAGE_LOST_PREROLL.
+ * @pending: Result location for the pending state, or NULL
+ *
+ * Extract the pending state from the lost_preroll message. The pending state is
+ * the state the element was in when it lost the preroll buffer.
+ *
+ * MT safe.
+ */
+void
+gst_message_parse_lost_preroll (GstMessage *message, GstState *pending)
+{
+  const GstStructure *structure;
+
+  g_return_if_fail (GST_IS_MESSAGE (message));
+  g_return_if_fail (GST_MESSAGE_TYPE (message) == GST_MESSAGE_LOST_PREROLL);
+
+  structure = gst_message_get_structure (message);
+  if (pending)
+    *pending = g_value_get_enum (gst_structure_get_value (structure, "pending"));
+}
+
+/**
+ * gst_message_parse_prerolled:
+ * @message: A valid #GstMessage of type GST_MESSAGE_PREROLLED.
+ * @live: if the object is synchronized against the clock
+ * @upstream_live: if an upstream object is synchronized against the clock
+ * @min_latency: the minimal latency 
+ * @max_latency: the maximal latency 
+ *
+ * Extract the prerolled message.
+ *
+ * MT safe.
+ */
+void
+gst_message_parse_prerolled (GstMessage * message, gboolean *live, gboolean *upstream_live,
+    gint64 *min_latency, gint64 *max_latency)
+{
+  const GstStructure *structure;
+
+  g_return_if_fail (GST_IS_MESSAGE (message));
+  g_return_if_fail (GST_MESSAGE_TYPE (message) == GST_MESSAGE_PREROLLED);
+
+  structure = gst_message_get_structure (message);
+  if (live)
+    *live = g_value_get_boolean (gst_structure_get_value (structure, "live"));
+  if (upstream_live)
+    *upstream_live = g_value_get_boolean (gst_structure_get_value (structure, "upstream-live"));
+  if (min_latency)
+    *min_latency = g_value_get_int64 (gst_structure_get_value (structure, "min-latency"));
+  if (max_latency)
+    *max_latency = g_value_get_int64 (gst_structure_get_value (structure, "max-latency"));
+}
+
Index: gst/gstmessage.h
===================================================================
RCS file: /cvs/gstreamer/gstreamer/gst/gstmessage.h,v
retrieving revision 1.43
diff -u -B -b -p -r1.43 gstmessage.h
--- gst/gstmessage.h	9 Oct 2006 11:06:50 -0000	1.43
+++ gst/gstmessage.h	10 Jan 2007 13:59:07 -0000
@@ -55,6 +55,8 @@ typedef struct _GstMessageClass GstMessa
  * @GST_MESSAGE_SEGMENT_START: pipeline started playback of a segment.
  * @GST_MESSAGE_SEGMENT_DONE: pipeline completed playback of a segment.
  * @GST_MESSAGE_DURATION: The duration of a pipeline changed.
+ * @GST_MESSAGE_LOST_PREROLL: Posted by ASYNC elements when they are flushed.
+ * @GST_MESSAGE_PREROLLED: Posted by elements when they preroll.
  * @GST_MESSAGE_ANY: mask for all of the above messages.
  *
  * The different message types that are available.
@@ -84,6 +86,8 @@ typedef enum
   GST_MESSAGE_SEGMENT_START     = (1 << 16),
   GST_MESSAGE_SEGMENT_DONE      = (1 << 17),
   GST_MESSAGE_DURATION          = (1 << 18),
+  GST_MESSAGE_LOST_PREROLL      = (1 << 19),
+  GST_MESSAGE_PREROLLED         = (1 << 20),
   GST_MESSAGE_ANY               = ~0
 } GstMessageType;
 
@@ -259,6 +263,9 @@ GstMessage *	gst_message_new_element		(G
 GstMessage *	gst_message_new_segment_start	(GstObject * src, GstFormat format, gint64 position);
 GstMessage *	gst_message_new_segment_done	(GstObject * src, GstFormat format, gint64 position);
 GstMessage *	gst_message_new_duration	(GstObject * src, GstFormat format, gint64 duration);
+GstMessage *	gst_message_new_lost_preroll	(GstObject * src, GstState pending);
+GstMessage *	gst_message_new_prerolled	(GstObject * src, gboolean live, gboolean upstream_live,
+		                                 gint64 min_latency, gint64 max_latency);
 GstMessage *	gst_message_new_custom		(GstMessageType type,
 						 GstObject    * src,
 						 GstStructure * structure);
@@ -275,6 +282,9 @@ void		gst_message_parse_new_clock	(GstMe
 void		gst_message_parse_segment_start (GstMessage *message, GstFormat *format, gint64 *position);
 void		gst_message_parse_segment_done	(GstMessage *message, GstFormat *format, gint64 *position);
 void		gst_message_parse_duration	(GstMessage *message, GstFormat *format, gint64 *duration);
+void		gst_message_parse_lost_preroll	(GstMessage *message, GstState *pending);
+void 		gst_message_parse_prerolled	(GstMessage *message, gboolean *live, gboolean *upstream_live,
+		                                 gint64 *min_latency, gint64 *max_latency);
 
 const GstStructure *  gst_message_get_structure	(GstMessage *message);
 
Index: gst/gstquery.c
===================================================================
RCS file: /cvs/gstreamer/gstreamer/gst/gstquery.c,v
retrieving revision 1.36
diff -u -B -b -p -r1.36 gstquery.c
--- gst/gstquery.c	6 Oct 2006 14:46:04 -0000	1.36
+++ gst/gstquery.c	10 Jan 2007 13:59:08 -0000
@@ -537,6 +537,102 @@ gst_query_parse_duration (GstQuery * que
 }
 
 /**
+ * gst_query_new_latency:
+ * @format: the #GstFormat for this latency query
+ *
+ * Constructs a new latency query object in the given format. 
+ * Use gst_query_unref() when done with it. A latency query is usually performed
+ * by sinks to compensate for additional latency introduced by elements in the
+ * pipeline.
+ *
+ * Returns: A #GstQuery
+ *
+ * Since: 0.10.12
+ */
+GstQuery*
+gst_query_new_latency (GstFormat format)
+{
+  GstQuery *query;
+  GstStructure *structure;
+
+  structure = gst_structure_empty_new ("GstQueryLatency");
+  gst_structure_set (structure,
+      "format", GST_TYPE_FORMAT, format,
+      "live", G_TYPE_BOOLEAN, FALSE,
+      "min-latency", G_TYPE_INT64, (gint64)0, 
+      "max-latency", G_TYPE_INT64, (gint64)-1, 
+      NULL);
+
+  query = gst_query_new (GST_QUERY_LATENCY, structure);
+
+  return query;
+}
+
+/**
+ * gst_query_set_latency:
+ * @query: a #GstQuery
+ * @format: the #GstFormat 
+ * @live: indication if upstream is live
+ * @min_latency: the minimal latency of the live element
+ * @max_latency: the maximal latency of the live element
+ *
+ * Answer a latency query by setting the requested values in the given format.
+ *
+ * Since: 0.10.12
+ */
+void
+gst_query_set_latency (GstQuery *query, GstFormat format,
+     gboolean live, gint64 min_latency, gint64 max_latency)
+{
+  GstStructure *structure;
+
+  g_return_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_LATENCY);
+
+  structure = gst_query_get_structure (query);
+  gst_structure_set (structure,
+      "format", GST_TYPE_FORMAT, format,
+      "live", G_TYPE_BOOLEAN, live,
+      "min-latency", G_TYPE_INT64, min_latency, 
+      "max-latency", G_TYPE_INT64, max_latency, 
+      NULL);
+}
+
+/**
+ * gst_query_parse_latency:
+ * @query: a #GstQuery
+ * @format: the storage for the #GstFormat of the duration value, or NULL.
+ * @live: the storage for the live value or NULL
+ * @min_latency: the storage for the min latency or NULL
+ * @max_latency: the storage for the max latency or NULL
+ *
+ * Parse a latency query answer. 
+ *
+ * Since: 0.10.12
+ */
+void
+gst_query_parse_latency (GstQuery *query, GstFormat *format,
+    gboolean *live, gint64 *min_latency, gint64 *max_latency)
+{
+  GstStructure *structure;
+
+  g_return_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_LATENCY);
+
+  structure = gst_query_get_structure (query);
+  if (format)
+    *format = g_value_get_enum (gst_structure_get_value (structure,
+            "format"));
+  if (live)
+    *live = g_value_get_boolean (gst_structure_get_value (structure,
+            "live"));
+  if (min_latency)
+    *min_latency = g_value_get_int64 (gst_structure_get_value (structure,
+            "min-latency"));
+  if (max_latency)
+    *max_latency = g_value_get_int64 (gst_structure_get_value (structure,
+            "max-latency"));
+}
+
+/**
  * gst_query_new_convert:
  * @src_format: the source #GstFormat for the new query
  * @value: the value to convert
Index: gst/gstquery.h
===================================================================
RCS file: /cvs/gstreamer/gstreamer/gst/gstquery.h,v
retrieving revision 1.34
diff -u -B -b -p -r1.34 gstquery.h
--- gst/gstquery.h	26 Jul 2006 22:59:19 -0000	1.34
+++ gst/gstquery.h	10 Jan 2007 13:59:08 -0000
@@ -205,6 +205,13 @@ GstQuery*	gst_query_new_duration		(GstFo
 void		gst_query_set_duration		(GstQuery *query, GstFormat format, gint64 duration);
 void		gst_query_parse_duration	(GstQuery *query, GstFormat *format, gint64 *duration);
 
+/* latency query */
+GstQuery*	gst_query_new_latency		(GstFormat format);
+void		gst_query_set_latency		(GstQuery *query, GstFormat format, 
+						 gboolean live, gint64 min_latency, gint64 max_latency);
+void		gst_query_parse_latency		(GstQuery *query, GstFormat *format,
+						 gboolean *live, gint64 *min_latency, gint64 *max_latency);
+
 /* convert query */
 GstQuery*	gst_query_new_convert		(GstFormat src_format, gint64 value, GstFormat dest_format);
 void		gst_query_set_convert		(GstQuery *query, GstFormat src_format, gint64 src_value,
@@ -222,11 +228,9 @@ void            gst_query_parse_segment 
 /* application specific query */
 GstQuery *	gst_query_new_application 	(GstQueryType type,
                                                  GstStructure *structure);
-
-
 GstStructure *  gst_query_get_structure		(GstQuery *query);
 
-/* moved from old gstqueryutils.h */
+/* seeking query */
 GstQuery*       gst_query_new_seeking           (GstFormat format);
 void            gst_query_set_seeking           (GstQuery *query, GstFormat format,
                                                  gboolean seekable,
@@ -236,7 +240,7 @@ void            gst_query_parse_seeking 
                                                  gboolean *seekable,
                                                  gint64 *segment_start,
                                                  gint64 *segment_end);
-
+/* formats query */
 GstQuery*       gst_query_new_formats           (void);
 void            gst_query_set_formats           (GstQuery *query, gint n_formats, ...);
 void            gst_query_set_formatsv          (GstQuery *query, gint n_formats, GstFormat *formats);
Index: libs/gst/base/gstbasesink.c
===================================================================
RCS file: /cvs/gstreamer/gstreamer/libs/gst/base/gstbasesink.c,v
retrieving revision 1.159
diff -u -B -b -p -r1.159 gstbasesink.c
--- libs/gst/base/gstbasesink.c	6 Jan 2007 17:18:03 -0000	1.159
+++ libs/gst/base/gstbasesink.c	10 Jan 2007 13:59:08 -0000
@@ -125,8 +125,10 @@
  *
  * The qos property will enable the quality-of-service features of the basesink
  * which gather statistics about the real-time performance of the clock
- * synchronisation. For each dropped buffer it will also send a QoS message
- * upstream.
+ * synchronisation. For each buffer received in the sink, statistics are
+ * gathered and a QOS event is send upstream with these numbers. This
+ * information can then be used by upstream elements to reduce their processing
+ * rate, for example.
  *
  * Last reviewed on 2006-09-27 (0.10.11)
  */
@@ -334,7 +336,7 @@ gst_base_sink_class_init (GstBaseSinkCla
           G_PARAM_READWRITE));
 
   g_object_class_install_property (gobject_class, PROP_QOS,
-      g_param_spec_boolean ("qos", "Qos", "Generate QoS events upstream",
+      g_param_spec_boolean ("qos", "Qos", "Generate Quality-of-Service events upstream",
           DEFAULT_QOS, G_PARAM_READWRITE));
 
   gstelement_class->change_state =
@@ -567,7 +569,7 @@ gst_base_sink_get_max_lateness (GstBaseS
  * @sink: the sink
  * @enabled: the new qos value.
  *
- * Configures @sink to send QoS events upstream.
+ * Configures @sink to send Quality-of-Service events upstream.
  *
  * Since: 0.10.5
  */
@@ -581,10 +583,10 @@ gst_base_sink_set_qos_enabled (GstBaseSi
  * gst_base_sink_is_qos_enabled:
  * @sink: the sink
  *
- * Checks if @sink is currently configured to send QoS events
+ * Checks if @sink is currently configured to send Quality-of-Service events
  * upstream.
  *
- * Returns: TRUE if the sink is configured to perform QoS.
+ * Returns: TRUE if the sink is configured to perform Quality-of-Service.
  *
  * Since: 0.10.5
  */
@@ -749,7 +751,9 @@ gst_base_sink_commit_state (GstBaseSink 
   GstState current, next, pending, post_pending;
   GstMessage *message;
   gboolean post_paused = FALSE;
+  gboolean post_prerolled = FALSE;
   gboolean post_playing = FALSE;
+  gboolean sync;
 
   /* we are certainly not playing async anymore now */
   basesink->playing_async = FALSE;
@@ -759,6 +763,7 @@ gst_base_sink_commit_state (GstBaseSink 
   next = GST_STATE_NEXT (basesink);
   pending = GST_STATE_PENDING (basesink);
   post_pending = pending;
+  sync = basesink->sync;
 
   switch (pending) {
     case GST_STATE_PLAYING:
@@ -788,6 +793,7 @@ gst_base_sink_commit_state (GstBaseSink 
     case GST_STATE_PAUSED:
       GST_DEBUG_OBJECT (basesink, "commiting state to PAUSED");
       post_paused = TRUE;
+      post_prerolled = TRUE;
       post_pending = GST_STATE_VOID_PENDING;
       break;
     case GST_STATE_READY:
@@ -815,6 +821,33 @@ gst_base_sink_commit_state (GstBaseSink 
         next, pending, GST_STATE_VOID_PENDING);
     gst_element_post_message (GST_ELEMENT_CAST (basesink), message);
   }
+  if (post_prerolled) {
+    GstQuery *query;
+    GstFormat format;
+    gboolean upstream_live;
+    gint64 min_latency, max_latency;
+    GstPad *peer;
+
+    format = GST_FORMAT_TIME;
+    upstream_live = FALSE;
+    min_latency = 0;
+    max_latency = -1;
+
+    if ((peer = gst_pad_get_peer (basesink->sinkpad))) {
+      query = gst_query_new_latency (format);
+
+      if (gst_pad_query (peer, query)) 
+        gst_query_parse_latency (query, &format, &upstream_live, &min_latency, &max_latency);
+
+      gst_query_unref (query);
+      gst_object_unref (peer);
+    }
+
+    message = gst_message_new_prerolled (GST_OBJECT_CAST (basesink), 
+	      sync, upstream_live, min_latency, max_latency);
+
+    gst_element_post_message (GST_ELEMENT_CAST (basesink), message);
+  }
   /* and mark dirty */
   if (post_paused || post_playing) {
     gst_element_post_message (GST_ELEMENT_CAST (basesink),
@@ -1245,7 +1278,7 @@ gst_base_sink_send_qos (GstBaseSink * ba
   GstEvent *event;
   gboolean res;
 
-  /* generate QoS event */
+  /* generate Quality-of-Service event */
   GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, basesink,
       "qos: proportion: %lf, diff %" G_GINT64_FORMAT ", timestamp %"
       GST_TIME_FORMAT, proportion, diff, GST_TIME_ARGS (time));
@@ -1272,7 +1305,7 @@ gst_base_sink_perform_qos (GstBaseSink *
 
   start = priv->current_rstart;
 
-  /* if QoS disabled, do nothing */
+  /* if Quality-of-Service disabled, do nothing */
   if (!g_atomic_int_get (&priv->qos_enabled) || start == -1)
     return;
 
@@ -2590,6 +2623,7 @@ gst_base_sink_change_state (GstElement *
   switch (transition) {
     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
       GST_DEBUG_OBJECT (basesink, "PLAYING to PAUSED");
+      /* FIXME, make sure we cannot enter _render first */
 
       /* we need to call ::unlock before locking PREROLL_LOCK
        * since we lock it before going into ::render */
Index: libs/gst/base/gstbasesrc.c
===================================================================
RCS file: /cvs/gstreamer/gstreamer/libs/gst/base/gstbasesrc.c,v
retrieving revision 1.124
diff -u -B -b -p -r1.124 gstbasesrc.c
--- libs/gst/base/gstbasesrc.c	5 Jan 2007 11:57:49 -0000	1.124
+++ libs/gst/base/gstbasesrc.c	10 Jan 2007 13:59:14 -0000
@@ -717,6 +717,20 @@ gst_base_src_default_query (GstBaseSrc *
       break;
     }
     case GST_QUERY_LATENCY:
+    {
+      GstFormat format;
+      gint64 min_latency, max_latency;
+
+      gst_query_parse_latency (query, &format, NULL, &min_latency, &max_latency);
+
+      /* we can only update the live flag */
+      GST_LIVE_LOCK (src);
+      gst_query_set_latency (query, format, src->is_live, min_latency, max_latency);
+      GST_LIVE_UNLOCK (src);
+
+      res = TRUE;
+      break;
+    }
     case GST_QUERY_JITTER:
     case GST_QUERY_RATE:
     default:
Index: tests/check/generic/sinks.c
===================================================================
RCS file: /cvs/gstreamer/gstreamer/tests/check/generic/sinks.c,v
retrieving revision 1.19
diff -u -B -b -p -r1.19 sinks.c
--- tests/check/generic/sinks.c	1 Jul 2006 20:56:55 -0000	1.19
+++ tests/check/generic/sinks.c	10 Jan 2007 13:59:14 -0000
@@ -313,7 +313,7 @@ GST_START_TEST (test_livesrc_sink)
         if (n_sink == 2) {
           fail_unless (old == GST_STATE_READY, "unexpected old");
           fail_unless (new == GST_STATE_PAUSED, "unexpected new");
-          fail_unless (pending == GST_STATE_PLAYING, "unexpected pending");
+          fail_unless (pending == GST_STATE_VOID_PENDING, "unexpected pending");
         } else if (n_sink == 1) {
           fail_unless (old == GST_STATE_PAUSED, "unexpected old");
           fail_unless (new == GST_STATE_PLAYING, "unexpected new");
Index: tests/check/gst/gstbin.c
===================================================================
RCS file: /cvs/gstreamer/gstreamer/tests/check/gst/gstbin.c,v
retrieving revision 1.51
diff -u -B -b -p -r1.51 gstbin.c
--- tests/check/gst/gstbin.c	9 Dec 2006 20:23:10 -0000	1.51
+++ tests/check/gst/gstbin.c	10 Jan 2007 13:59:14 -0000
@@ -277,7 +277,7 @@ GST_START_TEST (test_message_state_chang
   fail_unless (pending == GST_STATE_VOID_PENDING);
 
   /* wait for async thread to settle down */
-  while (GST_OBJECT_REFCOUNT_VALUE (pipeline) > 2)
+  while (GST_OBJECT_REFCOUNT_VALUE (pipeline) > 3)
     THREAD_SWITCH ();
 
   /* each object is referenced by a message;
Index: tests/check/pipelines/cleanup.c
===================================================================
RCS file: /cvs/gstreamer/gstreamer/tests/check/pipelines/cleanup.c,v
retrieving revision 1.9
diff -u -B -b -p -r1.9 cleanup.c
--- tests/check/pipelines/cleanup.c	1 Jul 2006 20:56:56 -0000	1.9
+++ tests/check/pipelines/cleanup.c	10 Jan 2007 13:59:14 -0000
@@ -88,7 +88,7 @@ GST_START_TEST (test_pipeline_unref)
   sink = gst_bin_get_by_name (GST_BIN (pipeline), "sink");
   fail_if (sink == NULL);
 
-  run_pipeline (pipeline, s, GST_MESSAGE_NEW_CLOCK | GST_MESSAGE_STATE_CHANGED,
+  run_pipeline (pipeline, s, GST_MESSAGE_NEW_CLOCK | GST_MESSAGE_STATE_CHANGED | GST_MESSAGE_PREROLLED,
       GST_MESSAGE_EOS);
   while (GST_OBJECT_REFCOUNT_VALUE (src) > 1)
     THREAD_SWITCH ();
Index: tests/check/pipelines/simple-launch-lines.c
===================================================================
RCS file: /cvs/gstreamer/gstreamer/tests/check/pipelines/simple-launch-lines.c,v
retrieving revision 1.21
diff -u -B -b -p -r1.21 simple-launch-lines.c
--- tests/check/pipelines/simple-launch-lines.c	1 Jul 2006 20:56:56 -0000	1.21
+++ tests/check/pipelines/simple-launch-lines.c	10 Jan 2007 13:59:14 -0000
@@ -97,23 +97,23 @@ GST_START_TEST (test_2_elements)
 
   s = "fakesrc can-activate-push=false ! fakesink can-activate-pull=true";
   run_pipeline (setup_pipeline (s), s,
-      GST_MESSAGE_NEW_CLOCK | GST_MESSAGE_STATE_CHANGED, GST_MESSAGE_UNKNOWN);
+      GST_MESSAGE_NEW_CLOCK | GST_MESSAGE_STATE_CHANGED | GST_MESSAGE_PREROLLED, GST_MESSAGE_UNKNOWN);
 
   s = "fakesrc can-activate-push=true ! fakesink can-activate-pull=false";
   run_pipeline (setup_pipeline (s), s,
-      GST_MESSAGE_NEW_CLOCK | GST_MESSAGE_STATE_CHANGED, GST_MESSAGE_UNKNOWN);
+      GST_MESSAGE_NEW_CLOCK | GST_MESSAGE_STATE_CHANGED | GST_MESSAGE_PREROLLED, GST_MESSAGE_UNKNOWN);
 
   s = "fakesrc can-activate-push=false num-buffers=10 ! fakesink can-activate-pull=true";
   run_pipeline (setup_pipeline (s), s,
-      GST_MESSAGE_NEW_CLOCK | GST_MESSAGE_STATE_CHANGED, GST_MESSAGE_EOS);
+      GST_MESSAGE_NEW_CLOCK | GST_MESSAGE_STATE_CHANGED | GST_MESSAGE_PREROLLED, GST_MESSAGE_EOS);
 
   s = "fakesrc can-activate-push=true num-buffers=10 ! fakesink can-activate-pull=false";
   run_pipeline (setup_pipeline (s), s,
-      GST_MESSAGE_NEW_CLOCK | GST_MESSAGE_STATE_CHANGED, GST_MESSAGE_EOS);
+      GST_MESSAGE_NEW_CLOCK | GST_MESSAGE_STATE_CHANGED | GST_MESSAGE_PREROLLED, GST_MESSAGE_EOS);
 
   s = "fakesrc can-activate-push=false ! fakesink can-activate-pull=false";
   ASSERT_CRITICAL (run_pipeline (setup_pipeline (s), s,
-          GST_MESSAGE_NEW_CLOCK | GST_MESSAGE_STATE_CHANGED,
+          GST_MESSAGE_NEW_CLOCK | GST_MESSAGE_STATE_CHANGED | GST_MESSAGE_PREROLLED,
           GST_MESSAGE_UNKNOWN));
 }
 
