summaryrefslogtreecommitdiff
path: root/ext/webrtc/gstwebrtcbin.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/webrtc/gstwebrtcbin.c')
-rw-r--r--ext/webrtc/gstwebrtcbin.c1058
1 files changed, 893 insertions, 165 deletions
diff --git a/ext/webrtc/gstwebrtcbin.c b/ext/webrtc/gstwebrtcbin.c
index 268432caa..60614b829 100644
--- a/ext/webrtc/gstwebrtcbin.c
+++ b/ext/webrtc/gstwebrtcbin.c
@@ -28,6 +28,8 @@
#include "utils.h"
#include "webrtcsdp.h"
#include "webrtctransceiver.h"
+#include "webrtcdatachannel.h"
+#include "sctptransport.h"
#include <stdio.h>
#include <stdlib.h>
@@ -50,8 +52,8 @@
/*
* This webrtcbin implements the majority of the W3's peerconnection API and
* implementation guide where possible. Generating offers, answers and setting
- * local and remote SDP's are all supported. To start with, only the media
- * interface has been implemented (no datachannel yet).
+ * local and remote SDP's are all supported. Both media descriptions and
+ * descriptions involving data channels are supported.
*
* Each input/output pad is equivalent to a Track in W3 parlance which are
* added/removed from the bin. The number of requested sink pads is the number
@@ -70,7 +72,6 @@
* LS groups
* bundling
* setting custom DTLS certificates
- * data channel
*
* seperate session id's from mlineindex properly
* how to deal with replacing a input/output track/stream
@@ -108,6 +109,32 @@ _have_nice_elements (GstWebRTCBin * webrtc)
}
static gboolean
+_have_sctp_elements (GstWebRTCBin * webrtc)
+{
+ GstPluginFeature *feature;
+
+ feature = gst_registry_lookup_feature (gst_registry_get (), "sctpdec");
+ if (feature) {
+ gst_object_unref (feature);
+ } else {
+ GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
+ ("%s", "sctp elements are not available"));
+ return FALSE;
+ }
+
+ feature = gst_registry_lookup_feature (gst_registry_get (), "sctpenc");
+ if (feature) {
+ gst_object_unref (feature);
+ } else {
+ GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
+ ("%s", "sctp elements are not available"));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
_have_dtls_elements (GstWebRTCBin * webrtc)
{
GstPluginFeature *feature;
@@ -273,7 +300,8 @@ gst_webrtc_bin_pad_new (const gchar * name, GstPadDirection direction)
G_DEFINE_TYPE_WITH_CODE (GstWebRTCBin, gst_webrtc_bin, GST_TYPE_BIN,
G_ADD_PRIVATE (GstWebRTCBin)
GST_DEBUG_CATEGORY_INIT (gst_webrtc_bin_debug, "webrtcbin", 0,
- "webrtcbin element"););
+ "webrtcbin element");
+ );
static GstPad *_connect_input_stream (GstWebRTCBin * webrtc,
GstWebRTCBinPad * pad);
@@ -303,6 +331,8 @@ enum
ADD_TRANSCEIVER_SIGNAL,
GET_TRANSCEIVERS_SIGNAL,
ADD_TURN_SERVER_SIGNAL,
+ CREATE_DATA_CHANNEL_SIGNAL,
+ ON_DATA_CHANNEL_SIGNAL,
LAST_SIGNAL,
};
@@ -524,6 +554,47 @@ _find_pad (GstWebRTCBin * webrtc, gconstpointer data, FindPadFunc func)
return NULL;
}
+typedef gboolean (*FindDataChannelFunc) (GstWebRTCDataChannel * p1,
+ gconstpointer data);
+
+static GstWebRTCDataChannel *
+_find_data_channel (GstWebRTCBin * webrtc, gconstpointer data,
+ FindDataChannelFunc func)
+{
+ int i;
+
+ for (i = 0; i < webrtc->priv->data_channels->len; i++) {
+ GstWebRTCDataChannel *channel =
+ g_array_index (webrtc->priv->data_channels, GstWebRTCDataChannel *,
+ i);
+
+ if (func (channel, data))
+ return channel;
+ }
+
+ return NULL;
+}
+
+static gboolean
+data_channel_match_for_id (GstWebRTCDataChannel * channel, gint * id)
+{
+ return channel->id == *id;
+}
+
+static GstWebRTCDataChannel *
+_find_data_channel_for_id (GstWebRTCBin * webrtc, gint id)
+{
+ GstWebRTCDataChannel *channel;
+
+ channel = _find_data_channel (webrtc, &id,
+ (FindDataChannelFunc) data_channel_match_for_id);
+
+ GST_TRACE_OBJECT (webrtc,
+ "Found data channel %" GST_PTR_FORMAT " for id %i", channel, id);
+
+ return channel;
+}
+
static void
_add_pad_to_list (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
{
@@ -1415,7 +1486,6 @@ _create_transport_channel (GstWebRTCBin * webrtc, guint session_id)
{
GstWebRTCDTLSTransport *transport;
TransportStream *ret;
- gchar *pad_name;
/* FIXME: how to parametrize the sender and the receiver */
ret = transport_stream_new (webrtc, session_id);
@@ -1439,6 +1509,44 @@ _create_transport_channel (GstWebRTCBin * webrtc, guint session_id)
G_CALLBACK (_on_dtls_transport_notify_state), webrtc);
}
+ GST_TRACE_OBJECT (webrtc,
+ "Create transport %" GST_PTR_FORMAT " for session %u", ret, session_id);
+
+ return ret;
+}
+
+static gboolean
+_message_media_is_datachannel (const GstSDPMessage * msg, guint media_id)
+{
+ const GstSDPMedia *media;
+
+ if (!msg)
+ return FALSE;
+
+ if (gst_sdp_message_medias_len (msg) <= media_id)
+ return FALSE;
+
+ media = gst_sdp_message_get_media (msg, media_id);
+
+ if (g_strcmp0 (gst_sdp_media_get_media (media), "application") != 0)
+ return FALSE;
+
+ if (gst_sdp_media_formats_len (media) != 1)
+ return FALSE;
+
+ if (g_strcmp0 (gst_sdp_media_get_format (media, 0),
+ "webrtc-datachannel") != 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static TransportStream *
+_create_rtp_transport_channel (GstWebRTCBin * webrtc, guint session_id)
+{
+ TransportStream *ret = _create_transport_channel (webrtc, session_id);
+ gchar *pad_name;
+
gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (ret->send_bin));
gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (ret->receive_bin));
@@ -1456,15 +1564,217 @@ _create_transport_channel (GstWebRTCBin * webrtc, guint session_id)
g_array_append_val (webrtc->priv->transports, ret);
- GST_TRACE_OBJECT (webrtc,
- "Create transport %" GST_PTR_FORMAT " for session %u", ret, session_id);
-
gst_element_sync_state_with_parent (GST_ELEMENT (ret->send_bin));
gst_element_sync_state_with_parent (GST_ELEMENT (ret->receive_bin));
return ret;
}
+/* this is called from the webrtc thread with the pc lock held */
+static void
+_on_data_channel_ready_state (GstWebRTCDataChannel * channel,
+ GParamSpec * pspec, GstWebRTCBin * webrtc)
+{
+ GstWebRTCDataChannelState ready_state;
+ guint i;
+
+ g_object_get (channel, "ready-state", &ready_state, NULL);
+
+ if (ready_state == GST_WEBRTC_DATA_CHANNEL_STATE_OPEN) {
+ gboolean found = FALSE;
+
+ for (i = 0; i < webrtc->priv->pending_data_channels->len; i++) {
+ GstWebRTCDataChannel *c;
+
+ c = g_array_index (webrtc->priv->pending_data_channels,
+ GstWebRTCDataChannel *, i);
+ if (c == channel) {
+ found = TRUE;
+ g_array_remove_index (webrtc->priv->pending_data_channels, i);
+ break;
+ }
+ }
+ if (found == FALSE) {
+ GST_FIXME_OBJECT (webrtc, "Received open for unknown data channel");
+ return;
+ }
+
+ g_array_append_val (webrtc->priv->data_channels, channel);
+
+ g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_DATA_CHANNEL_SIGNAL], 0,
+ gst_object_ref (channel));
+ }
+}
+
+static void
+_link_data_channel_to_sctp (GstWebRTCBin * webrtc,
+ GstWebRTCDataChannel * channel)
+{
+ if (webrtc->priv->sctp_transport && !channel->sctp_transport) {
+ gint id;
+
+ g_object_get (channel, "id", &id, NULL);
+
+ if (webrtc->priv->sctp_transport->association_established && id != -1) {
+ gchar *pad_name;
+
+ gst_webrtc_data_channel_set_sctp_transport (channel,
+ webrtc->priv->sctp_transport);
+ pad_name = g_strdup_printf ("sink_%u", id);
+ if (!gst_element_link_pads (channel->appsrc, "src",
+ channel->sctp_transport->sctpenc, pad_name))
+ g_warn_if_reached ();
+ g_free (pad_name);
+ }
+ }
+}
+
+static void
+_on_sctpdec_pad_added (GstElement * sctpdec, GstPad * pad,
+ GstWebRTCBin * webrtc)
+{
+ GstWebRTCDataChannel *channel;
+ guint stream_id;
+ GstPad *sink_pad;
+
+ if (sscanf (GST_PAD_NAME (pad), "src_%u", &stream_id) != 1)
+ return;
+
+ PC_LOCK (webrtc);
+ channel = _find_data_channel_for_id (webrtc, stream_id);
+ if (!channel) {
+ channel = g_object_new (GST_TYPE_WEBRTC_DATA_CHANNEL, NULL);
+ channel->id = stream_id;
+ channel->webrtcbin = webrtc;
+
+ gst_bin_add (GST_BIN (webrtc), channel->appsrc);
+ gst_bin_add (GST_BIN (webrtc), channel->appsink);
+
+ gst_element_sync_state_with_parent (channel->appsrc);
+ gst_element_sync_state_with_parent (channel->appsink);
+
+ _link_data_channel_to_sctp (webrtc, channel);
+
+ g_array_append_val (webrtc->priv->pending_data_channels, channel);
+ }
+
+ g_signal_connect (channel, "notify::ready-state",
+ G_CALLBACK (_on_data_channel_ready_state), webrtc);
+
+ sink_pad = gst_element_get_static_pad (channel->appsink, "sink");
+ if (gst_pad_link (pad, sink_pad) != GST_PAD_LINK_OK)
+ GST_WARNING_OBJECT (channel, "Failed to link sctp pad %s with channel %"
+ GST_PTR_FORMAT, GST_PAD_NAME (pad), channel);
+ gst_object_unref (sink_pad);
+ PC_UNLOCK (webrtc);
+}
+
+static void
+_on_sctp_state_notify (GstWebRTCSCTPTransport * sctp, GParamSpec * pspec,
+ GstWebRTCBin * webrtc)
+{
+ GstWebRTCSCTPTransportState state;
+
+ g_object_get (sctp, "state", &state, NULL);
+
+ if (state == GST_WEBRTC_SCTP_TRANSPORT_STATE_CONNECTED) {
+ int i;
+
+ PC_LOCK (webrtc);
+ GST_DEBUG_OBJECT (webrtc, "SCTP association established");
+
+ for (i = 0; i < webrtc->priv->data_channels->len; i++) {
+ GstWebRTCDataChannel *channel;
+
+ channel =
+ g_array_index (webrtc->priv->data_channels, GstWebRTCDataChannel *,
+ i);
+
+ _link_data_channel_to_sctp (webrtc, channel);
+
+ if (!channel->negotiated && !channel->opened)
+ gst_webrtc_data_channel_start_negotiation (channel);
+ }
+ PC_UNLOCK (webrtc);
+ }
+}
+
+static TransportStream *
+_create_data_channel_transports (GstWebRTCBin * webrtc, guint session_id)
+{
+ if (!webrtc->priv->data_channel_transport) {
+ TransportStream *stream = _create_transport_channel (webrtc, session_id);
+ GstWebRTCSCTPTransport *sctp_transport;
+ int i;
+
+ webrtc->priv->data_channel_transport = stream;
+
+ g_object_set (stream, "rtcp-mux", TRUE, NULL);
+
+ gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (stream->send_bin));
+ gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (stream->receive_bin));
+
+ if (!(sctp_transport = webrtc->priv->sctp_transport)) {
+ sctp_transport = gst_webrtc_sctp_transport_new ();
+ sctp_transport->transport =
+ g_object_ref (webrtc->priv->data_channel_transport->transport);
+ sctp_transport->webrtcbin = webrtc;
+
+ gst_bin_add (GST_BIN (webrtc), sctp_transport->sctpdec);
+ gst_bin_add (GST_BIN (webrtc), sctp_transport->sctpenc);
+ }
+
+ g_signal_connect (sctp_transport->sctpdec, "pad-added",
+ G_CALLBACK (_on_sctpdec_pad_added), webrtc);
+ g_signal_connect (sctp_transport, "notify::state",
+ G_CALLBACK (_on_sctp_state_notify), webrtc);
+
+ if (!gst_element_link_pads (GST_ELEMENT (stream->receive_bin), "data_src",
+ GST_ELEMENT (sctp_transport->sctpdec), "sink"))
+ g_warn_if_reached ();
+
+ if (!gst_element_link_pads (GST_ELEMENT (sctp_transport->sctpenc), "src",
+ GST_ELEMENT (stream->send_bin), "data_sink"))
+ g_warn_if_reached ();
+
+ for (i = 0; i < webrtc->priv->data_channels->len; i++) {
+ GstWebRTCDataChannel *channel;
+
+ channel =
+ g_array_index (webrtc->priv->data_channels, GstWebRTCDataChannel *,
+ i);
+
+ _link_data_channel_to_sctp (webrtc, channel);
+ }
+
+ gst_element_sync_state_with_parent (GST_ELEMENT (stream->send_bin));
+ gst_element_sync_state_with_parent (GST_ELEMENT (stream->receive_bin));
+
+ if (!webrtc->priv->sctp_transport) {
+ gst_element_sync_state_with_parent (GST_ELEMENT
+ (sctp_transport->sctpdec));
+ gst_element_sync_state_with_parent (GST_ELEMENT
+ (sctp_transport->sctpenc));
+ }
+
+ g_array_append_val (webrtc->priv->transports, stream);
+
+ webrtc->priv->sctp_transport = sctp_transport;
+ }
+
+ return webrtc->priv->data_channel_transport;
+}
+
+static TransportStream *
+_create_transport_stream (GstWebRTCBin * webrtc, guint session_id,
+ gboolean is_datachannel)
+{
+ if (is_datachannel)
+ return _create_data_channel_transports (webrtc, session_id);
+ else
+ return _create_rtp_transport_channel (webrtc, session_id);
+}
+
static guint
g_array_find_uint (GArray * array, guint val)
{
@@ -1797,7 +2107,7 @@ sdp_media_from_transceiver (GstWebRTCBin * webrtc, GstSDPMedia * media,
/* FIXME: bundle */
item = _find_transport_for_session (webrtc, media_idx);
if (!item)
- item = _create_transport_channel (webrtc, media_idx);
+ item = _create_transport_stream (webrtc, media_idx, FALSE);
webrtc_transceiver_set_transport (WEBRTC_TRANSCEIVER (trans), item);
}
@@ -1873,6 +2183,58 @@ _create_offer_task (GstWebRTCBin * webrtc, const GstStructure * options)
gst_sdp_media_uninit (&media);
}
+ /* add data channel support */
+ if (webrtc->priv->data_channels->len > 0) {
+ GstSDPMedia media = { 0, };
+ gchar *ufrag, *pwd, *sdp_mid;
+
+ gst_sdp_media_init (&media);
+ /* mandated by JSEP */
+ gst_sdp_media_add_attribute (&media, "setup", "actpass");
+
+ /* FIXME: only needed when restarting ICE */
+ _generate_ice_credentials (&ufrag, &pwd);
+ gst_sdp_media_add_attribute (&media, "ice-ufrag", ufrag);
+ gst_sdp_media_add_attribute (&media, "ice-pwd", pwd);
+ g_free (ufrag);
+ g_free (pwd);
+
+ gst_sdp_media_set_media (&media, "application");
+ gst_sdp_media_set_port_info (&media, 9, 0);
+ gst_sdp_media_set_proto (&media, "UDP/DTLS/SCTP");
+ gst_sdp_media_add_connection (&media, "IN", "IP4", "0.0.0.0", 0, 0);
+ gst_sdp_media_add_format (&media, "webrtc-datachannel");
+
+ sdp_mid = g_strdup_printf ("%s%u", gst_sdp_media_get_media (&media),
+ webrtc->priv->media_counter++);
+ gst_sdp_media_add_attribute (&media, "mid", sdp_mid);
+ g_free (sdp_mid);
+
+ /* FIXME: negotiate this properly */
+ gst_sdp_media_add_attribute (&media, "sctp-port", "5000");
+
+ _create_data_channel_transports (webrtc, webrtc->priv->transceivers->len);
+ {
+ gchar *cert, *fingerprint, *val;
+
+ g_object_get (webrtc->priv->sctp_transport->transport, "certificate",
+ &cert, NULL);
+
+ fingerprint =
+ _generate_fingerprint_from_certificate (cert, G_CHECKSUM_SHA256);
+ g_free (cert);
+ val =
+ g_strdup_printf ("%s %s",
+ _g_checksum_to_webrtc_string (G_CHECKSUM_SHA256), fingerprint);
+ g_free (fingerprint);
+
+ gst_sdp_media_add_attribute (&media, "fingerprint", val);
+ g_free (val);
+ }
+
+ gst_sdp_message_add_media (ret, &media);
+ }
+
/* FIXME: pre-emptively setup receiving elements when needed */
/* XXX: only true for the initial offerer */
@@ -2045,7 +2407,6 @@ _create_answer_task (GstWebRTCBin * webrtc, const GstStructure * options)
gst_sdp_media_new (&media);
gst_sdp_media_set_port_info (media, 9, 0);
- gst_sdp_media_set_proto (media, "UDP/TLS/RTP/SAVPF");
gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
{
@@ -2060,6 +2421,7 @@ _create_answer_task (GstWebRTCBin * webrtc, const GstStructure * options)
offer_media =
(GstSDPMedia *) gst_sdp_message_get_media (pending_remote->sdp, i);
+
for (j = 0; j < gst_sdp_media_attributes_len (offer_media); j++) {
const GstSDPAttribute *attr =
gst_sdp_media_get_attribute (offer_media, j);
@@ -2071,155 +2433,217 @@ _create_answer_task (GstWebRTCBin * webrtc, const GstStructure * options)
}
}
- offer_caps = gst_caps_new_empty ();
- for (j = 0; j < gst_sdp_media_formats_len (offer_media); j++) {
- guint pt = atoi (gst_sdp_media_get_format (offer_media, j));
- GstCaps *caps;
+ /* set the a=setup: attribute */
+ offer_setup = _get_dtls_setup_from_media (offer_media);
+ answer_setup = _intersect_dtls_setup (offer_setup);
+ if (answer_setup == GST_WEBRTC_DTLS_SETUP_NONE) {
+ GST_WARNING_OBJECT (webrtc, "Could not intersect offer setup with "
+ "transceiver direction");
+ goto rejected;
+ }
+ _media_replace_setup (media, answer_setup);
- caps = gst_sdp_media_get_caps_from_media (offer_media, pt);
+ if (g_strcmp0 (gst_sdp_media_get_media (offer_media), "application") == 0) {
+ int sctp_port;
- /* gst_sdp_media_get_caps_from_media() produces caps with name
- * "application/x-unknown" which will fail intersection with
- * "application/x-rtp" caps so mangle the returns caps to have the
- * correct name here */
- for (k = 0; k < gst_caps_get_size (caps); k++) {
- GstStructure *s = gst_caps_get_structure (caps, k);
- gst_structure_set_name (s, "application/x-rtp");
+ if (gst_sdp_media_formats_len (offer_media) != 1) {
+ GST_WARNING_OBJECT (webrtc, "Could not find a format in the m= line "
+ "for webrtc-datachannel");
+ goto rejected;
+ }
+ if (g_strcmp0 (gst_sdp_media_get_format (offer_media, 0),
+ "webrtc-datachannel") != 0) {
+ GST_WARNING_OBJECT (webrtc,
+ "format field of data channel m= line "
+ "is not \'webrtc-datachannel\'");
+ goto rejected;
+ }
+ sctp_port = _get_sctp_port_from_media (offer_media);
+ if (sctp_port == -1) {
+ GST_WARNING_OBJECT (webrtc, "media does not contain a sctp port");
+ goto rejected;
}
- gst_caps_append (offer_caps, caps);
- }
+ /* XXX: older browsers will produce a different SDP format for data
+ * channel that is currently not parsed correctly */
+ gst_sdp_media_set_proto (media, "UDP/DTLS/SCTP");
- for (j = 0; j < webrtc->priv->transceivers->len; j++) {
- GstCaps *trans_caps;
+ gst_sdp_media_set_media (media, "application");
+ gst_sdp_media_set_port_info (media, 9, 0);
+ gst_sdp_media_add_format (media, "webrtc-datachannel");
- rtp_trans =
- g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
- j);
- trans_caps = _find_codec_preferences (webrtc, rtp_trans, GST_PAD_SINK, j);
+ /* FIXME: negotiate this properly on renegotiation */
+ gst_sdp_media_add_attribute (media, "sctp-port", "5000");
- GST_TRACE_OBJECT (webrtc, "trying to compare %" GST_PTR_FORMAT
- " and %" GST_PTR_FORMAT, offer_caps, trans_caps);
+ _create_data_channel_transports (webrtc, i);
- /* FIXME: technically this is a little overreaching as some fields we
- * we can deal with not having and/or we may have unrecognized fields
- * that we cannot actually support */
- if (trans_caps) {
- answer_caps = gst_caps_intersect (offer_caps, trans_caps);
- if (answer_caps && !gst_caps_is_empty (answer_caps)) {
- GST_LOG_OBJECT (webrtc,
- "found compatible transceiver %" GST_PTR_FORMAT
- " for offer media %u", trans, i);
- if (trans_caps)
- gst_caps_unref (trans_caps);
- break;
- } else {
- if (answer_caps) {
- gst_caps_unref (answer_caps);
- answer_caps = NULL;
+ {
+ gchar *cert, *fingerprint, *val;
+
+ g_object_get (webrtc->priv->sctp_transport->transport, "certificate",
+ &cert, NULL);
+
+ fingerprint =
+ _generate_fingerprint_from_certificate (cert, G_CHECKSUM_SHA256);
+ g_free (cert);
+ val =
+ g_strdup_printf ("%s %s",
+ _g_checksum_to_webrtc_string (G_CHECKSUM_SHA256), fingerprint);
+ g_free (fingerprint);
+
+ gst_sdp_media_add_attribute (media, "fingerprint", val);
+ g_free (val);
+ }
+ } else if (g_strcmp0 (gst_sdp_media_get_media (offer_media), "audio") == 0
+ || g_strcmp0 (gst_sdp_media_get_media (offer_media), "video") == 0) {
+ gst_sdp_media_set_proto (media, "UDP/TLS/RTP/SAVPF");
+
+ offer_caps = gst_caps_new_empty ();
+ for (j = 0; j < gst_sdp_media_formats_len (offer_media); j++) {
+ guint pt = atoi (gst_sdp_media_get_format (offer_media, j));
+ GstCaps *caps;
+
+ caps = gst_sdp_media_get_caps_from_media (offer_media, pt);
+
+ /* gst_sdp_media_get_caps_from_media() produces caps with name
+ * "application/x-unknown" which will fail intersection with
+ * "application/x-rtp" caps so mangle the returns caps to have the
+ * correct name here */
+ for (k = 0; k < gst_caps_get_size (caps); k++) {
+ GstStructure *s = gst_caps_get_structure (caps, k);
+ gst_structure_set_name (s, "application/x-rtp");
+ }
+
+ gst_caps_append (offer_caps, caps);
+ }
+
+ for (j = 0; j < webrtc->priv->transceivers->len; j++) {
+ GstCaps *trans_caps;
+
+ rtp_trans =
+ g_array_index (webrtc->priv->transceivers,
+ GstWebRTCRTPTransceiver *, j);
+ trans_caps =
+ _find_codec_preferences (webrtc, rtp_trans, GST_PAD_SINK, j);
+
+ GST_TRACE_OBJECT (webrtc, "trying to compare %" GST_PTR_FORMAT
+ " and %" GST_PTR_FORMAT, offer_caps, trans_caps);
+
+ /* FIXME: technically this is a little overreaching as some fields we
+ * we can deal with not having and/or we may have unrecognized fields
+ * that we cannot actually support */
+ if (trans_caps) {
+ answer_caps = gst_caps_intersect (offer_caps, trans_caps);
+ if (answer_caps && !gst_caps_is_empty (answer_caps)) {
+ GST_LOG_OBJECT (webrtc,
+ "found compatible transceiver %" GST_PTR_FORMAT
+ " for offer media %u", trans, i);
+ if (trans_caps)
+ gst_caps_unref (trans_caps);
+ break;
+ } else {
+ if (answer_caps) {
+ gst_caps_unref (answer_caps);
+ answer_caps = NULL;
+ }
+ if (trans_caps)
+ gst_caps_unref (trans_caps);
+ rtp_trans = NULL;
}
- if (trans_caps)
- gst_caps_unref (trans_caps);
+ } else {
rtp_trans = NULL;
}
- } else {
- rtp_trans = NULL;
}
- }
- if (rtp_trans) {
- answer_dir = rtp_trans->direction;
- g_assert (answer_caps != NULL);
- } else {
- /* if no transceiver, then we only receive that stream and respond with
- * the exact same caps */
- /* FIXME: how to validate that subsequent elements can actually receive
- * this payload/format */
- answer_dir = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY;
- answer_caps = gst_caps_ref (offer_caps);
- }
+ if (rtp_trans) {
+ answer_dir = rtp_trans->direction;
+ g_assert (answer_caps != NULL);
+ } else {
+ /* if no transceiver, then we only receive that stream and respond with
+ * the exact same caps */
+ /* FIXME: how to validate that subsequent elements can actually receive
+ * this payload/format */
+ answer_dir = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY;
+ answer_caps = gst_caps_ref (offer_caps);
+ }
- if (!rtp_trans) {
- trans = _create_webrtc_transceiver (webrtc, answer_dir, i);
- rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
- } else {
- trans = WEBRTC_TRANSCEIVER (rtp_trans);
- }
+ if (!rtp_trans) {
+ trans = _create_webrtc_transceiver (webrtc, answer_dir, i);
+ rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
+ } else {
+ trans = WEBRTC_TRANSCEIVER (rtp_trans);
+ }
- if (!trans->do_nack) {
- answer_caps = gst_caps_make_writable (answer_caps);
- for (k = 0; k < gst_caps_get_size (answer_caps); k++) {
- GstStructure *s = gst_caps_get_structure (answer_caps, k);
- gst_structure_remove_fields (s, "rtcp-fb-nack", NULL);
+ if (!trans->do_nack) {
+ answer_caps = gst_caps_make_writable (answer_caps);
+ for (k = 0; k < gst_caps_get_size (answer_caps); k++) {
+ GstStructure *s = gst_caps_get_structure (answer_caps, k);
+ gst_structure_remove_fields (s, "rtcp-fb-nack", NULL);
+ }
}
- }
- gst_sdp_media_set_media_from_caps (answer_caps, media);
+ gst_sdp_media_set_media_from_caps (answer_caps, media);
- _get_rtx_target_pt_and_ssrc_from_caps (answer_caps, &target_pt,
- &target_ssrc);
+ _get_rtx_target_pt_and_ssrc_from_caps (answer_caps, &target_pt,
+ &target_ssrc);
- original_target_pt = target_pt;
+ original_target_pt = target_pt;
- _media_add_fec (media, trans, offer_caps, &target_pt);
- if (trans->do_nack) {
- _media_add_rtx (media, trans, offer_caps, target_pt, target_ssrc);
- if (target_pt != original_target_pt)
- _media_add_rtx (media, trans, offer_caps, original_target_pt,
- target_ssrc);
- }
+ _media_add_fec (media, trans, offer_caps, &target_pt);
+ if (trans->do_nack) {
+ _media_add_rtx (media, trans, offer_caps, target_pt, target_ssrc);
+ if (target_pt != original_target_pt)
+ _media_add_rtx (media, trans, offer_caps, original_target_pt,
+ target_ssrc);
+ }
- if (answer_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY)
- _media_add_ssrcs (media, answer_caps, webrtc,
- WEBRTC_TRANSCEIVER (rtp_trans));
+ if (answer_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY)
+ _media_add_ssrcs (media, answer_caps, webrtc,
+ WEBRTC_TRANSCEIVER (rtp_trans));
- gst_caps_unref (answer_caps);
- answer_caps = NULL;
+ gst_caps_unref (answer_caps);
+ answer_caps = NULL;
- /* set the new media direction */
- offer_dir = _get_direction_from_media (offer_media);
- answer_dir = _intersect_answer_directions (offer_dir, answer_dir);
- if (answer_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE) {
- GST_WARNING_OBJECT (webrtc, "Could not intersect offer direction with "
- "transceiver direction");
- goto rejected;
- }
- _media_replace_direction (media, answer_dir);
+ /* set the new media direction */
+ offer_dir = _get_direction_from_media (offer_media);
+ answer_dir = _intersect_answer_directions (offer_dir, answer_dir);
+ if (answer_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE) {
+ GST_WARNING_OBJECT (webrtc, "Could not intersect offer direction with "
+ "transceiver direction");
+ goto rejected;
+ }
+ _media_replace_direction (media, answer_dir);
+
+ /* FIXME: bundle! */
+ if (!trans->stream) {
+ TransportStream *item = _find_transport_for_session (webrtc, i);
+ if (!item)
+ item = _create_transport_stream (webrtc, i, FALSE);
+ webrtc_transceiver_set_transport (trans, item);
+ }
+ /* set the a=fingerprint: for this transport */
+ g_object_get (trans->stream->transport, "certificate", &cert, NULL);
+
+ {
+ gchar *fingerprint, *val;
+
+ fingerprint =
+ _generate_fingerprint_from_certificate (cert, G_CHECKSUM_SHA256);
+ g_free (cert);
+ val =
+ g_strdup_printf ("%s %s",
+ _g_checksum_to_webrtc_string (G_CHECKSUM_SHA256), fingerprint);
+ g_free (fingerprint);
+
+ gst_sdp_media_add_attribute (media, "fingerprint", val);
+ g_free (val);
+ }
- /* set the a=setup: attribute */
- offer_setup = _get_dtls_setup_from_media (offer_media);
- answer_setup = _intersect_dtls_setup (offer_setup);
- if (answer_setup == GST_WEBRTC_DTLS_SETUP_NONE) {
- GST_WARNING_OBJECT (webrtc, "Could not intersect offer direction with "
- "transceiver direction");
+ gst_caps_unref (offer_caps);
+ } else {
+ GST_WARNING_OBJECT (webrtc, "unknown m= line media name");
goto rejected;
}
- _media_replace_setup (media, answer_setup);
-
- /* FIXME: bundle! */
- if (!trans->stream) {
- TransportStream *item = _find_transport_for_session (webrtc, i);
- if (!item)
- item = _create_transport_channel (webrtc, i);
- webrtc_transceiver_set_transport (trans, item);
- }
- /* set the a=fingerprint: for this transport */
- g_object_get (trans->stream->transport, "certificate", &cert, NULL);
-
- {
- gchar *fingerprint, *val;
-
- fingerprint =
- _generate_fingerprint_from_certificate (cert, G_CHECKSUM_SHA256);
- g_free (cert);
- val =
- g_strdup_printf ("%s %s",
- _g_checksum_to_webrtc_string (G_CHECKSUM_SHA256), fingerprint);
- g_free (fingerprint);
-
- gst_sdp_media_add_attribute (media, "fingerprint", val);
- g_free (val);
- }
if (0) {
rejected:
@@ -2230,8 +2654,6 @@ _create_answer_task (GstWebRTCBin * webrtc, const GstStructure * options)
}
gst_sdp_message_add_media (ret, media);
gst_sdp_media_free (media);
-
- gst_caps_unref (offer_caps);
}
/* FIXME: can we add not matched transceivers? */
@@ -2410,7 +2832,7 @@ _connect_input_stream (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
/* FIXME: bundle */
item = _find_transport_for_session (webrtc, pad->mlineindex);
if (!item)
- item = _create_transport_channel (webrtc, pad->mlineindex);
+ item = _create_transport_stream (webrtc, pad->mlineindex, FALSE);
webrtc_transceiver_set_transport (trans, item);
}
@@ -2454,7 +2876,7 @@ _connect_output_stream (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
/* FIXME: bundle */
item = _find_transport_for_session (webrtc, pad->mlineindex);
if (!item)
- item = _create_transport_channel (webrtc, pad->mlineindex);
+ item = _create_transport_stream (webrtc, pad->mlineindex, FALSE);
webrtc_transceiver_set_transport (trans, item);
}
@@ -2512,10 +2934,9 @@ _filter_sdp_fields (GQuark field_id, const GValue * value,
static void
_update_transceiver_from_sdp_media (GstWebRTCBin * webrtc,
const GstSDPMessage * sdp, guint media_idx,
- GstWebRTCRTPTransceiver * rtp_trans)
+ TransportStream * stream, GstWebRTCRTPTransceiver * rtp_trans)
{
WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
- TransportStream *stream = trans->stream;
GstWebRTCRTPTransceiverDirection prev_dir = rtp_trans->current_direction;
GstWebRTCRTPTransceiverDirection new_dir;
const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
@@ -2534,14 +2955,6 @@ _update_transceiver_from_sdp_media (GstWebRTCBin * webrtc,
}
}
- if (!stream) {
- /* FIXME: find an existing transport for e.g. bundle/reconfiguration */
- stream = _find_transport_for_session (webrtc, media_idx);
- if (!stream)
- stream = _create_transport_channel (webrtc, media_idx);
- webrtc_transceiver_set_transport (trans, stream);
- }
-
{
const GstSDPMedia *local_media, *remote_media;
GstWebRTCRTPTransceiverDirection local_dir, remote_dir;
@@ -2725,6 +3138,121 @@ _update_transceiver_from_sdp_media (GstWebRTCBin * webrtc,
}
}
+/* must be called with the pc lock held */
+static gint
+_generate_data_channel_id (GstWebRTCBin * webrtc)
+{
+ gboolean is_client;
+ gint new_id = -1, max_channels = 0;
+
+ if (webrtc->priv->sctp_transport) {
+ g_object_get (webrtc->priv->sctp_transport, "max-channels", &max_channels,
+ NULL);
+ }
+ if (max_channels <= 0) {
+ max_channels = 65534;
+ }
+
+ g_object_get (webrtc->priv->sctp_transport->transport, "client", &is_client,
+ NULL);
+
+ /* TODO: a better search algorithm */
+ do {
+ GstWebRTCDataChannel *channel;
+
+ new_id++;
+
+ if (new_id < 0 || new_id >= max_channels) {
+ /* exhausted id space */
+ GST_WARNING_OBJECT (webrtc, "Could not find a suitable "
+ "data channel id (max %i)", max_channels);
+ return -1;
+ }
+
+ /* client must generate even ids, server must generate odd ids */
+ if (new_id % 2 == ! !is_client)
+ continue;
+
+ channel = _find_data_channel_for_id (webrtc, new_id);
+ if (!channel)
+ break;
+ } while (TRUE);
+
+ return new_id;
+}
+
+static void
+_update_data_channel_from_sdp_media (GstWebRTCBin * webrtc,
+ const GstSDPMessage * sdp, guint media_idx, TransportStream * stream)
+{
+ const GstSDPMedia *local_media, *remote_media;
+ GstWebRTCDTLSSetup local_setup, remote_setup, new_setup;
+ TransportReceiveBin *receive;
+ int local_port, remote_port;
+ guint64 local_max_size, remote_max_size, max_size;
+ int i;
+
+ local_media =
+ gst_sdp_message_get_media (webrtc->current_local_description->sdp,
+ media_idx);
+ remote_media =
+ gst_sdp_message_get_media (webrtc->current_remote_description->sdp,
+ media_idx);
+
+ 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)
+ return;
+
+ /* data channel is always rtcp-muxed to avoid generating ICE candidates
+ * for RTCP */
+ g_object_set (stream, "rtcp-mux", TRUE, "dtls-client",
+ new_setup == GST_WEBRTC_DTLS_SETUP_ACTIVE, NULL);
+
+ 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)
+ return;
+
+ if (0 == (local_max_size =
+ _get_sctp_max_message_size_from_media (local_media)))
+ local_max_size = G_MAXUINT64;
+ if (0 == (remote_max_size =
+ _get_sctp_max_message_size_from_media (remote_media)))
+ remote_max_size = G_MAXUINT64;
+ max_size = MIN (local_max_size, remote_max_size);
+
+ webrtc->priv->sctp_transport->max_message_size = max_size;
+
+ g_object_set (webrtc->priv->sctp_transport->sctpdec, "local-sctp-port",
+ local_port, NULL);
+ g_object_set (webrtc->priv->sctp_transport->sctpenc, "remote-sctp-port",
+ remote_port, NULL);
+
+ for (i = 0; i < webrtc->priv->data_channels->len; i++) {
+ GstWebRTCDataChannel *channel;
+
+ channel =
+ g_array_index (webrtc->priv->data_channels, GstWebRTCDataChannel *, i);
+
+ if (channel->id == -1)
+ channel->id = _generate_data_channel_id (webrtc);
+ if (channel->id == -1)
+ GST_ELEMENT_WARNING (webrtc, RESOURCE, NOT_FOUND,
+ ("%s", "Failed to generate an identifier for a data channel"), NULL);
+
+ if (webrtc->priv->sctp_transport->association_established
+ && !channel->negotiated && !channel->opened) {
+ _link_data_channel_to_sctp (webrtc, channel);
+ gst_webrtc_data_channel_start_negotiation (channel);
+ }
+ }
+
+ receive = TRANSPORT_RECEIVE_BIN (stream->receive_bin);
+ transport_receive_bin_set_receive_state (receive, RECEIVE_STATE_PASS);
+}
+
static gboolean
_find_compatible_unassociated_transceiver (GstWebRTCRTPTransceiver * p1,
gconstpointer data)
@@ -2745,6 +3273,7 @@ _update_transceivers_from_sdp (GstWebRTCBin * webrtc, SDPSource source,
for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) {
const GstSDPMedia *media = gst_sdp_message_get_media (sdp->sdp, i);
+ TransportStream *stream;
GstWebRTCRTPTransceiver *trans;
/* skip rejected media */
@@ -2753,24 +3282,41 @@ _update_transceivers_from_sdp (GstWebRTCBin * webrtc, SDPSource source,
trans = _find_transceiver_for_sdp_media (webrtc, sdp->sdp, i);
+ stream = _find_transport_for_session (webrtc, i);
+ if (!stream) {
+ stream = _create_transport_stream (webrtc, i,
+ _message_media_is_datachannel (sdp->sdp, i));
+ if (trans)
+ 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.");
return FALSE;
} else {
- if (trans) {
- _update_transceiver_from_sdp_media (webrtc, sdp->sdp, i, trans);
+ if (g_strcmp0 (gst_sdp_media_get_media (media), "audio") == 0 ||
+ g_strcmp0 (gst_sdp_media_get_media (media), "video") == 0) {
+ if (trans) {
+ _update_transceiver_from_sdp_media (webrtc, sdp->sdp, i, stream,
+ trans);
+ } else {
+ trans = _find_transceiver (webrtc, NULL,
+ (FindTransceiverFunc) _find_compatible_unassociated_transceiver);
+ /* XXX: default to the advertised direction in the sdp for new
+ * transceviers. The spec doesn't actually say what happens here, only
+ * that calls to setDirection will change the value. Nothing about
+ * a default value when the transceiver is created internally */
+ if (!trans)
+ trans =
+ GST_WEBRTC_RTP_TRANSCEIVER (_create_webrtc_transceiver (webrtc,
+ _get_direction_from_media (media), i));
+ _update_transceiver_from_sdp_media (webrtc, sdp->sdp, i, stream,
+ trans);
+ }
+ } else if (_message_media_is_datachannel (sdp->sdp, i)) {
+ _update_data_channel_from_sdp_media (webrtc, sdp->sdp, i, stream);
} else {
- trans = _find_transceiver (webrtc, NULL,
- (FindTransceiverFunc) _find_compatible_unassociated_transceiver);
- /* XXX: default to the advertised direction in the sdp for new
- * transceviers. The spec doesn't actually say what happens here, only
- * that calls to setDirection will change the value. Nothing about
- * a default value when the transceiver is created internally */
- if (!trans)
- trans =
- GST_WEBRTC_RTP_TRANSCEIVER (_create_webrtc_transceiver (webrtc,
- _get_direction_from_media (media), i));
- _update_transceiver_from_sdp_media (webrtc, sdp->sdp, i, trans);
+ GST_ERROR_OBJECT (webrtc, "Unknown media type in SDP at index %u", i);
}
}
}
@@ -2982,8 +3528,6 @@ _set_description_task (GstWebRTCBin * webrtc, struct set_description *sd)
g_free (to);
}
- /* TODO: necessary data channel modifications */
-
if (sd->sdp->type == GST_WEBRTC_SDP_TYPE_ROLLBACK) {
/* FIXME:
* If the mid value of an RTCRtpTransceiver was set to a non-null value
@@ -3001,8 +3545,8 @@ _set_description_task (GstWebRTCBin * webrtc, struct set_description *sd)
}
if (webrtc->signaling_state == GST_WEBRTC_SIGNALING_STATE_STABLE) {
- GList *tmp;
gboolean prev_need_negotiation = webrtc->priv->need_negotiation;
+ GList *tmp;
/* media modifications */
_update_transceivers_from_sdp (webrtc, sd->source, sd->sdp);
@@ -3040,7 +3584,9 @@ _set_description_task (GstWebRTCBin * webrtc, struct set_description *sd)
/* FIXME: bundle */
item = _find_transport_for_session (webrtc, i);
if (!item)
- item = _create_transport_channel (webrtc, i);
+ item =
+ _create_transport_stream (webrtc, i,
+ _message_media_is_datachannel (sd->sdp->sdp, i));
_get_ice_credentials_from_sdp_media (sd->sdp->sdp, i, &ufrag, &pwd);
gst_webrtc_ice_set_local_credentials (webrtc->priv->ice,
@@ -3060,7 +3606,9 @@ _set_description_task (GstWebRTCBin * webrtc, struct set_description *sd)
/* FIXME: bundle */
item = _find_transport_for_session (webrtc, i);
if (!item)
- item = _create_transport_channel (webrtc, i);
+ item =
+ _create_transport_stream (webrtc, i,
+ _message_media_is_datachannel (sd->sdp->sdp, i));
_get_ice_credentials_from_sdp_media (sd->sdp->sdp, i, &ufrag, &pwd);
gst_webrtc_ice_set_remote_credentials (webrtc->priv->ice,
@@ -3374,6 +3922,130 @@ copy_sticky_events (GstPad * pad, GstEvent ** event, gpointer user_data)
return TRUE;
}
+static GstWebRTCDataChannel *
+gst_webrtc_bin_create_data_channel (GstWebRTCBin * webrtc, const gchar * label,
+ GstStructure * init_params)
+{
+ gboolean ordered;
+ gint max_packet_lifetime;
+ gint max_retransmits;
+ const gchar *protocol;
+ gboolean negotiated;
+ gint id;
+ GstWebRTCPriorityType priority;
+ GstWebRTCDataChannel *ret;
+ gint max_channels = 65534;
+
+ g_return_val_if_fail (GST_IS_WEBRTC_BIN (webrtc), NULL);
+ g_return_val_if_fail (label != NULL, NULL);
+ g_return_val_if_fail (strlen (label) <= 65535, NULL);
+ g_return_val_if_fail (webrtc->priv->is_closed != TRUE, NULL);
+
+ if (!init_params
+ || !gst_structure_get_boolean (init_params, "ordered", &ordered))
+ ordered = TRUE;
+ if (!init_params
+ || !gst_structure_get_int (init_params, "max-packet-lifetime",
+ &max_packet_lifetime))
+ max_packet_lifetime = -1;
+ if (!init_params
+ || !gst_structure_get_boolean (init_params, "max-retransmits",
+ &max_retransmits))
+ max_retransmits = -1;
+ /* both retransmits and lifetime cannot be set */
+ g_return_val_if_fail ((max_packet_lifetime == -1)
+ || (max_retransmits == -1), NULL);
+
+ if (!init_params
+ || !(protocol = gst_structure_get_string (init_params, "protocol")))
+ protocol = "";
+ g_return_val_if_fail (strlen (protocol) <= 65535, NULL);
+
+ if (!init_params
+ || !gst_structure_get_boolean (init_params, "negotiated", &negotiated))
+ negotiated = FALSE;
+ if (!negotiated || !init_params
+ || !gst_structure_get_int (init_params, "id", &id))
+ id = -1;
+ if (negotiated)
+ g_return_val_if_fail (id != -1, NULL);
+ g_return_val_if_fail (id < 65535, NULL);
+
+ if (!init_params
+ || !gst_structure_get_enum (init_params, "priority",
+ GST_TYPE_WEBRTC_PRIORITY_TYPE, (gint *) & priority))
+ priority = GST_WEBRTC_PRIORITY_TYPE_LOW;
+
+ /* FIXME: clamp max-retransmits and max-packet-lifetime */
+
+ if (webrtc->priv->sctp_transport) {
+ /* Let transport be the connection's [[SctpTransport]] slot.
+ *
+ * If the [[DataChannelId]] slot is not null, transport is in
+ * connected state and [[DataChannelId]] is greater or equal to the
+ * transport's [[MaxChannels]] slot, throw an OperationError.
+ */
+ g_object_get (webrtc->priv->sctp_transport, "max-channels", &max_channels,
+ NULL);
+
+ g_return_val_if_fail (id <= max_channels, NULL);
+ }
+
+ if (!_have_nice_elements (webrtc) || !_have_dtls_elements (webrtc) ||
+ !_have_sctp_elements (webrtc))
+ return NULL;
+
+ PC_LOCK (webrtc);
+ /* check if the id has been used already */
+ if (id != -1) {
+ GstWebRTCDataChannel *channel = _find_data_channel_for_id (webrtc, id);
+ if (channel) {
+ GST_ELEMENT_WARNING (webrtc, LIBRARY, SETTINGS,
+ ("Attempting to add a data channel with a duplicate ID: %i", id),
+ NULL);
+ PC_UNLOCK (webrtc);
+ return NULL;
+ }
+ } else if (webrtc->current_local_description
+ && webrtc->current_remote_description && webrtc->priv->sctp_transport
+ && webrtc->priv->sctp_transport->transport) {
+ /* else we can only generate an id if we're configured already. The other
+ * case for generating an id is on sdp setting */
+ id = _generate_data_channel_id (webrtc);
+ if (id == -1) {
+ GST_ELEMENT_WARNING (webrtc, RESOURCE, NOT_FOUND,
+ ("%s", "Failed to generate an identifier for a data channel"), NULL);
+ PC_UNLOCK (webrtc);
+ return NULL;
+ }
+ }
+
+ ret = g_object_new (GST_TYPE_WEBRTC_DATA_CHANNEL, "label", label,
+ "ordered", ordered, "max-packet-lifetime", max_packet_lifetime,
+ "max-retransmits", max_retransmits, "protocol", protocol,
+ "negotiated", negotiated, "id", id, "priority", priority, NULL);
+
+ if (ret) {
+ gst_bin_add (GST_BIN (webrtc), ret->appsrc);
+ gst_bin_add (GST_BIN (webrtc), ret->appsink);
+
+ gst_element_sync_state_with_parent (ret->appsrc);
+ gst_element_sync_state_with_parent (ret->appsink);
+
+ ret = gst_object_ref (ret);
+ ret->webrtcbin = webrtc;
+ g_array_append_val (webrtc->priv->data_channels, ret);
+ _link_data_channel_to_sctp (webrtc, ret);
+ if (webrtc->priv->sctp_transport &&
+ webrtc->priv->sctp_transport->association_established
+ && !ret->negotiated)
+ gst_webrtc_data_channel_start_negotiation (ret);
+ }
+
+ PC_UNLOCK (webrtc);
+ return ret;
+}
+
/* === rtpbin signal implementations === */
static void
@@ -4001,6 +4673,8 @@ gst_webrtc_bin_dispose (GObject * object)
g_array_free (webrtc->priv->ice_stream_map, TRUE);
webrtc->priv->ice_stream_map = NULL;
+ g_clear_object (&webrtc->priv->sctp_transport);
+
G_OBJECT_CLASS (parent_class)->dispose (object);
}
@@ -4017,6 +4691,14 @@ gst_webrtc_bin_finalize (GObject * object)
g_array_free (webrtc->priv->transceivers, TRUE);
webrtc->priv->transceivers = NULL;
+ if (webrtc->priv->data_channels)
+ g_array_free (webrtc->priv->data_channels, TRUE);
+ webrtc->priv->data_channels = NULL;
+
+ if (webrtc->priv->pending_data_channels)
+ g_array_free (webrtc->priv->pending_data_channels, TRUE);
+ webrtc->priv->pending_data_channels = NULL;
+
if (webrtc->priv->pending_ice_candidates)
g_array_free (webrtc->priv->pending_ice_candidates, TRUE);
webrtc->priv->pending_ice_candidates = NULL;
@@ -4313,6 +4995,16 @@ gst_webrtc_bin_class_init (GstWebRTCBinClass * klass)
G_TYPE_NONE, 1, GST_TYPE_WEBRTC_RTP_TRANSCEIVER);
/**
+ * GstWebRTCBin::on-data-channel:
+ * @object: the #GstWebRtcBin
+ * @candidate: the new #GstWebRTCDataChannel
+ */
+ gst_webrtc_bin_signals[ON_DATA_CHANNEL_SIGNAL] =
+ g_signal_new ("on-data-channel", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic,
+ G_TYPE_NONE, 1, GST_TYPE_WEBRTC_DATA_CHANNEL);
+
+ /**
* GstWebRTCBin::add-transceiver:
* @object: the #GstWebRtcBin
* @direction: the direction of the new transceiver
@@ -4351,6 +5043,33 @@ gst_webrtc_bin_class_init (GstWebRTCBinClass * klass)
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_CALLBACK (gst_webrtc_bin_add_turn_server), NULL, NULL,
g_cclosure_marshal_generic, G_TYPE_BOOLEAN, 1, G_TYPE_STRING);
+
+ /*
+ * GstWebRTCBin::create-data-channel:
+ * @object: the #GstWebRtcBin
+ * @label: the label for the data channel
+ * @options: a #GstStructure of options for creating the data channel
+ *
+ * The options dictionary is the same format as the RTCDataChannelInit
+ * members outlined https://www.w3.org/TR/webrtc/#dom-rtcdatachannelinit and
+ * and reproduced below
+ *
+ * ordered G_TYPE_BOOLEAN Whether the channal will send data with guarenteed ordering
+ * max-packet-lifetime G_TYPE_INT The time in milliseconds to attempt transmitting unacknowledged data. -1 for unset
+ * max-retransmits G_TYPE_INT The number of times data will be attempted to be transmitted without acknowledgement before dropping
+ * protocol G_TYPE_STRING The subprotocol used by this channel
+ * negotiated G_TYPE_BOOLEAN Whether the created data channel should not perform in-band chnanel announcment. If %TRUE, then application must negotiate the channel itself and create the corresponding channel on the peer with the same id.
+ * id G_TYPE_INT Override the default identifier selection of this channel
+ * priority GST_TYPE_WEBRTC_PRIORITY_TYPE The priority to use for this channel
+ *
+ * Returns: a new data channel object
+ */
+ gst_webrtc_bin_signals[CREATE_DATA_CHANNEL_SIGNAL] =
+ g_signal_new_class_handler ("create-data-channel",
+ G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_CALLBACK (gst_webrtc_bin_create_data_channel), NULL, NULL,
+ g_cclosure_marshal_generic, GST_TYPE_WEBRTC_DATA_CHANNEL, 2,
+ G_TYPE_STRING, GST_TYPE_STRUCTURE);
}
static void
@@ -4404,6 +5123,15 @@ gst_webrtc_bin_init (GstWebRTCBin * webrtc)
g_array_set_clear_func (webrtc->priv->transports,
(GDestroyNotify) _transport_free);
+ webrtc->priv->data_channels = g_array_new (FALSE, TRUE, sizeof (gpointer));
+ g_array_set_clear_func (webrtc->priv->data_channels,
+ (GDestroyNotify) _deref_and_unref);
+
+ webrtc->priv->pending_data_channels =
+ g_array_new (FALSE, TRUE, sizeof (gpointer));
+ g_array_set_clear_func (webrtc->priv->pending_data_channels,
+ (GDestroyNotify) _deref_and_unref);
+
webrtc->priv->session_mid_map =
g_array_new (FALSE, TRUE, sizeof (SessionMidItem));
g_array_set_clear_func (webrtc->priv->session_mid_map,