summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/pc/peer_connection_header_extension_unittest.cc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /third_party/libwebrtc/pc/peer_connection_header_extension_unittest.cc
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/libwebrtc/pc/peer_connection_header_extension_unittest.cc')
-rw-r--r--third_party/libwebrtc/pc/peer_connection_header_extension_unittest.cc589
1 files changed, 589 insertions, 0 deletions
diff --git a/third_party/libwebrtc/pc/peer_connection_header_extension_unittest.cc b/third_party/libwebrtc/pc/peer_connection_header_extension_unittest.cc
new file mode 100644
index 0000000000..b1c6c3cfb5
--- /dev/null
+++ b/third_party/libwebrtc/pc/peer_connection_header_extension_unittest.cc
@@ -0,0 +1,589 @@
+/*
+ * Copyright 2020 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <memory>
+#include <string>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "absl/strings/string_view.h"
+#include "absl/types/optional.h"
+#include "api/call/call_factory_interface.h"
+#include "api/jsep.h"
+#include "api/media_types.h"
+#include "api/peer_connection_interface.h"
+#include "api/rtc_error.h"
+#include "api/rtc_event_log/rtc_event_log_factory.h"
+#include "api/rtc_event_log/rtc_event_log_factory_interface.h"
+#include "api/rtp_parameters.h"
+#include "api/rtp_transceiver_direction.h"
+#include "api/rtp_transceiver_interface.h"
+#include "api/scoped_refptr.h"
+#include "api/task_queue/default_task_queue_factory.h"
+#include "api/task_queue/task_queue_factory.h"
+#include "media/base/fake_media_engine.h"
+#include "media/base/media_engine.h"
+#include "p2p/base/fake_port_allocator.h"
+#include "p2p/base/port_allocator.h"
+#include "pc/peer_connection_wrapper.h"
+#include "pc/session_description.h"
+#include "pc/test/mock_peer_connection_observers.h"
+#include "rtc_base/internal/default_socket_server.h"
+#include "rtc_base/rtc_certificate_generator.h"
+#include "rtc_base/strings/string_builder.h"
+#include "rtc_base/thread.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+#include "test/scoped_key_value_config.h"
+
+namespace webrtc {
+
+using ::testing::Combine;
+using ::testing::ElementsAre;
+using ::testing::Field;
+using ::testing::Return;
+using ::testing::Values;
+
+class PeerConnectionHeaderExtensionTest
+ : public ::testing::TestWithParam<
+ std::tuple<cricket::MediaType, SdpSemantics>> {
+ protected:
+ PeerConnectionHeaderExtensionTest()
+ : socket_server_(rtc::CreateDefaultSocketServer()),
+ main_thread_(socket_server_.get()),
+ extensions_(
+ {RtpHeaderExtensionCapability("uri1",
+ 1,
+ RtpTransceiverDirection::kStopped),
+ RtpHeaderExtensionCapability("uri2",
+ 2,
+ RtpTransceiverDirection::kSendOnly),
+ RtpHeaderExtensionCapability("uri3",
+ 3,
+ RtpTransceiverDirection::kRecvOnly),
+ RtpHeaderExtensionCapability(
+ "uri4",
+ 4,
+ RtpTransceiverDirection::kSendRecv)}) {}
+
+ std::unique_ptr<PeerConnectionWrapper> CreatePeerConnection(
+ cricket::MediaType media_type,
+ absl::optional<SdpSemantics> semantics) {
+ auto voice = std::make_unique<cricket::FakeVoiceEngine>();
+ auto video = std::make_unique<cricket::FakeVideoEngine>();
+ if (media_type == cricket::MediaType::MEDIA_TYPE_AUDIO)
+ voice->SetRtpHeaderExtensions(extensions_);
+ else
+ video->SetRtpHeaderExtensions(extensions_);
+ auto media_engine = std::make_unique<cricket::CompositeMediaEngine>(
+ std::move(voice), std::move(video));
+ PeerConnectionFactoryDependencies factory_dependencies;
+ factory_dependencies.network_thread = rtc::Thread::Current();
+ factory_dependencies.worker_thread = rtc::Thread::Current();
+ factory_dependencies.signaling_thread = rtc::Thread::Current();
+ factory_dependencies.task_queue_factory = CreateDefaultTaskQueueFactory();
+ factory_dependencies.media_engine = std::move(media_engine);
+ factory_dependencies.call_factory = CreateCallFactory();
+ factory_dependencies.event_log_factory =
+ std::make_unique<RtcEventLogFactory>(
+ factory_dependencies.task_queue_factory.get());
+
+ auto pc_factory =
+ CreateModularPeerConnectionFactory(std::move(factory_dependencies));
+
+ auto fake_port_allocator = std::make_unique<cricket::FakePortAllocator>(
+ rtc::Thread::Current(),
+ std::make_unique<rtc::BasicPacketSocketFactory>(socket_server_.get()),
+ &field_trials_);
+ auto observer = std::make_unique<MockPeerConnectionObserver>();
+ PeerConnectionInterface::RTCConfiguration config;
+ if (semantics)
+ config.sdp_semantics = *semantics;
+ PeerConnectionDependencies pc_dependencies(observer.get());
+ pc_dependencies.allocator = std::move(fake_port_allocator);
+ auto result = pc_factory->CreatePeerConnectionOrError(
+ config, std::move(pc_dependencies));
+ EXPECT_TRUE(result.ok());
+ observer->SetPeerConnectionInterface(result.value().get());
+ return std::make_unique<PeerConnectionWrapper>(
+ pc_factory, result.MoveValue(), std::move(observer));
+ }
+
+ webrtc::test::ScopedKeyValueConfig field_trials_;
+ std::unique_ptr<rtc::SocketServer> socket_server_;
+ rtc::AutoSocketServerThread main_thread_;
+ std::vector<RtpHeaderExtensionCapability> extensions_;
+};
+
+TEST_P(PeerConnectionHeaderExtensionTest, TransceiverOffersHeaderExtensions) {
+ cricket::MediaType media_type;
+ SdpSemantics semantics;
+ std::tie(media_type, semantics) = GetParam();
+ if (semantics != SdpSemantics::kUnifiedPlan)
+ return;
+ std::unique_ptr<PeerConnectionWrapper> wrapper =
+ CreatePeerConnection(media_type, semantics);
+ auto transceiver = wrapper->AddTransceiver(media_type);
+ EXPECT_EQ(transceiver->GetHeaderExtensionsToNegotiate(), extensions_);
+}
+
+TEST_P(PeerConnectionHeaderExtensionTest,
+ SenderReceiverCapabilitiesReturnNotStoppedExtensions) {
+ cricket::MediaType media_type;
+ SdpSemantics semantics;
+ std::tie(media_type, semantics) = GetParam();
+ std::unique_ptr<PeerConnectionWrapper> wrapper =
+ CreatePeerConnection(media_type, semantics);
+ EXPECT_THAT(wrapper->pc_factory()
+ ->GetRtpSenderCapabilities(media_type)
+ .header_extensions,
+ ElementsAre(Field(&RtpHeaderExtensionCapability::uri, "uri2"),
+ Field(&RtpHeaderExtensionCapability::uri, "uri3"),
+ Field(&RtpHeaderExtensionCapability::uri, "uri4")));
+ EXPECT_EQ(wrapper->pc_factory()
+ ->GetRtpReceiverCapabilities(media_type)
+ .header_extensions,
+ wrapper->pc_factory()
+ ->GetRtpSenderCapabilities(media_type)
+ .header_extensions);
+}
+
+TEST_P(PeerConnectionHeaderExtensionTest, OffersUnstoppedDefaultExtensions) {
+ cricket::MediaType media_type;
+ SdpSemantics semantics;
+ std::tie(media_type, semantics) = GetParam();
+ if (semantics != SdpSemantics::kUnifiedPlan)
+ return;
+ std::unique_ptr<PeerConnectionWrapper> wrapper =
+ CreatePeerConnection(media_type, semantics);
+ auto transceiver = wrapper->AddTransceiver(media_type);
+ auto session_description = wrapper->CreateOffer();
+ EXPECT_THAT(session_description->description()
+ ->contents()[0]
+ .media_description()
+ ->rtp_header_extensions(),
+ ElementsAre(Field(&RtpExtension::uri, "uri2"),
+ Field(&RtpExtension::uri, "uri3"),
+ Field(&RtpExtension::uri, "uri4")));
+}
+
+TEST_P(PeerConnectionHeaderExtensionTest, OffersUnstoppedModifiedExtensions) {
+ cricket::MediaType media_type;
+ SdpSemantics semantics;
+ std::tie(media_type, semantics) = GetParam();
+ if (semantics != SdpSemantics::kUnifiedPlan)
+ return;
+ std::unique_ptr<PeerConnectionWrapper> wrapper =
+ CreatePeerConnection(media_type, semantics);
+ auto transceiver = wrapper->AddTransceiver(media_type);
+ auto modified_extensions = transceiver->GetHeaderExtensionsToNegotiate();
+ modified_extensions[0].direction = RtpTransceiverDirection::kSendRecv;
+ modified_extensions[3].direction = RtpTransceiverDirection::kStopped;
+ EXPECT_TRUE(
+ transceiver->SetHeaderExtensionsToNegotiate(modified_extensions).ok());
+ auto session_description = wrapper->CreateOffer();
+ EXPECT_THAT(session_description->description()
+ ->contents()[0]
+ .media_description()
+ ->rtp_header_extensions(),
+ ElementsAre(Field(&RtpExtension::uri, "uri1"),
+ Field(&RtpExtension::uri, "uri2"),
+ Field(&RtpExtension::uri, "uri3")));
+}
+
+TEST_P(PeerConnectionHeaderExtensionTest, AnswersUnstoppedModifiedExtensions) {
+ cricket::MediaType media_type;
+ SdpSemantics semantics;
+ std::tie(media_type, semantics) = GetParam();
+ if (semantics != SdpSemantics::kUnifiedPlan)
+ return;
+ std::unique_ptr<PeerConnectionWrapper> pc1 =
+ CreatePeerConnection(media_type, semantics);
+ std::unique_ptr<PeerConnectionWrapper> pc2 =
+ CreatePeerConnection(media_type, semantics);
+ auto transceiver1 = pc1->AddTransceiver(media_type);
+
+ auto offer = pc1->CreateOfferAndSetAsLocal(
+ PeerConnectionInterface::RTCOfferAnswerOptions());
+ pc2->SetRemoteDescription(std::move(offer));
+
+ ASSERT_EQ(pc2->pc()->GetTransceivers().size(), 1u);
+ auto transceiver2 = pc2->pc()->GetTransceivers()[0];
+ auto modified_extensions = transceiver2->GetHeaderExtensionsToNegotiate();
+ // Don't offer uri4.
+ modified_extensions[3].direction = RtpTransceiverDirection::kStopped;
+ transceiver2->SetHeaderExtensionsToNegotiate(modified_extensions);
+
+ auto answer = pc2->CreateAnswerAndSetAsLocal(
+ PeerConnectionInterface::RTCOfferAnswerOptions());
+ EXPECT_THAT(answer->description()
+ ->contents()[0]
+ .media_description()
+ ->rtp_header_extensions(),
+ ElementsAre(Field(&RtpExtension::uri, "uri2"),
+ Field(&RtpExtension::uri, "uri3")));
+}
+
+TEST_P(PeerConnectionHeaderExtensionTest, NegotiatedExtensionsAreAccessible) {
+ cricket::MediaType media_type;
+ SdpSemantics semantics;
+ std::tie(media_type, semantics) = GetParam();
+ if (semantics != SdpSemantics::kUnifiedPlan)
+ return;
+ std::unique_ptr<PeerConnectionWrapper> pc1 =
+ CreatePeerConnection(media_type, semantics);
+ auto transceiver1 = pc1->AddTransceiver(media_type);
+ auto modified_extensions = transceiver1->GetHeaderExtensionsToNegotiate();
+ modified_extensions[3].direction = RtpTransceiverDirection::kStopped;
+ transceiver1->SetHeaderExtensionsToNegotiate(modified_extensions);
+ auto offer = pc1->CreateOfferAndSetAsLocal(
+ PeerConnectionInterface::RTCOfferAnswerOptions());
+
+ std::unique_ptr<PeerConnectionWrapper> pc2 =
+ CreatePeerConnection(media_type, semantics);
+ auto transceiver2 = pc2->AddTransceiver(media_type);
+ pc2->SetRemoteDescription(std::move(offer));
+ auto answer = pc2->CreateAnswerAndSetAsLocal(
+ PeerConnectionInterface::RTCOfferAnswerOptions());
+ pc1->SetRemoteDescription(std::move(answer));
+
+ // PC1 has exts 2-4 unstopped and PC2 has exts 1-3 unstopped -> ext 2, 3
+ // survives.
+ EXPECT_THAT(transceiver1->GetNegotiatedHeaderExtensions(),
+ ElementsAre(Field(&RtpHeaderExtensionCapability::direction,
+ RtpTransceiverDirection::kStopped),
+ Field(&RtpHeaderExtensionCapability::direction,
+ RtpTransceiverDirection::kSendRecv),
+ Field(&RtpHeaderExtensionCapability::direction,
+ RtpTransceiverDirection::kSendRecv),
+ Field(&RtpHeaderExtensionCapability::direction,
+ RtpTransceiverDirection::kStopped)));
+}
+
+TEST_P(PeerConnectionHeaderExtensionTest, OfferedExtensionsArePerTransceiver) {
+ cricket::MediaType media_type;
+ SdpSemantics semantics;
+ std::tie(media_type, semantics) = GetParam();
+ if (semantics != SdpSemantics::kUnifiedPlan)
+ return;
+ std::unique_ptr<PeerConnectionWrapper> pc1 =
+ CreatePeerConnection(media_type, semantics);
+ auto transceiver1 = pc1->AddTransceiver(media_type);
+ auto modified_extensions = transceiver1->GetHeaderExtensionsToNegotiate();
+ modified_extensions[3].direction = RtpTransceiverDirection::kStopped;
+ transceiver1->SetHeaderExtensionsToNegotiate(modified_extensions);
+ auto transceiver2 = pc1->AddTransceiver(media_type);
+
+ auto session_description = pc1->CreateOffer();
+ EXPECT_THAT(session_description->description()
+ ->contents()[0]
+ .media_description()
+ ->rtp_header_extensions(),
+ ElementsAre(Field(&RtpExtension::uri, "uri2"),
+ Field(&RtpExtension::uri, "uri3")));
+ EXPECT_THAT(session_description->description()
+ ->contents()[1]
+ .media_description()
+ ->rtp_header_extensions(),
+ ElementsAre(Field(&RtpExtension::uri, "uri2"),
+ Field(&RtpExtension::uri, "uri3"),
+ Field(&RtpExtension::uri, "uri4")));
+}
+
+TEST_P(PeerConnectionHeaderExtensionTest, RemovalAfterRenegotiation) {
+ cricket::MediaType media_type;
+ SdpSemantics semantics;
+ std::tie(media_type, semantics) = GetParam();
+ if (semantics != SdpSemantics::kUnifiedPlan)
+ return;
+ std::unique_ptr<PeerConnectionWrapper> pc1 =
+ CreatePeerConnection(media_type, semantics);
+ std::unique_ptr<PeerConnectionWrapper> pc2 =
+ CreatePeerConnection(media_type, semantics);
+ auto transceiver1 = pc1->AddTransceiver(media_type);
+
+ auto offer = pc1->CreateOfferAndSetAsLocal(
+ PeerConnectionInterface::RTCOfferAnswerOptions());
+ pc2->SetRemoteDescription(std::move(offer));
+ auto answer = pc2->CreateAnswerAndSetAsLocal(
+ PeerConnectionInterface::RTCOfferAnswerOptions());
+ pc1->SetRemoteDescription(std::move(answer));
+
+ auto modified_extensions = transceiver1->GetHeaderExtensionsToNegotiate();
+ modified_extensions[3].direction = RtpTransceiverDirection::kStopped;
+ transceiver1->SetHeaderExtensionsToNegotiate(modified_extensions);
+ auto session_description = pc1->CreateOffer();
+ EXPECT_THAT(session_description->description()
+ ->contents()[0]
+ .media_description()
+ ->rtp_header_extensions(),
+ ElementsAre(Field(&RtpExtension::uri, "uri2"),
+ Field(&RtpExtension::uri, "uri3")));
+}
+
+TEST_P(PeerConnectionHeaderExtensionTest,
+ StoppedByDefaultExtensionCanBeActivatedByRemoteSdp) {
+ cricket::MediaType media_type;
+ SdpSemantics semantics;
+ std::tie(media_type, semantics) = GetParam();
+ if (semantics != SdpSemantics::kUnifiedPlan)
+ return;
+ std::unique_ptr<PeerConnectionWrapper> pc1 =
+ CreatePeerConnection(media_type, semantics);
+ std::unique_ptr<PeerConnectionWrapper> pc2 =
+ CreatePeerConnection(media_type, semantics);
+ auto transceiver1 = pc1->AddTransceiver(media_type);
+
+ auto offer = pc1->CreateOfferAndSetAsLocal(
+ PeerConnectionInterface::RTCOfferAnswerOptions());
+ pc2->SetRemoteDescription(std::move(offer));
+ auto answer = pc2->CreateAnswerAndSetAsLocal(
+ PeerConnectionInterface::RTCOfferAnswerOptions());
+ std::string sdp;
+ ASSERT_TRUE(answer->ToString(&sdp));
+ // We support uri1 but it is stopped by default. Let the remote reactivate it.
+ sdp += "a=extmap:15 uri1\r\n";
+ auto modified_answer = CreateSessionDescription(SdpType::kAnswer, sdp);
+ pc1->SetRemoteDescription(std::move(modified_answer));
+ EXPECT_THAT(transceiver1->GetNegotiatedHeaderExtensions(),
+ ElementsAre(Field(&RtpHeaderExtensionCapability::direction,
+ RtpTransceiverDirection::kSendRecv),
+ Field(&RtpHeaderExtensionCapability::direction,
+ RtpTransceiverDirection::kSendRecv),
+ Field(&RtpHeaderExtensionCapability::direction,
+ RtpTransceiverDirection::kSendRecv),
+ Field(&RtpHeaderExtensionCapability::direction,
+ RtpTransceiverDirection::kSendRecv)));
+}
+
+TEST_P(PeerConnectionHeaderExtensionTest,
+ UnknownExtensionInRemoteOfferDoesNotShowUp) {
+ cricket::MediaType media_type;
+ SdpSemantics semantics;
+ std::tie(media_type, semantics) = GetParam();
+ if (semantics != SdpSemantics::kUnifiedPlan)
+ return;
+ std::unique_ptr<PeerConnectionWrapper> pc =
+ CreatePeerConnection(media_type, semantics);
+ std::string sdp =
+ "v=0\r\n"
+ "o=- 0 3 IN IP4 127.0.0.1\r\n"
+ "s=-\r\n"
+ "t=0 0\r\n"
+ "a=fingerprint:sha-256 "
+ "A7:24:72:CA:6E:02:55:39:BA:66:DF:6E:CC:4C:D8:B0:1A:BF:1A:56:65:7D:F4:03:"
+ "AD:7E:77:43:2A:29:EC:93\r\n"
+ "a=ice-ufrag:6HHHdzzeIhkE0CKj\r\n"
+ "a=ice-pwd:XYDGVpfvklQIEnZ6YnyLsAew\r\n";
+ if (media_type == cricket::MEDIA_TYPE_AUDIO) {
+ sdp +=
+ "m=audio 9 RTP/AVPF 111\r\n"
+ "a=rtpmap:111 fake_audio_codec/8000\r\n";
+ } else {
+ sdp +=
+ "m=video 9 RTP/AVPF 111\r\n"
+ "a=rtpmap:111 fake_video_codec/90000\r\n";
+ }
+ sdp +=
+ "c=IN IP4 0.0.0.0\r\n"
+ "a=rtcp-mux\r\n"
+ "a=sendonly\r\n"
+ "a=mid:audio\r\n"
+ "a=setup:actpass\r\n"
+ "a=extmap:1 urn:bogus\r\n";
+ auto offer = CreateSessionDescription(SdpType::kOffer, sdp);
+ pc->SetRemoteDescription(std::move(offer));
+ pc->CreateAnswerAndSetAsLocal(
+ PeerConnectionInterface::RTCOfferAnswerOptions());
+ ASSERT_GT(pc->pc()->GetTransceivers().size(), 0u);
+ auto transceiver = pc->pc()->GetTransceivers()[0];
+ auto negotiated = transceiver->GetNegotiatedHeaderExtensions();
+ EXPECT_EQ(negotiated.size(),
+ transceiver->GetHeaderExtensionsToNegotiate().size());
+ // All extensions are stopped, the "bogus" one does not show up.
+ for (const auto& extension : negotiated) {
+ EXPECT_EQ(extension.direction, RtpTransceiverDirection::kStopped);
+ EXPECT_NE(extension.uri, "urn:bogus");
+ }
+}
+
+// These tests are regression tests for behavior that the API
+// enables in a proper way. It conflicts with the behavior
+// of the API to only offer non-stopped extensions.
+TEST_P(PeerConnectionHeaderExtensionTest,
+ SdpMungingAnswerWithoutApiUsageEnablesExtensions) {
+ cricket::MediaType media_type;
+ SdpSemantics semantics;
+ std::tie(media_type, semantics) = GetParam();
+ if (semantics != SdpSemantics::kUnifiedPlan)
+ return;
+ std::unique_ptr<PeerConnectionWrapper> pc =
+ CreatePeerConnection(media_type, semantics);
+ std::string sdp =
+ "v=0\r\n"
+ "o=- 0 3 IN IP4 127.0.0.1\r\n"
+ "s=-\r\n"
+ "t=0 0\r\n"
+ "a=fingerprint:sha-256 "
+ "A7:24:72:CA:6E:02:55:39:BA:66:DF:6E:CC:4C:D8:B0:1A:BF:1A:56:65:7D:F4:03:"
+ "AD:7E:77:43:2A:29:EC:93\r\n"
+ "a=ice-ufrag:6HHHdzzeIhkE0CKj\r\n"
+ "a=ice-pwd:XYDGVpfvklQIEnZ6YnyLsAew\r\n";
+ if (media_type == cricket::MEDIA_TYPE_AUDIO) {
+ sdp +=
+ "m=audio 9 RTP/AVPF 111\r\n"
+ "a=rtpmap:111 fake_audio_codec/8000\r\n";
+ } else {
+ sdp +=
+ "m=video 9 RTP/AVPF 111\r\n"
+ "a=rtpmap:111 fake_video_codec/90000\r\n";
+ }
+ sdp +=
+ "c=IN IP4 0.0.0.0\r\n"
+ "a=rtcp-mux\r\n"
+ "a=sendrecv\r\n"
+ "a=mid:audio\r\n"
+ "a=setup:actpass\r\n"
+ "a=extmap:1 uri1\r\n";
+ auto offer = CreateSessionDescription(SdpType::kOffer, sdp);
+ pc->SetRemoteDescription(std::move(offer));
+ auto answer =
+ pc->CreateAnswer(PeerConnectionInterface::RTCOfferAnswerOptions());
+ std::string modified_sdp;
+ ASSERT_TRUE(answer->ToString(&modified_sdp));
+ modified_sdp += "a=extmap:1 uri1\r\n";
+ auto modified_answer =
+ CreateSessionDescription(SdpType::kAnswer, modified_sdp);
+ ASSERT_TRUE(pc->SetLocalDescription(std::move(modified_answer)));
+
+ auto session_description = pc->CreateOffer();
+ EXPECT_THAT(session_description->description()
+ ->contents()[0]
+ .media_description()
+ ->rtp_header_extensions(),
+ ElementsAre(Field(&RtpExtension::uri, "uri1"),
+ Field(&RtpExtension::uri, "uri2"),
+ Field(&RtpExtension::uri, "uri3"),
+ Field(&RtpExtension::uri, "uri4")));
+}
+
+TEST_P(PeerConnectionHeaderExtensionTest,
+ SdpMungingOfferWithoutApiUsageEnablesExtensions) {
+ cricket::MediaType media_type;
+ SdpSemantics semantics;
+ std::tie(media_type, semantics) = GetParam();
+ if (semantics != SdpSemantics::kUnifiedPlan)
+ return;
+ std::unique_ptr<PeerConnectionWrapper> pc =
+ CreatePeerConnection(media_type, semantics);
+ pc->AddTransceiver(media_type);
+
+ auto offer =
+ pc->CreateOffer(PeerConnectionInterface::RTCOfferAnswerOptions());
+ std::string modified_sdp;
+ ASSERT_TRUE(offer->ToString(&modified_sdp));
+ modified_sdp += "a=extmap:1 uri1\r\n";
+ auto modified_offer = CreateSessionDescription(SdpType::kOffer, modified_sdp);
+ ASSERT_TRUE(pc->SetLocalDescription(std::move(modified_offer)));
+
+ auto offer2 =
+ pc->CreateOffer(PeerConnectionInterface::RTCOfferAnswerOptions());
+ EXPECT_THAT(offer2->description()
+ ->contents()[0]
+ .media_description()
+ ->rtp_header_extensions(),
+ ElementsAre(Field(&RtpExtension::uri, "uri2"),
+ Field(&RtpExtension::uri, "uri3"),
+ Field(&RtpExtension::uri, "uri4"),
+ Field(&RtpExtension::uri, "uri1")));
+}
+
+TEST_P(PeerConnectionHeaderExtensionTest, EnablingExtensionsAfterRemoteOffer) {
+ cricket::MediaType media_type;
+ SdpSemantics semantics;
+ std::tie(media_type, semantics) = GetParam();
+ if (semantics != SdpSemantics::kUnifiedPlan)
+ return;
+ std::unique_ptr<PeerConnectionWrapper> pc =
+ CreatePeerConnection(media_type, semantics);
+ std::string sdp =
+ "v=0\r\n"
+ "o=- 0 3 IN IP4 127.0.0.1\r\n"
+ "s=-\r\n"
+ "t=0 0\r\n"
+ "a=fingerprint:sha-256 "
+ "A7:24:72:CA:6E:02:55:39:BA:66:DF:6E:CC:4C:D8:B0:1A:BF:1A:56:65:7D:F4:03:"
+ "AD:7E:77:43:2A:29:EC:93\r\n"
+ "a=ice-ufrag:6HHHdzzeIhkE0CKj\r\n"
+ "a=ice-pwd:XYDGVpfvklQIEnZ6YnyLsAew\r\n";
+ if (media_type == cricket::MEDIA_TYPE_AUDIO) {
+ sdp +=
+ "m=audio 9 RTP/AVPF 111\r\n"
+ "a=rtpmap:111 fake_audio_codec/8000\r\n";
+ } else {
+ sdp +=
+ "m=video 9 RTP/AVPF 111\r\n"
+ "a=rtpmap:111 fake_video_codec/90000\r\n";
+ }
+ sdp +=
+ "c=IN IP4 0.0.0.0\r\n"
+ "a=rtcp-mux\r\n"
+ "a=sendrecv\r\n"
+ "a=mid:audio\r\n"
+ "a=setup:actpass\r\n"
+ "a=extmap:5 uri1\r\n";
+ auto offer = CreateSessionDescription(SdpType::kOffer, sdp);
+ pc->SetRemoteDescription(std::move(offer));
+
+ ASSERT_GT(pc->pc()->GetTransceivers().size(), 0u);
+ auto transceiver = pc->pc()->GetTransceivers()[0];
+ auto modified_extensions = transceiver->GetHeaderExtensionsToNegotiate();
+ modified_extensions[0].direction = RtpTransceiverDirection::kSendRecv;
+ transceiver->SetHeaderExtensionsToNegotiate(modified_extensions);
+
+ pc->CreateAnswerAndSetAsLocal(
+ PeerConnectionInterface::RTCOfferAnswerOptions());
+
+ auto session_description = pc->CreateOffer();
+ auto extensions = session_description->description()
+ ->contents()[0]
+ .media_description()
+ ->rtp_header_extensions();
+ EXPECT_THAT(extensions, ElementsAre(Field(&RtpExtension::uri, "uri1"),
+ Field(&RtpExtension::uri, "uri2"),
+ Field(&RtpExtension::uri, "uri3"),
+ Field(&RtpExtension::uri, "uri4")));
+ // Check uri1's id still matches the remote id.
+ EXPECT_EQ(extensions[0].id, 5);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ ,
+ PeerConnectionHeaderExtensionTest,
+ Combine(Values(SdpSemantics::kPlanB_DEPRECATED, SdpSemantics::kUnifiedPlan),
+ Values(cricket::MediaType::MEDIA_TYPE_AUDIO,
+ cricket::MediaType::MEDIA_TYPE_VIDEO)),
+ [](const testing::TestParamInfo<
+ PeerConnectionHeaderExtensionTest::ParamType>& info) {
+ cricket::MediaType media_type;
+ SdpSemantics semantics;
+ std::tie(media_type, semantics) = info.param;
+ return (rtc::StringBuilder("With")
+ << (semantics == SdpSemantics::kPlanB_DEPRECATED ? "PlanB"
+ : "UnifiedPlan")
+ << "And"
+ << (media_type == cricket::MediaType::MEDIA_TYPE_AUDIO ? "Voice"
+ : "Video")
+ << "Engine")
+ .str();
+ });
+
+} // namespace webrtc