webrtc: implement support for msid values

Local msid values are taken from sink pad property, or fallback to the
previously used cname.

The remote msid values are exposed on the relevant src pads.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3106>
This commit is contained in:
Matthew Waters 2022-10-18 12:17:04 +11:00
parent 3bb8700577
commit 993bc8fc01
6 changed files with 511 additions and 53 deletions

View file

@ -236665,12 +236665,13 @@
"caps": "application/x-rtp:\n",
"direction": "sink",
"presence": "request",
"type": "GstWebRTCBinPad"
"type": "GstWebRTCBinSinkPad"
},
"src_%%u": {
"caps": "application/x-rtp:\n",
"direction": "src",
"presence": "sometimes"
"presence": "sometimes",
"type": "GstWebRTCBinSrcPad"
}
},
"properties": {
@ -237128,6 +237129,60 @@
"writable": false
}
}
},
"GstWebRTCBinSinkPad": {
"hierarchy": [
"GstWebRTCBinSinkPad",
"GstWebRTCBinPad",
"GstGhostPad",
"GstProxyPad",
"GstPad",
"GstObject",
"GInitiallyUnowned",
"GObject"
],
"kind": "object",
"properties": {
"msid": {
"blurb": "Local MediaStream ID to use for this pad (NULL = unset)",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "NULL",
"mutable": "null",
"readable": true,
"type": "gchararray",
"writable": true
}
}
},
"GstWebRTCBinSrcPad": {
"hierarchy": [
"GstWebRTCBinSrcPad",
"GstWebRTCBinPad",
"GstGhostPad",
"GstProxyPad",
"GstPad",
"GstObject",
"GInitiallyUnowned",
"GObject"
],
"kind": "object",
"properties": {
"msid": {
"blurb": "Remote MediaStream ID in use for this pad (NULL = not advertised)",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "NULL",
"mutable": "null",
"readable": true,
"type": "gchararray",
"writable": false
}
}
}
},
"package": "GStreamer Bad Plug-ins",

View file

