commit f3a967c6177f79346082dd5a97dac16af7f36f44
parent fae0f39dcc4a61317b7aa6ad025c9f504d4b5258
Author: Dan Baker <dbaker@mozilla.com>
Date: Mon, 27 Oct 2025 13:31:42 -0600
Bug 1995393 - Vendor libwebrtc from 4bafeff6ae
Upstream commit: https://webrtc.googlesource.com/src/+/4bafeff6ae5b8658c2b0e5899e0c3289e800e2e9
Make outbound-rtp lifetime spec-compliant (behind flag).
The spec says to delay the creation of the outbound-rtp until the SDP
answer is set. This is equivalent to checking if the currentDirection is
set, since that is one of the side-effects of negotiating an m-section.
Plan B is opted out of this logic because currentDirection is not
applicable to Plan B. Some drive-by comments are added to the
inbound-rtp code paths for symmetry and clarity.
Bug: chromium:406585888
Change-Id: Ib2c7eb99316cd1f82269a756029d19d4c71b0e46
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/406261
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Henrik Boström <hbos@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#45446}
Diffstat:
4 files changed, 74 insertions(+), 8 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-27T19:29:02.897056+00:00.
+libwebrtc updated from /Users/danielbaker/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2025-10-27T19:31:26.560039+00:00.
# base of lastest vendoring
-5b6cc53a2e
+4bafeff6ae
diff --git a/third_party/libwebrtc/pc/rtc_stats_collector.cc b/third_party/libwebrtc/pc/rtc_stats_collector.cc
@@ -1174,6 +1174,7 @@ RTCStatsCollector::RTCStatsCollector(PeerConnectionInternal* pc,
const Environment& env,
int64_t cache_lifetime_us)
: pc_(pc),
+ is_unified_plan_(pc->IsUnifiedPlan()),
env_(env),
stats_timestamp_with_environment_clock_(
pc->GetConfiguration().stats_timestamp_with_environment_clock()),
@@ -1750,9 +1751,11 @@ void RTCStatsCollector::ProduceAudioRTPStreamStats_n(
for (const VoiceReceiverInfo& voice_receiver_info :
stats.track_media_info_map.voice_media_info()->receivers) {
if (!voice_receiver_info.connected()) {
- continue;
+ continue; // The SSRC is not known yet.
}
if (spec_lifetime && voice_receiver_info.packets_received == 0) {
+ // The SSRC is known despite not receiving any packets. This happens if
+ // SSRC is signalled in the SDP which we should not rely on for getStats.
continue;
}
// Inbound.
@@ -1799,8 +1802,13 @@ void RTCStatsCollector::ProduceAudioRTPStreamStats_n(
std::map<std::string, RTCOutboundRtpStreamStats*> audio_outbound_rtps;
for (const VoiceSenderInfo& voice_sender_info :
stats.track_media_info_map.voice_media_info()->senders) {
- if (!voice_sender_info.connected())
- continue;
+ if (!voice_sender_info.connected()) {
+ continue; // The SSRC is not known yet.
+ }
+ if (spec_lifetime && is_unified_plan_ &&
+ !stats.current_direction.has_value()) {
+ continue; // The SSRC is known but the O/A has not completed.
+ }
auto outbound_audio = CreateOutboundRTPStreamStatsFromVoiceSenderInfo(
transport_id, mid, *stats.track_media_info_map.voice_media_info(),
voice_sender_info, timestamp, report);
@@ -1858,9 +1866,11 @@ void RTCStatsCollector::ProduceVideoRTPStreamStats_n(
for (const VideoReceiverInfo& video_receiver_info :
stats.track_media_info_map.video_media_info()->receivers) {
if (!video_receiver_info.connected()) {
- continue;
+ continue; // The SSRC is not known yet.
}
if (spec_lifetime && video_receiver_info.packets_received == 0) {
+ // The SSRC is known despite not receiving any packets. This happens if
+ // SSRC is signalled in the SDP which we should not rely on for getStats.
continue;
}
auto inbound_video = CreateInboundRTPStreamStatsFromVideoReceiverInfo(
@@ -1899,8 +1909,13 @@ void RTCStatsCollector::ProduceVideoRTPStreamStats_n(
std::map<std::string, RTCOutboundRtpStreamStats*> video_outbound_rtps;
for (const VideoSenderInfo& video_sender_info :
stats.track_media_info_map.video_media_info()->senders) {
- if (!video_sender_info.connected())
- continue;
+ if (!video_sender_info.connected()) {
+ continue; // The SSRC is not known yet.
+ }
+ if (spec_lifetime && is_unified_plan_ &&
+ !stats.current_direction.has_value()) {
+ continue; // The SSRC is known but the O/A has not completed.
+ }
auto outbound_video = CreateOutboundRTPStreamStatsFromVideoSenderInfo(
transport_id, mid, *stats.track_media_info_map.video_media_info(),
video_sender_info, timestamp, report);
diff --git a/third_party/libwebrtc/pc/rtc_stats_collector.h b/third_party/libwebrtc/pc/rtc_stats_collector.h
@@ -250,6 +250,7 @@ class RTCStatsCollector : public RefCountInterface {
scoped_refptr<RtpReceiverInternal> receiver_selector);
PeerConnectionInternal* const pc_;
+ const bool is_unified_plan_;
const Environment env_;
const bool stats_timestamp_with_environment_clock_;
Thread* const signaling_thread_;
diff --git a/third_party/libwebrtc/pc/rtc_stats_integrationtest.cc b/third_party/libwebrtc/pc/rtc_stats_integrationtest.cc
@@ -1250,6 +1250,56 @@ class RTCStatsRtpLifetimeTest : public RTCStatsIntegrationTest {
}
};
+TEST_F(RTCStatsRtpLifetimeTest, AudioOutboundRtpMissingBeforeStable) {
+ // Caller to send audio.
+ scoped_refptr<MediaStreamInterface> stream = caller_->GetUserMedia(
+ /*audio=*/true, {}, /*video=*/false);
+ scoped_refptr<AudioTrackInterface> track = stream->GetAudioTracks()[0];
+ caller_->pc()->AddTransceiver(track, {});
+
+ // Setting the offer is not enough to make the outbound-rtp appear.
+ auto offer = caller_->AwaitCreateOffer();
+ caller_->AwaitSetLocalDescription(offer.get());
+ scoped_refptr<const RTCStatsReport> report = GetStats(caller_->pc());
+ std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
+ report->GetStatsOfType<RTCOutboundRtpStreamStats>();
+ EXPECT_THAT(outbound_rtps, SizeIs(0));
+
+ // Once the O/A completes, the outbound-rtp immediately appears (the stats
+ // cache is cleared).
+ callee_->AwaitSetRemoteDescription(offer.get());
+ auto answer = callee_->AwaitCreateAnswer();
+ caller_->AwaitSetRemoteDescription(answer.get());
+ report = GetStats(caller_->pc());
+ outbound_rtps = report->GetStatsOfType<RTCOutboundRtpStreamStats>();
+ EXPECT_THAT(outbound_rtps, SizeIs(1));
+}
+
+TEST_F(RTCStatsRtpLifetimeTest, VideoOutboundRtpMissingBeforeStable) {
+ // Caller to send video.
+ scoped_refptr<MediaStreamInterface> stream = caller_->GetUserMedia(
+ /*audio=*/false, {}, /*video=*/true);
+ scoped_refptr<VideoTrackInterface> track = stream->GetVideoTracks()[0];
+ caller_->pc()->AddTransceiver(track, {});
+
+ // Setting the offer is not enough to make the outbound-rtp appear.
+ auto offer = caller_->AwaitCreateOffer();
+ caller_->AwaitSetLocalDescription(offer.get());
+ scoped_refptr<const RTCStatsReport> report = GetStats(caller_->pc());
+ std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
+ report->GetStatsOfType<RTCOutboundRtpStreamStats>();
+ EXPECT_THAT(outbound_rtps, SizeIs(0));
+
+ // Once the O/A completes, the outbound-rtp immediately appears (the stats
+ // cache is cleared).
+ callee_->AwaitSetRemoteDescription(offer.get());
+ auto answer = callee_->AwaitCreateAnswer();
+ caller_->AwaitSetRemoteDescription(answer.get());
+ report = GetStats(caller_->pc());
+ outbound_rtps = report->GetStatsOfType<RTCOutboundRtpStreamStats>();
+ EXPECT_THAT(outbound_rtps, SizeIs(1));
+}
+
TEST_F(RTCStatsRtpLifetimeTest, AudioInboundRtpMissingBeforeFirstPacket) {
// Caller to send audio.
scoped_refptr<MediaStreamInterface> stream = caller_->GetUserMedia(