Index: configure.ac
===================================================================
RCS file: /cvs/gstreamer/gstreamer/configure.ac,v
retrieving revision 1.514
diff -u -r1.514 configure.ac
--- configure.ac	26 Oct 2007 18:39:03 -0000	1.514
+++ configure.ac	14 Nov 2007 15:27:58 -0000
@@ -358,6 +358,58 @@
 AC_FUNC_MMAP
 AM_CONDITIONAL(HAVE_MMAP, test "x$ac_cv_func_mmap_fixed_mapped" = "xyes")
 
+dnl Check for POSIX timers
+AC_CHECK_FUNCS(clock_gettime, [], [
+  AC_CHECK_LIB(rt, clock_gettime, [
+    AC_DEFINE(HAVE_CLOCK_GETTIME, 1)
+    LIBS="$LIBS -lrt"
+  ])
+])
+
+AC_CACHE_CHECK(for posix timers, gst_cv_posix_timers,AC_TRY_RUN([
+#include <time.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+      int main() {
+#if defined(_POSIX_TIMERS) && _POSIX_TIMERS >= 0 && defined(CLOCK_REALTIME)
+        return 0;
+#else
+        return 1;
+#endif
+      }],gst_cv_posix_timers=yes,gst_cv_posix_timers=no))
+
+if test "$gst_cv_posix_timers" = "yes"; then
+  AC_DEFINE(HAVE_POSIX_TIMERS,1,[Have posix timers])
+  GST_HAVE_POSIX_TIMERS_DEFINE="#define GST_HAVE_POSIX_TIMERS 1"
+else
+  GST_HAVE_POSIX_TIMERS_DEFINE="#define GST_HAVE_POSIX_TIMERS 0"
+fi
+AC_SUBST(GST_HAVE_POSIX_TIMERS_DEFINE)
+AM_CONDITIONAL(GST_HAVE_POSIX_TIMERS, test "$gst_cv_posix_timers" = "yes")
+
+AC_CACHE_CHECK(for monotonic clock, gst_cv_monotonic_clock,AC_TRY_RUN([
+#include <time.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+      int main() {
+#if defined(_POSIX_MONOTONIC_CLOCK) && _POSIX_MONOTONIC_CLOCK >= 0 && defined(CLOCK_MONOTONIC)
+        return 0;
+#else
+        return 1;
+#endif
+      }],gst_cv_monotonic_clock=yes,gst_cv_monotonic_clock=no))
+
+if test "$gst_cv_monotonic_clock" = "yes"; then
+  AC_DEFINE(HAVE_MONOTONIC_CLOCK,1,[Have a monotonic clock])
+  GST_HAVE_MONOTONIC_CLOCK_DEFINE="#define GST_HAVE_MONOTONIC_CLOCK 1"
+else
+  GST_HAVE_MONOTONIC_CLOCK_DEFINE="#define GST_HAVE_MONOTONIC_CLOCK 0"
+fi
+AC_SUBST(GST_HAVE_MONOTONIC_CLOCK_DEFINE)
+AM_CONDITIONAL(GST_HAVE_MONOTONIC_CLOCK, test "$gst_cv_monotonic_clock" = "yes")
+
 dnl Check for a way to display the function name in debug output
 AG_GST_CHECK_FUNCTION
 
Index: gst/gstclock.h
===================================================================
RCS file: /cvs/gstreamer/gstreamer/gst/gstclock.h,v
retrieving revision 1.67
diff -u -r1.67 gstclock.h
--- gst/gstclock.h	3 Oct 2007 11:16:48 -0000	1.67
+++ gst/gstclock.h	14 Nov 2007 15:27:59 -0000
@@ -106,6 +106,42 @@
  */
 #define GST_NSECOND (GST_SECOND / G_GINT64_CONSTANT (1000000000))
 
+
+/**
+ * GST_TIME_AS_SECONDS:
+ * @time: the time
+ *
+ * Convert a #GstClockTime to seconds.
+ *
+ * Since: 0.10.11
+ */
+#define GST_TIME_AS_SECONDS(time)  ((time) / GST_SECOND)
+/**
+ * GST_TIME_AS_MSECONDS:
+ *
+ * Convert a #GstClockTime to milliseconds (1/1000 of a second).
+ *
+ * Since: 0.10.11
+ */
+#define GST_TIME_AS_MSECONDS(time) (((time) + G_GINT64_CONSTANT (1000000)) / \
+    G_GINT64_CONSTANT (1000000))
+/**
+ * GST_TIME_TO_USECONDS:
+ *
+ * Convert a #GstClockTime to microseconds (1/1000000 of a second).
+ *
+ * Since: 0.10.11
+ */
+#define GST_TIME_AS_USECONDS(time) ((time) / G_GINT64_CONSTANT (1000))
+/**
+ * GST_TIME_TO_NSECONDS:
+ *
+ * Convert a #GstClockTime to nanoseconds (1/1000000000 of a second).
+ *
+ * Since: 0.10.11
+ */
+#define GST_TIME_AS_NSECONDS(time) (time)
+
 /**
  * GST_CLOCK_DIFF:
  * @s: the first time
Index: gst/gstconfig.h.in
===================================================================
RCS file: /cvs/gstreamer/gstreamer/gst/gstconfig.h.in,v
retrieving revision 1.26
diff -u -r1.26 gstconfig.h.in
--- gst/gstconfig.h.in	20 Apr 2007 08:39:35 -0000	1.26
+++ gst/gstconfig.h.in	14 Nov 2007 15:27:59 -0000
@@ -177,6 +177,12 @@
  * refcounting */
 @GST_HAVE_GLIB_2_8_DEFINE@
 
