summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Waters <matthew@centricular.com>2023-05-11 17:28:52 +1000
committerGStreamer Marge Bot <gitlab-merge-bot@gstreamer-foundation.org>2023-05-12 04:52:22 +0000
commitb10ec569d74f65568d365543fa441eb33304889f (patch)
tree5bb2417427411e4ed772273e0a9f33c023696f79
parent5e45a1b1bd0ec4355346c60ef40c14fa91aa9b6c (diff)
downloadgstreamer-b10ec569d74f65568d365543fa441eb33304889f.tar.gz
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>
-rw-r--r--subprojects/gst-plugins-bad/ext/webrtc/gstwebrtcbin.c93
-rw-r--r--subprojects/gst-plugins-bad/tests/check/elements/webrtcbin.c61
2 files changed, 138 insertions, 16 deletions
diff --git a/subprojects/gst-plugins-bad/ext/webrtc/gstwebrtcbin.c b/subprojects/gst-plugins-bad/ext/webrtc/gstwebrtcbin.c
index 4e9cd2e7f5..c235b5ac81 100644
--- a/subprojects/gst-plugins-bad/ext/webrtc/gstwebrtcbin.c
+++ b/subprojects/gst-plugins-bad/ext/webrtc/gstwebrtcbin.c
@@ -821,6 +821,30 @@ _find_transport_for_session (GstWebRTCBin * webrtc, guint session_id)
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);
static GstWebRTCBinPad *
@@ -1601,13 +1625,6 @@ _update_ice_gathering_state_task (GstWebRTCBin * webrtc, gpointer data)
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 *
_update_ice_connection_state_task (GstWebRTCBin * webrtc, gpointer data)
{
@@ -2117,10 +2134,26 @@ _on_ice_transport_notify_state (GstWebRTCICETransport * transport,
}
static void
+_on_local_ice_candidate_cb (GstWebRTCICE * ice, guint session_id,
+ gchar * candidate, GstWebRTCBin * webrtc);
+
+static void
_on_ice_transport_notify_gathering_state (GstWebRTCICETransport * transport,
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
@@ -5411,6 +5444,24 @@ _add_ice_candidate_to_sdp (GstWebRTCBin * webrtc,
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
_filter_sdp_fields (GQuark field_id, const GValue * value,
GstStructure * new_structure)
@@ -6861,7 +6912,7 @@ _on_local_ice_candidate_task (GstWebRTCBin * webrtc)
IceCandidateItem *item = &g_array_index (items, IceCandidateItem, i);
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=" */
cand += 2;
}
@@ -6876,12 +6927,24 @@ _on_local_ice_candidate_task (GstWebRTCBin * webrtc)
* FIXME: This ICE candidate should be stored somewhere with
* the associated mid and also merged back into any subsequent
* local descriptions on renegotiation */
- if (webrtc->current_local_description)
- _add_ice_candidate_to_sdp (webrtc, webrtc->current_local_description->sdp,
- item->mlineindex, cand);
- if (webrtc->pending_local_description)
- _add_ice_candidate_to_sdp (webrtc, webrtc->pending_local_description->sdp,
- item->mlineindex, cand);
+ if (webrtc->current_local_description) {
+ if (cand && cand[0] != '\0') {
+ _add_ice_candidate_to_sdp (webrtc,
+ webrtc->current_local_description->sdp, item->mlineindex, cand);
+ } else {
+ _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);
g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_ICE_CANDIDATE_SIGNAL],
diff --git a/subprojects/gst-plugins-bad/tests/check/elements/webrtcbin.c b/subprojects/gst-plugins-bad/tests/check/elements/webrtcbin.c
index 617dee57c6..0b09ca0e9f 100644
--- a/subprojects/gst-plugins-bad/tests/check/elements/webrtcbin.c
+++ b/subprojects/gst-plugins-bad/tests/check/elements/webrtcbin.c
@@ -845,7 +845,7 @@ test_webrtc_wait_for_ice_gathering_complete (struct test_webrtc *t)
g_mutex_lock (&t->lock);
g_object_get (t->webrtc1, "ice-gathering-state", &ice_state1, 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) {
g_cond_wait (&t->cond, &t->lock);
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 peer_number;
+ if (!candidate || candidate[0] == '\0')
+ return;
+
regex =
g_regex_new ("candidate:(\\d+) (1) (UDP|TCP) (\\d+) ([0-9.]+|[0-9a-f:]+)"
" (\\d+) typ ([a-z]+)", 0, 0, NULL);
@@ -5708,6 +5711,61 @@ GST_START_TEST (test_msid)
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 *
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_add_turn_server);
tcase_add_test (tc, test_msid);
+ tcase_add_test (tc, test_ice_end_of_candidates);
if (sctpenc && sctpdec) {
tcase_add_test (tc, test_data_channel_create);
tcase_add_test (tc, test_data_channel_remote_notify);