diff --git a/gnl/gnlghostpad.c b/gnl/gnlghostpad.c
index 70a1044..d30167a 100644
--- a/gnl/gnlghostpad.c
+++ b/gnl/gnlghostpad.c
@@ -35,8 +35,11 @@ struct _GnlPadPrivate
   GstPadDirection dir;
   GstPadEventFunction eventfunc;
   GstPadQueryFunction queryfunc;
+  GThread *loop_thread;
 };
 
+static void *loop_seek_thread (void *param);
+
 static GstEvent *
 translate_incoming_seek (GnlObject * object, GstEvent * event)
 {
@@ -251,10 +254,15 @@ translate_outgoing_new_segment (GnlObject * object, GstEvent * event)
   if (G_UNLIKELY (nstream > G_MAXINT64))
     GST_WARNING_OBJECT (object, "Return value too big...");
 
+  if (GNL_OBJECT_LOOP (object)) {
+    stop = start + GNL_OBJECT_MEDIA_DURATION (object);
+  }
+
   GST_DEBUG_OBJECT (object,
       "Sending NEWSEGMENT %" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT " // %"
       GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (stop),
       GST_TIME_ARGS (nstream));
+
   event2 =
       gst_event_new_new_segment (update, rate, format, start, stop,
       (gint64) nstream);
@@ -296,10 +304,15 @@ translate_incoming_new_segment (GnlObject * object, GstEvent * event)
   if (G_UNLIKELY (nstream > G_MAXINT64))
     GST_WARNING_OBJECT (object, "Return value too big...");
 
+  if (GNL_OBJECT_LOOP (object)) {
+    stop = start + GNL_OBJECT_MEDIA_DURATION (object);
+  }
+
   GST_DEBUG_OBJECT (object,
       "Sending NEWSEGMENT %" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT " // %"
       GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (stop),
       GST_TIME_ARGS (nstream));
+
   event2 =
       gst_event_new_new_segment (update, rate, format, start, stop,
       (gint64) nstream);
@@ -329,6 +342,32 @@ internalpad_event_function (GstPad * internal, GstEvent * event)
         case GST_EVENT_NEWSEGMENT:
           event = translate_outgoing_new_segment (object, event);
           break;
+        case GST_EVENT_EOS:
+        {
+          if (GNL_OBJECT_LOOP (object)) {
+            gboolean keepit;
+            GST_LOG_OBJECT (object, "Received EOS");
+            object->loop_progress += GNL_OBJECT_MEDIA_DURATION (object);
+            keepit = GNL_OBJECT_DURATION (object) > 0
+                && GNL_OBJECT_LOOP_PROGRESS (object) >=
+                GNL_OBJECT_DURATION (object);
+            if (!keepit) {
+              GstEvent *event = gst_event_new_seek (1.0, GST_FORMAT_TIME,
+                  GST_SEEK_FLAG_ACCURATE,
+                  GST_SEEK_TYPE_SET, GNL_OBJECT_MEDIA_START (object),
+                  GST_SEEK_TYPE_SET,
+                  GNL_OBJECT_MEDIA_STOP (object));
+              g_mutex_lock (GNL_OBJECT_LOOP_MUTEX (object));
+              GNL_OBJECT_LOOP_SEEK_EVENT (object) = event;
+              GNL_OBJECT_LOOP_STATE (object) = GNL_OBJECT_LOOP_STATE_EVENT;
+              g_mutex_unlock (GNL_OBJECT_LOOP_MUTEX (object));
+              GST_LOG_OBJECT (object, "broadcasting loop seek event");
+              g_cond_broadcast (GNL_OBJECT_LOOP_CONDITION (object));
+              return FALSE;
+            }
+          }
+        }
+          break;
         default:
           break;
       }
@@ -728,6 +767,22 @@ gnl_object_remove_ghost_pad (GnlObject * object, GstPad * ghost)
   GST_DEBUG_OBJECT (object, "ghostpad %s:%s", GST_DEBUG_PAD_NAME (ghost));
 
   priv = gst_pad_get_element_private (ghost);
+
+  if (priv->loop_thread != 0) {
+    GST_LOG_OBJECT (object, "shutting down the seek thread");
+    GNL_OBJECT_LOOP_STATE (object) = GNL_OBJECT_LOOP_STATE_DONE;
+    g_cond_broadcast (GNL_OBJECT_LOOP_CONDITION (object));
+    g_thread_join (priv->loop_thread);
+  }
+
+  if (GNL_OBJECT_LOOP_SEEK_EVENT (object)) {
+    gst_event_unref (GNL_OBJECT_LOOP_SEEK_EVENT (object));
+    GNL_OBJECT_LOOP_SEEK_EVENT (object) = NULL;
+  }
+
+  g_cond_free (GNL_OBJECT_LOOP_CONDITION (object));
+  g_mutex_free (GNL_OBJECT_LOOP_MUTEX (object));
+
   gst_ghost_pad_set_target (GST_GHOST_PAD (ghost), NULL);
   gst_element_remove_pad (GST_ELEMENT (object), ghost);
   if (priv)