+/* whether or not POSIX realtime clock is supported */
+@GST_HAVE_POSIX_TIMERS_DEFINE@
+
+/* whether or not POSIX monotonic clock is supported */
+@GST_HAVE_MONOTONIC_CLOCK_DEFINE@
+
 /***** Deal with XML stuff, we have to handle both loadsave and registry *****/
 /* FIXME: move include to where we need it */
 /*#if (! (defined(GST_DISABLE_LOADSAVE) && defined(GST_DISABLE_REGISTRY)) )*/
Index: gst/gstsystemclock.c
===================================================================
RCS file: /cvs/gstreamer/gstreamer/gst/gstsystemclock.c,v
retrieving revision 1.48
diff -u -r1.48 gstsystemclock.c
--- gst/gstsystemclock.c	20 Sep 2007 07:42:33 -0000	1.48
+++ gst/gstsystemclock.c	14 Nov 2007 15:27:59 -0000
@@ -40,26 +40,210 @@
 
 #include "gst_private.h"
 #include "gstinfo.h"
+#include "gstconfig.h"
 
 #include "gstsystemclock.h"
 
+#if GST_HAVE_POSIX_TIMERS
+#include <time.h>
+#endif
+
 /* Define this to get some extra debug about jitter from each clock_wait */
 #undef WAIT_DEBUGGING
 
