commit 2d2804e92a8f4e374438c81448de9718999f5626
parent a28cad6403de2b85d603b709660c9c5a68c81670
Author: Dan Baker <dbaker@mozilla.com>
Date: Mon, 27 Oct 2025 11:41:46 -0600
Bug 1995393 - Vendor libwebrtc from b62361bfe9
Upstream commit: https://webrtc.googlesource.com/src/+/b62361bfe989884f4137039d1e75d67b260bd1b2
Expose video encode PSNR (in supported codecs) in stats
the Y, U and V components, applications can do a weighted average.
https://webrtc-review.googlesource.com/c/src/+/368960 implements the
codec changes, this change wires those up to getStats.
spec PR:
https://github.com/w3c/webrtc-stats/pull/794
BUG=webrtc:388070060
Change-Id: Idba317422e8cfe40f3c2c7b16e4072d2c6042b3f
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/375021
Commit-Queue: Philipp Hancke <phancke@meta.com>
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#45414}
Diffstat:
7 files changed, 76 insertions(+), 9 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-27T17:38:23.244502+00:00.
+libwebrtc updated from /Users/danielbaker/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2025-10-27T17:41:31.035918+00:00.
# base of lastest vendoring
-dedf36cfdc
+b62361bfe9
diff --git a/third_party/libwebrtc/api/stats/rtcstats_objects.h b/third_party/libwebrtc/api/stats/rtcstats_objects.h
@@ -365,7 +365,10 @@ class RTC_EXPORT RTCOutboundRtpStreamStats final
std::optional<uint32_t> fir_count;
std::optional<uint32_t> pli_count;
std::optional<uint32_t> nack_count;
+ // QP and PSNR are only defined for video.
std::optional<uint64_t> qp_sum;
+ std::optional<std::map<std::string, double>> psnr_sum; // y, u, v.
+ std::optional<uint32_t> psnr_measurements;
std::optional<bool> active;
// In JavaScript, this is only exposed if HW exposure is allowed.
std::optional<bool> power_efficient_encoder;
diff --git a/third_party/libwebrtc/pc/rtc_stats_collector.cc b/third_party/libwebrtc/pc/rtc_stats_collector.cc
@@ -804,8 +804,21 @@ CreateOutboundRTPStreamStatsFromVideoSenderInfo(
static_cast<uint32_t>(video_sender_info.firs_received);
outbound_video->pli_count =
static_cast<uint32_t>(video_sender_info.plis_received);
- if (video_sender_info.qp_sum.has_value())
+ if (video_sender_info.qp_sum.has_value()) {
outbound_video->qp_sum = *video_sender_info.qp_sum;
+ }
+ // psnrSum is gated on psnrMeasurements > 0.
+ if (video_sender_info.psnr_measurements > 0) {
+ outbound_video->psnr_measurements = video_sender_info.psnr_measurements;
+ outbound_video->psnr_sum = {
+ {"y", video_sender_info.psnr_sum.y},
+ {"u", video_sender_info.psnr_sum.u},
+ {"v", video_sender_info.psnr_sum.v},
+ };
+ }
+ if (video_sender_info.psnr_measurements > 0) {
+ outbound_video->psnr_measurements = video_sender_info.psnr_measurements;
+ }
if (video_sender_info.target_bitrate.has_value()) {
outbound_video->target_bitrate = video_sender_info.target_bitrate->bps();
}
diff --git a/third_party/libwebrtc/pc/rtc_stats_collector_unittest.cc b/third_party/libwebrtc/pc/rtc_stats_collector_unittest.cc
@@ -2620,6 +2620,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRtpStreamStats_Video) {
300;
video_media_info.senders[0].quality_limitation_resolution_changes = 56u;
video_media_info.senders[0].qp_sum = std::nullopt;
+ video_media_info.senders[0].psnr_measurements = 0;
video_media_info.senders[0].content_type = VideoContentType::UNSPECIFIED;
video_media_info.senders[0].encoder_implementation_name = std::nullopt;
video_media_info.senders[0].power_efficient_encoder = false;
@@ -2706,6 +2707,8 @@ TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRtpStreamStats_Video) {
expected_video.rtx_ssrc = 4404;
// `expected_video.content_type` should be undefined.
// `expected_video.qp_sum` should be undefined.
+ // `expected_video.psnr_sum` should be undefined.
+ // `expected_video.psnr_measurements` should be undefined.
// `expected_video.encoder_implementation` should be undefined.
ASSERT_TRUE(report->Get(expected_video.id()));
@@ -2716,6 +2719,10 @@ TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRtpStreamStats_Video) {
// Set previously undefined values and "GetStats" again.
video_media_info.senders[0].qp_sum = 9;
expected_video.qp_sum = 9;
+ video_media_info.senders[0].psnr_sum = {.y = 29, .u = 30, .v = 31};
+ video_media_info.senders[0].psnr_measurements = 1;
+ expected_video.psnr_sum = {{"y", 29}, {"u", 30}, {"v", 31}};
+ expected_video.psnr_measurements = 1;
video_media_info.senders[0].content_type = VideoContentType::SCREENSHARE;
expected_video.content_type = "screenshare";
video_media_info.senders[0].encoder_implementation_name = "libfooencoder";
diff --git a/third_party/libwebrtc/pc/rtc_stats_integrationtest.cc b/third_party/libwebrtc/pc/rtc_stats_integrationtest.cc
@@ -39,6 +39,7 @@
#include "rtc_base/thread.h"
#include "rtc_base/trace_event.h"
#include "rtc_base/virtual_socket_server.h"
+#include "test/create_test_field_trials.h"
#include "test/gmock.h"
#include "test/gtest.h"
#include "test/wait_until.h"
@@ -49,7 +50,7 @@ namespace webrtc {
namespace {
-const int64_t kGetStatsTimeoutMs = 10000;
+constexpr int64_t kGetStatsTimeoutMs = 10000;
class RTCStatsIntegrationTest : public ::testing::Test {
public:
@@ -67,17 +68,22 @@ class RTCStatsIntegrationTest : public ::testing::Test {
worker_thread_.get());
}
- void StartCall() {
+ void StartCall() { StartCall(""); }
+ void StartCall(const char* field_trial_string) {
// Create PeerConnections and "connect" sigslots
PeerConnectionInterface::RTCConfiguration config;
config.sdp_semantics = SdpSemantics::kUnifiedPlan;
PeerConnectionInterface::IceServer ice_server;
ice_server.uri = "stun:1.1.1.1:3478";
config.servers.push_back(ice_server);
- EXPECT_TRUE(caller_->CreatePc(config, CreateBuiltinAudioEncoderFactory(),
- CreateBuiltinAudioDecoderFactory()));
- EXPECT_TRUE(callee_->CreatePc(config, CreateBuiltinAudioEncoderFactory(),
- CreateBuiltinAudioDecoderFactory()));
+ EXPECT_TRUE(
+ caller_->CreatePc(config, CreateBuiltinAudioEncoderFactory(),
+ CreateBuiltinAudioDecoderFactory(),
+ CreateTestFieldTrialsPtr(field_trial_string)));
+ EXPECT_TRUE(
+ callee_->CreatePc(config, CreateBuiltinAudioEncoderFactory(),
+ CreateBuiltinAudioDecoderFactory(),
+ CreateTestFieldTrialsPtr(field_trial_string)));
PeerConnectionTestWrapper::Connect(caller_.get(), callee_.get());
// Get user media for audio and video
@@ -571,6 +577,7 @@ class RTCStatsReportVerifier {
verifier.TestAttributeIsUndefined(inbound_stream.decoder_implementation);
verifier.TestAttributeIsUndefined(inbound_stream.power_efficient_decoder);
}
+
// As long as the corruption detection RTP header extension is not activated
// it should not aggregate any corruption score. The tests where this header
// extension is enabled are located in pc/peer_connection_integrationtest.cc
@@ -793,6 +800,10 @@ class RTCStatsReportVerifier {
RTCAudioSourceStats::kType);
verifier.TestAttributeIsUndefined(outbound_stream.qp_sum);
}
+ // TODO: bugs.webrtc.org/388070060 - PSNR stats are disabled by default.
+ verifier.TestAttributeIsUndefined(outbound_stream.psnr_sum);
+ verifier.TestAttributeIsUndefined(outbound_stream.psnr_measurements);
+
verifier.TestAttributeIsNonNegative<uint32_t>(outbound_stream.nack_count);
verifier.TestAttributeIsOptionalIDReference(
outbound_stream.remote_id, RTCRemoteInboundRtpStreamStats::kType);
@@ -1198,6 +1209,29 @@ TEST_F(RTCStatsIntegrationTest, GetStatsContainsNoDuplicateAttributes) {
}
}
}
+
+TEST_F(RTCStatsIntegrationTest, ExperimentalPsnrStats) {
+ StartCall("WebRTC-Video-CalculatePsnr/Enabled/");
+
+ // This assumes all other stats are ok and tests the stats which should be
+ // different under the field trial.
+ scoped_refptr<const RTCStatsReport> report = GetStatsFromCaller();
+ for (const RTCStats& stats : *report) {
+ if (stats.type() == RTCOutboundRtpStreamStats::kType) {
+ const RTCOutboundRtpStreamStats& outbound_stream(
+ stats.cast_to<RTCOutboundRtpStreamStats>());
+ RTCStatsVerifier verifier(report.get(), &outbound_stream);
+ if (outbound_stream.kind.has_value() &&
+ *outbound_stream.kind == "video") {
+ verifier.TestAttributeIsDefined(outbound_stream.psnr_sum);
+ verifier.TestAttributeIsNonNegative(outbound_stream.psnr_measurements);
+ } else {
+ verifier.TestAttributeIsUndefined(outbound_stream.psnr_sum);
+ verifier.TestAttributeIsUndefined(outbound_stream.psnr_measurements);
+ }
+ }
+ }
+}
#endif // WEBRTC_HAVE_SCTP
} // namespace
diff --git a/third_party/libwebrtc/stats/rtcstats_objects.cc b/third_party/libwebrtc/stats/rtcstats_objects.cc
@@ -323,6 +323,8 @@ WEBRTC_RTCSTATS_IMPL(
AttributeInit("pliCount", &pli_count),
AttributeInit("nackCount", &nack_count),
AttributeInit("qpSum", &qp_sum),
+ AttributeInit("psnrSum", &psnr_sum),
+ AttributeInit("psnrMeasurements", &psnr_measurements),
AttributeInit("active", &active),
AttributeInit("powerEfficientEncoder", &power_efficient_encoder),
AttributeInit("scalabilityMode", &scalability_mode),
diff --git a/third_party/libwebrtc/video/send_statistics_proxy.cc b/third_party/libwebrtc/video/send_statistics_proxy.cc
@@ -1053,6 +1053,14 @@ void SendStatisticsProxy::OnSendEncodedImage(
}
}
+ std::optional<EncodedImage::Psnr> psnr = encoded_image.psnr();
+ if (psnr.has_value()) {
+ stats->psnr_sum.y += psnr->y;
+ stats->psnr_sum.u += psnr->u;
+ stats->psnr_sum.v += psnr->v;
+ stats->psnr_measurements += 1;
+ }
+
// If any of the simulcast streams have a huge frame, it should be counted
// as a single difficult input frame.
// https://w3c.github.io/webrtc-stats/#dom-rtcvideosenderstats-hugeframessent