gst/rtpmanager/gstrtpbin.*: Add signal to notify listeners when a sender becomes...
[platform/upstream/gst-plugins-good.git] / gst / rtpmanager / gstrtpbin.c
index 0906a8d..7f402c3 100644 (file)
  * SECTION:element-gstrtpbin
  * @see_also: gstrtpjitterbuffer, gstrtpsession, gstrtpptdemux, gstrtpssrcdemux
  *
- * <refsect2>
- * <para>
- * RTP bin combines the functions of gstrtpsession, gstrtpssrcdemux, gstrtpjitterbuffer
- * and gstrtpptdemux in one element. It allows for multiple RTP sessions that will
- * be synchronized together using RTCP SR packets.
- * </para>
- * <para>
- * gstrtpbin is configured with a number of request pads that define the
- * functionality that is activated, similar to the gstrtpsession element.
- * </para>
- * <para>
- * To use gstrtpbin as an RTP receiver, request a recv_rtp_sink_%%d pad. The session
+ * RTP bin combines the functions of #GstRtpSession, #GstRtpsSrcDemux,
+ * #GstRtpJitterBuffer and #GstRtpPtDemux in one element. It allows for multiple
+ * RTP sessions that will be synchronized together using RTCP SR packets.
+ * 
+ * #GstRtpBin is configured with a number of request pads that define the
+ * functionality that is activated, similar to the #GstRtpSession element.
+ * 
+ * To use #GstRtpBin as an RTP receiver, request a recv_rtp_sink_%%d pad. The session
  * number must be specified in the pad name. 
  * Data received on the recv_rtp_sink_%%d pad will be processed in the gstrtpsession
- * manager and after being validated forwarded on gstrtpssrcdemuxer element. Each
- * RTP stream is demuxed based on the SSRC and send to a gstrtpjitterbuffer. After
+ * manager and after being validated forwarded on #GstRtpsSrcDemux element. Each
+ * RTP stream is demuxed based on the SSRC and send to a #GstRtpJitterBuffer. After
  * the packets are released from the jitterbuffer, they will be forwarded to a
- * gstrtpptdemuxer element. The gstrtpptdemuxer element will demux the packets based
+ * #GstRtpsSrcDemux element. The #GstRtpsSrcDemux element will demux the packets based
  * on the payload type and will create a unique pad recv_rtp_src_%%d_%%d_%%d on
  * gstrtpbin with the session number, SSRC and payload type respectively as the pad
  * name.
- * </para>
- * <para>
- * To also use gstrtpbin as an RTCP receiver, request a recv_rtcp_sink_%%d pad. The
+ * 
+ * To also use #GstRtpBin as an RTCP receiver, request a recv_rtcp_sink_%%d pad. The
  * session number must be specified in the pad name.
- * </para>
- * <para>
+ * 
  * If you want the session manager to generate and send RTCP packets, request
  * the send_rtcp_src_%%d pad with the session number in the pad name. Packet pushed
  * on this pad contain SR/RR RTCP reports that should be sent to all participants
  * in the session.
- * </para>
- * <para>
- * To use gstrtpbin as a sender, request a send_rtp_sink_%%d pad, which will
+ * 
+ * To use #GstRtpBin as a sender, request a send_rtp_sink_%%d pad, which will
  * automatically create a send_rtp_src_%%d pad. If the session number is not provided,
  * the pad from the lowest available session will be returned. The session manager will modify the
  * SSRC in the RTP packets to its own SSRC and wil forward the packets on the
  * send_rtp_src_%%d pad after updating its internal state.
- * </para>
- * <para>
+ * 
  * The session manager needs the clock-rate of the payload types it is handling
- * and will signal the GstRtpSession::request-pt-map signal when it needs such a
- * mapping. One can clear the cached values with the GstRtpSession::clear-pt-map
+ * and will signal the #GstRtpSession::request-pt-map signal when it needs such a
+ * mapping. One can clear the cached values with the #GstRtpSession::clear-pt-map
  * signal.
- * </para>
+ * 
+ * <refsect2>
  * <title>Example pipelines</title>
