static GstClockTime
gst_net_client_clock_get_internal_time (GstClock * clock)
{
  GstNetClientClock *self = GST_NET_CLIENT_CLOCK (clock);

  if (!gst_clock_is_synced (self->priv->internal_clock)) {
    GstClockTime now = gst_clock_get_internal_time (self->priv->internal_clock);
    return gst_clock_adjust_with_calibration (self->priv->internal_clock, now,
        self->priv->internal_base_time, self->priv->base_time, 1, 1);
  }

  return gst_clock_get_time (self->priv->internal_clock);
}
Ejemplo n.º 2
0
/**
 * gst_clock_adjust_unlocked:
 * @clock: a #GstClock to use
 * @internal: a clock time
 *
 * Converts the given @internal clock time to the external time, adjusting for the
 * rate and reference time set with gst_clock_set_calibration() and making sure
 * that the returned time is increasing. This function should be called with the
 * clock's OBJECT_LOCK held and is mainly used by clock subclasses.
 *
 * This function is the reverse of gst_clock_unadjust_unlocked().
 *
 * Returns: the converted time of the clock.
 */
GstClockTime
gst_clock_adjust_unlocked (GstClock * clock, GstClockTime internal)
{
  GstClockTime ret, cinternal, cexternal, cnum, cdenom;
  GstClockPrivate *priv = clock->priv;

  /* get calibration values for readability */
  cinternal = priv->internal_calibration;
  cexternal = priv->external_calibration;
  cnum = priv->rate_numerator;
  cdenom = priv->rate_denominator;

  ret =
      gst_clock_adjust_with_calibration (clock, internal, cinternal, cexternal,
      cnum, cdenom);

  /* make sure the time is increasing */
  priv->last_time = MAX (ret, priv->last_time);

  return priv->last_time;
}
Ejemplo n.º 3
0
void
gst_decklink_video_src_convert_to_external_clock (GstDecklinkVideoSrc * self,
    GstClockTime * timestamp, GstClockTime * duration)
{
  GstClock *clock;

  g_assert (timestamp != NULL);

  if (*timestamp == GST_CLOCK_TIME_NONE)
    return;

  clock = gst_element_get_clock (GST_ELEMENT_CAST (self));
  if (clock && clock != self->input->clock) {
    GstClockTime internal, external, rate_n, rate_d;
    GstClockTimeDiff external_start_time_diff;

    gst_clock_get_calibration (self->input->clock, &internal, &external,
        &rate_n, &rate_d);

    if (rate_n != rate_d && self->internal_base_time != GST_CLOCK_TIME_NONE) {
      GstClockTime internal_timestamp = *timestamp;

      // Convert to the running time corresponding to both clock times
      internal -= self->internal_base_time;
      external -= self->external_base_time;

      // Get the difference in the internal time, note
      // that the capture time is internal time.
      // Then scale this difference and offset it to
      // our external time. Now we have the running time
      // according to our external clock.
      //
      // For the duration we just scale
      *timestamp =
          gst_clock_adjust_with_calibration (NULL, internal_timestamp, internal,
          external, rate_n, rate_d);

      GST_LOG_OBJECT (self,
          "Converted %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT " (external: %"
          GST_TIME_FORMAT " internal %" GST_TIME_FORMAT " rate: %lf)",
          GST_TIME_ARGS (internal_timestamp), GST_TIME_ARGS (*timestamp),
          GST_TIME_ARGS (external), GST_TIME_ARGS (internal),
          ((gdouble) rate_n) / ((gdouble) rate_d));

      if (duration) {
        GstClockTime internal_duration = *duration;

        *duration = gst_util_uint64_scale (internal_duration, rate_d, rate_n);

        GST_LOG_OBJECT (self,
            "Converted duration %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT
            " (external: %" GST_TIME_FORMAT " internal %" GST_TIME_FORMAT
            " rate: %lf)", GST_TIME_ARGS (internal_duration),
            GST_TIME_ARGS (*duration), GST_TIME_ARGS (external),
            GST_TIME_ARGS (internal), ((gdouble) rate_n) / ((gdouble) rate_d));
      }
    } else {
      GST_LOG_OBJECT (self, "No clock conversion needed, relative rate is 1.0");
    }

    // Add the diff between the external time when we
    // went to playing and the external time when the
    // pipeline went to playing. Otherwise we will
    // always start outputting from 0 instead of the
    // current running time.
    external_start_time_diff =
        gst_element_get_base_time (GST_ELEMENT_CAST (self));
    external_start_time_diff =
        self->external_base_time - external_start_time_diff;
    *timestamp += external_start_time_diff;
  } else {
    GST_LOG_OBJECT (self, "No clock conversion needed, same clocks");
  }
}
static void
gst_decklink_audio_src_got_packet (GstElement * element,
    IDeckLinkAudioInputPacket * packet, GstClockTime capture_time,
    GstClockTime packet_time, gboolean no_signal)
{
  GstDecklinkAudioSrc *self = GST_DECKLINK_AUDIO_SRC_CAST (element);
  GstClockTime timestamp;

  GST_LOG_OBJECT (self,
      "Got audio packet at %" GST_TIME_FORMAT " / %" GST_TIME_FORMAT
      ", no signal %d", GST_TIME_ARGS (capture_time),
      GST_TIME_ARGS (packet_time), no_signal);

  g_mutex_lock (&self->input->lock);
  if (self->input->videosrc) {
    GstDecklinkVideoSrc *videosrc =
        GST_DECKLINK_VIDEO_SRC_CAST (gst_object_ref (self->input->videosrc));

    if (videosrc->drop_no_signal_frames && no_signal) {
      g_mutex_unlock (&self->input->lock);
      return;
    }

    if (videosrc->first_time == GST_CLOCK_TIME_NONE)
      videosrc->first_time = packet_time;

    if (videosrc->skip_first_time > 0
        && packet_time - videosrc->first_time < videosrc->skip_first_time) {
      GST_DEBUG_OBJECT (self,
          "Skipping frame as requested: %" GST_TIME_FORMAT " < %"
          GST_TIME_FORMAT, GST_TIME_ARGS (packet_time),
          GST_TIME_ARGS (videosrc->skip_first_time + videosrc->first_time));
      g_mutex_unlock (&self->input->lock);
      return;
    }

    if (videosrc->output_stream_time)
      timestamp = packet_time;
    else
      timestamp = gst_clock_adjust_with_calibration (NULL, packet_time,
          videosrc->current_time_mapping.xbase,
          videosrc->current_time_mapping.b, videosrc->current_time_mapping.num,
          videosrc->current_time_mapping.den);
  } else {
    timestamp = capture_time;
  }
  g_mutex_unlock (&self->input->lock);

  GST_LOG_OBJECT (self, "Converted times to %" GST_TIME_FORMAT,
      GST_TIME_ARGS (timestamp));

  g_mutex_lock (&self->lock);
  if (!self->flushing) {
    CapturePacket *p;

    while (g_queue_get_length (&self->current_packets) >= self->buffer_size) {
      p = (CapturePacket *) g_queue_pop_head (&self->current_packets);
      GST_WARNING_OBJECT (self, "Dropping old packet at %" GST_TIME_FORMAT,
          GST_TIME_ARGS (p->timestamp));
      capture_packet_free (p);
    }

    p = (CapturePacket *) g_malloc0 (sizeof (CapturePacket));
    p->packet = packet;
    p->timestamp = timestamp;
    p->no_signal = no_signal;
    packet->AddRef ();
    g_queue_push_tail (&self->current_packets, p);
    g_cond_signal (&self->cond);
  }
  g_mutex_unlock (&self->lock);
}
static void
gst_net_client_internal_clock_observe_times (GstNetClientInternalClock * self,
    GstClockTime local_1, GstClockTime remote_1, GstClockTime remote_2,
    GstClockTime local_2)
{
  GstClockTime current_timeout = 0;
  GstClockTime local_avg, remote_avg;
  gdouble r_squared;
  GstClock *clock;
  GstClockTime rtt, rtt_limit, min_update_interval;
  /* Use for discont tracking */
  GstClockTime time_before = 0;
  GstClockTime min_guess = 0;
  GstClockTimeDiff time_discont = 0;
  gboolean synched, now_synched;
  GstClockTime internal_time, external_time, rate_num, rate_den;
  GstClockTime orig_internal_time, orig_external_time, orig_rate_num,
      orig_rate_den;
  GstClockTime max_discont;
  GstClockTime last_rtts[MEDIAN_PRE_FILTERING_WINDOW];
  GstClockTime median;
  gint i;

  GST_OBJECT_LOCK (self);
  rtt_limit = self->roundtrip_limit;

  GST_LOG_OBJECT (self,
      "local1 %" G_GUINT64_FORMAT " remote1 %" G_GUINT64_FORMAT " remote2 %"
      G_GUINT64_FORMAT " local2 %" G_GUINT64_FORMAT, local_1, remote_1,
      remote_2, local_2);

  /* If the server told us a poll interval and it's bigger than the
   * one configured via the property, use the server's */
  if (self->last_remote_poll_interval != GST_CLOCK_TIME_NONE &&
      self->last_remote_poll_interval > self->minimum_update_interval)
    min_update_interval = self->last_remote_poll_interval;
  else
    min_update_interval = self->minimum_update_interval;
  GST_OBJECT_UNLOCK (self);

  if (local_2 < local_1) {
    GST_LOG_OBJECT (self, "Dropping observation: receive time %" GST_TIME_FORMAT
        " < send time %" GST_TIME_FORMAT, GST_TIME_ARGS (local_1),
        GST_TIME_ARGS (local_2));
    goto bogus_observation;
  }

  if (remote_2 < remote_1) {
    GST_LOG_OBJECT (self,
        "Dropping observation: remote receive time %" GST_TIME_FORMAT
        " < send time %" GST_TIME_FORMAT, GST_TIME_ARGS (remote_1),
        GST_TIME_ARGS (remote_2));
    goto bogus_observation;
  }

  /* The round trip time is (assuming symmetric path delays)
   * delta = (local_2 - local_1) - (remote_2 - remote_1)
   */

  rtt = GST_CLOCK_DIFF (local_1, local_2) - GST_CLOCK_DIFF (remote_1, remote_2);

  if ((rtt_limit > 0) && (rtt > rtt_limit)) {
    GST_LOG_OBJECT (self,
        "Dropping observation: RTT %" GST_TIME_FORMAT " > limit %"
        GST_TIME_FORMAT, GST_TIME_ARGS (rtt), GST_TIME_ARGS (rtt_limit));
    goto bogus_observation;
  }

  for (i = 1; i < MEDIAN_PRE_FILTERING_WINDOW; i++)
    self->last_rtts[i - 1] = self->last_rtts[i];
  self->last_rtts[i - 1] = rtt;

  if (self->last_rtts_missing) {
    self->last_rtts_missing--;
  } else {
    memcpy (&last_rtts, &self->last_rtts, sizeof (last_rtts));
    g_qsort_with_data (&last_rtts,
        MEDIAN_PRE_FILTERING_WINDOW, sizeof (GstClockTime),
        (GCompareDataFunc) compare_clock_time, NULL);

    median = last_rtts[MEDIAN_PRE_FILTERING_WINDOW / 2];

    /* FIXME: We might want to use something else here, like only allowing
     * things in the interquartile range, or also filtering away delays that
     * are too small compared to the median. This here worked well enough
     * in tests so far.
     */
    if (rtt > 2 * median) {
      GST_LOG_OBJECT (self,
          "Dropping observation, long RTT %" GST_TIME_FORMAT " > 2 * median %"
          GST_TIME_FORMAT, GST_TIME_ARGS (rtt), GST_TIME_ARGS (median));
      goto bogus_observation;
    }
  }

  /* Track an average round trip time, for a bit of smoothing */
  /* Always update before discarding a sample, so genuine changes in
   * the network get picked up, eventually */
  if (self->rtt_avg == GST_CLOCK_TIME_NONE)
    self->rtt_avg = rtt;
  else if (rtt < self->rtt_avg) /* Shorter RTTs carry more weight than longer */
    self->rtt_avg = (3 * self->rtt_avg + rtt) / 4;
  else
    self->rtt_avg = (15 * self->rtt_avg + rtt) / 16;

  if (rtt > 2 * self->rtt_avg) {
    GST_LOG_OBJECT (self,
        "Dropping observation, long RTT %" GST_TIME_FORMAT " > 2 * avg %"
        GST_TIME_FORMAT, GST_TIME_ARGS (rtt), GST_TIME_ARGS (self->rtt_avg));
    goto bogus_observation;
  }

  /* The difference between the local and remote clock (again assuming
   * symmetric path delays):
   *
   * local_1 + delta / 2 - remote_1 = theta
   * or
   * local_2 - delta / 2 - remote_2 = theta
   *
   * which gives after some simple algebraic transformations:
   *
   *         (remote_1 - local_1) + (remote_2 - local_2)
   * theta = -------------------------------------------
   *                              2
   *
   *
   * Thus remote time at local_avg is equal to:
   *
   * local_avg + theta =
   *
   * local_1 + local_2   (remote_1 - local_1) + (remote_2 - local_2)
   * ----------------- + -------------------------------------------
   *         2                                2
   *
   * =
   *
   * remote_1 + remote_2
   * ------------------- = remote_avg
   *          2
   *
   * We use this for our clock estimation, i.e. local_avg at remote clock
   * being the same as remote_avg.
   */

  local_avg = (local_2 + local_1) / 2;
  remote_avg = (remote_2 + remote_1) / 2;

  GST_LOG_OBJECT (self,
      "remoteavg %" G_GUINT64_FORMAT " localavg %" G_GUINT64_FORMAT,
      remote_avg, local_avg);

  clock = GST_CLOCK_CAST (self);

  /* Store what the clock produced as 'now' before this update */
  gst_clock_get_calibration (GST_CLOCK_CAST (self), &orig_internal_time,
      &orig_external_time, &orig_rate_num, &orig_rate_den);
  internal_time = orig_internal_time;
  external_time = orig_external_time;
  rate_num = orig_rate_num;
  rate_den = orig_rate_den;

  min_guess =
      gst_clock_adjust_with_calibration (GST_CLOCK_CAST (self), local_1,
      internal_time, external_time, rate_num, rate_den);
  time_before =
      gst_clock_adjust_with_calibration (GST_CLOCK_CAST (self), local_2,
      internal_time, external_time, rate_num, rate_den);

  /* Maximum discontinuity, when we're synched with the master. Could make this a property,
   * but this value seems to work fine */
  max_discont = self->rtt_avg / 4;

  /* If the remote observation was within a max_discont window around our min/max estimates, we're synched */
  synched =
      (GST_CLOCK_DIFF (remote_avg, min_guess) < (GstClockTimeDiff) (max_discont)
      && GST_CLOCK_DIFF (time_before,
          remote_avg) < (GstClockTimeDiff) (max_discont));

  if (gst_clock_add_observation_unapplied (GST_CLOCK_CAST (self),
          local_avg, remote_avg, &r_squared, &internal_time, &external_time,
          &rate_num, &rate_den)) {

    /* Now compare the difference (discont) in the clock
     * after this observation */
    time_discont = GST_CLOCK_DIFF (time_before,
        gst_clock_adjust_with_calibration (GST_CLOCK_CAST (self), local_2,
            internal_time, external_time, rate_num, rate_den));

    /* If we were in sync with the remote clock, clamp the allowed
     * discontinuity to within quarter of one RTT. In sync means our send/receive estimates
     * of remote time correctly windowed the actual remote time observation */
    if (synched && ABS (time_discont) > max_discont) {
      GstClockTimeDiff offset;
      GST_DEBUG_OBJECT (clock,
          "Too large a discont, clamping to 1/4 average RTT = %"
          GST_TIME_FORMAT, GST_TIME_ARGS (max_discont));
      if (time_discont > 0) {   /* Too large a forward step - add a -ve offset */
        offset = max_discont - time_discont;
        if (-offset > external_time)
          external_time = 0;
        else
          external_time += offset;
      } else {                  /* Too large a backward step - add a +ve offset */
        offset = -(max_discont + time_discont);
        external_time += offset;
      }

      time_discont += offset;
    }

    /* Check if the new clock params would have made our observation within range */
    now_synched =
        (GST_CLOCK_DIFF (remote_avg,
            gst_clock_adjust_with_calibration (GST_CLOCK_CAST (self),
                local_1, internal_time, external_time, rate_num,
                rate_den)) < (GstClockTimeDiff) (max_discont))
        &&
        (GST_CLOCK_DIFF (gst_clock_adjust_with_calibration
            (GST_CLOCK_CAST (self), local_2, internal_time, external_time,
                rate_num, rate_den),
            remote_avg) < (GstClockTimeDiff) (max_discont));

    /* Only update the clock if we had synch or just gained it */
    if (synched || now_synched || self->skipped_updates > MAX_SKIPPED_UPDATES) {
      gst_clock_set_calibration (GST_CLOCK_CAST (self), internal_time,
          external_time, rate_num, rate_den);
      /* ghetto formula - shorter timeout for bad correlations */
      current_timeout = (1e-3 / (1 - MIN (r_squared, 0.99999))) * GST_SECOND;
      current_timeout =
          MIN (current_timeout, gst_clock_get_timeout (GST_CLOCK_CAST (self)));
      self->skipped_updates = 0;

      /* FIXME: When do we consider the clock absolutely not synced anymore? */
      gst_clock_set_synced (GST_CLOCK (self), TRUE);
    } else {
      /* Restore original calibration vars for the report, we're not changing the clock */
      internal_time = orig_internal_time;
      external_time = orig_external_time;
      rate_num = orig_rate_num;
      rate_den = orig_rate_den;
      time_discont = 0;
      self->skipped_updates++;
    }
  }

  /* Limit the polling to at most one per minimum_update_interval */
  if (rtt < min_update_interval)
    current_timeout = MAX (min_update_interval - rtt, current_timeout);

  GST_OBJECT_LOCK (self);
  if (self->busses) {
    GstStructure *s;
    GstMessage *msg;
    GList *l;

    /* Output a stats message, whether we updated the clock or not */
    s = gst_structure_new ("gst-netclock-statistics",
        "synchronised", G_TYPE_BOOLEAN, synched,
        "rtt", G_TYPE_UINT64, rtt,
        "rtt-average", G_TYPE_UINT64, self->rtt_avg,
        "local", G_TYPE_UINT64, local_avg,
        "remote", G_TYPE_UINT64, remote_avg,
        "discontinuity", G_TYPE_INT64, time_discont,
        "remote-min-estimate", G_TYPE_UINT64, min_guess,
        "remote-max-estimate", G_TYPE_UINT64, time_before,
        "remote-min-error", G_TYPE_INT64, GST_CLOCK_DIFF (remote_avg,
            min_guess), "remote-max-error", G_TYPE_INT64,
        GST_CLOCK_DIFF (remote_avg, time_before), "request-send", G_TYPE_UINT64,
        local_1, "request-receive", G_TYPE_UINT64, local_2, "r-squared",
        G_TYPE_DOUBLE, r_squared, "timeout", G_TYPE_UINT64, current_timeout,
        "internal-time", G_TYPE_UINT64, internal_time, "external-time",
        G_TYPE_UINT64, external_time, "rate-num", G_TYPE_UINT64, rate_num,
        "rate-den", G_TYPE_UINT64, rate_den, "rate", G_TYPE_DOUBLE,
        (gdouble) (rate_num) / rate_den, "local-clock-offset", G_TYPE_INT64,
        GST_CLOCK_DIFF (internal_time, external_time), NULL);
    msg = gst_message_new_element (GST_OBJECT (self), s);

    for (l = self->busses; l; l = l->next)
      gst_bus_post (l->data, gst_message_ref (msg));
    gst_message_unref (msg);
  }
  GST_OBJECT_UNLOCK (self);

  GST_INFO ("next timeout: %" GST_TIME_FORMAT, GST_TIME_ARGS (current_timeout));
  self->timeout_expiration = gst_util_get_timestamp () + current_timeout;

  return;

bogus_observation:
  /* Schedule a new packet again soon */
  self->timeout_expiration = gst_util_get_timestamp () + (GST_SECOND / 4);
  return;
}