rtpsession: Make it possible to favor new sources in case of SSRC conflict

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
This commit is contained in:
Olivier Crête 2010-03-05 16:08:45 +01:00 committed by Wim Taymans
parent f336ea283f
commit a6dfe96169
4 changed files with 102 additions and 29 deletions

View 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);

View file

@ -191,6 +191,7 @@ struct _RTPSession {
RTPSessionStats stats;
gboolean change_ssrc;
gboolean favor_new;
};
/**

View 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;
}
/**

View 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);