tests/webrtc: always use a unique SSRC for each stream
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / tests / check / elements / webrtcbin.c
index da09e5d..ecb5567 100644 (file)
@@ -35,8 +35,8 @@
 #include "../../../ext/webrtc/utils.c"
 
 #define OPUS_RTP_CAPS(pt) "application/x-rtp,payload=" G_STRINGIFY(pt) ",encoding-name=OPUS,media=audio,clock-rate=48000,ssrc=(uint)3384078950"
-#define VP8_RTP_CAPS(pt) "application/x-rtp,payload=" G_STRINGIFY(pt) ",encoding-name=VP8,media=video,clock-rate=90000,ssrc=(uint)3484078950"
-#define H264_RTP_CAPS(pt) "application/x-rtp,payload=" G_STRINGIFY(pt) ",encoding-name=H264,media=video,clock-rate=90000,ssrc=(uint)3484078951"
+#define VP8_RTP_CAPS(pt) "application/x-rtp,payload=" G_STRINGIFY(pt) ",encoding-name=VP8,media=video,clock-rate=90000,ssrc=(uint)3484078951"
+#define H264_RTP_CAPS(pt) "application/x-rtp,payload=" G_STRINGIFY(pt) ",encoding-name=H264,media=video,clock-rate=90000,ssrc=(uint)3484078952"
 
 #define TEST_IS_OFFER_ELEMENT(t, e) ((((t)->offerror == 1 && (e) == (t)->webrtc1) || ((t)->offerror == 2 && (e) == (t)->webrtc2)) ? TRUE : FALSE)
 #define TEST_GET_OFFEROR(t) (TEST_IS_OFFER_ELEMENT(t, t->webrtc1) ? (t)->webrtc1 : t->webrtc2)
@@ -982,20 +982,24 @@ on_sdp_media_setup (struct test_webrtc *t, GstElement * element,
 }
 
 static void
-add_fake_audio_src_harness (GstHarness * h, gint pt)
+add_fake_audio_src_harness (GstHarness * h, gint pt, guint ssrc)
 {
   GstCaps *caps = gst_caps_from_string (OPUS_RTP_CAPS (pt));
   GstStructure *s = gst_caps_get_structure (caps, 0);
+  if (ssrc != 0)
+    gst_structure_set (s, "ssrc", G_TYPE_UINT, ssrc, NULL);
   gst_structure_set (s, "payload", G_TYPE_INT, pt, NULL);
   gst_harness_set_src_caps (h, caps);
   gst_harness_add_src_parse (h, "fakesrc is-live=true", TRUE);
 }
 
 static void
-add_fake_video_src_harness (GstHarness * h, gint pt)
+add_fake_video_src_harness (GstHarness * h, gint pt, guint ssrc)
 {
   GstCaps *caps = gst_caps_from_string (VP8_RTP_CAPS (pt));
   GstStructure *s = gst_caps_get_structure (caps, 0);
+  if (ssrc != 0)
+    gst_structure_set (s, "ssrc", G_TYPE_UINT, ssrc, NULL);
   gst_structure_set (s, "payload", G_TYPE_INT, pt, NULL);
   gst_harness_set_src_caps (h, caps);
   gst_harness_add_src_parse (h, "fakesrc is-live=true", TRUE);
@@ -1012,7 +1016,7 @@ create_audio_test (void)
   t->on_pad_added = _pad_added_fakesink;
 
   h = gst_harness_new_with_element (t->webrtc1, "sink_0", NULL);
-  add_fake_audio_src_harness (h, 96);
+  add_fake_audio_src_harness (h, 96, 0xDEADBEEF);
   t->harnesses = g_list_prepend (t->harnesses, h);
 
   return t;
@@ -1128,7 +1132,7 @@ create_audio_video_test (void)
   GstHarness *h;
 
   h = gst_harness_new_with_element (t->webrtc1, "sink_1", NULL);
-  add_fake_video_src_harness (h, 97);
+  add_fake_video_src_harness (h, 97, 0xBEEFDEAD);
   t->harnesses = g_list_prepend (t->harnesses, h);
 
   return t;
@@ -1192,7 +1196,7 @@ GST_START_TEST (test_media_direction)
   /* check the default media directions for transceivers */
 
   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, 0xDEADBEEF);
   t->harnesses = g_list_prepend (t->harnesses, h);
 
   test_validate_sdp (t, &offer, &answer);
@@ -1628,6 +1632,30 @@ GST_START_TEST (test_get_transceivers)
 
 GST_END_TEST;
 