+static gboolean gst_system_clock_id_wait_async_unlocked (GstClock * clock,
+    GstClockEntry * entry);
+
+typedef struct _TimeoutSource TimeoutSource;
+
+struct _TimeoutSource
+{
+  GSource source;
+
+  GstClock *clock;
+
+  GstClockTime time;
+  gboolean time_valid;
+};
+
+#define TIMEOUT_SOURCE_CAST(s)	  	((TimeoutSource *)(s))
+#define TIMEOUT_SOURCE_CLOCK(s)	  	(TIMEOUT_SOURCE_CAST(s)->clock)
+#define TIMEOUT_SOURCE_TIME(s)          (TIMEOUT_SOURCE_CAST(s)->time)
+#define TIMEOUT_SOURCE_TIME_VALID(s)    (TIMEOUT_SOURCE_CAST(s)->time_valid)
+
+static GstClockTime
+timeout_get_now (GstClock *clock, GSource *source)
+{
+  if (!TIMEOUT_SOURCE_TIME_VALID (source)) {
+    GstClockTime real;
+
+    real = GST_CLOCK_GET_CLASS (clock)->get_internal_time (clock);
+    TIMEOUT_SOURCE_TIME (source) = gst_clock_adjust_unlocked (clock, real);
+  }
+  return TIMEOUT_SOURCE_TIME (source);
+}
+
+static gboolean
+timeout_prepare (GSource *source, gint *timeout)
+{
+  GstClock * clock = TIMEOUT_SOURCE_CLOCK (source);
+  gboolean ret;
+
+  GST_OBJECT_LOCK (clock);
+  if (clock->entries) {
+    GstClockEntry *entry;
+
+    entry = (GstClockEntry *) clock->entries->data;
+    if (entry->status != GST_CLOCK_UNSCHEDULED) {
+      GstClockTime now, entryt;
+      GstClockTimeDiff diff;
+
+      now = timeout_get_now (clock, source);
+      entryt = GST_CLOCK_ENTRY_TIME (entry);
+      diff = GST_CLOCK_DIFF (now, entryt);
+
+      ret = diff <= 0;
+      *timeout = GST_TIME_AS_MSECONDS (diff);
+      if (*timeout == 0)
+	ret = TRUE;
+
+      GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p diff: %"G_GINT64_FORMAT
+	      ", timeout %d", entry, diff, *timeout);
+
+    } else {
+      GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p unscheduled", entry);
+      ret = TRUE;
+    }
+  } else {
+    GST_CAT_DEBUG (GST_CAT_CLOCK, "no entries");
+    /* no entries, wait forever */
+    *timeout = -1;
+    ret = FALSE;
+  }
+  TIMEOUT_SOURCE_TIME_VALID (source) = ret;
+  GST_OBJECT_UNLOCK (clock);
+
+  GST_CAT_DEBUG (GST_CAT_CLOCK, "prepare %d", ret);
+
+  return ret;
+}
+
+static gboolean
+timeout_check (GSource *source)
+{
+  gboolean ret;
+  gint timeout;
+
+  GST_CAT_DEBUG (GST_CAT_CLOCK, "checking");
+  ret = timeout_prepare (source, &timeout);
+  TIMEOUT_SOURCE_TIME_VALID (source) = ret;
+  GST_CAT_DEBUG (GST_CAT_CLOCK, "check %d", ret);
+
+  return ret;
+}
+
+static gboolean
+timeout_dispatch (GSource *source, GSourceFunc callback, gpointer user_data)
+{
+  GstClock * clock = TIMEOUT_SOURCE_CLOCK (source);
+
+  GST_OBJECT_LOCK (clock);
+  if (clock->entries) {
+    GstClockEntry *entry;
+
+    entry = (GstClockEntry *) clock->entries->data;
+    if (entry->status != GST_CLOCK_UNSCHEDULED) {
+      clock->entries = g_list_delete_link (clock->entries, clock->entries);
+
+      entry->status = GST_CLOCK_OK;
+      /* entry timed out normally, fire the callback and move to the next
+       * entry */
+      GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry %p unlocked", entry);
+      if (entry->func) {
+        /* unlock before firing the callback */
+        GST_OBJECT_UNLOCK (clock);
+        GST_CAT_DEBUG (GST_CAT_CLOCK, "calling func for entry %p", entry);
+        entry->func (clock, entry->time, (GstClockID) entry,
+            entry->user_data);
+        GST_OBJECT_LOCK (clock);
+      }
+    } else {
+      GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry %p unscheduled", entry);
+      clock->entries = g_list_delete_link (clock->entries, clock->entries);
+    }
+    gst_clock_id_unref (entry);
+    GST_CAT_DEBUG (GST_CAT_CLOCK, "broadcast");
+    GST_CLOCK_BROADCAST (clock); 
+  }
+  TIMEOUT_SOURCE_TIME_VALID (source) = FALSE;
+  GST_OBJECT_UNLOCK (clock);
+
+  GST_CAT_DEBUG (GST_CAT_CLOCK, "dispatch");
+
+  return TRUE;
+}
+
+static
+GSourceFuncs timeout_funcs =
+{
+  timeout_prepare,
+  timeout_check,
+  timeout_dispatch,
+  NULL
+};
+
+static inline GSource *
+timeout_source_new (GstClock * clock)
+{
+  GSource *source;
+
+  source = g_source_new (&timeout_funcs, sizeof (TimeoutSource));
+  TIMEOUT_SOURCE_CLOCK (source) = clock;
+
+  return source;
+}
+
+enum
+{
+  PROP_0,
+  PROP_CLOCK_TYPE
+      /* FILL ME */
+};
+
+#define GST_TYPE_CLOCK_TYPE (gst_clock_type_get_type())
+static GType
+gst_clock_type_get_type (void)
+{
+  static GType clock_type_type = 0;
+  static const GEnumValue clock_types[] = {
+    {GST_CLOCK_TYPE_REALTIME, "Realtime", "realtime"},
+    {GST_CLOCK_TYPE_MONOTONIC, "Monotonic", "monotonic"},
+    {0, NULL, NULL},
+  };
+
+  if (!clock_type_type) {
+    clock_type_type = g_enum_register_static ("GstClockType", clock_types);
+  }
+  return clock_type_type;
+}
+
 /* the one instance of the systemclock */
 static GstClock *_the_system_clock = NULL;
 
 static void gst_system_clock_class_init (GstSystemClockClass * klass);
 static void gst_system_clock_init (GstSystemClock * clock);
 static void gst_system_clock_dispose (GObject * object);
+static void gst_system_clock_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec);
+static void gst_system_clock_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec);
 
 static GstClockTime gst_system_clock_get_internal_time (GstClock * clock);
 static guint64 gst_system_clock_get_resolution (GstClock * clock);
 static GstClockReturn gst_system_clock_id_wait_jitter (GstClock * clock,
     GstClockEntry * entry, GstClockTimeDiff * jitter);
 static GstClockReturn gst_system_clock_id_wait_jitter_unlocked