- * <para>
- * <programlisting>
+ * |[
  * gst-launch udpsrc port=5000 caps="application/x-rtp, ..." ! .recv_rtp_sink_0 \
  *     gstrtpbin ! rtptheoradepay ! theoradec ! xvimagesink
- * </programlisting>
- * Receive RTP data from port 5000 and send to the session 0 in gstrtpbin.
- * </para>
- * <para>
- * <programlisting>
+ * ]| Receive RTP data from port 5000 and send to the session 0 in gstrtpbin.
+ * |[
  * gst-launch gstrtpbin name=rtpbin \
  *         v4l2src ! ffmpegcolorspace ! ffenc_h263 ! rtph263ppay ! rtpbin.send_rtp_sink_0 \
  *                   rtpbin.send_rtp_src_0 ! udpsink port=5000                            \
@@ -85,8 +74,7 @@
  *                   rtpbin.send_rtp_src_1 ! udpsink port=5002                            \
  *                   rtpbin.send_rtcp_src_1 ! udpsink port=5003 sync=false async=false    \
  *                   udpsrc port=5007 ! rtpbin.recv_rtcp_sink_1
- * </programlisting>
- * Encode and payload H263 video captured from a v4l2src. Encode and payload AMR
+ * ]| Encode and payload H263 video captured from a v4l2src. Encode and payload AMR
  * audio generated from audiotestsrc. The video is sent to session 0 in rtpbin
  * and the audio is sent to session 1. Video packets are sent on UDP port 5000
  * and audio packets on port 5002. The video RTCP packets for session 0 are sent
  * is received on port 5007. Since RTCP packets from the sender should be sent
  * as soon as possible and do not participate in preroll, sync=false and 
  * async=false is configured on udpsink
- * </para>
- * <para>
- * <programlisting>
- *  gst-launch -v gstrtpbin name=rtpbin                                          \
+ * |[
+ * gst-launch -v gstrtpbin name=rtpbin                                          \
  *     udpsrc caps="application/x-rtp,media=(string)video,clock-rate=(int)90000,encoding-name=(string)H263-1998" \
  *             port=5000 ! rtpbin.recv_rtp_sink_0                                \
  *         rtpbin. ! rtph263pdepay ! ffdec_h263 ! xvimagesink                    \
  *         rtpbin. ! rtpamrdepay ! amrnbdec ! alsasink                           \
  *      udpsrc port=5003 ! rtpbin.recv_rtcp_sink_1                               \
  *      rtpbin.send_rtcp_src_1 ! udpsink port=5007 sync=false async=false
- * </programlisting>
- * Receive H263 on port 5000, send it through rtpbin in session 0, depayload,
+ * ]| Receive H263 on port 5000, send it through rtpbin in session 0, depayload,
  * decode and display the video.
  * Receive AMR on port 5002, send it through rtpbin in session 1, depayload,
  * decode and play the audio.
  * synchronisation.
  * Send RTCP reports for session 0 on port 5005 and RTCP reports for session 1
  * on port 5007.
- * </para>
  * </refsect2>
  *
  * Last reviewed on 2007-08-30 (0.10.6)
 
 #include "gstrtpbin-marshal.h"
 #include "gstrtpbin.h"
+#include "gstrtpsession.h"
+#include "gstrtpjitterbuffer.h"
 
 GST_DEBUG_CATEGORY_STATIC (gst_rtp_bin_debug);
 #define GST_CAT_DEFAULT gst_rtp_bin_debug
@@ -251,6 +237,7 @@ enum
   SIGNAL_ON_BYE_SSRC,
   SIGNAL_ON_BYE_TIMEOUT,
   SIGNAL_ON_TIMEOUT,
+  SIGNAL_ON_SENDER_TIMEOUT,
   LAST_SIGNAL
 };
 
@@ -333,11 +320,11 @@ struct _GstRtpBinStream
   gint64 unix_delta;
 
   /* for lip-sync */
+  guint64 last_clock_base;
   guint64 clock_base;
   guint64 clock_base_time;
   gint clock_rate;
   gint64 ts_offset;
-  gint64 prev_ts_offset;
   gint last_pt;
 };
 
@@ -469,6 +456,13 @@ on_timeout (GstElement * session, guint32 ssrc, GstRtpBinSession * sess)
       sess->id, ssrc);
 }
 
+static void
+on_sender_timeout (GstElement * session, guint32 ssrc, GstRtpBinSession * sess)
+{
+  g_signal_emit (sess->bin, gst_rtp_bin_signals[SIGNAL_ON_SENDER_TIMEOUT], 0,
+      sess->id, ssrc);
+}
+
 /* create a session with the given id.  Must be called with RTP_BIN_LOCK */
 static GstRtpBinSession *
 create_session (GstRtpBin * rtpbin, gint id)
