rtppassthroughpay: Add ability to regenerate RTP timestamps

Timestamps are untouched by default, but the new mode can now be enabled to replace RTP timestamps
with ones generated from the buffer PTS. Making it an enum in case different modes are needed in the future.
That allows for a rtpjitterbuffer to do proper drift compensation, so that the stream coming out of gst-rtsp-server
is not drifting compared to the pipeline clock and also not compared to the RTCP NTP times.

Most of the code is borrowed from rtpbasepayload, as it's exactly its behaviour which I wanted to bring here.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7526>
This commit is contained in:
Piotr Brzeziński 2024-09-16 12:27:05 +02:00 committed by GStreamer Marge Bot
parent 252378f1ae
commit 363154d855
3 changed files with 133 additions and 0 deletions

View file

@ -16298,6 +16298,18 @@
"type": "guint",
"writable": true
},
"retimestamp-mode": {
"blurb": "RTP timestamp regeneration mode",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "disabled (0)",
"mutable": "null",
"readable": true,
"type": "GstRtpPassthroughPayRetimestampMode",
"writable": true
},
"seqnum": {
"blurb": "The RTP sequence number of the last processed packet",
"conditionally-available": false,
@ -17745,6 +17757,21 @@
}
]
},
"GstRtpPassthroughPayRetimestampMode": {
"kind": "enum",
"values": [
{
"desc": "No retimestamping",
"name": "disabled",
"value": "0"
},
{
"desc": "Retimestamp based on PTS of each buffer",
"name": "enabled",
"value": "1"
}
]
},
"GstVP8RTPPayMode": {
"kind": "enum",
"values": [

View file

@ -72,6 +72,28 @@ GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
GST_STATIC_CAPS ("application/x-rtp, " "payload = (int) [ 0, 127 ],"
"clock-rate = (int) [ 1, 2147483647 ]"));
#define GST_RTPPASSTHROUGHPAY_RETIMESTAMP_MODE_TYPE (gst_rtp_passthrough_pay_retimestamp_mode_get_type())
static GType
gst_rtp_passthrough_pay_retimestamp_mode_get_type (void)
{
static GType mode_type = 0;
static const GEnumValue enum_values[] = {
{GST_RTPPASSTHROUGHPAY_RETIMESTAMP_MODE_DISABLED, "No retimestamping",
"disabled"},
{GST_RTPPASSTHROUGHPAY_RETIMESTAMP_MODE_ENABLED,
"Retimestamp based on PTS of each buffer", "enabled"},
{0, NULL, NULL},
};
if (!mode_type)
mode_type =
g_enum_register_static ("GstRtpPassthroughPayRetimestampMode",
enum_values);
return mode_type;
}
enum
{
PROP_0,
@ -82,6 +104,7 @@ enum
PROP_SEQNUM_OFFSET,
PROP_TIMESTAMP,
PROP_TIMESTAMP_OFFSET,
PROP_RETIMESTAMP_MODE,
};
static void gst_rtp_passthrough_pay_get_property (GObject * object,
@ -208,6 +231,21 @@ gst_rtp_passthrough_pay_class_init (GstRtpPassthroughPayClass
g_param_spec_boxed ("stats", "Statistics", "Various statistics",
GST_TYPE_STRUCTURE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
/**
* GstRtpPassthroughPay:retimestamp-mode:
*
* If enabled, this mode will cause RTP timestamps to be re-generated
* based on the PTS of each processed buffer.
*
* Since: 1.26
*/
g_object_class_install_property (gobject_class, PROP_RETIMESTAMP_MODE,
g_param_spec_enum ("retimestamp-mode", "Retimestamp mode",
"RTP timestamp regeneration mode",
GST_RTPPASSTHROUGHPAY_RETIMESTAMP_MODE_TYPE,
GST_RTPPASSTHROUGHPAY_RETIMESTAMP_MODE_DISABLED,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
element_class->change_state = gst_rtp_passthrough_pay_change_state;
gst_element_class_add_static_pad_template (element_class, &sink_template);
@ -221,6 +259,8 @@ gst_rtp_passthrough_pay_class_init (GstRtpPassthroughPayClass
GST_DEBUG_CATEGORY_INIT (gst_rtp_passthrough_pay_debug, "rtppassthroughpay",
0, "RTP Passthrough Payloader");
gst_type_mark_as_plugin_api (GST_RTPPASSTHROUGHPAY_RETIMESTAMP_MODE_TYPE, 0);
}
static void
@ -260,6 +300,9 @@ gst_rtp_passthrough_pay_get_property (GObject * object, guint prop_id,
case PROP_STATS:
g_value_take_boxed (value, gst_rtp_passthrough_pay_create_stats (self));
break;
case PROP_RETIMESTAMP_MODE:
g_value_set_enum (value, self->retimestamp_mode);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -287,6 +330,9 @@ gst_rtp_passthrough_pay_set_property (GObject * object, guint prop_id,
GST_FIXME_OBJECT (self,
"Setting the seqnum-offset property has no effect");
break;
case PROP_RETIMESTAMP_MODE:
self->retimestamp_mode = g_value_get_enum (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -325,6 +371,46 @@ gst_rtp_passthrough_pay_change_state (GstElement * element,
return ret;
}
static void
gst_rtppassthrough_pay_handle_retimestamp (GstRtpPassthroughPay * self,
GstRTPBuffer * rtp_buffer)
{
GstClockTime buf_pts = GST_BUFFER_PTS (rtp_buffer->buffer);
guint32 old_timestamp = gst_rtp_buffer_get_timestamp (rtp_buffer);
guint32 new_timestamp;
/* Code below is mostly borrowed from gst_rtp_base_payload_prepare_push() */
if (GST_CLOCK_TIME_IS_VALID (buf_pts)) {
guint64 rtime_hz, rtime_ns;
rtime_ns =
gst_segment_to_running_time (&self->segment, GST_FORMAT_TIME, buf_pts);
if (!GST_CLOCK_TIME_IS_VALID (rtime_ns)) {
GST_LOG_OBJECT (self, "Clipped pts, using base RTP timestamp");
rtime_hz = 0;
} else {
GST_LOG_OBJECT (self,
"Using running_time %" GST_TIME_FORMAT " for RTP timestamp",
GST_TIME_ARGS (rtime_ns));
rtime_hz =
gst_util_uint64_scale_int (rtime_ns, self->clock_rate, GST_SECOND);
}
/* add running_time in clock-rate units to the base timestamp */
new_timestamp = self->timestamp_offset + rtime_hz;
} else {
GST_LOG_OBJECT (self,
"Using previous RTP timestamp %" G_GUINT32_FORMAT, self->timestamp);
/* no timestamp to convert, take previous timestamp */
new_timestamp = self->timestamp;
}
GST_LOG_OBJECT (self, "Retimestamped from %u to %u", old_timestamp,
new_timestamp);
gst_rtp_buffer_set_timestamp (rtp_buffer, new_timestamp);
}
static GstFlowReturn
gst_rtp_passthrough_pay_chain (GstPad * pad,
GstObject * parent, GstBuffer * buffer)
@ -370,8 +456,13 @@ gst_rtp_passthrough_pay_chain (GstPad * pad,
g_object_notify (G_OBJECT (self), "seqnum-offset");
}
if (self->retimestamp_mode != GST_RTPPASSTHROUGHPAY_RETIMESTAMP_MODE_DISABLED) {
gst_rtppassthrough_pay_handle_retimestamp (self, &rtp_buffer);
}
timestamp = gst_rtp_buffer_get_timestamp (&rtp_buffer);
self->timestamp = timestamp;
if (!self->timestamp_offset_set) {
self->timestamp_offset = timestamp;
self->timestamp_offset_set = TRUE;

View file

@ -39,6 +39,19 @@ G_BEGIN_DECLS
typedef struct _GstRtpPassthroughPay GstRtpPassthroughPay;
typedef struct _GstRtpPassthroughPayClass GstRtpPassthroughPayClass;
/**
* GstRtpPassthroughPayRetimestampMode:
* @GST_RTPPASSTHROUGHPAY_RETIMESTAMP_MODE_DISABLED: Leave RTP timestamps unchanged
* @GST_RTPPASSTHROUGHPAY_RETIMESTAMP_MODE_ENABLED: Retimestamp based on buffer PTS
*
* Since: 1.26
*/
typedef enum
{
GST_RTPPASSTHROUGHPAY_RETIMESTAMP_MODE_DISABLED = 0,
GST_RTPPASSTHROUGHPAY_RETIMESTAMP_MODE_ENABLED = 1,
} GstRtpPassthroughPayRetimestampMode;
struct _GstRtpPassthroughPayClass
{
GstElementClass parent_class;
@ -64,6 +77,8 @@ struct _GstRtpPassthroughPay
guint seqnum;
guint seqnum_offset;
GstClockTime pts_or_dts;
GstRtpPassthroughPayRetimestampMode retimestamp_mode;
};
GType gst_rtp_passthrough_pay_get_type (void);