+static void
+on_sdp_media_check_mid (struct test_webrtc *t, GstElement * element,
+    GstWebRTCSessionDescription * desc, gpointer user_data)
+{
+  const char **mid = user_data;
+  guint i;
+
+  for (i = 0; i < gst_sdp_message_medias_len (desc->sdp); i++) {
+    const GstSDPMedia *media = gst_sdp_message_get_media (desc->sdp, i);
+    gboolean seen_mid = FALSE;
+    guint j;
+
+    for (j = 0; j < gst_sdp_media_attributes_len (media); j++) {
+      const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, j);
+
+      if (g_strcmp0 (attr->key, "mid") == 0) {
+        fail_unless (!seen_mid);
+        seen_mid = TRUE;
+        fail_unless_equals_string (attr->value, mid[i]);
+      }
+    }
+  }
+}
+
 GST_START_TEST (test_add_recvonly_transceiver)
 {
   struct test_webrtc *t = test_webrtc_new ();
@@ -1640,11 +1668,12 @@ GST_START_TEST (test_add_recvonly_transceiver)
       media_format_count, &no_duplicate_payloads);
   VAL_SDP_INIT (count, _count_num_sdp_media, GUINT_TO_POINTER (1),
       &media_formats);
+  const char *expected_mid[] = { "gst", };
+  VAL_SDP_INIT (mid, on_sdp_media_check_mid, expected_mid, &count);
   const gchar *expected_offer_setup[] = { "actpass", };
-  VAL_SDP_INIT (offer_setup, on_sdp_media_setup, expected_offer_setup, &count);
+  VAL_SDP_INIT (offer_setup, on_sdp_media_setup, expected_offer_setup, &mid);
   const gchar *expected_answer_setup[] = { "active", };
-  VAL_SDP_INIT (answer_setup, on_sdp_media_setup, expected_answer_setup,
-      &count);
+  VAL_SDP_INIT (answer_setup, on_sdp_media_setup, expected_answer_setup, &mid);
   const gchar *expected_offer_direction[] = { "recvonly", };
   VAL_SDP_INIT (offer, on_sdp_media_direction, expected_offer_direction,
       &offer_setup);
@@ -1661,7 +1690,7 @@ GST_START_TEST (test_add_recvonly_transceiver)
   t->on_pad_added = _pad_added_fakesink;
 
   /* setup recvonly transceiver */
-  caps = gst_caps_from_string (OPUS_RTP_CAPS (96));
+  caps = gst_caps_from_string (OPUS_RTP_CAPS (96) ", a-mid=(string)gst");
   direction = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY;
   g_signal_emit_by_name (t->webrtc1, "add-transceiver", direction, caps,
       &trans);
@@ -1671,7 +1700,7 @@ GST_START_TEST (test_add_recvonly_transceiver)
 
   /* setup sendonly peer */
   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, 0xDEADBEEF);
   t->harnesses = g_list_prepend (t->harnesses, h);
   test_validate_sdp (t, &offer, &answer);
 
@@ -1715,6 +1744,7 @@ GST_START_TEST (test_recvonly_sendonly)
 
   /* setup recvonly transceiver */
   caps = gst_caps_from_string (OPUS_RTP_CAPS (96));
+  gst_caps_set_simple (caps, "ssrc", G_TYPE_UINT, 0xDEADBEEF, NULL);
   direction = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY;
   g_signal_emit_by_name (t->webrtc1, "add-transceiver", direction, caps,
       &trans);
@@ -1724,7 +1754,7 @@ GST_START_TEST (test_recvonly_sendonly)
 
   /* setup sendonly stream */
   h = gst_harness_new_with_element (t->webrtc1, "sink_1", NULL);
-  add_fake_audio_src_harness (h, 96);
+  add_fake_audio_src_harness (h, 96, 0xBEEFDEAD);
   t->harnesses = g_list_prepend (t->harnesses, h);
   g_signal_emit_by_name (t->webrtc1, "get-transceivers", &transceivers);
   fail_unless (transceivers != NULL);
@@ -1737,7 +1767,7 @@ GST_START_TEST (test_recvonly_sendonly)
 
   /* setup sendonly peer */
   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, 0xDEADBEEF);
   t->harnesses = g_list_prepend (t->harnesses, h);
 
   test_validate_sdp (t, &offer, &answer);
