static gboolean
gst_adaptive_demux_update_test_clock (gpointer user_data)
{
  GstAdaptiveDemuxTestEnginePrivate *priv =
      (GstAdaptiveDemuxTestEnginePrivate *) user_data;
  GstClockID id;
  GstClockTime next_entry;
  GstTestClock *clock = GST_TEST_CLOCK (priv->engine.clock);

  fail_unless (clock != NULL);
  next_entry = gst_test_clock_get_next_entry_time (clock);
  if (next_entry != GST_CLOCK_TIME_NONE) {
    /* tests that do not want the manifest to update will set the update period
     * to a big value, eg 500s. The manifest update task will register an alarm
     * for that value.
     * We do not want the clock to jump to that. If it does, the manifest update
     * task will keep scheduling and use all the cpu power, starving the other
     * threads.
     * Usually the test require the clock to update with approx 3s, so we will
     * allow only updates smaller than 100s
     */
    GstClockTime curr_time = gst_clock_get_time (GST_CLOCK (clock));
    if (next_entry - curr_time < 100 * GST_SECOND) {
      gst_test_clock_set_time (clock, next_entry);
      id = gst_test_clock_process_next_clock_id (clock);
      fail_unless (id != NULL);
      gst_clock_id_unref (id);
    }
  }
  return TRUE;
}
static GstClockReturn
gst_test_clock_wait_async (GstClock * clock, GstClockEntry * entry)
{
  GstTestClock *test_clock = GST_TEST_CLOCK (clock);

  GST_OBJECT_LOCK (test_clock);

  if (GST_CLOCK_ENTRY_STATUS (entry) == GST_CLOCK_UNSCHEDULED)
    goto was_unscheduled;

  GST_CAT_DEBUG_OBJECT (GST_CAT_TEST_CLOCK, test_clock,
      "requesting asynchronous clock notification at %" GST_TIME_FORMAT,
      GST_TIME_ARGS (GST_CLOCK_ENTRY_TIME (entry)));

  gst_test_clock_add_entry (test_clock, entry, NULL);

  GST_OBJECT_UNLOCK (test_clock);

  return GST_CLOCK_OK;

  /* ERRORS */
was_unscheduled:
  {
    GST_CAT_DEBUG_OBJECT (GST_CAT_TEST_CLOCK, test_clock,
        "entry was unscheduled");
    GST_OBJECT_UNLOCK (test_clock);
    return GST_CLOCK_UNSCHEDULED;
  }
}
static void
gst_test_clock_constructed (GObject * object)
{
  GstTestClock *test_clock = GST_TEST_CLOCK (object);
  GstTestClockPrivate *priv = GST_TEST_CLOCK_GET_PRIVATE (test_clock);

  priv->internal_time = priv->start_time;

  G_OBJECT_CLASS (parent_class)->constructed (object);
}
static void
gst_test_clock_finalize (GObject * object)
{
  GstTestClock *test_clock = GST_TEST_CLOCK (object);
  GstTestClockPrivate *priv = GST_TEST_CLOCK_GET_PRIVATE (test_clock);

  g_cond_clear (&priv->entry_added_cond);
  g_cond_clear (&priv->entry_processed_cond);

  G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_test_clock_unschedule (GstClock * clock, GstClockEntry * entry)
{
  GstTestClock *test_clock = GST_TEST_CLOCK (clock);

  GST_OBJECT_LOCK (test_clock);

  GST_CAT_DEBUG_OBJECT (GST_CAT_TEST_CLOCK, test_clock,
      "unscheduling requested clock notification at %" GST_TIME_FORMAT,
      GST_TIME_ARGS (GST_CLOCK_ENTRY_TIME (entry)));

  GST_CLOCK_ENTRY_STATUS (entry) = GST_CLOCK_UNSCHEDULED;
  gst_test_clock_remove_entry (test_clock, entry);

  GST_OBJECT_UNLOCK (test_clock);
}
static void
gst_test_clock_get_property (GObject * object, guint property_id,
    GValue * value, GParamSpec * pspec)
{
  GstTestClock *test_clock = GST_TEST_CLOCK (object);
  GstTestClockPrivate *priv = GST_TEST_CLOCK_GET_PRIVATE (test_clock);

  switch (property_id) {
    case PROP_START_TIME:
      g_value_set_uint64 (value, priv->start_time);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
  }
}
static void
gst_test_clock_dispose (GObject * object)
{
  GstTestClock *test_clock = GST_TEST_CLOCK (object);
  GstTestClockPrivate *priv = GST_TEST_CLOCK_GET_PRIVATE (test_clock);

  GST_OBJECT_LOCK (test_clock);

  while (priv->entry_contexts != NULL) {
    GstClockEntryContext *ctx = priv->entry_contexts->data;
    gst_test_clock_remove_entry (test_clock, ctx->clock_entry);
  }

  GST_OBJECT_UNLOCK (test_clock);

  G_OBJECT_CLASS (parent_class)->dispose (object);
}
static GstClockTime
gst_test_clock_get_internal_time (GstClock * clock)
{
  GstTestClock *test_clock = GST_TEST_CLOCK (clock);
  GstTestClockPrivate *priv = GST_TEST_CLOCK_GET_PRIVATE (test_clock);
  GstClockTime result;

  GST_OBJECT_LOCK (test_clock);

  GST_CAT_TRACE_OBJECT (GST_CAT_TEST_CLOCK, test_clock,
      "retrieving test clock time %" GST_TIME_FORMAT,
      GST_TIME_ARGS (priv->internal_time));
  result = priv->internal_time;

  GST_OBJECT_UNLOCK (test_clock);

  return result;
}
static void
gst_test_clock_set_property (GObject * object, guint property_id,
    const GValue * value, GParamSpec * pspec)
{
  GstTestClock *test_clock = GST_TEST_CLOCK (object);
  GstTestClockPrivate *priv = GST_TEST_CLOCK_GET_PRIVATE (test_clock);

  switch (property_id) {
    case PROP_START_TIME:
      priv->start_time = g_value_get_uint64 (value);
      GST_CAT_TRACE_OBJECT (GST_CAT_TEST_CLOCK, test_clock,
          "test clock start time initialized at %" GST_TIME_FORMAT,
          GST_TIME_ARGS (priv->start_time));
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
  }
}
static GstClockReturn
gst_test_clock_wait (GstClock * clock,
    GstClockEntry * entry, GstClockTimeDiff * jitter)
{
  GstTestClock *test_clock = GST_TEST_CLOCK (clock);
  GstTestClockPrivate *priv = GST_TEST_CLOCK_GET_PRIVATE (test_clock);

  GST_OBJECT_LOCK (test_clock);

  GST_CAT_DEBUG_OBJECT (GST_CAT_TEST_CLOCK, test_clock,
      "requesting synchronous clock notification at %" GST_TIME_FORMAT,
      GST_TIME_ARGS (GST_CLOCK_ENTRY_TIME (entry)));

  if (GST_CLOCK_ENTRY_STATUS (entry) == GST_CLOCK_UNSCHEDULED)
    goto was_unscheduled;

  if (gst_test_clock_lookup_entry_context (test_clock, entry) == NULL)
    gst_test_clock_add_entry (test_clock, entry, jitter);

  GST_CLOCK_ENTRY_STATUS (entry) = GST_CLOCK_BUSY;

  while (GST_CLOCK_ENTRY_STATUS (entry) == GST_CLOCK_BUSY)
    g_cond_wait (&priv->entry_processed_cond, GST_OBJECT_GET_LOCK (test_clock));

  GST_OBJECT_UNLOCK (test_clock);

  return GST_CLOCK_ENTRY_STATUS (entry);

  /* ERRORS */
was_unscheduled:
  {
    GST_CAT_DEBUG_OBJECT (GST_CAT_TEST_CLOCK, test_clock,
        "entry was unscheduled");
    GST_OBJECT_UNLOCK (test_clock);
    return GST_CLOCK_UNSCHEDULED;
  }
}