#define DEFAULT_MAX_MISORDER_TIME 2000
#define DEFAULT_RFC7273_SYNC FALSE
#define DEFAULT_MAX_STREAMS G_MAXUINT
+#define DEFAULT_MAX_TS_OFFSET_ADJUSTMENT 0
enum
{
PROP_MAX_DROPOUT_TIME,
PROP_MAX_MISORDER_TIME,
PROP_RFC7273_SYNC,
- PROP_MAX_STREAMS
+ PROP_MAX_STREAMS,
+ PROP_MAX_TS_OFFSET_ADJUSTMENT
};
#define GST_RTP_BIN_RTCP_SYNC_TYPE (gst_rtp_bin_rtcp_sync_get_type())
g_object_set (buffer, "max-dropout-time", rtpbin->max_dropout_time,
"max-misorder-time", rtpbin->max_misorder_time, NULL);
g_object_set (buffer, "rfc7273-sync", rtpbin->rfc7273_sync, NULL);
+ g_object_set (buffer, "max-ts-offset-adjustment",
+ rtpbin->max_ts_offset_adjustment, NULL);
g_signal_emit (rtpbin, gst_rtp_bin_signals[SIGNAL_NEW_JITTERBUFFER], 0,
buffer, session->id, ssrc);
0, G_MAXUINT, DEFAULT_MAX_STREAMS,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ /**
+ * GstRtpBin:max-ts-offset-adjustment:
+ *
+ * Syncing time stamps to NTP time adds a time offset. This parameter
+ * specifies the maximum number of nanoseconds per frame that this time offset
+ * may be adjusted with. This is used to avoid sudden large changes to time
+ * stamps.
+ */
+ g_object_class_install_property (gobject_class, PROP_MAX_TS_OFFSET_ADJUSTMENT,
+ g_param_spec_uint64 ("max-ts-offset-adjustment",
+ "Max Timestamp Offset Adjustment",
+ "The maximum number of nanoseconds per frame that time stamp offsets "
+ "may be adjusted (0 = no limit).", 0, G_MAXUINT64,
+ DEFAULT_MAX_TS_OFFSET_ADJUSTMENT, G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_rtp_bin_change_state);
gstelement_class->request_new_pad =
GST_DEBUG_FUNCPTR (gst_rtp_bin_request_new_pad);
rtpbin->max_misorder_time = DEFAULT_MAX_MISORDER_TIME;
rtpbin->rfc7273_sync = DEFAULT_RFC7273_SYNC;
rtpbin->max_streams = DEFAULT_MAX_STREAMS;
+ rtpbin->max_ts_offset_adjustment = DEFAULT_MAX_TS_OFFSET_ADJUSTMENT;
/* some default SDES entries */
cname = g_strdup_printf ("user%u@host-%x", g_random_int (), g_random_int ());
case PROP_MAX_STREAMS:
rtpbin->max_streams = g_value_get_uint (value);
break;
+ case PROP_MAX_TS_OFFSET_ADJUSTMENT:
+ rtpbin->max_ts_offset_adjustment = g_value_get_uint64 (value);
+ gst_rtp_bin_propagate_property_to_jitterbuffer (rtpbin,
+ "max-ts-offset-adjustment", value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
case PROP_MAX_STREAMS:
g_value_set_uint (value, rtpbin->max_streams);
break;
+ case PROP_MAX_TS_OFFSET_ADJUSTMENT:
+ g_value_set_uint64 (value, rtpbin->max_ts_offset_adjustment);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
guint32 max_misorder_time;
gboolean rfc7273_sync;
guint max_streams;
+ guint64 max_ts_offset_adjustment;
/* a list of session */
GSList *sessions;
#define DEFAULT_LATENCY_MS 200
#define DEFAULT_DROP_ON_LATENCY FALSE
#define DEFAULT_TS_OFFSET 0
+#define DEFAULT_MAX_TS_OFFSET_ADJUSTMENT 0
#define DEFAULT_DO_LOST FALSE
#define DEFAULT_MODE RTP_JITTER_BUFFER_MODE_SLAVE
#define DEFAULT_PERCENT 0
PROP_LATENCY,
PROP_DROP_ON_LATENCY,
PROP_TS_OFFSET,
+ PROP_MAX_TS_OFFSET_ADJUSTMENT,
PROP_DO_LOST,
PROP_MODE,
PROP_PERCENT,
guint64 latency_ns;
gboolean drop_on_latency;
gint64 ts_offset;
+ guint64 max_ts_offset_adjustment;
gboolean do_lost;
gboolean do_retransmission;
gboolean rtx_next_seqnum;
gint last_pt;
gint32 clock_rate;
gint64 clock_base;
- gint64 prev_ts_offset;
+ gint64 ts_offset_remainder;
/* when we are shutting down */
GstFlowReturn srcresult;
/* for the jitter */
GstClockTime last_dts;
+ GstClockTime last_pts;
guint64 last_rtptime;
GstClockTime avg_jitter;
};
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
+ * GstRtpJitterBuffer:max-ts-offset-adjustment:
+ *
+ * The maximum number of nanoseconds per frame that time offset may be
+ * adjusted with. This is used to avoid sudden large changes to time stamps.
+ */
+ g_object_class_install_property (gobject_class, PROP_MAX_TS_OFFSET_ADJUSTMENT,
+ g_param_spec_uint64 ("max-ts-offset-adjustment",
+ "Max Timestamp Offset Adjustment",
+ "The maximum number of nanoseconds per frame that time stamp "
+ "offsets may be adjusted (0 = no limit).", 0, G_MAXUINT64,
+ DEFAULT_MAX_TS_OFFSET_ADJUSTMENT, G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
* GstRtpJitterBuffer:do-lost:
*
* Send out a GstRTPPacketLost event downstream when a packet is considered
priv->latency_ms = DEFAULT_LATENCY_MS;
priv->latency_ns = priv->latency_ms * GST_MSECOND;
priv->drop_on_latency = DEFAULT_DROP_ON_LATENCY;
+ priv->ts_offset = DEFAULT_TS_OFFSET;
+ priv->max_ts_offset_adjustment = DEFAULT_MAX_TS_OFFSET_ADJUSTMENT;
priv->do_lost = DEFAULT_DO_LOST;
priv->do_retransmission = DEFAULT_DO_RETRANSMISSION;
priv->rtx_next_seqnum = DEFAULT_RTX_NEXT_SEQNUM;
priv->max_misorder_time = DEFAULT_MAX_MISORDER_TIME;
priv->faststart_min_packets = DEFAULT_FASTSTART_MIN_PACKETS;
+ priv->ts_offset_remainder = 0;
priv->last_dts = -1;
+ priv->last_pts = -1;
priv->last_rtptime = -1;
priv->avg_jitter = 0;
priv->timers = g_array_new (FALSE, TRUE, sizeof (TimerData));
priv->srcresult = GST_FLOW_OK;
gst_segment_init (&priv->segment, GST_FORMAT_TIME);
priv->last_popped_seqnum = -1;
- priv->last_out_time = -1;
+ priv->last_out_time = GST_CLOCK_TIME_NONE;
priv->next_seqnum = -1;
priv->seqnum_base = -1;
priv->ips_rtptime = -1;
return message;
}
+static void
+update_offset (GstRtpJitterBuffer * jitterbuffer)
+{
+ GstRtpJitterBufferPrivate *priv;
+
+ priv = jitterbuffer->priv;
+
+ if (priv->ts_offset_remainder != 0) {
+ GST_DEBUG ("adjustment %" G_GUINT64_FORMAT " remain %" G_GINT64_FORMAT
+ " off %" G_GINT64_FORMAT, priv->max_ts_offset_adjustment,
+ priv->ts_offset_remainder, priv->ts_offset);
+ if (ABS (priv->ts_offset_remainder) > priv->max_ts_offset_adjustment) {
+ if (priv->ts_offset_remainder > 0) {
+ priv->ts_offset += priv->max_ts_offset_adjustment;
+ priv->ts_offset_remainder -= priv->max_ts_offset_adjustment;
+ } else {
+ priv->ts_offset -= priv->max_ts_offset_adjustment;
+ priv->ts_offset_remainder += priv->max_ts_offset_adjustment;
+ }
+ } else {
+ priv->ts_offset += priv->ts_offset_remainder;
+ priv->ts_offset_remainder = 0;
+ }
+ }
+}
+
static GstClockTime
apply_offset (GstRtpJitterBuffer * jitterbuffer, GstClockTime timestamp)
{
gst_segment_position_from_running_time (&priv->segment,
GST_FORMAT_TIME, item->pts);
+ /* if this is a new frame, check if ts_offset needs to be updated */
+ if (pts != priv->last_pts) {
+ update_offset (jitterbuffer);
+ }
+
/* apply timestamp with offset to buffer now */
GST_BUFFER_DTS (outbuf) = apply_offset (jitterbuffer, dts);
GST_BUFFER_PTS (outbuf) = apply_offset (jitterbuffer, pts);
/* update the elapsed time when we need to check against the npt stop time. */
update_estimated_eos (jitterbuffer, item);
+ /* verify that an offset has not caused time stamps to go backwards, if so
+ * handle by reusing the previous timestamp */
+ if (priv->last_out_time != GST_CLOCK_TIME_NONE &&
+ GST_BUFFER_PTS (outbuf) < priv->last_out_time) {
+ GST_DEBUG_OBJECT (jitterbuffer, "buffer PTS %" GST_TIME_FORMAT
+ " older than preceding PTS %" GST_TIME_FORMAT
+ " adjusting to %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (GST_BUFFER_PTS (outbuf)),
+ GST_TIME_ARGS (priv->last_out_time),
+ GST_TIME_ARGS (priv->last_out_time));
+ GST_BUFFER_PTS (outbuf) = priv->last_out_time;
+ }
+
+ priv->last_pts = pts;
priv->last_out_time = GST_BUFFER_PTS (outbuf);
break;
case ITEM_TYPE_LOST:
break;
case PROP_TS_OFFSET:
JBUF_LOCK (priv);
- priv->ts_offset = g_value_get_int64 (value);
+ if (priv->max_ts_offset_adjustment != 0) {
+ gint64 new_offset = g_value_get_int64 (value);
+
+ if (new_offset > priv->ts_offset) {
+ priv->ts_offset_remainder = new_offset - priv->ts_offset;
+ } else {
+ priv->ts_offset_remainder = -(priv->ts_offset - new_offset);
+ }
+ } else {
+ priv->ts_offset = g_value_get_int64 (value);
+ priv->ts_offset_remainder = 0;
+ }
priv->ts_discont = TRUE;
JBUF_UNLOCK (priv);
break;
+ case PROP_MAX_TS_OFFSET_ADJUSTMENT:
+ JBUF_LOCK (priv);
+ priv->max_ts_offset_adjustment = g_value_get_uint64 (value);
+ JBUF_UNLOCK (priv);
+ break;
case PROP_DO_LOST:
JBUF_LOCK (priv);
priv->do_lost = g_value_get_boolean (value);
g_value_set_int64 (value, priv->ts_offset);
JBUF_UNLOCK (priv);
break;
+ case PROP_MAX_TS_OFFSET_ADJUSTMENT:
+ JBUF_LOCK (priv);
+ g_value_set_uint64 (value, priv->max_ts_offset_adjustment);
+ JBUF_UNLOCK (priv);
+ break;
case PROP_DO_LOST:
JBUF_LOCK (priv);
g_value_set_boolean (value, priv->do_lost);
return GST_FLOW_OK;
if (src->pt_set && src->pt != pinfo->pt) {
- GST_WARNING ("Changing pt from %u to %u for SSRC %u", src->pt, pinfo->pt, src->ssrc);
+ GST_WARNING ("Changing pt from %u to %u for SSRC %u", src->pt, pinfo->pt,
+ src->ssrc);
}
src->pt = pinfo->pt;
#define DEFAULT_USER_AGENT "GStreamer/" PACKAGE_VERSION
#define DEFAULT_MAX_RTCP_RTP_TIME_DIFF 1000
#define DEFAULT_RFC7273_SYNC FALSE
+#define DEFAULT_MAX_TS_OFFSET_ADJUSTMENT 0
enum
{
PROP_NTP_TIME_SOURCE,
PROP_USER_AGENT,
PROP_MAX_RTCP_RTP_TIME_DIFF,
- PROP_RFC7273_SYNC
+ PROP_RFC7273_SYNC,
+ PROP_MAX_TS_OFFSET_ADJUSTMENT
};
#define GST_TYPE_RTSP_NAT_METHOD (gst_rtsp_nat_method_get_type())
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
+ * GstRTSPSrc:max-ts-offset-adjustment:
+ *
+ * Syncing time stamps to NTP time adds a time offset. This parameter
+ * specifies the maximum number of nanoseconds per frame that this time offset
+ * may be adjusted with. This is used to avoid sudden large changes to time
+ * stamps.
+ */
+ g_object_class_install_property (gobject_class, PROP_MAX_TS_OFFSET_ADJUSTMENT,
+ g_param_spec_uint64 ("max-ts-offset-adjustment",
+ "Max Timestamp Offset Adjustment",
+ "The maximum number of nanoseconds per frame that time stamp offsets "
+ "may be adjusted (0 = no limit).", 0, G_MAXUINT64,
+ DEFAULT_MAX_TS_OFFSET_ADJUSTMENT, G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
* GstRTSPSrc::handle-request:
* @rtspsrc: a #GstRTSPSrc
* @request: a #GstRTSPMessage
src->user_agent = g_strdup (DEFAULT_USER_AGENT);
src->max_rtcp_rtp_time_diff = DEFAULT_MAX_RTCP_RTP_TIME_DIFF;
src->rfc7273_sync = DEFAULT_RFC7273_SYNC;
+ src->max_ts_offset_adjustment = DEFAULT_MAX_TS_OFFSET_ADJUSTMENT;
/* get a list of all extensions */
src->extensions = gst_rtsp_ext_list_get ();
case PROP_RFC7273_SYNC:
rtspsrc->rfc7273_sync = g_value_get_boolean (value);
break;
+ case PROP_MAX_TS_OFFSET_ADJUSTMENT:
+ rtspsrc->max_ts_offset_adjustment = g_value_get_uint64 (value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
case PROP_RFC7273_SYNC:
g_value_set_boolean (value, rtspsrc->rfc7273_sync);
break;
+ case PROP_MAX_TS_OFFSET_ADJUSTMENT:
+ g_value_set_uint64 (value, rtspsrc->max_ts_offset_adjustment);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
src->max_rtcp_rtp_time_diff, NULL);
}
+ if (g_object_class_find_property (klass, "max-ts-offset-adjustment")) {
+ g_object_set (src->manager, "max-ts-offset-adjustment",
+ src->max_ts_offset_adjustment, NULL);
+ }
+
/* buffer mode pauses are handled by adding offsets to buffer times,
* but some depayloaders may have a hard time syncing output times
* with such input times, e.g. container ones, most notably ASF */
gchar *user_agent;
GstClockTime max_rtcp_rtp_time_diff;
gboolean rfc7273_sync;
+ guint64 max_ts_offset_adjustment;
/* state */
GstRTSPState state;