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.
This commit is contained in:
Wim Taymans 2010-01-20 17:04:03 +01:00 committed by Wim Taymans
parent 2baa107562
commit 83cb1aecc8
5 changed files with 81 additions and 62 deletions

View file

@ -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 (&current);
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;

View file

@ -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;

View file

@ -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__ */

View file

@ -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 {

View file

@ -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,