@@ -2764,7 +2794,7 @@ GST_START_TEST (test_duplicate_nego)
   t->negotiation_data = &negotiation_flag;
 
   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, 0xDEADBEEF);
   t->harnesses = g_list_prepend (t->harnesses, h);
 
   test_validate_sdp (t, &offer, &answer);
@@ -2807,11 +2837,11 @@ GST_START_TEST (test_dual_audio)
   /* test that each mline gets a unique transceiver even with the same caps */
 
   h = gst_harness_new_with_element (t->webrtc1, "sink_1", NULL);
-  add_fake_audio_src_harness (h, 96);
+  add_fake_audio_src_harness (h, 96, 0xBEEFDEAD);
   t->harnesses = g_list_prepend (t->harnesses, h);
 
   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, 0xDEADBEEF);
   t->harnesses = g_list_prepend (t->harnesses, h);
 
   t->on_negotiation_needed = NULL;
@@ -2993,13 +3023,13 @@ GST_START_TEST (test_renego_add_stream)
 
   /* 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);
+  add_fake_audio_src_harness (h, 96, 0xDEADBEEF);
   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);
+  add_fake_audio_src_harness (h, 98, 0xBEEFFFFF);
   t->harnesses = g_list_prepend (t->harnesses, h);
 
   media_formats.next = &renego_fingerprint;
@@ -3049,7 +3079,7 @@ GST_START_TEST (test_renego_stream_add_data_channel)
 
   /* 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);
+  add_fake_audio_src_harness (h, 96, 0xDEADBEEF);
   t->harnesses = g_list_prepend (t->harnesses, h);
 
   test_validate_sdp (t, &offer, &answer);
@@ -3119,7 +3149,7 @@ GST_START_TEST (test_renego_data_channel_add_stream)
   test_validate_sdp_full (t, &offer, &answer, 0, FALSE);
 
   h = gst_harness_new_with_element (t->webrtc1, "sink_1", NULL);
-  add_fake_audio_src_harness (h, 97);
+  add_fake_audio_src_harness (h, 97, 0xDEADBEEF);
   t->harnesses = g_list_prepend (t->harnesses, h);
 
   media_formats.next = &renego_fingerprint;
@@ -3175,7 +3205,7 @@ GST_START_TEST (test_renego_stream_data_channel_add_stream)
   t->on_pad_added = _pad_added_fakesink;
 
   h = gst_harness_new_with_element (t->webrtc1, "sink_0", NULL);
-  add_fake_audio_src_harness (h, 97);
+  add_fake_audio_src_harness (h, 97, 0xDEADBEEF);
   t->harnesses = g_list_prepend (t->harnesses, h);
 
   fail_if (gst_element_set_state (t->webrtc1,
@@ -3189,7 +3219,7 @@ GST_START_TEST (test_renego_stream_data_channel_add_stream)
   test_validate_sdp_full (t, &offer, &answer, 0, FALSE);
 
   h = gst_harness_new_with_element (t->webrtc1, "sink_2", NULL);
-  add_fake_audio_src_harness (h, 97);
+  add_fake_audio_src_harness (h, 97, 0xBEEFDEAD);
   t->harnesses = g_list_prepend (t->harnesses, h);
 
   media_formats.next = &renego_fingerprint;
@@ -3268,13 +3298,13 @@ GST_START_TEST (test_bundle_renego_add_stream)
 
   /* 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);
+  add_fake_audio_src_harness (h, 96, 0xDEADBEEF);
   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);
+  add_fake_audio_src_harness (h, 98, 0xBEEFFFFF);
   t->harnesses = g_list_prepend (t->harnesses, h);
 
   offer_setup.next = &offer_bundle_only_sdp;
@@ -3348,13 +3378,13 @@ GST_START_TEST (test_bundle_max_compat_max_bundle_renego_add_stream)
 
   /* 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);
+  add_fake_audio_src_harness (h, 96, 0xDEADBEEF);
   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);
+  add_fake_audio_src_harness (h, 98, 0xBEEFFFFF);
   t->harnesses = g_list_prepend (t->harnesses, h);
 
   media_formats.next = &bundle_sdp;
@@ -3396,7 +3426,7 @@ GST_START_TEST (test_renego_transceiver_set_direction)
 
   /* 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);
+  add_fake_audio_src_harness (h, 96, 0xDEADBEEF);
   t->harnesses = g_list_prepend (t->harnesses, h);
 
   test_validate_sdp (t, &offer, &answer);
@@ -3490,6 +3520,19 @@ offer_set_produced_error (struct test_webrtc *t, GstElement * element,
   test_webrtc_signal_state_unlocked (t, STATE_CUSTOM);
 }
 
+static void
+offer_created_produced_error (struct test_webrtc *t, GstElement * element,
+    GstPromise * promise, gpointer user_data)
+{
+  const GstStructure *reply;
+  GError *error = NULL;
+
+  reply = gst_promise_get_reply (promise);
+  fail_unless (gst_structure_get (reply, "error", G_TYPE_ERROR, &error, NULL));
+  GST_INFO ("error produced: %s", error->message);
+  g_clear_error (&error);
+}
+
 GST_START_TEST (test_renego_lose_media_fails)
 {
   struct test_webrtc *t = create_audio_video_test ();
@@ -3564,7 +3607,7 @@ GST_START_TEST (test_bundle_codec_preferences_rtx_no_duplicate_payloads)
 
   /* setup sendonly peer */
   h = gst_harness_new_with_element (t->webrtc2, "sink_0", NULL);