-    (GstClock * clock, GstClockEntry * entry, GstClockTimeDiff * jitter,
-    gboolean restart);
+    (GstClock * clock, GstClockEntry * entry, GstClockTimeDiff * jitter);
 static GstClockReturn gst_system_clock_id_wait_async (GstClock * clock,
     GstClockEntry * entry);
 static void gst_system_clock_id_unschedule (GstClock * clock,
@@ -112,6 +296,13 @@
   parent_class = g_type_class_peek_parent (klass);
 
   gobject_class->dispose = gst_system_clock_dispose;
+  gobject_class->set_property = gst_system_clock_set_property;
+  gobject_class->get_property = gst_system_clock_get_property;
+
+  g_object_class_install_property (gobject_class, PROP_CLOCK_TYPE,
+      g_param_spec_enum ("clock-type", "Clock type",
+          "The type of underlying clock implementation used",
+          GST_TYPE_CLOCK_TYPE, GST_CLOCK_TYPE_REALTIME, G_PARAM_READWRITE));
 
   gstclock_class->get_internal_time = gst_system_clock_get_internal_time;
   gstclock_class->get_resolution = gst_system_clock_get_resolution;
@@ -123,17 +314,29 @@
 static void
 gst_system_clock_init (GstSystemClock * clock)
 {
+  GMainContext *context;
+  GSource *source;
+
+  clock->ABI.abidata.clock_type = GST_CLOCK_TYPE_REALTIME;
+
   GST_OBJECT_FLAG_SET (clock,
       GST_CLOCK_FLAG_CAN_DO_SINGLE_SYNC |
       GST_CLOCK_FLAG_CAN_DO_SINGLE_ASYNC |
       GST_CLOCK_FLAG_CAN_DO_PERIODIC_SYNC |
       GST_CLOCK_FLAG_CAN_DO_PERIODIC_ASYNC);
 
+  context = g_main_context_new ();
+  clock->ABI.abidata.loop = g_main_loop_new (context, FALSE);
+  source = timeout_source_new (GST_CLOCK_CAST (clock));
+  g_source_attach (source, context);
+  g_main_context_unref (context);
+  g_source_unref (source);
+
 #if 0
   /* Uncomment this to start the async clock thread straight away */
-  GST_CLOCK_LOCK (clock);
+  GST_OBJECT_LOCK (clock);
   gst_system_clock_start_async (clock);
-  GST_CLOCK_UNLOCK (clock);
+  GST_OBJECT_UNLOCK (clock);
 #endif
 }
 
@@ -142,7 +345,7 @@
 {
   GstClock *clock = (GstClock *) object;
 
-  GstSystemClock *sysclock = GST_SYSTEM_CLOCK (clock);
+  GstSystemClock *sysclock = GST_SYSTEM_CLOCK_CAST (clock);
   GList *entries;
 
   /* else we have to stop the thread */
@@ -157,7 +360,7 @@
   }
   g_list_free (clock->entries);
   clock->entries = NULL;
-  GST_CLOCK_BROADCAST (clock);
+  g_main_loop_quit (sysclock->ABI.abidata.loop);
   GST_OBJECT_UNLOCK (clock);
 
   if (sysclock->thread)
@@ -165,6 +368,9 @@
   sysclock->thread = NULL;
   GST_CAT_DEBUG (GST_CAT_CLOCK, "joined thread");
 
+  g_main_loop_unref (sysclock->ABI.abidata.loop);
+  sysclock->ABI.abidata.loop = NULL;
+
   G_OBJECT_CLASS (parent_class)->dispose (object);
 
   if (_the_system_clock == clock) {
@@ -173,6 +379,40 @@
   }
 }
 
