commit b8e83518521a2cc07126849dbb65a5cfbfb623d5
parent 1864b6c3f32253b662755953db92075a3b1ce9cf
Author: Dan Baker <dbaker@mozilla.com>
Date: Mon, 27 Oct 2025 15:38:10 -0600
Bug 1995393 - Vendor libwebrtc from a81f50de17
Upstream commit: https://webrtc.googlesource.com/src/+/a81f50de1711ece4d4c051bc1750cbf54177240b
Implement remembering HeaderExtensionsToNegotiate
This changes HeaderExtensionsToNegotiate to remember the result
of the last negotiation.
Corresponding spec change: https://github.com/w3c/webrtc-extensions/pull/238
Bug: webrtc:439514253
Change-Id: I420b65f252398b1bb72d1938c48dc548ec18fd60
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/404946
Reviewed-by: Per Kjellander <perkj@webrtc.org>
Commit-Queue: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#45493}
Diffstat:
5 files changed, 221 insertions(+), 13 deletions(-)
diff --git a/third_party/libwebrtc/README.mozilla.last-vendor b/third_party/libwebrtc/README.mozilla.last-vendor
@@ -1,4 +1,4 @@
# ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /Users/danielbaker/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc
-libwebrtc updated from /Users/danielbaker/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2025-10-27T21:35:53.516558+00:00.
+libwebrtc updated from /Users/danielbaker/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2025-10-27T21:37:58.629238+00:00.
# base of lastest vendoring
-02f0646666
+a81f50de17
diff --git a/third_party/libwebrtc/experiments/field_trials.py b/third_party/libwebrtc/experiments/field_trials.py
@@ -95,6 +95,9 @@ ACTIVE_FIELD_TRIALS: FrozenSet[FieldTrial] = frozenset([
FieldTrial('WebRTC-EnableDtlsPqc',
404763475,
date(2026,6,1)),
+ FieldTrial('WebRTC-HeaderExtensionNegotiateMemory',
+ 439514253,
+ date(2026, 2, 18)),
FieldTrial('WebRTC-IPv6NetworkResolutionFixes',
42224598,
date(2024, 4, 1)),
diff --git a/third_party/libwebrtc/pc/peer_connection_header_extension_unittest.cc b/third_party/libwebrtc/pc/peer_connection_header_extension_unittest.cc
@@ -72,13 +72,16 @@ class PeerConnectionHeaderExtensionTest
std::unique_ptr<PeerConnectionWrapper> CreatePeerConnection(
MediaType media_type,
- std::optional<SdpSemantics> semantics) {
+ std::optional<SdpSemantics> semantics,
+ std::string field_trials_string = "") {
auto media_engine = std::make_unique<FakeMediaEngine>();
if (media_type == MediaType::AUDIO)
media_engine->fake_voice_engine()->SetRtpHeaderExtensions(extensions_);
else
media_engine->fake_video_engine()->SetRtpHeaderExtensions(extensions_);
PeerConnectionFactoryDependencies factory_dependencies;
+ factory_dependencies.env =
+ CreateEnvironment(CreateTestFieldTrialsPtr(field_trials_string));
factory_dependencies.network_thread = Thread::Current();
factory_dependencies.worker_thread = Thread::Current();
factory_dependencies.signaling_thread = Thread::Current();
@@ -269,10 +272,10 @@ TEST_P(PeerConnectionHeaderExtensionTest, OfferedExtensionsArePerTransceiver) {
std::unique_ptr<PeerConnectionWrapper> pc1 =
CreatePeerConnection(media_type, semantics);
auto transceiver1 = pc1->AddTransceiver(media_type);
+ auto transceiver2 = 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()
@@ -422,8 +425,8 @@ TEST_P(PeerConnectionHeaderExtensionTest,
std::tie(media_type, semantics) = GetParam();
if (semantics != SdpSemantics::kUnifiedPlan)
return;
- std::unique_ptr<PeerConnectionWrapper> pc =
- CreatePeerConnection(media_type, semantics);
+ std::unique_ptr<PeerConnectionWrapper> pc = CreatePeerConnection(
+ media_type, semantics, "WebRTC-HeaderExtensionNegotiateMemory/Disabled/");
std::string sdp =
"v=0\r\n"
"o=- 0 3 IN IP4 127.0.0.1\r\n"
@@ -472,6 +475,62 @@ TEST_P(PeerConnectionHeaderExtensionTest,
Field(&RtpExtension::uri, "uri3"),
Field(&RtpExtension::uri, "uri4")));
}
+TEST_P(PeerConnectionHeaderExtensionTest,
+ SdpMungingAnswerWithoutApiUsageEnablesExtensionsWithMemory) {
+ 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, "WebRTC-HeaderExtensionNegotiateMemory/Enabled/");
+ 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 == MediaType::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";
+ std::unique_ptr<SessionDescriptionInterface> offer =
+ CreateSessionDescription(SdpType::kOffer, sdp);
+ pc->SetRemoteDescription(std::move(offer));
+ std::unique_ptr<SessionDescriptionInterface> 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();
+ // With memory enabled, next offer will only contain the extensions
+ // that were agreed to in previous offer/answer.
+ EXPECT_THAT(session_description->description()
+ ->contents()[0]
+ .media_description()
+ ->rtp_header_extensions(),
+ ElementsAre(Field(&RtpExtension::uri, "uri1")));
+}
TEST_P(PeerConnectionHeaderExtensionTest,
SdpMungingOfferWithoutApiUsageEnablesExtensions) {
@@ -504,14 +563,15 @@ TEST_P(PeerConnectionHeaderExtensionTest,
Field(&RtpExtension::uri, "uri1")));
}
-TEST_P(PeerConnectionHeaderExtensionTest, EnablingExtensionsAfterRemoteOffer) {
+TEST_P(PeerConnectionHeaderExtensionTest,
+ EnablingExtensionsAfterRemoteOfferWithoutHeaderExtensionMemory) {
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::unique_ptr<PeerConnectionWrapper> pc = CreatePeerConnection(
+ media_type, semantics, "WebRTC-HeaderExtensionNegotiateMemory/Disabled/");
std::string sdp =
"v=0\r\n"
"o=- 0 3 IN IP4 127.0.0.1\r\n"
@@ -564,6 +624,64 @@ TEST_P(PeerConnectionHeaderExtensionTest, EnablingExtensionsAfterRemoteOffer) {
EXPECT_EQ(extensions[0].id, 5);
}
+TEST_P(PeerConnectionHeaderExtensionTest,
+ EnablingExtensionsAfterRemoteOfferWithHeaderExtensionMemory) {
+ 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, "WebRTC-HeaderExtensionNegotiateMemory/Enabled/");
+ 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 == MediaType::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";
+ std::unique_ptr<SessionDescriptionInterface> 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")));
+ // Check uri1's id still matches the remote id.
+ EXPECT_EQ(extensions[0].id, 5);
+}
+
TEST_P(PeerConnectionHeaderExtensionTest, SenderParametersReflectNegotiation) {
SdpSemantics semantics;
MediaType media_type;
@@ -600,6 +718,71 @@ TEST_P(PeerConnectionHeaderExtensionTest, SenderParametersReflectNegotiation) {
}
}
+TEST_P(PeerConnectionHeaderExtensionTest,
+ TransceiversAddedAfterFirstDoNotCopy) {
+ MediaType media_type;
+ SdpSemantics semantics;
+ std::tie(media_type, semantics) = GetParam();
+ if (semantics != SdpSemantics::kUnifiedPlan) {
+ GTEST_SKIP() << "This test only works with Unified Plan";
+ }
+ std::unique_ptr<PeerConnectionWrapper> pc1 = CreatePeerConnection(
+ media_type, semantics, "WebRTC-HeaderExtensionNegotiateMemory/Disabled/");
+
+ 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,
+ TransceiversAddedAfterFirstTransceiverCopyExtensions) {
+ MediaType media_type;
+ SdpSemantics semantics;
+ std::tie(media_type, semantics) = GetParam();
+ if (semantics != SdpSemantics::kUnifiedPlan) {
+ GTEST_SKIP() << "This test only works with Unified Plan";
+ }
+ std::unique_ptr<PeerConnectionWrapper> pc1 = CreatePeerConnection(
+ media_type, semantics, "WebRTC-HeaderExtensionNegotiateMemory/Enabled/");
+ 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")));
+ // the uri4 extension is disabled in the newly added transceiver too
+ EXPECT_THAT(session_description->description()
+ ->contents()[1]
+ .media_description()
+ ->rtp_header_extensions(),
+ ElementsAre(Field(&RtpExtension::uri, "uri2"),
+ Field(&RtpExtension::uri, "uri3")));
+}
+
INSTANTIATE_TEST_SUITE_P(
,
PeerConnectionHeaderExtensionTest,
diff --git a/third_party/libwebrtc/pc/rtp_transceiver.cc b/third_party/libwebrtc/pc/rtp_transceiver.cc
@@ -837,8 +837,13 @@ void RtpTransceiver::OnNegotiationUpdate(
const MediaContentDescription* content) {
RTC_DCHECK_RUN_ON(thread_);
RTC_DCHECK(content);
- if (sdp_type == SdpType::kAnswer)
+ if (sdp_type == SdpType::kAnswer) {
negotiated_header_extensions_ = content->rtp_header_extensions();
+ if (context_->env().field_trials().IsEnabled(
+ "WebRTC-HeaderExtensionNegotiateMemory")) {
+ header_extensions_to_negotiate_ = GetNegotiatedHeaderExtensions();
+ }
+ }
}
void RtpTransceiver::SetPeerConnectionClosed() {
diff --git a/third_party/libwebrtc/pc/rtp_transmission_manager.cc b/third_party/libwebrtc/pc/rtp_transmission_manager.cc
@@ -322,13 +322,30 @@ RtpTransmissionManager::CreateAndAddTransceiver(
// Allow receiver IDs to conflict since those come from remote SDP (which
// could be invalid, but should not cause a crash).
RTC_DCHECK(!FindSenderById(sender->id()));
+ std::vector<RtpHeaderExtensionCapability> header_extensions;
+ if (env_.field_trials().IsEnabled("WebRTC-HeaderExtensionNegotiateMemory")) {
+ // If we have already negotiated header extensions for this type,
+ // reuse the negotiated state for new transceivers of the same type.
+ for (const auto& transceiver : transceivers()->List()) {
+ if (transceiver->media_type() == sender->media_type()) {
+ header_extensions = transceiver->GetHeaderExtensionsToNegotiate();
+ break;
+ }
+ }
+ }
+ if (header_extensions.empty()) {
+ if (sender->media_type() == MediaType::AUDIO) {
+ header_extensions = media_engine()->voice().GetRtpHeaderExtensions();
+ } else {
+ header_extensions = media_engine()->video().GetRtpHeaderExtensions();
+ }
+ }
+
auto transceiver = RtpTransceiverProxyWithInternal<RtpTransceiver>::Create(
signaling_thread(),
make_ref_counted<RtpTransceiver>(
env_, sender, receiver, context_, codec_lookup_helper_,
- sender->media_type() == MediaType::AUDIO
- ? media_engine()->voice().GetRtpHeaderExtensions()
- : media_engine()->video().GetRtpHeaderExtensions(),
+ std::move(header_extensions),
[this_weak_ptr = weak_ptr_factory_.GetWeakPtr()]() {
if (this_weak_ptr) {
this_weak_ptr->OnNegotiationNeeded();