rtpsession: Make it possible to favor new sources in case of SSRC conflict
authorOlivier CrĂȘte <olivier.crete@collabora.co.uk>
Fri, 5 Mar 2010 15:08:45 +0000 (16:08 +0100)
committerWim Taymans <wim.taymans@collabora.co.uk>
Wed, 10 Mar 2010 10:21:19 +0000 (11:21 +0100)
Add a "favor-new" property that tells the session to favor new sources when
there is a SSRC conflict. This is useful for SIP calls and other such cases
where a remote loop is extremely unlikely.

Fixes #607615

gst/rtpmanager/rtpsession.c
gst/rtpmanager/rtpsession.h
gst/rtpmanager/rtpsource.c
gst/rtpmanager/rtpsource.h

index 99ec2e5..8ffb5a4 100644 (file)
@@ -66,6 +66,7 @@ enum
   PROP_NUM_SOURCES,
   PROP_NUM_ACTIVE_SOURCES,
   PROP_SOURCES,
+  PROP_FAVOR_NEW,
   PROP_LAST
 };
 
@@ -307,6 +308,12 @@ rtp_session_class_init (RTPSessionClass * klass)
           "An array of all known sources in the session",
           G_TYPE_VALUE_ARRAY, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
 
+  g_object_class_install_property (gobject_class, PROP_FAVOR_NEW,
+      g_param_spec_boolean ("favor-new", "Favor new sources",
+          "Resolve SSRC conflict in favor of new sources", FALSE,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+
   klass->get_source_by_ssrc =
       GST_DEBUG_FUNCPTR (rtp_session_get_source_by_ssrc);
 
@@ -431,6 +438,9 @@ rtp_session_set_property (GObject * object, guint prop_id,
     case PROP_SDES:
       rtp_session_set_sdes_struct (sess, g_value_get_boxed (value));
       break;
+    case PROP_FAVOR_NEW:
+      sess->favor_new = g_value_get_boolean (value);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -473,6 +483,9 @@ rtp_session_get_property (GObject * object, guint prop_id,
     case PROP_SOURCES:
       g_value_take_boxed (value, rtp_session_create_sources (sess));
       break;
+    case PROP_FAVOR_NEW:
+      g_value_set_boolean (value, sess->favor_new);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -978,33 +991,70 @@ check_collision (RTPSession * sess, RTPSource * source,
     return FALSE;
 
   if (sess->source != source) {
+    GstNetAddress *from;
+    gboolean have_from;
+
     /* This is not our local source, but lets check if two remote
      * source collide
      */
+
     if (rtp) {
-      if (source->have_rtp_from) {
-        if (gst_netaddress_equal (&source->rtp_from, &arrival->address))
-          /* Address is the same */
-          return FALSE;
-      } else {
-        /* We don't already have a from address for RTP, just set it */
-        rtp_source_set_rtp_from (source, &arrival->address);
+      from = &source->rtp_from;
+      have_from = source->have_rtp_from;
+    } else {
+      from = &source->rtcp_from;
+      have_from = source->have_rtcp_from;
+    }
+
+    if (have_from) {
+      if (gst_netaddress_equal (from, &arrival->address)) {
+        /* Address is the same */
         return FALSE;
+      } else {
+        GST_LOG ("we have a third-party collision or loop ssrc:%x",
+            rtp_source_get_ssrc (source));
+        if (sess->favor_new) {
+          if (rtp_source_find_conflicting_address (source,
+                  &arrival->address, arrival->current_time)) {
+            gchar buf1[40];
+            gst_netaddress_to_string (&arrival->address, buf1, 40);
+            GST_LOG ("Known conflict on %x for %s, dropping packet",
+                rtp_source_get_ssrc (source), buf1);
+            return TRUE;
+          } else {
+            gchar buf1[40], buf2[40];
+
+            /* Current address is not a known conflict, lets assume this is
+             * a new source. Save old address in possible conflict list
+             */
+            rtp_source_add_conflicting_address (source, from,
+                arrival->current_time);
+
+            gst_netaddress_to_string (from, buf1, 40);
+            gst_netaddress_to_string (&arrival->address, buf2, 40);
+            GST_DEBUG ("New conflict for ssrc %x, replacing %s with %s,"
+                " saving old as known conflict",
+                rtp_source_get_ssrc (source), buf1, buf2);
+
+            if (rtp)
+              rtp_source_set_rtp_from (source, &arrival->address);
+            else
+              rtp_source_set_rtcp_from (source, &arrival->address);
+            return FALSE;
+          }
+        } else {
+          /* Don't need to save old addresses, we ignore new sources */
+          return TRUE;
+        }
       }
     } else {
-      if (source->have_rtcp_from) {
-        if (gst_netaddress_equal (&source->rtcp_from, &arrival->address))
-          /* Address is the same */
-          return FALSE;
-      } else {
-        /* We don't already have a from address for RTCP, just set it */
+      /* We don't already have a from address for RTP, just set it */
+      if (rtp)
+        rtp_source_set_rtp_from (source, &arrival->address);
+      else
         rtp_source_set_rtcp_from (source, &arrival->address);
-        return FALSE;
-      }
+      return FALSE;
     }
-    /* We received RTP or RTCP from this source before but the network address
-     * changed. In this case, we have third-party collision or loop */
-    GST_DEBUG ("we have a third-party collision or loop");
 
     /* FIXME: Log 3rd party collision somehow
      * Maybe should be done in upper layer, only the SDES can tell us
@@ -1013,7 +1063,7 @@ check_collision (RTPSession * sess, RTPSource * source,
   } else {
     /* This is sending with our ssrc, is it an address we already know */
 
-    if (rtp_source_find_add_conflicting_address (source, &arrival->address,
+    if (rtp_source_find_conflicting_address (source, &arrival->address,
             arrival->current_time)) {
       /* Its a known conflict, its probably a loop, not a collision
        * lets just drop the incoming packet
@@ -1022,6 +1072,9 @@ check_collision (RTPSession * sess, RTPSource * source,
     } else {
       /* Its a new collision, lets change our SSRC */
 
+      rtp_source_add_conflicting_address (source, &arrival->address,
+          arrival->current_time);
+
       GST_DEBUG ("Collision for SSRC %x", rtp_source_get_ssrc (source));
       on_ssrc_collision (sess, source);
 
index 1a5909a..445efd5 100644 (file)
@@ -191,6 +191,7 @@ struct _RTPSession {
   RTPSessionStats stats;
 
   gboolean      change_ssrc;
+  gboolean      favor_new;
 };
 
 /**
index 568811b..247fa7a 100644 (file)
@@ -1587,23 +1587,22 @@ rtp_source_get_last_rb (RTPSource * src, guint8 * fractionlost,
 }
 
 /**
- * rtp_source_find_add_conflicting_address:
+ * rtp_source_find_conflicting_address:
  * @src: The source the packet came in
  * @address: address to check for
- * @time: The time when the packet that is in conflict arrived
+ * @time: The time when the packet that is possibly in conflict arrived
  *
- * Checks if an address which has a conflict is already known,
- *  otherwise remembers it to prevent loops.
+ * Checks if an address which has a conflict is already known. If it is
+ * a known conflict, remember the time
  *
  * Returns: TRUE if it was a known conflict, FALSE otherwise
  */
 
 gboolean
-rtp_source_find_add_conflicting_address (RTPSource * src,
-    GstNetAddress * address, GstClockTime time)
+rtp_source_find_conflicting_address (RTPSource * src, GstNetAddress * address,
+    GstClockTime time)
 {
   GList *item;
-  RTPConflictingAddress *new_conflict;
 
   for (item = g_list_first (src->conflicting_addresses);
       item; item = g_list_next (item)) {
@@ -1615,6 +1614,24 @@ rtp_source_find_add_conflicting_address (RTPSource * src,
     }
   }
 
+  return FALSE;
+}
+
+/**
+ * rtp_source_add_conflicting_address:
+ * @src: The source the packet came in
+ * @address: address to remember
+ * @time: The time when the packet that is in conflict arrived
+ *
+ * Adds a new conflict address
+ */
+
+void
+rtp_source_add_conflicting_address (RTPSource * src,
+    GstNetAddress * address, GstClockTime time)
+{
+  RTPConflictingAddress *new_conflict;
+
   new_conflict = g_new0 (RTPConflictingAddress, 1);
 
   memcpy (&new_conflict->address, address, sizeof (GstNetAddress));
@@ -1622,8 +1639,6 @@ rtp_source_find_add_conflicting_address (RTPSource * src,
 
   src->conflicting_addresses = g_list_prepend (src->conflicting_addresses,
       new_conflict);
-
-  return FALSE;
 }
 
 /**
index 7cba4e3..49c6528 100644 (file)
@@ -236,7 +236,11 @@ gboolean        rtp_source_get_last_rb         (RTPSource *src, guint8 *fraction
 
 void            rtp_source_reset               (RTPSource * src);
 
-gboolean        rtp_source_find_add_conflicting_address (RTPSource * src,
+gboolean        rtp_source_find_conflicting_address (RTPSource * src,
+                                                GstNetAddress *address,
+                                                GstClockTime time);
+
+void            rtp_source_add_conflicting_address (RTPSource * src,
                                                 GstNetAddress *address,
                                                 GstClockTime time);