@@ -766,9 +821,51 @@ gnl_object_ghost_pad_set_target (GnlObject * object, GstPad * ghost,
   if (!GST_OBJECT_IS_FLOATING (ghost))
     control_internal_pad (ghost, object);
 
+  if (GNL_OBJECT_LOOP (object)) {
+    GST_LOG_OBJECT (object, "Looping, setting up event probe and thread");
+    priv->loop_thread = g_thread_create (loop_seek_thread, ghost, TRUE, NULL);
+  }
+
   return TRUE;
 }
 
+static void *
+loop_seek_thread (void *param)
+{
+  GstPad *ghost = (GstPad *) param;
+  GnlPadPrivate *priv = gst_pad_get_element_private (ghost);
+  GnlObject *object = priv->object;
+  GstEvent *event;
+  GST_DEBUG_OBJECT (object, "starting loop_seek_thread");
+  g_mutex_lock (GNL_OBJECT_LOOP_MUTEX (object));
+  while (GNL_OBJECT_LOOP_STATE (object) != GNL_OBJECT_LOOP_STATE_DONE) {
+    GST_LOG_OBJECT (object, "loop_seek_thread waking up");
+    if (GNL_OBJECT_LOOP_STATE (object) == GNL_OBJECT_LOOP_STATE_EVENT) {
+      GstPad *ghostedpad = gst_ghost_pad_get_target (GST_GHOST_PAD (ghost));
+
+      event = GNL_OBJECT_LOOP_SEEK_EVENT (object);
+      GNL_OBJECT_LOOP_SEEK_EVENT (object) = NULL;
+      GNL_OBJECT_LOOP_STATE (object) = GNL_OBJECT_LOOP_STATE_NONE;
+      g_mutex_unlock (GNL_OBJECT_LOOP_MUTEX (object));
+
+      GST_LOG_OBJECT (object, "We have a loop event");
+      if (!(gst_pad_send_event (ghostedpad, event))) {
+        GST_ELEMENT_ERROR (object, RESOURCE, SEEK, (NULL),
+            ("Sending seek in loop_seek_thread failed"));
+      }
+      g_mutex_lock (GNL_OBJECT_LOOP_MUTEX (object));
+    }
+    GST_LOG_OBJECT (object, "Waiting for condition variable");
+    g_cond_wait (GNL_OBJECT_LOOP_CONDITION (object),
+        GNL_OBJECT_LOOP_MUTEX (object));
+  }
+
+  g_mutex_unlock (GNL_OBJECT_LOOP_MUTEX (object));
+  GST_DEBUG_OBJECT (object, "loop_seek_thread ending");
+
+  return NULL;
+}
+
 void
 gnl_init_ghostpad_category (void)
 {
diff --git a/gnl/gnlobject.c b/gnl/gnlobject.c
index 7c58314..a7983d1 100644
--- a/gnl/gnlobject.c
+++ b/gnl/gnlobject.c
@@ -57,7 +57,8 @@ enum
   ARG_PRIORITY,
   ARG_ACTIVE,
   ARG_CAPS,
-  ARG_EXPANDABLE
+  ARG_EXPANDABLE,
+  ARG_LOOP
 };
 
 static void gnl_object_dispose (GObject * object);
@@ -247,6 +248,17 @@ gnl_object_class_init (GnlObjectClass * klass)
       g_param_spec_boolean ("expandable", "Expandable",
           "Expand to the full duration of the container composition", FALSE,
           G_PARAM_READWRITE));
+
+  /**
+   * GnlObject:loop
+   *
+   * If TRUE, this object is looping.  If object is looping, rate will
+   * be 1, one loop will take media-duration nanoseconds and will loop
+   * for duration.
+   */
+  g_object_class_install_property (gobject_class, ARG_LOOP,
+      g_param_spec_boolean ("loop", "Loop",
+          "Set to TRUE to loop clip", FALSE, G_PARAM_READWRITE));
 }
 
 static void
@@ -264,12 +276,18 @@ gnl_object_init (GnlObject * object, GnlObjectClass * klass)
   object->rate_1 = TRUE;
   object->priority = 0;
   object->active = TRUE;
+  object->loop = FALSE;
 
   object->caps = gst_caps_new_any ();
 
   object->segment_rate = 1.0;
   object->segment_start = -1;
   object->segment_stop = -1;
+
+  object->loop_state = GNL_OBJECT_LOOP_STATE_NONE;
+  object->loop_progress = 0;
+  object->loop_condition = g_cond_new ();
+  object->loop_mutex = g_mutex_new ();
 }
 
 static void
@@ -471,8 +489,12 @@ update_values (GnlObject * object)
   }
 
   /* check if media start/duration has changed */
