2 * Copyright (C) 2017 Matthew Waters <matthew@centricular.com>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
24 #include "webrtcsdp.h"
31 #define IS_EMPTY_SDP_ATTRIBUTE(val) (val == NULL || g_strcmp0(val, "") == 0)
34 _sdp_source_to_string (SDPSource source)
47 _check_valid_state_for_sdp_change (GstWebRTCSignalingState state,
48 SDPSource source, GstWebRTCSDPType type, GError ** error)
50 #define STATE(val) GST_WEBRTC_SIGNALING_STATE_ ## val
51 #define TYPE(val) GST_WEBRTC_SDP_TYPE_ ## val
53 if (source == SDP_LOCAL && type == TYPE (OFFER) && state == STATE (STABLE))
55 if (source == SDP_LOCAL && type == TYPE (OFFER)
56 && state == STATE (HAVE_LOCAL_OFFER))
58 if (source == SDP_LOCAL && type == TYPE (ANSWER)
59 && state == STATE (HAVE_REMOTE_OFFER))
61 if (source == SDP_LOCAL && type == TYPE (PRANSWER)
62 && state == STATE (HAVE_REMOTE_OFFER))
64 if (source == SDP_LOCAL && type == TYPE (PRANSWER)
65 && state == STATE (HAVE_LOCAL_PRANSWER))
68 if (source == SDP_REMOTE && type == TYPE (OFFER) && state == STATE (STABLE))
70 if (source == SDP_REMOTE && type == TYPE (OFFER)
71 && state == STATE (HAVE_REMOTE_OFFER))
73 if (source == SDP_REMOTE && type == TYPE (ANSWER)
74 && state == STATE (HAVE_LOCAL_OFFER))
76 if (source == SDP_REMOTE && type == TYPE (PRANSWER)
77 && state == STATE (HAVE_LOCAL_OFFER))
79 if (source == SDP_REMOTE && type == TYPE (PRANSWER)
80 && state == STATE (HAVE_REMOTE_PRANSWER))
84 gchar *state_str = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
86 gchar *type_str = _enum_value_to_string (GST_TYPE_WEBRTC_SDP_TYPE, type);
87 g_set_error (error, GST_WEBRTC_BIN_ERROR,
88 GST_WEBRTC_BIN_ERROR_INVALID_STATE,
89 "Not in the correct state (%s) for setting %s %s description",
90 state_str, _sdp_source_to_string (source), type_str);
102 _check_sdp_crypto (SDPSource source, GstWebRTCSessionDescription * sdp,
105 const gchar *message_fingerprint, *fingerprint;
106 const GstSDPKey *key;
109 key = gst_sdp_message_get_key (sdp->sdp);
110 if (!IS_EMPTY_SDP_ATTRIBUTE (key->data)) {
111 g_set_error_literal (error, GST_WEBRTC_BIN_ERROR,
112 GST_WEBRTC_BIN_ERROR_BAD_SDP, "sdp contains a k line");
116 message_fingerprint = fingerprint =
117 gst_sdp_message_get_attribute_val (sdp->sdp, "fingerprint");
118 for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) {
119 const GstSDPMedia *media = gst_sdp_message_get_media (sdp->sdp, i);
120 const gchar *media_fingerprint =
121 gst_sdp_media_get_attribute_val (media, "fingerprint");
123 if (!IS_EMPTY_SDP_ATTRIBUTE (message_fingerprint)
124 && !IS_EMPTY_SDP_ATTRIBUTE (media_fingerprint)) {
125 g_set_error (error, GST_WEBRTC_BIN_ERROR,
126 GST_WEBRTC_BIN_ERROR_FINGERPRINT,
127 "No fingerprint lines in sdp for media %u", i);
130 if (IS_EMPTY_SDP_ATTRIBUTE (fingerprint)) {
131 fingerprint = media_fingerprint;
133 if (!IS_EMPTY_SDP_ATTRIBUTE (media_fingerprint)
134 && g_strcmp0 (fingerprint, media_fingerprint) != 0) {
135 g_set_error (error, GST_WEBRTC_BIN_ERROR,
136 GST_WEBRTC_BIN_ERROR_FINGERPRINT,
137 "Fingerprint in media %u differs from %s fingerprint. "
138 "\'%s\' != \'%s\'", i, message_fingerprint ? "global" : "previous",
139 fingerprint, media_fingerprint);
149 _session_has_attribute_key (const GstSDPMessage * msg, const gchar * key)
152 for (i = 0; i < gst_sdp_message_attributes_len (msg); i++) {
153 const GstSDPAttribute *attr = gst_sdp_message_get_attribute (msg, i);
155 if (g_strcmp0 (attr->key, key) == 0)
163 _session_has_attribute_key_value (const GstSDPMessage * msg, const gchar * key,
167 for (i = 0; i < gst_sdp_message_attributes_len (msg); i++) {
168 const GstSDPAttribute *attr = gst_sdp_message_get_attribute (msg, i);
170 if (g_strcmp0 (attr->key, key) == 0 && g_strcmp0 (attr->value, value) == 0)
178 _check_trickle_ice (GstSDPMessage * msg, GError ** error)
180 if (!_session_has_attribute_key_value (msg, "ice-options", "trickle")) {
181 g_set_error_literal (error, GST_WEBRTC_BIN_ERROR,
182 GST_WEBRTC_BIN_ERROR_BAD_SDP,
183 "No required \'a=ice-options:trickle\' line in sdp");
189 _media_has_attribute_key (const GstSDPMedia * media, const gchar * key)
192 for (i = 0; i < gst_sdp_media_attributes_len (media); i++) {
193 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i);
195 if (g_strcmp0 (attr->key, key) == 0)
203 _media_has_mid (const GstSDPMedia * media, guint media_idx, GError ** error)
205 const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid");
206 if (IS_EMPTY_SDP_ATTRIBUTE (mid)) {
207 g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
208 "media %u is missing or contains an empty \'mid\' attribute",
216 _media_get_ice_ufrag (const GstSDPMessage * msg, guint media_idx)
218 const gchar *ice_ufrag;
220 ice_ufrag = gst_sdp_message_get_attribute_val (msg, "ice-ufrag");
221 if (IS_EMPTY_SDP_ATTRIBUTE (ice_ufrag)) {
222 const GstSDPMedia *media = gst_sdp_message_get_media (msg, media_idx);
223 ice_ufrag = gst_sdp_media_get_attribute_val (media, "ice-ufrag");
224 if (IS_EMPTY_SDP_ATTRIBUTE (ice_ufrag))
231 _media_get_ice_pwd (const GstSDPMessage * msg, guint media_idx)
233 const gchar *ice_pwd;
235 ice_pwd = gst_sdp_message_get_attribute_val (msg, "ice-pwd");
236 if (IS_EMPTY_SDP_ATTRIBUTE (ice_pwd)) {
237 const GstSDPMedia *media = gst_sdp_message_get_media (msg, media_idx);
238 ice_pwd = gst_sdp_media_get_attribute_val (media, "ice-pwd");
239 if (IS_EMPTY_SDP_ATTRIBUTE (ice_pwd))
246 _media_has_setup (const GstSDPMedia * media, guint media_idx, GError ** error)
248 static const gchar *valid_setups[] = { "actpass", "active", "passive", NULL };
249 const gchar *setup = gst_sdp_media_get_attribute_val (media, "setup");
250 if (IS_EMPTY_SDP_ATTRIBUTE (setup)) {
251 g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
252 "media %u is missing or contains an empty \'setup\' attribute",
256 if (!g_strv_contains (valid_setups, setup)) {
257 g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
258 "media %u contains unknown \'setup\' attribute, \'%s\'", media_idx,
267 _media_has_dtls_id (const GstSDPMedia * media, guint media_idx, GError ** error)
269 const gchar *dtls_id = gst_sdp_media_get_attribute_val (media, "ice-pwd");
270 if (IS_EMPTY_SDP_ATTRIBUTE (dtls_id)) {
271 g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
272 "media %u is missing or contains an empty \'dtls-id\' attribute",
280 validate_sdp (GstWebRTCSignalingState state, SDPSource source,
281 GstWebRTCSessionDescription * sdp, GError ** error)
283 const gchar *group, *bundle_ice_ufrag = NULL, *bundle_ice_pwd = NULL;
284 gchar **group_members = NULL;
285 gboolean is_bundle = FALSE;
288 if (!_check_valid_state_for_sdp_change (state, source, sdp->type, error))
290 if (!_check_sdp_crypto (source, sdp, error))
292 /* not explicitly required
293 if (ICE && !_check_trickle_ice (sdp->sdp))
295 group = gst_sdp_message_get_attribute_val (sdp->sdp, "group");
296 is_bundle = group && g_str_has_prefix (group, "BUNDLE");
298 group_members = g_strsplit (&group[6], " ", -1);
300 for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) {
301 const GstSDPMedia *media = gst_sdp_message_get_media (sdp->sdp, i);
303 gboolean media_in_bundle = FALSE;
304 if (!_media_has_mid (media, i, error))
306 mid = gst_sdp_media_get_attribute_val (media, "mid");
307 media_in_bundle = is_bundle
308 && g_strv_contains ((const gchar **) group_members, mid);
309 if (!_media_get_ice_ufrag (sdp->sdp, i)) {
310 g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
311 "media %u is missing or contains an empty \'ice-ufrag\' attribute",
315 if (!_media_get_ice_pwd (sdp->sdp, i)) {
316 g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
317 "media %u is missing or contains an empty \'ice-pwd\' attribute", i);
320 if (!_media_has_setup (media, i, error))
322 /* check parameters in bundle are the same */
323 if (media_in_bundle) {
324 const gchar *ice_ufrag =
325 gst_sdp_media_get_attribute_val (media, "ice-ufrag");
326 const gchar *ice_pwd = gst_sdp_media_get_attribute_val (media, "ice-pwd");
327 if (!bundle_ice_ufrag)
328 bundle_ice_ufrag = ice_ufrag;
329 else if (g_strcmp0 (bundle_ice_ufrag, ice_ufrag) != 0) {
330 g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
331 "media %u has different ice-ufrag values in bundle. "
332 "%s != %s", i, bundle_ice_ufrag, ice_ufrag);
335 if (!bundle_ice_pwd) {
336 bundle_ice_pwd = ice_pwd;
337 } else if (g_strcmp0 (bundle_ice_pwd, ice_pwd) != 0) {
338 g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
339 "media %u has different ice-pwd values in bundle. "
340 "%s != %s", i, bundle_ice_pwd, ice_pwd);
346 g_strfreev (group_members);
351 g_strfreev (group_members);
355 GstWebRTCRTPTransceiverDirection
356 _get_direction_from_media (const GstSDPMedia * media)
358 GstWebRTCRTPTransceiverDirection new_dir =
359 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE;
362 for (i = 0; i < gst_sdp_media_attributes_len (media); i++) {
363 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i);
365 if (g_strcmp0 (attr->key, "sendonly") == 0) {
366 if (new_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE) {
367 GST_ERROR ("Multiple direction attributes");
368 return GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE;
370 new_dir = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY;
371 } else if (g_strcmp0 (attr->key, "sendrecv") == 0) {
372 if (new_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE) {
373 GST_ERROR ("Multiple direction attributes");
374 return GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE;
376 new_dir = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV;
377 } else if (g_strcmp0 (attr->key, "recvonly") == 0) {
378 if (new_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE) {
379 GST_ERROR ("Multiple direction attributes");
380 return GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE;
382 new_dir = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY;
383 } else if (g_strcmp0 (attr->key, "inactive") == 0) {
384 if (new_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE) {
385 GST_ERROR ("Multiple direction attributes");
386 return GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE;
388 new_dir = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE;
395 #define DIR(val) GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_ ## val
396 GstWebRTCRTPTransceiverDirection
397 _intersect_answer_directions (GstWebRTCRTPTransceiverDirection offer,
398 GstWebRTCRTPTransceiverDirection answer)
400 if (offer == DIR (INACTIVE) || answer == DIR (INACTIVE))
401 return DIR (INACTIVE);
402 if (offer == DIR (SENDONLY) && answer == DIR (SENDRECV))
403 return DIR (RECVONLY);
404 if (offer == DIR (SENDONLY) && answer == DIR (RECVONLY))
405 return DIR (RECVONLY);
406 if (offer == DIR (RECVONLY) && answer == DIR (SENDRECV))
407 return DIR (SENDONLY);
408 if (offer == DIR (RECVONLY) && answer == DIR (SENDONLY))
409 return DIR (SENDONLY);
410 if (offer == DIR (SENDRECV) && answer == DIR (SENDRECV))
411 return DIR (SENDRECV);
412 if (offer == DIR (SENDRECV) && answer == DIR (SENDONLY))
413 return DIR (SENDONLY);
414 if (offer == DIR (SENDRECV) && answer == DIR (RECVONLY))
415 return DIR (RECVONLY);
416 if (offer == DIR (RECVONLY) && answer == DIR (RECVONLY))
417 return DIR (INACTIVE);
418 if (offer == DIR (SENDONLY) && answer == DIR (SENDONLY))
419 return DIR (INACTIVE);
425 _media_replace_direction (GstSDPMedia * media,
426 GstWebRTCRTPTransceiverDirection direction)
432 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
435 for (i = 0; i < gst_sdp_media_attributes_len (media); i++) {
436 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i);
438 if (g_strcmp0 (attr->key, "sendonly") == 0
439 || g_strcmp0 (attr->key, "sendrecv") == 0
440 || g_strcmp0 (attr->key, "recvonly") == 0
441 || g_strcmp0 (attr->key, "inactive") == 0) {
442 GstSDPAttribute new_attr = { 0, };
443 GST_TRACE ("replace %s with %s", attr->key, dir_str);
444 gst_sdp_attribute_set (&new_attr, dir_str, "");
445 gst_sdp_media_replace_attribute (media, i, &new_attr);
451 GST_TRACE ("add %s", dir_str);
452 gst_sdp_media_add_attribute (media, dir_str, "");
456 GstWebRTCRTPTransceiverDirection
457 _get_final_direction (GstWebRTCRTPTransceiverDirection local_dir,
458 GstWebRTCRTPTransceiverDirection remote_dir)
460 GstWebRTCRTPTransceiverDirection new_dir;
461 new_dir = DIR (NONE);
464 new_dir = DIR (INACTIVE);
467 if (remote_dir == DIR (SENDONLY)) {
468 GST_ERROR ("remote SDP has the same directionality. "
469 "This is not legal.");
471 } else if (remote_dir == DIR (INACTIVE)) {
472 new_dir = DIR (INACTIVE);
474 new_dir = DIR (SENDONLY);
478 if (remote_dir == DIR (RECVONLY)) {
479 GST_ERROR ("remote SDP has the same directionality. "
480 "This is not legal.");
482 } else if (remote_dir == DIR (INACTIVE)) {
483 new_dir = DIR (INACTIVE);
485 new_dir = DIR (RECVONLY);
489 if (remote_dir == DIR (INACTIVE)) {
490 new_dir = DIR (INACTIVE);
491 } else if (remote_dir == DIR (SENDONLY)) {
492 new_dir = DIR (RECVONLY);
493 } else if (remote_dir == DIR (RECVONLY)) {
494 new_dir = DIR (SENDONLY);
495 } else if (remote_dir == DIR (SENDRECV)) {
496 new_dir = DIR (SENDRECV);
500 g_assert_not_reached ();
504 if (new_dir == DIR (NONE)) {
505 GST_ERROR ("Abnormal situation!");
514 #define SETUP(val) GST_WEBRTC_DTLS_SETUP_ ## val
516 _get_dtls_setup_from_media (const GstSDPMedia * media)
520 for (i = 0; i < gst_sdp_media_attributes_len (media); i++) {
521 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i);
523 if (g_strcmp0 (attr->key, "setup") == 0) {
524 if (g_strcmp0 (attr->value, "actpass") == 0) {
525 return SETUP (ACTPASS);
526 } else if (g_strcmp0 (attr->value, "active") == 0) {
527 return SETUP (ACTIVE);
528 } else if (g_strcmp0 (attr->value, "passive") == 0) {
529 return SETUP (PASSIVE);
531 GST_ERROR ("unknown setup value %s", attr->value);
537 GST_LOG ("no setup attribute in media");
542 _intersect_dtls_setup (GstWebRTCDTLSSetup offer)
545 case SETUP (NONE): /* default is active */
546 case SETUP (ACTPASS):
547 case SETUP (PASSIVE):
548 return SETUP (ACTIVE);
550 return SETUP (PASSIVE);
557 _media_replace_setup (GstSDPMedia * media, GstWebRTCDTLSSetup setup)
562 setup_str = _enum_value_to_string (GST_TYPE_WEBRTC_DTLS_SETUP, setup);
564 for (i = 0; i < gst_sdp_media_attributes_len (media); i++) {
565 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i);
567 if (g_strcmp0 (attr->key, "setup") == 0) {
568 GstSDPAttribute new_attr = { 0, };
569 GST_TRACE ("replace setup:%s with setup:%s", attr->value, setup_str);
570 gst_sdp_attribute_set (&new_attr, "setup", setup_str);
571 gst_sdp_media_replace_attribute (media, i, &new_attr);
576 GST_TRACE ("add setup:%s", setup_str);
577 gst_sdp_media_add_attribute (media, "setup", setup_str);
582 _get_final_setup (GstWebRTCDTLSSetup local_setup,
583 GstWebRTCDTLSSetup remote_setup)
585 GstWebRTCDTLSSetup new_setup;
587 new_setup = SETUP (NONE);
588 switch (local_setup) {
590 /* someone's done a bad job of mangling the SDP. or bugs */
591 g_critical ("Received a locally generated sdp without a parseable "
592 "\'a=setup\' line. This indicates a bug somewhere. Bailing");
595 if (remote_setup == SETUP (ACTIVE)) {
596 GST_ERROR ("remote SDP has the same "
597 "\'a=setup:active\' attribute. This is not legal");
600 new_setup = SETUP (ACTIVE);
602 case SETUP (PASSIVE):
603 if (remote_setup == SETUP (PASSIVE)) {
604 GST_ERROR ("remote SDP has the same "
605 "\'a=setup:passive\' attribute. This is not legal");
608 new_setup = SETUP (PASSIVE);
610 case SETUP (ACTPASS):
611 if (remote_setup == SETUP (ACTPASS)) {
612 GST_ERROR ("remote SDP has the same "
613 "\'a=setup:actpass\' attribute. This is not legal");
616 if (remote_setup == SETUP (ACTIVE))
617 new_setup = SETUP (PASSIVE);
618 else if (remote_setup == SETUP (PASSIVE))
619 new_setup = SETUP (ACTIVE);
620 else if (remote_setup == SETUP (NONE)) {
621 /* XXX: what to do here? */
622 GST_WARNING ("unspecified situation. local: "
623 "\'a=setup:actpass\' remote: none/unparseable");
624 new_setup = SETUP (ACTIVE);
628 g_assert_not_reached ();
631 if (new_setup == SETUP (NONE)) {
632 GST_ERROR ("Abnormal situation!");
642 _generate_fingerprint_from_certificate (gchar * certificate,
643 GChecksumType checksum_type)
645 gchar **lines, *line;
646 guchar *tmp, *decoded, *digest;
648 GString *fingerprint;
649 gsize decoded_length, digest_size;
654 g_return_val_if_fail (certificate != NULL, NULL);
656 /* 1. decode the certificate removing newlines and the certificate header
658 decoded = tmp = g_new0 (guchar, (strlen (certificate) / 4) * 3 + 3);
659 lines = g_strsplit (certificate, "\n", 0);
660 for (i = 0, line = lines[i]; line; line = lines[++i]) {
661 if (line[0] && !g_str_has_prefix (line, "-----"))
662 tmp += g_base64_decode_step (line, strlen (line), tmp, &state, &save);
665 decoded_length = tmp - decoded;
667 /* 2. compute a checksum of the decoded certificate */
668 checksum = g_checksum_new (checksum_type);
669 digest_size = g_checksum_type_get_length (checksum_type);
670 digest = g_new (guint8, digest_size);
671 g_checksum_update (checksum, decoded, decoded_length);
672 g_checksum_get_digest (checksum, digest, &digest_size);
675 /* 3. hex encode the checksum separated with ':'s */
676 fingerprint = g_string_new (NULL);
677 for (i = 0; i < digest_size; i++) {
679 g_string_append (fingerprint, ":");
680 g_string_append_printf (fingerprint, "%02X", digest[i]);
684 g_checksum_free (checksum);
686 return g_string_free (fingerprint, FALSE);
689 #define DEFAULT_ICE_UFRAG_LEN 32
690 #define DEFAULT_ICE_PASSWORD_LEN 32
691 static const gchar *ice_credential_chars =
692 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789" "+/";
695 _generate_ice_credentials (gchar ** ufrag, gchar ** password)
699 *ufrag = g_malloc0 (DEFAULT_ICE_UFRAG_LEN + 1);
700 for (i = 0; i < DEFAULT_ICE_UFRAG_LEN; i++)
702 ice_credential_chars[g_random_int_range (0,
703 strlen (ice_credential_chars))];
705 *password = g_malloc0 (DEFAULT_ICE_PASSWORD_LEN + 1);
706 for (i = 0; i < DEFAULT_ICE_PASSWORD_LEN; i++)
708 ice_credential_chars[g_random_int_range (0,
709 strlen (ice_credential_chars))];
713 _get_sctp_port_from_media (const GstSDPMedia * media)
719 if (gst_sdp_media_formats_len (media) != 1) {
720 /* only exactly one format is supported */
724 format = gst_sdp_media_get_format (media, 0);
726 if (g_strcmp0 (format, "webrtc-datachannel") == 0) {
727 /* draft-ietf-mmusic-sctp-sdp-21, e.g. Firefox 63 and later */
729 for (i = 0; i < gst_sdp_media_attributes_len (media); i++) {
730 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i);
732 if (g_strcmp0 (attr->key, "sctp-port") == 0) {
733 gint64 port = g_ascii_strtoll (attr->value, &endptr, 10);
734 if (endptr == attr->value) {
735 /* conversion error */
742 /* draft-ietf-mmusic-sctp-sdp-05, e.g. Chrome as recent as 75 */
743 gint64 port = g_ascii_strtoll (format, &endptr, 10);
744 if (endptr == format) {
745 /* conversion error */
749 for (i = 0; i < gst_sdp_media_attributes_len (media); i++) {
750 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i);
752 if (g_strcmp0 (attr->key, "sctpmap") == 0 && atoi (attr->value) == port) {
753 /* a=sctpmap:5000 webrtc-datachannel 256 */
754 gchar **parts = g_strsplit (attr->value, " ", 3);
755 if (!parts[1] || g_strcmp0 (parts[1], "webrtc-datachannel") != 0) {
768 _get_sctp_max_message_size_from_media (const GstSDPMedia * media)
772 for (i = 0; i < gst_sdp_media_attributes_len (media); i++) {
773 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i);
775 if (g_strcmp0 (attr->key, "max-message-size") == 0)
776 return atoi (attr->value);
783 _message_media_is_datachannel (const GstSDPMessage * msg, guint media_id)
785 const GstSDPMedia *media;
790 if (gst_sdp_message_medias_len (msg) <= media_id)
793 media = gst_sdp_message_get_media (msg, media_id);
795 if (g_strcmp0 (gst_sdp_media_get_media (media), "application") != 0)
798 if (gst_sdp_media_formats_len (media) != 1)
801 if (g_strcmp0 (gst_sdp_media_get_format (media, 0),
802 "webrtc-datachannel") != 0)
809 _message_get_datachannel_index (const GstSDPMessage * msg)
813 for (i = 0; i < gst_sdp_message_medias_len (msg); i++) {
814 if (_message_media_is_datachannel (msg, i)) {
815 g_assert (i < G_MAXUINT);
824 _get_ice_credentials_from_sdp_media (const GstSDPMessage * sdp, guint media_idx,
825 gchar ** ufrag, gchar ** pwd)
833 /* search in the corresponding media section */
834 const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
835 const gchar *tmp_ufrag =
836 gst_sdp_media_get_attribute_val (media, "ice-ufrag");
837 const gchar *tmp_pwd = gst_sdp_media_get_attribute_val (media, "ice-pwd");
838 if (tmp_ufrag && tmp_pwd) {
839 *ufrag = g_strdup (tmp_ufrag);
840 *pwd = g_strdup (tmp_pwd);
845 /* then in the sdp message itself */
846 for (i = 0; i < gst_sdp_message_attributes_len (sdp); i++) {
847 const GstSDPAttribute *attr = gst_sdp_message_get_attribute (sdp, i);
849 if (g_strcmp0 (attr->key, "ice-ufrag") == 0) {
851 *ufrag = g_strdup (attr->value);
852 } else if (g_strcmp0 (attr->key, "ice-pwd") == 0) {
854 *pwd = g_strdup (attr->value);
857 if (!*ufrag && !*pwd) {
858 /* Check in the medias themselves. According to JSEP, they should be
859 * identical FIXME: only for bundle-d streams */
860 for (i = 0; i < gst_sdp_message_medias_len (sdp); i++) {
861 const GstSDPMedia *media = gst_sdp_message_get_media (sdp, i);
862 const gchar *tmp_ufrag =
863 gst_sdp_media_get_attribute_val (media, "ice-ufrag");
864 const gchar *tmp_pwd = gst_sdp_media_get_attribute_val (media, "ice-pwd");
865 if (tmp_ufrag && tmp_pwd) {
866 *ufrag = g_strdup (tmp_ufrag);
867 *pwd = g_strdup (tmp_pwd);
875 _parse_bundle (GstSDPMessage * sdp, GStrv * bundled)
878 gboolean ret = FALSE;
880 group = gst_sdp_message_get_attribute_val (sdp, "group");
882 if (group && g_str_has_prefix (group, "BUNDLE ")) {
883 *bundled = g_strsplit (group + strlen ("BUNDLE "), " ", 0);
885 if (!(*bundled)[0]) {
886 GST_ERROR ("Invalid format for BUNDLE group, expected at least "
887 "one mid (%s)", group);
888 g_strfreev (*bundled);
904 _get_bundle_index (GstSDPMessage * sdp, GStrv bundled, guint * idx)
906 gboolean ret = FALSE;
909 for (i = 0; i < gst_sdp_message_medias_len (sdp); i++) {
910 const GstSDPMedia *media = gst_sdp_message_get_media (sdp, i);
911 const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid");
913 if (!g_strcmp0 (mid, bundled[0])) {