From 21bf3a35ac93b37b73e62842a32f3e6daeeeee1a Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Tue, 9 Oct 2018 02:38:14 +1100 Subject: [PATCH] webrtc/datachannel: fix support for prenegotiated channels With prenegotiated channels, the data-channel protocol is not used and instead the channel's negotiation is intended to be performed out of band in some application-specific manner. Comes with test! --- ext/webrtc/webrtcdatachannel.c | 34 +++++++++++++++--- tests/check/elements/webrtcbin.c | 78 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 4 deletions(-) diff --git a/ext/webrtc/webrtcdatachannel.c b/ext/webrtc/webrtcdatachannel.c index 47263e7..08e2a26 100644 --- a/ext/webrtc/webrtcdatachannel.c +++ b/ext/webrtc/webrtcdatachannel.c @@ -46,8 +46,7 @@ GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); #define gst_webrtc_data_channel_parent_class parent_class G_DEFINE_TYPE_WITH_CODE (GstWebRTCDataChannel, gst_webrtc_data_channel, GST_TYPE_OBJECT, GST_DEBUG_CATEGORY_INIT (gst_webrtc_data_channel_debug, - "webrtcdatachannel", 0, "webrtcdatachannel"); - ); + "webrtcdatachannel", 0, "webrtcdatachannel");); enum { @@ -844,7 +843,8 @@ gst_webrtc_data_channel_send_string (GstWebRTCDataChannel * channel, GstBuffer *buffer; GstFlowReturn ret; - g_return_if_fail (!channel->negotiated && channel->opened); + if (!channel->negotiated) + g_return_if_fail (channel->opened); g_return_if_fail (channel->sctp_transport != NULL); if (!str) { @@ -893,6 +893,28 @@ gst_webrtc_data_channel_send_string (GstWebRTCDataChannel * channel, } } +static void +_on_sctp_notify_state_unlocked (GObject * sctp_transport, + GstWebRTCDataChannel * channel) +{ + GstWebRTCSCTPTransportState state; + + g_object_get (sctp_transport, "state", &state, NULL); + if (state == GST_WEBRTC_SCTP_TRANSPORT_STATE_CONNECTED) { + if (channel->negotiated) + _channel_enqueue_task (channel, (ChannelTask) _emit_on_open, NULL, NULL); + } +} + +static void +_on_sctp_notify_state (GObject * sctp_transport, GParamSpec * pspec, + GstWebRTCDataChannel * channel) +{ + GST_OBJECT_LOCK (channel); + _on_sctp_notify_state_unlocked (sctp_transport, channel); + GST_OBJECT_UNLOCK (channel); +} + void gst_webrtc_data_channel_set_sctp_transport (GstWebRTCDataChannel * channel, GstWebRTCSCTPTransport * sctp) @@ -907,9 +929,13 @@ gst_webrtc_data_channel_set_sctp_transport (GstWebRTCDataChannel * channel, gst_object_replace ((GstObject **) & channel->sctp_transport, GST_OBJECT (sctp)); - if (sctp) + if (sctp) { g_signal_connect (sctp, "stream-reset", G_CALLBACK (_on_sctp_reset_stream), channel); + g_signal_connect (sctp, "notify::state", G_CALLBACK (_on_sctp_notify_state), + channel); + _on_sctp_notify_state_unlocked (G_OBJECT (sctp), channel); + } GST_OBJECT_UNLOCK (channel); } diff --git a/tests/check/elements/webrtcbin.c b/tests/check/elements/webrtcbin.c index 2f34200..8b3f7b7 100644 --- a/tests/check/elements/webrtcbin.c +++ b/tests/check/elements/webrtcbin.c @@ -1993,6 +1993,83 @@ GST_START_TEST (test_data_channel_max_message_size) GST_END_TEST; +static void +_on_ready_state_notify (GObject * channel, GParamSpec * pspec, + struct test_webrtc *t) +{ + gint *n_ready = t->data_channel_data; + GstWebRTCDataChannelState ready_state; + + g_object_get (channel, "ready-state", &ready_state, NULL); + + if (ready_state == GST_WEBRTC_DATA_CHANNEL_STATE_OPEN) { + if (++(*n_ready) >= 2) + test_webrtc_signal_state (t, STATE_CUSTOM); + } +} + +GST_START_TEST (test_data_channel_pre_negotiated) +{ + struct test_webrtc *t = test_webrtc_new (); + GObject *channel1 = NULL, *channel2 = NULL; + struct validate_sdp offer = { on_sdp_has_datachannel, NULL }; + struct validate_sdp answer = { on_sdp_has_datachannel, NULL }; + GstStructure *s; + gint n_ready = 0; + + t->on_negotiation_needed = NULL; + t->offer_data = &offer; + t->on_offer_created = validate_sdp; + t->answer_data = &answer; + t->on_answer_created = validate_sdp; + t->on_ice_candidate = NULL; + + fail_if (gst_element_set_state (t->webrtc1, + GST_STATE_READY) == GST_STATE_CHANGE_FAILURE); + fail_if (gst_element_set_state (t->webrtc2, + GST_STATE_READY) == GST_STATE_CHANGE_FAILURE); + + s = gst_structure_new ("application/data-channel", "negotiated", + G_TYPE_BOOLEAN, TRUE, "id", G_TYPE_INT, 1, NULL); + + g_signal_emit_by_name (t->webrtc1, "create-data-channel", "label", s, + &channel1); + g_assert_nonnull (channel1); + g_signal_emit_by_name (t->webrtc2, "create-data-channel", "label", s, + &channel2); + g_assert_nonnull (channel2); + + fail_if (gst_element_set_state (t->webrtc1, + GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE); + fail_if (gst_element_set_state (t->webrtc2, + GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE); + + test_webrtc_create_offer (t, t->webrtc1); + test_webrtc_wait_for_answer_error_eos (t); + fail_unless (t->state == STATE_ANSWER_CREATED); + + t->data_channel_data = &n_ready; + + g_signal_connect (channel1, "notify::ready-state", + G_CALLBACK (_on_ready_state_notify), t); + g_signal_connect (channel2, "notify::ready-state", + G_CALLBACK (_on_ready_state_notify), t); + + test_webrtc_wait_for_state_mask (t, 1 << STATE_CUSTOM); + test_webrtc_signal_state (t, STATE_NEW); + + have_data_channel_transfer_string (t, t->webrtc1, channel1, channel2); + + test_webrtc_wait_for_state_mask (t, 1 << STATE_CUSTOM); + + g_object_unref (channel1); + g_object_unref (channel2); + gst_structure_free (s); + test_webrtc_free (t); +} + +GST_END_TEST; + static Suite * webrtcbin_suite (void) { @@ -2032,6 +2109,7 @@ webrtcbin_suite (void) tcase_add_test (tc, test_data_channel_create_after_negotiate); tcase_add_test (tc, test_data_channel_low_threshold); tcase_add_test (tc, test_data_channel_max_message_size); + tcase_add_test (tc, test_data_channel_pre_negotiated); } else { GST_WARNING ("Some required elements were not found. " "All datachannel are disabled. sctpenc %p, sctpdec %p", sctpenc, -- 2.7.4