+static void
+gst_system_clock_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstSystemClock *src = GST_SYSTEM_CLOCK (object);
+
+  switch (prop_id) {
+    case PROP_CLOCK_TYPE:
+      src->ABI.abidata.clock_type = g_value_get_enum (value);
+      GST_CAT_DEBUG (GST_CAT_CLOCK, "clock-type set to %d",
+          src->ABI.abidata.clock_type);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_system_clock_get_property (GObject * object, guint prop_id, GValue * value,
+    GParamSpec * pspec)
+{
+  GstSystemClock *src = GST_SYSTEM_CLOCK (object);
+
+  switch (prop_id) {
+    case PROP_CLOCK_TYPE:
+      g_value_set_enum (value, src->ABI.abidata.clock_type);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
 /**
  * gst_system_clock_obtain:
  *
@@ -233,113 +473,75 @@
 
   GST_CAT_DEBUG (GST_CAT_CLOCK, "enter system clock thread");
   GST_OBJECT_LOCK (clock);
-  /* signal spinup */
   GST_CLOCK_BROADCAST (clock);
-  /* now enter our (almost) infinite loop */
-  while (!sysclock->stopping) {
-    GstClockEntry *entry;
-    GstClockTime requested;
-    GstClockReturn res;
-
-    /* check if something to be done */
-    while (clock->entries == NULL) {
-      GST_CAT_DEBUG (GST_CAT_CLOCK, "no clock entries, waiting..");
-      /* wait for work to do */
-      GST_CLOCK_WAIT (clock);
-      GST_CAT_DEBUG (GST_CAT_CLOCK, "got signal");
-      /* clock was stopping, exit */
-      if (sysclock->stopping)
-        goto exit;
-    }
-
-    /* pick the next entry */
-    entry = clock->entries->data;
-    /* if it was unscheduled, just move on to the next entry */
-    if (entry->status == GST_CLOCK_UNSCHEDULED) {
-      GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p was unscheduled", entry);
-      goto next_entry;
-    }
+  GST_OBJECT_UNLOCK (clock);
 
-    requested = entry->time;
+  /* and run */
+  g_main_loop_run (sysclock->ABI.abidata.loop);
 
-    /* now wait for the entry, we already hold the lock */
-    res =
-        gst_system_clock_id_wait_jitter_unlocked (clock, (GstClockID) entry,
-        NULL, FALSE);
-
-    switch (res) {
-      case GST_CLOCK_UNSCHEDULED:
-        /* entry was unscheduled, move to the next */
-        GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry %p unscheduled", entry);
-        goto next_entry;
-      case GST_CLOCK_OK:
-      case GST_CLOCK_EARLY:
-      {
-        /* entry timed out normally, fire the callback and move to the next
-         * entry */
-        GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry %p unlocked", entry);
-        if (entry->func) {
-          /* unlock before firing the callback */
-          GST_OBJECT_UNLOCK (clock);
-          entry->func (clock, entry->time, (GstClockID) entry,
-              entry->user_data);
-          GST_OBJECT_LOCK (clock);
-        }
-        if (entry->type == GST_CLOCK_ENTRY_PERIODIC) {
-          /* adjust time now */
-          entry->time = requested + entry->interval;
-          /* and resort the list now */
-          clock->entries =
-              g_list_sort (clock->entries, gst_clock_id_compare_func);
-          /* and restart */
-          continue;
-        } else {
-          goto next_entry;
-        }
-      }
-      case GST_CLOCK_BUSY:
-        /* somebody unlocked the entry but is was not canceled, This means that
-         * either a new entry was added in front of the queue or some other entry
-         * was canceled. Whatever it is, pick the head entry of the list and
-         * continue waiting. */
-        GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry %p needs restart", entry);
-        continue;
-      default:
-        GST_CAT_DEBUG (GST_CAT_CLOCK,
-            "strange result %d waiting for %p, skipping", res, entry);
-        g_warning ("%s: strange result %d waiting for %p, skipping",
-            GST_OBJECT_NAME (clock), res, entry);
-        goto next_entry;
-    }
-  next_entry:
-    /* we remove the current entry and unref it */
-    clock->entries = g_list_remove (clock->entries, entry);
-    gst_clock_id_unref ((GstClockID) entry);
-  }
-exit:
+  GST_OBJECT_LOCK (clock);
   /* signal exit */
   GST_CLOCK_BROADCAST (clock);
   GST_OBJECT_UNLOCK (clock);
   GST_CAT_DEBUG (GST_CAT_CLOCK, "exit system clock thread");
 }
 
+#if GST_HAVE_POSIX_TIMERS
+static inline clockid_t
+clock_type_to_posix_id (GstClockType clock_type)
+{
+#if GST_HAVE_MONOTONIC_CLOCK
+    if (clock_type == GST_CLOCK_TYPE_MONOTONIC)
+      return CLOCK_MONOTONIC;
+    else
+#endif
+      return CLOCK_REALTIME;
+}
+#endif
+
 /* MT safe */
 static GstClockTime
 gst_system_clock_get_internal_time (GstClock * clock)
 {
+#if GST_HAVE_POSIX_TIMERS
+  GstSystemClock *sysclock = GST_SYSTEM_CLOCK_CAST (clock);
+  clockid_t ptype;
+  struct timespec ts;
+
+  ptype = clock_type_to_posix_id (sysclock->ABI.abidata.clock_type);
+
+  if (G_UNLIKELY (clock_gettime (ptype, &ts)))
+    return GST_CLOCK_TIME_NONE;
+
+  return GST_TIMESPEC_TO_TIME (ts);
+#else
   GTimeVal timeval;
 
   g_get_current_time (&timeval);
 
   return GST_TIMEVAL_TO_TIME (timeval);
+#endif
 }
 
 static guint64
 gst_system_clock_get_resolution (GstClock * clock)
 {
+#if GST_HAVE_POSIX_TIMERS
+  GstSystemClock *sysclock = GST_SYSTEM_CLOCK_CAST (clock);
+  clockid_t ptype;
+  struct timespec ts;
+
+  ptype = clock_type_to_posix_id (sysclock->ABI.abidata.clock_type);
+
+  if (G_UNLIKELY (clock_getres (ptype, &ts)))
+    return GST_CLOCK_TIME_NONE;
+
+  return GST_TIMESPEC_TO_TIME (ts);
+#else
   return 1 * GST_USECOND;
+#endif
 }
-
+  
 /* synchronously wait on the given GstClockEntry.
  *
  * We do this by blocking on the global clock GCond variable with
@@ -358,88 +560,97 @@
  */
 static GstClockReturn
 gst_system_clock_id_wait_jitter_unlocked (GstClock * clock,
-    GstClockEntry * entry, GstClockTimeDiff * jitter, gboolean restart)
+    GstClockEntry * entry, GstClockTimeDiff * jitter)
 {
-  GstClockTime entryt, real, now, target;
+  GstClockTime entryt, real, now;
   GstClockTimeDiff diff;
+  gboolean first = TRUE;
 
-  /* need to call the overridden method */
-  real = GST_CLOCK_GET_CLASS (clock)->get_internal_time (clock);
   entryt = GST_CLOCK_ENTRY_TIME (entry);
 
-  now = gst_clock_adjust_unlocked (clock, real);
-  if (jitter) {
-    *jitter = GST_CLOCK_DIFF (entryt, now);
-  }
-  diff = entryt - now;
-  /* FIXME: should that be not just:
-   * target = GST_CLOCK_GET_CLASS (clock)->get_internal_time (clock) + diff;
-   * or even
-   * target = real + diff;
-   */
-  target = gst_system_clock_get_internal_time (clock) + diff;
-
-  GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p"
-      " target %" GST_TIME_FORMAT
-      " entry %" GST_TIME_FORMAT
-      " now %" GST_TIME_FORMAT
-      " real %" GST_TIME_FORMAT
-      " diff (entry-now) %" G_GINT64_FORMAT,
-      entry,
-      GST_TIME_ARGS (target),
-      GST_TIME_ARGS (entryt), GST_TIME_ARGS (now), GST_TIME_ARGS (real), diff);
+  while (TRUE) {
+    /* need to call the overridden method */
+    real = GST_CLOCK_GET_CLASS (clock)->get_internal_time (clock);
+    now = gst_clock_adjust_unlocked (clock, real);
+
+    /* the amount of time we need to wait */
+    diff = GST_CLOCK_DIFF (now, entryt);
+
+    GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p"
+        " entry %" GST_TIME_FORMAT
+        " now %" GST_TIME_FORMAT
+        " real %" GST_TIME_FORMAT
+        " diff (entry-now) %" G_GINT64_FORMAT,
+        entry, GST_TIME_ARGS (entryt), GST_TIME_ARGS (now),
+        GST_TIME_ARGS (real), diff);
+
+    /* only store the jitter when start synchronising, retries may not update
+     * this value anymore. NOTE, negative value for jitter means "in time" */
+    if (jitter && first)
+      *jitter = -diff;
+
+    /* negative diff means we are late, if this is the first time we try
+     * (first == TRUE), we are EARLY, else we are ok and stop. */
+    if (diff <= 0) {
+      GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p does not need sync", entry);
+      entry->status = (first ? GST_CLOCK_EARLY : GST_CLOCK_OK);
+      break;
+    }
+    first = FALSE;
 
-  if (diff > 0) {
-    GTimeVal tv;
+    /* small waits, FIXME could even be implemented with nanosleep...  */
+    if (diff < GST_MSECOND) {
+      g_usleep (GST_TIME_AS_USECONDS (diff));
+      entry->status = GST_CLOCK_OK;
+      break;
+    }
 
-#ifdef WAIT_DEBUGGING
-    GstClockTime result, final;
-#endif
+    /* register entry and start to wait */
+    if (!gst_system_clock_id_wait_async_unlocked (clock, entry)) {
+      return GST_CLOCK_ERROR;
+    }
+    GST_CLOCK_WAIT (clock);
 
-    GST_TIME_TO_TIMEVAL (target, tv);
+    /* entry stopped, break the loop */
+    if (entry->status != GST_CLOCK_BUSY)
+      break;
 
-    while (TRUE) {
-      /* now wait on the entry, it either times out or the cond is signaled. */
-      if (!GST_CLOCK_TIMED_WAIT (clock, &tv)) {
-        /* timeout, this is fine, we can report success now */
-        GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p unlocked after timeout", entry);
-        entry->status = GST_CLOCK_OK;
+    GST_CAT_DEBUG (GST_CAT_CLOCK, "continue waiting for entry %p", entry);
+  }
 
+  /* we stopped synchronisation for some reason, do some debugging */
+  switch (entry->status) {
+    case GST_CLOCK_OK:
+    {
 #ifdef WAIT_DEBUGGING
-        real = GST_CLOCK_GET_CLASS (clock)->get_internal_time (clock);
-        result = gst_clock_adjust_unlocked (clock, real);
-        final = gst_system_clock_get_internal_time (clock);
-        GST_CAT_DEBUG (GST_CAT_CLOCK, "Waited for %" G_GINT64_FORMAT
-            " got %" G_GINT64_FORMAT " diff %" G_GINT64_FORMAT
-            " %g target-offset %" G_GINT64_FORMAT " %g", entryt, result,
-            result - entryt,
-            (double) (GstClockTimeDiff) (result - entryt) / GST_SECOND,
-            (final - target),
-            ((double) (GstClockTimeDiff) (final - target)) / GST_SECOND);
+      GstClockTime result;
 #endif
-        break;
-      } else {
-        /* the waiting is interrupted because the GCond was signaled. This can
-         * be because this or some other entry was unscheduled. */
-        GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p unlocked with signal", entry);
-        /* if the entry is unscheduled, we can stop waiting for it, else we
-         * continue our while loop. */
-        if (entry->status == GST_CLOCK_UNSCHEDULED)
-          break;
-        /* else restart if we must */
-        if (!restart)
-          break;
-
-        /* this can happen if the entry got unlocked because of an async entry
-         * was added to the head of the async queue. */
-        GST_CAT_DEBUG (GST_CAT_CLOCK, "continue waiting for entry %p", entry);
-      }
+      /* timeout, this is fine, we can report success now */
+      GST_CAT_DEBUG (GST_CAT_CLOCK,
+         "entry %p unlocked after successful timeout", entry);
+
+#ifdef WAIT_DEBUGGING
+      real = GST_CLOCK_GET_CLASS (clock)->get_internal_time (clock);
+      result = gst_clock_adjust_unlocked (clock, real);
+      GST_CAT_DEBUG (GST_CAT_CLOCK, "Waited for %" G_GINT64_FORMAT
+          " got %" G_GINT64_FORMAT " diff %" G_GINT64_FORMAT
+          " %g", entryt, result, result - entryt,
+          (double) (GstClockTimeDiff) (result - entryt) / GST_SECOND);
+#endif
+      break;
     }
-  } else if (diff == 0) {
-    entry->status = GST_CLOCK_OK;
-  } else {
-    entry->status = GST_CLOCK_EARLY;
+    case GST_CLOCK_UNSCHEDULED:
+      GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p unscheduled", entry);
+      break;
+    case GST_CLOCK_EARLY:
+      GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p was early", entry);
+      break;
+    default:
+      GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p unlocked for unknown reason",
+          entry);
+      break;
   }
+
   return entry->status;
 }
 
