From 83cb1aecc8218dabc2a7a99a916c73a97037db74 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 20 Jan 2010 17:04:03 +0100 Subject: [PATCH] rtpbin: change how NTP time is calculated in RTCP Don't calculate the NTP time based on the running_time of the pipeline but from the systemclock. This allows us to generate more accurate NTP timestamps in case the systemclock is synchronized with NTP or similar. --- gst/rtpmanager/gstrtpsession.c | 41 ++++++++++---------- gst/rtpmanager/rtpsession.c | 17 +++++---- gst/rtpmanager/rtpsession.h | 4 +- gst/rtpmanager/rtpsource.c | 69 ++++++++++++++++++++-------------- gst/rtpmanager/rtpsource.h | 12 +++--- 5 files changed, 81 insertions(+), 62 deletions(-) diff --git a/gst/rtpmanager/gstrtpsession.c b/gst/rtpmanager/gstrtpsession.c index be749dea42..3dddf8dd7c 100644 --- a/gst/rtpmanager/gstrtpsession.c +++ b/gst/rtpmanager/gstrtpsession.c @@ -713,24 +713,29 @@ gst_rtp_session_get_property (GObject * object, guint prop_id, } static void -get_current_times (GstRtpSession * rtpsession, - GstClockTime * running_time, guint64 * ntpnstime) +get_current_times (GstRtpSession * rtpsession, GstClockTime * running_time, + guint64 * ntpnstime) { guint64 ntpns; GstClock *clock; - GstClockTime base_time, ntpnsbase, rt; + GstClockTime base_time, rt; + GTimeVal current; GST_OBJECT_LOCK (rtpsession); if ((clock = GST_ELEMENT_CLOCK (rtpsession))) { base_time = GST_ELEMENT_CAST (rtpsession)->base_time; - ntpnsbase = rtpsession->priv->ntpnsbase; gst_object_ref (clock); GST_OBJECT_UNLOCK (rtpsession); + /* get current NTP time */ + g_get_current_time (¤t); + ntpns = GST_TIMEVAL_TO_TIME (current); + + /* add constant to convert from 1970 based time to 1900 based time */ + ntpns += (2208988800LL * GST_SECOND); + /* get current clock time and convert to running time */ rt = gst_clock_get_time (clock) - base_time; - /* add NTP base offset to get NTP ns time */ - ntpns = rt + ntpnsbase; gst_object_unref (clock); } else { @@ -751,6 +756,7 @@ rtcp_thread (GstRtpSession * rtpsession) GstClockTime current_time; GstClockTime next_timeout; guint64 ntpnstime; + GstClockTime running_time; GST_DEBUG_OBJECT (rtpsession, "entering RTCP thread"); @@ -789,7 +795,7 @@ rtcp_thread (GstRtpSession * rtpsession) current_time = gst_clock_get_time (rtpsession->priv->sysclock); /* get current NTP time */ - get_current_times (rtpsession, NULL, &ntpnstime); + get_current_times (rtpsession, &running_time, &ntpnstime); /* we get unlocked because we need to perform reconsideration, don't perform * the timeout but get a new reporting estimate. */ @@ -798,7 +804,8 @@ rtcp_thread (GstRtpSession * rtpsession) /* perform actions, we ignore result. Release lock because it might push. */ GST_RTP_SESSION_UNLOCK (rtpsession); - rtp_session_on_timeout (rtpsession->priv->session, current_time, ntpnstime); + rtp_session_on_timeout (rtpsession->priv->session, current_time, ntpnstime, + running_time); GST_RTP_SESSION_LOCK (rtpsession); } /* mark the thread as stopped now */ @@ -1602,9 +1609,8 @@ gst_rtp_session_chain_send_rtp_common (GstPad * pad, gpointer data, GstRtpSession *rtpsession; GstRtpSessionPrivate *priv; GstFlowReturn ret; - GstClockTime timestamp; + GstClockTime timestamp, running_time; GstClockTime current_time; - guint64 ntpnstime; rtpsession = GST_RTP_SESSION (gst_pad_get_parent (pad)); priv = rtpsession->priv; @@ -1625,23 +1631,20 @@ gst_rtp_session_chain_send_rtp_common (GstPad * pad, gpointer data, } else { timestamp = GST_BUFFER_TIMESTAMP (GST_BUFFER_CAST (data)); } + if (GST_CLOCK_TIME_IS_VALID (timestamp)) { /* convert to running time using the segment start value. */ - ntpnstime = + running_time = gst_segment_to_running_time (&rtpsession->send_rtp_seg, GST_FORMAT_TIME, timestamp); - /* convert to NTP time by adding the NTP base */ - ntpnstime += priv->ntpnsbase; } else { - /* no timestamp, we could take the current running_time and convert it to - * NTP time. */ - ntpnstime = -1; + /* no timestamp. */ + running_time = -1; } current_time = gst_clock_get_time (priv->sysclock); - ret = - rtp_session_send_rtp (priv->session, data, is_list, current_time, - ntpnstime); + ret = rtp_session_send_rtp (priv->session, data, is_list, current_time, + running_time); if (ret != GST_FLOW_OK) goto push_error; diff --git a/gst/rtpmanager/rtpsession.c b/gst/rtpmanager/rtpsession.c index 7abaf3467b..ace3dc2f91 100644 --- a/gst/rtpmanager/rtpsession.c +++ b/gst/rtpmanager/rtpsession.c @@ -1956,9 +1956,9 @@ ignore: * rtp_session_send_rtp: * @sess: an #RTPSession * @data: pointer to either an RTP buffer or a list of RTP buffers + * @is_list: TRUE when @data is a buffer list * @current_time: the current system time - * @ntpnstime: the NTP time in nanoseconds of when this buffer was captured. - * This is the buffer timestamp converted to NTP time. + * @running_time: the running time of @data * * Send the RTP buffer in the session manager. This function takes ownership of * @buffer. @@ -1967,7 +1967,7 @@ ignore: */ GstFlowReturn rtp_session_send_rtp (RTPSession * sess, gpointer data, gboolean is_list, - GstClockTime current_time, guint64 ntpnstime) + GstClockTime current_time, GstClockTime running_time) { GstFlowReturn result; RTPSource *source; @@ -1997,7 +1997,7 @@ rtp_session_send_rtp (RTPSession * sess, gpointer data, gboolean is_list, prevsender = RTP_SOURCE_IS_SENDER (source); /* we use our own source to send */ - result = rtp_source_send_rtp (source, data, is_list, ntpnstime); + result = rtp_source_send_rtp (source, data, is_list, running_time); if (RTP_SOURCE_IS_SENDER (source) && !prevsender) sess->stats.sender_sources++; @@ -2175,6 +2175,7 @@ typedef struct GstBuffer *rtcp; GstClockTime current_time; guint64 ntpnstime; + GstClockTime running_time; GstClockTime interval; GstRTCPPacket packet; gboolean is_bye; @@ -2199,8 +2200,8 @@ session_start_rtcp (RTPSession * sess, ReportData * data) gst_rtcp_buffer_add_packet (data->rtcp, GST_RTCP_TYPE_SR, packet); /* get latest stats */ - rtp_source_get_new_sr (own, data->ntpnstime, &ntptime, &rtptime, - &packet_count, &octet_count); + rtp_source_get_new_sr (own, data->ntpnstime, data->running_time, + &ntptime, &rtptime, &packet_count, &octet_count); /* store stats */ rtp_source_process_sr (own, data->current_time, ntptime, rtptime, packet_count, octet_count); @@ -2448,6 +2449,7 @@ is_rtcp_time (RTPSession * sess, GstClockTime current_time, ReportData * data) * @sess: an #RTPSession * @current_time: the current system time * @ntpnstime: the current NTP time in nanoseconds + * @running_time: the current running_time of the pipeline * * Perform maintenance actions after the timeout obtained with * rtp_session_next_timeout() expired. @@ -2462,7 +2464,7 @@ is_rtcp_time (RTPSession * sess, GstClockTime current_time, ReportData * data) */ GstFlowReturn rtp_session_on_timeout (RTPSession * sess, GstClockTime current_time, - guint64 ntpnstime) + guint64 ntpnstime, GstClockTime running_time) { GstFlowReturn result = GST_FLOW_OK; GList *item; @@ -2481,6 +2483,7 @@ rtp_session_on_timeout (RTPSession * sess, GstClockTime current_time, data.ntpnstime = ntpnstime; data.is_bye = FALSE; data.has_sdes = FALSE; + data.running_time = running_time; own = sess->source; diff --git a/gst/rtpmanager/rtpsession.h b/gst/rtpmanager/rtpsession.h index 25e228b054..5cb38f6a6a 100644 --- a/gst/rtpmanager/rtpsession.h +++ b/gst/rtpmanager/rtpsession.h @@ -292,7 +292,7 @@ GstFlowReturn rtp_session_process_rtcp (RTPSession *sess, GstBuffer /* processing packets for sending */ GstFlowReturn rtp_session_send_rtp (RTPSession *sess, gpointer data, gboolean is_list, - GstClockTime current_time, guint64 ntpnstime); + GstClockTime current_time, GstClockTime running_time); /* stopping the session */ GstFlowReturn rtp_session_schedule_bye (RTPSession *sess, const gchar *reason, @@ -301,6 +301,6 @@ GstFlowReturn rtp_session_schedule_bye (RTPSession *sess, const gcha /* get interval for next RTCP interval */ GstClockTime rtp_session_next_timeout (RTPSession *sess, GstClockTime current_time); GstFlowReturn rtp_session_on_timeout (RTPSession *sess, GstClockTime current_time, - guint64 ntpnstime); + guint64 ntpnstime, GstClockTime running_time); #endif /* __RTP_SESSION_H__ */ diff --git a/gst/rtpmanager/rtpsource.c b/gst/rtpmanager/rtpsource.c index 6a46873c3b..ed4c4336df 100644 --- a/gst/rtpmanager/rtpsource.c +++ b/gst/rtpmanager/rtpsource.c @@ -1047,8 +1047,7 @@ set_ssrc (GstBuffer ** buffer, guint group, guint idx, RTPSource * src) * @src: an #RTPSource * @data: an RTP buffer or a list of RTP buffers * @is_list: if @data is a buffer or list - * @ntpnstime: the NTP time when this buffer was captured in nanoseconds. This - * is the buffer timestamp converted to NTP time. + * @running_time: the running time of @data * * Send @data (an RTP buffer or list of buffers) originating from @src. * This will make @src a sender. This function takes ownership of @data and @@ -1058,13 +1057,13 @@ set_ssrc (GstBuffer ** buffer, guint group, guint idx, RTPSource * src) */ GstFlowReturn rtp_source_send_rtp (RTPSource * src, gpointer data, gboolean is_list, - guint64 ntpnstime) + GstClockTime running_time) { GstFlowReturn result; guint len; guint32 rtptime; guint64 ext_rtptime; - guint64 ntp_diff, rtp_diff; + guint64 rt_diff, rtp_diff; guint64 elapsed; GstBufferList *list = NULL; GstBuffer *buffer = NULL; @@ -1104,8 +1103,8 @@ rtp_source_send_rtp (RTPSource * src, gpointer data, gboolean is_list, src->stats.octets_sent += len; src->bytes_sent += len; - if (src->prev_ntpnstime) { - elapsed = ntpnstime - src->prev_ntpnstime; + if (src->prev_rtime) { + elapsed = running_time - src->prev_rtime; if (elapsed > (G_GINT64_CONSTANT (1) << 31)) { guint64 rate; @@ -1122,12 +1121,12 @@ rtp_source_send_rtp (RTPSource * src, gpointer data, gboolean is_list, else src->bitrate = ((src->bitrate * 3) + rate) / 4; - src->prev_ntpnstime = ntpnstime; + src->prev_rtime = running_time; src->bytes_sent = 0; } } else { GST_LOG ("Reset bitrate measurement"); - src->prev_ntpnstime = ntpnstime; + src->prev_rtime = running_time; src->bitrate = 0; } @@ -1139,24 +1138,24 @@ rtp_source_send_rtp (RTPSource * src, gpointer data, gboolean is_list, ext_rtptime = src->last_rtptime; ext_rtptime = gst_rtp_buffer_ext_timestamp (&ext_rtptime, rtptime); - GST_LOG ("SSRC %08x, RTP %" G_GUINT64_FORMAT ", NTP %" GST_TIME_FORMAT, - src->ssrc, ext_rtptime, GST_TIME_ARGS (ntpnstime)); + GST_LOG ("SSRC %08x, RTP %" G_GUINT64_FORMAT ", running_time %" + GST_TIME_FORMAT, src->ssrc, ext_rtptime, GST_TIME_ARGS (running_time)); if (ext_rtptime > src->last_rtptime) { rtp_diff = ext_rtptime - src->last_rtptime; - ntp_diff = ntpnstime - src->last_ntpnstime; + rt_diff = running_time - src->last_rtime; /* calc the diff so we can detect drift at the sender. This can also be used * to guestimate the clock rate if the NTP time is locked to the RTP * timestamps (as is the case when the capture device is providing the clock). */ - GST_LOG ("SSRC %08x, diff RTP %" G_GUINT64_FORMAT ", diff NTP %" - GST_TIME_FORMAT, src->ssrc, rtp_diff, GST_TIME_ARGS (ntp_diff)); + GST_LOG ("SSRC %08x, diff RTP %" G_GUINT64_FORMAT ", diff running_time %" + GST_TIME_FORMAT, src->ssrc, rtp_diff, GST_TIME_ARGS (rt_diff)); } /* we keep track of the last received RTP timestamp and the corresponding - * NTP timestamp so that we can use this info when constructing SR reports */ + * buffer running_time so that we can use this info when constructing SR reports */ + src->last_rtime = running_time; src->last_rtptime = ext_rtptime; - src->last_ntpnstime = ntpnstime; /* push packet */ if (!src->callbacks.push_rtp) @@ -1312,6 +1311,7 @@ rtp_source_process_rb (RTPSource * src, GstClockTime time, guint8 fractionlost, * rtp_source_get_new_sr: * @src: an #RTPSource * @ntpnstime: the current time in nanoseconds since 1970 + * @running_time: the current running_time of the pipeline. * @ntptime: the NTP time in 32.32 fixed point * @rtptime: the RTP time corresponding to @ntptime * @packet_count: the packet count @@ -1319,12 +1319,19 @@ rtp_source_process_rb (RTPSource * src, GstClockTime time, guint8 fractionlost, * * Get new values to put into a new SR report from this source. * + * @running_time and @ntpnstime are captured at the same time and represent the + * running time of the pipeline clock and the absolute current system time in + * nanoseconds respectively. Together with the last running_time and rtp timestamp + * we have observed in the source, we can generate @ntptime and @rtptime for an SR + * packet. @ntptime is basically the fixed point representation of @ntpnstime + * and @rtptime the associated RTP timestamp. + * * Returns: %TRUE on success. */ gboolean rtp_source_get_new_sr (RTPSource * src, guint64 ntpnstime, - guint64 * ntptime, guint32 * rtptime, guint32 * packet_count, - guint32 * octet_count) + GstClockTime running_time, guint64 * ntptime, guint32 * rtptime, + guint32 * packet_count, guint32 * octet_count) { guint64 t_rtp; guint64 t_current_ntp; @@ -1332,30 +1339,36 @@ rtp_source_get_new_sr (RTPSource * src, guint64 ntpnstime, g_return_val_if_fail (RTP_IS_SOURCE (src), FALSE); - /* use the sync params to interpolate the date->time member to rtptime. We - * use the last sent timestamp and rtptime as reference points. We assume - * that the slope of the rtptime vs timestamp curve is 1, which is certainly + /* We last saw a buffer with last_rtptime at last_rtime. Given a running_time + * and an NTP time, we can scale the RTP timestamps so that they match the + * given NTP time. for scaling, we assume that the slope of the rtptime vs + * running_time vs ntptime curve is close to 1, which is certainly * sufficient for the frequency at which we report SR and the rate we send * out RTP packets. */ t_rtp = src->last_rtptime; - GST_DEBUG ("last_ntpnstime %" GST_TIME_FORMAT ", last_rtptime %" - G_GUINT64_FORMAT, GST_TIME_ARGS (src->last_ntpnstime), t_rtp); + GST_DEBUG ("last_rtime %" GST_TIME_FORMAT ", last_rtptime %" + G_GUINT64_FORMAT, GST_TIME_ARGS (src->last_rtime), t_rtp); if (src->clock_rate != -1) { - /* get the diff with the SR time */ - diff = GST_CLOCK_DIFF (src->last_ntpnstime, ntpnstime); + /* get the diff between the clock running_time and the buffer running_time. + * This is the elapsed time, as measured against the pipeline clock, between + * when the rtp timestamp was observed and the current running_time. + * + * We need to apply this diff to the RTP timestamp to get the RTP timestamp + * for the given ntpnstime. */ + diff = GST_CLOCK_DIFF (src->last_rtime, running_time); /* now translate the diff to RTP time, handle positive and negative cases. * If there is no diff, we already set rtptime correctly above. */ if (diff > 0) { - GST_DEBUG ("ntpnstime %" GST_TIME_FORMAT ", diff %" GST_TIME_FORMAT, - GST_TIME_ARGS (ntpnstime), GST_TIME_ARGS (diff)); + GST_DEBUG ("running_time %" GST_TIME_FORMAT ", diff %" GST_TIME_FORMAT, + GST_TIME_ARGS (running_time), GST_TIME_ARGS (diff)); t_rtp += gst_util_uint64_scale_int (diff, src->clock_rate, GST_SECOND); } else { diff = -diff; - GST_DEBUG ("ntpnstime %" GST_TIME_FORMAT ", diff -%" GST_TIME_FORMAT, - GST_TIME_ARGS (ntpnstime), GST_TIME_ARGS (diff)); + GST_DEBUG ("running_time %" GST_TIME_FORMAT ", diff -%" GST_TIME_FORMAT, + GST_TIME_ARGS (running_time), GST_TIME_ARGS (diff)); t_rtp -= gst_util_uint64_scale_int (diff, src->clock_rate, GST_SECOND); } } else { diff --git a/gst/rtpmanager/rtpsource.h b/gst/rtpmanager/rtpsource.h index b0c9bd0c49..6d43a02554 100644 --- a/gst/rtpmanager/rtpsource.h +++ b/gst/rtpmanager/rtpsource.h @@ -136,12 +136,12 @@ struct _RTPSource { GstClockTime last_activity; GstClockTime last_rtp_activity; + GstClockTime last_rtime; GstClockTime last_rtptime; - GstClockTime last_ntpnstime; /* for bitrate estimation */ guint64 bitrate; - GstClockTime prev_ntpnstime; + GstClockTime prev_rtime; guint64 bytes_sent; GQueue *packets; @@ -192,8 +192,8 @@ void rtp_source_set_rtcp_from (RTPSource *src, GstNetAddress *a /* handling RTP */ GstFlowReturn rtp_source_process_rtp (RTPSource *src, GstBuffer *buffer, RTPArrivalStats *arrival); -GstFlowReturn rtp_source_send_rtp (RTPSource *src, gpointer data, gboolean is_list, guint64 ntpnstime); - +GstFlowReturn rtp_source_send_rtp (RTPSource *src, gpointer data, gboolean is_list, + GstClockTime running_time); /* RTCP messages */ void rtp_source_process_bye (RTPSource *src, const gchar *reason); void rtp_source_process_sr (RTPSource *src, GstClockTime time, guint64 ntptime, @@ -202,8 +202,8 @@ void rtp_source_process_rb (RTPSource *src, GstClockTime tim gint32 packetslost, guint32 exthighestseq, guint32 jitter, guint32 lsr, guint32 dlsr); -gboolean rtp_source_get_new_sr (RTPSource *src, guint64 ntpnstime, guint64 *ntptime, - guint32 *rtptime, guint32 *packet_count, +gboolean rtp_source_get_new_sr (RTPSource *src, guint64 ntpnstime, GstClockTime running_time, + guint64 *ntptime, guint32 *rtptime, guint32 *packet_count, guint32 *octet_count); gboolean rtp_source_get_new_rb (RTPSource *src, GstClockTime time, guint8 *fractionlost, gint32 *packetslost, guint32 *exthighestseq, guint32 *jitter,