tests/webrtc: test for enabled bundled fec/rtx
authorMatthew Waters <matthew@centricular.com>
Tue, 16 Nov 2021 07:11:49 +0000 (18:11 +1100)
committerGStreamer Marge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Tue, 29 Mar 2022 23:55:40 +0000 (23:55 +0000)
Doesn't actually check that any fec/rtx happens, just that the pipeline
is vaguely sane and doesn't error.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1664>

subprojects/gst-plugins-bad/tests/check/elements/webrtcbin.c

index d3043da..279496e 100644 (file)
@@ -29,6 +29,7 @@
 #include <gst/check/gstcheck.h>
 #include <gst/check/gstharness.h>
 #include <gst/webrtc/webrtc.h>
+#include <gst/rtp/rtp.h>
 #include "../../../ext/webrtc/webrtcsdp.h"
 #include "../../../ext/webrtc/webrtcsdp.c"
 #include "../../../ext/webrtc/utils.h"
@@ -4227,7 +4228,7 @@ _pad_added_harness (struct test_webrtc *t, GstElement * element,
     GstPad * pad, gpointer user_data)
 {
   GstHarness *h;
-  GstHarness **sink_harness = user_data;
+  GList **sink_harness = user_data;
 
   if (GST_PAD_DIRECTION (pad) != GST_PAD_SRC)
     return;
@@ -4236,7 +4237,7 @@ _pad_added_harness (struct test_webrtc *t, GstElement * element,
   t->harnesses = g_list_prepend (t->harnesses, h);
 
   if (sink_harness) {
-    *sink_harness = h;
+    *sink_harness = g_list_prepend (*sink_harness, h);
     g_cond_broadcast (&t->cond);
   }
 }
@@ -4273,6 +4274,7 @@ GST_START_TEST (test_codec_preferences_negotiation_srcpad)
   VAL_SDP_INIT (answer_non_reject, _count_non_rejected_media,
       GUINT_TO_POINTER (0), &count);
   GstHarness *h;
+  GList *sink_harnesses = NULL;
   GstHarness *sink_harness = NULL;
   guint i;
   GstElement *rtpbin2;
@@ -4281,7 +4283,7 @@ GST_START_TEST (test_codec_preferences_negotiation_srcpad)
   t->on_negotiation_needed = NULL;
   t->on_ice_candidate = NULL;
   t->on_pad_added = _pad_added_harness;
-  t->pad_added_data = &sink_harness;
+  t->pad_added_data = &sink_harnesses;
 
   rtpbin2 = gst_bin_get_by_name (GST_BIN (t->webrtc2), "rtpbin");
   fail_unless (rtpbin2 != NULL);
@@ -4304,10 +4306,12 @@ GST_START_TEST (test_codec_preferences_negotiation_srcpad)
     gst_harness_push_from_src (h);
 
   g_mutex_lock (&t->lock);
-  while (sink_harness == NULL) {
+  while (sink_harnesses == NULL) {
     gst_harness_push_from_src (h);
     g_cond_wait_until (&t->cond, &t->lock, g_get_monotonic_time () + 5000);
   }
+  fail_unless_equals_int (1, g_list_length (sink_harnesses));
+  sink_harness = (GstHarness *) sink_harnesses->data;
   g_mutex_unlock (&t->lock);
   fail_unless (sink_harness->element == t->webrtc2);
 
@@ -4340,6 +4344,8 @@ GST_START_TEST (test_codec_preferences_negotiation_srcpad)
   }
 
   test_webrtc_free (t);
+
+  g_list_free (sink_harnesses);
 }
 
 GST_END_TEST;
@@ -4560,6 +4566,167 @@ GST_START_TEST (test_bundle_mid_header_extension)
 
 GST_END_TEST;
 
