Add support for updating histogram for received fraction loss ("WebRTC.Video.ReceivedPacketsLostInPercent") when running new video api.

Add tests for verifying that video histograms are updated.

BUG=
R=pbos@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/44309004

Cr-Commit-Position: refs/heads/master@{#9085}
diff --git a/webrtc/video/end_to_end_tests.cc b/webrtc/video/end_to_end_tests.cc
index 389d773..abe7f1d 100644
--- a/webrtc/video/end_to_end_tests.cc
+++ b/webrtc/video/end_to_end_tests.cc
@@ -78,6 +78,7 @@
   void TestSendsSetSsrcs(size_t num_ssrcs, bool send_single_ssrc_first);
   void TestRtpStatePreservation(bool use_rtx);
   void TestReceivedFecPacketsNotNacked(const FakeNetworkPipe::Config& config);
+  void VerifyHistogramStats(bool use_rtx, bool use_red);
 };
 
 TEST_F(EndToEndTest, ReceiverCanBeStartedTwice) {
@@ -1497,11 +1498,12 @@
     int64_t start_runtime_ms_;
   } test;
 
+  test::ClearHistograms();
   RunBaseTest(&test);
 
-  EXPECT_NE(-1, test::LastHistogramSample(
+  EXPECT_EQ(1, test::NumHistogramSamples(
       "WebRTC.Video.UniqueNackRequestsSentInPercent"));
-  EXPECT_NE(-1, test::LastHistogramSample(
+  EXPECT_EQ(1, test::NumHistogramSamples(
       "WebRTC.Video.UniqueNackRequestsReceivedInPercent"));
   EXPECT_GT(test::LastHistogramSample(
       "WebRTC.Video.NackPacketsSentPerMinute"), 0);
@@ -1509,6 +1511,165 @@
       "WebRTC.Video.NackPacketsReceivedPerMinute"), 0);
 }
 
+void EndToEndTest::VerifyHistogramStats(bool use_rtx, bool use_red) {
+  class StatsObserver : public test::EndToEndTest, public PacketReceiver {
+   public:
+    StatsObserver(bool use_rtx, bool use_red)
+        : EndToEndTest(kLongTimeoutMs),
+          use_rtx_(use_rtx),
+          use_red_(use_red),
+          sender_call_(nullptr),
+          receiver_call_(nullptr),
+          start_runtime_ms_(-1) {}
+
+   private:
+    Action OnSendRtp(const uint8_t* packet, size_t length) override {
+      if (MinMetricRunTimePassed())
+        observation_complete_->Set();
+
+      return SEND_PACKET;
+    }
+
+    DeliveryStatus DeliverPacket(const uint8_t* packet,
+                                 size_t length) override {
+      // GetStats calls GetSendChannelRtcpStatistics
+      // (via VideoSendStream::GetRtt) which updates ReportBlockStats used by
+      // WebRTC.Video.SentPacketsLostInPercent.
+      // TODO(asapersson): Remove dependency on calling GetStats.
+      sender_call_->GetStats();
+      return receiver_call_->Receiver()->DeliverPacket(packet, length);
+    }
+
+    bool MinMetricRunTimePassed() {
+      int64_t now = Clock::GetRealTimeClock()->TimeInMilliseconds();
+      if (start_runtime_ms_ == -1) {
+        start_runtime_ms_ = now;
+        return false;
+      }
+      int64_t elapsed_sec = (now - start_runtime_ms_) / 1000;
+      return elapsed_sec > metrics::kMinRunTimeInSeconds * 2;
+    }
+
+    void ModifyConfigs(VideoSendStream::Config* send_config,
+                       std::vector<VideoReceiveStream::Config>* receive_configs,
+                       VideoEncoderConfig* encoder_config) override {
+      // NACK
+      send_config->rtp.nack.rtp_history_ms = kNackRtpHistoryMs;
+      (*receive_configs)[0].rtp.nack.rtp_history_ms = kNackRtpHistoryMs;
+      // FEC
+      if (use_red_) {
+        send_config->rtp.fec.ulpfec_payload_type = kUlpfecPayloadType;
+        send_config->rtp.fec.red_payload_type = kRedPayloadType;
+        (*receive_configs)[0].rtp.fec.red_payload_type = kRedPayloadType;
+        (*receive_configs)[0].rtp.fec.ulpfec_payload_type = kUlpfecPayloadType;
+      }
+      // RTX
+      if (use_rtx_) {
+        send_config->rtp.rtx.ssrcs.push_back(kSendRtxSsrcs[0]);
+        send_config->rtp.rtx.payload_type = kSendRtxPayloadType;
+        (*receive_configs)[0].rtp.rtx[kFakeSendPayloadType].ssrc =
+            kSendRtxSsrcs[0];
+        (*receive_configs)[0].rtp.rtx[kFakeSendPayloadType].payload_type =
+            kSendRtxPayloadType;
+      }
+    }
+
+    void OnCallsCreated(Call* sender_call, Call* receiver_call) override {
+      sender_call_ = sender_call;
+      receiver_call_ = receiver_call;
+    }
+
+    void SetReceivers(PacketReceiver* send_transport_receiver,
+                      PacketReceiver* receive_transport_receiver) override {
+      test::RtpRtcpObserver::SetReceivers(this, receive_transport_receiver);
+    }
+
+    void PerformTest() override {
+      EXPECT_EQ(kEventSignaled, Wait())
+          << "Timed out waiting for packet to be NACKed.";
+    }
+
+    bool use_rtx_;
+    bool use_red_;
+    Call* sender_call_;
+    Call* receiver_call_;
+    int64_t start_runtime_ms_;
+  } test(use_rtx, use_red);
+
+  test::ClearHistograms();
+  RunBaseTest(&test);
+
+  // Verify that stats have been updated once.
+  EXPECT_EQ(1, test::NumHistogramSamples(
+      "WebRTC.Video.NackPacketsSentPerMinute"));
+  EXPECT_EQ(1, test::NumHistogramSamples(
+      "WebRTC.Video.NackPacketsReceivedPerMinute"));
+  EXPECT_EQ(1, test::NumHistogramSamples(
+      "WebRTC.Video.FirPacketsSentPerMinute"));
+  EXPECT_EQ(1, test::NumHistogramSamples(
+      "WebRTC.Video.FirPacketsReceivedPerMinute"));
+  EXPECT_EQ(1, test::NumHistogramSamples(
+      "WebRTC.Video.PliPacketsSentPerMinute"));
+  EXPECT_EQ(1, test::NumHistogramSamples(
+      "WebRTC.Video.PliPacketsReceivedPerMinute"));
+
+  EXPECT_EQ(1, test::NumHistogramSamples(
+      "WebRTC.Video.KeyFramesSentInPermille"));
+  EXPECT_EQ(1, test::NumHistogramSamples(
+      "WebRTC.Video.KeyFramesReceivedInPermille"));
+
+  EXPECT_EQ(1, test::NumHistogramSamples(
+      "WebRTC.Video.SentPacketsLostInPercent"));
+  EXPECT_EQ(1, test::NumHistogramSamples(
+      "WebRTC.Video.ReceivedPacketsLostInPercent"));
+
+  EXPECT_EQ(1, test::NumHistogramSamples(
+      "WebRTC.Video.DecodedFramesPerSecond"));
+
+  EXPECT_EQ(1, test::NumHistogramSamples(
+      "WebRTC.Video.BitrateSentInKbps"));
+  EXPECT_EQ(1, test::NumHistogramSamples(
+      "WebRTC.Video.BitrateReceivedInKbps"));
+  EXPECT_EQ(1, test::NumHistogramSamples(
+      "WebRTC.Video.MediaBitrateSentInKbps"));
+  EXPECT_EQ(1, test::NumHistogramSamples(
+      "WebRTC.Video.MediaBitrateReceivedInKbps"));
+  EXPECT_EQ(1, test::NumHistogramSamples(
+      "WebRTC.Video.PaddingBitrateSentInKbps"));
+  EXPECT_EQ(1, test::NumHistogramSamples(
+      "WebRTC.Video.PaddingBitrateReceivedInKbps"));
+  EXPECT_EQ(1, test::NumHistogramSamples(
+      "WebRTC.Video.RetransmittedBitrateSentInKbps"));
+  EXPECT_EQ(1, test::NumHistogramSamples(
+      "WebRTC.Video.RetransmittedBitrateReceivedInKbps"));
+
+  int num_rtx_samples = use_rtx ? 1 : 0;
+  EXPECT_EQ(num_rtx_samples, test::NumHistogramSamples(
+      "WebRTC.Video.RtxBitrateSentInKbps"));
+  EXPECT_EQ(num_rtx_samples, test::NumHistogramSamples(
+      "WebRTC.Video.RtxBitrateReceivedInKbps"));
+
+  int num_red_samples = use_red ? 1 : 0;
+  EXPECT_EQ(num_red_samples, test::NumHistogramSamples(
+      "WebRTC.Video.FecBitrateSentInKbps"));
+  EXPECT_EQ(num_red_samples, test::NumHistogramSamples(
+      "WebRTC.Video.FecBitrateReceivedInKbps"));
+  EXPECT_EQ(num_red_samples, test::NumHistogramSamples(
+      "WebRTC.Video.ReceivedFecPacketsInPercent"));
+}
+
+TEST_F(EndToEndTest, VerifyHistogramStatsWithRtx) {
+  const bool kEnabledRtx = true;
+  const bool kEnabledRed = false;
+  VerifyHistogramStats(kEnabledRtx, kEnabledRed);
+}
+
+TEST_F(EndToEndTest, VerifyHistogramStatsWithRed) {
+  const bool kEnabledRtx = false;
+  const bool kEnabledRed = true;
+  VerifyHistogramStats(kEnabledRtx, kEnabledRed);
+}
+
 void EndToEndTest::TestXrReceiverReferenceTimeReport(bool enable_rrtr) {
   static const int kNumRtcpReportPacketsToObserve = 5;
   class RtcpXrObserver : public test::EndToEndTest {
diff --git a/webrtc/video/receive_statistics_proxy.cc b/webrtc/video/receive_statistics_proxy.cc
index 83b34dd..3637bc1 100644
--- a/webrtc/video/receive_statistics_proxy.cc
+++ b/webrtc/video/receive_statistics_proxy.cc
@@ -12,6 +12,7 @@
 
 #include "webrtc/system_wrappers/interface/clock.h"
 #include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
+#include "webrtc/system_wrappers/interface/metrics.h"
 
 namespace webrtc {
 
@@ -24,7 +25,21 @@
   stats_.ssrc = ssrc;
 }
 
-ReceiveStatisticsProxy::~ReceiveStatisticsProxy() {}
+ReceiveStatisticsProxy::~ReceiveStatisticsProxy() {
+  UpdateHistograms();
+}
+
+void ReceiveStatisticsProxy::UpdateHistograms() const {
+  int fraction_lost;
+  {
+    CriticalSectionScoped lock(crit_.get());
+    fraction_lost = report_block_stats_.FractionLostInPercent();
+  }
+  if (fraction_lost != -1) {
+    RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.ReceivedPacketsLostInPercent",
+        fraction_lost);
+  }
+}
 
 VideoReceiveStream::Stats ReceiveStatisticsProxy::GetStats() const {
   CriticalSectionScoped lock(crit_.get());
@@ -74,6 +89,7 @@
   if (stats_.ssrc != ssrc)
     return;
   stats_.rtcp_stats = statistics;
+  report_block_stats_.Store(statistics, ssrc, 0);
 }
 
 void ReceiveStatisticsProxy::CNameChanged(const char* cname, uint32_t ssrc) {
diff --git a/webrtc/video/receive_statistics_proxy.h b/webrtc/video/receive_statistics_proxy.h
index e27a19c..00ed786 100644
--- a/webrtc/video/receive_statistics_proxy.h
+++ b/webrtc/video/receive_statistics_proxy.h
@@ -20,6 +20,7 @@
 #include "webrtc/modules/video_coding/main/interface/video_coding_defines.h"
 #include "webrtc/video_engine/include/vie_codec.h"
 #include "webrtc/video_engine/include/vie_rtp_rtcp.h"
+#include "webrtc/video_engine/report_block_stats.h"
 #include "webrtc/video_receive_stream.h"
 #include "webrtc/video_renderer.h"
 
@@ -78,12 +79,14 @@
                            uint32_t ssrc) override;
 
  private:
+  void UpdateHistograms() const;
   Clock* const clock_;
 
   rtc::scoped_ptr<CriticalSectionWrapper> crit_;
   VideoReceiveStream::Stats stats_ GUARDED_BY(crit_);
   RateStatistics decode_fps_estimator_ GUARDED_BY(crit_);
   RateStatistics renders_fps_estimator_ GUARDED_BY(crit_);
+  ReportBlockStats report_block_stats_ GUARDED_BY(crit_);
 };
 
 }  // namespace webrtc