Add stats for video:
- number of sent/received RTCP NACK/FIR/PLI per minute
- percentage of unique sent/received NACK requests
- percentage of discarded/duplicated packets by the jitter buffer
- permille of sent/received key frames

BUG=crbug/419657
R=mflodman@webrtc.org, stefan@webrtc.org

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

git-svn-id: http://webrtc.googlecode.com/svn/trunk@7592 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/webrtc/modules/video_coding/main/source/jitter_buffer.cc b/webrtc/modules/video_coding/main/source/jitter_buffer.cc
index d09fccd..e3a4a8a 100644
--- a/webrtc/modules/video_coding/main/source/jitter_buffer.cc
+++ b/webrtc/modules/video_coding/main/source/jitter_buffer.cc
@@ -25,6 +25,7 @@
 #include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
 #include "webrtc/system_wrappers/interface/event_wrapper.h"
 #include "webrtc/system_wrappers/interface/logging.h"
+#include "webrtc/system_wrappers/interface/metrics.h"
 #include "webrtc/system_wrappers/interface/trace_event.h"
 
 namespace webrtc {
@@ -143,6 +144,8 @@
       drop_count_(0),
       num_consecutive_old_frames_(0),
       num_consecutive_old_packets_(0),
+      num_packets_(0),
+      num_duplicated_packets_(0),
       num_discarded_packets_(0),
       jitter_estimate_(clock),
       inter_frame_delay_(clock_->TimeInMilliseconds()),
@@ -190,6 +193,8 @@
     drop_count_ = rhs.drop_count_;
     num_consecutive_old_frames_ = rhs.num_consecutive_old_frames_;
     num_consecutive_old_packets_ = rhs.num_consecutive_old_packets_;
+    num_packets_ = rhs.num_packets_;
+    num_duplicated_packets_ = rhs.num_duplicated_packets_;
     num_discarded_packets_ = rhs.num_discarded_packets_;
     jitter_estimate_ = rhs.jitter_estimate_;
     inter_frame_delay_ = rhs.inter_frame_delay_;
@@ -238,6 +243,15 @@
   }
 }
 
