2 * Copyright (C) 2015 Sebastian Dröge <sebastian@centricular.com>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
22 * @short_description: Special clock that synchronizes to a remote time
23 * provider via PTP (IEEE1588:2008).
24 * @see_also: #GstClock, #GstNetClientClock, #GstPipeline
26 * GstPtpClock implements a PTP (IEEE1588:2008) ordinary clock in slave-only
27 * mode, that allows a GStreamer pipeline to synchronize to a PTP network
28 * clock in some specific domain.
30 * The PTP subsystem can be initialized with gst_ptp_init(), which then starts
31 * a helper process to do the actual communication via the PTP ports. This is
32 * required as PTP listens on ports < 1024 and thus requires special
33 * privileges. Once this helper process is started, the main process will
34 * synchronize to all PTP domains that are detected on the selected
37 * gst_ptp_clock_new() then allows to create a GstClock that provides the PTP
38 * time from a master clock inside a specific PTP domain. This clock will only
39 * return valid timestamps once the timestamps in the PTP domain are known. To
40 * check this, the GstPtpClock::internal-clock property and the related
41 * notify::clock signal can be used. Once the internal clock is not NULL, the
42 * PTP domain's time is known. Alternatively you can wait for this with
43 * gst_ptp_clock_wait_ready().
46 * To gather statistics about the PTP clock synchronization,
47 * gst_ptp_statistics_callback_add() can be used. This gives the application
48 * the possibility to collect all kinds of statistics from the clock
58 #include "gstptpclock.h"
62 #include "gstptp_private.h"
65 #include <sys/types.h>
68 #include <gst/base/base.h>
70 GST_DEBUG_CATEGORY_STATIC (ptp_debug);
71 #define GST_CAT_DEFAULT (ptp_debug)
73 /* IEEE 1588 7.7.3.1 */
74 #define PTP_ANNOUNCE_RECEIPT_TIMEOUT 4
76 /* Use a running average for calculating the mean path delay instead
77 * of just using the last measurement. Enabling this helps in unreliable
78 * networks, like wifi, with often changing delays
80 * Undef for following IEEE1588-2008 by the letter
82 #define USE_RUNNING_AVERAGE_DELAY 1
84 /* Filter out any measurements that are above a certain threshold compared to
85 * previous measurements. Enabling this helps filtering out outliers that
86 * happen fairly often in unreliable networks, like wifi.
88 * Undef for following IEEE1588-2008 by the letter
90 #define USE_MEASUREMENT_FILTERING 1
92 /* Select the first clock from which we capture a SYNC message as the master
93 * clock of the domain until we are ready to run the best master clock
94 * algorithm. This allows faster syncing but might mean a change of the master
95 * clock in the beginning. As all clocks in a domain are supposed to use the
96 * same time, this shouldn't be much of a problem.
98 * Undef for following IEEE1588-2008 by the letter
100 #define USE_OPPORTUNISTIC_CLOCK_SELECTION 1
102 /* Only consider SYNC messages for which we are allowed to send a DELAY_REQ
103 * afterwards. This allows better synchronization in networks with varying
104 * delays, as for every other SYNC message we would have to assume that it's
105 * the average of what we saw before. But that might be completely off
107 #define USE_ONLY_SYNC_WITH_DELAY 1
109 /* Filter out delay measurements that are too far away from the median of the
110 * last delay measurements, currently those that are more than 2 times as big.
111 * This increases accuracy a lot on wifi.
113 #define USE_MEDIAN_PRE_FILTERING 1
114 #define MEDIAN_PRE_FILTERING_WINDOW 9
116 /* How many updates should be skipped at maximum when using USE_MEASUREMENT_FILTERING */
117 #define MAX_SKIPPED_UPDATES 5
121 PTP_MESSAGE_TYPE_SYNC = 0x0,
122 PTP_MESSAGE_TYPE_DELAY_REQ = 0x1,
123 PTP_MESSAGE_TYPE_PDELAY_REQ = 0x2,
124 PTP_MESSAGE_TYPE_PDELAY_RESP = 0x3,
125 PTP_MESSAGE_TYPE_FOLLOW_UP = 0x8,
126 PTP_MESSAGE_TYPE_DELAY_RESP = 0x9,
127 PTP_MESSAGE_TYPE_PDELAY_RESP_FOLLOW_UP = 0xA,
128 PTP_MESSAGE_TYPE_ANNOUNCE = 0xB,
129 PTP_MESSAGE_TYPE_SIGNALING = 0xC,
130 PTP_MESSAGE_TYPE_MANAGEMENT = 0xD
135 guint64 seconds_field; /* 48 bits valid */
136 guint32 nanoseconds_field;
139 #define PTP_TIMESTAMP_TO_GST_CLOCK_TIME(ptp) (ptp.seconds_field * GST_SECOND + ptp.nanoseconds_field)
140 #define GST_CLOCK_TIME_TO_PTP_TIMESTAMP_SECONDS(gst) (((GstClockTime) gst) / GST_SECOND)
141 #define GST_CLOCK_TIME_TO_PTP_TIMESTAMP_NANOSECONDS(gst) (((GstClockTime) gst) % GST_SECOND)
145 guint64 clock_identity;
150 compare_clock_identity (const PtpClockIdentity * a, const PtpClockIdentity * b)
152 if (a->clock_identity < b->clock_identity)
154 else if (a->clock_identity > b->clock_identity)
157 if (a->port_number < b->port_number)
159 else if (a->port_number > b->port_number)
168 guint8 clock_accuracy;
169 guint16 offset_scaled_log_variance;
174 guint8 transport_specific;
175 PtpMessageType message_type;
176 /* guint8 reserved; */
178 guint16 message_length;
179 guint8 domain_number;
180 /* guint8 reserved; */
182 gint64 correction_field; /* 48.16 fixed point nanoseconds */
183 /* guint32 reserved; */
184 PtpClockIdentity source_port_identity;
186 guint8 control_field;
187 gint8 log_message_interval;
193 PtpTimestamp origin_timestamp;
194 gint16 current_utc_offset;
195 /* guint8 reserved; */
196 guint8 grandmaster_priority_1;
197 PtpClockQuality grandmaster_clock_quality;
198 guint8 grandmaster_priority_2;
199 guint64 grandmaster_identity;
200 guint16 steps_removed;
206 PtpTimestamp origin_timestamp;
211 PtpTimestamp precise_origin_timestamp;
216 PtpTimestamp origin_timestamp;
221 PtpTimestamp receive_timestamp;
222 PtpClockIdentity requesting_port_identity;
228 static GMutex ptp_lock;
229 static GCond ptp_cond;
230 static gboolean initted = FALSE;
231 static gboolean supported = TRUE;
232 static GPid ptp_helper_pid;
233 static GThread *ptp_helper_thread;
234 static GMainContext *main_context;
235 static GMainLoop *main_loop;
236 static GIOChannel *stdin_channel, *stdout_channel;
237 static GRand *delay_req_rand;
238 static PtpClockIdentity ptp_clock_id = { GST_PTP_CLOCK_ID_NONE, 0 };
242 GstClockTime receive_time;
244 PtpClockIdentity master_clock_identity;
246 guint8 grandmaster_priority_1;
247 PtpClockQuality grandmaster_clock_quality;
248 guint8 grandmaster_priority_2;
249 guint64 grandmaster_identity;
250 guint16 steps_removed;
254 } PtpAnnounceMessage;
258 PtpClockIdentity master_clock_identity;
260 GstClockTime announce_interval; /* last interval we received */
261 GQueue announce_messages;
267 PtpClockIdentity master_clock_identity;
270 GstClockTime sync_recv_time_local; /* t2 */
271 GstClockTime sync_send_time_remote; /* t1, might be -1 if FOLLOW_UP pending */
272 GstClockTime follow_up_recv_time_local;
274 GSource *timeout_source;
275 guint16 delay_req_seqnum;
276 GstClockTime delay_req_send_time_local; /* t3, -1 if we wait for FOLLOW_UP */
277 GstClockTime delay_req_recv_time_remote; /* t4, -1 if we wait */
278 GstClockTime delay_resp_recv_time_local;
280 gint64 correction_field_sync; /* sum of the correction fields of SYNC/FOLLOW_UP */
281 gint64 correction_field_delay; /* sum of the correction fields of DELAY_RESP */
285 ptp_pending_sync_free (PtpPendingSync * sync)
287 if (sync->timeout_source)
288 g_source_destroy (sync->timeout_source);
296 GstClockTime last_ptp_time;
297 GstClockTime last_local_time;
298 gint skipped_updates;
300 /* Used for selecting the master/grandmaster */
301 GList *announce_senders;
303 /* Last selected master clock */
304 gboolean have_master_clock;
305 PtpClockIdentity master_clock_identity;
306 guint64 grandmaster_identity;
308 /* Last SYNC or FOLLOW_UP timestamp we received */
309 GstClockTime last_ptp_sync_time;
310 GstClockTime sync_interval;
312 GstClockTime mean_path_delay;
313 GstClockTime last_delay_req, min_delay_req_interval;
314 guint16 last_delay_req_seqnum;
316 GstClockTime last_path_delays[MEDIAN_PRE_FILTERING_WINDOW];
317 gint last_path_delays_missing;
319 GQueue pending_syncs;
321 GstClock *domain_clock;
324 static GList *domain_data;
325 static GMutex domain_clocks_lock;
326 static GList *domain_clocks;
328 /* Protected by PTP lock */
329 static void emit_ptp_statistics (guint8 domain, const GstStructure * stats);
330 static GHookList domain_stats_hooks;
331 static gint domain_stats_n_hooks;
332 static gboolean domain_stats_hooks_initted = FALSE;
334 /* Converts log2 seconds to GstClockTime */
336 log2_to_clock_time (gint l)
339 return GST_SECOND >> (-l);
341 return GST_SECOND << l;
345 dump_ptp_message (PtpMessage * msg)
347 GST_TRACE ("PTP message:");
348 GST_TRACE ("\ttransport_specific: %u", msg->transport_specific);
349 GST_TRACE ("\tmessage_type: 0x%01x", msg->message_type);
350 GST_TRACE ("\tversion_ptp: %u", msg->version_ptp);
351 GST_TRACE ("\tmessage_length: %u", msg->message_length);
352 GST_TRACE ("\tdomain_number: %u", msg->domain_number);
353 GST_TRACE ("\tflag_field: 0x%04x", msg->flag_field);
354 GST_TRACE ("\tcorrection_field: %" G_GINT64_FORMAT ".%03u",
355 (msg->correction_field / 65536),
356 (guint) ((msg->correction_field & 0xffff) * 1000) / 65536);
357 GST_TRACE ("\tsource_port_identity: 0x%016" G_GINT64_MODIFIER "x %u",
358 msg->source_port_identity.clock_identity,
359 msg->source_port_identity.port_number);
360 GST_TRACE ("\tsequence_id: %u", msg->sequence_id);
361 GST_TRACE ("\tcontrol_field: 0x%02x", msg->control_field);
362 GST_TRACE ("\tmessage_interval: %" GST_TIME_FORMAT,
363 GST_TIME_ARGS (log2_to_clock_time (msg->log_message_interval)));
365 switch (msg->message_type) {
366 case PTP_MESSAGE_TYPE_ANNOUNCE:
367 GST_TRACE ("\tANNOUNCE:");
368 GST_TRACE ("\t\torigin_timestamp: %" G_GUINT64_FORMAT ".%09u",
369 msg->message_specific.announce.origin_timestamp.seconds_field,
370 msg->message_specific.announce.origin_timestamp.nanoseconds_field);
371 GST_TRACE ("\t\tcurrent_utc_offset: %d",
372 msg->message_specific.announce.current_utc_offset);
373 GST_TRACE ("\t\tgrandmaster_priority_1: %u",
374 msg->message_specific.announce.grandmaster_priority_1);
375 GST_TRACE ("\t\tgrandmaster_clock_quality: 0x%02x 0x%02x %u",
376 msg->message_specific.announce.grandmaster_clock_quality.clock_class,
377 msg->message_specific.announce.
378 grandmaster_clock_quality.clock_accuracy,
379 msg->message_specific.announce.
380 grandmaster_clock_quality.offset_scaled_log_variance);
381 GST_TRACE ("\t\tgrandmaster_priority_2: %u",
382 msg->message_specific.announce.grandmaster_priority_2);
383 GST_TRACE ("\t\tgrandmaster_identity: 0x%016" G_GINT64_MODIFIER "x",
384 msg->message_specific.announce.grandmaster_identity);
385 GST_TRACE ("\t\tsteps_removed: %u",
386 msg->message_specific.announce.steps_removed);
387 GST_TRACE ("\t\ttime_source: 0x%02x",
388 msg->message_specific.announce.time_source);
390 case PTP_MESSAGE_TYPE_SYNC:
391 GST_TRACE ("\tSYNC:");
392 GST_TRACE ("\t\torigin_timestamp: %" G_GUINT64_FORMAT ".%09u",
393 msg->message_specific.sync.origin_timestamp.seconds_field,
394 msg->message_specific.sync.origin_timestamp.nanoseconds_field);
396 case PTP_MESSAGE_TYPE_FOLLOW_UP:
397 GST_TRACE ("\tFOLLOW_UP:");
398 GST_TRACE ("\t\tprecise_origin_timestamp: %" G_GUINT64_FORMAT ".%09u",
399 msg->message_specific.follow_up.
400 precise_origin_timestamp.seconds_field,
401 msg->message_specific.follow_up.
402 precise_origin_timestamp.nanoseconds_field);
404 case PTP_MESSAGE_TYPE_DELAY_REQ:
405 GST_TRACE ("\tDELAY_REQ:");
406 GST_TRACE ("\t\torigin_timestamp: %" G_GUINT64_FORMAT ".%09u",
407 msg->message_specific.delay_req.origin_timestamp.seconds_field,
408 msg->message_specific.delay_req.origin_timestamp.nanoseconds_field);
410 case PTP_MESSAGE_TYPE_DELAY_RESP:
411 GST_TRACE ("\tDELAY_RESP:");
412 GST_TRACE ("\t\treceive_timestamp: %" G_GUINT64_FORMAT ".%09u",
413 msg->message_specific.delay_resp.receive_timestamp.seconds_field,
414 msg->message_specific.delay_resp.receive_timestamp.nanoseconds_field);
415 GST_TRACE ("\t\trequesting_port_identity: 0x%016" G_GINT64_MODIFIER
417 msg->message_specific.delay_resp.
418 requesting_port_identity.clock_identity,
419 msg->message_specific.delay_resp.
420 requesting_port_identity.port_number);
428 /* IEEE 1588-2008 5.3.3 */
430 parse_ptp_timestamp (PtpTimestamp * timestamp, GstByteReader * reader)
432 g_return_val_if_fail (gst_byte_reader_get_remaining (reader) >= 10, FALSE);
434 timestamp->seconds_field =
435 (((guint64) gst_byte_reader_get_uint32_be_unchecked (reader)) << 16) |
436 gst_byte_reader_get_uint16_be_unchecked (reader);
437 timestamp->nanoseconds_field =
438 gst_byte_reader_get_uint32_be_unchecked (reader);
440 if (timestamp->nanoseconds_field >= 1000000000)
446 /* IEEE 1588-2008 13.3 */
448 parse_ptp_message_header (PtpMessage * msg, GstByteReader * reader)
452 g_return_val_if_fail (gst_byte_reader_get_remaining (reader) >= 34, FALSE);
454 b = gst_byte_reader_get_uint8_unchecked (reader);
455 msg->transport_specific = b >> 4;
456 msg->message_type = b & 0x0f;
458 b = gst_byte_reader_get_uint8_unchecked (reader);
459 msg->version_ptp = b & 0x0f;
460 if (msg->version_ptp != 2) {
461 GST_WARNING ("Unsupported PTP message version (%u != 2)", msg->version_ptp);
465 msg->message_length = gst_byte_reader_get_uint16_be_unchecked (reader);
466 if (gst_byte_reader_get_remaining (reader) + 4 < msg->message_length) {
467 GST_WARNING ("Not enough data (%u < %u)",
468 gst_byte_reader_get_remaining (reader) + 4, msg->message_length);
472 msg->domain_number = gst_byte_reader_get_uint8_unchecked (reader);
473 gst_byte_reader_skip_unchecked (reader, 1);
475 msg->flag_field = gst_byte_reader_get_uint16_be_unchecked (reader);
476 msg->correction_field = gst_byte_reader_get_uint64_be_unchecked (reader);
477 gst_byte_reader_skip_unchecked (reader, 4);
479 msg->source_port_identity.clock_identity =
480 gst_byte_reader_get_uint64_be_unchecked (reader);
481 msg->source_port_identity.port_number =
482 gst_byte_reader_get_uint16_be_unchecked (reader);
484 msg->sequence_id = gst_byte_reader_get_uint16_be_unchecked (reader);
485 msg->control_field = gst_byte_reader_get_uint8_unchecked (reader);
486 msg->log_message_interval = gst_byte_reader_get_uint8_unchecked (reader);
491 /* IEEE 1588-2008 13.5 */
493 parse_ptp_message_announce (PtpMessage * msg, GstByteReader * reader)
495 g_return_val_if_fail (msg->message_type == PTP_MESSAGE_TYPE_ANNOUNCE, FALSE);
497 if (gst_byte_reader_get_remaining (reader) < 20)
500 if (!parse_ptp_timestamp (&msg->message_specific.announce.origin_timestamp,
504 msg->message_specific.announce.current_utc_offset =
505 gst_byte_reader_get_uint16_be_unchecked (reader);
506 gst_byte_reader_skip_unchecked (reader, 1);
508 msg->message_specific.announce.grandmaster_priority_1 =
509 gst_byte_reader_get_uint8_unchecked (reader);
510 msg->message_specific.announce.grandmaster_clock_quality.clock_class =
511 gst_byte_reader_get_uint8_unchecked (reader);
512 msg->message_specific.announce.grandmaster_clock_quality.clock_accuracy =
513 gst_byte_reader_get_uint8_unchecked (reader);
514 msg->message_specific.announce.
515 grandmaster_clock_quality.offset_scaled_log_variance =
516 gst_byte_reader_get_uint16_be_unchecked (reader);
517 msg->message_specific.announce.grandmaster_priority_2 =
518 gst_byte_reader_get_uint8_unchecked (reader);
519 msg->message_specific.announce.grandmaster_identity =
520 gst_byte_reader_get_uint64_be_unchecked (reader);
521 msg->message_specific.announce.steps_removed =
522 gst_byte_reader_get_uint16_be_unchecked (reader);
523 msg->message_specific.announce.time_source =
524 gst_byte_reader_get_uint8_unchecked (reader);
529 /* IEEE 1588-2008 13.6 */
531 parse_ptp_message_sync (PtpMessage * msg, GstByteReader * reader)
533 g_return_val_if_fail (msg->message_type == PTP_MESSAGE_TYPE_SYNC, FALSE);
535 if (gst_byte_reader_get_remaining (reader) < 10)
538 if (!parse_ptp_timestamp (&msg->message_specific.sync.origin_timestamp,
545 /* IEEE 1588-2008 13.6 */
547 parse_ptp_message_delay_req (PtpMessage * msg, GstByteReader * reader)
549 g_return_val_if_fail (msg->message_type == PTP_MESSAGE_TYPE_DELAY_REQ, FALSE);
551 if (gst_byte_reader_get_remaining (reader) < 10)
554 if (!parse_ptp_timestamp (&msg->message_specific.delay_req.origin_timestamp,
561 /* IEEE 1588-2008 13.7 */
563 parse_ptp_message_follow_up (PtpMessage * msg, GstByteReader * reader)
565 g_return_val_if_fail (msg->message_type == PTP_MESSAGE_TYPE_FOLLOW_UP, FALSE);
567 if (gst_byte_reader_get_remaining (reader) < 10)
570 if (!parse_ptp_timestamp (&msg->message_specific.
571 follow_up.precise_origin_timestamp, reader))
577 /* IEEE 1588-2008 13.8 */
579 parse_ptp_message_delay_resp (PtpMessage * msg, GstByteReader * reader)
581 g_return_val_if_fail (msg->message_type == PTP_MESSAGE_TYPE_DELAY_RESP,
584 if (gst_byte_reader_get_remaining (reader) < 20)
587 if (!parse_ptp_timestamp (&msg->message_specific.delay_resp.receive_timestamp,
591 msg->message_specific.delay_resp.requesting_port_identity.clock_identity =
592 gst_byte_reader_get_uint64_be_unchecked (reader);
593 msg->message_specific.delay_resp.requesting_port_identity.port_number =
594 gst_byte_reader_get_uint16_be_unchecked (reader);
600 parse_ptp_message (PtpMessage * msg, const guint8 * data, gsize size)
602 GstByteReader reader;
603 gboolean ret = FALSE;
605 gst_byte_reader_init (&reader, data, size);
607 if (!parse_ptp_message_header (msg, &reader)) {
608 GST_WARNING ("Failed to parse PTP message header");
612 switch (msg->message_type) {
613 case PTP_MESSAGE_TYPE_SYNC:
614 ret = parse_ptp_message_sync (msg, &reader);
616 case PTP_MESSAGE_TYPE_FOLLOW_UP:
617 ret = parse_ptp_message_follow_up (msg, &reader);
619 case PTP_MESSAGE_TYPE_DELAY_REQ:
620 ret = parse_ptp_message_delay_req (msg, &reader);
622 case PTP_MESSAGE_TYPE_DELAY_RESP:
623 ret = parse_ptp_message_delay_resp (msg, &reader);
625 case PTP_MESSAGE_TYPE_ANNOUNCE:
626 ret = parse_ptp_message_announce (msg, &reader);
637 compare_announce_message (const PtpAnnounceMessage * a,
638 const PtpAnnounceMessage * b)
640 /* IEEE 1588 Figure 27 */
641 if (a->grandmaster_identity == b->grandmaster_identity) {
642 if (a->steps_removed + 1 < b->steps_removed)
644 else if (a->steps_removed > b->steps_removed + 1)
647 /* Error cases are filtered out earlier */
648 if (a->steps_removed < b->steps_removed)
650 else if (a->steps_removed > b->steps_removed)
653 /* Error cases are filtered out earlier */
654 if (a->master_clock_identity.clock_identity <
655 b->master_clock_identity.clock_identity)
657 else if (a->master_clock_identity.clock_identity >
658 b->master_clock_identity.clock_identity)
661 /* Error cases are filtered out earlier */
662 if (a->master_clock_identity.port_number <
663 b->master_clock_identity.port_number)
665 else if (a->master_clock_identity.port_number >
666 b->master_clock_identity.port_number)
669 g_assert_not_reached ();
674 if (a->grandmaster_priority_1 < b->grandmaster_priority_1)
676 else if (a->grandmaster_priority_1 > b->grandmaster_priority_1)
679 if (a->grandmaster_clock_quality.clock_class <
680 b->grandmaster_clock_quality.clock_class)
682 else if (a->grandmaster_clock_quality.clock_class >
683 b->grandmaster_clock_quality.clock_class)
686 if (a->grandmaster_clock_quality.clock_accuracy <
687 b->grandmaster_clock_quality.clock_accuracy)
689 else if (a->grandmaster_clock_quality.clock_accuracy >
690 b->grandmaster_clock_quality.clock_accuracy)
693 if (a->grandmaster_clock_quality.offset_scaled_log_variance <
694 b->grandmaster_clock_quality.offset_scaled_log_variance)
696 else if (a->grandmaster_clock_quality.offset_scaled_log_variance >
697 b->grandmaster_clock_quality.offset_scaled_log_variance)
700 if (a->grandmaster_priority_2 < b->grandmaster_priority_2)
702 else if (a->grandmaster_priority_2 > b->grandmaster_priority_2)
705 if (a->grandmaster_identity < b->grandmaster_identity)
707 else if (a->grandmaster_identity > b->grandmaster_identity)
710 g_assert_not_reached ();
716 select_best_master_clock (PtpDomainData * domain, GstClockTime now)
718 GList *qualified_messages = NULL;
720 PtpAnnounceMessage *best = NULL;
722 /* IEEE 1588 9.3.2.5 */
723 for (l = domain->announce_senders; l; l = l->next) {
724 PtpAnnounceSender *sender = l->data;
725 GstClockTime window = 4 * sender->announce_interval;
728 for (m = sender->announce_messages.head; m; m = m->next) {
729 PtpAnnounceMessage *msg = m->data;
731 if (now - msg->receive_time <= window)
735 /* Only include the newest message of announce senders that had at least 2
736 * announce messages in the last 4 announce intervals. Which also means
737 * that we wait at least 4 announce intervals before we select a master
738 * clock. Until then we just report based on the newest SYNC we received
742 g_list_prepend (qualified_messages,
743 g_queue_peek_tail (&sender->announce_messages));
747 if (!qualified_messages) {
749 ("No qualified announce messages for domain %u, can't select a master clock",
751 domain->have_master_clock = FALSE;
755 for (l = qualified_messages; l; l = l->next) {
756 PtpAnnounceMessage *msg = l->data;
758 if (!best || compare_announce_message (msg, best) < 0)
762 if (domain->have_master_clock
763 && compare_clock_identity (&domain->master_clock_identity,
764 &best->master_clock_identity) == 0) {
765 GST_DEBUG ("Master clock in domain %u did not change", domain->domain);
767 GST_DEBUG ("Selected master clock for domain %u: 0x%016" G_GINT64_MODIFIER
768 "x %u with grandmaster clock 0x%016" G_GINT64_MODIFIER "x",
769 domain->domain, best->master_clock_identity.clock_identity,
770 best->master_clock_identity.port_number, best->grandmaster_identity);
772 domain->have_master_clock = TRUE;
773 domain->grandmaster_identity = best->grandmaster_identity;
775 /* Opportunistic master clock selection likely gave us the same master
776 * clock before, no need to reset all statistics */
777 if (compare_clock_identity (&domain->master_clock_identity,
778 &best->master_clock_identity) != 0) {
779 memcpy (&domain->master_clock_identity, &best->master_clock_identity,
780 sizeof (PtpClockIdentity));
781 domain->mean_path_delay = 0;
782 domain->last_delay_req = 0;
783 domain->last_path_delays_missing = 9;
784 domain->min_delay_req_interval = 0;
785 domain->sync_interval = 0;
786 domain->last_ptp_sync_time = 0;
787 domain->skipped_updates = 0;
788 g_queue_foreach (&domain->pending_syncs, (GFunc) ptp_pending_sync_free,
790 g_queue_clear (&domain->pending_syncs);
793 if (g_atomic_int_get (&domain_stats_n_hooks)) {
794 GstStructure *stats =
795 gst_structure_new (GST_PTP_STATISTICS_BEST_MASTER_CLOCK_SELECTED,
796 "domain", G_TYPE_UINT, domain->domain,
797 "master-clock-id", G_TYPE_UINT64,
798 domain->master_clock_identity.clock_identity,
799 "master-clock-port", G_TYPE_UINT,
800 domain->master_clock_identity.port_number,
801 "grandmaster-clock-id", G_TYPE_UINT64, domain->grandmaster_identity,
803 emit_ptp_statistics (domain->domain, stats);
804 gst_structure_free (stats);
810 handle_announce_message (PtpMessage * msg, GstClockTime receive_time)
813 PtpDomainData *domain = NULL;
814 PtpAnnounceSender *sender = NULL;
815 PtpAnnounceMessage *announce;
817 /* IEEE1588 9.3.2.2 e)
818 * Don't consider messages with the alternate master flag set
820 if ((msg->flag_field & 0x0100))
823 /* IEEE 1588 9.3.2.5 d)
824 * Don't consider announce messages with steps_removed>=255
826 if (msg->message_specific.announce.steps_removed >= 255)
829 for (l = domain_data; l; l = l->next) {
830 PtpDomainData *tmp = l->data;
832 if (tmp->domain == msg->domain_number) {
841 domain = g_new0 (PtpDomainData, 1);
842 domain->domain = msg->domain_number;
843 clock_name = g_strdup_printf ("ptp-clock-%u", domain->domain);
844 domain->domain_clock =
845 g_object_new (GST_TYPE_SYSTEM_CLOCK, "name", clock_name, NULL);
847 g_queue_init (&domain->pending_syncs);
848 domain->last_path_delays_missing = 9;
849 domain_data = g_list_prepend (domain_data, domain);
851 g_mutex_lock (&domain_clocks_lock);
852 domain_clocks = g_list_prepend (domain_clocks, domain);
853 g_mutex_unlock (&domain_clocks_lock);
855 if (g_atomic_int_get (&domain_stats_n_hooks)) {
856 GstStructure *stats =
857 gst_structure_new (GST_PTP_STATISTICS_NEW_DOMAIN_FOUND, "domain",
858 G_TYPE_UINT, domain->domain, "clock", GST_TYPE_CLOCK,
859 domain->domain_clock, NULL);
860 emit_ptp_statistics (domain->domain, stats);
861 gst_structure_free (stats);
865 for (l = domain->announce_senders; l; l = l->next) {
866 PtpAnnounceSender *tmp = l->data;
868 if (compare_clock_identity (&tmp->master_clock_identity,
869 &msg->source_port_identity) == 0) {
876 sender = g_new0 (PtpAnnounceSender, 1);
878 memcpy (&sender->master_clock_identity, &msg->source_port_identity,
879 sizeof (PtpClockIdentity));
880 g_queue_init (&sender->announce_messages);
881 domain->announce_senders =
882 g_list_prepend (domain->announce_senders, sender);
885 for (l = sender->announce_messages.head; l; l = l->next) {
886 PtpAnnounceMessage *tmp = l->data;
888 /* IEEE 1588 9.3.2.5 c)
889 * Don't consider identical messages, i.e. duplicates
891 if (tmp->sequence_id == msg->sequence_id)
895 sender->announce_interval = log2_to_clock_time (msg->log_message_interval);
897 announce = g_new0 (PtpAnnounceMessage, 1);
898 announce->receive_time = receive_time;
899 announce->sequence_id = msg->sequence_id;
900 memcpy (&announce->master_clock_identity, &msg->source_port_identity,
901 sizeof (PtpClockIdentity));
902 announce->grandmaster_identity =
903 msg->message_specific.announce.grandmaster_identity;
904 announce->grandmaster_priority_1 =
905 msg->message_specific.announce.grandmaster_priority_1;
906 announce->grandmaster_clock_quality.clock_class =
907 msg->message_specific.announce.grandmaster_clock_quality.clock_class;
908 announce->grandmaster_clock_quality.clock_accuracy =
909 msg->message_specific.announce.grandmaster_clock_quality.clock_accuracy;
910 announce->grandmaster_clock_quality.offset_scaled_log_variance =
911 msg->message_specific.announce.
912 grandmaster_clock_quality.offset_scaled_log_variance;
913 announce->grandmaster_priority_2 =
914 msg->message_specific.announce.grandmaster_priority_2;
915 announce->steps_removed = msg->message_specific.announce.steps_removed;
916 announce->time_source = msg->message_specific.announce.time_source;
917 g_queue_push_tail (&sender->announce_messages, announce);
919 select_best_master_clock (domain, receive_time);
923 send_delay_req_timeout (PtpPendingSync * sync)
925 StdIOHeader header = { 0, };
926 guint8 delay_req[44];
927 GstByteWriter writer;
932 header.type = TYPE_EVENT;
935 gst_byte_writer_init_with_data (&writer, delay_req, 44, FALSE);
936 gst_byte_writer_put_uint8_unchecked (&writer, PTP_MESSAGE_TYPE_DELAY_REQ);
937 gst_byte_writer_put_uint8_unchecked (&writer, 2);
938 gst_byte_writer_put_uint16_be_unchecked (&writer, 44);
939 gst_byte_writer_put_uint8_unchecked (&writer, sync->domain);
940 gst_byte_writer_put_uint8_unchecked (&writer, 0);
941 gst_byte_writer_put_uint16_be_unchecked (&writer, 0);
942 gst_byte_writer_put_uint64_be_unchecked (&writer, 0);
943 gst_byte_writer_put_uint32_be_unchecked (&writer, 0);
944 gst_byte_writer_put_uint64_be_unchecked (&writer,
945 ptp_clock_id.clock_identity);
946 gst_byte_writer_put_uint16_be_unchecked (&writer, ptp_clock_id.port_number);
947 gst_byte_writer_put_uint16_be_unchecked (&writer, sync->delay_req_seqnum);
948 gst_byte_writer_put_uint8_unchecked (&writer, 0x01);
949 gst_byte_writer_put_uint8_unchecked (&writer, 0x7f);
950 gst_byte_writer_put_uint64_be_unchecked (&writer, 0);
951 gst_byte_writer_put_uint16_be_unchecked (&writer, 0);
954 g_io_channel_write_chars (stdout_channel, (gchar *) & header,
955 sizeof (header), &written, &err);
956 if (status == G_IO_STATUS_ERROR) {
957 g_warning ("Failed to write to stdout: %s", err->message);
958 return G_SOURCE_REMOVE;
959 } else if (status == G_IO_STATUS_EOF) {
960 g_message ("EOF on stdout");
961 g_main_loop_quit (main_loop);
962 return G_SOURCE_REMOVE;
963 } else if (status != G_IO_STATUS_NORMAL) {
964 g_warning ("Unexpected stdout write status: %d", status);
965 g_main_loop_quit (main_loop);
966 return G_SOURCE_REMOVE;
967 } else if (written != sizeof (header)) {
968 g_warning ("Unexpected write size: %" G_GSIZE_FORMAT, written);
969 g_main_loop_quit (main_loop);
970 return G_SOURCE_REMOVE;
973 sync->delay_req_send_time_local = gst_util_get_timestamp ();
976 g_io_channel_write_chars (stdout_channel,
977 (const gchar *) delay_req, 44, &written, &err);
978 if (status == G_IO_STATUS_ERROR) {
979 g_warning ("Failed to write to stdout: %s", err->message);
980 g_main_loop_quit (main_loop);
981 return G_SOURCE_REMOVE;
982 } else if (status == G_IO_STATUS_EOF) {
983 g_message ("EOF on stdout");
984 g_main_loop_quit (main_loop);
985 return G_SOURCE_REMOVE;
986 } else if (status != G_IO_STATUS_NORMAL) {
987 g_warning ("Unexpected stdout write status: %d", status);
988 g_main_loop_quit (main_loop);
989 return G_SOURCE_REMOVE;
990 } else if (written != 44) {
991 g_warning ("Unexpected write size: %" G_GSIZE_FORMAT, written);
992 g_main_loop_quit (main_loop);
993 return G_SOURCE_REMOVE;
996 return G_SOURCE_REMOVE;
1000 send_delay_req (PtpDomainData * domain, PtpPendingSync * sync)
1002 GstClockTime now = gst_util_get_timestamp ();
1004 GSource *timeout_source;
1006 if (domain->last_delay_req != 0
1007 && domain->last_delay_req + domain->min_delay_req_interval > now)
1010 domain->last_delay_req = now;
1011 sync->delay_req_seqnum = domain->last_delay_req_seqnum++;
1013 /* IEEE 1588 9.5.11.2 */
1014 if (domain->last_delay_req == 0 || domain->min_delay_req_interval == 0)
1018 g_rand_int_range (delay_req_rand, 0,
1019 (domain->min_delay_req_interval * 2) / GST_MSECOND);
1021 sync->timeout_source = timeout_source = g_timeout_source_new (timeout);
1022 g_source_set_priority (timeout_source, G_PRIORITY_DEFAULT);
1023 g_source_set_callback (timeout_source, (GSourceFunc) send_delay_req_timeout,
1025 g_source_attach (timeout_source, main_context);
1030 /* Filtering of outliers for RTT and time calculations inspired
1031 * by the code from gstnetclientclock.c
1034 update_ptp_time (PtpDomainData * domain, PtpPendingSync * sync)
1036 GstClockTime internal_time, external_time, rate_num, rate_den;
1037 GstClockTime corrected_ptp_time, corrected_local_time;
1038 gdouble r_squared = 0.0;
1040 GstClockTimeDiff discont = 0;
1041 GstClockTime estimated_ptp_time = GST_CLOCK_TIME_NONE;
1042 #ifdef USE_MEASUREMENT_FILTERING
1043 GstClockTime orig_internal_time, orig_external_time, orig_rate_num,
1045 GstClockTime new_estimated_ptp_time;
1046 GstClockTime max_discont, estimated_ptp_time_min, estimated_ptp_time_max;
1047 gboolean now_synced;
1050 #ifdef USE_ONLY_SYNC_WITH_DELAY
1051 if (sync->delay_req_send_time_local == GST_CLOCK_TIME_NONE)
1055 /* IEEE 1588 11.2 */
1056 corrected_ptp_time =
1057 sync->sync_send_time_remote +
1058 (sync->correction_field_sync + 32768) / 65536;
1059 corrected_local_time = sync->sync_recv_time_local - domain->mean_path_delay;
1061 #ifdef USE_MEASUREMENT_FILTERING
1062 /* We check this here and when updating the mean path delay, because
1063 * we can get here without a delay response too */
1064 if (sync->follow_up_recv_time_local != GST_CLOCK_TIME_NONE
1065 && sync->follow_up_recv_time_local >
1066 sync->sync_recv_time_local + 2 * domain->mean_path_delay) {
1067 GST_WARNING ("Sync-follow-up delay for domain %u too big: %" GST_TIME_FORMAT
1068 " > 2 * %" GST_TIME_FORMAT, domain->domain,
1069 GST_TIME_ARGS (sync->follow_up_recv_time_local),
1070 GST_TIME_ARGS (domain->mean_path_delay));
1076 /* Set an initial local-remote relation */
1077 if (domain->last_ptp_time == 0)
1078 gst_clock_set_calibration (domain->domain_clock, corrected_local_time,
1079 corrected_ptp_time, 1, 1);
1081 #ifdef USE_MEASUREMENT_FILTERING
1082 /* Check if the corrected PTP time is +/- 3/4 RTT around what we would
1083 * estimate with our present knowledge about the clock
1085 /* Store what the clock produced as 'now' before this update */
1086 gst_clock_get_calibration (GST_CLOCK_CAST (domain->domain_clock),
1087 &orig_internal_time, &orig_external_time, &orig_rate_num, &orig_rate_den);
1088 internal_time = orig_internal_time;
1089 external_time = orig_external_time;
1090 rate_num = orig_rate_num;
1091 rate_den = orig_rate_den;
1093 /* 3/4 RTT window around the estimation */
1094 max_discont = domain->mean_path_delay * 3 / 2;
1096 /* Check if the estimated sync time is inside our window */
1097 estimated_ptp_time_min = corrected_local_time - max_discont;
1098 estimated_ptp_time_min =
1099 gst_clock_adjust_with_calibration (GST_CLOCK_CAST (domain->domain_clock),
1100 estimated_ptp_time_min, internal_time, external_time, rate_num, rate_den);
1101 estimated_ptp_time_max = corrected_local_time + max_discont;
1102 estimated_ptp_time_max =
1103 gst_clock_adjust_with_calibration (GST_CLOCK_CAST (domain->domain_clock),
1104 estimated_ptp_time_max, internal_time, external_time, rate_num, rate_den);
1106 synced = (estimated_ptp_time_min < corrected_ptp_time
1107 && corrected_ptp_time < estimated_ptp_time_max);
1109 GST_DEBUG ("Adding observation for domain %u: %" GST_TIME_FORMAT " - %"
1110 GST_TIME_FORMAT, domain->domain,
1111 GST_TIME_ARGS (corrected_ptp_time), GST_TIME_ARGS (corrected_local_time));
1113 GST_DEBUG ("Synced %d: %" GST_TIME_FORMAT " < %" GST_TIME_FORMAT " < %"
1114 GST_TIME_FORMAT, synced, GST_TIME_ARGS (estimated_ptp_time_min),
1115 GST_TIME_ARGS (corrected_ptp_time),
1116 GST_TIME_ARGS (estimated_ptp_time_max));
1118 if (gst_clock_add_observation_unapplied (domain->domain_clock,
1119 corrected_local_time, corrected_ptp_time, &r_squared,
1120 &internal_time, &external_time, &rate_num, &rate_den)) {
1121 GST_DEBUG ("Regression gave r_squared: %f", r_squared);
1123 /* Old estimated PTP time based on receive time and path delay */
1124 estimated_ptp_time = corrected_local_time;
1125 estimated_ptp_time =
1126 gst_clock_adjust_with_calibration (GST_CLOCK_CAST
1127 (domain->domain_clock), estimated_ptp_time, orig_internal_time,
1128 orig_external_time, orig_rate_num, orig_rate_den);
1130 /* New estimated PTP time based on receive time and path delay */
1131 new_estimated_ptp_time = corrected_local_time;
1132 new_estimated_ptp_time =
1133 gst_clock_adjust_with_calibration (GST_CLOCK_CAST
1134 (domain->domain_clock), new_estimated_ptp_time, internal_time,
1135 external_time, rate_num, rate_den);
1137 discont = GST_CLOCK_DIFF (estimated_ptp_time, new_estimated_ptp_time);
1138 if (synced && ABS (discont) > max_discont) {
1139 GstClockTimeDiff offset;
1140 GST_DEBUG ("Too large a discont %s%" GST_TIME_FORMAT
1141 ", clamping to 1/4 average RTT = %" GST_TIME_FORMAT,
1142 (discont < 0 ? "-" : ""), GST_TIME_ARGS (ABS (discont)),
1143 GST_TIME_ARGS (max_discont));
1144 if (discont > 0) { /* Too large a forward step - add a -ve offset */
1145 offset = max_discont - discont;
1146 if (-offset > external_time)
1149 external_time += offset;
1150 } else { /* Too large a backward step - add a +ve offset */
1151 offset = -(max_discont + discont);
1152 external_time += offset;
1157 GST_DEBUG ("Discont %s%" GST_TIME_FORMAT " (max: %" GST_TIME_FORMAT ")",
1158 (discont < 0 ? "-" : ""), GST_TIME_ARGS (ABS (discont)),
1159 GST_TIME_ARGS (max_discont));
1162 /* Check if the estimated sync time is now (still) inside our window */
1163 estimated_ptp_time_min = corrected_local_time - max_discont;
1164 estimated_ptp_time_min =
1165 gst_clock_adjust_with_calibration (GST_CLOCK_CAST
1166 (domain->domain_clock), estimated_ptp_time_min, internal_time,
1167 external_time, rate_num, rate_den);
1168 estimated_ptp_time_max = corrected_local_time + max_discont;
1169 estimated_ptp_time_max =
1170 gst_clock_adjust_with_calibration (GST_CLOCK_CAST
1171 (domain->domain_clock), estimated_ptp_time_max, internal_time,
1172 external_time, rate_num, rate_den);
1174 now_synced = (estimated_ptp_time_min < corrected_ptp_time
1175 && corrected_ptp_time < estimated_ptp_time_max);
1177 GST_DEBUG ("Now synced %d: %" GST_TIME_FORMAT " < %" GST_TIME_FORMAT " < %"
1178 GST_TIME_FORMAT, now_synced, GST_TIME_ARGS (estimated_ptp_time_min),
1179 GST_TIME_ARGS (corrected_ptp_time),
1180 GST_TIME_ARGS (estimated_ptp_time_max));
1182 if (synced || now_synced || domain->skipped_updates > MAX_SKIPPED_UPDATES) {
1183 gst_clock_set_calibration (GST_CLOCK_CAST (domain->domain_clock),
1184 internal_time, external_time, rate_num, rate_den);
1185 domain->skipped_updates = 0;
1187 domain->last_ptp_time = corrected_ptp_time;
1188 domain->last_local_time = corrected_local_time;
1190 domain->skipped_updates++;
1193 domain->last_ptp_time = corrected_ptp_time;
1194 domain->last_local_time = corrected_local_time;
1198 GST_DEBUG ("Adding observation for domain %u: %" GST_TIME_FORMAT " - %"
1199 GST_TIME_FORMAT, domain->domain,
1200 GST_TIME_ARGS (corrected_ptp_time), GST_TIME_ARGS (corrected_local_time));
1202 gst_clock_get_calibration (GST_CLOCK_CAST (domain->domain_clock),
1203 &internal_time, &external_time, &rate_num, &rate_den);
1205 estimated_ptp_time = corrected_local_time;
1206 estimated_ptp_time =
1207 gst_clock_adjust_with_calibration (GST_CLOCK_CAST
1208 (domain->domain_clock), estimated_ptp_time, internal_time,
1209 external_time, rate_num, rate_den);
1211 gst_clock_add_observation (domain->domain_clock,
1212 corrected_local_time, corrected_ptp_time, &r_squared);
1214 gst_clock_get_calibration (GST_CLOCK_CAST (domain->domain_clock),
1215 &internal_time, &external_time, &rate_num, &rate_den);
1218 domain->last_ptp_time = corrected_ptp_time;
1219 domain->last_local_time = corrected_local_time;
1222 #ifdef USE_MEASUREMENT_FILTERING
1225 if (g_atomic_int_get (&domain_stats_n_hooks)) {
1226 GstStructure *stats = gst_structure_new (GST_PTP_STATISTICS_TIME_UPDATED,
1227 "domain", G_TYPE_UINT, domain->domain,
1228 "mean-path-delay-avg", GST_TYPE_CLOCK_TIME, domain->mean_path_delay,
1229 "local-time", GST_TYPE_CLOCK_TIME, corrected_local_time,
1230 "ptp-time", GST_TYPE_CLOCK_TIME, corrected_ptp_time,
1231 "estimated-ptp-time", GST_TYPE_CLOCK_TIME, estimated_ptp_time,
1232 "discontinuity", G_TYPE_INT64, discont,
1233 "synced", G_TYPE_BOOLEAN, synced,
1234 "r-squared", G_TYPE_DOUBLE, r_squared,
1235 "internal-time", GST_TYPE_CLOCK_TIME, internal_time,
1236 "external-time", GST_TYPE_CLOCK_TIME, external_time,
1237 "rate-num", G_TYPE_UINT64, rate_num,
1238 "rate-den", G_TYPE_UINT64, rate_den,
1239 "rate", G_TYPE_DOUBLE, (gdouble) (rate_num) / rate_den,
1241 emit_ptp_statistics (domain->domain, stats);
1242 gst_structure_free (stats);
1247 #ifdef USE_MEDIAN_PRE_FILTERING
1249 compare_clock_time (const GstClockTime * a, const GstClockTime * b)
1260 update_mean_path_delay (PtpDomainData * domain, PtpPendingSync * sync)
1262 #ifdef USE_MEDIAN_PRE_FILTERING
1263 GstClockTime last_path_delays[G_N_ELEMENTS (domain->last_path_delays)];
1264 GstClockTime median;
1268 GstClockTime mean_path_delay, delay_req_delay = 0;
1271 /* IEEE 1588 11.3 */
1273 (sync->delay_req_recv_time_remote - sync->sync_send_time_remote +
1274 sync->sync_recv_time_local - sync->delay_req_send_time_local -
1275 (sync->correction_field_sync + sync->correction_field_delay +
1276 32768) / 65536) / 2;
1278 #ifdef USE_MEDIAN_PRE_FILTERING
1279 for (i = 1; i < G_N_ELEMENTS (domain->last_path_delays); i++)
1280 domain->last_path_delays[i - 1] = domain->last_path_delays[i];
1281 domain->last_path_delays[i - 1] = mean_path_delay;
1283 if (domain->last_path_delays_missing) {
1284 domain->last_path_delays_missing--;
1286 memcpy (&last_path_delays, &domain->last_path_delays,
1287 sizeof (last_path_delays));
1288 g_qsort_with_data (&last_path_delays,
1289 G_N_ELEMENTS (domain->last_path_delays), sizeof (GstClockTime),
1290 (GCompareDataFunc) compare_clock_time, NULL);
1292 median = last_path_delays[G_N_ELEMENTS (last_path_delays) / 2];
1294 /* FIXME: We might want to use something else here, like only allowing
1295 * things in the interquartile range, or also filtering away delays that
1296 * are too small compared to the median. This here worked well enough
1299 if (mean_path_delay > 2 * median) {
1300 GST_WARNING ("Path delay for domain %u too big compared to median: %"
1301 GST_TIME_FORMAT " > 2 * %" GST_TIME_FORMAT, domain->domain,
1302 GST_TIME_ARGS (mean_path_delay), GST_TIME_ARGS (median));
1309 #ifdef USE_RUNNING_AVERAGE_DELAY
1310 /* Track an average round trip time, for a bit of smoothing */
1311 /* Always update before discarding a sample, so genuine changes in
1312 * the network get picked up, eventually */
1313 if (domain->mean_path_delay == 0)
1314 domain->mean_path_delay = mean_path_delay;
1315 else if (mean_path_delay < domain->mean_path_delay) /* Shorter RTTs carry more weight than longer */
1316 domain->mean_path_delay =
1317 (3 * domain->mean_path_delay + mean_path_delay) / 4;
1319 domain->mean_path_delay =
1320 (15 * domain->mean_path_delay + mean_path_delay) / 16;
1322 domain->mean_path_delay = mean_path_delay;
1325 #ifdef USE_MEASUREMENT_FILTERING
1326 if (sync->follow_up_recv_time_local != GST_CLOCK_TIME_NONE &&
1327 domain->mean_path_delay != 0
1328 && sync->follow_up_recv_time_local >
1329 sync->sync_recv_time_local + 2 * domain->mean_path_delay) {
1330 GST_WARNING ("Sync-follow-up delay for domain %u too big: %" GST_TIME_FORMAT
1331 " > 2 * %" GST_TIME_FORMAT, domain->domain,
1332 GST_TIME_ARGS (sync->follow_up_recv_time_local -
1333 sync->sync_recv_time_local),
1334 GST_TIME_ARGS (domain->mean_path_delay));
1339 if (mean_path_delay > 2 * domain->mean_path_delay) {
1340 GST_WARNING ("Mean path delay for domain %u too big: %" GST_TIME_FORMAT
1341 " > 2 * %" GST_TIME_FORMAT, domain->domain,
1342 GST_TIME_ARGS (mean_path_delay),
1343 GST_TIME_ARGS (domain->mean_path_delay));
1350 sync->delay_resp_recv_time_local - sync->delay_req_send_time_local;
1352 #ifdef USE_MEASUREMENT_FILTERING
1353 /* delay_req_delay is a RTT, so 2 times the path delay */
1354 if (delay_req_delay > 4 * domain->mean_path_delay) {
1355 GST_WARNING ("Delay-request-response delay for domain %u too big: %"
1356 GST_TIME_FORMAT " > 4 * %" GST_TIME_FORMAT, domain->domain,
1357 GST_TIME_ARGS (delay_req_delay),
1358 GST_TIME_ARGS (domain->mean_path_delay));
1366 GST_DEBUG ("Got mean path delay for domain %u: %" GST_TIME_FORMAT " (new: %"
1367 GST_TIME_FORMAT ")", domain->domain,
1368 GST_TIME_ARGS (domain->mean_path_delay), GST_TIME_ARGS (mean_path_delay));
1369 GST_DEBUG ("Delay request delay for domain %u: %" GST_TIME_FORMAT,
1370 domain->domain, GST_TIME_ARGS (delay_req_delay));
1372 #ifdef USE_MEASUREMENT_FILTERING
1375 if (g_atomic_int_get (&domain_stats_n_hooks)) {
1376 GstStructure *stats =
1377 gst_structure_new (GST_PTP_STATISTICS_PATH_DELAY_MEASURED,
1378 "domain", G_TYPE_UINT, domain->domain,
1379 "mean-path-delay-avg", GST_TYPE_CLOCK_TIME, domain->mean_path_delay,
1380 "mean-path-delay", GST_TYPE_CLOCK_TIME, mean_path_delay,
1381 "delay-request-delay", GST_TYPE_CLOCK_TIME, delay_req_delay, NULL);
1382 emit_ptp_statistics (domain->domain, stats);
1383 gst_structure_free (stats);
1390 handle_sync_message (PtpMessage * msg, GstClockTime receive_time)
1393 PtpDomainData *domain = NULL;
1394 PtpPendingSync *sync = NULL;
1396 /* Don't consider messages with the alternate master flag set */
1397 if ((msg->flag_field & 0x0100))
1400 for (l = domain_data; l; l = l->next) {
1401 PtpDomainData *tmp = l->data;
1403 if (msg->domain_number == tmp->domain) {
1412 domain = g_new0 (PtpDomainData, 1);
1413 domain->domain = msg->domain_number;
1414 clock_name = g_strdup_printf ("ptp-clock-%u", domain->domain);
1415 domain->domain_clock =
1416 g_object_new (GST_TYPE_SYSTEM_CLOCK, "name", clock_name, NULL);
1417 g_free (clock_name);
1418 g_queue_init (&domain->pending_syncs);
1419 domain->last_path_delays_missing = 9;
1420 domain_data = g_list_prepend (domain_data, domain);
1422 g_mutex_lock (&domain_clocks_lock);
1423 domain_clocks = g_list_prepend (domain_clocks, domain);
1424 g_mutex_unlock (&domain_clocks_lock);
1427 /* If we have a master clock, ignore this message if it's not coming from there */
1428 if (domain->have_master_clock
1429 && compare_clock_identity (&domain->master_clock_identity,
1430 &msg->source_port_identity) != 0)
1433 #ifdef USE_OPPORTUNISTIC_CLOCK_SELECTION
1434 /* Opportunistic selection of master clock */
1435 if (!domain->have_master_clock)
1436 memcpy (&domain->master_clock_identity, &msg->source_port_identity,
1437 sizeof (PtpClockIdentity));
1439 if (!domain->have_master_clock)
1443 domain->sync_interval = log2_to_clock_time (msg->log_message_interval);
1445 /* Check if duplicated */
1446 for (l = domain->pending_syncs.head; l; l = l->next) {
1447 PtpPendingSync *tmp = l->data;
1449 if (tmp->sync_seqnum == msg->sequence_id)
1453 if (msg->message_specific.sync.origin_timestamp.seconds_field >
1454 GST_CLOCK_TIME_NONE / GST_SECOND) {
1455 GST_FIXME ("Unsupported sync message seconds field value: %"
1456 G_GUINT64_FORMAT " > %" G_GUINT64_FORMAT,
1457 msg->message_specific.sync.origin_timestamp.seconds_field,
1458 GST_CLOCK_TIME_NONE / GST_SECOND);
1462 sync = g_new0 (PtpPendingSync, 1);
1463 sync->domain = domain->domain;
1464 sync->sync_seqnum = msg->sequence_id;
1465 sync->sync_recv_time_local = receive_time;
1466 sync->sync_send_time_remote = GST_CLOCK_TIME_NONE;
1467 sync->follow_up_recv_time_local = GST_CLOCK_TIME_NONE;
1468 sync->delay_req_send_time_local = GST_CLOCK_TIME_NONE;
1469 sync->delay_req_recv_time_remote = GST_CLOCK_TIME_NONE;
1470 sync->delay_resp_recv_time_local = GST_CLOCK_TIME_NONE;
1472 /* 0.5 correction factor for division later */
1473 sync->correction_field_sync = msg->correction_field;
1475 if ((msg->flag_field & 0x0200)) {
1476 /* Wait for FOLLOW_UP */
1478 sync->sync_send_time_remote =
1479 PTP_TIMESTAMP_TO_GST_CLOCK_TIME (msg->message_specific.
1480 sync.origin_timestamp);
1482 if (domain->last_ptp_sync_time != 0
1483 && domain->last_ptp_sync_time >= sync->sync_send_time_remote) {
1484 GST_WARNING ("Backwards PTP times in domain %u: %" GST_TIME_FORMAT " >= %"
1485 GST_TIME_FORMAT, domain->domain,
1486 GST_TIME_ARGS (domain->last_ptp_sync_time),
1487 GST_TIME_ARGS (sync->sync_send_time_remote));
1488 ptp_pending_sync_free (sync);
1492 domain->last_ptp_sync_time = sync->sync_send_time_remote;
1494 if (send_delay_req (domain, sync)) {
1495 /* Sent delay request */
1497 update_ptp_time (domain, sync);
1498 ptp_pending_sync_free (sync);
1504 g_queue_push_tail (&domain->pending_syncs, sync);
1508 handle_follow_up_message (PtpMessage * msg, GstClockTime receive_time)
1511 PtpDomainData *domain = NULL;
1512 PtpPendingSync *sync = NULL;
1514 /* Don't consider messages with the alternate master flag set */
1515 if ((msg->flag_field & 0x0100))
1518 for (l = domain_data; l; l = l->next) {
1519 PtpDomainData *tmp = l->data;
1521 if (msg->domain_number == tmp->domain) {
1530 /* If we have a master clock, ignore this message if it's not coming from there */
1531 if (domain->have_master_clock
1532 && compare_clock_identity (&domain->master_clock_identity,
1533 &msg->source_port_identity) != 0)
1536 /* Check if we know about this one */
1537 for (l = domain->pending_syncs.head; l; l = l->next) {
1538 PtpPendingSync *tmp = l->data;
1540 if (tmp->sync_seqnum == msg->sequence_id) {
1549 /* Got a FOLLOW_UP for this already */
1550 if (sync->sync_send_time_remote != GST_CLOCK_TIME_NONE)
1553 if (sync->sync_recv_time_local >= receive_time) {
1554 GST_ERROR ("Got bogus follow up in domain %u: %" GST_TIME_FORMAT " > %"
1555 GST_TIME_FORMAT, domain->domain,
1556 GST_TIME_ARGS (sync->sync_recv_time_local),
1557 GST_TIME_ARGS (receive_time));
1558 g_queue_remove (&domain->pending_syncs, sync);
1559 ptp_pending_sync_free (sync);
1563 sync->correction_field_sync += msg->correction_field;
1564 sync->sync_send_time_remote =
1565 PTP_TIMESTAMP_TO_GST_CLOCK_TIME (msg->message_specific.
1566 follow_up.precise_origin_timestamp);
1567 sync->follow_up_recv_time_local = receive_time;
1569 if (domain->last_ptp_sync_time >= sync->sync_send_time_remote) {
1570 GST_WARNING ("Backwards PTP times in domain %u: %" GST_TIME_FORMAT " >= %"
1571 GST_TIME_FORMAT, domain->domain,
1572 GST_TIME_ARGS (domain->last_ptp_sync_time),
1573 GST_TIME_ARGS (sync->sync_send_time_remote));
1574 g_queue_remove (&domain->pending_syncs, sync);
1575 ptp_pending_sync_free (sync);
1579 domain->last_ptp_sync_time = sync->sync_send_time_remote;
1581 if (send_delay_req (domain, sync)) {
1582 /* Sent delay request */
1584 update_ptp_time (domain, sync);
1585 g_queue_remove (&domain->pending_syncs, sync);
1586 ptp_pending_sync_free (sync);
1592 handle_delay_resp_message (PtpMessage * msg, GstClockTime receive_time)
1595 PtpDomainData *domain = NULL;
1596 PtpPendingSync *sync = NULL;
1598 /* Don't consider messages with the alternate master flag set */
1599 if ((msg->flag_field & 0x0100))
1602 for (l = domain_data; l; l = l->next) {
1603 PtpDomainData *tmp = l->data;
1605 if (msg->domain_number == tmp->domain) {
1614 /* If we have a master clock, ignore this message if it's not coming from there */
1615 if (domain->have_master_clock
1616 && compare_clock_identity (&domain->master_clock_identity,
1617 &msg->source_port_identity) != 0)
1621 if (msg->message_specific.delay_resp.
1622 requesting_port_identity.clock_identity != ptp_clock_id.clock_identity
1623 || msg->message_specific.delay_resp.
1624 requesting_port_identity.port_number != ptp_clock_id.port_number)
1627 domain->min_delay_req_interval =
1628 log2_to_clock_time (msg->log_message_interval);
1630 /* Check if we know about this one */
1631 for (l = domain->pending_syncs.head; l; l = l->next) {
1632 PtpPendingSync *tmp = l->data;
1634 if (tmp->delay_req_seqnum == msg->sequence_id) {
1643 /* Got a DELAY_RESP for this already */
1644 if (sync->delay_req_recv_time_remote != GST_CLOCK_TIME_NONE)
1647 if (sync->delay_req_send_time_local > receive_time) {
1648 GST_ERROR ("Got bogus delay response in domain %u: %" GST_TIME_FORMAT " > %"
1649 GST_TIME_FORMAT, domain->domain,
1650 GST_TIME_ARGS (sync->delay_req_send_time_local),
1651 GST_TIME_ARGS (receive_time));
1652 g_queue_remove (&domain->pending_syncs, sync);
1653 ptp_pending_sync_free (sync);
1657 sync->correction_field_delay = msg->correction_field;
1659 sync->delay_req_recv_time_remote =
1660 PTP_TIMESTAMP_TO_GST_CLOCK_TIME (msg->message_specific.
1661 delay_resp.receive_timestamp);
1662 sync->delay_resp_recv_time_local = receive_time;
1664 if (domain->mean_path_delay != 0
1665 && sync->sync_send_time_remote > sync->delay_req_recv_time_remote) {
1666 GST_WARNING ("Sync send time after delay req receive time for domain %u: %"
1667 GST_TIME_FORMAT " > %" GST_TIME_FORMAT, domain->domain,
1668 GST_TIME_ARGS (sync->sync_send_time_remote),
1669 GST_TIME_ARGS (sync->delay_req_recv_time_remote));
1670 g_queue_remove (&domain->pending_syncs, sync);
1671 ptp_pending_sync_free (sync);
1675 if (update_mean_path_delay (domain, sync))
1676 update_ptp_time (domain, sync);
1677 g_queue_remove (&domain->pending_syncs, sync);
1678 ptp_pending_sync_free (sync);
1682 handle_ptp_message (PtpMessage * msg, GstClockTime receive_time)
1684 /* Ignore our own messages */
1685 if (msg->source_port_identity.clock_identity == ptp_clock_id.clock_identity &&
1686 msg->source_port_identity.port_number == ptp_clock_id.port_number)
1689 switch (msg->message_type) {
1690 case PTP_MESSAGE_TYPE_ANNOUNCE:
1691 handle_announce_message (msg, receive_time);
1693 case PTP_MESSAGE_TYPE_SYNC:
1694 handle_sync_message (msg, receive_time);
1696 case PTP_MESSAGE_TYPE_FOLLOW_UP:
1697 handle_follow_up_message (msg, receive_time);
1699 case PTP_MESSAGE_TYPE_DELAY_RESP:
1700 handle_delay_resp_message (msg, receive_time);
1708 have_stdin_data_cb (GIOChannel * channel, GIOCondition condition,
1717 if ((condition & G_IO_STATUS_EOF)) {
1718 GST_ERROR ("Got EOF on stdin");
1719 g_main_loop_quit (main_loop);
1720 return G_SOURCE_REMOVE;
1724 g_io_channel_read_chars (channel, (gchar *) & header, sizeof (header),
1726 if (status == G_IO_STATUS_ERROR) {
1727 GST_ERROR ("Failed to read from stdin: %s", err->message);
1728 g_main_loop_quit (main_loop);
1729 return G_SOURCE_REMOVE;
1730 } else if (status == G_IO_STATUS_EOF) {
1731 GST_ERROR ("Got EOF on stdin");
1732 g_main_loop_quit (main_loop);
1733 return G_SOURCE_REMOVE;
1734 } else if (status != G_IO_STATUS_NORMAL) {
1735 GST_ERROR ("Unexpected stdin read status: %d", status);
1736 g_main_loop_quit (main_loop);
1737 return G_SOURCE_REMOVE;
1738 } else if (read != sizeof (header)) {
1739 GST_ERROR ("Unexpected read size: %" G_GSIZE_FORMAT, read);
1740 g_main_loop_quit (main_loop);
1741 return G_SOURCE_REMOVE;
1742 } else if (header.size > 8192) {
1743 GST_ERROR ("Unexpected size: %u", header.size);
1744 g_main_loop_quit (main_loop);
1745 return G_SOURCE_REMOVE;
1748 status = g_io_channel_read_chars (channel, buffer, header.size, &read, &err);
1749 if (status == G_IO_STATUS_ERROR) {
1750 GST_ERROR ("Failed to read from stdin: %s", err->message);
1751 g_main_loop_quit (main_loop);
1752 return G_SOURCE_REMOVE;
1753 } else if (status == G_IO_STATUS_EOF) {
1754 GST_ERROR ("EOF on stdin");
1755 g_main_loop_quit (main_loop);
1756 return G_SOURCE_REMOVE;
1757 } else if (status != G_IO_STATUS_NORMAL) {
1758 GST_ERROR ("Unexpected stdin read status: %d", status);
1759 g_main_loop_quit (main_loop);
1760 return G_SOURCE_REMOVE;
1761 } else if (read != header.size) {
1762 GST_ERROR ("Unexpected read size: %" G_GSIZE_FORMAT, read);
1763 g_main_loop_quit (main_loop);
1764 return G_SOURCE_REMOVE;
1767 switch (header.type) {
1770 GstClockTime receive_time = gst_util_get_timestamp ();
1773 if (parse_ptp_message (&msg, (const guint8 *) buffer, header.size)) {
1774 dump_ptp_message (&msg);
1775 handle_ptp_message (&msg, receive_time);
1780 case TYPE_CLOCK_ID:{
1781 if (header.size != 8) {
1782 GST_ERROR ("Unexpected clock id size (%u != 8)", header.size);
1783 g_main_loop_quit (main_loop);
1784 return G_SOURCE_REMOVE;
1786 g_mutex_lock (&ptp_lock);
1787 ptp_clock_id.clock_identity = GST_READ_UINT64_BE (buffer);
1788 ptp_clock_id.port_number = getpid ();
1789 GST_DEBUG ("Got clock id 0x%016" G_GINT64_MODIFIER "x %u",
1790 ptp_clock_id.clock_identity, ptp_clock_id.port_number);
1791 g_cond_signal (&ptp_cond);
1792 g_mutex_unlock (&ptp_lock);
1797 return G_SOURCE_CONTINUE;
1800 /* Cleanup all announce messages and announce message senders
1801 * that are timed out by now, and clean up all pending syncs
1802 * that are missing their FOLLOW_UP or DELAY_RESP */
1804 cleanup_cb (gpointer data)
1806 GstClockTime now = gst_util_get_timestamp ();
1809 for (l = domain_data; l; l = l->next) {
1810 PtpDomainData *domain = l->data;
1812 for (n = domain->announce_senders; n;) {
1813 PtpAnnounceSender *sender = n->data;
1814 gboolean timed_out = TRUE;
1816 /* Keep only 5 messages per sender around */
1817 while (g_queue_get_length (&sender->announce_messages) > 5) {
1818 PtpAnnounceMessage *msg = g_queue_pop_head (&sender->announce_messages);
1822 for (m = sender->announce_messages.head; m; m = m->next) {
1823 PtpAnnounceMessage *msg = m->data;
1825 if (msg->receive_time +
1826 sender->announce_interval * PTP_ANNOUNCE_RECEIPT_TIMEOUT > now) {
1833 GST_DEBUG ("Announce sender 0x%016" G_GINT64_MODIFIER "x %u timed out",
1834 sender->master_clock_identity.clock_identity,
1835 sender->master_clock_identity.port_number);
1836 g_queue_foreach (&sender->announce_messages, (GFunc) g_free, NULL);
1837 g_queue_clear (&sender->announce_messages);
1840 if (g_queue_get_length (&sender->announce_messages) == 0) {
1841 GList *tmp = n->next;
1843 if (compare_clock_identity (&sender->master_clock_identity,
1844 &domain->master_clock_identity) == 0)
1845 GST_WARNING ("currently selected master clock timed out");
1847 domain->announce_senders =
1848 g_list_delete_link (domain->announce_senders, n);
1854 select_best_master_clock (domain, now);
1856 /* Clean up any pending syncs */
1857 for (n = domain->pending_syncs.head; n;) {
1858 PtpPendingSync *sync = n->data;
1859 gboolean timed_out = FALSE;
1861 /* Time out pending syncs after 4 sync intervals or 10 seconds,
1862 * and pending delay reqs after 4 delay req intervals or 10 seconds
1864 if (sync->delay_req_send_time_local != GST_CLOCK_TIME_NONE &&
1865 ((domain->min_delay_req_interval != 0
1866 && sync->delay_req_send_time_local +
1867 4 * domain->min_delay_req_interval < now)
1868 || (sync->delay_req_send_time_local + 10 * GST_SECOND < now))) {
1870 } else if ((domain->sync_interval != 0
1871 && sync->sync_recv_time_local + 4 * domain->sync_interval < now)
1872 || (sync->sync_recv_time_local + 10 * GST_SECOND < now)) {
1877 GList *tmp = n->next;
1878 ptp_pending_sync_free (sync);
1879 g_queue_delete_link (&domain->pending_syncs, n);
1887 return G_SOURCE_CONTINUE;
1891 ptp_helper_main (gpointer data)
1893 GSource *cleanup_source;
1895 GST_DEBUG ("Starting PTP helper loop");
1897 /* Check all 5 seconds, if we have to cleanup ANNOUNCE or pending syncs message */
1898 cleanup_source = g_timeout_source_new_seconds (5);
1899 g_source_set_priority (cleanup_source, G_PRIORITY_DEFAULT);
1900 g_source_set_callback (cleanup_source, (GSourceFunc) cleanup_cb, NULL, NULL);
1901 g_source_attach (cleanup_source, main_context);
1902 g_source_unref (cleanup_source);
1904 g_main_loop_run (main_loop);
1905 GST_DEBUG ("Stopped PTP helper loop");
1907 g_mutex_lock (&ptp_lock);
1908 ptp_clock_id.clock_identity = GST_PTP_CLOCK_ID_NONE;
1909 ptp_clock_id.port_number = 0;
1911 g_cond_signal (&ptp_cond);
1912 g_mutex_unlock (&ptp_lock);
1918 * gst_ptp_is_supported:
1920 * Check if PTP clocks are generally supported on this system, and if previous
1921 * initializations did not fail.
1923 * Returns: %TRUE if PTP clocks are generally supported on this system, and
1924 * previous initializations did not fail.
1929 gst_ptp_is_supported (void)
1935 * gst_ptp_is_initialized:
1937 * Check if the GStreamer PTP clock subsystem is initialized.
1939 * Returns: %TRUE if the GStreamer PTP clock subsystem is intialized.
1944 gst_ptp_is_initialized (void)
1951 * @clock_id: PTP clock id of this process' clock or %GST_PTP_CLOCK_ID_NONE
1952 * @interfaces: (transfer none) (array zero-terminated=1): network interfaces to run the clock on
1954 * Initialize the GStreamer PTP subsystem and create a PTP ordinary clock in
1955 * slave-only mode for all domains on the given @interfaces with the
1958 * If @clock_id is %GST_PTP_CLOCK_ID_NONE, a clock id is automatically
1959 * generated from the MAC address of the first network interface.
1962 * This function is automatically called by gst_ptp_clock_new() with default
1963 * parameters if it wasn't called before.
1965 * Returns: %TRUE if the GStreamer PTP clock subsystem could be initialized.
1970 gst_ptp_init (guint64 clock_id, gchar ** interfaces)
1974 gchar **argv = NULL;
1978 GSource *stdin_source;
1980 GST_DEBUG_CATEGORY_INIT (ptp_debug, "ptp", 0, "PTP clock");
1982 g_mutex_lock (&ptp_lock);
1984 GST_ERROR ("PTP not supported");
1990 GST_DEBUG ("PTP already initialized");
1995 if (ptp_helper_pid) {
1996 GST_DEBUG ("PTP currently initializing");
2000 if (!domain_stats_hooks_initted) {
2001 g_hook_list_init (&domain_stats_hooks, sizeof (GHook));
2002 domain_stats_hooks_initted = TRUE;
2006 if (clock_id != GST_PTP_CLOCK_ID_NONE)
2008 if (interfaces != NULL)
2009 argc += 2 * g_strv_length (interfaces);
2011 argv = g_new0 (gchar *, argc + 2);
2014 env = g_getenv ("GST_PTP_HELPER_1_0");
2016 env = g_getenv ("GST_PTP_HELPER");
2017 if (env != NULL && *env != '\0') {
2018 GST_LOG ("Trying GST_PTP_HELPER env var: %s", env);
2019 argv[argc_c++] = g_strdup (env);
2021 argv[argc_c++] = g_strdup (GST_PTP_HELPER_INSTALLED);
2024 if (clock_id != GST_PTP_CLOCK_ID_NONE) {
2025 argv[argc_c++] = g_strdup ("-c");
2026 argv[argc_c++] = g_strdup_printf ("0x%016" G_GINT64_MODIFIER "x", clock_id);
2029 if (interfaces != NULL) {
2030 gchar **ptr = interfaces;
2033 argv[argc_c++] = g_strdup ("-i");
2034 argv[argc_c++] = g_strdup (*ptr);
2039 main_context = g_main_context_new ();
2040 main_loop = g_main_loop_new (main_context, FALSE);
2043 g_thread_try_new ("ptp-helper-thread", ptp_helper_main, NULL, &err);
2044 if (!ptp_helper_thread) {
2045 GST_ERROR ("Failed to start PTP helper thread: %s", err->message);
2046 g_clear_error (&err);
2051 if (!g_spawn_async_with_pipes (NULL, argv, NULL, 0, NULL, NULL,
2052 &ptp_helper_pid, &fd_w, &fd_r, NULL, &err)) {
2053 GST_ERROR ("Failed to start ptp helper process: %s", err->message);
2054 g_clear_error (&err);
2060 stdin_channel = g_io_channel_unix_new (fd_r);
2061 g_io_channel_set_encoding (stdin_channel, NULL, NULL);
2062 g_io_channel_set_buffered (stdin_channel, FALSE);
2063 g_io_channel_set_close_on_unref (stdin_channel, TRUE);
2065 g_io_create_watch (stdin_channel, G_IO_IN | G_IO_PRI | G_IO_HUP);
2066 g_source_set_priority (stdin_source, G_PRIORITY_DEFAULT);
2067 g_source_set_callback (stdin_source, (GSourceFunc) have_stdin_data_cb, NULL,
2069 g_source_attach (stdin_source, main_context);
2070 g_source_unref (stdin_source);
2072 /* Create stdout channel */
2073 stdout_channel = g_io_channel_unix_new (fd_w);
2074 g_io_channel_set_encoding (stdout_channel, NULL, NULL);
2075 g_io_channel_set_close_on_unref (stdout_channel, TRUE);
2076 g_io_channel_set_buffered (stdout_channel, FALSE);
2078 delay_req_rand = g_rand_new ();
2083 GST_DEBUG ("Waiting for PTP to be initialized");
2085 while (ptp_clock_id.clock_identity == GST_PTP_CLOCK_ID_NONE && initted)
2086 g_cond_wait (&ptp_cond, &ptp_lock);
2090 GST_DEBUG ("Initialized and got clock id 0x%016" G_GINT64_MODIFIER "x %u",
2091 ptp_clock_id.clock_identity, ptp_clock_id.port_number);
2093 GST_ERROR ("Failed to initialize");
2101 if (ptp_helper_pid) {
2102 kill (ptp_helper_pid, SIGKILL);
2103 waitpid (ptp_helper_pid, NULL, 0);
2104 g_spawn_close_pid (ptp_helper_pid);
2109 g_io_channel_unref (stdin_channel);
2110 stdin_channel = NULL;
2112 g_io_channel_unref (stdout_channel);
2113 stdout_channel = NULL;
2115 if (main_loop && ptp_helper_thread) {
2116 g_main_loop_quit (main_loop);
2117 g_thread_join (ptp_helper_thread);
2119 ptp_helper_thread = NULL;
2121 g_main_loop_unref (main_loop);
2124 g_main_context_unref (main_context);
2125 main_context = NULL;
2128 g_rand_free (delay_req_rand);
2129 delay_req_rand = NULL;
2132 g_mutex_unlock (&ptp_lock);
2140 * Deinitialize the GStreamer PTP subsystem and stop the PTP clock. If there
2141 * are any remaining GstPtpClock instances, they won't be further synchronized
2142 * to the PTP network clock.
2147 gst_ptp_deinit (void)
2151 g_mutex_lock (&ptp_lock);
2153 if (ptp_helper_pid) {
2154 kill (ptp_helper_pid, SIGKILL);
2155 waitpid (ptp_helper_pid, NULL, 0);
2156 g_spawn_close_pid (ptp_helper_pid);
2161 g_io_channel_unref (stdin_channel);
2162 stdin_channel = NULL;
2164 g_io_channel_unref (stdout_channel);
2165 stdout_channel = NULL;
2167 if (main_loop && ptp_helper_thread) {
2168 GThread *tmp = ptp_helper_thread;
2169 ptp_helper_thread = NULL;
2170 g_mutex_unlock (&ptp_lock);
2171 g_main_loop_quit (main_loop);
2172 g_thread_join (tmp);
2173 g_mutex_lock (&ptp_lock);
2176 g_main_loop_unref (main_loop);
2179 g_main_context_unref (main_context);
2180 main_context = NULL;
2183 g_rand_free (delay_req_rand);
2184 delay_req_rand = NULL;
2186 for (l = domain_data; l; l = l->next) {
2187 PtpDomainData *domain = l->data;
2189 for (m = domain->announce_senders; m; m = m->next) {
2190 PtpAnnounceSender *sender = m->data;
2192 g_queue_foreach (&sender->announce_messages, (GFunc) g_free, NULL);
2193 g_queue_clear (&sender->announce_messages);
2196 g_list_free (domain->announce_senders);
2198 g_queue_foreach (&domain->pending_syncs, (GFunc) ptp_pending_sync_free,
2200 g_queue_clear (&domain->pending_syncs);
2201 gst_object_unref (domain->domain_clock);
2204 g_list_free (domain_data);
2206 g_list_foreach (domain_clocks, (GFunc) g_free, NULL);
2207 g_list_free (domain_clocks);
2208 domain_clocks = NULL;
2210 ptp_clock_id.clock_identity = GST_PTP_CLOCK_ID_NONE;
2211 ptp_clock_id.port_number = 0;
2215 g_mutex_unlock (&ptp_lock);
2218 #define DEFAULT_DOMAIN 0
2227 #define GST_PTP_CLOCK_GET_PRIVATE(obj) \
2228 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_PTP_CLOCK, GstPtpClockPrivate))
2230 struct _GstPtpClockPrivate
2233 GstClock *domain_clock;
2234 gulong domain_stats_id;
2237 #define gst_ptp_clock_parent_class parent_class
2238 G_DEFINE_TYPE (GstPtpClock, gst_ptp_clock, GST_TYPE_SYSTEM_CLOCK);
2240 static void gst_ptp_clock_set_property (GObject * object, guint prop_id,
2241 const GValue * value, GParamSpec * pspec);
2242 static void gst_ptp_clock_get_property (GObject * object, guint prop_id,
2243 GValue * value, GParamSpec * pspec);
2244 static void gst_ptp_clock_finalize (GObject * object);
2246 static GstClockTime gst_ptp_clock_get_internal_time (GstClock * clock);
2249 gst_ptp_clock_class_init (GstPtpClockClass * klass)
2251 GObjectClass *gobject_class;
2252 GstClockClass *clock_class;
2254 gobject_class = G_OBJECT_CLASS (klass);
2255 clock_class = GST_CLOCK_CLASS (klass);
2257 g_type_class_add_private (klass, sizeof (GstPtpClockPrivate));
2259 gobject_class->finalize = gst_ptp_clock_finalize;
2260 gobject_class->get_property = gst_ptp_clock_get_property;
2261 gobject_class->set_property = gst_ptp_clock_set_property;
2263 g_object_class_install_property (gobject_class, PROP_DOMAIN,
2264 g_param_spec_uint ("domain", "Domain",
2265 "The PTP domain", 0, G_MAXUINT8,
2267 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
2269 g_object_class_install_property (gobject_class, PROP_INTERNAL_CLOCK,
2270 g_param_spec_object ("internal-clock", "Internal Clock",
2271 "Internal clock", GST_TYPE_CLOCK,
2272 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2274 clock_class->get_internal_time = gst_ptp_clock_get_internal_time;
2278 gst_ptp_clock_init (GstPtpClock * self)
2280 GstPtpClockPrivate *priv;
2282 self->priv = priv = GST_PTP_CLOCK_GET_PRIVATE (self);
2284 GST_OBJECT_FLAG_SET (self, GST_CLOCK_FLAG_CAN_SET_MASTER);
2285 GST_OBJECT_FLAG_SET (self, GST_CLOCK_FLAG_NEEDS_STARTUP_SYNC);
2287 priv->domain = DEFAULT_DOMAIN;
2291 gst_ptp_clock_ensure_domain_clock (GstPtpClock * self)
2293 gboolean got_clock = TRUE;
2295 if (G_UNLIKELY (!self->priv->domain_clock)) {
2296 g_mutex_lock (&domain_clocks_lock);
2297 if (!self->priv->domain_clock) {
2302 for (l = domain_clocks; l; l = l->next) {
2303 PtpDomainData *clock_data = l->data;
2305 if (clock_data->domain == self->priv->domain
2306 && clock_data->last_ptp_time != 0) {
2307 self->priv->domain_clock = clock_data->domain_clock;
2313 g_mutex_unlock (&domain_clocks_lock);
2315 g_object_notify (G_OBJECT (self), "internal-clock");
2316 gst_clock_set_synced (GST_CLOCK (self), TRUE);
2324 gst_ptp_clock_stats_callback (guint8 domain, const GstStructure * stats,
2327 GstPtpClock *self = user_data;
2329 if (domain != self->priv->domain
2330 || !gst_structure_has_name (stats, GST_PTP_STATISTICS_TIME_UPDATED))
2333 /* Let's set our internal clock */
2334 if (!gst_ptp_clock_ensure_domain_clock (self))
2337 self->priv->domain_stats_id = 0;
2343 gst_ptp_clock_set_property (GObject * object, guint prop_id,
2344 const GValue * value, GParamSpec * pspec)
2346 GstPtpClock *self = GST_PTP_CLOCK (object);
2350 self->priv->domain = g_value_get_uint (value);
2351 gst_ptp_clock_ensure_domain_clock (self);
2352 if (!self->priv->domain_clock)
2353 self->priv->domain_stats_id =
2354 gst_ptp_statistics_callback_add (gst_ptp_clock_stats_callback, self,
2358 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2364 gst_ptp_clock_get_property (GObject * object, guint prop_id,
2365 GValue * value, GParamSpec * pspec)
2367 GstPtpClock *self = GST_PTP_CLOCK (object);
2371 g_value_set_uint (value, self->priv->domain);
2373 case PROP_INTERNAL_CLOCK:
2374 gst_ptp_clock_ensure_domain_clock (self);
2375 g_value_set_object (value, self->priv->domain_clock);
2378 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2384 gst_ptp_clock_finalize (GObject * object)
2386 GstPtpClock *self = GST_PTP_CLOCK (object);
2388 if (self->priv->domain_stats_id)
2389 gst_ptp_statistics_callback_remove (self->priv->domain_stats_id);
2391 G_OBJECT_CLASS (gst_ptp_clock_parent_class)->finalize (object);
2395 gst_ptp_clock_get_internal_time (GstClock * clock)
2397 GstPtpClock *self = GST_PTP_CLOCK (clock);
2399 gst_ptp_clock_ensure_domain_clock (self);
2401 if (!self->priv->domain_clock) {
2402 GST_ERROR_OBJECT (self, "Domain %u has no clock yet and is not synced",
2403 self->priv->domain);
2404 return GST_CLOCK_TIME_NONE;
2407 return gst_clock_get_time (self->priv->domain_clock);
2411 * gst_ptp_clock_new:
2412 * @name: Name of the clock
2413 * @domain: PTP domain
2415 * Creates a new PTP clock instance that exports the PTP time of the master
2416 * clock in @domain. This clock can be slaved to other clocks as needed.
2418 * If gst_ptp_init() was not called before, this will call gst_ptp_init() with
2419 * default parameters.
2422 * This clock only returns valid timestamps after it received the first
2423 * times from the PTP master clock on the network. Once this happens the
2424 * GstPtpClock::internal-clock property will become non-NULL. You can connect
2425 * to the notify::internal-clock signal to get notified about this, or
2426 * alternatively use gst_ptp_clock_wait_ready() to wait for this to happen.
2431 gst_ptp_clock_new (const gchar * name, guint domain)
2433 g_return_val_if_fail (name != NULL, NULL);
2434 g_return_val_if_fail (domain <= G_MAXUINT8, NULL);
2436 if (!initted && !gst_ptp_init (GST_PTP_CLOCK_ID_NONE, NULL)) {
2437 GST_ERROR ("Failed to initialize PTP");
2441 return g_object_new (GST_TYPE_PTP_CLOCK, "name", name, "domain", domain,
2448 const GstStructure *stats;
2449 } DomainStatsMarshalData;
2452 domain_stats_marshaller (GHook * hook, DomainStatsMarshalData * data)
2454 GstPtpStatisticsCallback callback = (GstPtpStatisticsCallback) hook->func;
2456 if (!callback (data->domain, data->stats, hook->data))
2457 g_hook_destroy (&domain_stats_hooks, hook->hook_id);
2461 emit_ptp_statistics (guint8 domain, const GstStructure * stats)
2463 DomainStatsMarshalData data = { domain, stats };
2465 g_mutex_lock (&ptp_lock);
2466 g_hook_list_marshal (&domain_stats_hooks, TRUE,
2467 (GHookMarshaller) domain_stats_marshaller, &data);
2468 g_mutex_unlock (&ptp_lock);
2472 * gst_ptp_statistics_callback_add:
2473 * @callback: GstPtpStatisticsCallback to call
2474 * @user_data: Data to pass to the callback
2475 * @destroy_data: GDestroyNotify to destroy the data
2477 * Installs a new statistics callback for gathering PTP statistics. See
2478 * GstPtpStatisticsCallback for a list of statistics that are provided.
2480 * Returns: Id for the callback that can be passed to
2481 * gst_ptp_statistics_callback_remove()
2486 gst_ptp_statistics_callback_add (GstPtpStatisticsCallback callback,
2487 gpointer user_data, GDestroyNotify destroy_data)
2491 g_mutex_lock (&ptp_lock);
2493 if (!domain_stats_hooks_initted) {
2494 g_hook_list_init (&domain_stats_hooks, sizeof (GHook));
2495 domain_stats_hooks_initted = TRUE;
2498 hook = g_hook_alloc (&domain_stats_hooks);
2499 hook->func = callback;
2500 hook->data = user_data;
2501 hook->destroy = destroy_data;
2502 g_hook_prepend (&domain_stats_hooks, hook);
2503 g_atomic_int_add (&domain_stats_n_hooks, 1);
2505 g_mutex_unlock (&ptp_lock);
2507 return hook->hook_id;
2511 * gst_ptp_statistics_callback_remove:
2512 * @id: Callback id to remove
2514 * Removes a PTP statistics callback that was previously added with
2515 * gst_ptp_statistics_callback_add().
2520 gst_ptp_statistics_callback_remove (gulong id)
2522 g_mutex_lock (&ptp_lock);
2523 if (g_hook_destroy (&domain_stats_hooks, id))
2524 g_atomic_int_add (&domain_stats_n_hooks, -1);
2525 g_mutex_unlock (&ptp_lock);
2528 #else /* HAVE_PTP */
2531 gst_ptp_clock_get_type (void)
2533 return G_TYPE_INVALID;
2537 gst_ptp_is_supported (void)
2543 gst_ptp_is_initialized (void)
2549 gst_ptp_init (guint64 clock_id, gchar ** interfaces)
2555 gst_ptp_deinit (void)
2560 gst_ptp_clock_new (const gchar * name, guint domain)
2566 gst_ptp_clock_wait_ready (GstPtpClock * self, GstClockTime timeout)
2572 gst_ptp_statistics_callback_add (GstPtpStatisticsCallback callback,
2573 gpointer user_data, GDestroyNotify destroy_data)
2579 gst_ptp_statistics_callback_remove (gulong id)