-  add_fake_video_src_harness (h, 96);
+  add_fake_video_src_harness (h, 96, 0xDEADBEEF);
   t->harnesses = g_list_prepend (t->harnesses, h);
   test_validate_sdp (t, &offer, &answer);
 
@@ -3573,6 +3616,130 @@ GST_START_TEST (test_bundle_codec_preferences_rtx_no_duplicate_payloads)
 
 GST_END_TEST;
 
+static void
+on_sdp_media_no_duplicate_extmaps (struct test_webrtc *t, GstElement * element,
+    GstWebRTCSessionDescription * desc, gpointer user_data)
+{
+  const GstSDPMedia *media = gst_sdp_message_get_media (desc->sdp, 0);
+
+  fail_unless (media != NULL);
+
+  fail_unless_equals_string (gst_sdp_media_get_attribute_val_n (media, "extmap",
+          0), "1 foobar");
+
+  fail_unless (gst_sdp_media_get_attribute_val_n (media, "extmap", 1) == NULL);
+}
+
+/* In this test, we validate that identical extmaps for multiple formats
+ * in the caps of a single transceiver are deduplicated. This is necessary
+ * because Firefox will complain about duplicate extmap ids and fail negotiation
+ * otherwise. */
+GST_START_TEST (test_codec_preferences_no_duplicate_extmaps)
+{
+  struct test_webrtc *t = test_webrtc_new ();
+  GstWebRTCRTPTransceiver *trans;
+  GstWebRTCRTPTransceiverDirection direction;
+  VAL_SDP_INIT (extmaps, on_sdp_media_no_duplicate_extmaps, NULL, NULL);
+  GstCaps *caps;
+  GstStructure *s;
+
+  caps = gst_caps_new_empty ();
+
+  s = gst_structure_from_string (VP8_RTP_CAPS (96), NULL);
+  gst_structure_set (s, "extmap-1", G_TYPE_STRING, "foobar", NULL);
+  gst_caps_append_structure (caps, s);
+  s = gst_structure_from_string (H264_RTP_CAPS (97), NULL);
+  gst_structure_set (s, "extmap-1", G_TYPE_STRING, "foobar", NULL);
+  gst_caps_append_structure (caps, s);
+
+  direction = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY;
+  g_signal_emit_by_name (t->webrtc1, "add-transceiver", direction, caps,
+      &trans);
+  gst_caps_unref (caps);
+  fail_unless (trans != NULL);
+
+  t->on_negotiation_needed = NULL;
+  t->on_pad_added = NULL;
+  t->on_ice_candidate = NULL;
+
+  test_validate_sdp (t, &extmaps, NULL);
+
+  test_webrtc_free (t);
+}
+
+GST_END_TEST;
+
+/* In this test, we validate that trying to use different values
+ * for the same extmap id in multiple formats in the caps of a
+ * single transceiver errors out when creating the offer. */
+GST_START_TEST (test_codec_preferences_incompatible_extmaps)
+{
+  struct test_webrtc *t = test_webrtc_new ();
+  GstWebRTCRTPTransceiver *trans;
+  GstWebRTCRTPTransceiverDirection direction;
+  GstCaps *caps;
+  GstStructure *s;
+
+  caps = gst_caps_new_empty ();
+
+  s = gst_structure_from_string (VP8_RTP_CAPS (96), NULL);
+  gst_structure_set (s, "extmap-1", G_TYPE_STRING, "foobar", NULL);
+  gst_caps_append_structure (caps, s);
+  s = gst_structure_from_string (H264_RTP_CAPS (97), NULL);
+  gst_structure_set (s, "extmap-1", G_TYPE_STRING, "foobaz", NULL);
+  gst_caps_append_structure (caps, s);
+
+  direction = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY;
+  g_signal_emit_by_name (t->webrtc1, "add-transceiver", direction, caps,
+      &trans);
+  gst_caps_unref (caps);
+  fail_unless (trans != NULL);
+
+  t->on_negotiation_needed = NULL;
+  t->on_pad_added = NULL;
+  t->on_ice_candidate = NULL;
+  t->on_offer_created = offer_created_produced_error;
+
+  test_validate_sdp_full (t, NULL, NULL, STATE_OFFER_CREATED, TRUE);
+
+  test_webrtc_free (t);
+}
+
+GST_END_TEST;
+
+/* In this test, we validate that extmap values must be of the correct type */
+GST_START_TEST (test_codec_preferences_invalid_extmap)
+{
+  struct test_webrtc *t = test_webrtc_new ();
+  GstWebRTCRTPTransceiver *trans;
+  GstWebRTCRTPTransceiverDirection direction;
+  GstCaps *caps;
+  GstStructure *s;
+
+  caps = gst_caps_new_empty ();
+
+  s = gst_structure_from_string (VP8_RTP_CAPS (96), NULL);
+  gst_structure_set (s, "extmap-1", G_TYPE_INT, 42, NULL);
+  gst_caps_append_structure (caps, s);
+
+  direction = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY;
+  g_signal_emit_by_name (t->webrtc1, "add-transceiver", direction, caps,
+      &trans);
+  gst_caps_unref (caps);
+  fail_unless (trans != NULL);
+
+  t->on_negotiation_needed = NULL;
+  t->on_pad_added = NULL;
+  t->on_ice_candidate = NULL;
+  t->on_offer_created = offer_created_produced_error;
+
+  test_validate_sdp_full (t, NULL, NULL, STATE_OFFER_CREATED, TRUE);
+
+  test_webrtc_free (t);
+}
+
+GST_END_TEST;
+
 GST_START_TEST (test_reject_request_pad)
 {
   struct test_webrtc *t = test_webrtc_new ();
@@ -3620,7 +3787,7 @@ GST_START_TEST (test_reject_request_pad)
   fail_unless (trans != NULL);
 
   h = gst_harness_new_with_element (t->webrtc2, "sink_0", NULL);
-  add_fake_video_src_harness (h, 96);
+  add_fake_video_src_harness (h, 96, 0xDEADBEEF);
   t->harnesses = g_list_prepend (t->harnesses, h);
 
   test_validate_sdp (t, &offer, &answer);
@@ -3714,7 +3881,7 @@ GST_START_TEST (test_reject_create_offer)
 
   /* setup sendonly peer */
   h = gst_harness_new_with_element (t->webrtc1, "sink_1", NULL);
-  add_fake_audio_src_harness (h, 96);
+  add_fake_audio_src_harness (h, 96, 0xDEADBEEF);
   t->harnesses = g_list_prepend (t->harnesses, h);
 
   /* Check that if there is no 0, we can't create an offer with a hole */
@@ -3724,15 +3891,17 @@ GST_START_TEST (test_reject_create_offer)
   fail_unless_equals_int (res, GST_PROMISE_RESULT_REPLIED);
   s = gst_promise_get_reply (promise);
   fail_unless (s != NULL);
-  fail_unless (gst_structure_has_name (s, "application/x-gstwebrtcbin-error"));
   gst_structure_get (s, "error", G_TYPE_ERROR, &error, NULL);
-  fail_unless (g_error_matches (error, GST_WEBRTC_BIN_ERROR,
-          GST_WEBRTC_BIN_ERROR_IMPOSSIBLE_MLINE_RESTRICTION));
+  fail_unless (g_error_matches (error, GST_WEBRTC_ERROR,
+          GST_WEBRTC_ERROR_INTERNAL_FAILURE));
+  fail_unless (g_str_match_string
+      ("has locked mline 1 but the whole offer only has 0 sections",
+          error->message, FALSE));
   g_clear_error (&error);
   gst_promise_unref (promise);
 
   h = gst_harness_new_with_element (t->webrtc1, "sink_%u", NULL);
-  add_fake_video_src_harness (h, 97);
+  add_fake_video_src_harness (h, 97, 0xBEEFDEAD);
   t->harnesses = g_list_prepend (t->harnesses, h);
 
   /* Adding a second sink, which will fill m-line 0, should fix it */
@@ -3762,7 +3931,7 @@ GST_START_TEST (test_reject_set_description)
 
   /* setup peer 1 */
   h = gst_harness_new_with_element (t->webrtc1, "sink_0", NULL);
-  add_fake_audio_src_harness (h, 96);
+  add_fake_audio_src_harness (h, 96, 0xDEADBEEF);
   t->harnesses = g_list_prepend (t->harnesses, h);
 
   /* Create a second side with specific video caps */
@@ -3781,7 +3950,6 @@ GST_START_TEST (test_reject_set_description)
   fail_unless_equals_int (res, GST_PROMISE_RESULT_REPLIED);
   s = gst_promise_get_reply (promise);
   fail_unless (s != NULL);
-  fail_unless (gst_structure_has_name (s, "application/x-gst-promise"));
   gst_structure_get (s, "offer", GST_TYPE_WEBRTC_SESSION_DESCRIPTION, &desc,
       NULL);
   fail_unless (desc != NULL);
@@ -3797,10 +3965,13 @@ GST_START_TEST (test_reject_set_description)
   res = gst_promise_wait (promise);
   fail_unless_equals_int (res, GST_PROMISE_RESULT_REPLIED);
   s = gst_promise_get_reply (promise);
-  fail_unless (gst_structure_has_name (s, "application/x-gstwebrtcbin-error"));
   gst_structure_get (s, "error", G_TYPE_ERROR, &error, NULL);
-  fail_unless (g_error_matches (error, GST_WEBRTC_BIN_ERROR,
-          GST_WEBRTC_BIN_ERROR_IMPOSSIBLE_MLINE_RESTRICTION));
+  fail_unless (g_error_matches (error, GST_WEBRTC_ERROR,
+          GST_WEBRTC_ERROR_INTERNAL_FAILURE));
+  fail_unless (g_str_match_string
+      ("m-line 0 was locked to audio, but SDP has audio media", error->message,
+          FALSE));
+
   g_clear_error (&error);
   fail_unless (s != NULL);
   gst_promise_unref (promise);
@@ -3874,7 +4045,7 @@ GST_START_TEST (test_force_second_media)
 
   /* setup peer */
   h = gst_harness_new_with_element (t->webrtc1, "sink_0", NULL);
-  add_fake_audio_src_harness (h, 96);
+  add_fake_audio_src_harness (h, 96, 0xDEADBEEF);
   t->harnesses = g_list_prepend (t->harnesses, h);
 
   /* Create a second side with specific video caps */
@@ -3886,7 +4057,7 @@ GST_START_TEST (test_force_second_media)
   fail_unless (pad != NULL);
   h = gst_harness_new_with_element (t->webrtc2, GST_PAD_NAME (pad), NULL);
   gst_object_unref (pad);
-  add_fake_video_src_harness (h, 97);
+  add_fake_video_src_harness (h, 97, 0xBEEFDEAD);
   t->harnesses = g_list_prepend (t->harnesses, h);
 
   test_validate_sdp (t, &offer_count, &answer_count);
@@ -3985,7 +4156,7 @@ GST_START_TEST (test_codec_preferences_negotiation_sinkpad)
   gst_object_unref (transceiver);
   gst_object_unref (pad);
 
-  add_fake_video_src_harness (h, 96);
+  add_fake_video_src_harness (h, 96, 0xDEADBEEF);
   t->harnesses = g_list_prepend (t->harnesses, h);
 
   promise = gst_promise_new ();
@@ -3994,10 +4165,12 @@ GST_START_TEST (test_codec_preferences_negotiation_sinkpad)
   fail_unless_equals_int (res, GST_PROMISE_RESULT_REPLIED);
   s = gst_promise_get_reply (promise);
   fail_unless (s != NULL);
-  fail_unless (gst_structure_has_name (s, "application/x-gstwebrtcbin-error"));
   gst_structure_get (s, "error", G_TYPE_ERROR, &error, NULL);
-  fail_unless (g_error_matches (error, GST_WEBRTC_BIN_ERROR,
-          GST_WEBRTC_BIN_ERROR_CAPS_NEGOTIATION_FAILED));
+  fail_unless (g_error_matches (error, GST_WEBRTC_ERROR,
+          GST_WEBRTC_ERROR_INTERNAL_FAILURE));
+  fail_unless (g_str_match_string
+      ("Caps negotiation on pad sink_0 failed against codec preferences",
+          error->message, FALSE));
   g_clear_error (&error);
   gst_promise_unref (promise);
 
@@ -4013,14 +4186,24 @@ GST_END_TEST;
 
 
 static void
-add_audio_test_src_harness (GstHarness * h)
+add_audio_test_src_harness (GstHarness * h, guint ssrc)
 {
 #define L16_CAPS "application/x-rtp, payload=11, media=audio," \
       " encoding-name=L16, clock-rate=44100, ssrc=(uint)3484078952"
   GstCaps *caps = gst_caps_from_string (L16_CAPS);
-  gst_harness_set_src_caps (h, caps);
+  GstElement *capsfilter;
+  if (ssrc != 0) {
+    gst_caps_set_simple (caps, "ssrc", G_TYPE_UINT, ssrc, NULL);
+  }
   gst_harness_add_src_parse (h, "audiotestsrc is-live=true ! rtpL16pay ! "
-      L16_CAPS " ! identity", TRUE);
+      "capsfilter name=capsfilter ! identity", TRUE);
+  capsfilter =
+      gst_bin_get_by_name (GST_BIN (h->src_harness->element), "capsfilter");
+  g_object_set (G_OBJECT (capsfilter), "caps", caps, NULL);
+  gst_harness_set_src_caps (h, caps);
+  caps = NULL;
+  gst_clear_object (&capsfilter);
+#undef L16_CAPS
 }
 
 static void
