*/
/**
* SECTION:gstptpclock
+ * @title: GstPtpClock
* @short_description: Special clock that synchronizes to a remote time
* provider via PTP (IEEE1588:2008).
* @see_also: #GstClock, #GstNetClientClock, #GstPipeline
* gst_ptp_clock_new() then allows to create a GstClock that provides the PTP
* time from a master clock inside a specific PTP domain. This clock will only
* return valid timestamps once the timestamps in the PTP domain are known. To
- * check this, the GstPtpClock::internal-clock property and the related
- * notify::clock signal can be used. Once the internal clock is not NULL, the
- * PTP domain's time is known. Alternatively you can wait for this with
- * gst_ptp_clock_wait_ready().
- *
+ * check this, you can use gst_clock_wait_for_sync(), the GstClock::synced
+ * signal and gst_clock_is_synced().
*
* To gather statistics about the PTP clock synchronization,
* gst_ptp_statistics_callback_add() can be used. This gives the application
#include "gstptpclock.h"
-#ifdef HAVE_PTP
-
#include "gstptp_private.h"
+#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
+#endif
+#ifdef G_OS_WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#endif
#include <sys/types.h>
+
+#ifdef HAVE_UNISTD_H
#include <unistd.h>
+#elif defined(G_OS_WIN32)
+#include <io.h>
+#endif
#include <gst/base/base.h>
/* IEEE 1588 7.7.3.1 */
#define PTP_ANNOUNCE_RECEIPT_TIMEOUT 4
+/* Use a running average for calculating the mean path delay instead
+ * of just using the last measurement. Enabling this helps in unreliable
+ * networks, like wifi, with often changing delays
+ *
+ * Undef for following IEEE1588-2008 by the letter
+ */
+#define USE_RUNNING_AVERAGE_DELAY 1
+
+/* Filter out any measurements that are above a certain threshold compared to
+ * previous measurements. Enabling this helps filtering out outliers that
+ * happen fairly often in unreliable networks, like wifi.
+ *
+ * Undef for following IEEE1588-2008 by the letter
+ */
+#define USE_MEASUREMENT_FILTERING 1
+
+/* Select the first clock from which we capture a SYNC message as the master
+ * clock of the domain until we are ready to run the best master clock
+ * algorithm. This allows faster syncing but might mean a change of the master
+ * clock in the beginning. As all clocks in a domain are supposed to use the
+ * same time, this shouldn't be much of a problem.
+ *
+ * Undef for following IEEE1588-2008 by the letter
+ */
+#define USE_OPPORTUNISTIC_CLOCK_SELECTION 1
+
+/* Only consider SYNC messages for which we are allowed to send a DELAY_REQ
+ * afterwards. This allows better synchronization in networks with varying
+ * delays, as for every other SYNC message we would have to assume that it's
+ * the average of what we saw before. But that might be completely off
+ */
+#define USE_ONLY_SYNC_WITH_DELAY 1
+
+/* Filter out delay measurements that are too far away from the median of the
+ * last delay measurements, currently those that are more than 2 times as big.
+ * This increases accuracy a lot on wifi.
+ */
+#define USE_MEDIAN_PRE_FILTERING 1
+#define MEDIAN_PRE_FILTERING_WINDOW 9
+
+/* How many updates should be skipped at maximum when using USE_MEASUREMENT_FILTERING */
#define MAX_SKIPPED_UPDATES 5
typedef enum
static GMutex ptp_lock;
static GCond ptp_cond;
static gboolean initted = FALSE;
+#ifdef HAVE_PTP
static gboolean supported = TRUE;
+#else
+static gboolean supported = FALSE;
+#endif
static GPid ptp_helper_pid;
static GThread *ptp_helper_thread;
static GMainContext *main_context;
static GMainLoop *main_loop;
static GIOChannel *stdin_channel, *stdout_channel;
static GRand *delay_req_rand;
+static GstClock *observation_system_clock;
static PtpClockIdentity ptp_clock_id = { GST_PTP_CLOCK_ID_NONE, 0 };
typedef struct
static void
ptp_pending_sync_free (PtpPendingSync * sync)
{
- if (sync->timeout_source)
+ if (sync->timeout_source) {
g_source_destroy (sync->timeout_source);
+ g_source_unref (sync->timeout_source);
+ }
g_free (sync);
}
GstClockTime last_delay_req, min_delay_req_interval;
guint16 last_delay_req_seqnum;
+ GstClockTime last_path_delays[MEDIAN_PRE_FILTERING_WINDOW];
+ gint last_path_delays_missing;
+
GQueue pending_syncs;
GstClock *domain_clock;
sizeof (PtpClockIdentity));
domain->mean_path_delay = 0;
domain->last_delay_req = 0;
+ domain->last_path_delays_missing = 9;
domain->min_delay_req_interval = 0;
domain->sync_interval = 0;
domain->last_ptp_sync_time = 0;
clock_name = g_strdup_printf ("ptp-clock-%u", domain->domain);
domain->domain_clock =
g_object_new (GST_TYPE_SYSTEM_CLOCK, "name", clock_name, NULL);
+ gst_object_ref_sink (domain->domain_clock);
g_free (clock_name);
g_queue_init (&domain->pending_syncs);
+ domain->last_path_delays_missing = 9;
domain_data = g_list_prepend (domain_data, domain);
g_mutex_lock (&domain_clocks_lock);
sizeof (header), &written, &err);
if (status == G_IO_STATUS_ERROR) {
g_warning ("Failed to write to stdout: %s", err->message);
+ g_clear_error (&err);
return G_SOURCE_REMOVE;
} else if (status == G_IO_STATUS_EOF) {
g_message ("EOF on stdout");
return G_SOURCE_REMOVE;
}
- sync->delay_req_send_time_local = gst_util_get_timestamp ();
+ sync->delay_req_send_time_local =
+ gst_clock_get_time (observation_system_clock);
status =
g_io_channel_write_chars (stdout_channel,
(const gchar *) delay_req, 44, &written, &err);
if (status == G_IO_STATUS_ERROR) {
g_warning ("Failed to write to stdout: %s", err->message);
+ g_clear_error (&err);
g_main_loop_quit (main_loop);
return G_SOURCE_REMOVE;
} else if (status == G_IO_STATUS_EOF) {
static gboolean
send_delay_req (PtpDomainData * domain, PtpPendingSync * sync)
{
- GstClockTime now = gst_util_get_timestamp ();
+ GstClockTime now = gst_clock_get_time (observation_system_clock);
guint timeout;
GSource *timeout_source;
update_ptp_time (PtpDomainData * domain, PtpPendingSync * sync)
{
GstClockTime internal_time, external_time, rate_num, rate_den;
+ GstClockTime corrected_ptp_time, corrected_local_time;
+ gdouble r_squared = 0.0;
+ gboolean synced;
+ GstClockTimeDiff discont = 0;
+ GstClockTime estimated_ptp_time = GST_CLOCK_TIME_NONE;
+#ifdef USE_MEASUREMENT_FILTERING
GstClockTime orig_internal_time, orig_external_time, orig_rate_num,
orig_rate_den;
- GstClockTime corrected_ptp_time, corrected_local_time;
+ GstClockTime new_estimated_ptp_time;
GstClockTime max_discont, estimated_ptp_time_min, estimated_ptp_time_max;
- gdouble r_squared;
- gboolean synced, now_synced;
- GstClockTimeDiff discont = 0;
- GstClockTime estimated_ptp_time = GST_CLOCK_TIME_NONE, new_estimated_ptp_time;
+ gboolean now_synced;
+#endif
+
+#ifdef USE_ONLY_SYNC_WITH_DELAY
+ GstClockTime mean_path_delay;
+
+ if (sync->delay_req_send_time_local == GST_CLOCK_TIME_NONE)
+ return;
+ /* IEEE 1588 11.3 */
+ mean_path_delay =
+ (sync->delay_req_recv_time_remote - sync->sync_send_time_remote +
+ sync->sync_recv_time_local - sync->delay_req_send_time_local -
+ (sync->correction_field_sync + sync->correction_field_delay +
+ 32768) / 65536) / 2;
+#endif
+
+ /* IEEE 1588 11.2 */
+ corrected_ptp_time =
+ sync->sync_send_time_remote +
+ (sync->correction_field_sync + 32768) / 65536;
+
+#ifdef USE_ONLY_SYNC_WITH_DELAY
+ corrected_local_time = sync->sync_recv_time_local - mean_path_delay;
+#else
+ corrected_local_time = sync->sync_recv_time_local - domain->mean_path_delay;
+#endif
+
+#ifdef USE_MEASUREMENT_FILTERING
/* We check this here and when updating the mean path delay, because
* we can get here without a delay response too */
if (sync->follow_up_recv_time_local != GST_CLOCK_TIME_NONE
" > 2 * %" GST_TIME_FORMAT, domain->domain,
GST_TIME_ARGS (sync->follow_up_recv_time_local),
GST_TIME_ARGS (domain->mean_path_delay));
+ synced = FALSE;
+ gst_clock_get_calibration (GST_CLOCK_CAST (domain->domain_clock),
+ &internal_time, &external_time, &rate_num, &rate_den);
goto out;
}
-
- /* IEEE 1588 11.2 */
- corrected_ptp_time =
- sync->sync_send_time_remote +
- (sync->correction_field_sync + 32768) / 65536;
- corrected_local_time = sync->sync_recv_time_local - domain->mean_path_delay;
+#endif
/* Set an initial local-remote relation */
if (domain->last_ptp_time == 0)
gst_clock_set_calibration (domain->domain_clock, corrected_local_time,
corrected_ptp_time, 1, 1);
+#ifdef USE_MEASUREMENT_FILTERING
/* Check if the corrected PTP time is +/- 3/4 RTT around what we would
* estimate with our present knowledge about the clock
*/
domain->last_local_time = corrected_local_time;
}
-out:
+#else
+ GST_DEBUG ("Adding observation for domain %u: %" GST_TIME_FORMAT " - %"
+ GST_TIME_FORMAT, domain->domain,
+ GST_TIME_ARGS (corrected_ptp_time), GST_TIME_ARGS (corrected_local_time));
+
+ gst_clock_get_calibration (GST_CLOCK_CAST (domain->domain_clock),
+ &internal_time, &external_time, &rate_num, &rate_den);
+
+ estimated_ptp_time = corrected_local_time;
+ estimated_ptp_time =
+ gst_clock_adjust_with_calibration (GST_CLOCK_CAST
+ (domain->domain_clock), estimated_ptp_time, internal_time,
+ external_time, rate_num, rate_den);
+
+ gst_clock_add_observation (domain->domain_clock,
+ corrected_local_time, corrected_ptp_time, &r_squared);
+
+ gst_clock_get_calibration (GST_CLOCK_CAST (domain->domain_clock),
+ &internal_time, &external_time, &rate_num, &rate_den);
+
+ synced = TRUE;
+ domain->last_ptp_time = corrected_ptp_time;
+ domain->last_local_time = corrected_local_time;
+#endif
+#ifdef USE_MEASUREMENT_FILTERING
+out:
+#endif
if (g_atomic_int_get (&domain_stats_n_hooks)) {
GstStructure *stats = gst_structure_new (GST_PTP_STATISTICS_TIME_UPDATED,
"domain", G_TYPE_UINT, domain->domain,
}
+#ifdef USE_MEDIAN_PRE_FILTERING
+static gint
+compare_clock_time (const GstClockTime * a, const GstClockTime * b)
+{
+ if (*a < *b)
+ return -1;
+ else if (*a > *b)
+ return 1;
+ return 0;
+}
+#endif
+
static gboolean
update_mean_path_delay (PtpDomainData * domain, PtpPendingSync * sync)
{
- GstClockTime mean_path_delay, delay_req_delay;
+#ifdef USE_MEDIAN_PRE_FILTERING
+ GstClockTime last_path_delays[MEDIAN_PRE_FILTERING_WINDOW];
+ GstClockTime median;
+ gint i;
+#endif
+
+ GstClockTime mean_path_delay, delay_req_delay = 0;
gboolean ret;
/* IEEE 1588 11.3 */
(sync->correction_field_sync + sync->correction_field_delay +
32768) / 65536) / 2;
+#ifdef USE_MEDIAN_PRE_FILTERING
+ for (i = 1; i < MEDIAN_PRE_FILTERING_WINDOW; i++)
+ domain->last_path_delays[i - 1] = domain->last_path_delays[i];
+ domain->last_path_delays[i - 1] = mean_path_delay;
+
+ if (domain->last_path_delays_missing) {
+ domain->last_path_delays_missing--;
+ } else {
+ memcpy (&last_path_delays, &domain->last_path_delays,
+ sizeof (last_path_delays));
+ g_qsort_with_data (&last_path_delays,
+ MEDIAN_PRE_FILTERING_WINDOW, sizeof (GstClockTime),
+ (GCompareDataFunc) compare_clock_time, NULL);
+
+ median = last_path_delays[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 (mean_path_delay > 2 * median) {
+ GST_WARNING ("Path delay for domain %u too big compared to median: %"
+ GST_TIME_FORMAT " > 2 * %" GST_TIME_FORMAT, domain->domain,
+ GST_TIME_ARGS (mean_path_delay), GST_TIME_ARGS (median));
+ ret = FALSE;
+ goto out;
+ }
+ }
+#endif
+
+#ifdef USE_RUNNING_AVERAGE_DELAY
/* 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 */
else
domain->mean_path_delay =
(15 * domain->mean_path_delay + mean_path_delay) / 16;
+#else
+ domain->mean_path_delay = mean_path_delay;
+#endif
+#ifdef USE_MEASUREMENT_FILTERING
if (sync->follow_up_recv_time_local != GST_CLOCK_TIME_NONE &&
domain->mean_path_delay != 0
&& sync->follow_up_recv_time_local >
sync->sync_recv_time_local + 2 * domain->mean_path_delay) {
GST_WARNING ("Sync-follow-up delay for domain %u too big: %" GST_TIME_FORMAT
" > 2 * %" GST_TIME_FORMAT, domain->domain,
- GST_TIME_ARGS (sync->follow_up_recv_time_local),
+ GST_TIME_ARGS (sync->follow_up_recv_time_local -
+ sync->sync_recv_time_local),
GST_TIME_ARGS (domain->mean_path_delay));
ret = FALSE;
goto out;
ret = FALSE;
goto out;
}
+#endif
delay_req_delay =
sync->delay_resp_recv_time_local - sync->delay_req_send_time_local;
+
+#ifdef USE_MEASUREMENT_FILTERING
/* delay_req_delay is a RTT, so 2 times the path delay */
if (delay_req_delay > 4 * domain->mean_path_delay) {
GST_WARNING ("Delay-request-response delay for domain %u too big: %"
ret = FALSE;
goto out;
}
+#endif
ret = TRUE;
GST_DEBUG ("Delay request delay for domain %u: %" GST_TIME_FORMAT,
domain->domain, GST_TIME_ARGS (delay_req_delay));
+#if defined(USE_MEASUREMENT_FILTERING) || defined(USE_MEDIAN_PRE_FILTERING)
out:
+#endif
if (g_atomic_int_get (&domain_stats_n_hooks)) {
GstStructure *stats =
gst_structure_new (GST_PTP_STATISTICS_PATH_DELAY_MEASURED,
if (!domain) {
gchar *clock_name;
+
domain = g_new0 (PtpDomainData, 1);
domain->domain = msg->domain_number;
clock_name = g_strdup_printf ("ptp-clock-%u", domain->domain);
domain->domain_clock =
g_object_new (GST_TYPE_SYSTEM_CLOCK, "name", clock_name, NULL);
+ gst_object_ref_sink (domain->domain_clock);
g_free (clock_name);
g_queue_init (&domain->pending_syncs);
+ domain->last_path_delays_missing = 9;
domain_data = g_list_prepend (domain_data, domain);
g_mutex_lock (&domain_clocks_lock);
&msg->source_port_identity) != 0)
return;
+#ifdef USE_OPPORTUNISTIC_CLOCK_SELECTION
/* Opportunistic selection of master clock */
if (!domain->have_master_clock)
memcpy (&domain->master_clock_identity, &msg->source_port_identity,
sizeof (PtpClockIdentity));
+#else
+ if (!domain->have_master_clock)
+ return;
+#endif
domain->sync_interval = log2_to_clock_time (msg->log_message_interval);
&read, &err);
if (status == G_IO_STATUS_ERROR) {
GST_ERROR ("Failed to read from stdin: %s", err->message);
+ g_clear_error (&err);
g_main_loop_quit (main_loop);
return G_SOURCE_REMOVE;
} else if (status == G_IO_STATUS_EOF) {
status = g_io_channel_read_chars (channel, buffer, header.size, &read, &err);
if (status == G_IO_STATUS_ERROR) {
GST_ERROR ("Failed to read from stdin: %s", err->message);
+ g_clear_error (&err);
g_main_loop_quit (main_loop);
return G_SOURCE_REMOVE;
} else if (status == G_IO_STATUS_EOF) {
switch (header.type) {
case TYPE_EVENT:
case TYPE_GENERAL:{
- GstClockTime receive_time = gst_util_get_timestamp ();
+ GstClockTime receive_time = gst_clock_get_time (observation_system_clock);
PtpMessage msg;
if (parse_ptp_message (&msg, (const guint8 *) buffer, header.size)) {
static gboolean
cleanup_cb (gpointer data)
{
- GstClockTime now = gst_util_get_timestamp ();
+ GstClockTime now = gst_clock_get_time (observation_system_clock);
GList *l, *m, *n;
for (l = domain_data; l; l = l->next) {
/**
* gst_ptp_init:
* @clock_id: PTP clock id of this process' clock or %GST_PTP_CLOCK_ID_NONE
- * @interfaces: (transfer none) (array zero-terminated=1): network interfaces to run the clock on
+ * @interfaces: (transfer none) (array zero-terminated=1) (allow-none): network interfaces to run the clock on
*
* Initialize the GStreamer PTP subsystem and create a PTP ordinary clock in
* slave-only mode for all domains on the given @interfaces with the
* If @clock_id is %GST_PTP_CLOCK_ID_NONE, a clock id is automatically
* generated from the MAC address of the first network interface.
*
- *
* This function is automatically called by gst_ptp_clock_new() with default
* parameters if it wasn't called before.
*
g_io_channel_set_buffered (stdout_channel, FALSE);
delay_req_rand = g_rand_new ();
+ observation_system_clock =
+ g_object_new (GST_TYPE_SYSTEM_CLOCK, "name", "ptp-observation-clock",
+ NULL);
+ gst_object_ref_sink (observation_system_clock);
initted = TRUE;
if (!ret) {
if (ptp_helper_pid) {
+#ifndef G_OS_WIN32
kill (ptp_helper_pid, SIGKILL);
waitpid (ptp_helper_pid, NULL, 0);
+#else
+ TerminateProcess (ptp_helper_pid, 1);
+ WaitForSingleObject (ptp_helper_pid, INFINITE);
+#endif
g_spawn_close_pid (ptp_helper_pid);
}
ptp_helper_pid = 0;
if (delay_req_rand)
g_rand_free (delay_req_rand);
delay_req_rand = NULL;
+
+ if (observation_system_clock)
+ gst_object_unref (observation_system_clock);
+ observation_system_clock = NULL;
}
g_mutex_unlock (&ptp_lock);
g_mutex_lock (&ptp_lock);
if (ptp_helper_pid) {
+#ifndef G_OS_WIN32
kill (ptp_helper_pid, SIGKILL);
waitpid (ptp_helper_pid, NULL, 0);
+#else
+ TerminateProcess (ptp_helper_pid, 1);
+ WaitForSingleObject (ptp_helper_pid, INFINITE);
+#endif
g_spawn_close_pid (ptp_helper_pid);
}
ptp_helper_pid = 0;
if (delay_req_rand)
g_rand_free (delay_req_rand);
delay_req_rand = NULL;
+ if (observation_system_clock)
+ gst_object_unref (observation_system_clock);
+ observation_system_clock = NULL;
for (l = domain_data; l; l = l->next) {
PtpDomainData *domain = l->data;
{
PROP_0,
PROP_DOMAIN,
- PROP_INTERNAL_CLOCK
+ PROP_INTERNAL_CLOCK,
+ PROP_MASTER_CLOCK_ID,
+ PROP_GRANDMASTER_CLOCK_ID
};
#define GST_PTP_CLOCK_GET_PRIVATE(obj) \
"Internal clock", GST_TYPE_CLOCK,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (gobject_class, PROP_MASTER_CLOCK_ID,
+ g_param_spec_uint64 ("master-clock-id", "Master Clock ID",
+ "Master Clock ID", 0, G_MAXUINT64, 0,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class, PROP_GRANDMASTER_CLOCK_ID,
+ g_param_spec_uint64 ("grandmaster-clock-id", "Grand Master Clock ID",
+ "Grand Master Clock ID", 0, G_MAXUINT64, 0,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
clock_class->get_internal_time = gst_ptp_clock_get_internal_time;
}
gst_ptp_clock_ensure_domain_clock (self);
g_value_set_object (value, self->priv->domain_clock);
break;
+ case PROP_MASTER_CLOCK_ID:
+ case PROP_GRANDMASTER_CLOCK_ID:{
+ GList *l;
+
+ g_mutex_lock (&domain_clocks_lock);
+ g_value_set_uint64 (value, 0);
+
+ for (l = domain_clocks; l; l = l->next) {
+ PtpDomainData *clock_data = l->data;
+
+ if (clock_data->domain == self->priv->domain) {
+ if (prop_id == PROP_MASTER_CLOCK_ID)
+ g_value_set_uint64 (value,
+ clock_data->master_clock_identity.clock_identity);
+ else
+ g_value_set_uint64 (value, clock_data->grandmaster_identity);
+ break;
+ }
+ }
+ g_mutex_unlock (&domain_clocks_lock);
+ break;
+ }
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
* If gst_ptp_init() was not called before, this will call gst_ptp_init() with
* default parameters.
*
- *
* This clock only returns valid timestamps after it received the first
* times from the PTP master clock on the network. Once this happens the
- * GstPtpClock::internal-clock property will become non-NULL. You can connect
- * to the notify::internal-clock signal to get notified about this, or
- * alternatively use gst_ptp_clock_wait_ready() to wait for this to happen.
+ * GstPtpClock::internal-clock property will become non-NULL. You can
+ * check this with gst_clock_wait_for_sync(), the GstClock::synced signal and
+ * gst_clock_is_synced().
+ *
+ * Returns: (transfer full): A new #GstClock
*
* Since: 1.6
*/
GstClock *
gst_ptp_clock_new (const gchar * name, guint domain)
{
- g_return_val_if_fail (name != NULL, NULL);
+ GstClock *clock;
+
g_return_val_if_fail (domain <= G_MAXUINT8, NULL);
if (!initted && !gst_ptp_init (GST_PTP_CLOCK_ID_NONE, NULL)) {
return NULL;
}
- return g_object_new (GST_TYPE_PTP_CLOCK, "name", name, "domain", domain,
+ clock = g_object_new (GST_TYPE_PTP_CLOCK, "name", name, "domain", domain,
NULL);
+
+ /* Clear floating flag */
+ gst_object_ref_sink (clock);
+
+ return clock;
}
typedef struct
g_atomic_int_add (&domain_stats_n_hooks, -1);
g_mutex_unlock (&ptp_lock);
}
-
-#else /* HAVE_PTP */
-
-GType
-gst_ptp_clock_get_type (void)
-{
- return G_TYPE_INVALID;
-}
-
-gboolean
-gst_ptp_is_supported (void)
-{
- return FALSE;
-}
-
-gboolean
-gst_ptp_is_initialized (void)
-{
- return FALSE;
-}
-
-gboolean
-gst_ptp_init (guint64 clock_id, gchar ** interfaces)
-{
- return FALSE;
-}
-
-void
-gst_ptp_deinit (void)
-{
-}
-
-GstClock *
-gst_ptp_clock_new (const gchar * name, guint domain)
-{
- return NULL;
-}
-
-gboolean
-gst_ptp_clock_wait_ready (GstPtpClock * self, GstClockTime timeout)
-{
- return FALSE;
-}
-
-gulong
-gst_ptp_statistics_callback_add (GstPtpStatisticsCallback callback,
- gpointer user_data, GDestroyNotify destroy_data)
-{
- return 0;
-}
-
-void
-gst_ptp_statistics_callback_remove (gulong id)
-{
- return;
-}
-
-#endif