summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorMathieu Duponchelle <mathieu@centricular.com>2017-11-29 17:57:52 +0100
committerMathieu Duponchelle <mathieu@centricular.com>2018-05-09 14:46:14 +0200
commit5c450c5992ca6dc7100207560224167797fe4c13 (patch)
treeb464b93ac7d8868cb2787e1dff9b9f0f9f93d346 /tests
parent54482a54d85a9f107b606ef65b06715d924b5041 (diff)
downloadgstreamer-plugins-bad-5c450c5992ca6dc7100207560224167797fe4c13.tar.gz
webrtcbin: implement support for FEC and RTX
https://bugzilla.gnome.org/show_bug.cgi?id=795044
Diffstat (limited to 'tests')
-rw-r--r--tests/check/elements/webrtcbin.c62
-rw-r--r--tests/examples/webrtc/Makefile.am15
-rw-r--r--tests/examples/webrtc/meson.build2
-rw-r--r--tests/examples/webrtc/webrtctransceiver.c218
4 files changed, 295 insertions, 2 deletions
diff --git a/tests/check/elements/webrtcbin.c b/tests/check/elements/webrtcbin.c
index b7f42e0f3..756f38021 100644
--- a/tests/check/elements/webrtcbin.c
+++ b/tests/check/elements/webrtcbin.c
@@ -822,6 +822,67 @@ GST_START_TEST (test_media_direction)
GST_END_TEST;
static void
+on_sdp_media_payload_types (struct test_webrtc *t, GstElement * element,
+ GstWebRTCSessionDescription * desc, gpointer user_data)
+{
+ const GstSDPMedia *vmedia;
+ guint j;
+
+ fail_unless_equals_int (gst_sdp_message_medias_len (desc->sdp), 2);
+
+ vmedia = gst_sdp_message_get_media (desc->sdp, 1);
+
+ for (j = 0; j < gst_sdp_media_attributes_len (vmedia); j++) {
+ const GstSDPAttribute *attr = gst_sdp_media_get_attribute (vmedia, j);
+
+ if (!g_strcmp0 (attr->key, "rtpmap")) {
+ if (g_str_has_prefix (attr->value, "97")) {
+ fail_unless_equals_string (attr->value, "97 VP8/90000");
+ } else if (g_str_has_prefix (attr->value, "96")) {
+ fail_unless_equals_string (attr->value, "96 red/90000");
+ } else if (g_str_has_prefix (attr->value, "98")) {
+ fail_unless_equals_string (attr->value, "98 ulpfec/90000");
+ } else if (g_str_has_prefix (attr->value, "99")) {
+ fail_unless_equals_string (attr->value, "99 rtx/90000");
+ } else if (g_str_has_prefix (attr->value, "100")) {
+ fail_unless_equals_string (attr->value, "100 rtx/90000");
+ }
+ }
+ }
+}
+
+/* In this test we verify that webrtcbin will pick available payload
+ * types when it needs to, in that example for RTX and FEC */
+GST_START_TEST (test_payload_types)
+{
+ struct test_webrtc *t = create_audio_video_test ();
+ struct validate_sdp offer = { on_sdp_media_payload_types, NULL };
+ GstWebRTCRTPTransceiver *trans;
+ GArray *transceivers;
+
+ t->offer_data = &offer;
+ t->on_offer_created = validate_sdp;
+ t->on_ice_candidate = NULL;
+ /* We don't really care about the answer here */
+ t->on_answer_created = NULL;
+
+ g_signal_emit_by_name (t->webrtc1, "get-transceivers", &transceivers);
+ fail_unless_equals_int (transceivers->len, 2);
+ trans = g_array_index (transceivers, GstWebRTCRTPTransceiver *, 1);
+ g_object_set (trans, "fec-type", GST_WEBRTC_FEC_TYPE_ULP_RED, "do-nack", TRUE,
+ NULL);
+ g_array_unref (transceivers);
+
+ test_webrtc_create_offer (t, t->webrtc1);
+
+ test_webrtc_wait_for_answer_error_eos (t);
+ fail_unless_equals_int (STATE_ANSWER_CREATED, t->state);
+ test_webrtc_free (t);
+}
+
+GST_END_TEST;
+
+static void
on_sdp_media_setup (struct test_webrtc *t, GstElement * element,
GstWebRTCSessionDescription * desc, gpointer user_data)
{
@@ -1367,6 +1428,7 @@ webrtcbin_suite (void)
tcase_add_test (tc, test_get_transceivers);
tcase_add_test (tc, test_add_recvonly_transceiver);
tcase_add_test (tc, test_recvonly_sendonly);
+ tcase_add_test (tc, test_payload_types);
}
if (nicesrc)
diff --git a/tests/examples/webrtc/Makefile.am b/tests/examples/webrtc/Makefile.am
index 520942d7f..6323263bf 100644
--- a/tests/examples/webrtc/Makefile.am
+++ b/tests/examples/webrtc/Makefile.am
@@ -1,5 +1,5 @@
-noinst_PROGRAMS = webrtc webrtcbidirectional webrtcswap
+noinst_PROGRAMS = webrtc webrtcbidirectional webrtcswap webrtctransceiver
webrtc_SOURCES = webrtc.c
webrtc_CFLAGS=\
@@ -39,3 +39,16 @@ webrtcswap_LDADD=\
$(GST_LIBS) \
$(GST_SDP_LIBS) \
$(top_builddir)/gst-libs/gst/webrtc/libgstwebrtc-@GST_API_VERSION@.la
+
+webrtctransceiver_SOURCES = webrtctransceiver.c
+webrtctransceiver_CFLAGS=\
+ -I$(top_srcdir)/gst-libs \
+ -I$(top_builddir)/gst-libs \
+ $(GST_PLUGINS_BASE_CFLAGS) \
+ $(GST_CFLAGS) \
+ $(GST_SDP_CFLAGS)
+webrtctransceiver_LDADD=\
+ $(GST_PLUGINS_BASE_LIBS) \
+ $(GST_LIBS) \
+ $(GST_SDP_LIBS) \
+ $(top_builddir)/gst-libs/gst/webrtc/libgstwebrtc-@GST_API_VERSION@.la
diff --git a/tests/examples/webrtc/meson.build b/tests/examples/webrtc/meson.build
index 7c2aab72e..90c15e5b7 100644
--- a/tests/examples/webrtc/meson.build
+++ b/tests/examples/webrtc/meson.build
@@ -1,4 +1,4 @@
-examples = ['webrtc', 'webrtcbidirectional', 'webrtcswap']
+examples = ['webrtc', 'webrtcbidirectional', 'webrtcswap', 'webrtctransceiver']
foreach example : examples
exe_name = example
diff --git a/tests/examples/webrtc/webrtctransceiver.c b/tests/examples/webrtc/webrtctransceiver.c
new file mode 100644
index 000000000..df1a18e3d
--- /dev/null
+++ b/tests/examples/webrtc/webrtctransceiver.c
@@ -0,0 +1,218 @@
+#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;
+static GstBus *bus1;
+
+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;
+ }
+ 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 ("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);
+
+ g_signal_emit_by_name (webrtc1, "set-remote-description", answer, NULL);
+ g_signal_emit_by_name (webrtc2, "set-local-description", answer, NULL);
+
+ 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 void
+_on_new_transceiver (GstElement * webrtc, GstWebRTCRTPTransceiver * trans)
+{
+ /* If we expected more than one transceiver, we would take a look at
+ * trans->mline, and compare it with webrtcbin's local description */
+ g_object_set (trans, "fec-type", GST_WEBRTC_FEC_TYPE_ULP_RED, NULL);
+}
+
+static void
+add_fec_to_offer (GstElement * webrtc)
+{
+ GstWebRTCRTPTransceiver *trans;
+ GArray *transceivers;
+
+ /* A transceiver has already been created when a sink pad was
+ * requested on the sending webrtcbin */
+
+ g_signal_emit_by_name (webrtc, "get-transceivers", &transceivers);
+
+ trans = g_array_index (transceivers, GstWebRTCRTPTransceiver *, 0);
+
+ g_object_set (trans, "fec-type", GST_WEBRTC_FEC_TYPE_ULP_RED,
+ "fec-percentage", 100, NULL);
+
+ g_array_unref (transceivers);
+}
+
+int
+main (int argc, char *argv[])
+{
+ gst_init (&argc, &argv);
+
+ loop = g_main_loop_new (NULL, FALSE);
+ pipe1 =
+ gst_parse_launch
+ ("videotestsrc pattern=ball ! video/x-raw ! queue ! vp8enc ! rtpvp8pay ! queue ! "
+ "application/x-rtp,media=video,payload=96,encoding-name=VP8 ! "
+ "webrtcbin name=send webrtcbin name=recv", 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), "send");
+ g_signal_connect (webrtc1, "on-negotiation-needed",
+ G_CALLBACK (_on_negotiation_needed), NULL);
+ add_fec_to_offer (webrtc1);
+
+ webrtc2 = gst_bin_get_by_name (GST_BIN (pipe1), "recv");
+ 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_signal_connect (webrtc2, "on-new-transceiver",
+ G_CALLBACK (_on_new_transceiver), NULL);
+
+ g_print ("Starting pipeline\n");
+ gst_element_set_state (GST_ELEMENT (pipe1), GST_STATE_PLAYING);
+
+ 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;
+}