@@ -450,7 +661,10 @@
   GstClockReturn ret;
 
   GST_OBJECT_LOCK (clock);
-  ret = gst_system_clock_id_wait_jitter_unlocked (clock, entry, jitter, TRUE);
+  if (entry->status == GST_CLOCK_UNSCHEDULED)
+    ret = GST_CLOCK_UNSCHEDULED;
+  else
+    ret = gst_system_clock_id_wait_jitter_unlocked (clock, entry, jitter);
   GST_OBJECT_UNLOCK (clock);
 
   return ret;
@@ -484,26 +698,19 @@
   return FALSE;
 }
 
-/* Add an entry to the list of pending async waits. The entry is inserted
- * in sorted order. If we inserted the entry at the head of the list, we
- * need to signal the thread as it might either be waiting on it or waiting
- * for a new entry.
- *
- * MT safe.
- */
-static GstClockReturn
-gst_system_clock_id_wait_async (GstClock * clock, GstClockEntry * entry)
+static gboolean
+gst_system_clock_id_wait_async_unlocked (GstClock * clock,
+    GstClockEntry * entry)
 {
-  GST_CAT_DEBUG (GST_CAT_CLOCK, "adding async entry %p", entry);
-
-  GST_OBJECT_LOCK (clock);
+  GstSystemClock *sysclock = GST_SYSTEM_CLOCK_CAST (clock);
 
   /* Start the clock async thread if needed */
-  if (!gst_system_clock_start_async (GST_SYSTEM_CLOCK (clock)))
+  if (!gst_system_clock_start_async (sysclock))
     goto thread_error;
 
   /* need to take a ref */
   gst_clock_id_ref ((GstClockID) entry);
+
   /* insert the entry in sorted order */
   clock->entries = g_list_insert_sorted (clock->entries, entry,
       gst_clock_id_compare_func);
@@ -518,15 +725,40 @@
      * have not timed out will have their status set to BUSY and should continue
      * to wait. In the case of the async ones, the new head entry should be
      * taken and waited for. */
-    GST_CLOCK_BROADCAST (clock);
+    g_main_context_wakeup (
+        g_main_loop_get_context (sysclock->ABI.abidata.loop));
   }
