From 325dac0fc21bf3e014f1a0bbe6de5dc711e21e30 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 29 Aug 2007 01:22:43 +0000 Subject: [PATCH] gst/rtpmanager/gstrtpsession.*: Distribute synchronisation parameters to the session manager so that it can generate ... Original commit message from CVS: * gst/rtpmanager/gstrtpsession.c: (stop_rtcp_thread), (gst_rtp_session_change_state), (gst_rtp_session_event_send_rtp_sink): * gst/rtpmanager/gstrtpsession.h: Distribute synchronisation parameters to the session manager so that it can generate correct SR packets for lip-sync. * gst/rtpmanager/rtpsession.c: (rtp_session_set_base_time), (rtp_session_set_timestamp_sync), (session_start_rtcp): * gst/rtpmanager/rtpsession.h: Add methods for setting sync parameters. Set correct RTP time in SR packets using the sync params. * gst/rtpmanager/rtpsource.c: (rtp_source_send_rtp): * gst/rtpmanager/rtpsource.h: Record last RTP <-> GST timestamp so that we can use them to convert NTP to RTP timestamps in SR packets. --- gst/rtpmanager/gstrtpsession.c | 46 ++++++++++++++++++++++++- gst/rtpmanager/gstrtpsession.h | 1 + gst/rtpmanager/rtpsession.c | 78 +++++++++++++++++++++++++++++++++++++++--- gst/rtpmanager/rtpsession.h | 7 +++- gst/rtpmanager/rtpsource.c | 19 ++++++++-- gst/rtpmanager/rtpsource.h | 2 ++ 6 files changed, 145 insertions(+), 8 deletions(-) diff --git a/gst/rtpmanager/gstrtpsession.c b/gst/rtpmanager/gstrtpsession.c index 7f5782b..554422f 100644 --- a/gst/rtpmanager/gstrtpsession.c +++ b/gst/rtpmanager/gstrtpsession.c @@ -648,8 +648,10 @@ gst_rtp_session_change_state (GstElement * element, GstStateChange transition) { GstStateChangeReturn res; GstRtpSession *rtpsession; + GstRtpSessionPrivate *priv; rtpsession = GST_RTP_SESSION (element); + priv = rtpsession->priv; switch (transition) { case GST_STATE_CHANGE_NULL_TO_READY: @@ -660,6 +662,7 @@ gst_rtp_session_change_state (GstElement * element, GstStateChange transition) break; case GST_STATE_CHANGE_PLAYING_TO_PAUSED: stop_rtcp_thread (rtpsession); + break; default: break; } @@ -668,9 +671,17 @@ gst_rtp_session_change_state (GstElement * element, GstStateChange transition) switch (transition) { case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + { + GstClockTime base_time; + + base_time = GST_ELEMENT_CAST (rtpsession)->base_time; + + rtp_session_set_base_time (priv->session, base_time); + if (!start_rtcp_thread (rtpsession)) goto failed_thread; break; + } case GST_STATE_CHANGE_PLAYING_TO_PAUSED: break; case GST_STATE_CHANGE_PAUSED_TO_READY: @@ -960,6 +971,40 @@ gst_rtp_session_event_send_rtp_sink (GstPad * pad, GstEvent * event) GST_DEBUG_OBJECT (rtpsession, "received event"); switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_NEWSEGMENT: + { + gboolean update; + gdouble rate, arate; + GstFormat format; + gint64 start, stop, time; + GstSegment *segment; + + segment = &rtpsession->send_rtp_seg; + + /* the newsegment event is needed to convert the RTP timestamp to + * running_time, which is needed to generate a mapping from RTP to NTP + * timestamps in SR reports */ + gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format, + &start, &stop, &time); + + GST_DEBUG_OBJECT (rtpsession, + "configured NEWSEGMENT update %d, rate %lf, applied rate %lf, " + "format GST_FORMAT_TIME, " + "%" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT + ", time %" GST_TIME_FORMAT ", accum %" GST_TIME_FORMAT, + update, rate, arate, GST_TIME_ARGS (segment->start), + GST_TIME_ARGS (segment->stop), GST_TIME_ARGS (segment->time), + GST_TIME_ARGS (segment->accum)); + + gst_segment_set_newsegment_full (segment, update, rate, + arate, format, start, stop, time); + + rtp_session_set_timestamp_sync (priv->session, start); + + /* push event forward */ + ret = gst_pad_push_event (rtpsession->send_rtp_src, event); + break; + } default: ret = gst_pad_push_event (rtpsession->send_rtp_src, event); break; @@ -991,7 +1036,6 @@ gst_rtp_session_chain_send_rtp (GstPad * pad, GstBuffer * buffer) return ret; } - /* Create sinkpad to receive RTP packets from senders. This will also create a * srcpad for the RTP packets. */ diff --git a/gst/rtpmanager/gstrtpsession.h b/gst/rtpmanager/gstrtpsession.h index 87f8ea7..09565ac 100644 --- a/gst/rtpmanager/gstrtpsession.h +++ b/gst/rtpmanager/gstrtpsession.h @@ -45,6 +45,7 @@ struct _GstRtpSession { GstPad *recv_rtp_sink; GstPad *recv_rtcp_sink; GstPad *send_rtp_sink; + GstSegment send_rtp_seg; GstPad *recv_rtp_src; GstPad *sync_src; diff --git a/gst/rtpmanager/rtpsession.c b/gst/rtpmanager/rtpsession.c index 6fa478c..275e7c7 100644 --- a/gst/rtpmanager/rtpsession.c +++ b/gst/rtpmanager/rtpsession.c @@ -1048,8 +1048,8 @@ ignore: } /* A Sender report contains statistics about how the sender is doing. This - * includes timing informataion about the relation between RTP and NTP - * timestamps is it using and the number of packets/bytes it sent to us. + * includes timing informataion such as the relation between RTP and NTP + * timestamps and the number of packets/bytes it sent to us. * * In this report is also included a set of report blocks related to how this * sender is receiving data (in case we (or somebody else) is also sending stuff @@ -1429,6 +1429,36 @@ invalid_packet: } } +/** + * rtp_session_set_send_sync + * @sess: an #RTPSession + * @base_time: the clock base time + * @start_time: the timestamp start time + * + * Establish a relation between the times returned by the get_time callback and + * the buffer timestamps. This information is used to convert the NTP times to + * RTP timestamps. + */ +void +rtp_session_set_base_time (RTPSession * sess, GstClockTime base_time) +{ + g_return_if_fail (RTP_IS_SESSION (sess)); + + RTP_SESSION_LOCK (sess); + sess->base_time = base_time; + RTP_SESSION_UNLOCK (sess); +} + +void +rtp_session_set_timestamp_sync (RTPSession * sess, GstClockTime start_timestamp) +{ + g_return_if_fail (RTP_IS_SESSION (sess)); + + RTP_SESSION_LOCK (sess); + sess->start_timestamp = start_timestamp; + RTP_SESSION_UNLOCK (sess); +} + static GstClockTime calculate_rtcp_interval (RTPSession * sess, gboolean deterministic, gboolean first) @@ -1575,16 +1605,56 @@ session_start_rtcp (RTPSession * sess, ReportData * data) if (RTP_SOURCE_IS_SENDER (own)) { guint64 ntptime; guint32 rtptime; + GstClockTime running_time; + GstClockTimeDiff diff; /* we are a sender, create SR */ GST_DEBUG ("create SR for SSRC %08x", own->ssrc); gst_rtcp_buffer_add_packet (data->rtcp, GST_RTCP_TYPE_SR, packet); - /* convert clock time to NTP time */ + /* use the sync params to interpollate 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 + * sufficient for the frequency at which we report SR and the rate we send + * out RTP packets. */ + rtptime = own->last_rtptime; + GST_DEBUG ("last_timestamp %" GST_TIME_FORMAT ", last_rtptime %" + G_GUINT32_FORMAT, GST_TIME_ARGS (own->last_timestamp), rtptime); + + if (own->clock_rate != -1) { + /* Start by calculating the running_time of the timestamp, this is a result + * in nanoseconds. */ + running_time = + (own->last_timestamp - sess->start_timestamp) + sess->base_time; + + /* get the diff with the SR time */ + diff = GST_CLOCK_DIFF (running_time, data->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 ("running_time %" GST_TIME_FORMAT ", diff %" GST_TIME_FORMAT, + GST_TIME_ARGS (running_time), GST_TIME_ARGS (diff)); + rtptime += gst_util_uint64_scale (diff, own->clock_rate, GST_SECOND); + } else { + diff = -diff; + GST_DEBUG ("running_time %" GST_TIME_FORMAT ", diff -%" GST_TIME_FORMAT, + GST_TIME_ARGS (running_time), GST_TIME_ARGS (diff)); + rtptime -= gst_util_uint64_scale (diff, own->clock_rate, GST_SECOND); + } + } else { + GST_WARNING ("no clock-rate, cannot interpollate rtp time"); + } + + /* convert clock time to NTP time. upper 32 bits should contain the seconds + * and the lower 32 bits, the fractions of a second. */ ntptime = gst_util_uint64_scale (data->time, (1LL << 32), GST_SECOND); + /* conversion from unix timestamp (seconds since 1970) to NTP (seconds + * since 1900). FIXME nothing says that the time is in unix timestamps. */ ntptime += (2208988800LL << 32); - rtptime = 0; + GST_DEBUG ("NTP %08x:%08x, RTP %" G_GUINT32_FORMAT, + (guint32) (ntptime >> 32), (guint32) (ntptime & 0xffffffff), rtptime); /* fill in sender report info, FIXME RTP timestamps missing */ gst_rtcp_packet_sr_set_sender_info (packet, own->ssrc, diff --git a/gst/rtpmanager/rtpsession.h b/gst/rtpmanager/rtpsession.h index 9082d9d..9380b55 100644 --- a/gst/rtpmanager/rtpsession.h +++ b/gst/rtpmanager/rtpsession.h @@ -189,6 +189,10 @@ struct _RTPSession { gpointer user_data; RTPSessionStats stats; + + /* for mapping RTP time to NTP time */ + GstClockTime start_timestamp; + GstClockTime base_time; }; /** @@ -251,7 +255,8 @@ GstFlowReturn rtp_session_process_rtcp (RTPSession *sess, GstBuffer /* processing packets for sending */ GstFlowReturn rtp_session_send_rtp (RTPSession *sess, GstBuffer *buffer); - +void rtp_session_set_base_time (RTPSession *sess, GstClockTime base_time); +void rtp_session_set_timestamp_sync (RTPSession *sess, GstClockTime start_timestamp); /* stopping the session */ GstFlowReturn rtp_session_send_bye (RTPSession *sess, const gchar *reason); diff --git a/gst/rtpmanager/rtpsource.c b/gst/rtpmanager/rtpsource.c index 1a98951..ad491bd 100644 --- a/gst/rtpmanager/rtpsource.c +++ b/gst/rtpmanager/rtpsource.c @@ -456,6 +456,7 @@ rtp_source_send_rtp (RTPSource * src, GstBuffer * buffer) { GstFlowReturn result = GST_FLOW_OK; guint len; + GstClockTime timestamp; g_return_val_if_fail (RTP_IS_SOURCE (src), GST_FLOW_ERROR); g_return_val_if_fail (GST_IS_BUFFER (buffer), GST_FLOW_ERROR); @@ -469,18 +470,32 @@ rtp_source_send_rtp (RTPSource * src, GstBuffer * buffer) src->stats.packets_sent++; src->stats.octets_sent += len; + /* we keep track of the last received RTP timestamp and the corresponding + * GStreamer timestamp so that we can convert NTP time to RTP time when + * sending SR reports */ + src->last_rtptime = gst_rtp_buffer_get_timestamp (buffer); + + /* the timestamp can be undefined, in that case we use any previously + * received timestamp */ + timestamp = GST_BUFFER_TIMESTAMP (buffer); + if (timestamp != -1) + src->last_timestamp = timestamp; + /* push packet */ if (src->callbacks.push_rtp) { guint32 ssrc; ssrc = gst_rtp_buffer_get_ssrc (buffer); if (ssrc != src->ssrc) { - GST_DEBUG ("updating SSRC from %u to %u", ssrc, src->ssrc); + /* the SSRC of the packet is not correct, make a writable buffer and + * update the SSRC. This could involve a complete copy of the packet when + * it is not writable. Usually the payloader will use caps negotiation to + * get the correct SSRC. */ buffer = gst_buffer_make_writable (buffer); + GST_DEBUG ("updating SSRC from %u to %u", ssrc, src->ssrc); gst_rtp_buffer_set_ssrc (buffer, src->ssrc); } - GST_DEBUG ("pushing RTP packet %" G_GUINT64_FORMAT, src->stats.packets_sent); result = src->callbacks.push_rtp (src, buffer, src->user_data); diff --git a/gst/rtpmanager/rtpsource.h b/gst/rtpmanager/rtpsource.h index 0df03f4..7920b6f 100644 --- a/gst/rtpmanager/rtpsource.h +++ b/gst/rtpmanager/rtpsource.h @@ -139,6 +139,8 @@ struct _RTPSource { GstClockTime bye_time; GstClockTime last_activity; GstClockTime last_rtp_activity; + GstClockTime last_timestamp; + GstClockTime last_rtptime; GQueue *packets; -- 2.7.4