From c88bfc0b3e09e7d53672efbb45472b0986238b77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Tue, 15 Mar 2022 01:35:17 +0000 Subject: [PATCH] rtpjitterbuffer: add "add-reference-timestamp-meta" property When syncing to an RFC7273 clock this will add the original reconstructed reference clock timestamp to buffers in form of a GstReferenceTimestampMeta. This is useful when we want to process or analyse data based on the original timestamps untainted by any local adjustments, for example reconstruct AES67 audio streams with sample accuracy. Part-of: --- .../docs/gst_plugins_cache.json | 12 ++++ .../gst/rtpmanager/gstrtpjitterbuffer.c | 57 ++++++++++++++++++- .../gst/rtpmanager/rtpjitterbuffer.c | 6 +- .../gst/rtpmanager/rtpjitterbuffer.h | 2 +- 4 files changed, 73 insertions(+), 4 deletions(-) diff --git a/subprojects/gst-plugins-good/docs/gst_plugins_cache.json b/subprojects/gst-plugins-good/docs/gst_plugins_cache.json index 7dd832070a..f17a2575df 100644 --- a/subprojects/gst-plugins-good/docs/gst_plugins_cache.json +++ b/subprojects/gst-plugins-good/docs/gst_plugins_cache.json @@ -17523,6 +17523,18 @@ } }, "properties": { + "add-reference-timestamp-meta": { + "blurb": "Add Reference Timestamp Meta to buffers with the original clock timestamp before any adjustments when syncing to an RFC7273 clock.", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "false", + "mutable": "null", + "readable": true, + "type": "gboolean", + "writable": true + }, "do-lost": { "blurb": "Send an event downstream when a packet is lost", "conditionally-available": false, diff --git a/subprojects/gst-plugins-good/gst/rtpmanager/gstrtpjitterbuffer.c b/subprojects/gst-plugins-good/gst/rtpmanager/gstrtpjitterbuffer.c index db43cc8de0..3702a47292 100644 --- a/subprojects/gst-plugins-good/gst/rtpmanager/gstrtpjitterbuffer.c +++ b/subprojects/gst-plugins-good/gst/rtpmanager/gstrtpjitterbuffer.c @@ -153,6 +153,7 @@ enum #define DEFAULT_MAX_DROPOUT_TIME 60000 #define DEFAULT_MAX_MISORDER_TIME 2000 #define DEFAULT_RFC7273_SYNC FALSE +#define DEFAULT_ADD_REFERENCE_TIMESTAMP_META FALSE #define DEFAULT_FASTSTART_MIN_PACKETS 0 #define DEFAULT_AUTO_RTX_DELAY (20 * GST_MSECOND) @@ -186,6 +187,7 @@ enum PROP_MAX_DROPOUT_TIME, PROP_MAX_MISORDER_TIME, PROP_RFC7273_SYNC, + PROP_ADD_REFERENCE_TIMESTAMP_META, PROP_FASTSTART_MIN_PACKETS }; @@ -334,6 +336,10 @@ struct _GstRtpJitterBufferPrivate guint32 max_dropout_time; guint32 max_misorder_time; guint faststart_min_packets; + gboolean add_reference_timestamp_meta; + + /* Reference for GstReferenceTimestampMeta */ + GstCaps *rfc7273_ref; /* the last seqnum we pushed out */ guint32 last_popped_seqnum; @@ -885,6 +891,23 @@ gst_rtp_jitter_buffer_class_init (GstRtpJitterBufferClass * klass) "(requires clock and offset to be provided)", DEFAULT_RFC7273_SYNC, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * GstRtpJitterBuffer:add-reference-timestamp-meta: + * + * When syncing to a RFC7273 clock, add #GstReferenceTimestampMeta + * to buffers with the original reconstructed reference clock timestamp. + * + * Since: 1.22 + */ + g_object_class_install_property (gobject_class, + PROP_ADD_REFERENCE_TIMESTAMP_META, + g_param_spec_boolean ("add-reference-timestamp-meta", + "Add Reference Timestamp Meta", + "Add Reference Timestamp Meta to buffers with the original clock timestamp " + "before any adjustments when syncing to an RFC7273 clock.", + DEFAULT_ADD_REFERENCE_TIMESTAMP_META, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** * GstRtpJitterBuffer:faststart-min-packets * @@ -1388,6 +1411,7 @@ gst_jitter_buffer_sink_parse_caps (GstRtpJitterBuffer * jitterbuffer, gint payload = -1; GstClockTime tval; const gchar *ts_refclk, *mediaclk; + GstCaps *ts_meta_ref = NULL; priv = jitterbuffer->priv; @@ -1503,6 +1527,10 @@ gst_jitter_buffer_sink_parse_caps (GstRtpJitterBuffer * jitterbuffer, hostname = g_strdup (host); 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); + g_free (hostname); } } else if (g_str_has_prefix (ts_refclk, "ptp=IEEE1588-2008:")) { @@ -1514,6 +1542,10 @@ gst_jitter_buffer_sink_parse_caps (GstRtpJitterBuffer * jitterbuffer, domain = 0; clock = gst_ptp_clock_new (NULL, domain); + + ts_meta_ref = gst_caps_new_simple ("timestamp/x-ptp", + "version", G_TYPE_STRING, "IEEE1588-2008", + "domain", G_TYPE_INT, domain, NULL); } else { GST_FIXME_OBJECT (jitterbuffer, "Unsupported timestamp reference clock"); } @@ -1536,6 +1568,7 @@ gst_jitter_buffer_sink_parse_caps (GstRtpJitterBuffer * jitterbuffer, rtp_jitter_buffer_set_media_clock (priv->jbuf, NULL, -1); } + gst_caps_take (&priv->rfc7273_ref, ts_meta_ref); return TRUE; /* ERRORS */ @@ -1721,6 +1754,7 @@ gst_rtp_jitter_buffer_change_state (GstElement * element, JBUF_UNLOCK (priv); g_thread_join (priv->timer_thread); priv->timer_thread = NULL; + gst_clear_caps (&priv->rfc7273_ref); break; case GST_STATE_CHANGE_READY_TO_NULL: break; @@ -2914,6 +2948,7 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent, GstFlowReturn ret = GST_FLOW_OK; GstClockTime now; GstClockTime dts, pts; + GstClockTime ntp_time; guint64 latency_ts; gboolean head; gboolean duplicate; @@ -3073,7 +3108,7 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent, pts = rtp_jitter_buffer_calculate_pts (priv->jbuf, dts, estimated_dts, rtptime, gst_element_get_base_time (GST_ELEMENT_CAST (jitterbuffer)), - 0, FALSE); + 0, FALSE, &ntp_time); if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (pts))) { /* A valid timestamp cannot be calculated, discard packet */ @@ -3136,7 +3171,7 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent, pts = rtp_jitter_buffer_calculate_pts (priv->jbuf, dts, estimated_dts, rtptime, gst_element_get_base_time (GST_ELEMENT_CAST (jitterbuffer)), - gap, is_rtx); + gap, is_rtx, &ntp_time); if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (pts))) { /* A valid timestamp cannot be calculated, discard packet */ @@ -3248,6 +3283,14 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent, JBUF_SIGNAL_EVENT (priv); } } + // ntp_time will only be set in case of RFC7273 sync + if (priv->add_reference_timestamp_meta && GST_CLOCK_TIME_IS_VALID (ntp_time) + && priv->rfc7273_ref != NULL) { + buffer = gst_buffer_make_writable (buffer); + + gst_buffer_add_reference_timestamp_meta (buffer, + priv->rfc7273_ref, ntp_time, GST_CLOCK_TIME_NONE); + } /* If we estimated the DTS, don't consider it in the clock skew calculations * later. The code above always sets dts to pts or the other way around if @@ -4741,6 +4784,11 @@ gst_rtp_jitter_buffer_set_property (GObject * object, priv->faststart_min_packets = g_value_get_uint (value); JBUF_UNLOCK (priv); break; + case PROP_ADD_REFERENCE_TIMESTAMP_META: + JBUF_LOCK (priv); + priv->add_reference_timestamp_meta = g_value_get_boolean (value); + JBUF_UNLOCK (priv); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -4897,6 +4945,11 @@ gst_rtp_jitter_buffer_get_property (GObject * object, g_value_set_uint (value, priv->faststart_min_packets); JBUF_UNLOCK (priv); break; + case PROP_ADD_REFERENCE_TIMESTAMP_META: + JBUF_LOCK (priv); + g_value_set_boolean (value, priv->add_reference_timestamp_meta); + 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 5893c66673..af8cf637d3 100644 --- a/subprojects/gst-plugins-good/gst/rtpmanager/rtpjitterbuffer.c +++ b/subprojects/gst-plugins-good/gst/rtpmanager/rtpjitterbuffer.c @@ -703,7 +703,7 @@ queue_do_insert (RTPJitterBuffer * jbuf, GList * list, GList * item) GstClockTime rtp_jitter_buffer_calculate_pts (RTPJitterBuffer * jbuf, GstClockTime dts, gboolean estimated_dts, guint32 rtptime, GstClockTime base_time, - gint gap, gboolean is_rtx) + gint gap, gboolean is_rtx, GstClockTime * p_ntp_time) { guint64 ext_rtptime; GstClockTime gstrtptime, pts; @@ -711,6 +711,8 @@ rtp_jitter_buffer_calculate_pts (RTPJitterBuffer * jbuf, GstClockTime dts, guint64 media_clock_offset; gboolean rfc7273_mode; + *p_ntp_time = GST_CLOCK_TIME_NONE; + /* rtp time jumps are checked for during skew calculation, but bypassed * in other mode, so mind those here and reset jb if needed. * Only reset if valid input time, which is likely for UDP input @@ -951,6 +953,8 @@ rtp_jitter_buffer_calculate_pts (RTPJitterBuffer * jbuf, GstClockTime dts, GST_DEBUG ("RFC7273 packet NTP time %" GST_TIME_FORMAT " (RTP: %" G_GUINT64_FORMAT ")", GST_TIME_ARGS (ntptime), ntprtptime); + *p_ntp_time = ntptime; + /* 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 8accee4b40..d0e60c275d 100644 --- a/subprojects/gst-plugins-good/gst/rtpmanager/rtpjitterbuffer.h +++ b/subprojects/gst-plugins-good/gst/rtpmanager/rtpjitterbuffer.h @@ -210,7 +210,7 @@ void rtp_jitter_buffer_get_sync (RTPJitterBuffer *jbuf, GstClockTime rtp_jitter_buffer_calculate_pts (RTPJitterBuffer * jbuf, GstClockTime dts, gboolean estimated_dts, guint32 rtptime, GstClockTime base_time, gint gap, - gboolean is_rtx); + gboolean is_rtx, GstClockTime * p_ntp_time); gboolean rtp_jitter_buffer_can_fast_start (RTPJitterBuffer * jbuf, gint num_packet);