-  GST_OBJECT_UNLOCK (clock);
 
-  return GST_CLOCK_OK;
+  return TRUE;
 
 thread_error:
   /* Could not start the async clock thread */
-  return GST_CLOCK_ERROR;
+  return FALSE;
+}
+
+/* Add an entry to the list of pending async waits. The entry is inserted
+ * in sorted order. If we inserted the entry at the head of the list, we
+ * need to signal the thread as it might either be waiting on it or waiting
+ * for a new entry.
+ *
+ * MT safe.
+ */
+static GstClockReturn
+gst_system_clock_id_wait_async (GstClock * clock, GstClockEntry * entry)
+{
+  GstClockReturn ret;
+
+  GST_CAT_DEBUG (GST_CAT_CLOCK, "adding entry %p", entry);
+
+  GST_OBJECT_LOCK (clock);
+  if (gst_system_clock_id_wait_async_unlocked (clock, entry)) {
+    ret = GST_CLOCK_OK;
+  } else {
+    ret = GST_CLOCK_ERROR;
+  }
+  GST_OBJECT_UNLOCK (clock);
+
+  return ret;
 }
 
 /* unschedule an entry. This will set the state of the entry to GST_CLOCK_UNSCHEDULED
@@ -539,6 +771,8 @@
 static void
 gst_system_clock_id_unschedule (GstClock * clock, GstClockEntry * entry)
 {
+  GstSystemClock *sysclock = GST_SYSTEM_CLOCK_CAST (clock);
+
   GST_CAT_DEBUG (GST_CAT_CLOCK, "unscheduling entry %p", entry);
 
   GST_OBJECT_LOCK (clock);
@@ -546,7 +780,11 @@
    * not timeout will be woken up but immediatly go to sleep again because their
    * status would still be busy. */
   entry->status = GST_CLOCK_UNSCHEDULED;