@@ -521,6 +515,8 @@ create_session (GstRtpBin * rtpbin, gint id)
   g_signal_connect (sess->session, "on-bye-timeout",
       (GCallback) on_bye_timeout, sess);
   g_signal_connect (sess->session, "on-timeout", (GCallback) on_timeout, sess);
+  g_signal_connect (sess->session, "on-sender-timeout",
+      (GCallback) on_sender_timeout, sess);
 
   /* FIXME, change state only to what's needed */
   gst_bin_add (GST_BIN_CAST (rtpbin), session);
@@ -634,11 +630,24 @@ get_pt_map (GstRtpBinSession * session, guint pt)
   g_value_init (&ret, GST_TYPE_CAPS);
   g_value_set_boxed (&ret, NULL);
 
+  GST_RTP_SESSION_UNLOCK (session);
+
   g_signal_emitv (args, gst_rtp_bin_signals[SIGNAL_REQUEST_PT_MAP], 0, &ret);
 
+  GST_RTP_SESSION_LOCK (session);
+
   g_value_unset (&args[0]);
   g_value_unset (&args[1]);
   g_value_unset (&args[2]);
+
+  /* look in the cache again because we let the lock go */
+  caps = g_hash_table_lookup (session->ptmap, GINT_TO_POINTER (pt));
+  if (caps) {
+    gst_caps_ref (caps);
+    g_value_unset (&ret);
+    goto done;
+  }
+
   caps = (GstCaps *) g_value_dup_boxed (&ret);
   g_value_unset (&ret);
   if (!caps)