@ -254,13 +254,9 @@ gst_webrtc_bin_pad_finalize (GObject * object)
{
GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (object);
if (pad->trans)
gst_object_unref (pad->trans);
pad->trans = NULL;
if (pad->received_caps)
gst_caps_unref (pad->received_caps);
pad->received_caps = NULL;
gst_clear_object (&pad->trans);
gst_clear_caps (&pad->received_caps);
g_clear_pointer (&pad->msid, g_free);
G_OBJECT_CLASS (gst_webrtc_bin_pad_parent_class)->finalize (object);
}
@ -457,33 +453,177 @@ gst_webrtc_bin_pad_init (GstWebRTCBinPad * pad)
}
static GstWebRTCBinPad *
gst_webrtc_bin_pad_new (const gchar * name, GstPadDirection direction)
gst_webrtc_bin_pad_new (const gchar * name, GstPadDirection direction,
char *msid)
{
GstWebRTCBinPad *pad;
GstPadTemplate *template;
GType pad_type;
if (direction == GST_PAD_SINK)
if (direction == GST_PAD_SINK) {
template = gst_static_pad_template_get (&sink_template);
else if (direction == GST_PAD_SRC)
pad_type = GST_TYPE_WEBRTC_BIN_SINK_PAD;
} else if (direction == GST_PAD_SRC) {
template = gst_static_pad_template_get (&src_template);
else
pad_type = GST_TYPE_WEBRTC_BIN_SRC_PAD;
} else {
g_assert_not_reached ();
}
pad =
g_object_new (gst_webrtc_bin_pad_get_type (), "name", name, "direction",
g_object_new (pad_type, "name", name, "direction",
direction, "template", template, NULL);
gst_object_unref (template);
if (direction == GST_PAD_SINK) {
gst_pad_set_event_function (GST_PAD (pad), gst_webrtcbin_sink_event);
gst_pad_set_query_function (GST_PAD (pad), gst_webrtcbin_sink_query);
}
pad->msid = msid;
GST_DEBUG_OBJECT (pad, "new visible pad with direction %s",
direction == GST_PAD_SRC ? "src" : "sink");
return pad;
}
enum
{
PROP_SINK_PAD_MSID = 1,
};
/**
* GstWebRTCBinSinkPad:
*
* Since: 1.22
*/
struct _GstWebRTCBinSinkPad
{
GstWebRTCBinPad pad;
};
G_DEFINE_TYPE (GstWebRTCBinSinkPad, gst_webrtc_bin_sink_pad,
GST_TYPE_WEBRTC_BIN_PAD);
static void
gst_webrtc_bin_sink_pad_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (object);
switch (prop_id) {
case PROP_SINK_PAD_MSID:
g_value_set_string (value, pad->msid);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_webrtc_bin_sink_pad_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (object);
switch (prop_id) {
case PROP_SINK_PAD_MSID:
g_free (pad->msid);
pad->msid = g_value_dup_string (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_webrtc_bin_sink_pad_class_init (GstWebRTCBinSinkPadClass * klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
gobject_class->get_property = gst_webrtc_bin_sink_pad_get_property;
gobject_class->set_property = gst_webrtc_bin_sink_pad_set_property;
/**
* GstWebRTCBinSinkPad:msid:
*
* The MediaStream Identifier to use for this pad (MediaStreamTrack).
* Fallback is the RTP SDES cname value if not provided.
*
* Since: 1.22
*/
g_object_class_install_property (gobject_class,
PROP_SINK_PAD_MSID,
g_param_spec_string ("msid", "MSID",
"Local MediaStream ID to use for this pad (NULL = unset)", NULL,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
}
static void
gst_webrtc_bin_sink_pad_init (GstWebRTCBinSinkPad * pad)
{
gst_pad_set_event_function (GST_PAD (pad), gst_webrtcbin_sink_event);
gst_pad_set_query_function (GST_PAD (pad), gst_webrtcbin_sink_query);
}
enum
{
PROP_SRC_PAD_MSID = 1,
};
/**
* GstWebRTCBinSrcPad:
*
* Since: 1.22
*/
struct _GstWebRTCBinSrcPad
{
GstWebRTCBinPad pad;
};
G_DEFINE_TYPE (GstWebRTCBinSrcPad, gst_webrtc_bin_src_pad,
GST_TYPE_WEBRTC_BIN_PAD);
static void
gst_webrtc_bin_src_pad_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (object);
switch (prop_id) {
case PROP_SRC_PAD_MSID:
g_value_set_string (value, pad->msid);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_webrtc_bin_src_pad_class_init (GstWebRTCBinSrcPadClass * klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
gobject_class->get_property = gst_webrtc_bin_src_pad_get_property;
/**
* GstWebRTCBinSrcPad:msid:
*
* The MediaStream Identifier the remote peer used for this pad (MediaStreamTrack).
* Will be NULL if not advertised in the remote SDP.
*
* Since: 1.22
*/
g_object_class_install_property (gobject_class,
PROP_SRC_PAD_MSID,
g_param_spec_string ("msid", "MSID",
"Remote MediaStream ID in use for this pad (NULL = not advertised)",
NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
}
static void
gst_webrtc_bin_src_pad_init (GstWebRTCBinSrcPad * pad)
{
}
#define gst_webrtc_bin_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstWebRTCBin, gst_webrtc_bin, GST_TYPE_BIN,
G_ADD_PRIVATE (GstWebRTCBin)
@ -2867,15 +3007,27 @@ _media_add_rtx_ssrc (GQuark field_id, const GValue * value, RtxSsrcData * data)
gchar *str;
GstStructure *sdes;
const gchar *cname;
GstWebRTCBinPad *sink_pad;
const char *msid = NULL;
g_object_get (data->webrtc->rtpbin, "sdes", &sdes, NULL);
/* http://www.freesoft.org/CIE/RFC/1889/24.htm */
cname = gst_structure_get_string (sdes, "cname");
sink_pad =
_find_pad_for_transceiver (data->webrtc, GST_PAD_SINK,
GST_WEBRTC_RTP_TRANSCEIVER (data->trans));
if (sink_pad)
msid = sink_pad->msid;
/* fallback to cname if no msid provided */
if (!msid)
msid = cname;
/* https://tools.ietf.org/html/draft-ietf-mmusic-msid-16 */
/* FIXME: the ssrc is not present in RFC8830, do we still need that? */
str =
g_strdup_printf ("%u msid:%s %s", g_value_get_uint (value),
cname, GST_OBJECT_NAME (data->trans));
msid, GST_OBJECT_NAME (data->trans));
gst_sdp_media_add_attribute (data->media, "ssrc", str);
g_free (str);
@ -2883,6 +3035,7 @@ _media_add_rtx_ssrc (GQuark field_id, const GValue * value, RtxSsrcData * data)
gst_sdp_media_add_attribute (data->media, "ssrc", str);
g_free (str);
gst_clear_object (&sink_pad);
gst_structure_free (sdes);
return TRUE;
@ -2911,10 +3064,22 @@ _media_add_ssrcs (GstSDPMedia * media, GstCaps * caps, GstWebRTCBin * webrtc,
if (gst_structure_get_uint (s, "ssrc", &ssrc)) {
gchar *str;
GstWebRTCBinPad *sink_pad;
const char *msid = NULL;
sink_pad =
_find_pad_for_transceiver (webrtc, GST_PAD_SINK,
GST_WEBRTC_RTP_TRANSCEIVER (trans));
if (sink_pad)
msid = sink_pad->msid;
/* fallback to cname if no msid provided */
if (!msid)
msid = cname;
/* https://tools.ietf.org/html/draft-ietf-mmusic-msid-16 */
/* FIXME: the ssrc is not present in RFC8830, do we still need that? */
str =
g_strdup_printf ("%u msid:%s %s", ssrc, cname,
g_strdup_printf ("%u msid:%s %s", ssrc, msid,
GST_OBJECT_NAME (trans));
gst_sdp_media_add_attribute (media, "ssrc", str);
g_free (str);
@ -2922,6 +3087,8 @@ _media_add_ssrcs (GstSDPMedia * media, GstCaps * caps, GstWebRTCBin * webrtc,
str = g_strdup_printf ("%u cname:%s", ssrc, cname);
gst_sdp_media_add_attribute (media, "ssrc", str);
g_free (str);
gst_clear_object (&sink_pad);
}
}
@ -4697,7 +4864,7 @@ gst_webrtc_bin_create_answer (GstWebRTCBin * webrtc,
static GstWebRTCBinPad *
_create_pad_for_sdp_media (GstWebRTCBin * webrtc, GstPadDirection direction,
GstWebRTCRTPTransceiver * trans, guint serial)
GstWebRTCRTPTransceiver * trans, guint serial, char *msid)
{
GstWebRTCBinPad *pad;
gchar *pad_name;
@ -4712,7 +4879,7 @@ _create_pad_for_sdp_media (GstWebRTCBin * webrtc, GstPadDirection direction,
pad_name =
g_strdup_printf ("%s_%u", direction == GST_PAD_SRC ? "src" : "sink",
serial);
pad = gst_webrtc_bin_pad_new (pad_name, direction);
pad = gst_webrtc_bin_pad_new (pad_name, direction, msid);
g_free (pad_name);
pad->trans = gst_object_ref (trans);
@ -5420,12 +5587,21 @@ _update_transceiver_from_sdp_media (GstWebRTCBin * webrtc,
WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
GstWebRTCRTPTransceiverDirection prev_dir = rtp_trans->current_direction;
GstWebRTCRTPTransceiverDirection new_dir;
const GstSDPMedia *local_media, *remote_media;
const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
GstWebRTCDTLSSetup new_setup;
char *local_msid = NULL;
gboolean new_rtcp_rsize;
ReceiveState receive_state = RECEIVE_STATE_UNSET;
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);
rtp_trans->mline = media_idx;
if (!g_strcmp0 (gst_sdp_media_get_media (media), "audio")) {
@ -5452,17 +5628,9 @@ _update_transceiver_from_sdp_media (GstWebRTCBin * webrtc,
}
{
const GstSDPMedia *local_media, *remote_media;
GstWebRTCRTPTransceiverDirection local_dir, remote_dir;
GstWebRTCDTLSSetup local_setup, remote_setup;
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);
@ -5555,16 +5723,30 @@ _update_transceiver_from_sdp_media (GstWebRTCBin * webrtc,
new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
GstWebRTCBinPad *pad =
_find_pad_for_transceiver (webrtc, GST_PAD_SINK, rtp_trans);
local_msid = _get_msid_from_media (local_media);
if (pad) {
GST_DEBUG_OBJECT (webrtc, "found existing send pad %" GST_PTR_FORMAT
" for transceiver %" GST_PTR_FORMAT, pad, trans);
" for transceiver %" GST_PTR_FORMAT " with msid \'%s\'", pad, trans,
pad->msid);
if (g_strcmp0 (pad->msid, local_msid) != 0) {
GST_DEBUG_OBJECT (webrtc, "send pad %" GST_PTR_FORMAT
" transceiver %" GST_PTR_FORMAT " changing msid from \'%s\'"
" to \'%s\'", pad, trans, pad->msid, local_msid);
g_clear_pointer (&pad->msid, g_free);
pad->msid = local_msid;
g_object_notify (G_OBJECT (pad), "msid");
local_msid = NULL;
} else {
g_clear_pointer (&local_msid, g_free);
}
gst_object_unref (pad);
} else {
GST_DEBUG_OBJECT (webrtc,
"creating new send pad for transceiver %" GST_PTR_FORMAT, trans);
pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SINK, rtp_trans,
G_MAXUINT);
G_MAXUINT, local_msid);
local_msid = NULL;
_connect_input_stream (webrtc, pad);
_add_pad (webrtc, pad);
}
@ -5573,15 +5755,30 @@ _update_transceiver_from_sdp_media (GstWebRTCBin * webrtc,
new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
GstWebRTCBinPad *pad =
_find_pad_for_transceiver (webrtc, GST_PAD_SRC, rtp_trans);
char *remote_msid = _get_msid_from_media (remote_media);
if (pad) {
GST_DEBUG_OBJECT (webrtc, "found existing receive pad %" GST_PTR_FORMAT
" for transceiver %" GST_PTR_FORMAT, pad, trans);
" for transceiver %" GST_PTR_FORMAT " with msid \'%s\'", pad, trans,
pad->msid);
if (g_strcmp0 (pad->msid, remote_msid) != 0) {
GST_DEBUG_OBJECT (webrtc, "receive pad %" GST_PTR_FORMAT
" transceiver %" GST_PTR_FORMAT " changing msid from \'%s\'"
" to \'%s\'", pad, trans, pad->msid, remote_msid);
g_clear_pointer (&pad->msid, g_free);
pad->msid = remote_msid;
remote_msid = NULL;
g_object_notify (G_OBJECT (pad), "msid");
} else {
g_clear_pointer (&remote_msid, g_free);
}
gst_object_unref (pad);
} else {
GST_DEBUG_OBJECT (webrtc,
"creating new receive pad for transceiver %" GST_PTR_FORMAT, trans);
pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SRC, rtp_trans,
G_MAXUINT);
G_MAXUINT, remote_msid);
remote_msid = NULL;
if (!trans->stream) {
TransportStream *item;
@ -5597,7 +5794,6 @@ _update_transceiver_from_sdp_media (GstWebRTCBin * webrtc,
* as soon as the pad is added */
_add_pad_to_list (webrtc, pad);
}
}
rtp_trans->mline = media_idx;
@ -7058,7 +7254,8 @@ on_rtpbin_pad_added (GstElement * rtpbin, GstPad * new_pad,
* or somesuch */
gst_clear_object (&pad);
pad =
_create_pad_for_sdp_media (webrtc, GST_PAD_SRC, rtp_trans, G_MAXUINT);
_create_pad_for_sdp_media (webrtc, GST_PAD_SRC, rtp_trans, G_MAXUINT,
NULL);
GST_TRACE_OBJECT (webrtc,
"duplicate output ssrc? created new pad %" GST_PTR_FORMAT " for %"
GST_PTR_FORMAT " for rtp pad %s", pad, rtp_trans, new_pad_name);
@ -7940,7 +8137,7 @@ gst_webrtc_bin_request_new_pad (GstElement * element, GstPadTemplate * templ,
}
}
}
pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SINK, trans, serial);
pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SINK, trans, serial, NULL);
pad->block_id = gst_pad_add_probe (GST_PAD (pad), GST_PAD_PROBE_TYPE_BLOCK |
GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BUFFER_LIST,
@ -8265,8 +8462,9 @@ gst_webrtc_bin_class_init (GstWebRTCBinClass * klass)
element_class->change_state = gst_webrtc_bin_change_state;
gst_element_class_add_static_pad_template_with_gtype (element_class,
&sink_template, GST_TYPE_WEBRTC_BIN_PAD);
gst_element_class_add_static_pad_template (element_class, &src_template);
&sink_template, GST_TYPE_WEBRTC_BIN_SINK_PAD);
gst_element_class_add_static_pad_template_with_gtype (element_class,
&src_template, GST_TYPE_WEBRTC_BIN_SRC_PAD);
gst_element_class_set_metadata (element_class, "WebRTC Bin",
"Filter/Network/WebRTC", "A bin for webrtc connections",
@ -8764,6 +8962,8 @@ gst_webrtc_bin_class_init (GstWebRTCBinClass * klass)
NULL, GST_TYPE_WEBRTC_DATA_CHANNEL, 2, G_TYPE_STRING, GST_TYPE_STRUCTURE);
gst_type_mark_as_plugin_api (GST_TYPE_WEBRTC_BIN_PAD, 0);
gst_type_mark_as_plugin_api (GST_TYPE_WEBRTC_BIN_SINK_PAD, 0);
gst_type_mark_as_plugin_api (GST_TYPE_WEBRTC_BIN_SRC_PAD, 0);
}
static void

View file

@ -38,6 +38,8 @@ GType gst_webrtc_bin_pad_get_type(void);
typedef struct _GstWebRTCBinPad GstWebRTCBinPad;
typedef struct _GstWebRTCBinPadClass GstWebRTCBinPadClass;
G_DEFINE_AUTOPTR_CLEANUP_FUNC (GstWebRTCBinPad, gst_object_unref);
struct _GstWebRTCBinPad
{
GstGhostPad parent;
@ -46,6 +48,7 @@ struct _GstWebRTCBinPad
gulong block_id;
GstCaps *received_caps;
char *msid;
};
struct _GstWebRTCBinPadClass
@ -53,6 +56,14 @@ struct _GstWebRTCBinPadClass
GstGhostPadClass parent_class;
};
G_DECLARE_FINAL_TYPE (GstWebRTCBinSinkPad, gst_webrtc_bin_sink_pad, GST,
WEBRTC_BIN_SINK_PAD, GstWebRTCBinPad);
#define GST_TYPE_WEBRTC_BIN_SINK_PAD (gst_webrtc_bin_sink_pad_get_type())
G_DECLARE_FINAL_TYPE (GstWebRTCBinSrcPad, gst_webrtc_bin_src_pad, GST,
WEBRTC_BIN_SRC_PAD, GstWebRTCBinPad);
#define GST_TYPE_WEBRTC_BIN_SRC_PAD (gst_webrtc_bin_src_pad_get_type())
GType gst_webrtc_bin_get_type(void);
#define GST_TYPE_WEBRTC_BIN (gst_webrtc_bin_get_type())
#define GST_WEBRTC_BIN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_WEBRTC_BIN,GstWebRTCBin))

View file

@ -223,3 +223,28 @@ webrtc_kind_from_caps (const GstCaps * caps)
return GST_WEBRTC_KIND_UNKNOWN;
}
char *
_get_msid_from_media (const GstSDPMedia * media)
{
int i;
for (i = 0; i < gst_sdp_media_attributes_len (media); i++) {
const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i);
const char *start, *end;
if (!attr->value)
continue;
start = strstr (attr->value, "msid:");
if (!start)
continue;
start += strlen ("msid:");
end = strstr (start, " ");
if (end)
return g_strndup (start, end - start);
}
return NULL;
}

View file

@ -66,6 +66,8 @@ G_GNUC_INTERNAL
GstCaps * _rtp_caps_from_media (const GstSDPMedia * media);
G_GNUC_INTERNAL
GstWebRTCKind webrtc_kind_from_caps (const GstCaps * caps);
G_GNUC_INTERNAL
char * _get_msid_from_media (const GstSDPMedia * media);
#define gst_webrtc_kind_to_string(kind) _enum_value_to_string(GST_TYPE_WEBRTC_KIND, kind)
#define gst_webrtc_rtp_transceiver_direction_to_string(dir) _enum_value_to_string(GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION, dir)

View file

@ -47,7 +47,7 @@
typedef enum
{
STATE_NEW,
STATE_NEW = 1,
STATE_NEGOTIATION_NEEDED,
STATE_OFFER_CREATED,
STATE_OFFER_SET,
@ -776,27 +776,56 @@ test_webrtc_create_offer (struct test_webrtc *t)
}
static TestState
test_webrtc_wait_for_state_mask (struct test_webrtc *t, TestState state)
test_webrtc_check_for_state_mask_unlocked (struct test_webrtc *t,
TestState state)
{
guint i;
GST_LOG ("attempting to check for state mask 0x%x", state);
for (i = 0; i < t->states->len; i++) {
TestState val = g_array_index (t->states, TestState, i);
if (((1 << val) & state) != 0) {
GST_DEBUG ("found state 0x%x in wait mask 0x%x at idx %u", val, state, i);
g_array_remove_range (t->states, 0, i + 1);
return val;
}
}
return 0;
}
static TestState
test_webrtc_check_for_state_mask (struct test_webrtc *t, TestState state)
{
TestState ret;
g_mutex_lock (&t->lock);
ret = test_webrtc_check_for_state_mask_unlocked (t, state);
g_mutex_unlock (&t->lock);
return ret;
}
static TestState
test_webrtc_wait_for_state_mask (struct test_webrtc *t, TestState state)
{
TestState ret = 0;
g_mutex_lock (&t->lock);
GST_LOG ("attempting to wait for state mask 0x%x", state);
while (TRUE) {
for (i = 0; i < t->states->len; i++) {
TestState val = g_array_index (t->states, TestState, i);
ret = test_webrtc_check_for_state_mask_unlocked (t, state);
if (ret)
break;
if (((1 << val) & state) != 0) {
GST_DEBUG ("found state 0x%x in wait mask 0x%x at idx %u", val, state,
i);
g_array_remove_range (t->states, 0, i + 1);
g_mutex_unlock (&t->lock);
return val;
}
}
g_cond_wait (&t->cond, &t->lock);
}
g_mutex_unlock (&t->lock);
return ret;
}
static TestState
@ -3862,7 +3891,7 @@ GST_START_TEST (test_codec_preferences_incompatible_extmaps)
t->on_ice_candidate = NULL;
t->on_offer_created = offer_created_produced_error;
test_validate_sdp_full (t, NULL, NULL, STATE_OFFER_CREATED, TRUE);
test_validate_sdp_full (t, NULL, NULL, 1 << STATE_ERROR, TRUE);
test_webrtc_free (t);
}
@ -3895,7 +3924,7 @@ GST_START_TEST (test_codec_preferences_invalid_extmap)
t->on_ice_candidate = NULL;
t->on_offer_created = offer_created_produced_error;
test_validate_sdp_full (t, NULL, NULL, STATE_OFFER_CREATED, TRUE);
test_validate_sdp_full (t, NULL, NULL, 1 << STATE_ERROR, TRUE);
test_webrtc_free (t);
}
@ -5540,6 +5569,141 @@ GST_START_TEST (test_data_channel_recreate_offer)
GST_END_TEST;
static void
validate_msid (struct test_webrtc *t, GstElement * element,
GstWebRTCSessionDescription * desc, gpointer user_data)
{
char **expected_msid = user_data;
int i;
for (i = 0; i < gst_sdp_message_medias_len (desc->sdp); i++) {
const GstSDPMedia *media = gst_sdp_message_get_media (desc->sdp, i);
gboolean have_msid = FALSE;
char *prev_msid = NULL;
int j;
for (j = 0; j < gst_sdp_media_attributes_len (media); j++) {
const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, j);
const char *start;
if (!attr->value)
continue;
start = strstr (attr->value, "msid:");
if (start) {
const char *end;
char *msid;
start += strlen ("msid:");
end = strstr (start, " ");
msid = g_strndup (start, end - start);
fail_unless (end, "Invalid msid attribute");
fail_if (have_msid && g_strcmp0 (prev_msid, msid) != 0,
"different values for multiple msid values at mline %u, "
"prev msid %s, msid %s", i, prev_msid, msid);
have_msid = TRUE;
fail_unless_equals_string (msid, expected_msid[i]);
g_clear_pointer (&prev_msid, g_free);
prev_msid = msid;
}
}
g_clear_pointer (&prev_msid, g_free);
fail_unless (have_msid, "no msid attribute in media %u", i);
}
}
static void
_pad_added_src_check_msid (struct test_webrtc *t, GstElement * element,
GstPad * pad, gpointer user_data)
{
const char *expected_msid = user_data;
char *msid;
if (GST_PAD_DIRECTION (pad) != GST_PAD_SRC)
return;
g_object_get (pad, "msid", &msid, NULL);
fail_unless_equals_string (msid, expected_msid);
g_clear_pointer (&msid, g_free);
test_webrtc_signal_state_unlocked (t, STATE_CUSTOM);
}
GST_START_TEST (test_msid)
{
struct test_webrtc *t = create_audio_test ();
VAL_SDP_INIT (no_duplicate_payloads, on_sdp_media_no_duplicate_payloads,
NULL, NULL);
guint media_format_count[] = { 1, 5 };
VAL_SDP_INIT (media_formats, on_sdp_media_count_formats,
media_format_count, &no_duplicate_payloads);
VAL_SDP_INIT (count, _count_num_sdp_media, GUINT_TO_POINTER (2),
&media_formats);
const gchar *expected_offer_msid[] = { "a1", "a1", };
VAL_SDP_INIT (offer_msid, validate_msid, expected_offer_msid, &count);
const gchar *expected_offer_setup[] = { "actpass", "actpass", };
VAL_SDP_INIT (offer_setup, on_sdp_media_setup, expected_offer_setup,
&offer_msid);
const gchar *expected_offer_direction[] = { "sendrecv", "sendrecv", };
VAL_SDP_INIT (offer, on_sdp_media_direction, expected_offer_direction,
&offer_setup);
const gchar *expected_answer_setup[] = { "active", "active", };
VAL_SDP_INIT (answer_setup, on_sdp_media_setup, expected_answer_setup,
&count);
const gchar *expected_answer_direction[] = { "recvonly", "recvonly", };
VAL_SDP_INIT (answer, on_sdp_media_direction, expected_answer_direction,
&answer_setup);
GstPad *pad;
GstHarness *src;
GstElement *rtpbin2;
t->on_pad_added = _pad_added_src_check_msid;
t->pad_added_data = (gpointer) "a1";
rtpbin2 = gst_bin_get_by_name (GST_BIN (t->webrtc2), "rtpbin");
fail_unless (rtpbin2 != NULL);
g_signal_connect (rtpbin2, "new-jitterbuffer",
G_CALLBACK (new_jitterbuffer_set_fast_start), NULL);
g_object_unref (rtpbin2);
g_signal_connect (t->webrtc1, "on-new-transceiver",
G_CALLBACK (on_new_transceiver_set_rtx_fec), NULL);
g_signal_connect (t->webrtc2, "on-new-transceiver",
G_CALLBACK (on_new_transceiver_set_rtx_fec), NULL);
src = gst_harness_new_with_element (t->webrtc1, "sink_1", NULL);
add_audio_test_src_harness (src, 0x12345678);
t->harnesses = g_list_prepend (t->harnesses, src);
pad = gst_element_get_static_pad (t->webrtc1, "sink_0");
g_object_set (pad, "msid", "a1", NULL);
gst_clear_object (&pad);
pad = gst_element_get_static_pad (t->webrtc1, "sink_1");
g_object_set (pad, "msid", "a1", NULL);
gst_clear_object (&pad);
test_validate_sdp (t, &offer, &answer);
fail_if (gst_element_set_state (t->webrtc1,
GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE);
fail_if (gst_element_set_state (t->webrtc2,
GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE);
while (TRUE) {
gst_harness_push_from_src (src);
if (test_webrtc_check_for_state_mask (t, 1 << STATE_CUSTOM))
break;
g_usleep (10 * 1000);
}
test_webrtc_free (t);
}
GST_END_TEST;
static Suite *
webrtcbin_suite (void)
{
@ -5604,6 +5768,7 @@ webrtcbin_suite (void)
tcase_add_test (tc, test_bundle_multiple_media_rtx_payload_mapping);
tcase_add_test (tc, test_invalid_add_media_in_answer);
tcase_add_test (tc, test_add_turn_server);
tcase_add_test (tc, test_msid);
if (sctpenc && sctpdec) {
tcase_add_test (tc, test_data_channel_create);
tcase_add_test (tc, test_data_channel_remote_notify);