* assert sending payload type matches the stream
* reconfiguration (of anything)
* LS groups
- * bundling
+ * balanced bundle policy
* setting custom DTLS certificates
*
* seperate session id's from mlineindex properly
PROP_PENDING_REMOTE_DESCRIPTION,
PROP_STUN_SERVER,
PROP_TURN_SERVER,
+ PROP_BUNDLE_POLICY,
};
static guint gst_webrtc_bin_signals[LAST_SIGNAL] = { 0 };
}
static TransportStream *
-_create_rtp_transport_channel (GstWebRTCBin * webrtc, guint session_id)
+_get_or_create_rtp_transport_channel (GstWebRTCBin * webrtc, guint session_id)
{
- TransportStream *ret = _create_transport_channel (webrtc, session_id);
+ TransportStream *ret;
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));
+ ret = _find_transport_for_session (webrtc, session_id);
- pad_name = g_strdup_printf ("recv_rtcp_sink_%u", ret->session_id);
- if (!gst_element_link_pads (GST_ELEMENT (ret->receive_bin), "rtcp_src",
- GST_ELEMENT (webrtc->rtpbin), pad_name))
- g_warn_if_reached ();
- g_free (pad_name);
+ if (!ret) {
+ ret = _create_transport_channel (webrtc, session_id);
+ gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (ret->send_bin));
+ gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (ret->receive_bin));
+ g_array_append_val (webrtc->priv->transports, ret);
- pad_name = g_strdup_printf ("send_rtcp_src_%u", ret->session_id);
- if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
- GST_ELEMENT (ret->send_bin), "rtcp_sink"))
- g_warn_if_reached ();
- g_free (pad_name);
+ pad_name = g_strdup_printf ("recv_rtcp_sink_%u", ret->session_id);
+ if (!gst_element_link_pads (GST_ELEMENT (ret->receive_bin), "rtcp_src",
+ GST_ELEMENT (webrtc->rtpbin), pad_name))
+ g_warn_if_reached ();
+ g_free (pad_name);
- g_array_append_val (webrtc->priv->transports, ret);
+ pad_name = g_strdup_printf ("send_rtcp_src_%u", ret->session_id);
+ if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
+ GST_ELEMENT (ret->send_bin), "rtcp_sink"))
+ g_warn_if_reached ();
+ g_free (pad_name);
+ }
gst_element_sync_state_with_parent (GST_ELEMENT (ret->send_bin));
gst_element_sync_state_with_parent (GST_ELEMENT (ret->receive_bin));
}
static TransportStream *
-_create_data_channel_transports (GstWebRTCBin * webrtc, guint session_id)
+_get_or_create_data_channel_transports (GstWebRTCBin * webrtc, guint session_id)
{
if (!webrtc->priv->data_channel_transport) {
- TransportStream *stream = _create_transport_channel (webrtc, session_id);
+ TransportStream *stream;
GstWebRTCSCTPTransport *sctp_transport;
int i;
+ stream = _find_transport_for_session (webrtc, session_id);
+
+ if (!stream) {
+ stream = _create_transport_channel (webrtc, session_id);
+ gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (stream->send_bin));
+ gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (stream->receive_bin));
+ g_array_append_val (webrtc->priv->transports, stream);
+ }
+
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 =
(sctp_transport->sctpenc));
}
- g_array_append_val (webrtc->priv->transports, stream);
-
webrtc->priv->sctp_transport = sctp_transport;
}
}
static TransportStream *
-_create_transport_stream (GstWebRTCBin * webrtc, guint session_id,
+_get_or_create_transport_stream (GstWebRTCBin * webrtc, guint session_id,
gboolean is_datachannel)
{
if (is_datachannel)
- return _create_data_channel_transports (webrtc, session_id);
+ return _get_or_create_data_channel_transports (webrtc, session_id);
else
- return _create_rtp_transport_channel (webrtc, session_id);
+ return _get_or_create_rtp_transport_channel (webrtc, session_id);
}
static guint
/* based off https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-18#section-5.2.1 */
static gboolean
sdp_media_from_transceiver (GstWebRTCBin * webrtc, GstSDPMedia * media,
- GstWebRTCRTPTransceiver * trans, GstWebRTCSDPType type, guint media_idx)
+ GstWebRTCRTPTransceiver * trans, GstWebRTCSDPType type, guint media_idx,
+ GString * bundled_mids, guint bundle_idx, gboolean bundle_only)
{
/* TODO:
* rtp header extensions
|| trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE)
return FALSE;
- gst_sdp_media_set_port_info (media, 9, 0);
+ gst_sdp_media_set_port_info (media, bundle_only ? 0 : 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);
+ if (bundle_only) {
+ gst_sdp_media_add_attribute (media, "bundle-only", NULL);
+ }
+
+ /* FIXME: negotiate this */
+ /* FIXME: when bundle_only, these should not be added:
+ * https://tools.ietf.org/html/draft-ietf-mmusic-sdp-bundle-negotiation-52#section-7.1.3
+ * However, this causes incompatibilities with current versions
+ * of the major browsers */
+ gst_sdp_media_add_attribute (media, "rtcp-mux", "");
+ gst_sdp_media_add_attribute (media, "rtcp-rsize", NULL);
+
direction =
_enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
trans->direction);
gst_sdp_media_add_attribute (media, direction, "");
g_free (direction);
- /* FIXME: negotiate this */
- gst_sdp_media_add_attribute (media, "rtcp-mux", "");
- gst_sdp_media_add_attribute (media, "rtcp-rsize", NULL);
if (type == GST_WEBRTC_SDP_TYPE_OFFER) {
caps = _find_codec_preferences (webrtc, trans, GST_PAD_SINK, media_idx);
if (!trans->sender->transport) {
TransportStream *item;
- /* FIXME: bundle */
- item = _find_transport_for_session (webrtc, media_idx);
- if (!item)
- item = _create_transport_stream (webrtc, media_idx, FALSE);
+
+ item =
+ _get_or_create_transport_stream (webrtc,
+ bundled_mids ? bundle_idx : media_idx, FALSE);
+
webrtc_transceiver_set_transport (WEBRTC_TRANSCEIVER (trans), item);
}
GstSDPMessage *ret;
int i;
gchar *str;
+ GString *bundled_mids = NULL;
+ gchar *bundle_ufrag = NULL;
+ gchar *bundle_pwd = NULL;
gst_sdp_message_new (&ret);
gst_sdp_message_add_attribute (ret, "msid-semantic", str);
g_free (str);
+ if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE) {
+ bundled_mids = g_string_new ("BUNDLE");
+ } else if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_COMPAT) {
+ bundled_mids = g_string_new ("BUNDLE");
+ }
+
+ if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) {
+ _generate_ice_credentials (&bundle_ufrag, &bundle_pwd);
+ }
+
/* for each rtp transceiver */
for (i = 0; i < webrtc->priv->transceivers->len; i++) {
GstWebRTCRTPTransceiver *trans;
GstSDPMedia media = { 0, };
gchar *ufrag, *pwd;
+ gboolean bundle_only = bundled_mids
+ && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE
+ && i != 0;
trans =
g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
gst_sdp_media_add_attribute (&media, "setup", "actpass");
/* FIXME: only needed when restarting ICE */
- _generate_ice_credentials (&ufrag, &pwd);
+ if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
+ _generate_ice_credentials (&ufrag, &pwd);
+ } else {
+ ufrag = g_strdup (bundle_ufrag);
+ pwd = g_strdup (bundle_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);
if (sdp_media_from_transceiver (webrtc, &media, trans,
- GST_WEBRTC_SDP_TYPE_OFFER, i))
+ GST_WEBRTC_SDP_TYPE_OFFER, i, bundled_mids, 0, bundle_only)) {
+ if (bundled_mids) {
+ const gchar *mid = gst_sdp_media_get_attribute_val (&media, "mid");
+
+ g_assert (mid);
+ g_string_append_printf (bundled_mids, " %s", mid);
+ }
gst_sdp_message_add_media (ret, &media);
- else
+ } else {
gst_sdp_media_uninit (&media);
+ }
}
/* add data channel support */
if (webrtc->priv->data_channels->len > 0) {
GstSDPMedia media = { 0, };
gchar *ufrag, *pwd, *sdp_mid;
+ gboolean bundle_only = bundled_mids
+ && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE
+ && webrtc->priv->transceivers->len != 0;
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);
+ if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
+ _generate_ice_credentials (&ufrag, &pwd);
+ } else {
+ ufrag = g_strdup (bundle_ufrag);
+ pwd = g_strdup (bundle_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_port_info (&media, bundle_only ? 0 : 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");
+ if (bundle_only)
+ gst_sdp_media_add_attribute (&media, "bundle-only", NULL);
+
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);
+ if (bundled_mids)
+ g_string_append_printf (bundled_mids, " %s", 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);
+ _get_or_create_data_channel_transports (webrtc,
+ bundled_mids ? 0 : webrtc->priv->transceivers->len);
{
gchar *cert, *fingerprint, *val;
gst_sdp_message_add_media (ret, &media);
}
+ if (bundled_mids) {
+ gchar *mids = g_string_free (bundled_mids, FALSE);
+
+ gst_sdp_message_add_attribute (ret, "group", mids);
+ g_free (mids);
+ }
+
+ if (bundle_ufrag)
+ g_free (bundle_ufrag);
+
+ if (bundle_pwd)
+ g_free (bundle_pwd);
+
/* FIXME: pre-emptively setup receiving elements when needed */
/* XXX: only true for the initial offerer */
gst_structure_get_uint (s, "ssrc", target_ssrc);
}
+static gboolean
+_parse_bundle (GstWebRTCBin * webrtc, GstSDPMessage * sdp, GStrv * bundled)
+{
+ const gchar *group;
+ gboolean ret = FALSE;
+
+ group = gst_sdp_message_get_attribute_val (sdp, "group");
+
+ if (group && g_str_has_prefix (group, "BUNDLE ")) {
+ *bundled = g_strsplit (group + strlen ("BUNDLE "), " ", 0);
+
+ if (!(*bundled)[0]) {
+ GST_ERROR_OBJECT (webrtc,
+ "Invalid format for BUNDLE group, expected at least one mid (%s)",
+ group);
+ goto done;
+ }
+ } else {
+ ret = TRUE;
+ goto done;
+ }
+
+ ret = TRUE;
+
+done:
+ return ret;
+}
+
+static gboolean
+_get_bundle_index (GstSDPMessage * sdp, GStrv bundled, guint * idx)
+{
+ gboolean ret = FALSE;
+ guint i;
+
+ for (i = 0; i < gst_sdp_message_medias_len (sdp); i++) {
+ const GstSDPMedia *media = gst_sdp_message_get_media (sdp, i);
+ const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid");
+
+ if (!g_strcmp0 (mid, bundled[0])) {
+ *idx = i;
+ ret = TRUE;
+ break;
+ }
+ }
+
+ return ret;
+}
+
static GstSDPMessage *
_create_answer_task (GstWebRTCBin * webrtc, const GstStructure * options)
{
const GstWebRTCSessionDescription *pending_remote =
webrtc->pending_remote_description;
guint i;
+ GStrv bundled = NULL;
+ guint bundle_idx = 0;
+ GString *bundled_mids = NULL;
+ gchar *bundle_ufrag = NULL;
+ gchar *bundle_pwd = NULL;
if (!webrtc->pending_remote_description) {
GST_ERROR_OBJECT (webrtc,
return NULL;
}
+ if (!_parse_bundle (webrtc, pending_remote->sdp, &bundled))
+ goto out;
+
+ if (bundled) {
+ 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]);
+ goto out;
+ }
+
+ if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) {
+ bundled_mids = g_string_new ("BUNDLE");
+ }
+
+ _generate_ice_credentials (&bundle_ufrag, &bundle_pwd);
+ }
+
gst_sdp_message_new (&ret);
/* FIXME: session id and version need special handling depending on the state we're in */
}
for (i = 0; i < gst_sdp_message_medias_len (pending_remote->sdp); i++) {
- /* FIXME:
- * bundle policy
- */
GstSDPMedia *media = NULL;
GstSDPMedia *offer_media;
GstWebRTCRTPTransceiver *rtp_trans = NULL;
gint target_pt = -1;
gint original_target_pt = -1;
guint target_ssrc = 0;
+ gboolean bundle_only;
+
+ offer_media =
+ (GstSDPMedia *) gst_sdp_message_get_media (pending_remote->sdp, i);
+ bundle_only = _media_has_attribute_key (offer_media, "bundle-only");
gst_sdp_media_new (&media);
- gst_sdp_media_set_port_info (media, 9, 0);
+ if (bundle_only && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE)
+ gst_sdp_media_set_port_info (media, 0, 0);
+ else
+ gst_sdp_media_set_port_info (media, 9, 0);
gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
{
/* FIXME: only needed when restarting ICE */
gchar *ufrag, *pwd;
- _generate_ice_credentials (&ufrag, &pwd);
+ if (!bundled) {
+ _generate_ice_credentials (&ufrag, &pwd);
+ } else {
+ ufrag = g_strdup (bundle_ufrag);
+ pwd = g_strdup (bundle_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);
}
- 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);
/* FIXME: negotiate this properly on renegotiation */
gst_sdp_media_add_attribute (media, "sctp-port", "5000");
- _create_data_channel_transports (webrtc, i);
+ _get_or_create_data_channel_transports (webrtc,
+ bundled_mids ? bundle_idx : i);
+
+ if (bundled_mids) {
+ const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid");
+
+ g_assert (mid);
+ g_string_append_printf (bundled_mids, " %s", mid);
+ }
{
gchar *cert, *fingerprint, *val;
}
_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);
+ TransportStream *item;
+
+ if (bundled_mids) {
+ const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid");
+ item = _get_or_create_transport_stream (webrtc, bundle_idx, FALSE);
+
+ g_assert (mid);
+ g_string_append_printf (bundled_mids, " %s", mid);
+ } else {
+ item = _get_or_create_transport_stream (webrtc, i, FALSE);
+ }
webrtc_transceiver_set_transport (trans, item);
}
/* set the a=fingerprint: for this transport */
gst_sdp_media_free (media);
}
+ if (bundled_mids) {
+ gchar *mids = g_string_free (bundled_mids, FALSE);
+
+ gst_sdp_message_add_attribute (ret, "group", mids);
+ g_free (mids);
+ }
+
+ if (bundle_ufrag)
+ g_free (bundle_ufrag);
+
+ if (bundle_pwd)
+ g_free (bundle_pwd);
+
/* FIXME: can we add not matched transceivers? */
/* XXX: only true for the initial offerer */
g_object_set (webrtc->priv->ice, "controller", FALSE, NULL);
+out:
+ if (bundled)
+ g_strfreev (bundled);
+
return ret;
}
_connect_input_stream (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
{
/*
+ * Not-bundle case:
+ *
* ,-------------------------webrtcbin-------------------------,
* ; ;
* ; ,-------rtpbin-------, ,--transport_send_%u--, ;
* ; '--------------------' ;
* '--------------------- -------------------------------------'
*/
+
+/*
+ * Bundle case:
+ * ,--------------------------------webrtcbin--------------------------------,
+ * ; ;
+ * ; ,-------rtpbin-------, ,--transport_send_%u--, ;
+ * ; ; send_rtp_src_%u o---o rtp_sink ; ;
+ * ; ; ; ; ; ;
+ * ; ; send_rtcp_src_%u o---o rtcp_sink ; ;
+ * ; sink_%u ,---funnel---, ; ; '---------------------' ;
+ * o---------o sink_%u ; ; ; ;
+ * ; sink_%u ; src o-o send_rtp_sink_%u ; ;
+ * o---------o sink_%u ; ; ; ;
+ * ; '------------' '--------------------' ;
+ * '-------------------------------------------------------------------------'
+ */
GstPadTemplate *rtp_templ;
GstPad *rtp_sink;
gchar *pad_name;
GST_INFO_OBJECT (pad, "linking input stream %u", pad->mlineindex);
- rtp_templ =
- _find_pad_template (webrtc->rtpbin, GST_PAD_SINK, GST_PAD_REQUEST,
- "send_rtp_sink_%u");
- g_assert (rtp_templ);
+ trans = WEBRTC_TRANSCEIVER (pad->trans);
- pad_name = g_strdup_printf ("send_rtp_sink_%u", pad->mlineindex);
- rtp_sink =
- gst_element_request_pad (webrtc->rtpbin, rtp_templ, pad_name, NULL);
- g_free (pad_name);
- gst_ghost_pad_set_target (GST_GHOST_PAD (pad), rtp_sink);
- gst_object_unref (rtp_sink);
+ g_assert (trans->stream);
- trans = WEBRTC_TRANSCEIVER (pad->trans);
+ if (!webrtc->rtpfunnel) {
+ rtp_templ =
+ _find_pad_template (webrtc->rtpbin, GST_PAD_SINK, GST_PAD_REQUEST,
+ "send_rtp_sink_%u");
+ g_assert (rtp_templ);
- if (!trans->stream) {
- TransportStream *item;
- /* FIXME: bundle */
- item = _find_transport_for_session (webrtc, pad->mlineindex);
- if (!item)
- item = _create_transport_stream (webrtc, pad->mlineindex, FALSE);
- webrtc_transceiver_set_transport (trans, item);
- }
+ pad_name = g_strdup_printf ("send_rtp_sink_%u", pad->mlineindex);
+ rtp_sink =
+ gst_element_request_pad (webrtc->rtpbin, rtp_templ, pad_name, NULL);
+ g_free (pad_name);
+ gst_ghost_pad_set_target (GST_GHOST_PAD (pad), rtp_sink);
+ gst_object_unref (rtp_sink);
- pad_name = g_strdup_printf ("send_rtp_src_%u", pad->mlineindex);
- if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
- GST_ELEMENT (trans->stream->send_bin), "rtp_sink"))
- g_warn_if_reached ();
- g_free (pad_name);
+ pad_name = g_strdup_printf ("send_rtp_src_%u", pad->mlineindex);
+ if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
+ GST_ELEMENT (trans->stream->send_bin), "rtp_sink"))
+ g_warn_if_reached ();
+ g_free (pad_name);
+ } else {
+ gchar *pad_name = g_strdup_printf ("sink_%u", pad->mlineindex);
+ GstPad *funnel_sinkpad =
+ gst_element_get_request_pad (webrtc->rtpfunnel, pad_name);
+
+ gst_ghost_pad_set_target (GST_GHOST_PAD (pad), funnel_sinkpad);
+
+ g_free (pad_name);
+ gst_object_unref (funnel_sinkpad);
+ }
gst_element_sync_state_with_parent (GST_ELEMENT (trans->stream->send_bin));
}
/* output pads are receiving elements */
-static GstWebRTCBinPad *
-_connect_output_stream (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
+static void
+_connect_output_stream (GstWebRTCBin * webrtc,
+ TransportStream * stream, guint session_id)
{
/*
* ,------------------------webrtcbin------------------------,
* '---------------------------------------------------------'
*/
gchar *pad_name;
- WebRTCTransceiver *trans;
-
- g_return_val_if_fail (pad->trans != NULL, NULL);
- GST_INFO_OBJECT (pad, "linking output stream %u", pad->mlineindex);
+ GST_INFO_OBJECT (webrtc, "linking output stream %u", session_id);
- trans = WEBRTC_TRANSCEIVER (pad->trans);
- if (!trans->stream) {
- TransportStream *item;
- /* FIXME: bundle */
- item = _find_transport_for_session (webrtc, pad->mlineindex);
- if (!item)
- item = _create_transport_stream (webrtc, pad->mlineindex, FALSE);
- webrtc_transceiver_set_transport (trans, item);
- }
-
- pad_name = g_strdup_printf ("recv_rtp_sink_%u", pad->mlineindex);
- if (!gst_element_link_pads (GST_ELEMENT (trans->stream->receive_bin),
+ pad_name = g_strdup_printf ("recv_rtp_sink_%u", session_id);
+ if (!gst_element_link_pads (GST_ELEMENT (stream->receive_bin),
"rtp_src", GST_ELEMENT (webrtc->rtpbin), pad_name))
g_warn_if_reached ();
g_free (pad_name);
- gst_element_sync_state_with_parent (GST_ELEMENT (trans->stream->receive_bin));
-
- return pad;
+ gst_element_sync_state_with_parent (GST_ELEMENT (stream->receive_bin));
}
typedef struct
static void
_update_transceiver_from_sdp_media (GstWebRTCBin * webrtc,
const GstSDPMessage * sdp, guint media_idx,
- TransportStream * stream, GstWebRTCRTPTransceiver * rtp_trans)
+ TransportStream * stream, GstWebRTCRTPTransceiver * rtp_trans,
+ GStrv bundled, guint bundle_idx, gboolean * should_connect_bundle_stream)
{
WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
GstWebRTCRTPTransceiverDirection prev_dir = rtp_trans->current_direction;
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)
return;
GST_DEBUG_OBJECT (webrtc, "mapping sdp media level attributes to caps");
gst_sdp_media_attributes_to_caps (media, global_caps);
- /* clear the ptmap */
- g_array_set_size (stream->ptmap, 0);
+ if (!bundled) {
+ /* clear the ptmap */
+ g_array_set_size (stream->ptmap, 0);
+ }
len = gst_sdp_media_formats_len (media);
for (i = 0; i < len; i++) {
gst_caps_unref (global_caps);
}
- new_rtcp_mux = _media_has_attribute_key (local_media, "rtcp-mux")
- && _media_has_attribute_key (remote_media, "rtcp-mux");
- new_rtcp_rsize = _media_has_attribute_key (local_media, "rtcp-rsize")
- && _media_has_attribute_key (remote_media, "rtcp-rsize");
+ if (prev_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE
+ && prev_dir != new_dir) {
+ GST_FIXME_OBJECT (webrtc, "implement transceiver direction changes");
+ return;
+ }
+
+ if (!bundled || bundle_idx == media_idx) {
+ new_rtcp_mux = _media_has_attribute_key (local_media, "rtcp-mux")
+ && _media_has_attribute_key (remote_media, "rtcp-mux");
+ new_rtcp_rsize = _media_has_attribute_key (local_media, "rtcp-rsize")
+ && _media_has_attribute_key (remote_media, "rtcp-rsize");
- {
- GObject *session;
- g_signal_emit_by_name (webrtc->rtpbin, "get-internal-session",
- media_idx, &session);
- if (session) {
- g_object_set (session, "rtcp-reduced-size", new_rtcp_rsize, NULL);
- g_object_unref (session);
+ {
+ GObject *session;
+ g_signal_emit_by_name (webrtc->rtpbin, "get-internal-session",
+ media_idx, &session);
+ if (session) {
+ g_object_set (session, "rtcp-reduced-size", new_rtcp_rsize, NULL);
+ g_object_unref (session);
+ }
}
- }
- }
- if (prev_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE
- && prev_dir != new_dir) {
- GST_FIXME_OBJECT (webrtc, "implement transceiver direction changes");
- return;
+ g_object_set (stream, "rtcp-mux", new_rtcp_mux, NULL);
+ }
}
- /* FIXME: bundle! */
- g_object_set (stream, "rtcp-mux", new_rtcp_mux, NULL);
-
if (new_dir != prev_dir) {
- TransportReceiveBin *receive;
-
GST_TRACE_OBJECT (webrtc, "transceiver direction change");
- /* FIXME: this may not always be true. e.g. bundle */
- g_assert (media_idx == stream->session_id);
-
if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY ||
new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
GstWebRTCBinPad *pad =
_connect_input_stream (webrtc, pad);
_add_pad (webrtc, pad);
}
- g_object_set (stream, "dtls-client",
- new_setup == GST_WEBRTC_DTLS_SETUP_ACTIVE, NULL);
}
if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY ||
new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
"creating new receive pad for transceiver %" GST_PTR_FORMAT, trans);
pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SRC, media_idx);
pad->trans = gst_object_ref (rtp_trans);
- _connect_output_stream (webrtc, pad);
+
+ if (!trans->stream) {
+ TransportStream *item;
+
+ item =
+ _get_or_create_transport_stream (webrtc,
+ bundled ? bundle_idx : media_idx, FALSE);
+ webrtc_transceiver_set_transport (trans, item);
+ }
+
+ if (!bundled)
+ _connect_output_stream (webrtc, trans->stream, media_idx);
+ else
+ *should_connect_bundle_stream = TRUE;
/* delay adding the pad until rtpbin creates the recv output pad
* to ghost to so queries/events travel through the pipeline correctly
* as soon as the pad is added */
_add_pad_to_list (webrtc, pad);
}
- g_object_set (stream, "dtls-client",
- new_setup == GST_WEBRTC_DTLS_SETUP_ACTIVE, NULL);
+
+ transport_receive_bin_set_receive_state (stream->receive_bin,
+ RECEIVE_STATE_PASS);
+ } else if (!bundled) {
+ transport_receive_bin_set_receive_state (stream->receive_bin,
+ RECEIVE_STATE_DROP);
}
- receive = TRANSPORT_RECEIVE_BIN (stream->receive_bin);
- if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY ||
- new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV)
- transport_receive_bin_set_receive_state (receive, RECEIVE_STATE_PASS);
- else
- transport_receive_bin_set_receive_state (receive, RECEIVE_STATE_DROP);
+ if (!bundled || bundle_idx == media_idx)
+ g_object_set (stream, "dtls-client",
+ new_setup == GST_WEBRTC_DTLS_SETUP_ACTIVE, NULL);
rtp_trans->mline = media_idx;
rtp_trans->current_direction = new_dir;
return TRUE;
}
+static void
+_connect_rtpfunnel (GstWebRTCBin * webrtc, guint session_id)
+{
+ gchar *pad_name;
+ GstPad *queue_srcpad;
+ GstPad *rtp_sink;
+ TransportStream *stream = _find_transport_for_session (webrtc, session_id);
+ GstElement *queue;
+
+ g_assert (stream);
+
+ if (webrtc->rtpfunnel)
+ goto done;
+
+ webrtc->rtpfunnel = gst_element_factory_make ("rtpfunnel", NULL);
+ gst_bin_add (GST_BIN (webrtc), webrtc->rtpfunnel);
+ gst_element_sync_state_with_parent (webrtc->rtpfunnel);
+
+ queue = gst_element_factory_make ("queue", NULL);
+ gst_bin_add (GST_BIN (webrtc), queue);
+ gst_element_sync_state_with_parent (queue);
+
+ gst_element_link (webrtc->rtpfunnel, queue);
+
+ queue_srcpad = gst_element_get_static_pad (queue, "src");
+
+ pad_name = g_strdup_printf ("send_rtp_sink_%d", session_id);
+ rtp_sink = gst_element_get_request_pad (webrtc->rtpbin, pad_name);
+ g_free (pad_name);
+ gst_pad_link (queue_srcpad, rtp_sink);
+ gst_object_unref (queue_srcpad);
+ gst_object_unref (rtp_sink);
+
+ pad_name = g_strdup_printf ("send_rtp_src_%d", session_id);
+ if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
+ GST_ELEMENT (stream->send_bin), "rtp_sink"))
+ g_warn_if_reached ();
+ g_free (pad_name);
+
+done:
+ return;
+}
+
static gboolean
_update_transceivers_from_sdp (GstWebRTCBin * webrtc, SDPSource source,
GstWebRTCSessionDescription * sdp)
{
int i;
+ gboolean ret = FALSE;
+ GStrv bundled = NULL;
+ guint bundle_idx = 0;
+ gboolean should_connect_bundle_stream = FALSE;
+ TransportStream *bundle_stream = NULL;
+
+ if (!_parse_bundle (webrtc, sdp->sdp, &bundled))
+ 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]);
+ goto done;
+ }
+
+ bundle_stream = _get_or_create_transport_stream (webrtc, bundle_idx,
+ _message_media_is_datachannel (sdp->sdp, bundle_idx));
+
+ g_array_set_size (bundle_stream->ptmap, 0);
+
+ _connect_rtpfunnel (webrtc, bundle_idx);
+ }
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;
+ guint transport_idx;
/* skip rejected media */
if (gst_sdp_media_get_port (media) == 0)
continue;
+ if (bundled)
+ transport_idx = bundle_idx;
+ else
+ transport_idx = i;
+
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);
- }
+ stream = _get_or_create_transport_stream (webrtc, transport_idx,
+ _message_media_is_datachannel (sdp->sdp, transport_idx));
+
+ 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;
+ goto done;
} else {
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);
+ trans, bundled, bundle_idx, &should_connect_bundle_stream);
} else {
trans = _find_transceiver (webrtc, NULL,
(FindTransceiverFunc) _find_compatible_unassociated_transceiver);
* 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)
+ 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);
+ trans, bundled, bundle_idx, &should_connect_bundle_stream);
}
} else if (_message_media_is_datachannel (sdp->sdp, i)) {
_update_data_channel_from_sdp_media (webrtc, sdp->sdp, i, stream);
}
}
- return TRUE;
+ if (should_connect_bundle_stream) {
+ g_assert (bundle_stream);
+ _connect_output_stream (webrtc, bundle_stream, bundle_idx);
+ }
+
+ ret = TRUE;
+
+done:
+ if (bundled)
+ g_strfreev (bundled);
+ return ret;
}
static void
{
GstWebRTCSignalingState new_signaling_state = webrtc->signaling_state;
GError *error = NULL;
+ GStrv bundled = NULL;
+ guint bundle_idx = 0;
+ guint i;
{
gchar *state = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
goto out;
}
+ if (!_parse_bundle (webrtc, sd->sdp->sdp, &bundled))
+ 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]);
+ goto out;
+ }
+ }
+
switch (sd->sdp->type) {
case GST_WEBRTC_SDP_TYPE_OFFER:{
if (sd->source == SDP_LOCAL) {
for (tmp = webrtc->priv->pending_sink_transceivers; tmp; tmp = tmp->next) {
GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (tmp->data);
+ const GstSDPMedia *media;
+
+ media = gst_sdp_message_get_media (sd->sdp->sdp, pad->mlineindex);
+ /* skip rejected media */
+ /* FIXME: arrange for an appropriate flow return */
+ if (gst_sdp_media_get_port (media) == 0)
+ continue;
_connect_input_stream (webrtc, pad);
gst_pad_remove_probe (GST_PAD (pad), pad->block_id);
}
}
- if (sd->source == SDP_LOCAL) {
- int i;
+ for (i = 0; i < gst_sdp_message_medias_len (sd->sdp->sdp); i++) {
+ gchar *ufrag, *pwd;
+ TransportStream *item;
- for (i = 0; i < gst_sdp_message_medias_len (sd->sdp->sdp); i++) {
- gchar *ufrag, *pwd;
- TransportStream *item;
+ item =
+ _get_or_create_transport_stream (webrtc, bundled ? bundle_idx : i,
+ _message_media_is_datachannel (sd->sdp->sdp, bundled ? bundle_idx : i));
- /* FIXME: bundle */
- item = _find_transport_for_session (webrtc, i);
- if (!item)
- item =
- _create_transport_stream (webrtc, i,
- _message_media_is_datachannel (sd->sdp->sdp, i));
+ if (sd->source == SDP_REMOTE) {
+ const GstSDPMedia *media = gst_sdp_message_get_media (sd->sdp->sdp, i);
+ guint j;
- _get_ice_credentials_from_sdp_media (sd->sdp->sdp, i, &ufrag, &pwd);
- gst_webrtc_ice_set_local_credentials (webrtc->priv->ice,
- item->stream, ufrag, pwd);
- g_free (ufrag);
- g_free (pwd);
- }
- }
+ for (j = 0; j < gst_sdp_media_attributes_len (media); j++) {
+ const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, j);
- if (sd->source == SDP_REMOTE) {
- int i;
+ if (g_strcmp0 (attr->key, "ssrc") == 0) {
+ GStrv split = g_strsplit (attr->value, " ", 0);
+ guint32 ssrc;
- for (i = 0; i < gst_sdp_message_medias_len (sd->sdp->sdp); i++) {
- gchar *ufrag, *pwd;
- TransportStream *item;
+ if (split[0] && sscanf (split[0], "%u", &ssrc) && split[1]
+ && g_str_has_prefix (split[1], "cname:")) {
+ SsrcMapItem ssrc_item;
- /* FIXME: bundle */
- item = _find_transport_for_session (webrtc, i);
- if (!item)
- item =
- _create_transport_stream (webrtc, i,
- _message_media_is_datachannel (sd->sdp->sdp, i));
+ ssrc_item.media_idx = i;
+ ssrc_item.ssrc = ssrc;
+ g_array_append_val (item->remote_ssrcmap, ssrc_item);
+ }
+ g_strfreev (split);
+ }
+ }
+ }
+
+ if (bundled && bundle_idx != i)
+ continue;
- _get_ice_credentials_from_sdp_media (sd->sdp->sdp, i, &ufrag, &pwd);
+ _get_ice_credentials_from_sdp_media (sd->sdp->sdp, i, &ufrag, &pwd);
+
+ if (sd->source == SDP_LOCAL)
+ gst_webrtc_ice_set_local_credentials (webrtc->priv->ice,
+ item->stream, ufrag, pwd);
+ else
gst_webrtc_ice_set_remote_credentials (webrtc->priv->ice,
item->stream, ufrag, pwd);
- g_free (ufrag);
- g_free (pwd);
- }
+ g_free (ufrag);
+ g_free (pwd);
}
- {
- int i;
- for (i = 0; i < webrtc->priv->ice_stream_map->len; i++) {
- IceStreamItem *item =
- &g_array_index (webrtc->priv->ice_stream_map, IceStreamItem, i);
+ for (i = 0; i < webrtc->priv->ice_stream_map->len; i++) {
+ IceStreamItem *item =
+ &g_array_index (webrtc->priv->ice_stream_map, IceStreamItem, i);
- gst_webrtc_ice_gather_candidates (webrtc->priv->ice, item->stream);
- }
+ gst_webrtc_ice_gather_candidates (webrtc->priv->ice, item->stream);
}
if (webrtc->current_local_description && webrtc->current_remote_description) {
}
out:
+ if (bundled)
+ g_strfreev (bundled);
+
PC_UNLOCK (webrtc);
gst_promise_reply (sd->promise, NULL);
PC_LOCK (webrtc);
{
IceCandidateItem *item = g_new0 (IceCandidateItem, 1);
- /* FIXME: bundle support */
item->mlineindex = session_id;
item->candidate = g_strdup (candidate);
WebRTCTransceiver *trans;
TransportStream *stream;
GstWebRTCBinPad *pad;
+ guint media_idx = 0;
+ guint i;
if (sscanf (new_pad_name, "recv_rtp_src_%u_%u_%u", &session_id, &ssrc,
&pt) != 3) {
if (!stream)
g_warn_if_reached ();
- /* FIXME: bundle! */
- rtp_trans = _find_transceiver_for_mline (webrtc, session_id);
+ media_idx = session_id;
+
+ for (i = 0; i < stream->remote_ssrcmap->len; i++) {
+ SsrcMapItem *item =
+ &g_array_index (stream->remote_ssrcmap, SsrcMapItem, i);
+ if (item->ssrc == ssrc) {
+ media_idx = item->media_idx;
+ break;
+ }
+ }
+
+ rtp_trans = _find_transceiver_for_mline (webrtc, media_idx);
if (!rtp_trans)
g_warn_if_reached ();
trans = WEBRTC_TRANSCEIVER (rtp_trans);
case PROP_TURN_SERVER:
g_object_set_property (G_OBJECT (webrtc->priv->ice), pspec->name, value);
break;
+ case PROP_BUNDLE_POLICY:
+ if (g_value_get_enum (value) == GST_WEBRTC_BUNDLE_POLICY_BALANCED) {
+ GST_ERROR_OBJECT (object, "Balanced bundle policy not implemented yet");
+ } else {
+ webrtc->bundle_policy = g_value_get_enum (value);
+ }
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
case PROP_TURN_SERVER:
g_object_get_property (G_OBJECT (webrtc->priv->ice), pspec->name, value);
break;
+ case PROP_BUNDLE_POLICY:
+ g_value_set_enum (value, webrtc->bundle_policy);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
GST_WEBRTC_ICE_GATHERING_STATE_NEW,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (gobject_class,
+ PROP_BUNDLE_POLICY,
+ g_param_spec_enum ("bundle-policy", "Bundle Policy",
+ "The policy to apply for bundling",
+ GST_TYPE_WEBRTC_BUNDLE_POLICY,
+ GST_WEBRTC_BUNDLE_POLICY_NONE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
/**
* GstWebRTCBin::create-offer:
* @object: the #GstWebRtcBin
GST_END_TEST;
+typedef struct
+{
+ guint num_media;
+ guint num_active_media;
+ const gchar **bundled;
+ const gchar **bundled_only;
+} BundleCheckData;
+
+static gboolean
+_parse_bundle (GstSDPMessage * sdp, GStrv * bundled)
+{
+ const gchar *group;
+ gboolean ret = FALSE;
+
+ group = gst_sdp_message_get_attribute_val (sdp, "group");
+
+ if (group && g_str_has_prefix (group, "BUNDLE ")) {
+ *bundled = g_strsplit (group + strlen ("BUNDLE "), " ", 0);
+
+ if (!(*bundled)[0]) {
+ GST_ERROR
+ ("Invalid format for BUNDLE group, expected at least one mid (%s)",
+ group);
+ goto done;
+ }
+ } else {
+ ret = TRUE;
+ goto done;
+ }
+
+ ret = TRUE;
+
+done:
+ return ret;
+}
+
+static gboolean
+_media_has_attribute_key (const GstSDPMedia * media, const gchar * key)
+{
+ int i;
+ for (i = 0; i < gst_sdp_media_attributes_len (media); i++) {
+ const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i);
+
+ if (g_strcmp0 (attr->key, key) == 0)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+_check_bundled_sdp_media (struct test_webrtc *t, GstElement * element,
+ GstWebRTCSessionDescription * sd, gpointer user_data)
+{
+ gchar **bundled = NULL;
+ BundleCheckData *data = (BundleCheckData *) user_data;
+ guint i;
+ guint active_media;
+
+ fail_unless_equals_int (gst_sdp_message_medias_len (sd->sdp),
+ data->num_media);
+
+ fail_unless (_parse_bundle (sd->sdp, &bundled));
+
+ if (!bundled) {
+ fail_unless_equals_int (g_strv_length ((GStrv) data->bundled), 0);
+ } else {
+ fail_unless_equals_int (g_strv_length (bundled),
+ g_strv_length ((GStrv) data->bundled));
+ }
+
+ for (i = 0; data->bundled[i]; i++) {
+ fail_unless (g_strv_contains ((const gchar **) bundled, data->bundled[i]));
+ }
+
+ active_media = 0;
+
+ for (i = 0; i < gst_sdp_message_medias_len (sd->sdp); i++) {
+ const GstSDPMedia *media = gst_sdp_message_get_media (sd->sdp, i);
+ const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid");
+
+ if (g_strv_contains ((const gchar **) data->bundled_only, mid))
+ fail_unless (_media_has_attribute_key (media, "bundle-only"));
+
+ if (gst_sdp_media_get_port (media) != 0)
+ active_media += 1;
+ }
+
+ fail_unless_equals_int (active_media, data->num_active_media);
+
+ if (bundled)
+ g_strfreev (bundled);
+}
+
+GST_START_TEST (test_bundle_audio_video_max_bundle_max_bundle)
+{
+ struct test_webrtc *t = create_audio_video_test ();
+ const gchar *bundle[] = { "audio0", "video1", NULL };
+ const gchar *offer_bundle_only[] = { "video1", NULL };
+ const gchar *answer_bundle_only[] = { NULL };
+ /* We set a max-bundle policy on the offering webrtcbin,
+ * this means that all the offered medias should be part
+ * of the group:BUNDLE attribute, and they should be marked
+ * as bundle-only
+ */
+ BundleCheckData offer_data = {
+ 2,
+ 1,
+ bundle,
+ offer_bundle_only,
+ };
+ /* We also set a max-bundle policy on the answering webrtcbin,
+ * this means that all the offered medias should be part
+ * of the group:BUNDLE attribute, but need not be marked
+ * as bundle-only.
+ */
+ BundleCheckData answer_data = {
+ 2,
+ 2,
+ bundle,
+ answer_bundle_only,
+ };
+ struct validate_sdp offer = { _check_bundled_sdp_media, &offer_data };
+ struct validate_sdp answer = { _check_bundled_sdp_media, &answer_data };
+
+ gst_util_set_object_arg (G_OBJECT (t->webrtc1), "bundle-policy",
+ "max-bundle");
+ gst_util_set_object_arg (G_OBJECT (t->webrtc2), "bundle-policy",
+ "max-bundle");
+
+ 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);
+
+ test_webrtc_create_offer (t, t->webrtc1);
+
+ test_webrtc_wait_for_answer_error_eos (t);
+ fail_unless_equals_int (STATE_ANSWER_CREATED, t->state);
+
+ test_webrtc_free (t);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_bundle_audio_video_max_compat_max_bundle)
+{
+ struct test_webrtc *t = create_audio_video_test ();
+ const gchar *bundle[] = { "audio0", "video1", NULL };
+ const gchar *bundle_only[] = { NULL };
+ /* We set a max-compat policy on the offering webrtcbin,
+ * this means that all the offered medias should be part
+ * of the group:BUNDLE attribute, and they should *not* be marked
+ * as bundle-only
+ */
+ BundleCheckData offer_data = {
+ 2,
+ 2,
+ bundle,
+ bundle_only,
+ };
+ /* We set a max-bundle policy on the answering webrtcbin,
+ * this means that all the offered medias should be part
+ * of the group:BUNDLE attribute, but need not be marked
+ * as bundle-only.
+ */
+ BundleCheckData answer_data = {
+ 2,
+ 2,
+ bundle,
+ bundle_only,
+ };
+ struct validate_sdp offer = { _check_bundled_sdp_media, &offer_data };
+ struct validate_sdp answer = { _check_bundled_sdp_media, &answer_data };
+
+ gst_util_set_object_arg (G_OBJECT (t->webrtc1), "bundle-policy",
+ "max-compat");
+ gst_util_set_object_arg (G_OBJECT (t->webrtc2), "bundle-policy",
+ "max-bundle");
+
+ 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);
+
+ test_webrtc_create_offer (t, t->webrtc1);
+
+ test_webrtc_wait_for_answer_error_eos (t);
+ fail_unless_equals_int (STATE_ANSWER_CREATED, t->state);
+
+ test_webrtc_free (t);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_bundle_audio_video_max_bundle_none)
+{
+ struct test_webrtc *t = create_audio_video_test ();
+ const gchar *offer_bundle[] = { "audio0", "video1", NULL };
+ const gchar *offer_bundle_only[] = { "video1", NULL };
+ const gchar *answer_bundle[] = { NULL };
+ const gchar *answer_bundle_only[] = { NULL };
+ /* We set a max-bundle policy on the offering webrtcbin,
+ * this means that all the offered medias should be part
+ * of the group:BUNDLE attribute, and they should be marked
+ * as bundle-only
+ */
+ BundleCheckData offer_data = {
+ 2,
+ 1,
+ offer_bundle,
+ offer_bundle_only,
+ };
+ /* We set a none policy on the answering webrtcbin,
+ * this means that the answer should contain no bundled
+ * medias, and as the bundle-policy of the offering webrtcbin
+ * is set to max-bundle, only one media should be active.
+ */
+ BundleCheckData answer_data = {
+ 2,
+ 1,
+ answer_bundle,
+ answer_bundle_only,
+ };
+ struct validate_sdp offer = { _check_bundled_sdp_media, &offer_data };
+ struct validate_sdp answer = { _check_bundled_sdp_media, &answer_data };
+
+ gst_util_set_object_arg (G_OBJECT (t->webrtc1), "bundle-policy",
+ "max-bundle");
+ gst_util_set_object_arg (G_OBJECT (t->webrtc2), "bundle-policy", "none");
+
+ 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);
+
+ test_webrtc_create_offer (t, t->webrtc1);
+
+ test_webrtc_wait_for_answer_error_eos (t);
+ fail_unless_equals_int (STATE_ANSWER_CREATED, t->state);
+
+ test_webrtc_free (t);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_bundle_audio_video_data)
+{
+ struct test_webrtc *t = create_audio_video_test ();
+ const gchar *bundle[] = { "audio0", "video1", "application2", NULL };
+ const gchar *offer_bundle_only[] = { "video1", "application2", NULL };
+ const gchar *answer_bundle_only[] = { NULL };
+ GObject *channel = NULL;
+ /* We set a max-bundle policy on the offering webrtcbin,
+ * this means that all the offered medias should be part
+ * of the group:BUNDLE attribute, and they should be marked
+ * as bundle-only
+ */
+ BundleCheckData offer_data = {
+ 3,
+ 1,
+ bundle,
+ offer_bundle_only,
+ };
+ /* We also set a max-bundle policy on the answering webrtcbin,
+ * this means that all the offered medias should be part
+ * of the group:BUNDLE attribute, but need not be marked
+ * as bundle-only.
+ */
+ BundleCheckData answer_data = {
+ 3,
+ 3,
+ bundle,
+ answer_bundle_only,
+ };
+ struct validate_sdp offer = { _check_bundled_sdp_media, &offer_data };
+ struct validate_sdp answer = { _check_bundled_sdp_media, &answer_data };
+
+ gst_util_set_object_arg (G_OBJECT (t->webrtc1), "bundle-policy",
+ "max-bundle");
+ gst_util_set_object_arg (G_OBJECT (t->webrtc2), "bundle-policy",
+ "max-bundle");
+
+ 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);
+
+ g_signal_emit_by_name (t->webrtc1, "create-data-channel", "label", NULL,
+ &channel);
+
+ test_webrtc_create_offer (t, t->webrtc1);
+
+ test_webrtc_wait_for_answer_error_eos (t);
+ fail_unless_equals_int (STATE_ANSWER_CREATED, t->state);
+
+ g_object_unref (channel);
+ test_webrtc_free (t);
+}
+
+GST_END_TEST;
+
static Suite *
webrtcbin_suite (void)
{
tcase_add_test (tc, test_add_recvonly_transceiver);
tcase_add_test (tc, test_recvonly_sendonly);
tcase_add_test (tc, test_payload_types);
+ tcase_add_test (tc, test_bundle_audio_video_max_bundle_max_bundle);
+ tcase_add_test (tc, test_bundle_audio_video_max_bundle_none);
+ tcase_add_test (tc, test_bundle_audio_video_max_compat_max_bundle);
if (sctpenc && sctpdec) {
tcase_add_test (tc, test_data_channel_create);
tcase_add_test (tc, test_data_channel_remote_notify);
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);
+ tcase_add_test (tc, test_bundle_audio_video_data);
} else {
GST_WARNING ("Some required elements were not found. "
"All datachannel are disabled. sctpenc %p, sctpdec %p", sctpenc,