-  GST_CAT_DEBUG (GST_CAT_CLOCK, "sending signal");
-  GST_CLOCK_BROADCAST (clock);
+  if (clock->entries == NULL || clock->entries->data == entry) {
+    GST_CAT_DEBUG (GST_CAT_CLOCK, "sending signal");
+    g_main_context_wakeup (
+        g_main_loop_get_context (sysclock->ABI.abidata.loop));
+    GST_CLOCK_BROADCAST (clock);
+  }
   GST_OBJECT_UNLOCK (clock);
 }
Index: gst/gstsystemclock.h
===================================================================
RCS file: /cvs/gstreamer/gstreamer/gst/gstsystemclock.h,v
retrieving revision 1.15
diff -u -r1.15 gstsystemclock.h
--- gst/gstsystemclock.h	28 Oct 2005 18:14:24 -0000	1.15
+++ gst/gstsystemclock.h	14 Nov 2007 15:27:59 -0000
@@ -35,12 +35,24 @@
 #define GST_SYSTEM_CLOCK_CLASS(klass) 		(G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_SYSTEM_CLOCK, GstSystemClockClass))
 #define GST_IS_SYSTEM_CLOCK_CLASS(klass) 	(G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_SYSTEM_CLOCK))
 #define GST_SYSTEM_CLOCK_GET_CLASS(obj) 	(G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_SYSTEM_CLOCK, GstSystemClockClass))
+#define GST_SYSTEM_CLOCK_CAST(obj)       ((GstSystemClock *)(obj))
 
 
 typedef struct _GstSystemClock GstSystemClock;
 typedef struct _GstSystemClockClass GstSystemClockClass;
 
 /**
+ * GstClockType:
+ * @GST_CLOCK_TYPE_REALTIME       : time since Epoch
+ * @GST_CLOCK_TYPE_MONOTONIC      : monotonic time since some unspecified
+ *                                  starting point
+ */
+typedef enum {
+  GST_CLOCK_TYPE_REALTIME       = 0,
+  GST_CLOCK_TYPE_MONOTONIC      = 1,
+} GstClockType;
+
+/**
  * GstSystemClock:
  * @clock: The parent clock
  *
@@ -53,7 +65,14 @@
   GThread	*thread;	/* thread for async notify */
   gboolean 	 stopping;
 
-  gpointer _gst_reserved[GST_PADDING];
+  /* ABI added */
+  union {
+    struct {
+      GstClockType   clock_type;
+      GMainLoop     *loop;       /* context for waiting */
+    } abidata;
+    gpointer _gst_reserved[GST_PADDING-0];
+  } ABI;
 };
 
 struct _GstSystemClockClass {
