diff --git a/subprojects/gst-plugins-good/docs/gst_plugins_cache.json b/subprojects/gst-plugins-good/docs/gst_plugins_cache.json index 90fb2b8061..8001ddd6e7 100644 --- a/subprojects/gst-plugins-good/docs/gst_plugins_cache.json +++ b/subprojects/gst-plugins-good/docs/gst_plugins_cache.json @@ -18773,6 +18773,18 @@ "type": "gboolean", "writable": true }, + "rfc7273-use-system-clock": { + "blurb": "Use system clock as RFC7273 media clock (requires system clock to be synced externally)", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "false", + "mutable": "null", + "readable": true, + "type": "gboolean", + "writable": true + }, "rtx-deadline": { "blurb": "The deadline for a valid RTX request in milliseconds. (-1 automatic)", "conditionally-available": false, diff --git a/subprojects/gst-plugins-good/gst/rtpmanager/gstrtpjitterbuffer.c b/subprojects/gst-plugins-good/gst/rtpmanager/gstrtpjitterbuffer.c index 5c05386cc8..a030e9ae5f 100644 --- a/subprojects/gst-plugins-good/gst/rtpmanager/gstrtpjitterbuffer.c +++ b/subprojects/gst-plugins-good/gst/rtpmanager/gstrtpjitterbuffer.c @@ -158,6 +158,7 @@ enum #define DEFAULT_ADD_REFERENCE_TIMESTAMP_META FALSE #define DEFAULT_FASTSTART_MIN_PACKETS 0 #define DEFAULT_SYNC_INTERVAL 0 +#define DEFAULT_RFC7273_USE_SYSTEM_CLOCK FALSE #define DEFAULT_AUTO_RTX_DELAY (20 * GST_MSECOND) #define DEFAULT_AUTO_RTX_TIMEOUT (40 * GST_MSECOND) @@ -193,6 +194,7 @@ enum PROP_ADD_REFERENCE_TIMESTAMP_META, PROP_FASTSTART_MIN_PACKETS, PROP_SYNC_INTERVAL, + PROP_RFC7273_USE_SYSTEM_CLOCK, }; #define JBUF_LOCK(priv) G_STMT_START { \ @@ -337,6 +339,7 @@ struct _GstRtpJitterBufferPrivate guint faststart_min_packets; gboolean add_reference_timestamp_meta; guint sync_interval; + gboolean rfc7273_use_system_clock; /* Reference for GstReferenceTimestampMeta */ GstCaps *reference_timestamp_caps; @@ -997,6 +1000,33 @@ gst_rtp_jitter_buffer_class_init (GstRtpJitterBufferClass * klass) 0, G_MAXUINT, DEFAULT_SYNC_INTERVAL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * GstRtpJitterBuffer:rfc7273-use-system-clock: + * + * Uses the system clock as media clock in RFC7273 mode instead of + * instantiating an NTP or PTP clock. + * + * This will always provide the correct sender timestamps in the + * `GstReferenceTimestampMeta` as long as the system clock is synced to the + * actual media clock with at most a few seconds difference. + * + * PTS on outgoing buffers would be as accurate as the synchronization + * between the actual media clock and the system clock. + * + * This can be useful if only recovery of the original sender timestamps is + * needed and syncing to a PTP/NTP clock would be unnecessarily complex, or + * if the system clock already is synchronized to the correct clock and + * doing it another time inside GStreamer would be unnecessary. + * + * Since: 1.24 + */ + g_object_class_install_property (gobject_class, PROP_RFC7273_USE_SYSTEM_CLOCK, + g_param_spec_boolean ("rfc7273-use-system-clock", + "Use system clock as RFC7273 clock", + "Use system clock as RFC7273 media clock (requires system clock " + "to be synced externally)", DEFAULT_RFC7273_USE_SYSTEM_CLOCK, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** * GstRtpJitterBuffer::request-pt-map: * @buffer: the object which received the signal @@ -1128,6 +1158,7 @@ gst_rtp_jitter_buffer_init (GstRtpJitterBuffer * jitterbuffer) priv->faststart_min_packets = DEFAULT_FASTSTART_MIN_PACKETS; priv->add_reference_timestamp_meta = DEFAULT_ADD_REFERENCE_TIMESTAMP_META; priv->sync_interval = DEFAULT_SYNC_INTERVAL; + priv->rfc7273_use_system_clock = DEFAULT_RFC7273_USE_SYSTEM_CLOCK; priv->ts_offset_remainder = 0; priv->last_dts = -1; @@ -1594,12 +1625,16 @@ gst_jitter_buffer_sink_parse_caps (GstRtpJitterBuffer * jitterbuffer, GST_TIME_ARGS (priv->npt_start), GST_TIME_ARGS (priv->npt_stop)); if ((ts_refclk = gst_structure_get_string (caps_struct, "a-ts-refclk"))) { + gboolean use_system_clock; GstClock *clock = NULL; guint64 clock_offset = -1; + gint64 clock_correction = 0; GST_DEBUG_OBJECT (jitterbuffer, "Have timestamp reference clock %s", ts_refclk); + use_system_clock = priv->rfc7273_use_system_clock; + if (g_str_has_prefix (ts_refclk, "ntp=")) { if (g_str_has_prefix (ts_refclk, "ntp=/traceable/")) { GST_FIXME_OBJECT (jitterbuffer, "Can't handle traceable NTP clocks"); @@ -1629,7 +1664,15 @@ gst_jitter_buffer_sink_parse_caps (GstRtpJitterBuffer * jitterbuffer, else hostname = g_strdup (host); - clock = gst_ntp_clock_new (NULL, hostname, port, 0); + if (use_system_clock) { + clock = + g_object_new (GST_TYPE_SYSTEM_CLOCK, "clock-type", + GST_CLOCK_TYPE_REALTIME, NULL); + /* difference between UNIX epoch and NTP epoch */ + clock_correction = GST_RTP_NTP_UNIX_OFFSET * GST_SECOND; + } else { + clock = gst_ntp_clock_new (NULL, hostname, port, 0); + } ts_meta_ref = gst_caps_new_simple ("timestamp/x-ntp", "host", G_TYPE_STRING, hostname, "port", G_TYPE_INT, port, NULL); @@ -1644,7 +1687,15 @@ gst_jitter_buffer_sink_parse_caps (GstRtpJitterBuffer * jitterbuffer, if (domainstr[0] != ':' || sscanf (domainstr, ":%u", &domain) != 1) domain = 0; - clock = gst_ptp_clock_new (NULL, domain); + if (use_system_clock) { + clock = + g_object_new (GST_TYPE_SYSTEM_CLOCK, "clock-type", + GST_CLOCK_TYPE_REALTIME, NULL); + /* difference between UNIX and PTP/TAI (37 leap seconds as of October 2023) */ + clock_correction = 37 * GST_SECOND; + } else { + clock = gst_ptp_clock_new (NULL, domain); + } ts_meta_ref = gst_caps_new_simple ("timestamp/x-ptp", "version", G_TYPE_STRING, "IEEE1588-2008", @@ -1652,7 +1703,14 @@ gst_jitter_buffer_sink_parse_caps (GstRtpJitterBuffer * jitterbuffer, } else if (!g_strcmp0 (ts_refclk, "local")) { ts_meta_ref = gst_caps_new_empty_simple ("timestamp/x-ntp"); } else { - GST_FIXME_OBJECT (jitterbuffer, "Unsupported timestamp reference clock"); + if (use_system_clock) { + clock = + g_object_new (GST_TYPE_SYSTEM_CLOCK, "clock-type", + GST_CLOCK_TYPE_REALTIME, NULL); + } else { + GST_FIXME_OBJECT (jitterbuffer, + "Unsupported timestamp reference clock"); + } } if ((mediaclk = gst_structure_get_string (caps_struct, "a-mediaclk"))) { @@ -1668,9 +1726,10 @@ gst_jitter_buffer_sink_parse_caps (GstRtpJitterBuffer * jitterbuffer, } } - rtp_jitter_buffer_set_media_clock (priv->jbuf, clock, clock_offset); + rtp_jitter_buffer_set_media_clock (priv->jbuf, clock, clock_offset, + clock_correction); } else { - rtp_jitter_buffer_set_media_clock (priv->jbuf, NULL, -1); + rtp_jitter_buffer_set_media_clock (priv->jbuf, NULL, -1, 0); ts_meta_ref = gst_caps_new_empty_simple ("timestamp/x-ntp"); } @@ -5211,6 +5270,12 @@ gst_rtp_jitter_buffer_set_property (GObject * object, priv->sync_interval = g_value_get_uint (value); JBUF_UNLOCK (priv); break; + case PROP_RFC7273_USE_SYSTEM_CLOCK: + JBUF_LOCK (priv); + priv->rfc7273_use_system_clock = g_value_get_boolean (value); + JBUF_UNLOCK (priv); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -5377,6 +5442,11 @@ gst_rtp_jitter_buffer_get_property (GObject * object, g_value_set_uint (value, priv->sync_interval); JBUF_UNLOCK (priv); break; + case PROP_RFC7273_USE_SYSTEM_CLOCK: + JBUF_LOCK (priv); + g_value_set_boolean (value, priv->rfc7273_use_system_clock); + JBUF_UNLOCK (priv); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; diff --git a/subprojects/gst-plugins-good/gst/rtpmanager/rtpjitterbuffer.c b/subprojects/gst-plugins-good/gst/rtpmanager/rtpjitterbuffer.c index b4bf660b65..423b0d4d80 100644 --- a/subprojects/gst-plugins-good/gst/rtpmanager/rtpjitterbuffer.c +++ b/subprojects/gst-plugins-good/gst/rtpmanager/rtpjitterbuffer.c @@ -288,7 +288,7 @@ media_clock_synced_cb (GstClock * clock, gboolean synced, */ void rtp_jitter_buffer_set_media_clock (RTPJitterBuffer * jbuf, GstClock * clock, - guint64 clock_offset) + guint64 clock_offset, gint64 clock_correction) { g_mutex_lock (&jbuf->clock_lock); if (jbuf->media_clock) { @@ -300,6 +300,7 @@ rtp_jitter_buffer_set_media_clock (RTPJitterBuffer * jbuf, GstClock * clock, } jbuf->media_clock = clock; jbuf->media_clock_offset = clock_offset; + jbuf->media_clock_correction = clock_correction; if (jbuf->pipeline_clock && jbuf->media_clock) { if (same_clock (jbuf->pipeline_clock, jbuf->media_clock)) { @@ -763,6 +764,7 @@ rtp_jitter_buffer_calculate_pts (RTPJitterBuffer * jbuf, GstClockTime dts, GstClockTime gstrtptime, pts; GstClock *media_clock, *pipeline_clock; guint64 media_clock_offset; + gint64 media_clock_correction; gboolean rfc7273_mode; *p_ntp_time = GST_CLOCK_TIME_NONE; @@ -809,6 +811,7 @@ rtp_jitter_buffer_calculate_pts (RTPJitterBuffer * jbuf, GstClockTime dts, pipeline_clock = jbuf->pipeline_clock ? gst_object_ref (jbuf->pipeline_clock) : NULL; media_clock_offset = jbuf->media_clock_offset; + media_clock_correction = jbuf->media_clock_correction; g_mutex_unlock (&jbuf->clock_lock); gstrtptime = @@ -944,6 +947,7 @@ rtp_jitter_buffer_calculate_pts (RTPJitterBuffer * jbuf, GstClockTime dts, /* Current NTP clock estimation */ ntptime = gst_clock_get_internal_time (media_clock); + ntptime += media_clock_correction; /* Current RTP time based on the estimated NTP clock and the corresponding * RTP time period start */ @@ -1038,6 +1042,11 @@ rtp_jitter_buffer_calculate_pts (RTPJitterBuffer * jbuf, GstClockTime dts, *p_ntp_time = ntptime; + if (media_clock_correction < 0 || ntptime >= media_clock_correction) + ntptime -= media_clock_correction; + else + ntptime = 0; + /* Packet timestamp converted to the pipeline clock. * Note that this includes again inaccuracy caused by the estimation of * the NTP vs. pipeline clock. */ diff --git a/subprojects/gst-plugins-good/gst/rtpmanager/rtpjitterbuffer.h b/subprojects/gst-plugins-good/gst/rtpmanager/rtpjitterbuffer.h index d0e60c275d..3d052df47d 100644 --- a/subprojects/gst-plugins-good/gst/rtpmanager/rtpjitterbuffer.h +++ b/subprojects/gst-plugins-good/gst/rtpmanager/rtpjitterbuffer.h @@ -108,6 +108,7 @@ struct _RTPJitterBuffer { GstClock *media_clock; gulong media_clock_synced_id; guint64 media_clock_offset; + gint64 media_clock_correction; gboolean rfc7273_sync; }; @@ -172,7 +173,7 @@ void rtp_jitter_buffer_set_delay (RTPJitterBuffer *jbuf, void rtp_jitter_buffer_set_clock_rate (RTPJitterBuffer *jbuf, guint32 clock_rate); guint32 rtp_jitter_buffer_get_clock_rate (RTPJitterBuffer *jbuf); -void rtp_jitter_buffer_set_media_clock (RTPJitterBuffer *jbuf, GstClock * clock, guint64 clock_offset); +void rtp_jitter_buffer_set_media_clock (RTPJitterBuffer *jbuf, GstClock * clock, guint64 clock_offset, gint64 clock_correction); void rtp_jitter_buffer_set_pipeline_clock (RTPJitterBuffer *jbuf, GstClock * clock); gboolean rtp_jitter_buffer_get_rfc7273_sync (RTPJitterBuffer *jbuf);