webrtc: advertise end-of-candidate with an empty candidate string

Just like what is done in the browsers.  When this is sent to the peer,
they will be able to know that no more candidates are coming and can
complete ICE.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4598>
This commit is contained in:
Matthew Waters 2023-05-11 17:28:52 +10:00 committed by GStreamer Marge Bot
parent 5e45a1b1bd
commit b10ec569d7
2 changed files with 138 additions and 16 deletions

View file

@ -821,6 +821,30 @@ _find_transport_for_session (GstWebRTCBin * webrtc, guint session_id)
return stream; return stream;
} }
static gboolean
match_stream_for_ice_transport (TransportStream * trans,
GstWebRTCICETransport * transport)
{
return trans->transport && trans->transport->transport == transport;
}
static TransportStream *
_find_transport_for_ice_transport (GstWebRTCBin * webrtc,
GstWebRTCICETransport * transport)
{
TransportStream *stream;
stream = _find_transport (webrtc, transport,
(FindTransportFunc) match_stream_for_ice_transport);
GST_TRACE_OBJECT (webrtc,
"Found transport %" GST_PTR_FORMAT " for ice transport %" GST_PTR_FORMAT,
stream, transport);
return stream;
}
typedef gboolean (*FindPadFunc) (GstWebRTCBinPad * p1, gconstpointer data); typedef gboolean (*FindPadFunc) (GstWebRTCBinPad * p1, gconstpointer data);
static GstWebRTCBinPad * static GstWebRTCBinPad *
@ -1601,13 +1625,6 @@ _update_ice_gathering_state_task (GstWebRTCBin * webrtc, gpointer data)
return NULL; return NULL;
} }
static void
_update_ice_gathering_state (GstWebRTCBin * webrtc)
{
gst_webrtc_bin_enqueue_task (webrtc, _update_ice_gathering_state_task, NULL,
NULL, NULL);
}
static GstStructure * static GstStructure *
_update_ice_connection_state_task (GstWebRTCBin * webrtc, gpointer data) _update_ice_connection_state_task (GstWebRTCBin * webrtc, gpointer data)
{ {
@ -2116,11 +2133,27 @@ _on_ice_transport_notify_state (GstWebRTCICETransport * transport,
_update_peer_connection_state (webrtc); _update_peer_connection_state (webrtc);
} }
static void
_on_local_ice_candidate_cb (GstWebRTCICE * ice, guint session_id,
gchar * candidate, GstWebRTCBin * webrtc);
static void static void
_on_ice_transport_notify_gathering_state (GstWebRTCICETransport * transport, _on_ice_transport_notify_gathering_state (GstWebRTCICETransport * transport,
GParamSpec * pspec, GstWebRTCBin * webrtc) GParamSpec * pspec, GstWebRTCBin * webrtc)
{ {
_update_ice_gathering_state (webrtc); GstWebRTCICEGatheringState ice_state;
g_object_get (transport, "gathering-state", &ice_state, NULL);
if (ice_state == GST_WEBRTC_ICE_GATHERING_STATE_COMPLETE) {
TransportStream *stream =
_find_transport_for_ice_transport (webrtc, transport);
/* signal end-of-candidates */
_on_local_ice_candidate_cb (webrtc->priv->ice, stream->session_id,
(char *) "", webrtc);
}
gst_webrtc_bin_enqueue_task (webrtc, _update_ice_gathering_state_task, NULL,
NULL, NULL);
} }
static void static void
@ -5411,6 +5444,24 @@ _add_ice_candidate_to_sdp (GstWebRTCBin * webrtc,
gst_sdp_media_add_attribute (media, "candidate", candidate + 10); gst_sdp_media_add_attribute (media, "candidate", candidate + 10);
} }
static void
_add_end_of_candidate_to_sdp (GstWebRTCBin * webrtc,
GstSDPMessage * sdp, gint mline_index)
{
GstSDPMedia *media = NULL;
if (mline_index < sdp->medias->len) {
media = &g_array_index (sdp->medias, GstSDPMedia, mline_index);
}
if (media == NULL) {
GST_WARNING_OBJECT (webrtc, "Couldn't find mline %d to merge ICE candidate",
mline_index);
return;
}
gst_sdp_media_add_attribute (media, "end-of-candidates", "");
}
static gboolean static gboolean
_filter_sdp_fields (GQuark field_id, const GValue * value, _filter_sdp_fields (GQuark field_id, const GValue * value,
GstStructure * new_structure) GstStructure * new_structure)
@ -6861,7 +6912,7 @@ _on_local_ice_candidate_task (GstWebRTCBin * webrtc)
IceCandidateItem *item = &g_array_index (items, IceCandidateItem, i); IceCandidateItem *item = &g_array_index (items, IceCandidateItem, i);
const gchar *cand = item->candidate; const gchar *cand = item->candidate;
if (!g_ascii_strncasecmp (cand, "a=candidate:", 12)) { if (cand && !g_ascii_strncasecmp (cand, "a=candidate:", 12)) {
/* stripping away "a=" */ /* stripping away "a=" */
cand += 2; cand += 2;
} }
@ -6876,12 +6927,24 @@ _on_local_ice_candidate_task (GstWebRTCBin * webrtc)
* FIXME: This ICE candidate should be stored somewhere with * FIXME: This ICE candidate should be stored somewhere with
* the associated mid and also merged back into any subsequent * the associated mid and also merged back into any subsequent
* local descriptions on renegotiation */ * local descriptions on renegotiation */
if (webrtc->current_local_description) if (webrtc->current_local_description) {
_add_ice_candidate_to_sdp (webrtc, webrtc->current_local_description->sdp, if (cand && cand[0] != '\0') {
item->mlineindex, cand); _add_ice_candidate_to_sdp (webrtc,
if (webrtc->pending_local_description) webrtc->current_local_description->sdp, item->mlineindex, cand);
_add_ice_candidate_to_sdp (webrtc, webrtc->pending_local_description->sdp, } else {
item->mlineindex, cand); _add_end_of_candidate_to_sdp (webrtc,
webrtc->current_local_description->sdp, item->mlineindex);
}
}
if (webrtc->pending_local_description) {
if (cand && cand[0] != '\0') {
_add_ice_candidate_to_sdp (webrtc,
webrtc->pending_local_description->sdp, item->mlineindex, cand);
} else {
_add_end_of_candidate_to_sdp (webrtc,
webrtc->pending_local_description->sdp, item->mlineindex);
}
}
PC_UNLOCK (webrtc); PC_UNLOCK (webrtc);
g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_ICE_CANDIDATE_SIGNAL], g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_ICE_CANDIDATE_SIGNAL],

