psnr: tweak sampling rate to make a new measurement every second more likely

When getStats is polled at exactly 1hz the current frame sampling may
occasionally yield no increase in measurements. Avoid this by doing a
simple linear extrapolation using the previous and current frame and
comparing that extrapolation to the 90k frequency.

Bug: webrtc:375021
Change-Id: I252a2b3fef87cbd728e40f83e7efe18f05aafc37
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/406143
Reviewed-by: Erik Språng <sprang@webrtc.org>
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Commit-Queue: Philipp Hancke <phancke@meta.com>
Cr-Commit-Position: refs/heads/main@{#45458}
diff --git a/modules/video_coding/utility/frame_sampler.cc b/modules/video_coding/utility/frame_sampler.cc
index b08f891..e50c184 100644
--- a/modules/video_coding/utility/frame_sampler.cc
+++ b/modules/video_coding/utility/frame_sampler.cc
@@ -10,18 +10,35 @@
 
 #include "modules/video_coding/utility/frame_sampler.h"
 
+#include <cstdint>
+
 #include "api/video/video_frame.h"
 #include "modules/include/module_common_types_public.h"
 
 namespace webrtc {
 
 constexpr int kTimestampDifference =
-    90'000 - 1;  // Sample every 90khz or once per second.
+    90'000;  // Sample every 90khz or once per second.
 
 bool FrameSampler::ShouldBeSampled(const VideoFrame& frame) {
-  if (!last_rtp_timestamp_sampled_.has_value() ||
-      (IsNewerTimestamp(frame.rtp_timestamp(),
-                        *last_rtp_timestamp_sampled_ + kTimestampDifference))) {
+  if (!last_rtp_timestamp_sampled_) {
+    // Since we can not know the frame rate from the first frame,
+    // assume 30fps for the extrapolation.
+    last_rtp_timestamp_ =
+        frame.rtp_timestamp() + kTimestampDifference / /*fps=*/30;
+    last_rtp_timestamp_sampled_ = frame.rtp_timestamp();
+    return true;
+  }
+  // Since getStats is commonly called once per second, sample if the
+  // extrapolated RTP timestamp of the next frame would be be too late for this.
+  // This is not strictly necessary but makes plotting the values once per
+  // second much easier.
+  uint32_t extrapolated_rtp_timestamp =
+      frame.rtp_timestamp() + (frame.rtp_timestamp() - *last_rtp_timestamp_);
+  last_rtp_timestamp_ = frame.rtp_timestamp();
+
+  if (IsNewerTimestamp(extrapolated_rtp_timestamp,
+                       *last_rtp_timestamp_sampled_ + kTimestampDifference)) {
     last_rtp_timestamp_sampled_ = frame.rtp_timestamp();
     return true;
   }
diff --git a/modules/video_coding/utility/frame_sampler.h b/modules/video_coding/utility/frame_sampler.h
index 403c1e7..0cbcc0a 100644
--- a/modules/video_coding/utility/frame_sampler.h
+++ b/modules/video_coding/utility/frame_sampler.h
@@ -32,6 +32,7 @@
 
  private:
   std::optional<uint32_t> last_rtp_timestamp_sampled_;
+  std::optional<uint32_t> last_rtp_timestamp_;
 };
 
 }  // namespace webrtc
diff --git a/modules/video_coding/utility/frame_sampler_unittest.cc b/modules/video_coding/utility/frame_sampler_unittest.cc
index 0587338..c3e9b5d 100644
--- a/modules/video_coding/utility/frame_sampler_unittest.cc
+++ b/modules/video_coding/utility/frame_sampler_unittest.cc
@@ -28,7 +28,22 @@
   EXPECT_TRUE(sampler.ShouldBeSampled(frame));
   frame.set_rtp_timestamp(45'000);
   EXPECT_FALSE(sampler.ShouldBeSampled(frame));
-  frame.set_rtp_timestamp(90'000);
+  frame.set_rtp_timestamp(90'000 - 3'000);
+  EXPECT_TRUE(sampler.ShouldBeSampled(frame));
+}
+
+TEST(FrameSampler, SamplesBasedOnRtpTimestampDeltaLessThanOneSecond) {
+  FrameSampler sampler;
+
+  auto buffer = make_ref_counted<I420Buffer>(320, 240);
+  VideoFrame frame =
+      VideoFrame::Builder().set_video_frame_buffer(buffer).build();
+
+  frame.set_rtp_timestamp(0);
+  EXPECT_TRUE(sampler.ShouldBeSampled(frame));
+  frame.set_rtp_timestamp(3'000);
+  EXPECT_FALSE(sampler.ShouldBeSampled(frame));
+  frame.set_rtp_timestamp(90'000 - 3'000);
   EXPECT_TRUE(sampler.ShouldBeSampled(frame));
 }
 
@@ -40,7 +55,7 @@
       VideoFrame::Builder().set_video_frame_buffer(buffer).build();
 
   // RTP timestamp wraps at 2**32.
-  frame.set_rtp_timestamp(0xffff'ffff - 4000);
+  frame.set_rtp_timestamp(0xffff'ffff - 3'000);
   EXPECT_TRUE(sampler.ShouldBeSampled(frame));
   frame.set_rtp_timestamp(41'000);
   EXPECT_FALSE(sampler.ShouldBeSampled(frame));