gst/rtpmanager/gstrtpsession.*: Distribute synchronisation parameters to the session...
authorWim Taymans <wim.taymans@gmail.com>
Wed, 29 Aug 2007 01:22:43 +0000 (01:22 +0000)
committerTim-Philipp Müller <tim.muller@collabora.co.uk>
Tue, 11 Aug 2009 01:30:29 +0000 (02:30 +0100)
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
gst/rtpmanager/gstrtpsession.h
gst/rtpmanager/rtpsession.c
gst/rtpmanager/rtpsession.h
gst/rtpmanager/rtpsource.c
gst/rtpmanager/rtpsource.h

index 7f5782b..554422f 100644 (file)
@@ -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.
  */
index 87f8ea7..09565ac 100644 (file)
@@ -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;
index 6fa478c..275e7c7 100644 (file)
@@ -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,
index 9082d9d..9380b55 100644 (file)
@@ -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);
 
index 1a98951..ad491bd 100644 (file)
@@ -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);
index 0df03f4..7920b6f 100644 (file)
@@ -139,6 +139,8 @@ struct _RTPSource {
   GstClockTime  bye_time;
   GstClockTime  last_activity;
   GstClockTime  last_rtp_activity;
+  GstClockTime  last_timestamp;
+  GstClockTime  last_rtptime;
 
   GQueue       *packets;