rtspsrc: Optionally timestamp RTP packets with their receive times in TCP/HTTP mode
authorSebastian Dröge <sebastian@centricular.com>
Wed, 3 Apr 2024 12:01:52 +0000 (15:01 +0300)
committerGStreamer Marge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Fri, 29 Nov 2024 11:45:15 +0000 (11:45 +0000)
Until https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6509
this was accidentally done inside rtpjitterbuffer for many years, and
doing so potentially solves problems on some streams while introducing
problems on others.

Make this configurable on rtspsrc and default to not set timestamps.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8004>

subprojects/gst-plugins-good/docs/gst_plugins_cache.json
subprojects/gst-plugins-good/gst/rtsp/gstrtspsrc.c
subprojects/gst-plugins-good/gst/rtsp/gstrtspsrc.h

index 3458510c07b96b0672b499f01b3ae7b9ebc38222..4629e8683670c82f575b08c91581a66a3d522a28 100644 (file)
                         "type": "guint64",
                         "writable": true
                     },
+                    "tcp-timestamp": {
+                        "blurb": "Timestamp RTP packets with receive times in TCP/HTTP mode",
+                        "conditionally-available": false,
+                        "construct": false,
+                        "construct-only": false,
+                        "controllable": false,
+                        "default": "false",
+                        "mutable": "null",
+                        "readable": true,
+                        "type": "gboolean",
+                        "writable": true
+                    },
                     "teardown-timeout": {
                         "blurb": "When transitioning PAUSED-READY, allow up to timeout (in nanoseconds) delay in order to send teardown (0 = disabled)",
                         "conditionally-available": false,
index 8aaad5d56163b7244bd3fe233f777888ca829b50..a12a6f0cc2d212fae15490ccb26d8c48762ccbac 100644 (file)
@@ -315,6 +315,7 @@ gst_rtsp_backchannel_get_type (void)
 #define DEFAULT_IS_LIVE TRUE
 #define DEFAULT_IGNORE_X_SERVER_REPLY FALSE
 #define DEFAULT_FORCE_NON_COMPLIANT_URL FALSE
+#define DEFAULT_TCP_TIMESTAMP FALSE
 
 enum
 {
@@ -367,6 +368,7 @@ enum
   PROP_IGNORE_X_SERVER_REPLY,
   PROP_EXTRA_HTTP_REQUEST_HEADERS,
   PROP_FORCE_NON_COMPLIANT_URL,
+  PROP_TCP_TIMESTAMP,
 };
 
 #define GST_TYPE_RTSP_NAT_METHOD (gst_rtsp_nat_method_get_type())
@@ -1119,6 +1121,37 @@ gst_rtspsrc_class_init (GstRTSPSrcClass * klass)
           "Extra headers to append to HTTP requests when in tunneled mode",
           GST_TYPE_STRUCTURE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
+   /**
+   * GstRTSPSrc:tcp-timestamp
+   *
+   * Timestamp all buffers with their receive time when receiving RTP packets
+   * over TCP or HTTP.
+   *
+   * When dealing with TCP based connections, setting timestamps for every
+   * packet is not done by default because a server typically bursts data, for
+   * which we don't want to compensate by speeding up the media. The other
+   * timestamps will be interpollated from this one using the RTP timestamps.
+   *
+   * This has the side effect that no drift compensation between the server
+   * and client is done, and over time the RTP timestamps will drift against
+   * the client's clock. This can lead to buffers (and observed end-to-end
+   * latency) to grow over time, or all packets arriving too late once a
+   * threshold is reached.
+   *
+   * Enabling this property will timestamp all RTP packets with their receive
+   * times, which gets around the drift problem but can cause other problems
+   * if the server is sending data not smoothly in real-time.
+   *
+   * Only applicable for RTSP over TCP or HTTP.
+   *
+   * Since: 1.24.10
+   */
+  g_object_class_install_property (gobject_class,
+      PROP_TCP_TIMESTAMP,
+      g_param_spec_boolean ("tcp-timestamp", "TCP Timestamp",
+          "Timestamp RTP packets with receive times in TCP/HTTP mode",
+          DEFAULT_TCP_TIMESTAMP, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
   /**
    * GstRTSPSrc:force-non-compliant-url
    *
@@ -1571,6 +1604,7 @@ gst_rtspsrc_init (GstRTSPSrc * src)
   src->prop_extra_http_request_headers =
       gst_structure_new_empty ("extra-http-request-headers");
   src->force_non_compliant_url = DEFAULT_FORCE_NON_COMPLIANT_URL;
+  src->tcp_timestamp = DEFAULT_TCP_TIMESTAMP;
 
   /* get a list of all extensions */
   src->extensions = gst_rtsp_ext_list_get ();
@@ -1934,6 +1968,9 @@ gst_rtspsrc_set_property (GObject * object, guint prop_id, const GValue * value,
     case PROP_FORCE_NON_COMPLIANT_URL:
       rtspsrc->force_non_compliant_url = g_value_get_boolean (value);
       break;
+    case PROP_TCP_TIMESTAMP:
+      rtspsrc->tcp_timestamp = g_value_get_boolean (value);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -2113,6 +2150,9 @@ gst_rtspsrc_get_property (GObject * object, guint prop_id, GValue * value,
     case PROP_FORCE_NON_COMPLIANT_URL:
       g_value_set_boolean (value, rtspsrc->force_non_compliant_url);
       break;
+    case PROP_TCP_TIMESTAMP:
+      g_value_set_boolean (value, rtspsrc->tcp_timestamp);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -5847,30 +5887,6 @@ gst_rtspsrc_handle_data (GstRTSPSrc * src, GstRTSPMessage * message)
     src->need_segment = TRUE;
   }
 
-  if (src->base_time == -1) {
-    /* Take current running_time. This timestamp will be put on
-     * the first buffer of each stream because we are a live source and so we
-     * timestamp with the running_time. When we are dealing with TCP, we also
-     * only timestamp the first buffer (using the DISCONT flag) because a server
-     * typically bursts data, for which we don't want to compensate by speeding
-     * up the media. The other timestamps will be interpollated from this one
-     * using the RTP timestamps. */
-    GST_OBJECT_LOCK (src);
-    if (GST_ELEMENT_CLOCK (src)) {
-      GstClockTime now;
-      GstClockTime base_time;
-
-      now = gst_clock_get_time (GST_ELEMENT_CLOCK (src));
-      base_time = GST_ELEMENT_CAST (src)->base_time;
-
-      src->base_time = now - base_time;
-
-      GST_DEBUG_OBJECT (src, "first buffer at time %" GST_TIME_FORMAT ", base %"
-          GST_TIME_FORMAT, GST_TIME_ARGS (now), GST_TIME_ARGS (base_time));
-    }
-    GST_OBJECT_UNLOCK (src);
-  }
-
   /* If needed send a new segment, don't forget we are live and buffer are
    * timestamped with running time */
   if (src->need_segment) {
@@ -5907,16 +5923,28 @@ gst_rtspsrc_handle_data (GstRTSPSrc * src, GstRTSPMessage * message)
     stream->need_caps = FALSE;
   }
 
+  if (!is_rtcp && (stream->discont || src->tcp_timestamp)) {
+    GstClockTime timestamp = GST_CLOCK_TIME_NONE;
+
+    GST_OBJECT_LOCK (src);
+    if (GST_ELEMENT_CLOCK (src)) {
+      GstClockTime now;
+
+      now = gst_clock_get_time (GST_ELEMENT_CLOCK (src));
+      timestamp = now - GST_ELEMENT_CAST (src)->base_time;
+    }
+    GST_OBJECT_UNLOCK (src);
+
+    GST_TRACE_OBJECT (src, "setting timestamp %" GST_TIME_FORMAT,
+        GST_TIME_ARGS (timestamp));
+
+    GST_BUFFER_DTS (buf) = timestamp;
+  }
+
   if (stream->discont && !is_rtcp) {
     /* mark first RTP buffer as discont */
     GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
     stream->discont = FALSE;
-    /* first buffer gets the timestamp, other buffers are not timestamped and
-     * their presentation time will be interpollated from the rtp timestamps. */
-    GST_DEBUG_OBJECT (src, "setting timestamp %" GST_TIME_FORMAT,
-        GST_TIME_ARGS (src->base_time));
-
-    GST_BUFFER_TIMESTAMP (buf) = src->base_time;
   }
 
   /* chain to the peer pad */
@@ -9224,7 +9252,6 @@ restart:
   src->need_range = FALSE;
 
   src->running = TRUE;
-  src->base_time = -1;
   src->state = GST_RTSP_STATE_PLAYING;
 
   /* mark discont */
index eae0376caa85d1c6e7074a33f2f93ec208a34a2a..6df84bf2acbc1ac79eb6e39dfa92a85f40fcd687 100644 (file)
@@ -213,7 +213,6 @@ struct _GstRTSPSrc {
   gboolean         need_segment;
   gboolean         clip_out_segment;
   GstSegment       out_segment;
-  GstClockTime     base_time;
 
   /* UDP mode loop */
   gint             pending_cmd;
@@ -282,6 +281,7 @@ struct _GstRTSPSrc {
   gboolean          ignore_x_server_reply;
   GstStructure     *prop_extra_http_request_headers;
   gboolean          force_non_compliant_url;
+  gboolean          tcp_timestamp;
 
   /* state */
   GstRTSPState       state;