From e2d88f05691d5b14023b70bb6be29326eba92b87 Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Wed, 26 Aug 2020 15:45:35 +1000 Subject: [PATCH] webrtc: propagate more errors through the promise Return errors on promises when things fail where available. Things like parsing errors, invalid states, missing fields, unsupported transitions, etc. Part-of: --- ext/webrtc/gstwebrtcbin.c | 158 ++++++++++++++++++++++++--------- ext/webrtc/utils.h | 1 + ext/webrtc/webrtcsdp.c | 7 +- ext/webrtc/webrtcsdp.h | 3 +- tests/check/elements/webrtcbin.c | 183 +++++++++++++++++++++++++++++++++------ 5 files changed, 281 insertions(+), 71 deletions(-) diff --git a/ext/webrtc/gstwebrtcbin.c b/ext/webrtc/gstwebrtcbin.c index d281b29..54f4a2f 100644 --- a/ext/webrtc/gstwebrtcbin.c +++ b/ext/webrtc/gstwebrtcbin.c @@ -2561,7 +2561,8 @@ _add_data_channel_offer (GstWebRTCBin * webrtc, GstSDPMessage * msg, /* TODO: use the options argument */ static GstSDPMessage * -_create_offer_task (GstWebRTCBin * webrtc, const GstStructure * options) +_create_offer_task (GstWebRTCBin * webrtc, const GstStructure * options, + GError ** error) { GstSDPMessage *ret; GString *bundled_mids = NULL; @@ -2604,8 +2605,8 @@ _create_offer_task (GstWebRTCBin * webrtc, const GstStructure * options) guint bundle_media_index; reserved_pts = gather_reserved_pts (webrtc); - if (last_offer && _parse_bundle (last_offer, &last_bundle) && last_bundle - && last_bundle && last_bundle[0] + if (last_offer && _parse_bundle (last_offer, &last_bundle, NULL) + && last_bundle && last_bundle && last_bundle[0] && _get_bundle_index (last_offer, last_bundle, &bundle_media_index)) { bundle_ufrag = g_strdup (_media_get_ice_ufrag (last_offer, bundle_media_index)); @@ -2881,7 +2882,8 @@ _get_rtx_target_pt_and_ssrc_from_caps (GstCaps * answer_caps, gint * target_pt, /* TODO: use the options argument */ static GstSDPMessage * -_create_answer_task (GstWebRTCBin * webrtc, const GstStructure * options) +_create_answer_task (GstWebRTCBin * webrtc, const GstStructure * options, + GError ** error) { GstSDPMessage *ret = NULL; const GstWebRTCSessionDescription *pending_remote = @@ -2896,12 +2898,13 @@ _create_answer_task (GstWebRTCBin * webrtc, const GstStructure * options) GstSDPMessage *last_answer = _get_latest_self_generated_sdp (webrtc); if (!webrtc->pending_remote_description) { - GST_ERROR_OBJECT (webrtc, + g_set_error_literal (error, GST_WEBRTC_BIN_ERROR, + GST_WEBRTC_BIN_ERROR_INVALID_STATE, "Asked to create an answer without a remote description"); return NULL; } - if (!_parse_bundle (pending_remote->sdp, &bundled)) + if (!_parse_bundle (pending_remote->sdp, &bundled, error)) goto out; if (bundled) { @@ -2909,8 +2912,8 @@ _create_answer_task (GstWebRTCBin * webrtc, const GstStructure * options) guint bundle_media_index; if (!_get_bundle_index (pending_remote->sdp, bundled, &bundle_idx)) { - GST_ERROR_OBJECT (webrtc, "Bundle tag is %s but no media found matching", - bundled[0]); + g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP, + "Bundle tag is %s but no media found matching", bundled[0]); goto out; } @@ -2918,7 +2921,7 @@ _create_answer_task (GstWebRTCBin * webrtc, const GstStructure * options) bundled_mids = g_string_new ("BUNDLE"); } - if (last_answer && _parse_bundle (last_answer, &last_bundle) + if (last_answer && _parse_bundle (last_answer, &last_bundle, NULL) && last_bundle && last_bundle[0] && _get_bundle_index (last_answer, last_bundle, &bundle_media_index)) { bundle_ufrag = @@ -3305,14 +3308,15 @@ _create_sdp_task (GstWebRTCBin * webrtc, struct create_sdp *data) GstWebRTCSessionDescription *desc = NULL; GstSDPMessage *sdp = NULL; GstStructure *s = NULL; + GError *error = NULL; GST_INFO_OBJECT (webrtc, "creating %s sdp with options %" GST_PTR_FORMAT, gst_webrtc_sdp_type_to_string (data->type), data->options); if (data->type == GST_WEBRTC_SDP_TYPE_OFFER) - sdp = _create_offer_task (webrtc, data->options); + sdp = _create_offer_task (webrtc, data->options, &error); else if (data->type == GST_WEBRTC_SDP_TYPE_ANSWER) - sdp = _create_answer_task (webrtc, data->options); + sdp = _create_answer_task (webrtc, data->options, &error); else { g_assert_not_reached (); goto out; @@ -3323,6 +3327,13 @@ _create_sdp_task (GstWebRTCBin * webrtc, struct create_sdp *data) s = gst_structure_new ("application/x-gst-promise", gst_webrtc_sdp_type_to_string (data->type), GST_TYPE_WEBRTC_SESSION_DESCRIPTION, desc, NULL); + } else { + g_warn_if_fail (error != NULL); + GST_WARNING_OBJECT (webrtc, "returning error: %s", + error ? error->message : "Unknown"); + s = gst_structure_new ("application/x-gstwebrtcbin-error", + "error", G_TYPE_ERROR, error, NULL); + g_clear_error (&error); } out: @@ -3775,7 +3786,7 @@ static void _update_transceiver_from_sdp_media (GstWebRTCBin * webrtc, const GstSDPMessage * sdp, guint media_idx, TransportStream * stream, GstWebRTCRTPTransceiver * rtp_trans, - GStrv bundled, guint bundle_idx) + GStrv bundled, guint bundle_idx, GError ** error) { WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans); GstWebRTCRTPTransceiverDirection prev_dir = rtp_trans->current_direction; @@ -3812,20 +3823,28 @@ _update_transceiver_from_sdp_media (GstWebRTCBin * webrtc, local_setup = _get_dtls_setup_from_media (local_media); remote_setup = _get_dtls_setup_from_media (remote_media); new_setup = _get_final_setup (local_setup, remote_setup); - if (new_setup == GST_WEBRTC_DTLS_SETUP_NONE) + if (new_setup == GST_WEBRTC_DTLS_SETUP_NONE) { + g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP, + "Cannot intersect direction attributes for media %u", media_idx); return; + } local_dir = _get_direction_from_media (local_media); remote_dir = _get_direction_from_media (remote_media); new_dir = _get_final_direction (local_dir, remote_dir); - - if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE) + if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE) { + g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP, + "Cannot intersect dtls setup attributes for media %u", media_idx); return; + } if (prev_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE && new_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE && prev_dir != new_dir) { - GST_FIXME_OBJECT (webrtc, "implement transceiver direction changes"); + g_set_error (error, GST_WEBRTC_BIN_ERROR, + GST_WEBRTC_BIN_ERROR_NOT_IMPLEMENTED, + "transceiver direction changes are not implemented. Media %u", + media_idx); return; } @@ -4023,7 +4042,8 @@ _generate_data_channel_id (GstWebRTCBin * webrtc) static void _update_data_channel_from_sdp_media (GstWebRTCBin * webrtc, - const GstSDPMessage * sdp, guint media_idx, TransportStream * stream) + const GstSDPMessage * sdp, guint media_idx, TransportStream * stream, + GError ** error) { const GstSDPMedia *local_media, *remote_media; GstWebRTCDTLSSetup local_setup, remote_setup, new_setup; @@ -4042,8 +4062,11 @@ _update_data_channel_from_sdp_media (GstWebRTCBin * webrtc, local_setup = _get_dtls_setup_from_media (local_media); remote_setup = _get_dtls_setup_from_media (remote_media); new_setup = _get_final_setup (local_setup, remote_setup); - if (new_setup == GST_WEBRTC_DTLS_SETUP_NONE) + if (new_setup == GST_WEBRTC_DTLS_SETUP_NONE) { + g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP, + "Cannot intersect dtls setup for media %u", media_idx); return; + } /* data channel is always rtcp-muxed to avoid generating ICE candidates * for RTCP */ @@ -4052,8 +4075,12 @@ _update_data_channel_from_sdp_media (GstWebRTCBin * webrtc, local_port = _get_sctp_port_from_media (local_media); remote_port = _get_sctp_port_from_media (local_media); - if (local_port == -1 || remote_port == -1) + if (local_port == -1 || remote_port == -1) { + g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP, + "Could not find sctp port for media %u (local %i, remote %i)", + media_idx, local_port, remote_port); return; + } if (0 == (local_max_size = _get_sctp_max_message_size_from_media (local_media))) @@ -4166,7 +4193,7 @@ done: static gboolean _update_transceivers_from_sdp (GstWebRTCBin * webrtc, SDPSource source, - GstWebRTCSessionDescription * sdp) + GstWebRTCSessionDescription * sdp, GError ** error) { int i; gboolean ret = FALSE; @@ -4177,14 +4204,14 @@ _update_transceivers_from_sdp (GstWebRTCBin * webrtc, SDPSource source, /* FIXME: With some peers, it's possible we could have * multiple bundles to deal with, although I've never seen one yet */ if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) - if (!_parse_bundle (sdp->sdp, &bundled)) + if (!_parse_bundle (sdp->sdp, &bundled, error)) goto done; if (bundled) { if (!_get_bundle_index (sdp->sdp, bundled, &bundle_idx)) { - GST_ERROR_OBJECT (webrtc, "Bundle tag is %s but no media found matching", - bundled[0]); + g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP, + "Bundle tag is %s but no media found matching", bundled[0]); goto done; } @@ -4235,7 +4262,8 @@ _update_transceivers_from_sdp (GstWebRTCBin * webrtc, SDPSource source, webrtc_transceiver_set_transport ((WebRTCTransceiver *) trans, stream); if (source == SDP_LOCAL && sdp->type == GST_WEBRTC_SDP_TYPE_OFFER && !trans) { - GST_ERROR ("State mismatch. Could not find local transceiver by mline."); + g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP, + "State mismatch. Could not find local transceiver by mline %u", i); goto done; } else { if (g_strcmp0 (gst_sdp_media_get_media (media), "audio") == 0 || @@ -4258,9 +4286,10 @@ _update_transceivers_from_sdp (GstWebRTCBin * webrtc, SDPSource source, } _update_transceiver_from_sdp_media (webrtc, sdp->sdp, i, stream, - trans, bundled, bundle_idx); + trans, bundled, bundle_idx, error); } else if (_message_media_is_datachannel (sdp->sdp, i)) { - _update_data_channel_from_sdp_media (webrtc, sdp->sdp, i, stream); + _update_data_channel_from_sdp_media (webrtc, sdp->sdp, i, stream, + error); } else { GST_ERROR_OBJECT (webrtc, "Unknown media type in SDP at index %u", i); } @@ -4284,6 +4313,20 @@ done: return ret; } +static gboolean +check_transceivers_not_removed (GstWebRTCBin * webrtc, + GstWebRTCSessionDescription * previous, GstWebRTCSessionDescription * new) +{ + if (!previous) + return TRUE; + + if (gst_sdp_message_medias_len (previous->sdp) > + gst_sdp_message_medias_len (new->sdp)) + return FALSE; + + return TRUE; +} + struct set_description { GstPromise *promise; @@ -4291,6 +4334,30 @@ struct set_description GstWebRTCSessionDescription *sdp; }; +static GstWebRTCSessionDescription * +get_previous_description (GstWebRTCBin * webrtc, SDPSource source, + GstWebRTCSDPType type) +{ + switch (type) { + case GST_WEBRTC_SDP_TYPE_OFFER: + case GST_WEBRTC_SDP_TYPE_PRANSWER: + case GST_WEBRTC_SDP_TYPE_ANSWER: + if (source == SDP_LOCAL) { + return webrtc->current_local_description; + } else { + return webrtc->current_remote_description; + } + case GST_WEBRTC_SDP_TYPE_ROLLBACK: + return NULL; + default: + /* other values mean memory corruption/uninitialized! */ + g_assert_not_reached (); + break; + } + + return NULL; +} + /* http://w3c.github.io/webrtc-pc/#set-description */ static void _set_description_task (GstWebRTCBin * webrtc, struct set_description *sd) @@ -4316,29 +4383,31 @@ _set_description_task (GstWebRTCBin * webrtc, struct set_description *sd) g_free (type_str); } - if (!validate_sdp (webrtc->signaling_state, sd->source, sd->sdp, &error)) { - GST_ERROR_OBJECT (webrtc, "%s", error->message); - g_clear_error (&error); + if (!validate_sdp (webrtc->signaling_state, sd->source, sd->sdp, &error)) goto out; - } - - if (webrtc->priv->is_closed) { - GST_WARNING_OBJECT (webrtc, "we are closed"); - goto out; - } if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) - if (!_parse_bundle (sd->sdp->sdp, &bundled)) + if (!_parse_bundle (sd->sdp->sdp, &bundled, &error)) goto out; if (bundled) { if (!_get_bundle_index (sd->sdp->sdp, bundled, &bundle_idx)) { - GST_ERROR_OBJECT (webrtc, "Bundle tag is %s but no media found matching", - bundled[0]); + g_set_error (&error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP, + "Bundle tag is %s but no matching media found", bundled[0]); goto out; } } + if (!check_transceivers_not_removed (webrtc, + get_previous_description (webrtc, sd->source, sd->sdp->type), + sd->sdp)) { + g_set_error_literal (&error, GST_WEBRTC_BIN_ERROR, + GST_WEBRTC_BIN_ERROR_BAD_SDP, + "m=lines removed from the SDP. Processing a completely new connection " + "is not currently supported."); + goto out; + } + switch (sd->sdp->type) { case GST_WEBRTC_SDP_TYPE_OFFER:{ if (sd->source == SDP_LOCAL) { @@ -4484,7 +4553,8 @@ _set_description_task (GstWebRTCBin * webrtc, struct set_description *sd) GList *tmp; /* media modifications */ - _update_transceivers_from_sdp (webrtc, sd->source, sd->sdp); + if (!_update_transceivers_from_sdp (webrtc, sd->source, sd->sdp, &error)) + goto out; for (tmp = webrtc->priv->pending_sink_transceivers; tmp;) { GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (tmp->data); @@ -4664,7 +4734,15 @@ out: g_strfreev (bundled); PC_UNLOCK (webrtc); - gst_promise_reply (sd->promise, NULL); + if (error) { + GST_WARNING_OBJECT (webrtc, "returning error: %s", error->message); + gst_promise_reply (sd->promise, + gst_structure_new ("application/x-getwebrtcbin-error", "error", + G_TYPE_ERROR, error, NULL)); + g_clear_error (&error); + } else { + gst_promise_reply (sd->promise, NULL); + } PC_LOCK (webrtc); } diff --git a/ext/webrtc/utils.h b/ext/webrtc/utils.h index ab4d58e..ac18673 100644 --- a/ext/webrtc/utils.h +++ b/ext/webrtc/utils.h @@ -40,6 +40,7 @@ typedef enum GST_WEBRTC_BIN_ERROR_SCTP_FAILURE, GST_WEBRTC_BIN_ERROR_DATA_CHANNEL_FAILURE, GST_WEBRTC_BIN_ERROR_CLOSED, + GST_WEBRTC_BIN_ERROR_NOT_IMPLEMENTED, } GstWebRTCError; GstPadTemplate * _find_pad_template (GstElement * element, diff --git a/ext/webrtc/webrtcsdp.c b/ext/webrtc/webrtcsdp.c index 6e7f4b3..2f8dcf6 100644 --- a/ext/webrtc/webrtcsdp.c +++ b/ext/webrtc/webrtcsdp.c @@ -872,7 +872,7 @@ _get_ice_credentials_from_sdp_media (const GstSDPMessage * sdp, guint media_idx, } gboolean -_parse_bundle (GstSDPMessage * sdp, GStrv * bundled) +_parse_bundle (GstSDPMessage * sdp, GStrv * bundled, GError ** error) { const gchar *group; gboolean ret = FALSE; @@ -883,8 +883,9 @@ _parse_bundle (GstSDPMessage * sdp, GStrv * bundled) *bundled = g_strsplit (group + strlen ("BUNDLE "), " ", 0); if (!(*bundled)[0]) { - GST_ERROR ("Invalid format for BUNDLE group, expected at least " - "one mid (%s)", group); + g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP, + "Invalid format for BUNDLE group, expected at least one mid (%s)", + group); g_strfreev (*bundled); *bundled = NULL; goto done; diff --git a/ext/webrtc/webrtcsdp.h b/ext/webrtc/webrtcsdp.h index 1501cbc..c55709b 100644 --- a/ext/webrtc/webrtcsdp.h +++ b/ext/webrtc/webrtcsdp.h @@ -101,7 +101,8 @@ gboolean _get_bundle_index (Gst guint * idx); G_GNUC_INTERNAL gboolean _parse_bundle (GstSDPMessage * sdp, - GStrv * bundled); + GStrv * bundled, + GError ** error); G_GNUC_INTERNAL const gchar * _media_get_ice_pwd (const GstSDPMessage * msg, diff --git a/tests/check/elements/webrtcbin.c b/tests/check/elements/webrtcbin.c index 3b79bed..89a141c 100644 --- a/tests/check/elements/webrtcbin.c +++ b/tests/check/elements/webrtcbin.c @@ -177,14 +177,19 @@ _on_answer_received (GstPromise * promise, gpointer user_data) GstElement *answerer = TEST_GET_ANSWERER (t); const GstStructure *reply; GstWebRTCSessionDescription *answer = NULL; - gchar *desc; + GError *error = NULL; reply = gst_promise_get_reply (promise); - gst_structure_get (reply, "answer", - GST_TYPE_WEBRTC_SESSION_DESCRIPTION, &answer, NULL); - desc = gst_sdp_message_as_text (answer->sdp); - GST_INFO ("Created Answer: %s", desc); - g_free (desc); + if (gst_structure_get (reply, "answer", + GST_TYPE_WEBRTC_SESSION_DESCRIPTION, &answer, NULL)) { + gchar *desc = gst_sdp_message_as_text (answer->sdp); + GST_INFO ("Created Answer: %s", desc); + g_free (desc); + } else if (gst_structure_get (reply, "error", G_TYPE_ERROR, &error, NULL)) { + GST_INFO ("Creating answer resulted in error: %s", error->message); + } else { + g_assert_not_reached (); + } g_mutex_lock (&t->lock); @@ -196,15 +201,28 @@ _on_answer_received (GstPromise * promise, gpointer user_data) } gst_promise_unref (promise); - promise = gst_promise_new_with_change_func (_on_answer_set, t, NULL); - g_signal_emit_by_name (answerer, "set-local-description", t->answer_desc, - promise); - promise = gst_promise_new_with_change_func (_on_answer_set, t, NULL); - g_signal_emit_by_name (offeror, "set-remote-description", t->answer_desc, - promise); + if (error) + goto error; + + if (t->answer_desc) { + promise = gst_promise_new_with_change_func (_on_answer_set, t, NULL); + g_signal_emit_by_name (answerer, "set-local-description", t->answer_desc, + promise); + promise = gst_promise_new_with_change_func (_on_answer_set, t, NULL); + g_signal_emit_by_name (offeror, "set-remote-description", t->answer_desc, + promise); + } test_webrtc_signal_state_unlocked (t, STATE_ANSWER_CREATED); g_mutex_unlock (&t->lock); + return; + +error: + g_clear_error (&error); + if (t->state < STATE_ERROR) + test_webrtc_signal_state_unlocked (t, STATE_ERROR); + g_mutex_unlock (&t->lock); + return; } static void @@ -232,14 +250,19 @@ _on_offer_received (GstPromise * promise, gpointer user_data) GstElement *answerer = TEST_GET_ANSWERER (t); const GstStructure *reply; GstWebRTCSessionDescription *offer = NULL; - gchar *desc; + GError *error = NULL; reply = gst_promise_get_reply (promise); - gst_structure_get (reply, "offer", - GST_TYPE_WEBRTC_SESSION_DESCRIPTION, &offer, NULL); - desc = gst_sdp_message_as_text (offer->sdp); - GST_INFO ("Created offer: %s", desc); - g_free (desc); + if (gst_structure_get (reply, "offer", + GST_TYPE_WEBRTC_SESSION_DESCRIPTION, &offer, NULL)) { + gchar *desc = gst_sdp_message_as_text (offer->sdp); + GST_INFO ("Created offer: %s", desc); + g_free (desc); + } else if (gst_structure_get (reply, "error", G_TYPE_ERROR, &error, NULL)) { + GST_INFO ("Creating offer resulted in error: %s", error->message); + } else { + g_assert_not_reached (); + } g_mutex_lock (&t->lock); @@ -251,18 +274,31 @@ _on_offer_received (GstPromise * promise, gpointer user_data) } gst_promise_unref (promise); - promise = gst_promise_new_with_change_func (_on_offer_set, t, NULL); - g_signal_emit_by_name (offeror, "set-local-description", t->offer_desc, - promise); - promise = gst_promise_new_with_change_func (_on_offer_set, t, NULL); - g_signal_emit_by_name (answerer, "set-remote-description", t->offer_desc, - promise); + if (error) + goto error; - promise = gst_promise_new_with_change_func (_on_answer_received, t, NULL); - g_signal_emit_by_name (answerer, "create-answer", NULL, promise); + if (t->offer_desc) { + promise = gst_promise_new_with_change_func (_on_offer_set, t, NULL); + g_signal_emit_by_name (offeror, "set-local-description", t->offer_desc, + promise); + promise = gst_promise_new_with_change_func (_on_offer_set, t, NULL); + g_signal_emit_by_name (answerer, "set-remote-description", t->offer_desc, + promise); + + promise = gst_promise_new_with_change_func (_on_answer_received, t, NULL); + g_signal_emit_by_name (answerer, "create-answer", NULL, promise); + } test_webrtc_signal_state_unlocked (t, STATE_OFFER_CREATED); g_mutex_unlock (&t->lock); + return; + +error: + g_clear_error (&error); + if (t->state < STATE_ERROR) + test_webrtc_signal_state_unlocked (t, STATE_ERROR); + g_mutex_unlock (&t->lock); + return; } static gboolean @@ -2053,7 +2089,7 @@ _check_bundle_tag (struct test_webrtc *t, GstElement * element, GStrv expected = user_data; guint i; - fail_unless (_parse_bundle (sd->sdp, &bundled)); + fail_unless (_parse_bundle (sd->sdp, &bundled, NULL)); if (!bundled) { fail_unless_equals_int (g_strv_length (expected), 0); @@ -2745,6 +2781,98 @@ GST_START_TEST (test_renego_transceiver_set_direction) GST_END_TEST; +static void +offer_remove_last_media (struct test_webrtc *t, GstElement * element, + GstPromise * promise, gpointer user_data) +{ + guint i, n; + GstSDPMessage *new, *old; + const GstSDPOrigin *origin; + const GstSDPConnection *conn; + + old = t->offer_desc->sdp; + fail_unless_equals_int (GST_SDP_OK, gst_sdp_message_new (&new)); + + origin = gst_sdp_message_get_origin (old); + conn = gst_sdp_message_get_connection (old); + fail_unless_equals_int (GST_SDP_OK, gst_sdp_message_set_version (new, + gst_sdp_message_get_version (old))); + fail_unless_equals_int (GST_SDP_OK, gst_sdp_message_set_origin (new, + origin->username, origin->sess_id, origin->sess_version, + origin->nettype, origin->addrtype, origin->addr)); + fail_unless_equals_int (GST_SDP_OK, gst_sdp_message_set_session_name (new, + gst_sdp_message_get_session_name (old))); + fail_unless_equals_int (GST_SDP_OK, gst_sdp_message_set_information (new, + gst_sdp_message_get_information (old))); + fail_unless_equals_int (GST_SDP_OK, gst_sdp_message_set_uri (new, + gst_sdp_message_get_uri (old))); + fail_unless_equals_int (GST_SDP_OK, gst_sdp_message_set_connection (new, + conn->nettype, conn->addrtype, conn->address, conn->ttl, + conn->addr_number)); + + n = gst_sdp_message_attributes_len (old); + for (i = 0; i < n; i++) { + const GstSDPAttribute *a = gst_sdp_message_get_attribute (old, i); + fail_unless_equals_int (GST_SDP_OK, gst_sdp_message_add_attribute (new, + a->key, a->value)); + } + + n = gst_sdp_message_medias_len (old); + fail_unless (n > 0); + for (i = 0; i < n - 1; i++) { + const GstSDPMedia *m = gst_sdp_message_get_media (old, i); + GstSDPMedia *new_m; + + fail_unless_equals_int (GST_SDP_OK, gst_sdp_media_copy (m, &new_m)); + fail_unless_equals_int (GST_SDP_OK, gst_sdp_message_add_media (new, new_m)); + gst_sdp_media_init (new_m); + gst_sdp_media_free (new_m); + } + + gst_webrtc_session_description_free (t->offer_desc); + t->offer_desc = gst_webrtc_session_description_new (GST_WEBRTC_SDP_TYPE_OFFER, + new); +} + +static void +offer_set_produced_error (struct test_webrtc *t, GstElement * element, + GstPromise * promise, gpointer user_data) +{ + const GstStructure *reply; + GError *error = NULL; + + reply = gst_promise_get_reply (promise); + fail_unless (gst_structure_get (reply, "error", G_TYPE_ERROR, &error, NULL)); + GST_INFO ("error produced: %s", error->message); + g_clear_error (&error); + + test_webrtc_signal_state_unlocked (t, STATE_CUSTOM); +} + +GST_START_TEST (test_renego_lose_media_fails) +{ + struct test_webrtc *t = create_audio_video_test (); + VAL_SDP_INIT (offer, _count_num_sdp_media, GUINT_TO_POINTER (2), NULL); + VAL_SDP_INIT (answer, _count_num_sdp_media, GUINT_TO_POINTER (2), NULL); + + /* check that removing an m=line will produce an error */ + + test_validate_sdp (t, &offer, &answer); + + test_webrtc_reset_negotiation (t); + + t->on_offer_created = offer_remove_last_media; + t->on_offer_set = offer_set_produced_error; + t->on_answer_created = NULL; + + test_webrtc_create_offer (t, t->webrtc1); + test_webrtc_wait_for_state_mask (t, 1 << STATE_CUSTOM); + + test_webrtc_free (t); +} + +GST_END_TEST; + static Suite * webrtcbin_suite (void) { @@ -2785,6 +2913,7 @@ webrtcbin_suite (void) tcase_add_test (tc, test_bundle_renego_add_stream); tcase_add_test (tc, test_bundle_max_compat_max_bundle_renego_add_stream); tcase_add_test (tc, test_renego_transceiver_set_direction); + tcase_add_test (tc, test_renego_lose_media_fails); if (sctpenc && sctpdec) { tcase_add_test (tc, test_data_channel_create); tcase_add_test (tc, test_data_channel_remote_notify); -- 2.7.4