View file

@ -845,7 +845,7 @@ test_webrtc_wait_for_ice_gathering_complete (struct test_webrtc *t)
g_mutex_lock (&t->lock); g_mutex_lock (&t->lock);
g_object_get (t->webrtc1, "ice-gathering-state", &ice_state1, NULL); g_object_get (t->webrtc1, "ice-gathering-state", &ice_state1, NULL);
g_object_get (t->webrtc2, "ice-gathering-state", &ice_state2, NULL); g_object_get (t->webrtc2, "ice-gathering-state", &ice_state2, NULL);
while (ice_state1 != GST_WEBRTC_ICE_GATHERING_STATE_COMPLETE && while (ice_state1 != GST_WEBRTC_ICE_GATHERING_STATE_COMPLETE ||
ice_state2 != GST_WEBRTC_ICE_GATHERING_STATE_COMPLETE) { ice_state2 != GST_WEBRTC_ICE_GATHERING_STATE_COMPLETE) {
g_cond_wait (&t->cond, &t->lock); g_cond_wait (&t->cond, &t->lock);
g_object_get (t->webrtc1, "ice-gathering-state", &ice_state1, NULL); g_object_get (t->webrtc1, "ice-gathering-state", &ice_state1, NULL);
@ -1211,6 +1211,9 @@ _check_ice_port_restriction (struct test_webrtc *t, GstElement * element,
guint port_as_int; guint port_as_int;
guint peer_number; guint peer_number;
if (!candidate || candidate[0] == '\0')
return;
regex = regex =
g_regex_new ("candidate:(\\d+) (1) (UDP|TCP) (\\d+) ([0-9.]+|[0-9a-f:]+)" g_regex_new ("candidate:(\\d+) (1) (UDP|TCP) (\\d+) ([0-9.]+|[0-9a-f:]+)"
" (\\d+) typ ([a-z]+)", 0, 0, NULL); " (\\d+) typ ([a-z]+)", 0, 0, NULL);
@ -5708,6 +5711,61 @@ GST_START_TEST (test_msid)
GST_END_TEST; GST_END_TEST;
static void
_check_ice_end_of_candidates (struct test_webrtc *t, GstElement * element,
guint mlineindex, gchar * candidate, GstElement * other, gpointer user_data)
{
gint *end_count = user_data;
if (!candidate || candidate[0] == '\0') {
g_atomic_int_inc (end_count);
}
}
static void
sdp_media_has_end_of_candidates (struct test_webrtc *t, GstElement * element,
GstWebRTCSessionDescription * desc, gpointer user_data)
{
guint i;
for (i = 0; i < gst_sdp_message_medias_len (desc->sdp); i++) {
const GstSDPMedia *media = gst_sdp_message_get_media (desc->sdp, i);
fail_unless_equals_string (gst_sdp_media_get_attribute_val_n (media,
"end-of-candidates", 0), "");
fail_unless (gst_sdp_media_get_attribute_val_n (media, "end-of-candidates",
1) == NULL);
}
}
GST_START_TEST (test_ice_end_of_candidates)
{
struct test_webrtc *t = create_audio_test ();
GstWebRTCSessionDescription *local_desc;
gint end_candidate_count = 0;
VAL_SDP_INIT (offer, _count_num_sdp_media, GUINT_TO_POINTER (1), NULL);
VAL_SDP_INIT (answer, _count_num_sdp_media, GUINT_TO_POINTER (1), NULL);
t->on_ice_candidate = _check_ice_end_of_candidates;
t->ice_candidate_data = &end_candidate_count;
test_validate_sdp (t, &offer, &answer);
test_webrtc_wait_for_ice_gathering_complete (t);
fail_unless_equals_int (end_candidate_count, 2);
g_object_get (t->webrtc1, "current-local-description", &local_desc, NULL);
sdp_media_has_end_of_candidates (t, t->webrtc1, local_desc, NULL);
gst_webrtc_session_description_free (local_desc);
test_webrtc_free (t);
}
GST_END_TEST;
static Suite * static Suite *
webrtcbin_suite (void) webrtcbin_suite (void)
{ {
@ -5773,6 +5831,7 @@ webrtcbin_suite (void)
tcase_add_test (tc, test_invalid_add_media_in_answer); tcase_add_test (tc, test_invalid_add_media_in_answer);
tcase_add_test (tc, test_add_turn_server); tcase_add_test (tc, test_add_turn_server);
tcase_add_test (tc, test_msid); tcase_add_test (tc, test_msid);
tcase_add_test (tc, test_ice_end_of_candidates);
if (sctpenc && sctpdec) { if (sctpenc && sctpdec) {
tcase_add_test (tc, test_data_channel_create); tcase_add_test (tc, test_data_channel_create);
tcase_add_test (tc, test_data_channel_remote_notify); tcase_add_test (tc, test_data_channel_remote_notify);