+void VCMJitterBuffer::UpdateHistograms() {
+  if (num_packets_ > 0) {
+    RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.DiscardedPacketsInPercent",
+        num_discarded_packets_ * 100 / num_packets_);
+    RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.DuplicatedPacketsInPercent",
+        num_duplicated_packets_ * 100 / num_packets_);
+  }
+}
+
 void VCMJitterBuffer::Start() {
   CriticalSectionScoped cs(crit_sect_);
   running_ = true;
@@ -250,6 +264,8 @@
 
   num_consecutive_old_frames_ = 0;
   num_consecutive_old_packets_ = 0;
+  num_packets_ = 0;
+  num_duplicated_packets_ = 0;
   num_discarded_packets_ = 0;
 
   // Start in a non-signaled state.
@@ -265,6 +281,7 @@
 
 void VCMJitterBuffer::Stop() {
   crit_sect_->Enter();
+  UpdateHistograms();
   running_ = false;
   last_decoded_state_.Reset();
   free_frames_.clear();
@@ -313,6 +330,16 @@
   return receive_statistics_;
 }
 
+int VCMJitterBuffer::num_packets() const {
+  CriticalSectionScoped cs(crit_sect_);
+  return num_packets_;
+}
+
+int VCMJitterBuffer::num_duplicated_packets() const {
+  CriticalSectionScoped cs(crit_sect_);
+  return num_duplicated_packets_;
+}
+
 int VCMJitterBuffer::num_discarded_packets() const {
   CriticalSectionScoped cs(crit_sect_);
   return num_discarded_packets_;
@@ -543,6 +570,7 @@
 // Gets frame to use for this timestamp. If no match, get empty frame.
 VCMFrameBufferEnum VCMJitterBuffer::GetFrame(const VCMPacket& packet,
                                              VCMFrameBuffer** frame) {
+  ++num_packets_;
   // Does this packet belong to an old frame?
   if (last_decoded_state_.IsOldPacket(&packet)) {
     // Account only for media packets.
@@ -747,6 +775,7 @@
     case kNoError:
     case kOutOfBoundsPacket:
     case kDuplicatePacket: {
+      ++num_duplicated_packets_;
       break;
     }
     case kFlushIndicator:
diff --git a/webrtc/modules/video_coding/main/source/jitter_buffer.h b/webrtc/modules/video_coding/main/source/jitter_buffer.h
index 6ed9cfb..182b80b 100644
--- a/webrtc/modules/video_coding/main/source/jitter_buffer.h
+++ b/webrtc/modules/video_coding/main/source/jitter_buffer.h
@@ -103,6 +103,12 @@
   // won't be able to decode them.
   int num_not_decodable_packets() const;
 
+  // Gets number of packets received.
+  int num_packets() const;
+
+  // Gets number of duplicated packets received.
+  int num_duplicated_packets() const;
+
   // Gets number of packets discarded by the jitter buffer.
   int num_discarded_packets() const;
 
@@ -271,6 +277,8 @@
 
   uint16_t EstimatedLowSequenceNumber(const VCMFrameBuffer& frame) const;
 
+  void UpdateHistograms();
+
   Clock* clock_;
   // If we are running (have started) or not.
   bool running_;
@@ -303,6 +311,10 @@
   int num_consecutive_old_frames_;
   // Number of packets in a row that have been too old.
   int num_consecutive_old_packets_;
+  // Number of packets received.
+  int num_packets_;
+  // Number of duplicated packets received.
+  int num_duplicated_packets_;
   // Number of packets discarded by the jitter buffer.
   int num_discarded_packets_;
 
diff --git a/webrtc/modules/video_coding/main/source/jitter_buffer_unittest.cc b/webrtc/modules/video_coding/main/source/jitter_buffer_unittest.cc
index 0490658..899a3ee 100644
--- a/webrtc/modules/video_coding/main/source/jitter_buffer_unittest.cc
+++ b/webrtc/modules/video_coding/main/source/jitter_buffer_unittest.cc
@@ -512,6 +512,8 @@
   packet_->markerBit = false;
   packet_->seqNum = seq_num_;
   packet_->timestamp = timestamp_;
+  EXPECT_EQ(0, jitter_buffer_->num_packets());
+  EXPECT_EQ(0, jitter_buffer_->num_duplicated_packets());
 
   bool retransmitted = false;
   EXPECT_EQ(kIncomplete, jitter_buffer_->InsertPacket(*packet_,
@@ -520,6 +522,8 @@
   VCMEncodedFrame* frame_out = DecodeCompleteFrame();
 
   EXPECT_TRUE(frame_out == NULL);
+  EXPECT_EQ(1, jitter_buffer_->num_packets());
+  EXPECT_EQ(0, jitter_buffer_->num_duplicated_packets());
 
   packet_->isFirstPacket = false;
   packet_->markerBit = true;
@@ -527,6 +531,8 @@
   // Insert a packet into a frame.
   EXPECT_EQ(kDuplicatePacket, jitter_buffer_->InsertPacket(*packet_,
                                                            &retransmitted));
+  EXPECT_EQ(2, jitter_buffer_->num_packets());
+  EXPECT_EQ(1, jitter_buffer_->num_duplicated_packets());
 
   seq_num_++;
   packet_->seqNum = seq_num_;
@@ -539,6 +545,8 @@
   CheckOutFrame(frame_out, 2 * size_, false);
 
   EXPECT_EQ(kVideoFrameKey, frame_out->FrameType());
+  EXPECT_EQ(3, jitter_buffer_->num_packets());
+  EXPECT_EQ(1, jitter_buffer_->num_duplicated_packets());
 }
 
 TEST_F(TestBasicJitterBuffer, H264InsertStartCode) {
diff --git a/webrtc/system_wrappers/interface/metrics.h b/webrtc/system_wrappers/interface/metrics.h
index be9564a..0390140 100644
--- a/webrtc/system_wrappers/interface/metrics.h
+++ b/webrtc/system_wrappers/interface/metrics.h
@@ -69,12 +69,26 @@
 // Also consider changing string to const char* when switching to atomics.
 
 // Histogram for counters.
+#define RTC_HISTOGRAM_COUNTS_100(name, sample) RTC_HISTOGRAM_COUNTS( \
+    name, sample, 1, 100, 50)
+
+#define RTC_HISTOGRAM_COUNTS_1000(name, sample) RTC_HISTOGRAM_COUNTS( \
+    name, sample, 1, 1000, 50)
+
+#define RTC_HISTOGRAM_COUNTS_10000(name, sample) RTC_HISTOGRAM_COUNTS( \
+    name, sample, 1, 10000, 50)
+
 #define RTC_HISTOGRAM_COUNTS(name, sample, min, max, bucket_count) \
     RTC_HISTOGRAM_COMMON_BLOCK(name, sample, \
         webrtc::metrics::HistogramFactoryGetCounts( \
             name, min, max, bucket_count))
 
+// Histogram for percentage.
+#define RTC_HISTOGRAM_PERCENTAGE(name, sample) \
+    RTC_HISTOGRAM_ENUMERATION(name, sample, 101)
+
 // Histogram for enumerators.
+// |boundary| should be above the max enumerator sample.
 #define RTC_HISTOGRAM_ENUMERATION(name, sample, boundary) \
     RTC_HISTOGRAM_COMMON_BLOCK(name, sample, \
         webrtc::metrics::HistogramFactoryGetEnumeration(name, boundary))
diff --git a/webrtc/video_engine/vie_channel.cc b/webrtc/video_engine/vie_channel.cc
index bbcb602..ded2140 100644
--- a/webrtc/video_engine/vie_channel.cc
+++ b/webrtc/video_engine/vie_channel.cc
@@ -25,6 +25,7 @@
 #include "webrtc/modules/video_render/include/video_render_defines.h"
 #include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
 #include "webrtc/system_wrappers/interface/logging.h"
+#include "webrtc/system_wrappers/interface/metrics.h"
 #include "webrtc/system_wrappers/interface/thread_wrapper.h"
 #include "webrtc/video_engine/call_stats.h"
 #include "webrtc/video_engine/include/vie_codec.h"
@@ -147,7 +148,8 @@
       sender_(sender),
       nack_history_size_sender_(kSendSidePacketHistorySize),
       max_nack_reordering_threshold_(kMaxPacketAgeToNack),
-      pre_render_callback_(NULL) {
+      pre_render_callback_(NULL),
+      start_ms_(Clock::GetRealTimeClock()->TimeInMilliseconds()) {
   RtpRtcp::Configuration configuration;
   configuration.id = ViEModuleId(engine_id, channel_id);
   configuration.audio = false;
@@ -222,6 +224,7 @@
 }
 
 ViEChannel::~ViEChannel() {
+  UpdateHistograms();
   // Make sure we don't get more callbacks from the RTP module.
   module_process_thread_.DeRegisterModule(vie_receiver_.GetReceiveStatistics());
   module_process_thread_.DeRegisterModule(rtp_rtcp_.get());
@@ -246,6 +249,55 @@
   VideoCodingModule::Destroy(vcm_);
 }
 
+void ViEChannel::UpdateHistograms() {
+  const float kMinCallLengthInMinutes = 0.5f;
+  float elapsed_minutes =
+      (Clock::GetRealTimeClock()->TimeInMilliseconds() - start_ms_) / 60000.0f;
+  if (elapsed_minutes < kMinCallLengthInMinutes) {
+    return;
+  }
+  RtcpPacketTypeCounter rtcp_sent;
+  RtcpPacketTypeCounter rtcp_received;
+  GetRtcpPacketTypeCounters(&rtcp_sent, &rtcp_received);
+
+  if (sender_) {
+    if (rtcp_received.nack_requests > 0) {
+      RTC_HISTOGRAM_PERCENTAGE(
+          "WebRTC.Video.UniqueNackRequestsReceivedInPercent",
+              rtcp_received.UniqueNackRequestsInPercent());
+    }
+    RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.NackPacketsReceivedPerMinute",
+        rtcp_received.nack_packets / elapsed_minutes);
+    RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.FirPacketsReceivedPerMinute",
+        rtcp_received.fir_packets / elapsed_minutes);
+    RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.PliPacketsReceivedPerMinute",
+        rtcp_received.pli_packets / elapsed_minutes);
+  } else if (vie_receiver_.GetRemoteSsrc() > 0)  {
+    // Get receive stats if we are receiving packets, i.e. there is a remote
+    // ssrc.
+    if (rtcp_sent.nack_requests > 0) {
+      RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.UniqueNackRequestsSentInPercent",
+          rtcp_sent.UniqueNackRequestsInPercent());
+    }
+    RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.NackPacketsSentPerMinute",
+        rtcp_sent.nack_packets / elapsed_minutes);
+    RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.FirPacketsSentPerMinute",
+        rtcp_sent.fir_packets / elapsed_minutes);
+    RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.PliPacketsSentPerMinute",
+        rtcp_sent.pli_packets / elapsed_minutes);
+
+    webrtc::VCMFrameCount frames;
+    if (vcm_->ReceivedFrameCount(frames) == VCM_OK) {
+      uint32_t total_frames = frames.numKeyFrames + frames.numDeltaFrames;
+      if (total_frames > 0) {
+        RTC_HISTOGRAM_COUNTS_1000("WebRTC.Video.KeyFramesReceivedInPermille",
+            static_cast<int>((frames.numKeyFrames * 1000.0f / total_frames) +
+                0.5f));
+      }
+    }
+  }
+}
+
 int32_t ViEChannel::SetSendCodec(const VideoCodec& video_codec,
                                  bool new_stream) {
   if (!sender_) {
diff --git a/webrtc/video_engine/vie_channel.h b/webrtc/video_engine/vie_channel.h
index e55ade7..0327906 100644
--- a/webrtc/video_engine/vie_channel.h
+++ b/webrtc/video_engine/vie_channel.h
@@ -383,6 +383,8 @@
   int GetRequiredNackListSize(int target_delay_ms);
   void SetRtxSendStatus(bool enable);
 
+  void UpdateHistograms();
+
   // ViEChannel exposes methods that allow to modify observers and callbacks
   // to be modified. Such an API-style is cumbersome to implement and maintain
   // at all the levels when comparing to only setting them at construction. As
@@ -499,6 +501,7 @@
   int nack_history_size_sender_;
   int max_nack_reordering_threshold_;
   I420FrameCallback* pre_render_callback_;
+  const int64_t start_ms_;
 
   std::map<uint32_t, RTCPReportBlock> prev_report_blocks_;
 };
diff --git a/webrtc/video_engine/vie_encoder.cc b/webrtc/video_engine/vie_encoder.cc
index 26ad8ac..9d6da97 100644
--- a/webrtc/video_engine/vie_encoder.cc
+++ b/webrtc/video_engine/vie_encoder.cc
@@ -26,6 +26,7 @@
 #include "webrtc/system_wrappers/interface/clock.h"
 #include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
 #include "webrtc/system_wrappers/interface/logging.h"
+#include "webrtc/system_wrappers/interface/metrics.h"
 #include "webrtc/system_wrappers/interface/tick_util.h"
 #include "webrtc/system_wrappers/interface/trace_event.h"
 #include "webrtc/video_engine/include/vie_codec.h"
@@ -156,7 +157,8 @@
     picture_id_rpsi_(0),
     qm_callback_(NULL),
     video_suspended_(false),
-    pre_encode_callback_(NULL) {
+    pre_encode_callback_(NULL),
+    start_ms_(Clock::GetRealTimeClock()->TimeInMilliseconds()) {
   RtpRtcp::Configuration configuration;
   configuration.id = ViEModuleId(engine_id_, channel_id_);
   configuration.audio = false;  // Video.
@@ -225,6 +227,7 @@
 }
 
 ViEEncoder::~ViEEncoder() {
+  UpdateHistograms();
   if (bitrate_controller_) {
     bitrate_controller_->RemoveBitrateObserver(bitrate_observer_.get());
   }
@@ -237,6 +240,25 @@
   delete qm_callback_;
 }
 
+void ViEEncoder::UpdateHistograms() {
+  const float kMinCallLengthInMinutes = 0.5f;
+  float elapsed_minutes =
+      (Clock::GetRealTimeClock()->TimeInMilliseconds() - start_ms_) / 60000.0f;
+  if (elapsed_minutes < kMinCallLengthInMinutes) {
+    return;
+  }
+  webrtc::VCMFrameCount frames;
+  if (vcm_.SentFrameCount(frames) != VCM_OK) {
+    return;
+  }
+  uint32_t total_frames = frames.numKeyFrames + frames.numDeltaFrames;
+  if (total_frames > 0) {
+    RTC_HISTOGRAM_COUNTS_1000("WebRTC.Video.KeyFramesSentInPermille",
+        static_cast<int>(
+            (frames.numKeyFrames * 1000.0f / total_frames) + 0.5f));
+  }
+}
+
 int ViEEncoder::Owner() const {
   return channel_id_;
 }
diff --git a/webrtc/video_engine/vie_encoder.h b/webrtc/video_engine/vie_encoder.h
index 51c1d01..36f87fa 100644
--- a/webrtc/video_engine/vie_encoder.h
+++ b/webrtc/video_engine/vie_encoder.h
@@ -193,6 +193,8 @@
  private:
   bool EncoderPaused() const EXCLUSIVE_LOCKS_REQUIRED(data_cs_);
 
+  void UpdateHistograms();
+
   int32_t engine_id_;
   const int channel_id_;
   const uint32_t number_of_cores_;
@@ -235,6 +237,7 @@
   QMVideoSettingsCallback* qm_callback_;
   bool video_suspended_ GUARDED_BY(data_cs_);
   I420FrameCallback* pre_encode_callback_ GUARDED_BY(callback_cs_);
+  const int64_t start_ms_;
 };
 
 }  // namespace webrtc