@@ -4091,7 +4274,7 @@ GST_START_TEST (test_codec_preferences_negotiation_srcpad)
   g_object_unref (rtpbin2);
 
   h = gst_harness_new_with_element (t->webrtc1, "sink_0", NULL);
-  add_audio_test_src_harness (h);
+  add_audio_test_src_harness (h, 0xDEADBEEF);
   t->harnesses = g_list_prepend (t->harnesses, h);
 
   test_validate_sdp (t, &offer, &answer);
@@ -4212,7 +4395,7 @@ GST_START_TEST (test_codec_preferences_in_on_new_transceiver)
 
   /* setup recvonly peer */
   h = gst_harness_new_with_element (t->webrtc2, "sink_0", NULL);
-  add_fake_video_src_harness (h, 101);
+  add_fake_video_src_harness (h, 101, 0);
   t->harnesses = g_list_prepend (t->harnesses, h);
 
   /* connect to "on-new-transceiver" to set codec-preferences to H264 */
@@ -4228,6 +4411,123 @@ GST_START_TEST (test_codec_preferences_in_on_new_transceiver)
 
 GST_END_TEST;
 
+GST_START_TEST (test_renego_rtx)
+{
+  struct test_webrtc *t = create_audio_video_test ();
+  VAL_SDP_INIT (no_duplicate_payloads, on_sdp_media_no_duplicate_payloads,
+      NULL, NULL);
+  guint media_format_count[] = { 1, 1 };
+  VAL_SDP_INIT (media_formats, on_sdp_media_count_formats,
+      media_format_count, &no_duplicate_payloads);
+  VAL_SDP_INIT (count_media, _count_num_sdp_media, GUINT_TO_POINTER (2),
+      &media_formats);
+  VAL_SDP_INIT (payloads, on_sdp_media_payload_types,
+      GUINT_TO_POINTER (1), &count_media);
+  const gchar *expected_offer_direction[] = { "sendrecv", "sendrecv", };
+  VAL_SDP_INIT (offer_direction, on_sdp_media_direction,
+      expected_offer_direction, &payloads);
+  const gchar *expected_answer_direction[] = { "recvonly", "recvonly", };
+  VAL_SDP_INIT (answer_direction, on_sdp_media_direction,
+      expected_answer_direction, &payloads);
+  const gchar *expected_offer_setup[] = { "actpass", "actpass", };
+  VAL_SDP_INIT (offer, on_sdp_media_setup, expected_offer_setup,
+      &offer_direction);
+  const gchar *expected_answer_setup[] = { "active", "active", };
+  VAL_SDP_INIT (answer, on_sdp_media_setup, expected_answer_setup,
+      &answer_direction);
+  GstWebRTCRTPTransceiver *trans;
+
+  t->on_negotiation_needed = NULL;
+  t->on_ice_candidate = NULL;
+  t->on_pad_added = _pad_added_fakesink;
+
+  test_validate_sdp (t, &offer, &answer);
+
+  test_webrtc_reset_negotiation (t);
+
+  g_signal_emit_by_name (t->webrtc1, "get-transceiver", 1, &trans);
+  g_object_set (trans, "do-nack", TRUE, "fec-type",
+      GST_WEBRTC_FEC_TYPE_ULP_RED, NULL);
+  g_clear_object (&trans);
+
+  g_signal_emit_by_name (t->webrtc2, "get-transceiver", 1, &trans);
+  g_object_set (trans, "do-nack", TRUE, "fec-type",
+      GST_WEBRTC_FEC_TYPE_ULP_RED, NULL);
+  g_clear_object (&trans);
+
+  /* adding RTX/RED/FEC increases the number of media formats */
+  media_format_count[1] = 5;
+
+  test_validate_sdp (t, &offer, &answer);
+
+  test_webrtc_free (t);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_bundle_mid_header_extension)
+{
+  struct test_webrtc *t = test_webrtc_new ();
+  GstWebRTCRTPTransceiverDirection direction;
+  GstWebRTCRTPTransceiver *trans;
+  VAL_SDP_INIT (no_duplicate_payloads, on_sdp_media_no_duplicate_payloads,
+      NULL, NULL);
+  guint media_format_count[] = { 1, 1, };
+  VAL_SDP_INIT (media_formats, on_sdp_media_count_formats,
+      media_format_count, &no_duplicate_payloads);
+  VAL_SDP_INIT (count, _count_num_sdp_media, GUINT_TO_POINTER (1),
+      &media_formats);
+  const char *expected_mid[] = { "gst", };
+  VAL_SDP_INIT (mid, on_sdp_media_check_mid, expected_mid, &count);
+  const gchar *expected_offer_setup[] = { "actpass", };
+  VAL_SDP_INIT (offer_setup, on_sdp_media_setup, expected_offer_setup, &mid);
+  const gchar *expected_answer_setup[] = { "active", };
+  VAL_SDP_INIT (answer_setup, on_sdp_media_setup, expected_answer_setup, &mid);
+  const gchar *expected_offer_direction[] = { "recvonly", };
+  VAL_SDP_INIT (offer, on_sdp_media_direction, expected_offer_direction,
+      &offer_setup);
+  const gchar *expected_answer_direction[] = { "sendonly", };
+  VAL_SDP_INIT (answer, on_sdp_media_direction, expected_answer_direction,
+      &answer_setup);
+  GstCaps *caps;
+  GstHarness *h;
+  guint mline;
+  char *trans_mid;
+
+  /* add a transceiver that will only receive an opus stream and check that
+   * the created offer is marked as recvonly */
+  t->on_negotiation_needed = NULL;
+  t->on_ice_candidate = NULL;
+  t->on_pad_added = _pad_added_fakesink;
+
+  /* setup recvonly transceiver */
+  caps = gst_caps_from_string (OPUS_RTP_CAPS (96) ", a-mid=(string)gst");
+  direction = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY;
+  g_signal_emit_by_name (t->webrtc1, "add-transceiver", direction, caps,
+      &trans);
+  gst_caps_unref (caps);
+  fail_unless (trans != NULL);
+  g_object_get (trans, "mlineindex", &mline, NULL);
+  fail_unless_equals_int (mline, -1);
+
+  /* setup sendonly peer */
+  h = gst_harness_new_with_element (t->webrtc2, "sink_0", NULL);
+  add_fake_audio_src_harness (h, 96, 0xDEADBEEF);
+  t->harnesses = g_list_prepend (t->harnesses, h);
+
+  test_validate_sdp (t, &offer, &answer);
+
+  g_object_get (trans, "mlineindex", &mline, "mid", &trans_mid, NULL);
+  fail_unless_equals_int (mline, 0);
+  fail_unless_equals_string (trans_mid, "gst");
+  g_clear_pointer (&trans_mid, g_free);
+  gst_object_unref (trans);
+
+  test_webrtc_free (t);
+}
+
+GST_END_TEST;
+
 static Suite *
 webrtcbin_suite (void)
 {
@@ -4279,6 +4579,11 @@ webrtcbin_suite (void)
     tcase_add_test (tc, test_codec_preferences_negotiation_sinkpad);
     tcase_add_test (tc, test_codec_preferences_negotiation_srcpad);
     tcase_add_test (tc, test_codec_preferences_in_on_new_transceiver);
+    tcase_add_test (tc, test_codec_preferences_no_duplicate_extmaps);
+    tcase_add_test (tc, test_codec_preferences_incompatible_extmaps);
+    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);
     if (sctpenc && sctpdec) {
       tcase_add_test (tc, test_data_channel_create);
       tcase_add_test (tc, test_data_channel_remote_notify);