+static void
+on_new_transceiver_set_rtx_fec (GstElement * webrtcbin, GObject * trans,
+    gpointer user_data)
+{
+  g_object_set (trans, "fec-type", GST_WEBRTC_FEC_TYPE_ULP_RED,
+      "fec-percentage", 100, "do-nack", TRUE, NULL);
+}
+
+GST_START_TEST (test_max_bundle_fec)
+{
+  struct test_webrtc *t = test_webrtc_new ();
+  guint media_format_count[] = { 5, 5, };
+  VAL_SDP_INIT (media_formats, on_sdp_media_count_formats,
+      media_format_count, NULL);
+  VAL_SDP_INIT (payloads, on_sdp_media_no_duplicate_payloads, NULL,
+      &media_formats);
+  VAL_SDP_INIT (count, _count_num_sdp_media, GUINT_TO_POINTER (2), &payloads);
+  VAL_SDP_INIT (offer_non_reject, _count_non_rejected_media,
+      GUINT_TO_POINTER (1), &count);
+  VAL_SDP_INIT (answer_non_reject, _count_non_rejected_media,
+      GUINT_TO_POINTER (2), &count);
+  const gchar *expected_offer_setup[] = { "actpass", "actpass", };
+  VAL_SDP_INIT (offer_setup, on_sdp_media_setup, expected_offer_setup,
+      &offer_non_reject);
+  const gchar *expected_answer_setup[] = { "active", "active", };
+  VAL_SDP_INIT (answer_setup, on_sdp_media_setup, expected_answer_setup,
+      &answer_non_reject);
+  const gchar *expected_offer_direction[] = { "sendrecv", "sendrecv", };
+  VAL_SDP_INIT (offer, on_sdp_media_direction, expected_offer_direction,
+      &offer_setup);
+  const gchar *expected_answer_direction[] = { "recvonly", "recvonly", };
+  VAL_SDP_INIT (answer, on_sdp_media_direction, expected_answer_direction,
+      &answer_setup);
+  GstHarness *src0, *src1;
+  GList *sink_harnesses = NULL;
+  guint i;
+  GstElement *rtpbin2;
+  GstBuffer *buf;
+  guint ssrcs[] = { 123456789, 987654321 };
+  GArray *ssrcs_received;
+
+  t->on_negotiation_needed = NULL;
+  t->on_ice_candidate = NULL;
+  t->on_pad_added = _pad_added_harness;
+  t->pad_added_data = &sink_harnesses;
+
+  gst_util_set_object_arg (G_OBJECT (t->webrtc1), "bundle-policy",
+      "max-bundle");
+  gst_util_set_object_arg (G_OBJECT (t->webrtc2), "bundle-policy",
+      "max-bundle");
+
+  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);
+
+  src0 = gst_harness_new_with_element (t->webrtc1, "sink_0", NULL);
+  add_audio_test_src_harness (src0, ssrcs[0]);
+  t->harnesses = g_list_prepend (t->harnesses, src0);
+
+  src1 = gst_harness_new_with_element (t->webrtc1, "sink_1", NULL);
+  add_audio_test_src_harness (src1, ssrcs[1]);
+  t->harnesses = g_list_prepend (t->harnesses, src1);
+
+  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);
+
+  for (i = 0; i < 10; i++) {
+    gst_harness_push_from_src (src0);
+    gst_harness_push_from_src (src1);
+  }
+
+  ssrcs_received = g_array_new (FALSE, TRUE, sizeof (guint32));
+
+  /* Get one buffer out for each ssrc sent.
+   */
+  g_mutex_lock (&t->lock);
+  while (ssrcs_received->len < G_N_ELEMENTS (ssrcs)) {
+    GList *l;
+    guint i;
+
+    gst_harness_push_from_src (src0);
+    gst_harness_push_from_src (src1);
+    if (g_list_length (sink_harnesses) < 2) {
+      g_cond_wait_until (&t->cond, &t->lock, g_get_monotonic_time () + 5000);
+      if (g_list_length (sink_harnesses) < 2)
+        continue;
+    }
+
+    for (l = sink_harnesses; l; l = l->next) {
+      GstHarness *sink_harness = (GstHarness *) l->data;
+      GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
+      GstWebRTCRTPTransceiver *rtp_trans;
+      char *trans_mid;
+      GstPad *srcpad;
+      guint ssrc;
+      guint mlineindex;
+      char *expected_mid;
+
+      fail_unless (sink_harness->element == t->webrtc2);
+
+      buf = gst_harness_try_pull (sink_harness);
+      if (!buf)
+        continue;
+
+      /* ensure that the resulting pad has the correct mid set */
+      srcpad = gst_pad_get_peer (sink_harness->sinkpad);
+      fail_unless (srcpad != NULL);
+      g_object_get (srcpad, "transceiver", &rtp_trans, NULL);
+      gst_clear_object (&srcpad);
+      fail_unless (rtp_trans);
+      g_object_get (rtp_trans, "mid", &trans_mid, "mlineindex", &mlineindex,
+          NULL);
+      gst_clear_object (&rtp_trans);
+      expected_mid = g_strdup_printf ("audio%u", mlineindex);
+      fail_unless (trans_mid != NULL);
+      fail_unless_equals_string (trans_mid, expected_mid);
+      g_clear_pointer (&trans_mid, g_free);
+      g_clear_pointer (&expected_mid, g_free);
+
+      fail_unless (gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp));
+
+      ssrc = gst_rtp_buffer_get_ssrc (&rtp);
+      for (i = 0; i < ssrcs_received->len; i++) {
+        if (g_array_index (ssrcs_received, guint, i) == ssrc)
+          break;
+      }
+      if (i == ssrcs_received->len) {
+        g_array_append_val (ssrcs_received, ssrc);
+      }
+
+      gst_rtp_buffer_unmap (&rtp);
+
+      gst_buffer_unref (buf);
+    }
+  }
+  g_mutex_unlock (&t->lock);
+
+  GST_DEBUG_BIN_TO_DOT_FILE (GST_BIN (t->webrtc1), GST_DEBUG_GRAPH_SHOW_ALL,
+      "webrtc1-fec-final");
+  GST_DEBUG_BIN_TO_DOT_FILE (GST_BIN (t->webrtc2), GST_DEBUG_GRAPH_SHOW_ALL,
+      "webrtc2-fec-final");
+
+  test_webrtc_free (t);
+  g_list_free (sink_harnesses);
+
+  g_array_unref (ssrcs_received);
+}
+
+GST_END_TEST;
+
 static Suite *
 webrtcbin_suite (void)
 {
@@ -4616,6 +4783,7 @@ webrtcbin_suite (void)
     tcase_add_test (tc, test_codec_preferences_invalid_extmap);
     tcase_add_test (tc, test_renego_rtx);
     tcase_add_test (tc, test_bundle_mid_header_extension);
+    tcase_add_test (tc, test_max_bundle_fec);
     if (sctpenc && sctpdec) {
       tcase_add_test (tc, test_data_channel_create);
       tcase_add_test (tc, test_data_channel_remote_notify);