mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-04-26 06:54:49 +00:00
webrtcbin: create and associate transceivers earlier in negotation
According to https://w3c.github.io/webrtc-pc/#set-the-session-description (steps in 4.6.10.), we should be creating and associating transceivers when setting session descriptions. Before this commit, webrtcbin deviated from the spec: 1. Transceivers from sink pads where created when the sink pad was requested, but not associated after setting local description, only when signaling is STABLE. 2. Transceivers from remote offers were not created after applying the the remote description, only when the answer is created, and were then only associated once signaling is STABLE. This commit makes webrtcbin follow the spec more closely with regards to timing of transceivers creation and association. A unit test is added, checking that the transceivers are created and associated after every session description is set. Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7156>
This commit is contained in:
parent
ef9875640e
commit
48ae40f477
4 changed files with 388 additions and 240 deletions
|
@ -748,6 +748,13 @@ transceiver_match_for_mid (GstWebRTCRTPTransceiver * trans, const gchar * mid)
|
||||||
return g_strcmp0 (trans->mid, mid) == 0;
|
return g_strcmp0 (trans->mid, mid) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
transceiver_match_for_pending_mid (GstWebRTCRTPTransceiver * trans,
|
||||||
|
const gchar * mid)
|
||||||
|
{
|
||||||
|
return g_strcmp0 (WEBRTC_TRANSCEIVER (trans)->pending_mid, mid) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
transceiver_match_for_mline (GstWebRTCRTPTransceiver * trans, guint * mline)
|
transceiver_match_for_mline (GstWebRTCRTPTransceiver * trans, guint * mline)
|
||||||
{
|
{
|
||||||
|
@ -786,6 +793,20 @@ _find_transceiver_for_mid (GstWebRTCBin * webrtc, const char *mid)
|
||||||
return trans;
|
return trans;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static GstWebRTCRTPTransceiver *
|
||||||
|
_find_transceiver_for_pending_mid (GstWebRTCBin * webrtc, const char *mid)
|
||||||
|
{
|
||||||
|
GstWebRTCRTPTransceiver *trans;
|
||||||
|
|
||||||
|
trans = _find_transceiver (webrtc, mid,
|
||||||
|
(FindTransceiverFunc) transceiver_match_for_pending_mid);
|
||||||
|
|
||||||
|
GST_TRACE_OBJECT (webrtc, "Found transceiver %" GST_PTR_FORMAT " for "
|
||||||
|
"pending mid %s", trans, mid);
|
||||||
|
|
||||||
|
return trans;
|
||||||
|
}
|
||||||
|
|
||||||
typedef gboolean (*FindTransportFunc) (TransportStream * p1,
|
typedef gboolean (*FindTransportFunc) (TransportStream * p1,
|
||||||
gconstpointer data);
|
gconstpointer data);
|
||||||
|
|
||||||
|
@ -4543,146 +4564,51 @@ _create_answer_task (GstWebRTCBin * webrtc, const GstStructure * options,
|
||||||
|
|
||||||
_remove_optional_offer_fields (offer_caps);
|
_remove_optional_offer_fields (offer_caps);
|
||||||
|
|
||||||
if (last_answer && i < gst_sdp_message_medias_len (last_answer)
|
rtp_trans = _find_transceiver_for_mid (webrtc, mid);
|
||||||
&& (rtp_trans = _find_transceiver_for_mid (webrtc, mid))) {
|
if (!rtp_trans) {
|
||||||
|
g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_INVALID_STATE,
|
||||||
|
"Transceiver for media with mid %s not found", mid);
|
||||||
|
gst_caps_unref (offer_caps);
|
||||||
|
goto rejected;
|
||||||
|
}
|
||||||
|
GstCaps *current_caps =
|
||||||
|
_find_codec_preferences (webrtc, rtp_trans, i, error);
|
||||||
|
if (*error) {
|
||||||
|
gst_caps_unref (offer_caps);
|
||||||
|
goto rejected;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last_answer && i < gst_sdp_message_medias_len (last_answer)) {
|
||||||
const GstSDPMedia *last_media =
|
const GstSDPMedia *last_media =
|
||||||
gst_sdp_message_get_media (last_answer, i);
|
gst_sdp_message_get_media (last_answer, i);
|
||||||
const gchar *last_mid =
|
const gchar *last_mid =
|
||||||
gst_sdp_media_get_attribute_val (last_media, "mid");
|
gst_sdp_media_get_attribute_val (last_media, "mid");
|
||||||
GstCaps *current_caps;
|
|
||||||
|
|
||||||
/* FIXME: assumes no shenanigans with recycling transceivers */
|
/* FIXME: assumes no shenanigans with recycling transceivers */
|
||||||
g_assert (g_strcmp0 (mid, last_mid) == 0);
|
g_assert (g_strcmp0 (mid, last_mid) == 0);
|
||||||
|
|
||||||
current_caps = _find_codec_preferences (webrtc, rtp_trans, i, error);
|
|
||||||
if (*error) {
|
|
||||||
gst_caps_unref (offer_caps);
|
|
||||||
goto rejected;
|
|
||||||
}
|
|
||||||
if (!current_caps)
|
if (!current_caps)
|
||||||
current_caps = _rtp_caps_from_media (last_media);
|
current_caps = _rtp_caps_from_media (last_media);
|
||||||
|
}
|
||||||
|
|
||||||
if (current_caps) {
|
if (current_caps) {
|
||||||
answer_caps = gst_caps_intersect (offer_caps, current_caps);
|
answer_caps = gst_caps_intersect (offer_caps, current_caps);
|
||||||
if (gst_caps_is_empty (answer_caps)) {
|
if (gst_caps_is_empty (answer_caps)) {
|
||||||
GST_WARNING_OBJECT (webrtc, "Caps from offer for m-line %d (%"
|
GST_WARNING_OBJECT (webrtc, "Caps from offer for m-line %d (%"
|
||||||
GST_PTR_FORMAT ") don't intersect with caps from codec"
|
GST_PTR_FORMAT ") don't intersect with caps from codec"
|
||||||
" preferences and transceiver %" GST_PTR_FORMAT, i, offer_caps,
|
" preferences and transceiver %" GST_PTR_FORMAT, i, offer_caps,
|
||||||
current_caps);
|
current_caps);
|
||||||
gst_caps_unref (current_caps);
|
|
||||||
gst_caps_unref (answer_caps);
|
|
||||||
gst_caps_unref (offer_caps);
|
|
||||||
goto rejected;
|
|
||||||
}
|
|
||||||
gst_caps_unref (current_caps);
|
gst_caps_unref (current_caps);
|
||||||
}
|
gst_caps_unref (answer_caps);
|
||||||
|
|
||||||
/* XXX: In theory we're meant to use the sendrecv formats for the
|
|
||||||
* inactive direction however we don't know what that may be and would
|
|
||||||
* require asking outside what it expects to possibly send later */
|
|
||||||
|
|
||||||
GST_LOG_OBJECT (webrtc, "Found existing previously negotiated "
|
|
||||||
"transceiver %" GST_PTR_FORMAT " from mid %s for mline %u "
|
|
||||||
"using caps %" GST_PTR_FORMAT, rtp_trans, mid, i, answer_caps);
|
|
||||||
} else {
|
|
||||||
for (j = 0; j < webrtc->priv->transceivers->len; j++) {
|
|
||||||
GstCaps *trans_caps;
|
|
||||||
|
|
||||||
rtp_trans = g_ptr_array_index (webrtc->priv->transceivers, j);
|
|
||||||
|
|
||||||
if (g_list_find (seen_transceivers, rtp_trans)) {
|
|
||||||
/* Don't double allocate a transceiver to multiple mlines */
|
|
||||||
rtp_trans = NULL;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
trans_caps = _find_codec_preferences (webrtc, rtp_trans, j, error);
|
|
||||||
if (*error) {
|
|
||||||
gst_caps_unref (offer_caps);
|
|
||||||
goto rejected;
|
|
||||||
}
|
|
||||||
|
|
||||||
GST_LOG_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);
|
|
||||||
gst_caps_unref (trans_caps);
|
|
||||||
if (answer_caps) {
|
|
||||||
if (!gst_caps_is_empty (answer_caps)) {
|
|
||||||
GST_LOG_OBJECT (webrtc,
|
|
||||||
"found compatible transceiver %" GST_PTR_FORMAT
|
|
||||||
" for offer media %u", rtp_trans, i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
gst_caps_unref (answer_caps);
|
|
||||||
answer_caps = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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 intersection with the transceivers codec preferences caps */
|
|
||||||
answer_dir = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY;
|
|
||||||
GST_WARNING_OBJECT (webrtc, "did not find compatible transceiver for "
|
|
||||||
"offer caps %" GST_PTR_FORMAT ", will only receive", offer_caps);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!rtp_trans) {
|
|
||||||
GstCaps *trans_caps;
|
|
||||||
GstWebRTCKind kind = GST_WEBRTC_KIND_UNKNOWN;
|
|
||||||
|
|
||||||
if (g_strcmp0 (gst_sdp_media_get_media (offer_media), "audio") == 0)
|
|
||||||
kind = GST_WEBRTC_KIND_AUDIO;
|
|
||||||
else if (g_strcmp0 (gst_sdp_media_get_media (offer_media),
|
|
||||||
"video") == 0)
|
|
||||||
kind = GST_WEBRTC_KIND_VIDEO;
|
|
||||||
else
|
|
||||||
GST_LOG_OBJECT (webrtc, "Unknown media kind %s",
|
|
||||||
GST_STR_NULL (gst_sdp_media_get_media (offer_media)));
|
|
||||||
|
|
||||||
trans = _create_webrtc_transceiver (webrtc, answer_dir, i, kind, NULL);
|
|
||||||
rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
|
|
||||||
|
|
||||||
PC_UNLOCK (webrtc);
|
|
||||||
g_signal_emit (webrtc,
|
|
||||||
gst_webrtc_bin_signals[ON_NEW_TRANSCEIVER_SIGNAL], 0, rtp_trans);
|
|
||||||
PC_LOCK (webrtc);
|
|
||||||
|
|
||||||
GST_LOG_OBJECT (webrtc, "Created new transceiver %" GST_PTR_FORMAT
|
|
||||||
" for mline %u with media kind %d", trans, i, kind);
|
|
||||||
|
|
||||||
trans_caps = _find_codec_preferences (webrtc, rtp_trans, i, error);
|
|
||||||
if (*error) {
|
|
||||||
gst_caps_unref (offer_caps);
|
gst_caps_unref (offer_caps);
|
||||||
goto rejected;
|
goto rejected;
|
||||||
}
|
}
|
||||||
|
gst_caps_unref (current_caps);
|
||||||
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);
|
|
||||||
gst_clear_caps (&trans_caps);
|
|
||||||
} else {
|
|
||||||
answer_caps = gst_caps_ref (offer_caps);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
trans = WEBRTC_TRANSCEIVER (rtp_trans);
|
answer_caps = gst_caps_ref (offer_caps);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
answer_dir = rtp_trans->direction;
|
||||||
|
trans = WEBRTC_TRANSCEIVER (rtp_trans);
|
||||||
|
|
||||||
seen_transceivers = g_list_prepend (seen_transceivers, rtp_trans);
|
seen_transceivers = g_list_prepend (seen_transceivers, rtp_trans);
|
||||||
|
|
||||||
if (gst_caps_is_empty (answer_caps)) {
|
if (gst_caps_is_empty (answer_caps)) {
|
||||||
|
@ -5739,6 +5665,7 @@ _update_transceiver_from_sdp_media (GstWebRTCBin * webrtc,
|
||||||
if (g_strcmp0 (attr->key, "mid") == 0) {
|
if (g_strcmp0 (attr->key, "mid") == 0) {
|
||||||
g_free (rtp_trans->mid);
|
g_free (rtp_trans->mid);
|
||||||
rtp_trans->mid = g_strdup (attr->value);
|
rtp_trans->mid = g_strdup (attr->value);
|
||||||
|
g_object_notify (G_OBJECT (rtp_trans), "mid");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6079,24 +6006,6 @@ _update_data_channel_from_sdp_media (GstWebRTCBin * webrtc,
|
||||||
transport_receive_bin_set_receive_state (receive, RECEIVE_STATE_PASS);
|
transport_receive_bin_set_receive_state (receive, RECEIVE_STATE_PASS);
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
|
||||||
_find_compatible_unassociated_transceiver (GstWebRTCRTPTransceiver * p1,
|
|
||||||
gconstpointer data)
|
|
||||||
{
|
|
||||||
GstWebRTCKind kind = GPOINTER_TO_INT (data);
|
|
||||||
|
|
||||||
if (p1->mid)
|
|
||||||
return FALSE;
|
|
||||||
if (p1->mline != -1)
|
|
||||||
return FALSE;
|
|
||||||
if (p1->stopped)
|
|
||||||
return FALSE;
|
|
||||||
if (p1->kind != GST_WEBRTC_KIND_UNKNOWN && p1->kind != kind)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
_connect_rtpfunnel (GstWebRTCBin * webrtc, guint session_id)
|
_connect_rtpfunnel (GstWebRTCBin * webrtc, guint session_id)
|
||||||
{
|
{
|
||||||
|
@ -6179,7 +6088,6 @@ _update_transceivers_from_sdp (GstWebRTCBin * webrtc, SDPSource source,
|
||||||
for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) {
|
for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) {
|
||||||
const GstSDPMedia *media = gst_sdp_message_get_media (sdp->sdp, i);
|
const GstSDPMedia *media = gst_sdp_message_get_media (sdp->sdp, i);
|
||||||
TransportStream *stream;
|
TransportStream *stream;
|
||||||
GstWebRTCRTPTransceiver *trans;
|
|
||||||
guint transport_idx;
|
guint transport_idx;
|
||||||
|
|
||||||
/* skip rejected media */
|
/* skip rejected media */
|
||||||
|
@ -6191,8 +6099,6 @@ _update_transceivers_from_sdp (GstWebRTCBin * webrtc, SDPSource source,
|
||||||
else
|
else
|
||||||
transport_idx = i;
|
transport_idx = i;
|
||||||
|
|
||||||
trans = _find_transceiver_for_sdp_media (webrtc, sdp->sdp, i);
|
|
||||||
|
|
||||||
stream = _get_or_create_transport_stream (webrtc, transport_idx,
|
stream = _get_or_create_transport_stream (webrtc, transport_idx,
|
||||||
_message_media_is_datachannel (sdp->sdp, transport_idx));
|
_message_media_is_datachannel (sdp->sdp, transport_idx));
|
||||||
if (!bundled) {
|
if (!bundled) {
|
||||||
|
@ -6203,60 +6109,28 @@ _update_transceivers_from_sdp (GstWebRTCBin * webrtc, SDPSource source,
|
||||||
ensure_rtx_hdr_ext (stream);
|
ensure_rtx_hdr_ext (stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trans)
|
if (g_strcmp0 (gst_sdp_media_get_media (media), "audio") == 0 ||
|
||||||
webrtc_transceiver_set_transport ((WebRTCTransceiver *) trans, stream);
|
g_strcmp0 (gst_sdp_media_get_media (media), "video") == 0) {
|
||||||
|
GstWebRTCRTPTransceiver *trans;
|
||||||
|
|
||||||
if (source == SDP_LOCAL && sdp->type == GST_WEBRTC_SDP_TYPE_OFFER && !trans) {
|
trans = _find_transceiver_for_sdp_media (webrtc, sdp->sdp, i);
|
||||||
g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
|
if (!trans) {
|
||||||
"State mismatch. Could not find local transceiver by mline %u", i);
|
g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_INVALID_STATE,
|
||||||
goto done;
|
"Transceiver for mline %d not found", i);
|
||||||
} else {
|
goto done;
|
||||||
if (g_strcmp0 (gst_sdp_media_get_media (media), "audio") == 0 ||
|
|
||||||
g_strcmp0 (gst_sdp_media_get_media (media), "video") == 0) {
|
|
||||||
GstWebRTCKind kind = GST_WEBRTC_KIND_UNKNOWN;
|
|
||||||
|
|
||||||
/* No existing transceiver, find an unused one */
|
|
||||||
if (!trans) {
|
|
||||||
if (g_strcmp0 (gst_sdp_media_get_media (media), "audio") == 0)
|
|
||||||
kind = GST_WEBRTC_KIND_AUDIO;
|
|
||||||
else if (g_strcmp0 (gst_sdp_media_get_media (media), "video") == 0)
|
|
||||||
kind = GST_WEBRTC_KIND_VIDEO;
|
|
||||||
else
|
|
||||||
GST_LOG_OBJECT (webrtc, "Unknown media kind %s",
|
|
||||||
GST_STR_NULL (gst_sdp_media_get_media (media)));
|
|
||||||
|
|
||||||
trans = _find_transceiver (webrtc, GINT_TO_POINTER (kind),
|
|
||||||
(FindTransceiverFunc) _find_compatible_unassociated_transceiver);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Still no transceiver? Create one */
|
|
||||||
/* XXX: default to the advertised direction in the sdp for new
|
|
||||||
* transceivers. 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) {
|
|
||||||
WebRTCTransceiver *t = _create_webrtc_transceiver (webrtc,
|
|
||||||
_get_direction_from_media (media), i, kind, NULL);
|
|
||||||
webrtc_transceiver_set_transport (t, stream);
|
|
||||||
trans = GST_WEBRTC_RTP_TRANSCEIVER (t);
|
|
||||||
PC_UNLOCK (webrtc);
|
|
||||||
g_signal_emit (webrtc,
|
|
||||||
gst_webrtc_bin_signals[ON_NEW_TRANSCEIVER_SIGNAL], 0, trans);
|
|
||||||
PC_LOCK (webrtc);
|
|
||||||
}
|
|
||||||
|
|
||||||
_update_transceiver_from_sdp_media (webrtc, sdp->sdp, i, stream,
|
|
||||||
trans, bundled, bundle_idx, error);
|
|
||||||
if (error && *error)
|
|
||||||
goto done;
|
|
||||||
} else if (_message_media_is_datachannel (sdp->sdp, i)) {
|
|
||||||
_update_data_channel_from_sdp_media (webrtc, sdp->sdp, i, stream,
|
|
||||||
error);
|
|
||||||
if (error && *error)
|
|
||||||
goto done;
|
|
||||||
} else {
|
|
||||||
GST_ERROR_OBJECT (webrtc, "Unknown media type in SDP at index %u", i);
|
|
||||||
}
|
}
|
||||||
|
webrtc_transceiver_set_transport (WEBRTC_TRANSCEIVER (trans), stream);
|
||||||
|
|
||||||
|
_update_transceiver_from_sdp_media (webrtc, sdp->sdp, i, stream,
|
||||||
|
trans, bundled, bundle_idx, error);
|
||||||
|
if (error && *error)
|
||||||
|
goto done;
|
||||||
|
} else if (_message_media_is_datachannel (sdp->sdp, i)) {
|
||||||
|
_update_data_channel_from_sdp_media (webrtc, sdp->sdp, i, stream, error);
|
||||||
|
if (error && *error)
|
||||||
|
goto done;
|
||||||
|
} else {
|
||||||
|
GST_ERROR_OBJECT (webrtc, "Unknown media type in SDP at index %u", i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6406,6 +6280,210 @@ get_last_generated_description (GstWebRTCBin * webrtc, SDPSource source,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* https://w3c.github.io/webrtc-pc/#set-description (steps in 4.6.10.) */
|
||||||
|
static gboolean
|
||||||
|
_create_and_associate_transceivers_from_sdp (GstWebRTCBin * webrtc,
|
||||||
|
struct set_description *sd, GError ** error)
|
||||||
|
{
|
||||||
|
gboolean ret = FALSE;
|
||||||
|
GStrv bundled = NULL;
|
||||||
|
guint bundle_idx = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (sd->sdp->type == GST_WEBRTC_SDP_TYPE_ROLLBACK) {
|
||||||
|
/* FIXME:
|
||||||
|
* If the mid value of an RTCRtpTransceiver was set to a non-null value
|
||||||
|
* by the RTCSessionDescription that is being rolled back, set the mid
|
||||||
|
* value of that transceiver to null, as described by [JSEP]
|
||||||
|
* (section 4.1.7.2.).
|
||||||
|
* If an RTCRtpTransceiver was created by applying the
|
||||||
|
* RTCSessionDescription that is being rolled back, and a track has not
|
||||||
|
* been attached to it via addTrack, remove that transceiver from
|
||||||
|
* connection's set of transceivers, as described by [JSEP]
|
||||||
|
* (section 4.1.7.2.).
|
||||||
|
* Restore the value of connection's [[ sctpTransport]] internal slot
|
||||||
|
* to its value at the last stable signaling state.
|
||||||
|
*/
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FIXME: With some peers, it's possible we could have
|
||||||
|
* multiple bundles to deal with, although I've never seen one yet */
|
||||||
|
if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE)
|
||||||
|
if (!_parse_bundle (sd->sdp->sdp, &bundled, error))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (bundled) {
|
||||||
|
if (!_get_bundle_index (sd->sdp->sdp, bundled, &bundle_idx)) {
|
||||||
|
g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
|
||||||
|
"Bundle tag is %s but no media found matching", bundled[0]);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < gst_sdp_message_medias_len (sd->sdp->sdp); i++) {
|
||||||
|
GstWebRTCRTPTransceiver *trans;
|
||||||
|
WebRTCTransceiver *wtrans;
|
||||||
|
const GstSDPMedia *media;
|
||||||
|
const gchar *mid;
|
||||||
|
guint transport_idx;
|
||||||
|
TransportStream *stream;
|
||||||
|
|
||||||
|
if (_message_media_is_datachannel (sd->sdp->sdp, i))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
media = gst_sdp_message_get_media (sd->sdp->sdp, i);
|
||||||
|
mid = gst_sdp_media_get_attribute_val (media, "mid");
|
||||||
|
|
||||||
|
/* XXX: not strictly required but a lot of functionality requires a mid */
|
||||||
|
if (!mid) {
|
||||||
|
g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
|
||||||
|
"Missing mid attribute in media");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bundled)
|
||||||
|
transport_idx = bundle_idx;
|
||||||
|
else
|
||||||
|
transport_idx = i;
|
||||||
|
|
||||||
|
trans = _find_transceiver_for_mid (webrtc, mid);
|
||||||
|
|
||||||
|
if (sd->source == SDP_LOCAL) {
|
||||||
|
/* If the media description was not yet associated with an RTCRtpTransceiver object then run the following steps: */
|
||||||
|
if (!trans) {
|
||||||
|
/* Let transceiver be the RTCRtpTransceiver used to create the media description. */
|
||||||
|
trans = _find_transceiver_for_pending_mid (webrtc, mid);
|
||||||
|
if (!trans) {
|
||||||
|
g_set_error (error, GST_WEBRTC_ERROR,
|
||||||
|
GST_WEBRTC_ERROR_INVALID_STATE,
|
||||||
|
"Transceiver used to created media with mid %s not found", mid);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
wtrans = WEBRTC_TRANSCEIVER (trans);
|
||||||
|
if (wtrans->mline_locked && trans->mline != i) {
|
||||||
|
g_set_error (error, GST_WEBRTC_ERROR,
|
||||||
|
GST_WEBRTC_ERROR_INTERNAL_FAILURE,
|
||||||
|
"Transceiver <%s> with mid %s has mline %d from session description "
|
||||||
|
"but transceiver has locked mline %u",
|
||||||
|
GST_OBJECT_NAME (trans), GST_STR_NULL (trans->mid), i,
|
||||||
|
trans->mline);
|
||||||
|
}
|
||||||
|
trans->mline = i;
|
||||||
|
/* Set transceiver.[[Mid]] to transceiver.[[JsepMid]] */
|
||||||
|
g_free (trans->mid);
|
||||||
|
trans->mid = g_strdup (mid);
|
||||||
|
g_object_notify (G_OBJECT (trans), "mid");
|
||||||
|
/* If transceiver.[[Stopped]] is true, abort these sub steps */
|
||||||
|
if (trans->stopped)
|
||||||
|
continue;
|
||||||
|
/* If the media description is indicated as using an existing media transport according to [RFC8843], let
|
||||||
|
* transport be the RTCDtlsTransport object representing the RTP/RTCP component of that transport.
|
||||||
|
* Otherwise, let transport be a newly created RTCDtlsTransport object with a new underlying RTCIceTransport.
|
||||||
|
*/
|
||||||
|
stream = _get_or_create_transport_stream (webrtc, transport_idx, FALSE);
|
||||||
|
webrtc_transceiver_set_transport (wtrans, stream);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!trans) {
|
||||||
|
/* RFC9429: If the "m=" section is "sendrecv" or "recvonly", and there are RtpTransceivers of the same type
|
||||||
|
* that were added to the PeerConnection by addTrack and are not associated with any "m=" section
|
||||||
|
* and are not stopped, find the first (according to the canonical order described in Section 5.2.1)
|
||||||
|
* such RtpTransceiver. */
|
||||||
|
GstWebRTCRTPTransceiverDirection direction =
|
||||||
|
_get_direction_from_media (media);
|
||||||
|
if (direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV
|
||||||
|
|| direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY) {
|
||||||
|
int j;
|
||||||
|
for (j = 0; j < webrtc->priv->transceivers->len; ++j) {
|
||||||
|
trans = g_ptr_array_index (webrtc->priv->transceivers, j);
|
||||||
|
if (trans->mid || trans->stopped) {
|
||||||
|
trans = NULL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FIXME: Here we shouldn't in theory need to match caps, as the spec says only about
|
||||||
|
* "RtpTransceivers of the same type". However, transceivers created by requesting sink
|
||||||
|
* pads (aka addTrack) may still have unknown type at this point. We may be missing updating
|
||||||
|
* the transceiver type early enough during caps negotation.
|
||||||
|
*/
|
||||||
|
GstCaps *trans_caps =
|
||||||
|
_find_codec_preferences (webrtc, trans, i, error);
|
||||||
|
if (error && *error)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (trans_caps) {
|
||||||
|
GstCaps *offer_caps = _rtp_caps_from_media (media);
|
||||||
|
GstCaps *caps = gst_caps_intersect (offer_caps, trans_caps);
|
||||||
|
gst_caps_unref (offer_caps);
|
||||||
|
gst_caps_unref (trans_caps);
|
||||||
|
if (caps) {
|
||||||
|
if (!gst_caps_is_empty (caps)) {
|
||||||
|
GST_LOG_OBJECT (webrtc,
|
||||||
|
"found compatible transceiver %" GST_PTR_FORMAT
|
||||||
|
" for offer media %u", trans, i);
|
||||||
|
gst_caps_unref (caps);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
gst_caps_unref (caps);
|
||||||
|
caps = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trans = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If no RtpTransceiver was found in the previous step, create one with a "recvonly" direction. */
|
||||||
|
if (!trans) {
|
||||||
|
wtrans = _create_webrtc_transceiver (webrtc,
|
||||||
|
GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY, i,
|
||||||
|
_get_kind_from_media (media), NULL);
|
||||||
|
trans = GST_WEBRTC_RTP_TRANSCEIVER (wtrans);
|
||||||
|
|
||||||
|
PC_UNLOCK (webrtc);
|
||||||
|
g_signal_emit (webrtc,
|
||||||
|
gst_webrtc_bin_signals[ON_NEW_TRANSCEIVER_SIGNAL], 0, trans);
|
||||||
|
PC_LOCK (webrtc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Associate the found or created RtpTransceiver with the "m=" section by setting the value of
|
||||||
|
* the RtpTransceiver's mid property to the MID of the "m=" section, and establish a mapping
|
||||||
|
* between the transceiver and the index of the "m=" section. */
|
||||||
|
wtrans = WEBRTC_TRANSCEIVER (trans);
|
||||||
|
if (wtrans->mline_locked && trans->mline != i) {
|
||||||
|
g_set_error (error, GST_WEBRTC_ERROR,
|
||||||
|
GST_WEBRTC_ERROR_INTERNAL_FAILURE,
|
||||||
|
"Transceiver <%s> with mid %s has mline %d from session description "
|
||||||
|
"but transceiver has locked mline %u",
|
||||||
|
GST_OBJECT_NAME (trans), GST_STR_NULL (trans->mid), i,
|
||||||
|
trans->mline);
|
||||||
|
}
|
||||||
|
trans->mline = i;
|
||||||
|
g_free (trans->mid);
|
||||||
|
trans->mid = g_strdup (mid);
|
||||||
|
g_object_notify (G_OBJECT (trans), "mid");
|
||||||
|
|
||||||
|
/* If description is of type "answer" or "pranswer", then run the following steps: */
|
||||||
|
if (sd->sdp->type == GST_WEBRTC_SDP_TYPE_ANSWER
|
||||||
|
|| sd->sdp->type == GST_WEBRTC_SDP_TYPE_PRANSWER) {
|
||||||
|
/* Set transceiver.[[CurrentDirection]] to direction. */
|
||||||
|
trans->current_direction = _get_direction_from_media (media);
|
||||||
|
}
|
||||||
|
/* Let transport be the RTCDtlsTransport object representing the RTP/RTCP component of the media transport
|
||||||
|
* used by transceiver's associated media description, according to [RFC8843]. */
|
||||||
|
if (!wtrans->stream) {
|
||||||
|
stream = _get_or_create_transport_stream (webrtc, transport_idx, FALSE);
|
||||||
|
webrtc_transceiver_set_transport (wtrans, stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = TRUE;
|
||||||
|
out:
|
||||||
|
g_strfreev (bundled);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/* http://w3c.github.io/webrtc-pc/#set-description */
|
/* http://w3c.github.io/webrtc-pc/#set-description */
|
||||||
static GstStructure *
|
static GstStructure *
|
||||||
|
@ -6568,21 +6646,8 @@ _set_description_task (GstWebRTCBin * webrtc, struct set_description *sd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sd->sdp->type == GST_WEBRTC_SDP_TYPE_ROLLBACK) {
|
if (!_create_and_associate_transceivers_from_sdp (webrtc, sd, &error))
|
||||||
/* FIXME:
|
goto out;
|
||||||
* If the mid value of an RTCRtpTransceiver was set to a non-null value
|
|
||||||
* by the RTCSessionDescription that is being rolled back, set the mid
|
|
||||||
* value of that transceiver to null, as described by [JSEP]
|
|
||||||
* (section 4.1.7.2.).
|
|
||||||
* If an RTCRtpTransceiver was created by applying the
|
|
||||||
* RTCSessionDescription that is being rolled back, and a track has not
|
|
||||||
* been attached to it via addTrack, remove that transceiver from
|
|
||||||
* connection's set of transceivers, as described by [JSEP]
|
|
||||||
* (section 4.1.7.2.).
|
|
||||||
* Restore the value of connection's [[ sctpTransport]] internal slot
|
|
||||||
* to its value at the last stable signaling state.
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
if (webrtc->signaling_state != new_signaling_state) {
|
if (webrtc->signaling_state != new_signaling_state) {
|
||||||
webrtc->signaling_state = new_signaling_state;
|
webrtc->signaling_state = new_signaling_state;
|
||||||
|
@ -6642,6 +6707,12 @@ _set_description_task (GstWebRTCBin * webrtc, struct set_description *sd)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!pad->trans->mid) {
|
||||||
|
GST_DEBUG_OBJECT (pad, "transceiver not associated. Skipping");
|
||||||
|
tmp = tmp->next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
media = gst_sdp_message_get_media (sd->sdp->sdp, pad->trans->mline);
|
media = gst_sdp_message_get_media (sd->sdp->sdp, pad->trans->mline);
|
||||||
/* skip rejected media */
|
/* skip rejected media */
|
||||||
if (gst_sdp_media_get_port (media) == 0) {
|
if (gst_sdp_media_get_port (media) == 0) {
|
||||||
|
|
|
@ -399,6 +399,17 @@ _get_direction_from_media (const GstSDPMedia * media)
|
||||||
return new_dir;
|
return new_dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GstWebRTCKind
|
||||||
|
_get_kind_from_media (const GstSDPMedia * media)
|
||||||
|
{
|
||||||
|
GstWebRTCKind kind = GST_WEBRTC_KIND_UNKNOWN;
|
||||||
|
if (!g_strcmp0 (gst_sdp_media_get_media (media), "audio"))
|
||||||
|
kind = GST_WEBRTC_KIND_AUDIO;
|
||||||
|
else if (!g_strcmp0 (gst_sdp_media_get_media (media), "video"))
|
||||||
|
kind = GST_WEBRTC_KIND_VIDEO;
|
||||||
|
return kind;
|
||||||
|
}
|
||||||
|
|
||||||
#define DIR(val) GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_ ## val
|
#define DIR(val) GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_ ## val
|
||||||
GstWebRTCRTPTransceiverDirection
|
GstWebRTCRTPTransceiverDirection
|
||||||
_intersect_answer_directions (GstWebRTCRTPTransceiverDirection offer,
|
_intersect_answer_directions (GstWebRTCRTPTransceiverDirection offer,
|
||||||
|
|
|
@ -46,6 +46,8 @@ gboolean validate_sdp (Gst
|
||||||
G_GNUC_INTERNAL
|
G_GNUC_INTERNAL
|
||||||
GstWebRTCRTPTransceiverDirection _get_direction_from_media (const GstSDPMedia * media);
|
GstWebRTCRTPTransceiverDirection _get_direction_from_media (const GstSDPMedia * media);
|
||||||
G_GNUC_INTERNAL
|
G_GNUC_INTERNAL
|
||||||
|
GstWebRTCKind _get_kind_from_media (const GstSDPMedia * media);
|
||||||
|
G_GNUC_INTERNAL
|
||||||
GstWebRTCRTPTransceiverDirection _intersect_answer_directions (GstWebRTCRTPTransceiverDirection offer,
|
GstWebRTCRTPTransceiverDirection _intersect_answer_directions (GstWebRTCRTPTransceiverDirection offer,
|
||||||
GstWebRTCRTPTransceiverDirection answer);
|
GstWebRTCRTPTransceiverDirection answer);
|
||||||
G_GNUC_INTERNAL
|
G_GNUC_INTERNAL
|
||||||
|
|
|
@ -50,9 +50,11 @@ typedef enum
|
||||||
STATE_NEW = 1,
|
STATE_NEW = 1,
|
||||||
STATE_NEGOTIATION_NEEDED,
|
STATE_NEGOTIATION_NEEDED,
|
||||||
STATE_OFFER_CREATED,
|
STATE_OFFER_CREATED,
|
||||||
STATE_OFFER_SET,
|
STATE_LOCAL_OFFER_SET,
|
||||||
|
STATE_REMOTE_OFFER_SET,
|
||||||
STATE_ANSWER_CREATED,
|
STATE_ANSWER_CREATED,
|
||||||
STATE_ANSWER_SET,
|
STATE_LOCAL_ANSWER_SET,
|
||||||
|
STATE_REMOTE_ANSWER_SET,
|
||||||
STATE_EOS,
|
STATE_EOS,
|
||||||
STATE_ERROR,
|
STATE_ERROR,
|
||||||
STATE_CUSTOM,
|
STATE_CUSTOM,
|
||||||
|
@ -100,7 +102,6 @@ struct test_webrtc
|
||||||
GstPromise * promise,
|
GstPromise * promise,
|
||||||
gpointer user_data);
|
gpointer user_data);
|
||||||
GstWebRTCSessionDescription *offer_desc;
|
GstWebRTCSessionDescription *offer_desc;
|
||||||
guint offer_set_count;
|
|
||||||
gpointer offer_data;
|
gpointer offer_data;
|
||||||
GDestroyNotify offer_notify;
|
GDestroyNotify offer_notify;
|
||||||
void (*on_offer_set) (struct test_webrtc * t,
|
void (*on_offer_set) (struct test_webrtc * t,
|
||||||
|
@ -114,7 +115,6 @@ struct test_webrtc
|
||||||
GstPromise * promise,
|
GstPromise * promise,
|
||||||
gpointer user_data);
|
gpointer user_data);
|
||||||
GstWebRTCSessionDescription *answer_desc;
|
GstWebRTCSessionDescription *answer_desc;
|
||||||
guint answer_set_count;
|
|
||||||
gpointer answer_data;
|
gpointer answer_data;
|
||||||
GDestroyNotify answer_notify;
|
GDestroyNotify answer_notify;
|
||||||
void (*on_answer_set) (struct test_webrtc * t,
|
void (*on_answer_set) (struct test_webrtc * t,
|
||||||
|
@ -178,19 +178,31 @@ test_webrtc_state_find_unlocked (struct test_webrtc *t, TestState state,
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void
|
static void
|
||||||
_on_answer_set (GstPromise * promise, gpointer user_data)
|
_on_local_answer_set (GstPromise * promise, gpointer user_data)
|
||||||
{
|
{
|
||||||
struct test_webrtc *t = user_data;
|
struct test_webrtc *t = user_data;
|
||||||
GstElement *answerer = TEST_GET_ANSWERER (t);
|
GstElement *answerer = TEST_GET_ANSWERER (t);
|
||||||
|
|
||||||
g_mutex_lock (&t->lock);
|
g_mutex_lock (&t->lock);
|
||||||
if (++t->answer_set_count >= 2) {
|
if (t->on_answer_set)
|
||||||
if (t->on_answer_set)
|
t->on_answer_set (t, answerer, promise, t->answer_set_data);
|
||||||
t->on_answer_set (t, answerer, promise, t->answer_set_data);
|
test_webrtc_signal_state_unlocked (t, STATE_LOCAL_ANSWER_SET);
|
||||||
test_webrtc_signal_state_unlocked (t, STATE_ANSWER_SET);
|
gst_promise_unref (promise);
|
||||||
g_cond_broadcast (&t->cond);
|
g_mutex_unlock (&t->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_on_remote_answer_set (GstPromise * promise, gpointer user_data)
|
||||||
|
{
|
||||||
|
struct test_webrtc *t = user_data;
|
||||||
|
GstElement *offeror = TEST_GET_OFFEROR (t);
|
||||||
|
|
||||||
|
g_mutex_lock (&t->lock);
|
||||||
|
if (t->on_answer_set)
|
||||||
|
t->on_answer_set (t, offeror, promise, t->answer_set_data);
|
||||||
|
test_webrtc_signal_state_unlocked (t, STATE_REMOTE_ANSWER_SET);
|
||||||
gst_promise_unref (promise);
|
gst_promise_unref (promise);
|
||||||
g_mutex_unlock (&t->lock);
|
g_mutex_unlock (&t->lock);
|
||||||
}
|
}
|
||||||
|
@ -231,10 +243,10 @@ _on_answer_received (GstPromise * promise, gpointer user_data)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
if (t->answer_desc) {
|
if (t->answer_desc) {
|
||||||
promise = gst_promise_new_with_change_func (_on_answer_set, t, NULL);
|
promise = gst_promise_new_with_change_func (_on_local_answer_set, t, NULL);
|
||||||
g_signal_emit_by_name (answerer, "set-local-description", t->answer_desc,
|
g_signal_emit_by_name (answerer, "set-local-description", t->answer_desc,
|
||||||
promise);
|
promise);
|
||||||
promise = gst_promise_new_with_change_func (_on_answer_set, t, NULL);
|
promise = gst_promise_new_with_change_func (_on_remote_answer_set, t, NULL);
|
||||||
g_signal_emit_by_name (offeror, "set-remote-description", t->answer_desc,
|
g_signal_emit_by_name (offeror, "set-remote-description", t->answer_desc,
|
||||||
promise);
|
promise);
|
||||||
}
|
}
|
||||||
|
@ -251,18 +263,29 @@ error:
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
_on_offer_set (GstPromise * promise, gpointer user_data)
|
_on_local_offer_set (GstPromise * promise, gpointer user_data)
|
||||||
{
|
{
|
||||||
struct test_webrtc *t = user_data;
|
struct test_webrtc *t = user_data;
|
||||||
GstElement *offeror = TEST_GET_OFFEROR (t);
|
GstElement *offeror = TEST_GET_OFFEROR (t);
|
||||||
|
|
||||||
g_mutex_lock (&t->lock);
|
g_mutex_lock (&t->lock);
|
||||||
if (++t->offer_set_count >= 2) {
|
if (t->on_offer_set)
|
||||||
if (t->on_offer_set)
|
t->on_offer_set (t, offeror, promise, t->offer_set_data);
|
||||||
t->on_offer_set (t, offeror, promise, t->offer_set_data);
|
test_webrtc_signal_state_unlocked (t, STATE_LOCAL_OFFER_SET);
|
||||||
test_webrtc_signal_state_unlocked (t, STATE_OFFER_SET);
|
gst_promise_unref (promise);
|
||||||
g_cond_broadcast (&t->cond);
|
g_mutex_unlock (&t->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_on_remote_offer_set (GstPromise * promise, gpointer user_data)
|
||||||
|
{
|
||||||
|
struct test_webrtc *t = user_data;
|
||||||
|
GstElement *answerer = TEST_GET_ANSWERER (t);
|
||||||
|
|
||||||
|
g_mutex_lock (&t->lock);
|
||||||
|
if (t->on_offer_set)
|
||||||
|
t->on_offer_set (t, answerer, promise, t->offer_set_data);
|
||||||
|
test_webrtc_signal_state_unlocked (t, STATE_REMOTE_OFFER_SET);
|
||||||
gst_promise_unref (promise);
|
gst_promise_unref (promise);
|
||||||
g_mutex_unlock (&t->lock);
|
g_mutex_unlock (&t->lock);
|
||||||
}
|
}
|
||||||
|
@ -309,10 +332,10 @@ _on_offer_received (GstPromise * promise, gpointer user_data)
|
||||||
g_mutex_unlock (&t->lock);
|
g_mutex_unlock (&t->lock);
|
||||||
|
|
||||||
if (t->offer_desc) {
|
if (t->offer_desc) {
|
||||||
promise = gst_promise_new_with_change_func (_on_offer_set, t, NULL);
|
promise = gst_promise_new_with_change_func (_on_local_offer_set, t, NULL);
|
||||||
g_signal_emit_by_name (offeror, "set-local-description", t->offer_desc,
|
g_signal_emit_by_name (offeror, "set-local-description", t->offer_desc,
|
||||||
promise);
|
promise);
|
||||||
promise = gst_promise_new_with_change_func (_on_offer_set, t, NULL);
|
promise = gst_promise_new_with_change_func (_on_remote_offer_set, t, NULL);
|
||||||
g_signal_emit_by_name (answerer, "set-remote-description", t->offer_desc,
|
g_signal_emit_by_name (answerer, "set-remote-description", t->offer_desc,
|
||||||
promise);
|
promise);
|
||||||
|
|
||||||
|
@ -689,11 +712,9 @@ test_webrtc_reset_negotiation (struct test_webrtc *t)
|
||||||
if (t->offer_desc)
|
if (t->offer_desc)
|
||||||
gst_webrtc_session_description_free (t->offer_desc);
|
gst_webrtc_session_description_free (t->offer_desc);
|
||||||
t->offer_desc = NULL;
|
t->offer_desc = NULL;
|
||||||
t->offer_set_count = 0;
|
|
||||||
if (t->answer_desc)
|
if (t->answer_desc)
|
||||||
gst_webrtc_session_description_free (t->answer_desc);
|
gst_webrtc_session_description_free (t->answer_desc);
|
||||||
t->answer_desc = NULL;
|
t->answer_desc = NULL;
|
||||||
t->answer_set_count = 0;
|
|
||||||
|
|
||||||
test_webrtc_signal_state (t, STATE_NEGOTIATION_NEEDED);
|
test_webrtc_signal_state (t, STATE_NEGOTIATION_NEEDED);
|
||||||
}
|
}
|
||||||
|
@ -832,7 +853,7 @@ static TestState
|
||||||
test_webrtc_wait_for_answer_error_eos (struct test_webrtc *t)
|
test_webrtc_wait_for_answer_error_eos (struct test_webrtc *t)
|
||||||
{
|
{
|
||||||
TestState states = 0;
|
TestState states = 0;
|
||||||
states |= (1 << STATE_ANSWER_SET);
|
states |= (1 << STATE_REMOTE_ANSWER_SET);
|
||||||
states |= (1 << STATE_EOS);
|
states |= (1 << STATE_EOS);
|
||||||
states |= (1 << STATE_ERROR);
|
states |= (1 << STATE_ERROR);
|
||||||
return test_webrtc_wait_for_state_mask (t, states);
|
return test_webrtc_wait_for_state_mask (t, states);
|
||||||
|
@ -961,7 +982,7 @@ test_validate_sdp_full (struct test_webrtc *t, struct validate_sdp *offer,
|
||||||
|
|
||||||
if (wait_mask == 0) {
|
if (wait_mask == 0) {
|
||||||
fail_unless_equals_int (test_webrtc_wait_for_answer_error_eos (t),
|
fail_unless_equals_int (test_webrtc_wait_for_answer_error_eos (t),
|
||||||
STATE_ANSWER_SET);
|
STATE_REMOTE_ANSWER_SET);
|
||||||
} else {
|
} else {
|
||||||
test_webrtc_wait_for_state_mask (t, wait_mask);
|
test_webrtc_wait_for_state_mask (t, wait_mask);
|
||||||
}
|
}
|
||||||
|
@ -1107,6 +1128,7 @@ on_sdp_media_setup (struct test_webrtc *t, GstElement * element,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
add_fake_audio_src_harness (GstHarness * h, gint pt, guint ssrc)
|
add_fake_audio_src_harness (GstHarness * h, gint pt, guint ssrc)
|
||||||
{
|
{
|
||||||
|
@ -1419,6 +1441,47 @@ GST_START_TEST (test_payload_types)
|
||||||
|
|
||||||
GST_END_TEST;
|
GST_END_TEST;
|
||||||
|
|
||||||
|
static void
|
||||||
|
_check_transceiver_mids (struct test_webrtc *t, GstElement * element,
|
||||||
|
GstPromise * promise, gpointer user_data)
|
||||||
|
{
|
||||||
|
const GArray *expected_mids = user_data;
|
||||||
|
GArray *transceivers;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
g_signal_emit_by_name (element, "get-transceivers", &transceivers);
|
||||||
|
fail_unless (transceivers != NULL);
|
||||||
|
fail_unless_equals_uint64 (transceivers->len, expected_mids->len);
|
||||||
|
for (i = 0; i < transceivers->len; ++i) {
|
||||||
|
GstWebRTCRTPTransceiver *trans =
|
||||||
|
g_array_index (transceivers, GstWebRTCRTPTransceiver *, i);
|
||||||
|
gchar *mid = g_array_index (expected_mids, char *, i);
|
||||||
|
fail_unless_equals_string (trans->mid, mid);
|
||||||
|
}
|
||||||
|
g_array_unref (transceivers);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_START_TEST (test_transceivers_mid)
|
||||||
|
{
|
||||||
|
struct test_webrtc *t = create_audio_video_test ();
|
||||||
|
const gchar *EXPECTED_MIDS_DATA[] = { "audio0", "video1" };
|
||||||
|
GArray *expected_mids = g_array_new (FALSE, FALSE, sizeof (gchar *));
|
||||||
|
g_array_append_vals (expected_mids, EXPECTED_MIDS_DATA,
|
||||||
|
sizeof (EXPECTED_MIDS_DATA) / sizeof (gchar *));
|
||||||
|
|
||||||
|
t->on_offer_set = _check_transceiver_mids;
|
||||||
|
t->offer_set_data = expected_mids;
|
||||||
|
|
||||||
|
t->on_answer_set = _check_transceiver_mids;
|
||||||
|
t->answer_set_data = expected_mids;
|
||||||
|
|
||||||
|
test_validate_sdp (t, NULL, NULL);
|
||||||
|
test_webrtc_free (t);
|
||||||
|
g_array_free (expected_mids, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
GST_START_TEST (test_no_nice_elements_request_pad)
|
GST_START_TEST (test_no_nice_elements_request_pad)
|
||||||
{
|
{
|
||||||
struct test_webrtc *t = test_webrtc_new ();
|
struct test_webrtc *t = test_webrtc_new ();
|
||||||
|
@ -1814,7 +1877,7 @@ GST_START_TEST (test_stats_with_stream)
|
||||||
gst_caps_unref (caps);
|
gst_caps_unref (caps);
|
||||||
|
|
||||||
test_webrtc_wait_for_answer_error_eos (t);
|
test_webrtc_wait_for_answer_error_eos (t);
|
||||||
test_webrtc_signal_state (t, STATE_ANSWER_SET);
|
test_webrtc_signal_state (t, STATE_REMOTE_ANSWER_SET);
|
||||||
|
|
||||||
p = gst_promise_new_with_change_func (_on_stats, t, NULL);
|
p = gst_promise_new_with_change_func (_on_stats, t, NULL);
|
||||||
g_signal_emit_by_name (t->webrtc1, "get-stats", NULL, p);
|
g_signal_emit_by_name (t->webrtc1, "get-stats", NULL, p);
|
||||||
|
@ -5905,7 +5968,7 @@ GST_START_TEST (test_sdp_session_setup_attribute)
|
||||||
fail_if (gst_element_set_state (t->webrtc2, GST_STATE_READY) ==
|
fail_if (gst_element_set_state (t->webrtc2, GST_STATE_READY) ==
|
||||||
GST_STATE_CHANGE_FAILURE);
|
GST_STATE_CHANGE_FAILURE);
|
||||||
test_webrtc_create_offer (t);
|
test_webrtc_create_offer (t);
|
||||||
test_webrtc_wait_for_state_mask (t, 1 << STATE_ANSWER_SET);
|
test_webrtc_wait_for_state_mask (t, 1 << STATE_REMOTE_ANSWER_SET);
|
||||||
|
|
||||||
test_webrtc_wait_for_ice_gathering_complete (t);
|
test_webrtc_wait_for_ice_gathering_complete (t);
|
||||||
|
|
||||||
|
@ -5943,6 +6006,7 @@ webrtcbin_suite (void)
|
||||||
tcase_add_test (tc, test_media_direction);
|
tcase_add_test (tc, test_media_direction);
|
||||||
tcase_add_test (tc, test_add_transceiver);
|
tcase_add_test (tc, test_add_transceiver);
|
||||||
tcase_add_test (tc, test_get_transceivers);
|
tcase_add_test (tc, test_get_transceivers);
|
||||||
|
tcase_add_test (tc, test_transceivers_mid);
|
||||||
tcase_add_test (tc, test_add_recvonly_transceiver);
|
tcase_add_test (tc, test_add_recvonly_transceiver);
|
||||||
tcase_add_test (tc, test_recvonly_sendonly);
|
tcase_add_test (tc, test_recvonly_sendonly);
|
||||||
tcase_add_test (tc, test_payload_types);
|
tcase_add_test (tc, test_payload_types);
|
||||||
|
|
Loading…
Reference in a new issue