-  if ((object->media_start != GST_CLOCK_TIME_NONE)
-      && ((object->media_start + object->media_duration) != object->media_stop)) {
+  if (((object->media_start != GST_CLOCK_TIME_NONE)
+          && ((object->media_start + object->media_duration) !=
+              object->media_stop))
+      || (object->loop
+          && ((object->media_start + object->media_duration) !=
+              object->media_stop))) {
     object->media_stop = object->media_start + object->media_duration;
     GST_LOG_OBJECT (object,
         "Updated media_stop value : %" GST_TIME_FORMAT
@@ -484,15 +506,26 @@ update_values (GnlObject * object)
   }
 
   /* check if rate has changed */
-  if ((object->media_duration != GST_CLOCK_TIME_NONE)
-      && (object->duration)
-      && (object->media_duration)
-      && (((gdouble) object->media_duration / (gdouble) object->duration) !=
-          object->rate)) {
-    object->rate =
-        (gdouble) object->media_duration / (gdouble) object->duration;
+  if (((object->media_duration != GST_CLOCK_TIME_NONE)
+          && (object->duration)
+          && (object->media_duration)
+          && (((gdouble) object->media_duration / (gdouble) object->duration) !=
+              object->rate))
+      || (object->loop && object->rate != 1)) {
+    if (!object->loop) {
+      object->rate =
+          (gdouble) object->media_duration / (gdouble) object->duration;
+    } else {
+      object->rate = 1;
+    }
+
     /* update the speedup rate_1 variable */
-    object->rate_1 = (object->media_duration == object->duration);
+    if (!object->loop) {
+      object->rate_1 = (object->media_duration == object->duration);
+    } else {
+      object->rate_1 = TRUE;
+    }
+
     GST_LOG_OBJECT (object,
         "Updated rate : %f [mduration:%" GST_TIME_FORMAT ", duration:%"
         GST_TIME_FORMAT "] rate_1:%d", object->rate,
@@ -541,6 +574,10 @@ gnl_object_set_property (GObject * object, guint prop_id,
       else
         GST_OBJECT_FLAG_UNSET (gnlobject, GNL_OBJECT_EXPANDABLE);
       break;
+    case ARG_LOOP:
+      gnlobject->loop = g_value_get_boolean (value);
+      update_values (gnlobject);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -587,6 +624,9 @@ gnl_object_get_property (GObject * object, guint prop_id,
     case ARG_EXPANDABLE:
       g_value_set_boolean (value, GNL_OBJECT_IS_EXPANDABLE (object));
       break;
+    case ARG_LOOP:
+      g_value_set_boolean (value, gnlobject->loop);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
diff --git a/gnl/gnlobject.h b/gnl/gnlobject.h
index 568b973..23fa9ae 100644
--- a/gnl/gnlobject.h
+++ b/gnl/gnlobject.h
@@ -60,6 +60,12 @@ typedef enum
   GNL_OBJECT_LAST_FLAG = (GST_BIN_FLAG_LAST << 16)
 } GnlObjectFlags;
 
+typedef enum
+{
+  GNL_OBJECT_LOOP_STATE_NONE,
+  GNL_OBJECT_LOOP_STATE_DONE,
+  GNL_OBJECT_LOOP_STATE_EVENT
+} GnlObjectLoopState;
 
 #define GNL_OBJECT_IS_SOURCE(obj) \
   (GST_OBJECT_FLAG_IS_SET(obj, GNL_OBJECT_SOURCE))
@@ -76,6 +82,12 @@ typedef enum
 #define GNL_OBJECT_MEDIA_STOP(obj) (GNL_OBJECT_CAST (obj)->media_stop)
 #define GNL_OBJECT_MEDIA_DURATION(obj) (GNL_OBJECT_CAST (obj)->media_duration)
 #define GNL_OBJECT_PRIORITY(obj) (GNL_OBJECT_CAST (obj)->priority)
+#define GNL_OBJECT_LOOP(obj) (GNL_OBJECT_CAST(obj)->loop)
+#define GNL_OBJECT_LOOP_PROGRESS(obj) (GNL_OBJECT_CAST(obj)->loop_progress)
+#define GNL_OBJECT_LOOP_MUTEX(obj) (GNL_OBJECT_CAST(obj)->loop_mutex)
+#define GNL_OBJECT_LOOP_CONDITION(obj) (GNL_OBJECT_CAST(obj)->loop_condition)
+#define GNL_OBJECT_LOOP_STATE(obj) (GNL_OBJECT_CAST(obj)->loop_state)
+#define GNL_OBJECT_LOOP_SEEK_EVENT(obj) (GNL_OBJECT_CAST(obj)->loop_seek_event)
 
 struct _GnlObject
 {
@@ -99,6 +111,26 @@ struct _GnlObject
   /* TRUE if rate == 1.0 */
   gboolean rate_1;
 
+  /* true if object is looping */
+  gboolean loop;
+
+  /* signals that an event is ready */
+  GCond *loop_condition;
+
+  /* paired with loop_condition */
+  GMutex *loop_mutex;
+
+  /* GNL_OBJECT_LOOP_STATE_EVENT if there is an event ready for seeking for looping 
+   * GNL_OBJECT_LOOP_STATE_DONE if we are stopping the looping thread
+  */
+  GnlObjectLoopState loop_state;
+
+  /* The event used for seeking while looping */
+  GstEvent *loop_seek_event;
+
+  /* amount of time we have currently looped.  When greater than duration, send EOS */
+  GstClockTime loop_progress;
+
   /* priority in parent */
   guint32 priority;
 
