mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-06-05 15:08:53 +00:00
webrtc: Initial support for stream addition/removal
Limitations: - No transport changes at all (ICE, DTLS) - Codec changes are untested and probably don't work - Stream removal doesn't remove transports (i.e. non-bundled transports will stay around until webrtcbin is shutdown) - Unified Plan SDP only. No Plan-B support.
This commit is contained in:
parent
015cb75f66
commit
177aa22bcd
16 changed files with 1623 additions and 381 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -75,6 +75,7 @@ gst*orc.h
|
||||||
/tests/examples/webrtc/webrtc
|
/tests/examples/webrtc/webrtc
|
||||||
/tests/examples/webrtc/webrtcbidirectional
|
/tests/examples/webrtc/webrtcbidirectional
|
||||||
/tests/examples/webrtc/webrtcswap
|
/tests/examples/webrtc/webrtcswap
|
||||||
|
/tests/examples/webrtc/webrtcrenego
|
||||||
/tests/examples/webrtc/webrtctransceiver
|
/tests/examples/webrtc/webrtctransceiver
|
||||||
|
|
||||||
Build
|
Build
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -131,6 +131,8 @@ struct _GstWebRTCBinPrivate
|
||||||
/* count of the number of media streams we've offered for uniqueness */
|
/* count of the number of media streams we've offered for uniqueness */
|
||||||
/* FIXME: overflow? */
|
/* FIXME: overflow? */
|
||||||
guint media_counter;
|
guint media_counter;
|
||||||
|
/* the number of times create_offer has been called for the version field */
|
||||||
|
guint offer_count;
|
||||||
|
|
||||||
GstStructure *stats;
|
GstStructure *stats;
|
||||||
};
|
};
|
||||||
|
|
|
@ -75,6 +75,36 @@ transport_stream_get_pt (TransportStream * stream, const gchar * encoding_name)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int *
|
||||||
|
transport_stream_get_all_pt (TransportStream * stream,
|
||||||
|
const gchar * encoding_name, gsize * pt_len)
|
||||||
|
{
|
||||||
|
guint i;
|
||||||
|
gsize ret_i = 0;
|
||||||
|
gsize ret_size = 8;
|
||||||
|
int *ret = NULL;
|
||||||
|
|
||||||
|
for (i = 0; i < stream->ptmap->len; i++) {
|
||||||
|
PtMapItem *item = &g_array_index (stream->ptmap, PtMapItem, i);
|
||||||
|
if (!gst_caps_is_empty (item->caps)) {
|
||||||
|
GstStructure *s = gst_caps_get_structure (item->caps, 0);
|
||||||
|
if (!g_strcmp0 (gst_structure_get_string (s, "encoding-name"),
|
||||||
|
encoding_name)) {
|
||||||
|
if (!ret)
|
||||||
|
ret = g_new0 (int, ret_size);
|
||||||
|
if (ret_i >= ret_size) {
|
||||||
|
ret_size *= 2;
|
||||||
|
ret = g_realloc_n (ret, ret_size, sizeof (int));
|
||||||
|
}
|
||||||
|
ret[ret_i++] = item->pt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*pt_len = ret_i;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
transport_stream_set_property (GObject * object, guint prop_id,
|
transport_stream_set_property (GObject * object, guint prop_id,
|
||||||
const GValue * value, GParamSpec * pspec)
|
const GValue * value, GParamSpec * pspec)
|
||||||
|
@ -152,6 +182,14 @@ transport_stream_dispose (GObject * object)
|
||||||
gst_object_unref (stream->rtcp_transport);
|
gst_object_unref (stream->rtcp_transport);
|
||||||
stream->rtcp_transport = NULL;
|
stream->rtcp_transport = NULL;
|
||||||
|
|
||||||
|
if (stream->rtxsend)
|
||||||
|
gst_object_unref (stream->rtxsend);
|
||||||
|
stream->rtxsend = NULL;
|
||||||
|
|
||||||
|
if (stream->rtxreceive)
|
||||||
|
gst_object_unref (stream->rtxreceive);
|
||||||
|
stream->rtxreceive = NULL;
|
||||||
|
|
||||||
GST_OBJECT_PARENT (object) = NULL;
|
GST_OBJECT_PARENT (object) = NULL;
|
||||||
|
|
||||||
G_OBJECT_CLASS (parent_class)->dispose (object);
|
G_OBJECT_CLASS (parent_class)->dispose (object);
|
||||||
|
|
|
@ -61,6 +61,10 @@ struct _TransportStream
|
||||||
|
|
||||||
GArray *ptmap; /* array of PtMapItem's */
|
GArray *ptmap; /* array of PtMapItem's */
|
||||||
GArray *remote_ssrcmap; /* array of SsrcMapItem's */
|
GArray *remote_ssrcmap; /* array of SsrcMapItem's */
|
||||||
|
gboolean output_connected; /* whether receive bin is connected to rtpbin */
|
||||||
|
|
||||||
|
GstElement *rtxsend;
|
||||||
|
GstElement *rtxreceive;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _TransportStreamClass
|
struct _TransportStreamClass
|
||||||
|
@ -72,6 +76,9 @@ TransportStream * transport_stream_new (GstWebRTCBin * webrtc,
|
||||||
guint session_id);
|
guint session_id);
|
||||||
int transport_stream_get_pt (TransportStream * stream,
|
int transport_stream_get_pt (TransportStream * stream,
|
||||||
const gchar * encoding_name);
|
const gchar * encoding_name);
|
||||||
|
int * transport_stream_get_all_pt (TransportStream * stream,
|
||||||
|
const gchar * encoding_name,
|
||||||
|
gsize * pt_len);
|
||||||
GstCaps * transport_stream_get_caps_for_pt (TransportStream * stream,
|
GstCaps * transport_stream_get_caps_for_pt (TransportStream * stream,
|
||||||
guint pt);
|
guint pt);
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,8 @@
|
||||||
# include "config.h"
|
# include "config.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "gstwebrtcbin.h"
|
#include "gstwebrtcbin.h"
|
||||||
|
|
||||||
|
@ -53,7 +55,22 @@ _find_pad_template (GstElement * element, GstPadDirection direction,
|
||||||
}
|
}
|
||||||
|
|
||||||
GstSDPMessage *
|
GstSDPMessage *
|
||||||
_get_latest_sdp (GstWebRTCBin * webrtc)
|
_get_latest_offer (GstWebRTCBin * webrtc)
|
||||||
|
{
|
||||||
|
if (webrtc->current_local_description &&
|
||||||
|
webrtc->current_local_description->type == GST_WEBRTC_SDP_TYPE_OFFER) {
|
||||||
|
return webrtc->current_local_description->sdp;
|
||||||
|
}
|
||||||
|
if (webrtc->current_remote_description &&
|
||||||
|
webrtc->current_remote_description->type == GST_WEBRTC_SDP_TYPE_OFFER) {
|
||||||
|
return webrtc->current_remote_description->sdp;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
GstSDPMessage *
|
||||||
|
_get_latest_answer (GstWebRTCBin * webrtc)
|
||||||
{
|
{
|
||||||
if (webrtc->current_local_description &&
|
if (webrtc->current_local_description &&
|
||||||
webrtc->current_local_description->type == GST_WEBRTC_SDP_TYPE_ANSWER) {
|
webrtc->current_local_description->type == GST_WEBRTC_SDP_TYPE_ANSWER) {
|
||||||
|
@ -63,14 +80,19 @@ _get_latest_sdp (GstWebRTCBin * webrtc)
|
||||||
webrtc->current_remote_description->type == GST_WEBRTC_SDP_TYPE_ANSWER) {
|
webrtc->current_remote_description->type == GST_WEBRTC_SDP_TYPE_ANSWER) {
|
||||||
return webrtc->current_remote_description->sdp;
|
return webrtc->current_remote_description->sdp;
|
||||||
}
|
}
|
||||||
if (webrtc->current_local_description &&
|
|
||||||
webrtc->current_local_description->type == GST_WEBRTC_SDP_TYPE_OFFER) {
|
return NULL;
|
||||||
return webrtc->current_local_description->sdp;
|
}
|
||||||
}
|
|
||||||
if (webrtc->current_remote_description &&
|
GstSDPMessage *
|
||||||
webrtc->current_remote_description->type == GST_WEBRTC_SDP_TYPE_OFFER) {
|
_get_latest_sdp (GstWebRTCBin * webrtc)
|
||||||
return webrtc->current_remote_description->sdp;
|
{
|
||||||
}
|
GstSDPMessage *ret = NULL;
|
||||||
|
|
||||||
|
if ((ret = _get_latest_answer (webrtc)))
|
||||||
|
return ret;
|
||||||
|
if ((ret = _get_latest_offer (webrtc)))
|
||||||
|
return ret;
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -142,3 +164,31 @@ _g_checksum_to_webrtc_string (GChecksumType type)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GstCaps *
|
||||||
|
_rtp_caps_from_media (const GstSDPMedia * media)
|
||||||
|
{
|
||||||
|
GstCaps *ret;
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
ret = gst_caps_new_empty ();
|
||||||
|
for (i = 0; i < gst_sdp_media_formats_len (media); i++) {
|
||||||
|
guint pt = atoi (gst_sdp_media_get_format (media, i));
|
||||||
|
GstCaps *caps;
|
||||||
|
|
||||||
|
caps = gst_sdp_media_get_caps_from_media (media, pt);
|
||||||
|
|
||||||
|
/* gst_sdp_media_get_caps_from_media() produces caps with name
|
||||||
|
* "application/x-unknown" which will fail intersection with
|
||||||
|
* "application/x-rtp" caps so mangle the returns caps to have the
|
||||||
|
* correct name here */
|
||||||
|
for (j = 0; j < gst_caps_get_size (caps); j++) {
|
||||||
|
GstStructure *s = gst_caps_get_structure (caps, j);
|
||||||
|
gst_structure_set_name (s, "application/x-rtp");
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_caps_append (ret, caps);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
|
@ -47,6 +47,8 @@ GstPadTemplate * _find_pad_template (GstElement * element,
|
||||||
const gchar * name);
|
const gchar * name);
|
||||||
|
|
||||||
GstSDPMessage * _get_latest_sdp (GstWebRTCBin * webrtc);
|
GstSDPMessage * _get_latest_sdp (GstWebRTCBin * webrtc);
|
||||||
|
GstSDPMessage * _get_latest_offer (GstWebRTCBin * webrtc);
|
||||||
|
GstSDPMessage * _get_latest_answer (GstWebRTCBin * webrtc);
|
||||||
|
|
||||||
GstWebRTCICEStream * _find_ice_stream_for_session (GstWebRTCBin * webrtc,
|
GstWebRTCICEStream * _find_ice_stream_for_session (GstWebRTCBin * webrtc,
|
||||||
guint session_id);
|
guint session_id);
|
||||||
|
@ -74,6 +76,8 @@ G_GNUC_INTERNAL
|
||||||
gchar * _enum_value_to_string (GType type, guint value);
|
gchar * _enum_value_to_string (GType type, guint value);
|
||||||
G_GNUC_INTERNAL
|
G_GNUC_INTERNAL
|
||||||
const gchar * _g_checksum_to_webrtc_string (GChecksumType type);
|
const gchar * _g_checksum_to_webrtc_string (GChecksumType type);
|
||||||
|
G_GNUC_INTERNAL
|
||||||
|
GstCaps * _rtp_caps_from_media (const GstSDPMedia * media);
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
|
|
|
@ -212,7 +212,7 @@ _media_has_mid (const GstSDPMedia * media, guint media_idx, GError ** error)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const gchar *
|
const gchar *
|
||||||
_media_get_ice_ufrag (const GstSDPMessage * msg, guint media_idx)
|
_media_get_ice_ufrag (const GstSDPMessage * msg, guint media_idx)
|
||||||
{
|
{
|
||||||
const gchar *ice_ufrag;
|
const gchar *ice_ufrag;
|
||||||
|
@ -227,7 +227,7 @@ _media_get_ice_ufrag (const GstSDPMessage * msg, guint media_idx)
|
||||||
return ice_ufrag;
|
return ice_ufrag;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const gchar *
|
const gchar *
|
||||||
_media_get_ice_pwd (const GstSDPMessage * msg, guint media_idx)
|
_media_get_ice_pwd (const GstSDPMessage * msg, guint media_idx)
|
||||||
{
|
{
|
||||||
const gchar *ice_pwd;
|
const gchar *ice_pwd;
|
||||||
|
@ -437,11 +437,13 @@ _media_replace_direction (GstSDPMedia * media,
|
||||||
|
|
||||||
if (g_strcmp0 (attr->key, "sendonly") == 0
|
if (g_strcmp0 (attr->key, "sendonly") == 0
|
||||||
|| g_strcmp0 (attr->key, "sendrecv") == 0
|
|| g_strcmp0 (attr->key, "sendrecv") == 0
|
||||||
|| g_strcmp0 (attr->key, "recvonly") == 0) {
|
|| g_strcmp0 (attr->key, "recvonly") == 0
|
||||||
|
|| g_strcmp0 (attr->key, "inactive") == 0) {
|
||||||
GstSDPAttribute new_attr = { 0, };
|
GstSDPAttribute new_attr = { 0, };
|
||||||
GST_TRACE ("replace %s with %s", attr->key, dir_str);
|
GST_TRACE ("replace %s with %s", attr->key, dir_str);
|
||||||
gst_sdp_attribute_set (&new_attr, dir_str, "");
|
gst_sdp_attribute_set (&new_attr, dir_str, "");
|
||||||
gst_sdp_media_replace_attribute (media, i, &new_attr);
|
gst_sdp_media_replace_attribute (media, i, &new_attr);
|
||||||
|
g_free (dir_str);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -768,6 +770,21 @@ _message_media_is_datachannel (const GstSDPMessage * msg, guint media_id)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
guint
|
||||||
|
_message_get_datachannel_index (const GstSDPMessage * msg)
|
||||||
|
{
|
||||||
|
guint i;
|
||||||
|
|
||||||
|
for (i = 0; i < gst_sdp_message_medias_len (msg); i++) {
|
||||||
|
if (_message_media_is_datachannel (msg, i)) {
|
||||||
|
g_assert (i < G_MAXUINT);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return G_MAXUINT;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
_get_ice_credentials_from_sdp_media (const GstSDPMessage * sdp, guint media_idx,
|
_get_ice_credentials_from_sdp_media (const GstSDPMessage * sdp, guint media_idx,
|
||||||
gchar ** ufrag, gchar ** pwd)
|
gchar ** ufrag, gchar ** pwd)
|
||||||
|
@ -833,6 +850,8 @@ _parse_bundle (GstSDPMessage * sdp, GStrv * bundled)
|
||||||
if (!(*bundled)[0]) {
|
if (!(*bundled)[0]) {
|
||||||
GST_ERROR ("Invalid format for BUNDLE group, expected at least "
|
GST_ERROR ("Invalid format for BUNDLE group, expected at least "
|
||||||
"one mid (%s)", group);
|
"one mid (%s)", group);
|
||||||
|
g_strfreev (*bundled);
|
||||||
|
*bundled = NULL;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -89,6 +89,8 @@ void _get_ice_credentials_from_sdp_media (con
|
||||||
G_GNUC_INTERNAL
|
G_GNUC_INTERNAL
|
||||||
gboolean _message_media_is_datachannel (const GstSDPMessage * msg,
|
gboolean _message_media_is_datachannel (const GstSDPMessage * msg,
|
||||||
guint media_id);
|
guint media_id);
|
||||||
|
G_GNUC_INTERNAL
|
||||||
|
guint _message_get_datachannel_index (const GstSDPMessage * msg);
|
||||||
|
|
||||||
G_GNUC_INTERNAL
|
G_GNUC_INTERNAL
|
||||||
gboolean _get_bundle_index (GstSDPMessage * sdp,
|
gboolean _get_bundle_index (GstSDPMessage * sdp,
|
||||||
|
@ -98,4 +100,11 @@ G_GNUC_INTERNAL
|
||||||
gboolean _parse_bundle (GstSDPMessage * sdp,
|
gboolean _parse_bundle (GstSDPMessage * sdp,
|
||||||
GStrv * bundled);
|
GStrv * bundled);
|
||||||
|
|
||||||
|
G_GNUC_INTERNAL
|
||||||
|
const gchar * _media_get_ice_pwd (const GstSDPMessage * msg,
|
||||||
|
guint media_idx);
|
||||||
|
G_GNUC_INTERNAL
|
||||||
|
const gchar * _media_get_ice_ufrag (const GstSDPMessage * msg,
|
||||||
|
guint media_idx);
|
||||||
|
|
||||||
#endif /* __WEBRTC_UTILS_H__ */
|
#endif /* __WEBRTC_UTILS_H__ */
|
||||||
|
|
|
@ -25,9 +25,14 @@
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "webrtctransceiver.h"
|
#include "webrtctransceiver.h"
|
||||||
|
|
||||||
|
#define GST_CAT_DEFAULT webrtc_transceiver_debug
|
||||||
|
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
|
||||||
|
|
||||||
#define webrtc_transceiver_parent_class parent_class
|
#define webrtc_transceiver_parent_class parent_class
|
||||||
G_DEFINE_TYPE (WebRTCTransceiver, webrtc_transceiver,
|
G_DEFINE_TYPE_WITH_CODE (WebRTCTransceiver, webrtc_transceiver,
|
||||||
GST_TYPE_WEBRTC_RTP_TRANSCEIVER);
|
GST_TYPE_WEBRTC_RTP_TRANSCEIVER,
|
||||||
|
GST_DEBUG_CATEGORY_INIT (webrtc_transceiver_debug,
|
||||||
|
"webrtctransceiver", 0, "webrtctransceiver"););
|
||||||
|
|
||||||
#define DEFAULT_FEC_TYPE GST_WEBRTC_FEC_TYPE_NONE
|
#define DEFAULT_FEC_TYPE GST_WEBRTC_FEC_TYPE_NONE
|
||||||
#define DEFAULT_DO_NACK FALSE
|
#define DEFAULT_DO_NACK FALSE
|
||||||
|
@ -172,6 +177,8 @@ webrtc_transceiver_finalize (GObject * object)
|
||||||
gst_structure_free (trans->local_rtx_ssrc_map);
|
gst_structure_free (trans->local_rtx_ssrc_map);
|
||||||
trans->local_rtx_ssrc_map = NULL;
|
trans->local_rtx_ssrc_map = NULL;
|
||||||
|
|
||||||
|
gst_caps_replace (&trans->last_configured_caps, NULL);
|
||||||
|
|
||||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,8 @@ struct _WebRTCTransceiver
|
||||||
GstWebRTCFECType fec_type;
|
GstWebRTCFECType fec_type;
|
||||||
guint fec_percentage;
|
guint fec_percentage;
|
||||||
gboolean do_nack;
|
gboolean do_nack;
|
||||||
|
|
||||||
|
GstCaps *last_configured_caps;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _WebRTCTransceiverClass
|
struct _WebRTCTransceiverClass
|
||||||
|
|
|
@ -39,7 +39,7 @@ GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
|
||||||
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstWebRTCRTPTransceiver,
|
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstWebRTCRTPTransceiver,
|
||||||
gst_webrtc_rtp_transceiver, GST_TYPE_OBJECT,
|
gst_webrtc_rtp_transceiver, GST_TYPE_OBJECT,
|
||||||
GST_DEBUG_CATEGORY_INIT (gst_webrtc_rtp_transceiver_debug,
|
GST_DEBUG_CATEGORY_INIT (gst_webrtc_rtp_transceiver_debug,
|
||||||
"webrtctransceiver", 0, "webrtctransceiver");
|
"webrtcrtptransceiver", 0, "webrtcrtptransceiver");
|
||||||
);
|
);
|
||||||
|
|
||||||
enum
|
enum
|
||||||
|
|
|
@ -41,6 +41,8 @@
|
||||||
#define TEST_GET_OFFEROR(t) (TEST_IS_OFFER_ELEMENT(t, t->webrtc1) ? (t)->webrtc1 : t->webrtc2)
|
#define TEST_GET_OFFEROR(t) (TEST_IS_OFFER_ELEMENT(t, t->webrtc1) ? (t)->webrtc1 : t->webrtc2)
|
||||||
#define TEST_GET_ANSWERER(t) (TEST_IS_OFFER_ELEMENT(t, t->webrtc1) ? (t)->webrtc2 : t->webrtc1)
|
#define TEST_GET_ANSWERER(t) (TEST_IS_OFFER_ELEMENT(t, t->webrtc1) ? (t)->webrtc2 : t->webrtc1)
|
||||||
|
|
||||||
|
#define TEST_SDP_IS_LOCAL(t, e, d) ((TEST_IS_OFFER_ELEMENT (t, e) ^ ((d)->type == GST_WEBRTC_SDP_TYPE_OFFER)) == 0)
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
STATE_NEW,
|
STATE_NEW,
|
||||||
|
@ -626,12 +628,12 @@ _pad_added_fakesink (struct test_webrtc *t, GstElement * element,
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
_count_num_sdp_media (struct test_webrtc *t, GstElement * element,
|
on_negotiation_needed_hit (struct test_webrtc *t, GstElement * element,
|
||||||
GstWebRTCSessionDescription * desc, gpointer user_data)
|
gpointer user_data)
|
||||||
{
|
{
|
||||||
guint expected = GPOINTER_TO_UINT (user_data);
|
guint *flag = (guint *) user_data;
|
||||||
|
|
||||||
fail_unless_equals_int (gst_sdp_message_medias_len (desc->sdp), expected);
|
*flag = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef void (*ValidateSDPFunc) (struct test_webrtc * t, GstElement * element,
|
typedef void (*ValidateSDPFunc) (struct test_webrtc * t, GstElement * element,
|
||||||
|
@ -645,6 +647,9 @@ struct validate_sdp
|
||||||
struct validate_sdp *next;
|
struct validate_sdp *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define VAL_SDP_INIT(name,func,data,next) \
|
||||||
|
struct validate_sdp name = { func, data, next }
|
||||||
|
|
||||||
static GstWebRTCSessionDescription *
|
static GstWebRTCSessionDescription *
|
||||||
_check_validate_sdp (struct test_webrtc *t, GstElement * element,
|
_check_validate_sdp (struct test_webrtc *t, GstElement * element,
|
||||||
GstPromise * promise, gpointer user_data)
|
GstPromise * promise, gpointer user_data)
|
||||||
|
@ -698,13 +703,20 @@ test_validate_sdp (struct test_webrtc *t, struct validate_sdp *offer,
|
||||||
fail_unless (t->state == STATE_ANSWER_CREATED);
|
fail_unless (t->state == STATE_ANSWER_CREATED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_count_num_sdp_media (struct test_webrtc *t, GstElement * element,
|
||||||
|
GstWebRTCSessionDescription * desc, gpointer user_data)
|
||||||
|
{
|
||||||
|
guint expected = GPOINTER_TO_UINT (user_data);
|
||||||
|
|
||||||
|
fail_unless_equals_int (gst_sdp_message_medias_len (desc->sdp), expected);
|
||||||
|
}
|
||||||
|
|
||||||
GST_START_TEST (test_sdp_no_media)
|
GST_START_TEST (test_sdp_no_media)
|
||||||
{
|
{
|
||||||
struct test_webrtc *t = test_webrtc_new ();
|
struct test_webrtc *t = test_webrtc_new ();
|
||||||
struct validate_sdp offer =
|
VAL_SDP_INIT (offer, _count_num_sdp_media, GUINT_TO_POINTER (0), NULL);
|
||||||
{ _count_num_sdp_media, GUINT_TO_POINTER (0), NULL };
|
VAL_SDP_INIT (answer, _count_num_sdp_media, GUINT_TO_POINTER (0), NULL);
|
||||||
struct validate_sdp answer =
|
|
||||||
{ _count_num_sdp_media, GUINT_TO_POINTER (0), NULL };
|
|
||||||
|
|
||||||
/* check that a no stream connection creates 0 media sections */
|
/* check that a no stream connection creates 0 media sections */
|
||||||
|
|
||||||
|
@ -756,10 +768,8 @@ create_audio_test (void)
|
||||||
GST_START_TEST (test_audio)
|
GST_START_TEST (test_audio)
|
||||||
{
|
{
|
||||||
struct test_webrtc *t = create_audio_test ();
|
struct test_webrtc *t = create_audio_test ();
|
||||||
struct validate_sdp offer =
|
VAL_SDP_INIT (offer, _count_num_sdp_media, GUINT_TO_POINTER (1), NULL);
|
||||||
{ _count_num_sdp_media, GUINT_TO_POINTER (1), NULL };
|
VAL_SDP_INIT (answer, _count_num_sdp_media, GUINT_TO_POINTER (1), NULL);
|
||||||
struct validate_sdp answer =
|
|
||||||
{ _count_num_sdp_media, GUINT_TO_POINTER (1), NULL };
|
|
||||||
|
|
||||||
/* check that a single stream connection creates the associated number
|
/* check that a single stream connection creates the associated number
|
||||||
* of media sections */
|
* of media sections */
|
||||||
|
@ -786,10 +796,8 @@ create_audio_video_test (void)
|
||||||
GST_START_TEST (test_audio_video)
|
GST_START_TEST (test_audio_video)
|
||||||
{
|
{
|
||||||
struct test_webrtc *t = create_audio_video_test ();
|
struct test_webrtc *t = create_audio_video_test ();
|
||||||
struct validate_sdp offer =
|
VAL_SDP_INIT (offer, _count_num_sdp_media, GUINT_TO_POINTER (2), NULL);
|
||||||
{ _count_num_sdp_media, GUINT_TO_POINTER (2), NULL };
|
VAL_SDP_INIT (answer, _count_num_sdp_media, GUINT_TO_POINTER (2), NULL);
|
||||||
struct validate_sdp answer =
|
|
||||||
{ _count_num_sdp_media, GUINT_TO_POINTER (2), NULL };
|
|
||||||
|
|
||||||
/* check that a dual stream connection creates the associated number
|
/* check that a dual stream connection creates the associated number
|
||||||
* of media sections */
|
* of media sections */
|
||||||
|
@ -850,15 +858,14 @@ GST_START_TEST (test_media_direction)
|
||||||
struct test_webrtc *t = create_audio_video_test ();
|
struct test_webrtc *t = create_audio_video_test ();
|
||||||
const gchar *expected_offer[] = { "sendrecv", "sendrecv" };
|
const gchar *expected_offer[] = { "sendrecv", "sendrecv" };
|
||||||
const gchar *expected_answer[] = { "sendrecv", "recvonly" };
|
const gchar *expected_answer[] = { "sendrecv", "recvonly" };
|
||||||
struct validate_sdp offer_direction =
|
|
||||||
{ on_sdp_media_direction, expected_offer, NULL };
|
|
||||||
struct validate_sdp offer =
|
|
||||||
{ _count_num_sdp_media, GUINT_TO_POINTER (2), &offer_direction };
|
|
||||||
struct validate_sdp answer_direction =
|
|
||||||
{ on_sdp_media_direction, expected_answer, NULL };
|
|
||||||
struct validate_sdp answer =
|
|
||||||
{ _count_num_sdp_media, GUINT_TO_POINTER (2), &answer_direction };
|
|
||||||
GstHarness *h;
|
GstHarness *h;
|
||||||
|
VAL_SDP_INIT (offer_direction, on_sdp_media_direction, expected_offer, NULL);
|
||||||
|
VAL_SDP_INIT (offer, _count_num_sdp_media, GUINT_TO_POINTER (2),
|
||||||
|
&offer_direction);
|
||||||
|
VAL_SDP_INIT (answer_direction, on_sdp_media_direction, expected_answer,
|
||||||
|
NULL);
|
||||||
|
VAL_SDP_INIT (answer, _count_num_sdp_media, GUINT_TO_POINTER (2),
|
||||||
|
&answer_direction);
|
||||||
|
|
||||||
/* check the default media directions for transceivers */
|
/* check the default media directions for transceivers */
|
||||||
|
|
||||||
|
@ -905,9 +912,8 @@ on_sdp_media_payload_types (struct test_webrtc *t, GstElement * element,
|
||||||
GST_START_TEST (test_payload_types)
|
GST_START_TEST (test_payload_types)
|
||||||
{
|
{
|
||||||
struct test_webrtc *t = create_audio_video_test ();
|
struct test_webrtc *t = create_audio_video_test ();
|
||||||
struct validate_sdp payloads = { on_sdp_media_payload_types, NULL, NULL };
|
VAL_SDP_INIT (payloads, on_sdp_media_payload_types, NULL, NULL);
|
||||||
struct validate_sdp offer =
|
VAL_SDP_INIT (offer, _count_num_sdp_media, GUINT_TO_POINTER (2), &payloads);
|
||||||
{ _count_num_sdp_media, GUINT_TO_POINTER (2), &payloads };
|
|
||||||
GstWebRTCRTPTransceiver *trans;
|
GstWebRTCRTPTransceiver *trans;
|
||||||
GArray *transceivers;
|
GArray *transceivers;
|
||||||
|
|
||||||
|
@ -956,8 +962,8 @@ GST_START_TEST (test_media_setup)
|
||||||
struct test_webrtc *t = create_audio_test ();
|
struct test_webrtc *t = create_audio_test ();
|
||||||
const gchar *expected_offer[] = { "actpass" };
|
const gchar *expected_offer[] = { "actpass" };
|
||||||
const gchar *expected_answer[] = { "active" };
|
const gchar *expected_answer[] = { "active" };
|
||||||
struct validate_sdp offer = { on_sdp_media_setup, expected_offer, NULL };
|
VAL_SDP_INIT (offer, on_sdp_media_setup, expected_offer, NULL);
|
||||||
struct validate_sdp answer = { on_sdp_media_setup, expected_answer, NULL };
|
VAL_SDP_INIT (answer, on_sdp_media_setup, expected_answer, NULL);
|
||||||
|
|
||||||
/* check the default dtls setup negotiation values */
|
/* check the default dtls setup negotiation values */
|
||||||
test_validate_sdp (t, &offer, &answer);
|
test_validate_sdp (t, &offer, &answer);
|
||||||
|
@ -1333,9 +1339,8 @@ GST_START_TEST (test_add_recvonly_transceiver)
|
||||||
GstWebRTCRTPTransceiver *trans;
|
GstWebRTCRTPTransceiver *trans;
|
||||||
const gchar *expected_offer[] = { "recvonly" };
|
const gchar *expected_offer[] = { "recvonly" };
|
||||||
const gchar *expected_answer[] = { "sendonly" };
|
const gchar *expected_answer[] = { "sendonly" };
|
||||||
struct validate_sdp offer = { on_sdp_media_direction, expected_offer, NULL };
|
VAL_SDP_INIT (offer, on_sdp_media_direction, expected_offer, NULL);
|
||||||
struct validate_sdp answer =
|
VAL_SDP_INIT (answer, on_sdp_media_direction, expected_answer, NULL);
|
||||||
{ on_sdp_media_direction, expected_answer, NULL };
|
|
||||||
GstCaps *caps;
|
GstCaps *caps;
|
||||||
GstHarness *h;
|
GstHarness *h;
|
||||||
|
|
||||||
|
@ -1372,9 +1377,8 @@ GST_START_TEST (test_recvonly_sendonly)
|
||||||
GstWebRTCRTPTransceiver *trans;
|
GstWebRTCRTPTransceiver *trans;
|
||||||
const gchar *expected_offer[] = { "recvonly", "sendonly" };
|
const gchar *expected_offer[] = { "recvonly", "sendonly" };
|
||||||
const gchar *expected_answer[] = { "sendonly", "recvonly" };
|
const gchar *expected_answer[] = { "sendonly", "recvonly" };
|
||||||
struct validate_sdp offer = { on_sdp_media_direction, expected_offer, NULL };
|
VAL_SDP_INIT (offer, on_sdp_media_direction, expected_offer, NULL);
|
||||||
struct validate_sdp answer =
|
VAL_SDP_INIT (answer, on_sdp_media_direction, expected_answer, NULL);
|
||||||
{ on_sdp_media_direction, expected_answer, NULL };
|
|
||||||
GstCaps *caps;
|
GstCaps *caps;
|
||||||
GstHarness *h;
|
GstHarness *h;
|
||||||
GArray *transceivers;
|
GArray *transceivers;
|
||||||
|
@ -1446,8 +1450,8 @@ GST_START_TEST (test_data_channel_create)
|
||||||
{
|
{
|
||||||
struct test_webrtc *t = test_webrtc_new ();
|
struct test_webrtc *t = test_webrtc_new ();
|
||||||
GObject *channel = NULL;
|
GObject *channel = NULL;
|
||||||
struct validate_sdp offer = { on_sdp_has_datachannel, NULL, NULL };
|
VAL_SDP_INIT (offer, on_sdp_has_datachannel, NULL, NULL);
|
||||||
struct validate_sdp answer = { on_sdp_has_datachannel, NULL, NULL };
|
VAL_SDP_INIT (answer, on_sdp_has_datachannel, NULL, NULL);
|
||||||
gchar *label;
|
gchar *label;
|
||||||
|
|
||||||
t->on_negotiation_needed = NULL;
|
t->on_negotiation_needed = NULL;
|
||||||
|
@ -1500,8 +1504,8 @@ GST_START_TEST (test_data_channel_remote_notify)
|
||||||
{
|
{
|
||||||
struct test_webrtc *t = test_webrtc_new ();
|
struct test_webrtc *t = test_webrtc_new ();
|
||||||
GObject *channel = NULL;
|
GObject *channel = NULL;
|
||||||
struct validate_sdp offer = { on_sdp_has_datachannel, NULL, NULL };
|
VAL_SDP_INIT (offer, on_sdp_has_datachannel, NULL, NULL);
|
||||||
struct validate_sdp answer = { on_sdp_has_datachannel, NULL, NULL };
|
VAL_SDP_INIT (answer, on_sdp_has_datachannel, NULL, NULL);
|
||||||
|
|
||||||
t->on_negotiation_needed = NULL;
|
t->on_negotiation_needed = NULL;
|
||||||
t->offer_data = &offer;
|
t->offer_data = &offer;
|
||||||
|
@ -1575,8 +1579,8 @@ GST_START_TEST (test_data_channel_transfer_string)
|
||||||
{
|
{
|
||||||
struct test_webrtc *t = test_webrtc_new ();
|
struct test_webrtc *t = test_webrtc_new ();
|
||||||
GObject *channel = NULL;
|
GObject *channel = NULL;
|
||||||
struct validate_sdp offer = { on_sdp_has_datachannel, NULL, NULL };
|
VAL_SDP_INIT (offer, on_sdp_has_datachannel, NULL, NULL);
|
||||||
struct validate_sdp answer = { on_sdp_has_datachannel, NULL, NULL };
|
VAL_SDP_INIT (answer, on_sdp_has_datachannel, NULL, NULL);
|
||||||
|
|
||||||
t->on_negotiation_needed = NULL;
|
t->on_negotiation_needed = NULL;
|
||||||
t->offer_data = &offer;
|
t->offer_data = &offer;
|
||||||
|
@ -1657,8 +1661,8 @@ GST_START_TEST (test_data_channel_transfer_data)
|
||||||
{
|
{
|
||||||
struct test_webrtc *t = test_webrtc_new ();
|
struct test_webrtc *t = test_webrtc_new ();
|
||||||
GObject *channel = NULL;
|
GObject *channel = NULL;
|
||||||
struct validate_sdp offer = { on_sdp_has_datachannel, NULL, NULL };
|
VAL_SDP_INIT (offer, on_sdp_has_datachannel, NULL, NULL);
|
||||||
struct validate_sdp answer = { on_sdp_has_datachannel, NULL, NULL };
|
VAL_SDP_INIT (answer, on_sdp_has_datachannel, NULL, NULL);
|
||||||
|
|
||||||
t->on_negotiation_needed = NULL;
|
t->on_negotiation_needed = NULL;
|
||||||
t->offer_data = &offer;
|
t->offer_data = &offer;
|
||||||
|
@ -1715,8 +1719,8 @@ GST_START_TEST (test_data_channel_create_after_negotiate)
|
||||||
{
|
{
|
||||||
struct test_webrtc *t = test_webrtc_new ();
|
struct test_webrtc *t = test_webrtc_new ();
|
||||||
GObject *channel = NULL;
|
GObject *channel = NULL;
|
||||||
struct validate_sdp offer = { on_sdp_has_datachannel, NULL, NULL };
|
VAL_SDP_INIT (offer, on_sdp_has_datachannel, NULL, NULL);
|
||||||
struct validate_sdp answer = { on_sdp_has_datachannel, NULL, NULL };
|
VAL_SDP_INIT (answer, on_sdp_has_datachannel, NULL, NULL);
|
||||||
|
|
||||||
t->on_negotiation_needed = NULL;
|
t->on_negotiation_needed = NULL;
|
||||||
t->offer_data = &offer;
|
t->offer_data = &offer;
|
||||||
|
@ -1776,8 +1780,8 @@ GST_START_TEST (test_data_channel_low_threshold)
|
||||||
{
|
{
|
||||||
struct test_webrtc *t = test_webrtc_new ();
|
struct test_webrtc *t = test_webrtc_new ();
|
||||||
GObject *channel = NULL;
|
GObject *channel = NULL;
|
||||||
struct validate_sdp offer = { on_sdp_has_datachannel, NULL, NULL };
|
VAL_SDP_INIT (offer, on_sdp_has_datachannel, NULL, NULL);
|
||||||
struct validate_sdp answer = { on_sdp_has_datachannel, NULL, NULL };
|
VAL_SDP_INIT (answer, on_sdp_has_datachannel, NULL, NULL);
|
||||||
|
|
||||||
t->on_negotiation_needed = NULL;
|
t->on_negotiation_needed = NULL;
|
||||||
t->offer_data = &offer;
|
t->offer_data = &offer;
|
||||||
|
@ -1849,8 +1853,8 @@ GST_START_TEST (test_data_channel_max_message_size)
|
||||||
{
|
{
|
||||||
struct test_webrtc *t = test_webrtc_new ();
|
struct test_webrtc *t = test_webrtc_new ();
|
||||||
GObject *channel = NULL;
|
GObject *channel = NULL;
|
||||||
struct validate_sdp offer = { on_sdp_has_datachannel, NULL, NULL };
|
VAL_SDP_INIT (offer, on_sdp_has_datachannel, NULL, NULL);
|
||||||
struct validate_sdp answer = { on_sdp_has_datachannel, NULL, NULL };
|
VAL_SDP_INIT (answer, on_sdp_has_datachannel, NULL, NULL);
|
||||||
|
|
||||||
t->on_negotiation_needed = NULL;
|
t->on_negotiation_needed = NULL;
|
||||||
t->offer_data = &offer;
|
t->offer_data = &offer;
|
||||||
|
@ -1904,8 +1908,8 @@ GST_START_TEST (test_data_channel_pre_negotiated)
|
||||||
{
|
{
|
||||||
struct test_webrtc *t = test_webrtc_new ();
|
struct test_webrtc *t = test_webrtc_new ();
|
||||||
GObject *channel1 = NULL, *channel2 = NULL;
|
GObject *channel1 = NULL, *channel2 = NULL;
|
||||||
struct validate_sdp offer = { on_sdp_has_datachannel, NULL, NULL };
|
VAL_SDP_INIT (offer, on_sdp_has_datachannel, NULL, NULL);
|
||||||
struct validate_sdp answer = { on_sdp_has_datachannel, NULL, NULL };
|
VAL_SDP_INIT (answer, on_sdp_has_datachannel, NULL, NULL);
|
||||||
GstStructure *s;
|
GstStructure *s;
|
||||||
gint n_ready = 0;
|
gint n_ready = 0;
|
||||||
|
|
||||||
|
@ -2028,17 +2032,16 @@ GST_START_TEST (test_bundle_audio_video_max_bundle_max_bundle)
|
||||||
const gchar *offer_bundle_only[] = { "video1", NULL };
|
const gchar *offer_bundle_only[] = { "video1", NULL };
|
||||||
const gchar *answer_bundle_only[] = { NULL };
|
const gchar *answer_bundle_only[] = { NULL };
|
||||||
|
|
||||||
struct validate_sdp count =
|
VAL_SDP_INIT (count, _count_num_sdp_media, GUINT_TO_POINTER (2), NULL);
|
||||||
{ _count_num_sdp_media, GUINT_TO_POINTER (2), NULL };
|
VAL_SDP_INIT (bundle_tag, _check_bundle_tag, bundle, &count);
|
||||||
struct validate_sdp bundle_tag = { _check_bundle_tag, bundle, &count };
|
VAL_SDP_INIT (offer_non_reject, _count_non_rejected_media,
|
||||||
struct validate_sdp offer_non_reject =
|
GUINT_TO_POINTER (1), &bundle_tag);
|
||||||
{ _count_non_rejected_media, GUINT_TO_POINTER (1), &bundle_tag };
|
VAL_SDP_INIT (answer_non_reject, _count_non_rejected_media,
|
||||||
struct validate_sdp answer_non_reject =
|
GUINT_TO_POINTER (2), &bundle_tag);
|
||||||
{ _count_non_rejected_media, GUINT_TO_POINTER (2), &bundle_tag };
|
VAL_SDP_INIT (offer, _check_bundle_only_media, &offer_bundle_only,
|
||||||
struct validate_sdp offer =
|
&offer_non_reject);
|
||||||
{ _check_bundle_only_media, &offer_bundle_only, &offer_non_reject };
|
VAL_SDP_INIT (answer, _check_bundle_only_media, &answer_bundle_only,
|
||||||
struct validate_sdp answer =
|
&answer_non_reject);
|
||||||
{ _check_bundle_only_media, &answer_bundle_only, &answer_non_reject };
|
|
||||||
|
|
||||||
/* We set a max-bundle policy on the offering webrtcbin,
|
/* We set a max-bundle policy on the offering webrtcbin,
|
||||||
* this means that all the offered medias should be part
|
* this means that all the offered medias should be part
|
||||||
|
@ -2068,13 +2071,12 @@ GST_START_TEST (test_bundle_audio_video_max_compat_max_bundle)
|
||||||
const gchar *bundle[] = { "audio0", "video1", NULL };
|
const gchar *bundle[] = { "audio0", "video1", NULL };
|
||||||
const gchar *bundle_only[] = { NULL };
|
const gchar *bundle_only[] = { NULL };
|
||||||
|
|
||||||
struct validate_sdp count =
|
VAL_SDP_INIT (count, _count_num_sdp_media, GUINT_TO_POINTER (2), NULL);
|
||||||
{ _count_num_sdp_media, GUINT_TO_POINTER (2), NULL };
|
VAL_SDP_INIT (bundle_tag, _check_bundle_tag, bundle, &count);
|
||||||
struct validate_sdp bundle_tag = { _check_bundle_tag, bundle, &count };
|
VAL_SDP_INIT (count_non_reject, _count_non_rejected_media,
|
||||||
struct validate_sdp count_non_reject =
|
GUINT_TO_POINTER (2), &bundle_tag);
|
||||||
{ _count_non_rejected_media, GUINT_TO_POINTER (2), &bundle_tag };
|
VAL_SDP_INIT (bundle_sdp, _check_bundle_only_media, &bundle_only,
|
||||||
struct validate_sdp bundle_sdp =
|
&count_non_reject);
|
||||||
{ _check_bundle_only_media, &bundle_only, &count_non_reject };
|
|
||||||
|
|
||||||
/* We set a max-compat policy on the offering webrtcbin,
|
/* We set a max-compat policy on the offering webrtcbin,
|
||||||
* this means that all the offered medias should be part
|
* this means that all the offered medias should be part
|
||||||
|
@ -2106,18 +2108,17 @@ GST_START_TEST (test_bundle_audio_video_max_bundle_none)
|
||||||
const gchar *answer_bundle[] = { NULL };
|
const gchar *answer_bundle[] = { NULL };
|
||||||
const gchar *answer_bundle_only[] = { NULL };
|
const gchar *answer_bundle_only[] = { NULL };
|
||||||
|
|
||||||
struct validate_sdp count =
|
VAL_SDP_INIT (count, _count_num_sdp_media, GUINT_TO_POINTER (2), NULL);
|
||||||
{ _count_num_sdp_media, GUINT_TO_POINTER (2), NULL };
|
VAL_SDP_INIT (count_non_reject, _count_non_rejected_media,
|
||||||
struct validate_sdp count_non_reject =
|
GUINT_TO_POINTER (1), &count);
|
||||||
{ _count_non_rejected_media, GUINT_TO_POINTER (1), &count };
|
VAL_SDP_INIT (offer_bundle_tag, _check_bundle_tag, offer_bundle,
|
||||||
struct validate_sdp offer_bundle_tag =
|
&count_non_reject);
|
||||||
{ _check_bundle_tag, offer_bundle, &count_non_reject };
|
VAL_SDP_INIT (answer_bundle_tag, _check_bundle_tag, answer_bundle,
|
||||||
struct validate_sdp answer_bundle_tag =
|
&count_non_reject);
|
||||||
{ _check_bundle_tag, answer_bundle, &count_non_reject };
|
VAL_SDP_INIT (offer, _check_bundle_only_media, &offer_bundle_only,
|
||||||
struct validate_sdp offer =
|
&offer_bundle_tag);
|
||||||
{ _check_bundle_only_media, &offer_bundle_only, &offer_bundle_tag };
|
VAL_SDP_INIT (answer, _check_bundle_only_media, &answer_bundle_only,
|
||||||
struct validate_sdp answer =
|
&answer_bundle_tag);
|
||||||
{ _check_bundle_only_media, &answer_bundle_only, &answer_bundle_tag };
|
|
||||||
|
|
||||||
/* We set a max-bundle policy on the offering webrtcbin,
|
/* We set a max-bundle policy on the offering webrtcbin,
|
||||||
* this means that all the offered medias should be part
|
* this means that all the offered medias should be part
|
||||||
|
@ -2148,17 +2149,16 @@ GST_START_TEST (test_bundle_audio_video_data)
|
||||||
const gchar *answer_bundle_only[] = { NULL };
|
const gchar *answer_bundle_only[] = { NULL };
|
||||||
GObject *channel = NULL;
|
GObject *channel = NULL;
|
||||||
|
|
||||||
struct validate_sdp count =
|
VAL_SDP_INIT (count, _count_num_sdp_media, GUINT_TO_POINTER (3), NULL);
|
||||||
{ _count_num_sdp_media, GUINT_TO_POINTER (3), NULL };
|
VAL_SDP_INIT (bundle_tag, _check_bundle_tag, bundle, &count);
|
||||||
struct validate_sdp bundle_tag = { _check_bundle_tag, bundle, &count };
|
VAL_SDP_INIT (offer_non_reject, _count_non_rejected_media,
|
||||||
struct validate_sdp offer_non_reject =
|
GUINT_TO_POINTER (1), &bundle_tag);
|
||||||
{ _count_non_rejected_media, GUINT_TO_POINTER (1), &bundle_tag };
|
VAL_SDP_INIT (answer_non_reject, _count_non_rejected_media,
|
||||||
struct validate_sdp answer_non_reject =
|
GUINT_TO_POINTER (3), &bundle_tag);
|
||||||
{ _count_non_rejected_media, GUINT_TO_POINTER (3), &bundle_tag };
|
VAL_SDP_INIT (offer, _check_bundle_only_media, &offer_bundle_only,
|
||||||
struct validate_sdp offer =
|
&offer_non_reject);
|
||||||
{ _check_bundle_only_media, &offer_bundle_only, &offer_non_reject };
|
VAL_SDP_INIT (answer, _check_bundle_only_media, &answer_bundle_only,
|
||||||
struct validate_sdp answer =
|
&answer_non_reject);
|
||||||
{ _check_bundle_only_media, &answer_bundle_only, &answer_non_reject };
|
|
||||||
|
|
||||||
/* We set a max-bundle policy on the offering webrtcbin,
|
/* We set a max-bundle policy on the offering webrtcbin,
|
||||||
* this means that all the offered medias should be part
|
* this means that all the offered medias should be part
|
||||||
|
@ -2196,19 +2196,23 @@ GST_START_TEST (test_duplicate_nego)
|
||||||
struct test_webrtc *t = create_audio_video_test ();
|
struct test_webrtc *t = create_audio_video_test ();
|
||||||
const gchar *expected_offer[] = { "sendrecv", "sendrecv" };
|
const gchar *expected_offer[] = { "sendrecv", "sendrecv" };
|
||||||
const gchar *expected_answer[] = { "sendrecv", "recvonly" };
|
const gchar *expected_answer[] = { "sendrecv", "recvonly" };
|
||||||
struct validate_sdp offer = { on_sdp_media_direction, expected_offer, NULL };
|
VAL_SDP_INIT (offer, on_sdp_media_direction, expected_offer, NULL);
|
||||||
struct validate_sdp answer =
|
VAL_SDP_INIT (answer, on_sdp_media_direction, expected_answer, NULL);
|
||||||
{ on_sdp_media_direction, expected_answer, NULL };
|
|
||||||
GstHarness *h;
|
GstHarness *h;
|
||||||
|
guint negotiation_flag = 0;
|
||||||
|
|
||||||
/* check that negotiating twice succeeds */
|
/* check that negotiating twice succeeds */
|
||||||
|
|
||||||
|
t->on_negotiation_needed = on_negotiation_needed_hit;
|
||||||
|
t->negotiation_data = &negotiation_flag;
|
||||||
|
|
||||||
h = gst_harness_new_with_element (t->webrtc2, "sink_0", NULL);
|
h = gst_harness_new_with_element (t->webrtc2, "sink_0", NULL);
|
||||||
add_fake_audio_src_harness (h, 96);
|
add_fake_audio_src_harness (h, 96);
|
||||||
t->harnesses = g_list_prepend (t->harnesses, h);
|
t->harnesses = g_list_prepend (t->harnesses, h);
|
||||||
|
|
||||||
t->on_negotiation_needed = NULL;
|
|
||||||
test_validate_sdp (t, &offer, &answer);
|
test_validate_sdp (t, &offer, &answer);
|
||||||
|
fail_unless_equals_int (negotiation_flag, 1);
|
||||||
|
|
||||||
test_webrtc_signal_state (t, STATE_NEGOTIATION_NEEDED);
|
test_webrtc_signal_state (t, STATE_NEGOTIATION_NEEDED);
|
||||||
test_validate_sdp (t, &offer, &answer);
|
test_validate_sdp (t, &offer, &answer);
|
||||||
|
|
||||||
|
@ -2222,9 +2226,8 @@ GST_START_TEST (test_dual_audio)
|
||||||
struct test_webrtc *t = create_audio_test ();
|
struct test_webrtc *t = create_audio_test ();
|
||||||
const gchar *expected_offer[] = { "sendrecv", "sendrecv", };
|
const gchar *expected_offer[] = { "sendrecv", "sendrecv", };
|
||||||
const gchar *expected_answer[] = { "sendrecv", "recvonly" };
|
const gchar *expected_answer[] = { "sendrecv", "recvonly" };
|
||||||
struct validate_sdp offer = { on_sdp_media_direction, expected_offer, NULL };
|
VAL_SDP_INIT (offer, on_sdp_media_direction, expected_offer, NULL);
|
||||||
struct validate_sdp answer =
|
VAL_SDP_INIT (answer, on_sdp_media_direction, expected_answer, NULL);
|
||||||
{ on_sdp_media_direction, expected_answer, NULL };
|
|
||||||
GstHarness *h;
|
GstHarness *h;
|
||||||
GstWebRTCRTPTransceiver *trans;
|
GstWebRTCRTPTransceiver *trans;
|
||||||
GArray *transceivers;
|
GArray *transceivers;
|
||||||
|
@ -2258,6 +2261,431 @@ GST_START_TEST (test_dual_audio)
|
||||||
test_webrtc_free (t);
|
test_webrtc_free (t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
|
static void
|
||||||
|
sdp_increasing_session_version (struct test_webrtc *t, GstElement * element,
|
||||||
|
GstWebRTCSessionDescription * desc, gpointer user_data)
|
||||||
|
{
|
||||||
|
GstWebRTCSessionDescription *previous;
|
||||||
|
const GstSDPOrigin *our_origin, *previous_origin;
|
||||||
|
const gchar *prop;
|
||||||
|
guint64 our_v, previous_v;
|
||||||
|
|
||||||
|
prop =
|
||||||
|
TEST_SDP_IS_LOCAL (t, element,
|
||||||
|
desc) ? "current-local-description" : "current-remote-description";
|
||||||
|
g_object_get (element, prop, &previous, NULL);
|
||||||
|
|
||||||
|
our_origin = gst_sdp_message_get_origin (desc->sdp);
|
||||||
|
previous_origin = gst_sdp_message_get_origin (previous->sdp);
|
||||||
|
|
||||||
|
our_v = g_ascii_strtoull (our_origin->sess_version, NULL, 10);
|
||||||
|
previous_v = g_ascii_strtoull (previous_origin->sess_version, NULL, 10);
|
||||||
|
|
||||||
|
ck_assert_int_lt (previous_v, our_v);
|
||||||
|
|
||||||
|
gst_webrtc_session_description_free (previous);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sdp_equal_session_id (struct test_webrtc *t, GstElement * element,
|
||||||
|
GstWebRTCSessionDescription * desc, gpointer user_data)
|
||||||
|
{
|
||||||
|
GstWebRTCSessionDescription *previous;
|
||||||
|
const GstSDPOrigin *our_origin, *previous_origin;
|
||||||
|
const gchar *prop;
|
||||||
|
|
||||||
|
prop =
|
||||||
|
TEST_SDP_IS_LOCAL (t, element,
|
||||||
|
desc) ? "current-local-description" : "current-remote-description";
|
||||||
|
g_object_get (element, prop, &previous, NULL);
|
||||||
|
|
||||||
|
our_origin = gst_sdp_message_get_origin (desc->sdp);
|
||||||
|
previous_origin = gst_sdp_message_get_origin (previous->sdp);
|
||||||
|
|
||||||
|
fail_unless_equals_string (previous_origin->sess_id, our_origin->sess_id);
|
||||||
|
gst_webrtc_session_description_free (previous);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sdp_media_equal_attribute (struct test_webrtc *t, GstElement * element,
|
||||||
|
GstWebRTCSessionDescription * desc, GstWebRTCSessionDescription * previous,
|
||||||
|
const gchar * attr)
|
||||||
|
{
|
||||||
|
guint i, n;
|
||||||
|
|
||||||
|
n = MIN (gst_sdp_message_medias_len (previous->sdp),
|
||||||
|
gst_sdp_message_medias_len (desc->sdp));
|
||||||
|
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
const GstSDPMedia *our_media, *other_media;
|
||||||
|
const gchar *our_mid, *other_mid;
|
||||||
|
|
||||||
|
our_media = gst_sdp_message_get_media (desc->sdp, i);
|
||||||
|
other_media = gst_sdp_message_get_media (previous->sdp, i);
|
||||||
|
|
||||||
|
our_mid = gst_sdp_media_get_attribute_val (our_media, attr);
|
||||||
|
other_mid = gst_sdp_media_get_attribute_val (other_media, attr);
|
||||||
|
|
||||||
|
fail_unless_equals_string (our_mid, other_mid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sdp_media_equal_mid (struct test_webrtc *t, GstElement * element,
|
||||||
|
GstWebRTCSessionDescription * desc, gpointer user_data)
|
||||||
|
{
|
||||||
|
GstWebRTCSessionDescription *previous;
|
||||||
|
const gchar *prop;
|
||||||
|
|
||||||
|
prop =
|
||||||
|
TEST_SDP_IS_LOCAL (t, element,
|
||||||
|
desc) ? "current-local-description" : "current-remote-description";
|
||||||
|
g_object_get (element, prop, &previous, NULL);
|
||||||
|
|
||||||
|
sdp_media_equal_attribute (t, element, desc, previous, "mid");
|
||||||
|
|
||||||
|
gst_webrtc_session_description_free (previous);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sdp_media_equal_ice_params (struct test_webrtc *t, GstElement * element,
|
||||||
|
GstWebRTCSessionDescription * desc, gpointer user_data)
|
||||||
|
{
|
||||||
|
GstWebRTCSessionDescription *previous;
|
||||||
|
const gchar *prop;
|
||||||
|
|
||||||
|
prop =
|
||||||
|
TEST_SDP_IS_LOCAL (t, element,
|
||||||
|
desc) ? "current-local-description" : "current-remote-description";
|
||||||
|
g_object_get (element, prop, &previous, NULL);
|
||||||
|
|
||||||
|
sdp_media_equal_attribute (t, element, desc, previous, "ice-ufrag");
|
||||||
|
sdp_media_equal_attribute (t, element, desc, previous, "ice-pwd");
|
||||||
|
|
||||||
|
gst_webrtc_session_description_free (previous);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sdp_media_equal_fingerprint (struct test_webrtc *t, GstElement * element,
|
||||||
|
GstWebRTCSessionDescription * desc, gpointer user_data)
|
||||||
|
{
|
||||||
|
GstWebRTCSessionDescription *previous;
|
||||||
|
const gchar *prop;
|
||||||
|
|
||||||
|
prop =
|
||||||
|
TEST_SDP_IS_LOCAL (t, element,
|
||||||
|
desc) ? "current-local-description" : "current-remote-description";
|
||||||
|
g_object_get (element, prop, &previous, NULL);
|
||||||
|
|
||||||
|
sdp_media_equal_attribute (t, element, desc, previous, "fingerprint");
|
||||||
|
|
||||||
|
gst_webrtc_session_description_free (previous);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_START_TEST (test_renego_add_stream)
|
||||||
|
{
|
||||||
|
struct test_webrtc *t = create_audio_video_test ();
|
||||||
|
const gchar *expected_offer[] = { "sendrecv", "sendrecv", "sendrecv" };
|
||||||
|
const gchar *expected_answer[] = { "sendrecv", "recvonly", "recvonly" };
|
||||||
|
VAL_SDP_INIT (offer, on_sdp_media_direction, expected_offer, NULL);
|
||||||
|
VAL_SDP_INIT (answer, on_sdp_media_direction, expected_answer, NULL);
|
||||||
|
VAL_SDP_INIT (renego_mid, sdp_media_equal_mid, NULL, NULL);
|
||||||
|
VAL_SDP_INIT (renego_ice_params, sdp_media_equal_ice_params, NULL,
|
||||||
|
&renego_mid);
|
||||||
|
VAL_SDP_INIT (renego_sess_id, sdp_equal_session_id, NULL, &renego_ice_params);
|
||||||
|
VAL_SDP_INIT (renego_sess_ver, sdp_increasing_session_version, NULL,
|
||||||
|
&renego_sess_id);
|
||||||
|
VAL_SDP_INIT (renego_fingerprint, sdp_media_equal_fingerprint, NULL,
|
||||||
|
&renego_sess_ver);
|
||||||
|
GstHarness *h;
|
||||||
|
|
||||||
|
/* negotiate an AV stream and then renegotiate an extra stream */
|
||||||
|
h = gst_harness_new_with_element (t->webrtc2, "sink_0", NULL);
|
||||||
|
add_fake_audio_src_harness (h, 96);
|
||||||
|
t->harnesses = g_list_prepend (t->harnesses, h);
|
||||||
|
|
||||||
|
test_validate_sdp (t, &offer, &answer);
|
||||||
|
|
||||||
|
h = gst_harness_new_with_element (t->webrtc1, "sink_2", NULL);
|
||||||
|
add_fake_audio_src_harness (h, 98);
|
||||||
|
t->harnesses = g_list_prepend (t->harnesses, h);
|
||||||
|
|
||||||
|
offer.next = &renego_fingerprint;
|
||||||
|
answer.next = &renego_fingerprint;
|
||||||
|
|
||||||
|
/* renegotiate! */
|
||||||
|
test_webrtc_signal_state (t, STATE_NEGOTIATION_NEEDED);
|
||||||
|
test_validate_sdp (t, &offer, &answer);
|
||||||
|
|
||||||
|
test_webrtc_free (t);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
|
GST_START_TEST (test_renego_stream_add_data_channel)
|
||||||
|
{
|
||||||
|
struct test_webrtc *t = create_audio_video_test ();
|
||||||
|
const gchar *expected_offer[] = { "sendrecv", "sendrecv" };
|
||||||
|
const gchar *expected_answer[] = { "sendrecv", "recvonly" };
|
||||||
|
|
||||||
|
VAL_SDP_INIT (offer, on_sdp_media_direction, expected_offer, NULL);
|
||||||
|
VAL_SDP_INIT (answer, on_sdp_media_direction, expected_answer, NULL);
|
||||||
|
VAL_SDP_INIT (renego_mid, sdp_media_equal_mid, NULL, NULL);
|
||||||
|
VAL_SDP_INIT (renego_ice_params, sdp_media_equal_ice_params, NULL,
|
||||||
|
&renego_mid);
|
||||||
|
VAL_SDP_INIT (renego_sess_id, sdp_equal_session_id, NULL, &renego_ice_params);
|
||||||
|
VAL_SDP_INIT (renego_sess_ver, sdp_increasing_session_version, NULL,
|
||||||
|
&renego_sess_id);
|
||||||
|
VAL_SDP_INIT (renego_fingerprint, sdp_media_equal_fingerprint, NULL,
|
||||||
|
&renego_sess_ver);
|
||||||
|
GObject *channel;
|
||||||
|
GstHarness *h;
|
||||||
|
|
||||||
|
/* negotiate an AV stream and then renegotiate a data channel */
|
||||||
|
h = gst_harness_new_with_element (t->webrtc2, "sink_0", NULL);
|
||||||
|
add_fake_audio_src_harness (h, 96);
|
||||||
|
t->harnesses = g_list_prepend (t->harnesses, h);
|
||||||
|
|
||||||
|
test_validate_sdp (t, &offer, &answer);
|
||||||
|
|
||||||
|
g_signal_emit_by_name (t->webrtc1, "create-data-channel", "label", NULL,
|
||||||
|
&channel);
|
||||||
|
|
||||||
|
offer.next = &renego_fingerprint;
|
||||||
|
answer.next = &renego_fingerprint;
|
||||||
|
|
||||||
|
/* renegotiate! */
|
||||||
|
test_webrtc_signal_state (t, STATE_NEGOTIATION_NEEDED);
|
||||||
|
test_validate_sdp (t, &offer, &answer);
|
||||||
|
|
||||||
|
g_object_unref (channel);
|
||||||
|
test_webrtc_free (t);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
|
GST_START_TEST (test_renego_data_channel_add_stream)
|
||||||
|
{
|
||||||
|
struct test_webrtc *t = test_webrtc_new ();
|
||||||
|
const gchar *expected_offer[] = { NULL, "sendrecv" };
|
||||||
|
const gchar *expected_answer[] = { NULL, "recvonly" };
|
||||||
|
VAL_SDP_INIT (offer, on_sdp_media_direction, expected_offer, NULL);
|
||||||
|
VAL_SDP_INIT (answer, on_sdp_media_direction, expected_answer, NULL);
|
||||||
|
VAL_SDP_INIT (renego_mid, sdp_media_equal_mid, NULL, NULL);
|
||||||
|
VAL_SDP_INIT (renego_ice_params, sdp_media_equal_ice_params, NULL,
|
||||||
|
&renego_mid);
|
||||||
|
VAL_SDP_INIT (renego_sess_id, sdp_equal_session_id, NULL, &renego_ice_params);
|
||||||
|
VAL_SDP_INIT (renego_sess_ver, sdp_increasing_session_version, NULL,
|
||||||
|
&renego_sess_id);
|
||||||
|
VAL_SDP_INIT (renego_fingerprint, sdp_media_equal_fingerprint, NULL,
|
||||||
|
&renego_sess_ver);
|
||||||
|
GObject *channel;
|
||||||
|
GstHarness *h;
|
||||||
|
|
||||||
|
/* negotiate an AV stream and then renegotiate a data channel */
|
||||||
|
t->on_negotiation_needed = NULL;
|
||||||
|
t->on_ice_candidate = NULL;
|
||||||
|
t->on_pad_added = _pad_added_fakesink;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
g_signal_emit_by_name (t->webrtc1, "create-data-channel", "label", NULL,
|
||||||
|
&channel);
|
||||||
|
|
||||||
|
test_validate_sdp (t, &offer, &answer);
|
||||||
|
|
||||||
|
h = gst_harness_new_with_element (t->webrtc1, "sink_1", NULL);
|
||||||
|
add_fake_audio_src_harness (h, 97);
|
||||||
|
t->harnesses = g_list_prepend (t->harnesses, h);
|
||||||
|
|
||||||
|
offer.next = &renego_fingerprint;
|
||||||
|
answer.next = &renego_fingerprint;
|
||||||
|
|
||||||
|
/* renegotiate! */
|
||||||
|
test_webrtc_signal_state (t, STATE_NEGOTIATION_NEEDED);
|
||||||
|
test_validate_sdp (t, &offer, &answer);
|
||||||
|
|
||||||
|
g_object_unref (channel);
|
||||||
|
test_webrtc_free (t);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
|
GST_START_TEST (test_bundle_renego_add_stream)
|
||||||
|
{
|
||||||
|
struct test_webrtc *t = create_audio_video_test ();
|
||||||
|
const gchar *expected_offer[] = { "sendrecv", "sendrecv", "sendrecv" };
|
||||||
|
const gchar *expected_answer[] = { "sendrecv", "recvonly", "recvonly" };
|
||||||
|
const gchar *bundle[] = { "audio0", "video1", "audio2", NULL };
|
||||||
|
const gchar *offer_bundle_only[] = { "video1", "audio2", NULL };
|
||||||
|
const gchar *answer_bundle_only[] = { NULL };
|
||||||
|
VAL_SDP_INIT (offer, on_sdp_media_direction, expected_offer, NULL);
|
||||||
|
VAL_SDP_INIT (answer, on_sdp_media_direction, expected_answer, NULL);
|
||||||
|
|
||||||
|
VAL_SDP_INIT (renego_mid, sdp_media_equal_mid, NULL, NULL);
|
||||||
|
VAL_SDP_INIT (renego_ice_params, sdp_media_equal_ice_params, NULL,
|
||||||
|
&renego_mid);
|
||||||
|
VAL_SDP_INIT (renego_sess_id, sdp_equal_session_id, NULL, &renego_ice_params);
|
||||||
|
VAL_SDP_INIT (renego_sess_ver, sdp_increasing_session_version, NULL,
|
||||||
|
&renego_sess_id);
|
||||||
|
VAL_SDP_INIT (renego_fingerprint, sdp_media_equal_fingerprint, NULL,
|
||||||
|
&renego_sess_ver);
|
||||||
|
VAL_SDP_INIT (bundle_tag, _check_bundle_tag, bundle, &renego_fingerprint);
|
||||||
|
VAL_SDP_INIT (offer_non_reject, _count_non_rejected_media,
|
||||||
|
GUINT_TO_POINTER (1), &bundle_tag);
|
||||||
|
VAL_SDP_INIT (answer_non_reject, _count_non_rejected_media,
|
||||||
|
GUINT_TO_POINTER (3), &bundle_tag);
|
||||||
|
VAL_SDP_INIT (offer_bundle_only_sdp, _check_bundle_only_media,
|
||||||
|
&offer_bundle_only, &offer_non_reject);
|
||||||
|
VAL_SDP_INIT (answer_bundle_only_sdp, _check_bundle_only_media,
|
||||||
|
&answer_bundle_only, &answer_non_reject);
|
||||||
|
GstHarness *h;
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
*/
|
||||||
|
gst_util_set_object_arg (G_OBJECT (t->webrtc1), "bundle-policy",
|
||||||
|
"max-bundle");
|
||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
gst_util_set_object_arg (G_OBJECT (t->webrtc2), "bundle-policy",
|
||||||
|
"max-bundle");
|
||||||
|
|
||||||
|
/* negotiate an AV stream and then renegotiate an extra stream */
|
||||||
|
h = gst_harness_new_with_element (t->webrtc2, "sink_0", NULL);
|
||||||
|
add_fake_audio_src_harness (h, 96);
|
||||||
|
t->harnesses = g_list_prepend (t->harnesses, h);
|
||||||
|
|
||||||
|
test_validate_sdp (t, &offer, &answer);
|
||||||
|
|
||||||
|
h = gst_harness_new_with_element (t->webrtc1, "sink_2", NULL);
|
||||||
|
add_fake_audio_src_harness (h, 98);
|
||||||
|
t->harnesses = g_list_prepend (t->harnesses, h);
|
||||||
|
|
||||||
|
offer.next = &offer_bundle_only_sdp;
|
||||||
|
answer.next = &answer_bundle_only_sdp;
|
||||||
|
|
||||||
|
/* renegotiate! */
|
||||||
|
test_webrtc_signal_state (t, STATE_NEGOTIATION_NEEDED);
|
||||||
|
test_validate_sdp (t, &offer, &answer);
|
||||||
|
|
||||||
|
test_webrtc_free (t);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
|
GST_START_TEST (test_bundle_max_compat_max_bundle_renego_add_stream)
|
||||||
|
{
|
||||||
|
struct test_webrtc *t = create_audio_video_test ();
|
||||||
|
const gchar *expected_offer[] = { "sendrecv", "sendrecv", "sendrecv" };
|
||||||
|
const gchar *expected_answer[] = { "sendrecv", "recvonly", "recvonly" };
|
||||||
|
const gchar *bundle[] = { "audio0", "video1", "audio2", NULL };
|
||||||
|
const gchar *bundle_only[] = { NULL };
|
||||||
|
VAL_SDP_INIT (offer, on_sdp_media_direction, expected_offer, NULL);
|
||||||
|
VAL_SDP_INIT (answer, on_sdp_media_direction, expected_answer, NULL);
|
||||||
|
|
||||||
|
VAL_SDP_INIT (renego_mid, sdp_media_equal_mid, NULL, NULL);
|
||||||
|
VAL_SDP_INIT (renego_ice_params, sdp_media_equal_ice_params, NULL,
|
||||||
|
&renego_mid);
|
||||||
|
VAL_SDP_INIT (renego_sess_id, sdp_equal_session_id, NULL, &renego_ice_params);
|
||||||
|
VAL_SDP_INIT (renego_sess_ver, sdp_increasing_session_version, NULL,
|
||||||
|
&renego_sess_id);
|
||||||
|
VAL_SDP_INIT (renego_fingerprint, sdp_media_equal_fingerprint, NULL,
|
||||||
|
&renego_sess_ver);
|
||||||
|
VAL_SDP_INIT (bundle_tag, _check_bundle_tag, bundle, &renego_fingerprint);
|
||||||
|
VAL_SDP_INIT (count_non_reject, _count_non_rejected_media,
|
||||||
|
GUINT_TO_POINTER (3), &bundle_tag);
|
||||||
|
VAL_SDP_INIT (bundle_sdp, _check_bundle_only_media, &bundle_only,
|
||||||
|
&count_non_reject);
|
||||||
|
GstHarness *h;
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
*/
|
||||||
|
gst_util_set_object_arg (G_OBJECT (t->webrtc1), "bundle-policy",
|
||||||
|
"max-compat");
|
||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
gst_util_set_object_arg (G_OBJECT (t->webrtc2), "bundle-policy",
|
||||||
|
"max-bundle");
|
||||||
|
|
||||||
|
/* negotiate an AV stream and then renegotiate an extra stream */
|
||||||
|
h = gst_harness_new_with_element (t->webrtc2, "sink_0", NULL);
|
||||||
|
add_fake_audio_src_harness (h, 96);
|
||||||
|
t->harnesses = g_list_prepend (t->harnesses, h);
|
||||||
|
|
||||||
|
test_validate_sdp (t, &offer, &answer);
|
||||||
|
|
||||||
|
h = gst_harness_new_with_element (t->webrtc1, "sink_2", NULL);
|
||||||
|
add_fake_audio_src_harness (h, 98);
|
||||||
|
t->harnesses = g_list_prepend (t->harnesses, h);
|
||||||
|
|
||||||
|
offer.next = &bundle_sdp;
|
||||||
|
answer.next = &bundle_sdp;
|
||||||
|
|
||||||
|
/* renegotiate! */
|
||||||
|
test_webrtc_signal_state (t, STATE_NEGOTIATION_NEEDED);
|
||||||
|
test_validate_sdp (t, &offer, &answer);
|
||||||
|
|
||||||
|
test_webrtc_free (t);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
|
GST_START_TEST (test_renego_transceiver_set_direction)
|
||||||
|
{
|
||||||
|
struct test_webrtc *t = create_audio_test ();
|
||||||
|
const gchar *expected_offer[] = { "sendrecv" };
|
||||||
|
const gchar *expected_answer[] = { "sendrecv" };
|
||||||
|
VAL_SDP_INIT (offer, on_sdp_media_direction, expected_offer, NULL);
|
||||||
|
VAL_SDP_INIT (answer, on_sdp_media_direction, expected_answer, NULL);
|
||||||
|
GstWebRTCRTPTransceiver *transceiver;
|
||||||
|
GstHarness *h;
|
||||||
|
GstPad *pad;
|
||||||
|
|
||||||
|
/* negotiate an AV stream and then change the transceiver direction */
|
||||||
|
h = gst_harness_new_with_element (t->webrtc2, "sink_0", NULL);
|
||||||
|
add_fake_audio_src_harness (h, 96);
|
||||||
|
t->harnesses = g_list_prepend (t->harnesses, h);
|
||||||
|
|
||||||
|
test_validate_sdp (t, &offer, &answer);
|
||||||
|
|
||||||
|
/* renegotiate an inactive transceiver! */
|
||||||
|
pad = gst_element_get_static_pad (t->webrtc1, "sink_0");
|
||||||
|
g_object_get (pad, "transceiver", &transceiver, NULL);
|
||||||
|
fail_unless (transceiver != NULL);
|
||||||
|
gst_webrtc_rtp_transceiver_set_direction (transceiver,
|
||||||
|
GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE);
|
||||||
|
expected_offer[0] = "inactive";
|
||||||
|
expected_answer[0] = "inactive";
|
||||||
|
|
||||||
|
/* TODO: also validate EOS events from the inactive change */
|
||||||
|
|
||||||
|
test_webrtc_signal_state (t, STATE_NEGOTIATION_NEEDED);
|
||||||
|
test_validate_sdp (t, &offer, &answer);
|
||||||
|
|
||||||
|
gst_object_unref (pad);
|
||||||
|
gst_object_unref (transceiver);
|
||||||
|
test_webrtc_free (t);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
static Suite *
|
static Suite *
|
||||||
webrtcbin_suite (void)
|
webrtcbin_suite (void)
|
||||||
{
|
{
|
||||||
|
@ -2294,6 +2722,10 @@ webrtcbin_suite (void)
|
||||||
tcase_add_test (tc, test_bundle_audio_video_max_compat_max_bundle);
|
tcase_add_test (tc, test_bundle_audio_video_max_compat_max_bundle);
|
||||||
tcase_add_test (tc, test_dual_audio);
|
tcase_add_test (tc, test_dual_audio);
|
||||||
tcase_add_test (tc, test_duplicate_nego);
|
tcase_add_test (tc, test_duplicate_nego);
|
||||||
|
tcase_add_test (tc, test_renego_add_stream);
|
||||||
|
tcase_add_test (tc, test_bundle_renego_add_stream);
|
||||||
|
tcase_add_test (tc, test_bundle_max_compat_max_bundle_renego_add_stream);
|
||||||
|
tcase_add_test (tc, test_renego_transceiver_set_direction);
|
||||||
if (sctpenc && sctpdec) {
|
if (sctpenc && sctpdec) {
|
||||||
tcase_add_test (tc, test_data_channel_create);
|
tcase_add_test (tc, test_data_channel_create);
|
||||||
tcase_add_test (tc, test_data_channel_remote_notify);
|
tcase_add_test (tc, test_data_channel_remote_notify);
|
||||||
|
@ -2304,6 +2736,8 @@ webrtcbin_suite (void)
|
||||||
tcase_add_test (tc, test_data_channel_max_message_size);
|
tcase_add_test (tc, test_data_channel_max_message_size);
|
||||||
tcase_add_test (tc, test_data_channel_pre_negotiated);
|
tcase_add_test (tc, test_data_channel_pre_negotiated);
|
||||||
tcase_add_test (tc, test_bundle_audio_video_data);
|
tcase_add_test (tc, test_bundle_audio_video_data);
|
||||||
|
tcase_add_test (tc, test_renego_stream_add_data_channel);
|
||||||
|
tcase_add_test (tc, test_renego_data_channel_add_stream);
|
||||||
} else {
|
} else {
|
||||||
GST_WARNING ("Some required elements were not found. "
|
GST_WARNING ("Some required elements were not found. "
|
||||||
"All datachannel tests are disabled. sctpenc %p, sctpdec %p", sctpenc,
|
"All datachannel tests are disabled. sctpenc %p, sctpdec %p", sctpenc,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
noinst_PROGRAMS = webrtc webrtcbidirectional webrtcswap webrtctransceiver
|
noinst_PROGRAMS = webrtc webrtcbidirectional webrtcswap webrtctransceiver webrtcrenego
|
||||||
|
|
||||||
webrtc_SOURCES = webrtc.c
|
webrtc_SOURCES = webrtc.c
|
||||||
webrtc_CFLAGS=\
|
webrtc_CFLAGS=\
|
||||||
|
@ -52,3 +52,16 @@ webrtctransceiver_LDADD=\
|
||||||
$(GST_LIBS) \
|
$(GST_LIBS) \
|
||||||
$(GST_SDP_LIBS) \
|
$(GST_SDP_LIBS) \
|
||||||
$(top_builddir)/gst-libs/gst/webrtc/libgstwebrtc-@GST_API_VERSION@.la
|
$(top_builddir)/gst-libs/gst/webrtc/libgstwebrtc-@GST_API_VERSION@.la
|
||||||
|
|
||||||
|
webrtcrenego_SOURCES = webrtcrenego.c
|
||||||
|
webrtcrenego_CFLAGS=\
|
||||||
|
-I$(top_srcdir)/gst-libs \
|
||||||
|
-I$(top_builddir)/gst-libs \
|
||||||
|
$(GST_PLUGINS_BASE_CFLAGS) \
|
||||||
|
$(GST_CFLAGS) \
|
||||||
|
$(GST_SDP_CFLAGS)
|
||||||
|
webrtcrenego_LDADD=\
|
||||||
|
$(GST_PLUGINS_BASE_LIBS) \
|
||||||
|
$(GST_LIBS) \
|
||||||
|
$(GST_SDP_LIBS) \
|
||||||
|
$(top_builddir)/gst-libs/gst/webrtc/libgstwebrtc-@GST_API_VERSION@.la
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
examples = ['webrtc', 'webrtcbidirectional', 'webrtcswap', 'webrtctransceiver']
|
examples = ['webrtc', 'webrtcbidirectional', 'webrtcswap', 'webrtctransceiver', 'webrtcrenego']
|
||||||
|
|
||||||
foreach example : examples
|
foreach example : examples
|
||||||
exe_name = example
|
exe_name = example
|
||||||
|
|
289
tests/examples/webrtc/webrtcrenego.c
Normal file
289
tests/examples/webrtc/webrtcrenego.c
Normal file
|
@ -0,0 +1,289 @@
|
||||||
|
#include <gst/gst.h>
|
||||||
|
#include <gst/sdp/sdp.h>
|
||||||
|
#include <gst/webrtc/webrtc.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static GMainLoop *loop;
|
||||||
|
static GstElement *pipe1, *webrtc1, *webrtc2, *extra_src;
|
||||||
|
static GstBus *bus1;
|
||||||
|
|
||||||
|
#define SEND_SRC(pattern) "videotestsrc is-live=true pattern=" pattern " ! timeoverlay ! queue ! vp8enc ! rtpvp8pay ! queue ! " \
|
||||||
|
"capsfilter caps=application/x-rtp,media=video,payload=96,encoding-name=VP8"
|
||||||
|
|
||||||
|
static void
|
||||||
|
_element_message (GstElement * parent, GstMessage * msg)
|
||||||
|
{
|
||||||
|
switch (GST_MESSAGE_TYPE (msg)) {
|
||||||
|
case GST_MESSAGE_EOS:{
|
||||||
|
GstElement *receive, *webrtc;
|
||||||
|
GstPad *pad, *peer;
|
||||||
|
|
||||||
|
g_print ("Got element EOS message from %s parent %s\n",
|
||||||
|
GST_OBJECT_NAME (msg->src), GST_OBJECT_NAME (parent));
|
||||||
|
|
||||||
|
receive = GST_ELEMENT (msg->src);
|
||||||
|
|
||||||
|
pad = gst_element_get_static_pad (receive, "sink");
|
||||||
|
peer = gst_pad_get_peer (pad);
|
||||||
|
|
||||||
|
webrtc = GST_ELEMENT (gst_pad_get_parent (peer));
|
||||||
|
gst_bin_remove (GST_BIN (pipe1), receive);
|
||||||
|
|
||||||
|
gst_pad_unlink (peer, pad);
|
||||||
|
gst_element_release_request_pad (webrtc, peer);
|
||||||
|
|
||||||
|
gst_object_unref (pad);
|
||||||
|
gst_object_unref (peer);
|
||||||
|
|
||||||
|
gst_element_set_state (receive, GST_STATE_NULL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
_bus_watch (GstBus * bus, GstMessage * msg, GstElement * pipe)
|
||||||
|
{
|
||||||
|
switch (GST_MESSAGE_TYPE (msg)) {
|
||||||
|
case GST_MESSAGE_STATE_CHANGED:
|
||||||
|
if (GST_ELEMENT (msg->src) == pipe) {
|
||||||
|
GstState old, new, pending;
|
||||||
|
|
||||||
|
gst_message_parse_state_changed (msg, &old, &new, &pending);
|
||||||
|
|
||||||
|
{
|
||||||
|
gchar *dump_name = g_strconcat ("state_changed-",
|
||||||
|
gst_element_state_get_name (old), "_",
|
||||||
|
gst_element_state_get_name (new), NULL);
|
||||||
|
GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (msg->src),
|
||||||
|
GST_DEBUG_GRAPH_SHOW_ALL, dump_name);
|
||||||
|
g_free (dump_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case GST_MESSAGE_ERROR:{
|
||||||
|
GError *err = NULL;
|
||||||
|
gchar *dbg_info = NULL;
|
||||||
|
|
||||||
|
GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipe),
|
||||||
|
GST_DEBUG_GRAPH_SHOW_ALL, "error");
|
||||||
|
|
||||||
|
gst_message_parse_error (msg, &err, &dbg_info);
|
||||||
|
g_printerr ("ERROR from element %s: %s\n",
|
||||||
|
GST_OBJECT_NAME (msg->src), err->message);
|
||||||
|
g_printerr ("Debugging info: %s\n", (dbg_info) ? dbg_info : "none");
|
||||||
|
g_error_free (err);
|
||||||
|
g_free (dbg_info);
|
||||||
|
g_main_loop_quit (loop);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GST_MESSAGE_EOS:{
|
||||||
|
GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipe),
|
||||||
|
GST_DEBUG_GRAPH_SHOW_ALL, "eos");
|
||||||
|
g_print ("EOS received\n");
|
||||||
|
g_main_loop_quit (loop);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GST_MESSAGE_ELEMENT:{
|
||||||
|
const GstStructure *s = gst_message_get_structure (msg);
|
||||||
|
if (g_strcmp0 (gst_structure_get_name (s), "GstBinForwarded") == 0) {
|
||||||
|
GstMessage *sub_msg;
|
||||||
|
|
||||||
|
gst_structure_get (s, "message", GST_TYPE_MESSAGE, &sub_msg, NULL);
|
||||||
|
_element_message (GST_ELEMENT (msg->src), sub_msg);
|
||||||
|
gst_message_unref (sub_msg);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_webrtc_pad_added (GstElement * webrtc, GstPad * new_pad, GstElement * pipe)
|
||||||
|
{
|
||||||
|
GstElement *out;
|
||||||
|
GstPad *sink;
|
||||||
|
|
||||||
|
if (GST_PAD_DIRECTION (new_pad) != GST_PAD_SRC)
|
||||||
|
return;
|
||||||
|
|
||||||
|
out = gst_parse_bin_from_description ("queue ! rtpvp8depay ! vp8dec ! "
|
||||||
|
"videoconvert ! queue ! xvimagesink", TRUE, NULL);
|
||||||
|
gst_bin_add (GST_BIN (pipe), out);
|
||||||
|
gst_element_sync_state_with_parent (out);
|
||||||
|
|
||||||
|
sink = out->sinkpads->data;
|
||||||
|
|
||||||
|
gst_pad_link (new_pad, sink);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_on_answer_received (GstPromise * promise, gpointer user_data)
|
||||||
|
{
|
||||||
|
GstWebRTCSessionDescription *answer = NULL;
|
||||||
|
const GstStructure *reply;
|
||||||
|
gchar *desc;
|
||||||
|
|
||||||
|
g_assert (gst_promise_wait (promise) == GST_PROMISE_RESULT_REPLIED);
|
||||||
|
reply = gst_promise_get_reply (promise);
|
||||||
|
gst_structure_get (reply, "answer",
|
||||||
|
GST_TYPE_WEBRTC_SESSION_DESCRIPTION, &answer, NULL);
|
||||||
|
gst_promise_unref (promise);
|
||||||
|
desc = gst_sdp_message_as_text (answer->sdp);
|
||||||
|
g_print ("Created answer:\n%s\n", desc);
|
||||||
|
g_free (desc);
|
||||||
|
|
||||||
|
/* this is one way to tell webrtcbin that we don't want to be notified when
|
||||||
|
* this task is complete: set a NULL promise */
|
||||||
|
g_signal_emit_by_name (webrtc1, "set-remote-description", answer, NULL);
|
||||||
|
/* this is another way to tell webrtcbin that we don't want to be notified
|
||||||
|
* when this task is complete: interrupt the promise */
|
||||||
|
promise = gst_promise_new ();
|
||||||
|
g_signal_emit_by_name (webrtc2, "set-local-description", answer, promise);
|
||||||
|
gst_promise_interrupt (promise);
|
||||||
|
gst_promise_unref (promise);
|
||||||
|
|
||||||
|
gst_webrtc_session_description_free (answer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_on_offer_received (GstPromise * promise, gpointer user_data)
|
||||||
|
{
|
||||||
|
GstWebRTCSessionDescription *offer = NULL;
|
||||||
|
const GstStructure *reply;
|
||||||
|
gchar *desc;
|
||||||
|
|
||||||
|
g_assert (gst_promise_wait (promise) == GST_PROMISE_RESULT_REPLIED);
|
||||||
|
reply = gst_promise_get_reply (promise);
|
||||||
|
gst_structure_get (reply, "offer",
|
||||||
|
GST_TYPE_WEBRTC_SESSION_DESCRIPTION, &offer, NULL);
|
||||||
|
gst_promise_unref (promise);
|
||||||
|
desc = gst_sdp_message_as_text (offer->sdp);
|
||||||
|
g_print ("Created offer:\n%s\n", desc);
|
||||||
|
g_free (desc);
|
||||||
|
|
||||||
|
g_signal_emit_by_name (webrtc1, "set-local-description", offer, NULL);
|
||||||
|
g_signal_emit_by_name (webrtc2, "set-remote-description", offer, NULL);
|
||||||
|
|
||||||
|
promise = gst_promise_new_with_change_func (_on_answer_received, user_data,
|
||||||
|
NULL);
|
||||||
|
g_signal_emit_by_name (webrtc2, "create-answer", NULL, promise);
|
||||||
|
|
||||||
|
gst_webrtc_session_description_free (offer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_on_negotiation_needed (GstElement * element, gpointer user_data)
|
||||||
|
{
|
||||||
|
GstPromise *promise;
|
||||||
|
|
||||||
|
promise = gst_promise_new_with_change_func (_on_offer_received, user_data,
|
||||||
|
NULL);
|
||||||
|
g_signal_emit_by_name (webrtc1, "create-offer", NULL, promise);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_on_ice_candidate (GstElement * webrtc, guint mlineindex, gchar * candidate,
|
||||||
|
GstElement * other)
|
||||||
|
{
|
||||||
|
g_signal_emit_by_name (other, "add-ice-candidate", mlineindex, candidate);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
stream_change (gpointer data)
|
||||||
|
{
|
||||||
|
if (!extra_src) {
|
||||||
|
g_print ("Adding extra stream\n");
|
||||||
|
extra_src =
|
||||||
|
gst_parse_bin_from_description (SEND_SRC ("circular"), TRUE, NULL);
|
||||||
|
|
||||||
|
gst_element_set_locked_state (extra_src, TRUE);
|
||||||
|
gst_bin_add (GST_BIN (pipe1), extra_src);
|
||||||
|
gst_element_link (extra_src, webrtc1);
|
||||||
|
gst_element_set_locked_state (extra_src, FALSE);
|
||||||
|
gst_element_sync_state_with_parent (extra_src);
|
||||||
|
GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipe1),
|
||||||
|
GST_DEBUG_GRAPH_SHOW_ALL, "add");
|
||||||
|
} else {
|
||||||
|
GstPad *pad, *peer;
|
||||||
|
GstWebRTCRTPTransceiver *transceiver;
|
||||||
|
|
||||||
|
g_print ("Removing extra stream\n");
|
||||||
|
pad = gst_element_get_static_pad (extra_src, "src");
|
||||||
|
peer = gst_pad_get_peer (pad);
|
||||||
|
gst_element_send_event (extra_src, gst_event_new_eos ());
|
||||||
|
|
||||||
|
g_object_get (peer, "transceiver", &transceiver, NULL);
|
||||||
|
gst_webrtc_rtp_transceiver_set_direction (transceiver,
|
||||||
|
GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE);
|
||||||
|
|
||||||
|
gst_element_set_locked_state (extra_src, TRUE);
|
||||||
|
gst_element_set_state (extra_src, GST_STATE_NULL);
|
||||||
|
gst_pad_unlink (pad, peer);
|
||||||
|
gst_element_release_request_pad (webrtc1, peer);
|
||||||
|
|
||||||
|
gst_object_unref (peer);
|
||||||
|
gst_object_unref (pad);
|
||||||
|
|
||||||
|
gst_bin_remove (GST_BIN (pipe1), extra_src);
|
||||||
|
extra_src = NULL;
|
||||||
|
GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipe1),
|
||||||
|
GST_DEBUG_GRAPH_SHOW_ALL, "remove");
|
||||||
|
}
|
||||||
|
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int argc, char *argv[])
|
||||||
|
{
|
||||||
|
gst_init (&argc, &argv);
|
||||||
|
|
||||||
|
loop = g_main_loop_new (NULL, FALSE);
|
||||||
|
pipe1 = gst_parse_launch (SEND_SRC ("smpte")
|
||||||
|
" ! webrtcbin name=smpte bundle-policy=max-bundle " SEND_SRC ("ball")
|
||||||
|
" ! webrtcbin name=ball bundle-policy=max-bundle", NULL);
|
||||||
|
g_object_set (pipe1, "message-forward", TRUE, NULL);
|
||||||
|
bus1 = gst_pipeline_get_bus (GST_PIPELINE (pipe1));
|
||||||
|
gst_bus_add_watch (bus1, (GstBusFunc) _bus_watch, pipe1);
|
||||||
|
|
||||||
|
webrtc1 = gst_bin_get_by_name (GST_BIN (pipe1), "smpte");
|
||||||
|
g_signal_connect (webrtc1, "on-negotiation-needed",
|
||||||
|
G_CALLBACK (_on_negotiation_needed), NULL);
|
||||||
|
g_signal_connect (webrtc1, "pad-added", G_CALLBACK (_webrtc_pad_added),
|
||||||
|
pipe1);
|
||||||
|
webrtc2 = gst_bin_get_by_name (GST_BIN (pipe1), "ball");
|
||||||
|
g_signal_connect (webrtc2, "pad-added", G_CALLBACK (_webrtc_pad_added),
|
||||||
|
pipe1);
|
||||||
|
g_signal_connect (webrtc1, "on-ice-candidate",
|
||||||
|
G_CALLBACK (_on_ice_candidate), webrtc2);
|
||||||
|
g_signal_connect (webrtc2, "on-ice-candidate",
|
||||||
|
G_CALLBACK (_on_ice_candidate), webrtc1);
|
||||||
|
|
||||||
|
g_print ("Starting pipeline\n");
|
||||||
|
gst_element_set_state (GST_ELEMENT (pipe1), GST_STATE_PLAYING);
|
||||||
|
|
||||||
|
g_timeout_add_seconds (5, stream_change, NULL);
|
||||||
|
|
||||||
|
g_main_loop_run (loop);
|
||||||
|
|
||||||
|
gst_element_set_state (GST_ELEMENT (pipe1), GST_STATE_NULL);
|
||||||
|
g_print ("Pipeline stopped\n");
|
||||||
|
|
||||||
|
gst_object_unref (webrtc1);
|
||||||
|
gst_object_unref (webrtc2);
|
||||||
|
gst_bus_remove_watch (bus1);
|
||||||
|
gst_object_unref (bus1);
|
||||||
|
gst_object_unref (pipe1);
|
||||||
|
|
||||||
|
gst_deinit ();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in a new issue