tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 6c2e49c88221012b7c5a928d59c26b054a3c9e23
parent f3a967c6177f79346082dd5a97dac16af7f36f44
Author: Dan Baker <dbaker@mozilla.com>
Date:   Mon, 27 Oct 2025 13:33:51 -0600

Bug 1995393 - Vendor libwebrtc from 9aa4278cf5

Upstream commit: https://webrtc.googlesource.com/src/+/9aa4278cf59fd1b6a4076046253df9a72713e170
    Fix simulcast stats bug with missing outbound-rtps.

    If simulcast is negotiated, we should see all SSRCs' outbound-rtps.

    The bug is that if "substreams" have not been populated yet because we
    have not started sending yet (e.g. ICE not connected), we return a
    single dummy "info" object. This CL updates this early return path by
    correctly setting the metrics that should always be present: ssrc, rid,
    encodingIndex and active. Packet counters and other stuff will be zero
    which makes sense prior to sending. Full stack test in
    peer_connection_encodings_integrationtest.cc.

    The code path that is changed no longer copies frames_encoded,
    total_encode_time_ms, total_encoded_bytes_target, frames_sent and
    huge_frames_sent which require us to update some expectations in
    webrtc_video_engine_unittest.cc. We should not copy these because
    outside of testing these would all be zero before sending and after
    sending, substreams exist making this code path N/A.

    Bug: chromium:406585888
    Change-Id: I2f7ad8c147254c69510c396b8736d8bb9293db4e
    Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/406280
    Commit-Queue: Henrik Boström <hbos@webrtc.org>
    Reviewed-by: Harald Alvestrand <hta@webrtc.org>
    Cr-Commit-Position: refs/heads/main@{#45447}

Diffstat:
Mthird_party/libwebrtc/README.mozilla.last-vendor | 4++--
Mthird_party/libwebrtc/media/engine/webrtc_video_engine.cc | 39+++++++++++++++++++--------------------
Mthird_party/libwebrtc/media/engine/webrtc_video_engine_unittest.cc | 16++++++++--------
Mthird_party/libwebrtc/pc/peer_connection_encodings_integrationtest.cc | 50+++++++++++++++++++++++++++++++++++++++++++++++++-
4 files changed, 78 insertions(+), 31 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:31:26.560039+00:00. +libwebrtc updated from /Users/danielbaker/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2025-10-27T19:33:32.697294+00:00. # base of lastest vendoring -4bafeff6ae +9aa4278cf5 diff --git a/third_party/libwebrtc/media/engine/webrtc_video_engine.cc b/third_party/libwebrtc/media/engine/webrtc_video_engine.cc @@ -2423,6 +2423,11 @@ WebRtcVideoSendChannel::WebRtcVideoSendStream::GetPerLayerVideoSenderInfos( common_info.codec_name = parameters_.codec_settings->codec.name; common_info.codec_payload_type = parameters_.codec_settings->codec.id; } + // If SVC is used, one stream is configured but multiple encodings exist. This + // is not spec-compliant, but it is how we've implemented SVC so this affects + // how the RTP stream's "active" value is determined. + bool is_svc = (parameters_.encoder_config.number_of_streams == 1 && + rtp_parameters_.encodings.size() > 1); std::vector<VideoSenderInfo> infos; VideoSendStream::Stats stats; if (stream_ == nullptr) { @@ -2466,24 +2471,23 @@ WebRtcVideoSendChannel::WebRtcVideoSendStream::GetPerLayerVideoSenderInfos( common_info.aggregated_huge_frames_sent = stats.huge_frames_sent; common_info.power_efficient_encoder = stats.power_efficient_encoder; - // The normal case is that substreams are present, handled below. But if - // substreams are missing (can happen before negotiated/connected where we - // have no stats yet) a single outbound-rtp is created representing any and - // all layers. + // The "typical case" where `substreams` exist because we have negotiated + // and connected is handled below, but prior to that `substreams` is empty. + // In this case we still need to return one "info" per SSRC and set a few + // stats that should never be missing. if (stats.substreams.empty()) { + size_t encoding_index = 0; for (uint32_t ssrc : parameters_.config.rtp.ssrcs) { - common_info.add_ssrc(ssrc); + auto info = common_info; + info.add_ssrc(ssrc); + info.rid = parameters_.config.rtp.GetRidForSsrc(ssrc); + info.encoding_index = encoding_index; + info.active = IsActiveFromEncodings( + !is_svc ? std::optional<uint32_t>(ssrc) : std::nullopt, + rtp_parameters_.encodings); + ++encoding_index; + infos.push_back(info); } - common_info.encoding_index = 0; - common_info.active = - IsActiveFromEncodings(std::nullopt, rtp_parameters_.encodings); - common_info.framerate_sent = stats.encode_frame_rate; - common_info.frames_encoded = stats.frames_encoded; - common_info.total_encode_time_ms = stats.total_encode_time_ms; - common_info.total_encoded_bytes_target = stats.total_encoded_bytes_target; - common_info.frames_sent = stats.frames_encoded; - common_info.huge_frames_sent = stats.huge_frames_sent; - infos.push_back(common_info); return infos; } } @@ -2498,11 +2502,6 @@ WebRtcVideoSendChannel::WebRtcVideoSendStream::GetPerLayerVideoSenderInfos( for (size_t i = 0; i < parameters_.config.rtp.ssrcs.size(); ++i) { encoding_index_by_ssrc[parameters_.config.rtp.ssrcs[i]] = i; } - // If SVC is used, one stream is configured but multiple encodings exist. This - // is not spec-compliant, but it is how we've implemented SVC so this affects - // how the RTP stream's "active" value is determined. - bool is_svc = (parameters_.encoder_config.number_of_streams == 1 && - rtp_parameters_.encodings.size() > 1); for (const auto& pair : outbound_rtp_substreams) { auto info = common_info; uint32_t ssrc = pair.first; diff --git a/third_party/libwebrtc/media/engine/webrtc_video_engine_unittest.cc b/third_party/libwebrtc/media/engine/webrtc_video_engine_unittest.cc @@ -5883,7 +5883,7 @@ TEST_F(WebRtcVideoChannelTest, GetStatsReportsCpuOveruseMetrics) { TEST_F(WebRtcVideoChannelTest, GetStatsReportsFramesEncoded) { FakeVideoSendStream* stream = AddSendStream(); VideoSendStream::Stats stats; - stats.frames_encoded = 13; + stats.substreams[123].frames_encoded = 13; stream->SetStats(stats); VideoMediaSendInfo send_info; @@ -5891,7 +5891,7 @@ TEST_F(WebRtcVideoChannelTest, GetStatsReportsFramesEncoded) { EXPECT_TRUE(send_channel_->GetStats(&send_info)); EXPECT_TRUE(receive_channel_->GetStats(&receive_info)); - EXPECT_EQ(stats.frames_encoded, send_info.senders[0].frames_encoded); + EXPECT_EQ(13u, send_info.senders[0].frames_encoded); } TEST_F(WebRtcVideoChannelTest, GetStatsReportsKeyFramesEncoded) { @@ -6007,9 +6007,10 @@ TEST_F(WebRtcVideoChannelTest, GetAggregatedStatsReportWithoutSubStreams) { EXPECT_EQ(sender.nacks_received, 0u); EXPECT_EQ(sender.send_frame_width, 0); EXPECT_EQ(sender.send_frame_height, 0); + EXPECT_EQ(sender.framerate_sent, 0); + EXPECT_EQ(sender.frames_encoded, 0u); EXPECT_EQ(sender.framerate_input, stats.input_frame_rate); - EXPECT_EQ(sender.framerate_sent, stats.encode_frame_rate); EXPECT_EQ(sender.nominal_bitrate, stats.media_bitrate_bps); EXPECT_NE(sender.adapt_reason & WebRtcVideoChannel::ADAPTREASON_CPU, 0); EXPECT_NE(sender.adapt_reason & WebRtcVideoChannel::ADAPTREASON_BANDWIDTH, 0); @@ -6021,22 +6022,21 @@ TEST_F(WebRtcVideoChannelTest, GetAggregatedStatsReportWithoutSubStreams) { stats.quality_limitation_resolution_changes); EXPECT_EQ(sender.avg_encode_ms, stats.avg_encode_time_ms); EXPECT_EQ(sender.encode_usage_percent, stats.encode_usage_percent); - EXPECT_EQ(sender.frames_encoded, stats.frames_encoded); // Comes from substream only. EXPECT_EQ(sender.key_frames_encoded, 0u); + EXPECT_EQ(sender.total_encode_time_ms, 0u); - EXPECT_EQ(sender.total_encode_time_ms, stats.total_encode_time_ms); EXPECT_EQ(sender.total_encoded_bytes_target, stats.total_encoded_bytes_target); // Comes from substream only. EXPECT_EQ(sender.total_packet_send_delay, TimeDelta::Zero()); EXPECT_EQ(sender.qp_sum, std::nullopt); + EXPECT_EQ(sender.frames_sent, 0u); + EXPECT_EQ(sender.huge_frames_sent, 0u); EXPECT_EQ(sender.has_entered_low_resolution, stats.has_entered_low_resolution); EXPECT_EQ(sender.content_type, VideoContentType::SCREENSHARE); - EXPECT_EQ(sender.frames_sent, stats.frames_encoded); - EXPECT_EQ(sender.huge_frames_sent, stats.huge_frames_sent); EXPECT_EQ(sender.rid, std::nullopt); } @@ -7030,7 +7030,7 @@ TEST_F(WebRtcVideoChannelTest, ReportsSsrcGroupsInStats) { EXPECT_TRUE(send_channel_->GetStats(&send_info)); EXPECT_TRUE(receive_channel_->GetStats(&receive_info)); - ASSERT_EQ(1u, send_info.senders.size()); + ASSERT_EQ(3u, send_info.senders.size()); ASSERT_EQ(1u, receive_info.receivers.size()); EXPECT_NE(sender_sp.ssrc_groups, receiver_sp.ssrc_groups); diff --git a/third_party/libwebrtc/pc/peer_connection_encodings_integrationtest.cc b/third_party/libwebrtc/pc/peer_connection_encodings_integrationtest.cc @@ -1306,7 +1306,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest, VP9_TargetBitrate_StandardL1T3) { } TEST_F(PeerConnectionEncodingsIntegrationTest, - SimulcastProducesUniqueSsrcAndRtxSsrcs) { + SimulcastProducesUniqueSsrcAndRtxSsrcsWhenConnected) { scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc(); scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc(); ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper); @@ -1347,6 +1347,54 @@ TEST_F(PeerConnectionEncodingsIntegrationTest, EXPECT_EQ(rtx_ssrcs.size(), 3u); } +// Similar to the above test, but we never exchange ICE candidates such that +// simulcast never has a chance to "ramp up". Despite this, we should see one +// outbound-rtp per encoding. +TEST_F(PeerConnectionEncodingsIntegrationTest, + SimulcastProducesUniqueSsrcAndRtxSsrcsWhenDisconnected) { + scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc(); + scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc(); + + std::vector<SimulcastLayer> layers = + CreateLayers({"f", "h", "q"}, /*active=*/true); + scoped_refptr<RtpTransceiverInterface> transceiver = + AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper, + layers); + + // Inactive the middle layer. + auto sender = transceiver->sender(); + auto parameters = sender->GetParameters(); + parameters.encodings[0].active = true; + parameters.encodings[1].active = false; + parameters.encodings[2].active = true; + EXPECT_TRUE(sender->SetParameters(parameters).ok()); + + NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper); + + // We have three outbound-rtps. + scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper); + std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps = + report->GetStatsOfType<RTCOutboundRtpStreamStats>(); + ASSERT_EQ(outbound_rtps.size(), 3u); + // SSRC and RTX is unique. + std::set<uint32_t> ssrcs; + std::set<uint32_t> rtx_ssrcs; + for (const auto& outbound_rtp : outbound_rtps) { + ASSERT_TRUE(outbound_rtp->ssrc.has_value()); + ASSERT_TRUE(outbound_rtp->rtx_ssrc.has_value()); + ssrcs.insert(*outbound_rtp->ssrc); + rtx_ssrcs.insert(*outbound_rtp->rtx_ssrc); + } + EXPECT_EQ(ssrcs.size(), 3u); + EXPECT_EQ(rtx_ssrcs.size(), 3u); + // RIDs and active are set. + auto outbound_rtp_by_rid = GetOutboundRtpStreamStatsByRid(report); + EXPECT_THAT( + outbound_rtp_by_rid, + UnorderedElementsAre(Pair("q", Active()), Pair("h", Not(Active())), + Pair("f", Active()))); +} + TEST_F(PeerConnectionEncodingsIntegrationTest, EncodingParameterCodecIsEmptyWhenCreatedAudio) { scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();