@@ -864,28 +873,32 @@ gst_rtp_bin_associate (GstRtpBin * bin, GstRtpBinStream * stream, guint8 len,
     /* calculate offsets for each stream */
     for (walk = client->streams; walk; walk = g_slist_next (walk)) {
       GstRtpBinStream *ostream = (GstRtpBinStream *) walk->data;
-
-      if (ostream->unix_delta == 0)
-        continue;
+      gint64 prev_ts_offset;
 
       ostream->ts_offset = ostream->unix_delta - min;
 
+      g_object_get (ostream->buffer, "ts-offset", &prev_ts_offset, NULL);
+
       /* delta changed, see how much */
-      if (ostream->prev_ts_offset != ostream->ts_offset) {
+      if (prev_ts_offset != ostream->ts_offset) {
         gint64 diff;
 
-        if (ostream->prev_ts_offset > ostream->ts_offset)
-          diff = ostream->prev_ts_offset - ostream->ts_offset;
+        if (prev_ts_offset > ostream->ts_offset)
+          diff = prev_ts_offset - ostream->ts_offset;
         else
-          diff = ostream->ts_offset - ostream->prev_ts_offset;
+          diff = ostream->ts_offset - prev_ts_offset;
+
+        GST_DEBUG_OBJECT (bin,
+            "ts-offset %" G_GUINT64_FORMAT ", prev %" G_GUINT64_FORMAT
+            ", diff: %" G_GINT64_FORMAT, ostream->ts_offset, prev_ts_offset,
+            diff);
 
-        /* only change diff when it changed more than 1 millisecond. This
+        /* only change diff when it changed more than 4 milliseconds. This
          * compensates for rounding errors in NTP to RTP timestamp
          * conversions */
-        if (diff > GST_MSECOND)
+        if (diff > 4 * GST_MSECOND && diff < (3 * GST_SECOND)) {
           g_object_set (ostream->buffer, "ts-offset", ostream->ts_offset, NULL);
-
-        ostream->prev_ts_offset = ostream->ts_offset;
+        }
       }
       GST_DEBUG_OBJECT (bin, "stream SSRC %08x, delta %" G_GINT64_FORMAT,
           ostream->ssrc, ostream->ts_offset);
@@ -932,6 +945,8 @@ gst_rtp_bin_sync_chain (GstPad * pad, GstBuffer * buffer)
   guint32 rtptime;
   gboolean have_sr, have_sdes;
   gboolean more;
+  guint64 clock_base;
+  guint64 clock_base_time;
 
   stream = gst_pad_get_element_private (pad);
   bin = stream->bin;
@@ -941,6 +956,20 @@ gst_rtp_bin_sync_chain (GstPad * pad, GstBuffer * buffer)
   if (!gst_rtcp_buffer_validate (buffer))
     goto invalid_rtcp;
 
+  /* get the last relation between the rtp timestamps and the gstreamer
+   * timestamps. We get this info directly from the jitterbuffer which
+   * constructs gstreamer timestamps from rtp timestamps */
+  gst_rtp_jitter_buffer_get_sync (GST_RTP_JITTER_BUFFER (stream->buffer),
+      &clock_base, &clock_base_time);
+
+  /* clock base changes when there is a huge gap in the timestamps or seqnum.
+   * When this happens we don't want to calculate the extended timestamp based
+   * on the previous one but reset the calculation. */
+  if (stream->last_clock_base != clock_base) {
+    stream->last_extrtptime = -1;
+    stream->last_clock_base = clock_base;
+  }
+
   have_sr = FALSE;
   have_sdes = FALSE;
   GST_RTCP_BUFFER_FOR_PACKETS (more, buffer, &packet) {
@@ -992,8 +1021,8 @@ gst_rtp_bin_sync_chain (GstPad * pad, GstBuffer * buffer)
             gst_rtcp_packet_sdes_get_entry (&packet, &type, &len, &data);
 
             if (type == GST_RTCP_SDES_CNAME) {
-              stream->clock_base = GST_BUFFER_OFFSET (buffer);
-              stream->clock_base_time = GST_BUFFER_OFFSET_END (buffer);
+              stream->clock_base = clock_base;
+              stream->clock_base_time = clock_base_time;
               /* associate the stream to CNAME */
               gst_rtp_bin_associate (bin, stream, len, data);
             }
@@ -1200,7 +1229,7 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass)
    * @rtpbin: the object which received the signal
    *
    * Clear all previously cached pt-mapping obtained with
-   * GstRtpBin::request-pt-map.
+   * #GstRtpBin::request-pt-map.
    */
   gst_rtp_bin_signals[SIGNAL_CLEAR_PT_MAP] =
       g_signal_new ("clear-pt-map", G_TYPE_FROM_CLASS (klass),
@@ -1313,6 +1342,19 @@ gst_rtp_bin_class_init (GstRtpBinClass * klass)
       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, on_timeout),
       NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2,
       G_TYPE_UINT, G_TYPE_UINT);
+  /**
+   * GstRtpBin::on-sender-timeout:
+   * @rtpbin: the object which received the signal
+   * @session: the session
+   * @ssrc: the SSRC 
+   *
+   * Notify of a sender SSRC that has timed out and became a receiver
+   */
+  gst_rtp_bin_signals[SIGNAL_ON_SENDER_TIMEOUT] =
+      g_signal_new ("on-sender-timeout", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, on_sender_timeout),
+      NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2,
+      G_TYPE_UINT, G_TYPE_UINT);
 
   g_object_class_install_property (gobject_class, PROP_SDES_CNAME,
       g_param_spec_string ("sdes-cname", "SDES CNAME",
@@ -1850,7 +1892,8 @@ new_ssrc_pad_found (GstElement * element, guint ssrc, GstPad * pad,
 
   rtpbin = session->bin;
 
-  GST_DEBUG_OBJECT (rtpbin, "new SSRC pad %08x", ssrc);
+  GST_DEBUG_OBJECT (rtpbin, "new SSRC pad %08x, %s:%s", ssrc,
+      GST_DEBUG_PAD_NAME (pad));
 
   GST_RTP_BIN_SHUTDOWN_LOCK (rtpbin, shutdown);
 
@@ -1878,6 +1921,7 @@ new_ssrc_pad_found (GstElement * element, guint ssrc, GstPad * pad,
           gst_caps_to_string (caps), GST_DEBUG_PAD_NAME (pad));
     }
 
+    stream->last_clock_base = -1;
     if (gst_structure_get_uint (s, "clock-base", &val))
       stream->clock_base = val;
     else
@@ -2315,6 +2359,7 @@ gst_rtp_bin_request_new_pad (GstElement * element,
   GstRtpBin *rtpbin;
   GstElementClass *klass;
   GstPad *result;
+
   gchar *pad_name = NULL;
 
   g_return_val_if_fail (templ != NULL, NULL);