Add video send bitrates to histogram stats:
- total bitrate ("WebRTC.Video.BitrateSentInKbps")
- media bitrate ("WebRTC.Video.MediaBitrateSentInKbps")
- rtx bitrate ("WebRTC.Video.RtxBitrateSentInKbps")
- padding bitrate ("WebRTC.Video.PaddingBitrateSentInKbps")
- retransmitted bitrate ("WebRTC.Video.RetransmittedBitrateInKbps")

Add retransmitted bytes to StreamDataCounters.

Change in UpdateRtpStats to also update counters for retransmitted packet.

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

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

git-svn-id: http://webrtc.googlecode.com/svn/trunk/webrtc@7838 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/common_types.h b/common_types.h
index 9cb56af..43d1bd7 100644
--- a/common_types.h
+++ b/common_types.h
@@ -229,13 +229,16 @@
   uint32_t unique_nack_requests;  // Number of unique NACKed RTP packets.
 };
 
-// Data usage statistics for a (rtp) stream
+// Data usage statistics for a (rtp) stream.
 struct StreamDataCounters {
   StreamDataCounters()
    : bytes(0),
      header_bytes(0),
      padding_bytes(0),
      packets(0),
+     retransmitted_bytes(0),
+     retransmitted_header_bytes(0),
+     retransmitted_padding_bytes(0),
      retransmitted_packets(0),
      fec_packets(0) {}
 
@@ -244,15 +247,34 @@
     header_bytes += other.header_bytes;
     padding_bytes += other.padding_bytes;
     packets += other.packets;
+    retransmitted_bytes += other.retransmitted_bytes;
+    retransmitted_header_bytes += other.retransmitted_header_bytes;
+    retransmitted_padding_bytes += other.retransmitted_padding_bytes;
     retransmitted_packets += other.retransmitted_packets;
     fec_packets += other.fec_packets;
   }
 
+  size_t TotalBytes() const {
+    return bytes + header_bytes + padding_bytes;
+  }
+
+  size_t RetransmittedBytes() const {
+    return retransmitted_bytes + retransmitted_header_bytes +
+           retransmitted_padding_bytes;
+  }
+
+  size_t MediaPayloadBytes() const {
+    return bytes - retransmitted_bytes;
+  }
+
   // TODO(pbos): Rename bytes -> media_bytes.
   size_t bytes;  // Payload bytes, excluding RTP headers and padding.
   size_t header_bytes;  // Number of bytes used by RTP headers.
   size_t padding_bytes;  // Number of padding bytes.
   uint32_t packets;  // Number of packets.
+  size_t retransmitted_bytes;  // Number of retransmitted payload bytes.
+  size_t retransmitted_header_bytes;  // Retransmitted bytes used by RTP header.
+  size_t retransmitted_padding_bytes;  // Retransmitted padding bytes.
   uint32_t retransmitted_packets;  // Number of retransmitted packets.
   uint32_t fec_packets;  // Number of redundancy packets.
 };
diff --git a/modules/rtp_rtcp/interface/receive_statistics.h b/modules/rtp_rtcp/interface/receive_statistics.h
index 8cbed80..cf6b975 100644
--- a/modules/rtp_rtcp/interface/receive_statistics.h
+++ b/modules/rtp_rtcp/interface/receive_statistics.h
@@ -57,7 +57,7 @@
 
   // Updates the receive statistics with this packet.
   virtual void IncomingPacket(const RTPHeader& rtp_header,
-                              size_t bytes,
+                              size_t packet_length,
                               bool retransmitted) = 0;
 
   // Increment counter for number of FEC packets received.
@@ -85,7 +85,7 @@
 class NullReceiveStatistics : public ReceiveStatistics {
  public:
   virtual void IncomingPacket(const RTPHeader& rtp_header,
-                              size_t bytes,
+                              size_t packet_length,
                               bool retransmitted) OVERRIDE;
   virtual void FecPacketReceived(uint32_t ssrc) OVERRIDE;
   virtual StatisticianMap GetActiveStatisticians() const OVERRIDE;
diff --git a/modules/rtp_rtcp/interface/rtp_rtcp.h b/modules/rtp_rtcp/interface/rtp_rtcp.h
index d49c8e3..bff099d 100644
--- a/modules/rtp_rtcp/interface/rtp_rtcp.h
+++ b/modules/rtp_rtcp/interface/rtp_rtcp.h
@@ -434,13 +434,21 @@
     virtual int32_t ResetSendDataCountersRTP() = 0;
 
     /*
-    *   statistics of the amount of data sent and received
+    *   Statistics of the amount of data sent
     *
     *   return -1 on failure else 0
     */
     virtual int32_t DataCountersRTP(
         size_t* bytesSent,
         uint32_t* packetsSent) const = 0;
+
+    /*
+    *   Get send statistics for the RTP and RTX stream.
+    */
+    virtual void GetSendStreamDataCounters(
+        StreamDataCounters* rtp_counters,
+        StreamDataCounters* rtx_counters) const = 0;
+
     /*
     *   Get received RTCP sender info
     *
@@ -455,6 +463,7 @@
     */
     virtual int32_t RemoteRTCPStat(
         std::vector<RTCPReportBlock>* receiveBlocks) const = 0;
+
     /*
     *   Set received RTCP report block
     *
diff --git a/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h b/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h
index c76347f..0edcf45 100644
--- a/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h
+++ b/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h
@@ -172,6 +172,8 @@
       int32_t());
   MOCK_CONST_METHOD2(DataCountersRTP,
       int32_t(size_t *bytesSent, uint32_t *packetsSent));
+  MOCK_CONST_METHOD2(GetSendStreamDataCounters,
+      void(StreamDataCounters*, StreamDataCounters*));
   MOCK_METHOD1(RemoteRTCPStat,
       int32_t(RTCPSenderInfo* senderInfo));
   MOCK_CONST_METHOD1(RemoteRTCPStat,
diff --git a/modules/rtp_rtcp/source/receive_statistics_impl.cc b/modules/rtp_rtcp/source/receive_statistics_impl.cc
index 8585e16..fd4b82e 100644
--- a/modules/rtp_rtcp/source/receive_statistics_impl.cc
+++ b/modules/rtp_rtcp/source/receive_statistics_impl.cc
@@ -68,26 +68,30 @@
 }
 
 void StreamStatisticianImpl::IncomingPacket(const RTPHeader& header,
-                                            size_t bytes,
+                                            size_t packet_length,
                                             bool retransmitted) {
-  UpdateCounters(header, bytes, retransmitted);
+  UpdateCounters(header, packet_length, retransmitted);
   NotifyRtpCallback();
 }
 
 void StreamStatisticianImpl::UpdateCounters(const RTPHeader& header,
-                                            size_t bytes,
+                                            size_t packet_length,
                                             bool retransmitted) {
   CriticalSectionScoped cs(stream_lock_.get());
   bool in_order = InOrderPacketInternal(header.sequenceNumber);
   ssrc_ = header.ssrc;
-  incoming_bitrate_.Update(bytes);
+  incoming_bitrate_.Update(packet_length);
   receive_counters_.bytes +=
-      bytes - (header.paddingLength + header.headerLength);
+      packet_length - (header.paddingLength + header.headerLength);
   receive_counters_.header_bytes += header.headerLength;
   receive_counters_.padding_bytes += header.paddingLength;
   ++receive_counters_.packets;
   if (!in_order && retransmitted) {
     ++receive_counters_.retransmitted_packets;
+    receive_counters_.retransmitted_bytes +=
+        packet_length - (header.paddingLength + header.headerLength);
+    receive_counters_.retransmitted_header_bytes += header.headerLength;
+    receive_counters_.retransmitted_padding_bytes += header.paddingLength;
   }
 
   if (receive_counters_.packets == 1) {
@@ -414,7 +418,7 @@
 }
 
 void ReceiveStatisticsImpl::IncomingPacket(const RTPHeader& header,
-                                           size_t bytes,
+                                           size_t packet_length,
                                            bool retransmitted) {
   StreamStatisticianImpl* impl;
   {
@@ -431,7 +435,7 @@
   // this whole ReceiveStatisticsImpl is destroyed. StreamStatisticianImpl has
   // it's own locking so don't hold receive_statistics_lock_ (potential
   // deadlock).
-  impl->IncomingPacket(header, bytes, retransmitted);
+  impl->IncomingPacket(header, packet_length, retransmitted);
 }
 
 void ReceiveStatisticsImpl::FecPacketReceived(uint32_t ssrc) {
@@ -527,7 +531,7 @@
 }
 
 void NullReceiveStatistics::IncomingPacket(const RTPHeader& rtp_header,
-                                           size_t bytes,
+                                           size_t packet_length,
                                            bool retransmitted) {}
 
 void NullReceiveStatistics::FecPacketReceived(uint32_t ssrc) {}
diff --git a/modules/rtp_rtcp/source/receive_statistics_impl.h b/modules/rtp_rtcp/source/receive_statistics_impl.h
index f3eaa58..87eea01 100644
--- a/modules/rtp_rtcp/source/receive_statistics_impl.h
+++ b/modules/rtp_rtcp/source/receive_statistics_impl.h
@@ -42,7 +42,7 @@
   virtual bool IsPacketInOrder(uint16_t sequence_number) const OVERRIDE;
 
   void IncomingPacket(const RTPHeader& rtp_header,
-                      size_t bytes,
+                      size_t packet_length,
                       bool retransmitted);
   void FecPacketReceived();
   void SetMaxReorderingThreshold(int max_reordering_threshold);
@@ -56,7 +56,7 @@
                     uint32_t receive_time_secs,
                     uint32_t receive_time_frac);
   void UpdateCounters(const RTPHeader& rtp_header,
-                      size_t bytes,
+                      size_t packet_length,
                       bool retransmitted);
   void NotifyRtpCallback() LOCKS_EXCLUDED(stream_lock_.get());
   void NotifyRtcpCallback() LOCKS_EXCLUDED(stream_lock_.get());
@@ -108,7 +108,7 @@
 
   // Implement ReceiveStatistics.
   virtual void IncomingPacket(const RTPHeader& header,
-                              size_t bytes,
+                              size_t packet_length,
                               bool retransmitted) OVERRIDE;
   virtual void FecPacketReceived(uint32_t ssrc) OVERRIDE;
   virtual StatisticianMap GetActiveStatisticians() const OVERRIDE;
diff --git a/modules/rtp_rtcp/source/receive_statistics_unittest.cc b/modules/rtp_rtcp/source/receive_statistics_unittest.cc
index 808a880..6cc6008 100644
--- a/modules/rtp_rtcp/source/receive_statistics_unittest.cc
+++ b/modules/rtp_rtcp/source/receive_statistics_unittest.cc
@@ -132,6 +132,23 @@
   EXPECT_EQ(2u, packets_received);
 }
 
+TEST_F(ReceiveStatisticsTest, GetReceiveStreamDataCounters) {
+  receive_statistics_->IncomingPacket(header1_, kPacketSize1, false);
+  StreamStatistician* statistician =
+      receive_statistics_->GetStatistician(kSsrc1);
+  ASSERT_TRUE(statistician != NULL);
+
+  StreamDataCounters counters;
+  statistician->GetReceiveStreamDataCounters(&counters);
+  EXPECT_EQ(1u, counters.packets);
+
+  // GetReceiveStreamDataCounters includes reset counter values.
+  statistician->ResetStatistics();
+  receive_statistics_->IncomingPacket(header1_, kPacketSize1, false);
+  statistician->GetReceiveStreamDataCounters(&counters);
+  EXPECT_EQ(2u, counters.packets);
+}
+
 TEST_F(ReceiveStatisticsTest, RtcpCallbacks) {
   class TestCallback : public RtcpStatisticsCallback {
    public:
@@ -232,20 +249,22 @@
     ++num_calls_;
   }
 
-  void ExpectMatches(uint32_t num_calls,
-                     uint32_t ssrc,
-                     size_t bytes,
-                     size_t padding,
-                     uint32_t packets,
-                     uint32_t retransmits,
-                     uint32_t fec) {
+  void Matches(uint32_t num_calls,
+               uint32_t ssrc,
+               const StreamDataCounters& expected) {
     EXPECT_EQ(num_calls, num_calls_);
     EXPECT_EQ(ssrc, ssrc_);
-    EXPECT_EQ(bytes, stats_.bytes);
-    EXPECT_EQ(padding, stats_.padding_bytes);
-    EXPECT_EQ(packets, stats_.packets);
-    EXPECT_EQ(retransmits, stats_.retransmitted_packets);
-    EXPECT_EQ(fec, stats_.fec_packets);
+    EXPECT_EQ(expected.bytes, stats_.bytes);
+    EXPECT_EQ(expected.header_bytes, stats_.header_bytes);
+    EXPECT_EQ(expected.padding_bytes, stats_.padding_bytes);
+    EXPECT_EQ(expected.packets, stats_.packets);
+    EXPECT_EQ(expected.retransmitted_bytes, stats_.retransmitted_bytes);
+    EXPECT_EQ(expected.retransmitted_header_bytes,
+              stats_.retransmitted_header_bytes);
+    EXPECT_EQ(expected.retransmitted_padding_bytes,
+              stats_.retransmitted_padding_bytes);
+    EXPECT_EQ(expected.retransmitted_packets, stats_.retransmitted_packets);
+    EXPECT_EQ(expected.fec_packets, stats_.fec_packets);
   }
 
   uint32_t num_calls_;
@@ -264,7 +283,17 @@
   header1_.headerLength = kHeaderLength;
   receive_statistics_->IncomingPacket(
       header1_, kPacketSize1 + kHeaderLength, false);
-  callback.ExpectMatches(1, kSsrc1, kPacketSize1, 0, 1, 0, 0);
+  StreamDataCounters expected;
+  expected.bytes = kPacketSize1;
+  expected.header_bytes = kHeaderLength;
+  expected.padding_bytes = 0;
+  expected.packets = 1;
+  expected.retransmitted_bytes = 0;
+  expected.retransmitted_header_bytes = 0;
+  expected.retransmitted_padding_bytes = 0;
+  expected.retransmitted_packets = 0;
+  expected.fec_packets = 0;
+  callback.Matches(1, kSsrc1, expected);
 
   ++header1_.sequenceNumber;
   clock_.AdvanceTimeMilliseconds(5);
@@ -272,14 +301,25 @@
   // Another packet of size kPacketSize1 with 9 bytes padding.
   receive_statistics_->IncomingPacket(
       header1_, kPacketSize1 + kHeaderLength + kPaddingLength, false);
-  callback.ExpectMatches(2, kSsrc1, 2 * kPacketSize1, kPaddingLength, 2, 0, 0);
+  expected.bytes = kPacketSize1 * 2;
+  expected.header_bytes = kHeaderLength * 2;
+  expected.padding_bytes = kPaddingLength;
+  expected.packets = 2;
+  callback.Matches(2, kSsrc1, expected);
 
   clock_.AdvanceTimeMilliseconds(5);
   // Retransmit last packet.
   receive_statistics_->IncomingPacket(
       header1_, kPacketSize1 + kHeaderLength + kPaddingLength, true);
-  callback.ExpectMatches(
-      3, kSsrc1, 3 * kPacketSize1, kPaddingLength * 2, 3, 1, 0);
+  expected.bytes = kPacketSize1 * 3;
+  expected.header_bytes = kHeaderLength * 3;
+  expected.padding_bytes = kPaddingLength * 2;
+  expected.packets = 3;
+  expected.retransmitted_bytes = kPacketSize1;
+  expected.retransmitted_header_bytes = kHeaderLength;
+  expected.retransmitted_padding_bytes = kPaddingLength;
+  expected.retransmitted_packets = 1;
+  callback.Matches(3, kSsrc1, expected);
 
   header1_.paddingLength = 0;
   ++header1_.sequenceNumber;
@@ -288,8 +328,11 @@
   receive_statistics_->IncomingPacket(
       header1_, kPacketSize1 + kHeaderLength, false);
   receive_statistics_->FecPacketReceived(kSsrc1);
-  callback.ExpectMatches(
-      5, kSsrc1, 4 * kPacketSize1, kPaddingLength * 2, 4, 1, 1);
+  expected.bytes = kPacketSize1 * 4;
+  expected.header_bytes = kHeaderLength * 4;
+  expected.packets = 4;
+  expected.fec_packets = 1;
+  callback.Matches(5, kSsrc1, expected);
 
   receive_statistics_->RegisterRtpStatisticsCallback(NULL);
 
@@ -298,8 +341,7 @@
   clock_.AdvanceTimeMilliseconds(5);
   receive_statistics_->IncomingPacket(
       header1_, kPacketSize1 + kHeaderLength, true);
-  callback.ExpectMatches(
-      5, kSsrc1, 4 * kPacketSize1, kPaddingLength * 2, 4, 1, 1);
+  callback.Matches(5, kSsrc1, expected);
 }
 
 TEST_F(ReceiveStatisticsTest, RtpCallbacksFecFirst) {
@@ -315,9 +357,16 @@
   header1_.headerLength = kHeaderLength;
   receive_statistics_->IncomingPacket(
       header1_, kPacketSize1 + kHeaderLength, false);
-  callback.ExpectMatches(1, kSsrc1, kPacketSize1, 0, 1, 0, 0);
+  StreamDataCounters expected;
+  expected.bytes = kPacketSize1;
+  expected.header_bytes = kHeaderLength;
+  expected.padding_bytes = 0;
+  expected.packets = 1;
+  expected.fec_packets = 0;
+  callback.Matches(1, kSsrc1, expected);
 
   receive_statistics_->FecPacketReceived(kSsrc1);
-  callback.ExpectMatches(2, kSsrc1, kPacketSize1, 0, 1, 0, 1);
+  expected.fec_packets = 1;
+  callback.Matches(2, kSsrc1, expected);
 }
 }  // namespace webrtc
diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl.cc b/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
index 44f7231..f1f1dd6 100644
--- a/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
+++ b/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
@@ -803,6 +803,7 @@
   return rtcp_sender_.RtcpXrReceiverReferenceTime();
 }
 
+// TODO(asapersson): Replace this method with the one below.
 int32_t ModuleRtpRtcpImpl::DataCountersRTP(
     size_t* bytes_sent,
     uint32_t* packets_sent) const {
@@ -821,6 +822,12 @@
   return 0;
 }
 
+void ModuleRtpRtcpImpl::GetSendStreamDataCounters(
+    StreamDataCounters* rtp_counters,
+    StreamDataCounters* rtx_counters) const {
+  rtp_sender_.GetDataCounters(rtp_counters, rtx_counters);
+}
+
 int32_t ModuleRtpRtcpImpl::RemoteRTCPStat(RTCPSenderInfo* sender_info) {
   return rtcp_receiver_.SenderInfoReceived(sender_info);
 }
diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl.h b/modules/rtp_rtcp/source/rtp_rtcp_impl.h
index dd99a9d..0b6de8e 100644
--- a/modules/rtp_rtcp/source/rtp_rtcp_impl.h
+++ b/modules/rtp_rtcp/source/rtp_rtcp_impl.h
@@ -177,6 +177,10 @@
   virtual int32_t DataCountersRTP(size_t* bytes_sent,
                                   uint32_t* packets_sent) const OVERRIDE;
 
+  virtual void GetSendStreamDataCounters(
+      StreamDataCounters* rtp_counters,
+      StreamDataCounters* rtx_counters) const OVERRIDE;
+
   // Get received RTCP report, sender info.
   virtual int32_t RemoteRTCPStat(RTCPSenderInfo* sender_info) OVERRIDE;
 
diff --git a/modules/rtp_rtcp/source/rtp_sender.cc b/modules/rtp_rtcp/source/rtp_sender.cc
index f1327ac..7c4307f 100644
--- a/modules/rtp_rtcp/source/rtp_sender.cc
+++ b/modules/rtp_rtcp/source/rtp_sender.cc
@@ -890,7 +890,7 @@
 }
 
 void RTPSender::UpdateRtpStats(const uint8_t* buffer,
-                               size_t size,
+                               size_t packet_length,
                                const RTPHeader& header,
                                bool is_rtx,
                                bool is_retransmit) {
@@ -905,7 +905,7 @@
     counters = &rtp_stats_;
   }
 
-  total_bitrate_sent_.Update(size);
+  total_bitrate_sent_.Update(packet_length);
   ++counters->packets;
   if (IsFecPacket(buffer, header)) {
     ++counters->fec_packets;
@@ -913,11 +913,15 @@
 
   if (is_retransmit) {
     ++counters->retransmitted_packets;
-  } else {
-    counters->bytes += size - (header.headerLength + header.paddingLength);
-    counters->header_bytes += header.headerLength;
-    counters->padding_bytes += header.paddingLength;
+    counters->retransmitted_bytes +=
+        packet_length - (header.headerLength + header.paddingLength);
+    counters->retransmitted_header_bytes += header.headerLength;
+    counters->retransmitted_padding_bytes += header.paddingLength;
   }
+  counters->bytes +=
+      packet_length - (header.headerLength + header.paddingLength);
+  counters->header_bytes += header.headerLength;
+  counters->padding_bytes += header.paddingLength;
 
   if (rtp_stats_callback_) {
     rtp_stats_callback_->DataCountersUpdated(*counters, ssrc);
diff --git a/modules/rtp_rtcp/source/rtp_sender.h b/modules/rtp_rtcp/source/rtp_sender.h
index 0558c19..795331a 100644
--- a/modules/rtp_rtcp/source/rtp_sender.h
+++ b/modules/rtp_rtcp/source/rtp_sender.h
@@ -327,7 +327,7 @@
                               const int64_t now_ms) const;
 
   void UpdateRtpStats(const uint8_t* buffer,
-                      size_t size,
+                      size_t packet_length,
                       const RTPHeader& header,
                       bool is_rtx,
                       bool is_retransmit);
diff --git a/modules/rtp_rtcp/source/rtp_sender_unittest.cc b/modules/rtp_rtcp/source/rtp_sender_unittest.cc
index 905b559..866258b 100644
--- a/modules/rtp_rtcp/source/rtp_sender_unittest.cc
+++ b/modules/rtp_rtcp/source/rtp_sender_unittest.cc
@@ -927,16 +927,20 @@
 
     uint32_t ssrc_;
     StreamDataCounters counters_;
-    bool Matches(uint32_t ssrc, size_t bytes, size_t header_bytes,
-                 size_t padding, uint32_t packets, uint32_t retransmits,
-                 uint32_t fec) {
-      return ssrc_ == ssrc &&
-          counters_.bytes == bytes &&
-          counters_.header_bytes == header_bytes &&
-          counters_.padding_bytes == padding &&
-          counters_.packets == packets &&
-          counters_.retransmitted_packets == retransmits &&
-          counters_.fec_packets == fec;
+    void Matches(uint32_t ssrc, const StreamDataCounters& counters) {
+      EXPECT_EQ(ssrc, ssrc_);
+      EXPECT_EQ(counters.bytes, counters_.bytes);
+      EXPECT_EQ(counters.header_bytes, counters_.header_bytes);
+      EXPECT_EQ(counters.padding_bytes, counters_.padding_bytes);
+      EXPECT_EQ(counters.packets, counters_.packets);
+      EXPECT_EQ(counters.retransmitted_bytes, counters_.retransmitted_bytes);
+      EXPECT_EQ(counters.retransmitted_header_bytes,
+                counters_.retransmitted_header_bytes);
+      EXPECT_EQ(counters.retransmitted_padding_bytes,
+                counters_.retransmitted_padding_bytes);
+      EXPECT_EQ(counters.retransmitted_packets,
+                counters_.retransmitted_packets);
+      EXPECT_EQ(counters.fec_packets, counters_.fec_packets);
     }
 
   } callback;
@@ -957,21 +961,37 @@
   ASSERT_EQ(0, rtp_sender_->SendOutgoingData(kVideoFrameKey, payload_type, 1234,
                                              4321, payload, sizeof(payload),
                                              NULL));
-
-  // {bytes = 6, header = 12, padding = 0, packets = 1, retrans = 0, fec = 0}
-  EXPECT_TRUE(callback.Matches(ssrc, 6, 12, 0, 1, 0, 0));
+  StreamDataCounters expected;
+  expected.bytes = 6;
+  expected.header_bytes = 12;
+  expected.padding_bytes = 0;
+  expected.packets = 1;
+  expected.retransmitted_bytes = 0;
+  expected.retransmitted_header_bytes = 0;
+  expected.retransmitted_padding_bytes = 0;
+  expected.retransmitted_packets = 0;
+  expected.fec_packets = 0;
+  callback.Matches(ssrc, expected);
 
   // Retransmit a frame.
   uint16_t seqno = rtp_sender_->SequenceNumber() - 1;
   rtp_sender_->ReSendPacket(seqno, 0);
-
-  // bytes = 6, header = 12, padding = 0, packets = 2, retrans = 1, fec = 0}
-  EXPECT_TRUE(callback.Matches(ssrc, 6, 12, 0, 2, 1, 0));
+  expected.bytes = 12;
+  expected.header_bytes = 24;
+  expected.packets = 2;
+  expected.retransmitted_bytes = 6;
+  expected.retransmitted_header_bytes = 12;
+  expected.retransmitted_padding_bytes = 0;
+  expected.retransmitted_packets = 1;
+  callback.Matches(ssrc, expected);
 
   // Send padding.
   rtp_sender_->TimeToSendPadding(kMaxPaddingSize);
-  // {bytes = 6, header = 24, padding = 224, packets = 3, retrans = 1, fec = 0}
-  EXPECT_TRUE(callback.Matches(ssrc, 6, 24, kMaxPaddingSize, 3, 1, 0));
+  expected.bytes = 12;
+  expected.header_bytes = 36;
+  expected.padding_bytes = kMaxPaddingSize;
+  expected.packets = 3;
+  callback.Matches(ssrc, expected);
 
   // Send FEC.
   rtp_sender_->SetGenericFECStatus(true, kRedPayloadType, kUlpfecPayloadType);
@@ -984,9 +1004,11 @@
   ASSERT_EQ(0, rtp_sender_->SendOutgoingData(kVideoFrameDelta, payload_type,
                                              1234, 4321, payload,
                                              sizeof(payload), NULL));
-
-  // {bytes = 34, header = 48, padding = 224, packets = 5, retrans = 1, fec = 1}
-  EXPECT_TRUE(callback.Matches(ssrc, 34, 48, kMaxPaddingSize, 5, 1, 1));
+  expected.bytes = 40;
+  expected.header_bytes = 60;
+  expected.packets = 5;
+  expected.fec_packets = 1;
+  callback.Matches(ssrc, expected);
 
   rtp_sender_->RegisterRtpStatisticsCallback(NULL);
 }
@@ -1147,9 +1169,12 @@
   EXPECT_EQ(rtx_stats.header_bytes, 24u);
   EXPECT_EQ(rtx_stats.padding_bytes, 2 * kMaxPaddingSize);
 
+  EXPECT_EQ(rtp_stats.TotalBytes(),
+      rtp_stats.bytes + rtp_stats.header_bytes + rtp_stats.padding_bytes);
+  EXPECT_EQ(rtx_stats.TotalBytes(),
+      rtx_stats.bytes + rtx_stats.header_bytes + rtx_stats.padding_bytes);
+
   EXPECT_EQ(transport_.total_bytes_sent_,
-            rtp_stats.bytes + rtp_stats.header_bytes + rtp_stats.padding_bytes +
-                rtx_stats.bytes + rtx_stats.header_bytes +
-                rtx_stats.padding_bytes);
+            rtp_stats.TotalBytes() + rtx_stats.TotalBytes());
 }
 }  // namespace webrtc
diff --git a/system_wrappers/interface/metrics.h b/system_wrappers/interface/metrics.h
index 36da4cd..1b9426e 100644
--- a/system_wrappers/interface/metrics.h
+++ b/system_wrappers/interface/metrics.h
@@ -78,6 +78,9 @@
 #define RTC_HISTOGRAM_COUNTS_10000(name, sample) RTC_HISTOGRAM_COUNTS( \
     name, sample, 1, 10000, 50)
 
+#define RTC_HISTOGRAM_COUNTS_100000(name, sample) RTC_HISTOGRAM_COUNTS( \
+    name, sample, 1, 100000, 50)
+
 #define RTC_HISTOGRAM_COUNTS(name, sample, min, max, bucket_count) \
     RTC_HISTOGRAM_COMMON_BLOCK(name, sample, \
         webrtc::metrics::HistogramFactoryGetCounts( \
diff --git a/video_engine/vie_channel.cc b/video_engine/vie_channel.cc
index e6fbf3c..7b65c76 100644
--- a/video_engine/vie_channel.cc
+++ b/video_engine/vie_channel.cc
@@ -148,7 +148,8 @@
       nack_history_size_sender_(kSendSidePacketHistorySize),
       max_nack_reordering_threshold_(kMaxPacketAgeToNack),
       pre_render_callback_(NULL),
-      start_ms_(Clock::GetRealTimeClock()->TimeInMilliseconds()) {
+      start_ms_(Clock::GetRealTimeClock()->TimeInMilliseconds()),
+      start_send_ms_(0) {
   RtpRtcp::Configuration configuration = CreateRtpRtcpConfiguration();
   configuration.remote_bitrate_estimator = remote_bitrate_estimator;
   configuration.receive_statistics = vie_receiver_.GetReceiveStatistics();
@@ -236,6 +237,7 @@
 }
 
 void ViEChannel::UpdateHistograms() {
+  // TODO(asapersson): Use time from first sent/received packet.
   float elapsed_minutes =
       (Clock::GetRealTimeClock()->TimeInMilliseconds() - start_ms_) / 60000.0f;
   if (elapsed_minutes < metrics::kMinRunTimeInSeconds / 60.0f) {
@@ -271,29 +273,55 @@
     RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.PliPacketsSentPerMinute",
         rtcp_sent.pli_packets / elapsed_minutes);
 
-    StreamDataCounters data;
-    StreamDataCounters rtx_data;
-    GetReceiveStreamDataCounters(&data, &rtx_data);
-    uint32_t media_bytes = data.bytes;
-    uint32_t rtx_bytes =
-        rtx_data.bytes + rtx_data.header_bytes + rtx_data.padding_bytes;
-    uint32_t total_bytes = data.bytes + data.header_bytes + data.padding_bytes;
-    total_bytes += rtx_bytes;
-    uint32_t padding_bytes = data.padding_bytes + rtx_data.padding_bytes;
+    StreamDataCounters rtp;
+    StreamDataCounters rtx;
+    GetReceiveStreamDataCounters(&rtp, &rtx);
+    StreamDataCounters rtp_rtx = rtp;
+    rtp_rtx.Add(rtx);
     RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.BitrateReceivedInKbps",
-        total_bytes * 8 / (elapsed_minutes * 60) / 1000);
+        rtp_rtx.TotalBytes() * 8 / (elapsed_minutes * 60) / 1000);
     RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.MediaBitrateReceivedInKbps",
-        media_bytes * 8 / (elapsed_minutes * 60) / 1000);
+        rtp.MediaPayloadBytes() * 8 / (elapsed_minutes * 60) / 1000);
     RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.PaddingBitrateReceivedInKbps",
-        padding_bytes * 8 / (elapsed_minutes * 60) / 1000);
+        rtp_rtx.padding_bytes * 8 / (elapsed_minutes * 60) / 1000);
+    RTC_HISTOGRAM_COUNTS_10000(
+        "WebRTC.Video.RetransmittedBitrateReceivedInKbps",
+            rtp_rtx.RetransmittedBytes() * 8 / (elapsed_minutes * 60) / 1000);
     uint32_t ssrc = 0;
     if (vie_receiver_.GetRtxSsrc(&ssrc)) {
       RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.RtxBitrateReceivedInKbps",
-          rtx_bytes * 8 / (elapsed_minutes * 60) / 1000);
+          rtx.TotalBytes() * 8 / (elapsed_minutes * 60) / 1000);
     }
   }
 }
 
+void ViEChannel::UpdateHistogramsAtStopSend() {
+  // TODO(asapersson): Use time from first sent packet.
+  int64_t elapsed_sec =
+      (Clock::GetRealTimeClock()->TimeInMilliseconds() - start_send_ms_) / 1000;
+  if (elapsed_sec < metrics::kMinRunTimeInSeconds) {
+    return;
+  }
+  StreamDataCounters rtp;
+  StreamDataCounters rtx;
+  GetSendStreamDataCounters(&rtp, &rtx);
+  StreamDataCounters rtp_rtx = rtp;
+  rtp_rtx.Add(rtx);
+  RTC_HISTOGRAM_COUNTS_100000("WebRTC.Video.BitrateSentInKbps",
+      rtp_rtx.TotalBytes() * 8 / elapsed_sec / 1000);
+  RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.MediaBitrateSentInKbps",
+      rtp.MediaPayloadBytes() * 8 / elapsed_sec / 1000);
+  RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.PaddingBitrateSentInKbps",
+      rtp_rtx.padding_bytes * 8 / elapsed_sec / 1000);
+  RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.RetransmittedBitrateSentInKbps",
+      rtp_rtx.RetransmittedBytes() * 8 / elapsed_sec / 1000);
+  uint32_t ssrc = 0;
+  if (vie_receiver_.GetRtxSsrc(&ssrc)) {
+    RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.RtxBitrateSentInKbps",
+        rtx.TotalBytes() * 8 / elapsed_sec / 1000);
+  }
+}
+
 int32_t ViEChannel::SetSendCodec(const VideoCodec& video_codec,
                                  bool new_stream) {
   if (!sender_) {
@@ -1174,20 +1202,44 @@
   return 0;
 }
 
+void ViEChannel::GetSendStreamDataCounters(
+    StreamDataCounters* rtp_counters,
+    StreamDataCounters* rtx_counters) const {
+  rtp_rtcp_->GetSendStreamDataCounters(rtp_counters, rtx_counters);
+  CriticalSectionScoped cs(rtp_rtcp_cs_.get());
+  for (std::list<RtpRtcp*>::const_iterator it = simulcast_rtp_rtcp_.begin();
+       it != simulcast_rtp_rtcp_.end();
+       it++) {
+    StreamDataCounters rtp_data;
+    StreamDataCounters rtx_data;
+    (*it)->GetSendStreamDataCounters(&rtp_data, &rtx_data);
+    rtp_counters->Add(rtp_data);
+    rtx_counters->Add(rtx_data);
+  }
+  for (std::list<RtpRtcp*>::const_iterator it = removed_rtp_rtcp_.begin();
+       it != removed_rtp_rtcp_.end(); ++it) {
+    StreamDataCounters rtp_data;
+    StreamDataCounters rtx_data;
+    (*it)->GetSendStreamDataCounters(&rtp_data, &rtx_data);
+    rtp_counters->Add(rtp_data);
+    rtx_counters->Add(rtx_data);
+  }
+}
+
 void ViEChannel::GetReceiveStreamDataCounters(
-    StreamDataCounters* data,
-    StreamDataCounters* rtx_data) const {
+    StreamDataCounters* rtp_counters,
+    StreamDataCounters* rtx_counters) const {
   StreamStatistician* statistician = vie_receiver_.GetReceiveStatistics()->
       GetStatistician(vie_receiver_.GetRemoteSsrc());
   if (statistician) {
-    statistician->GetReceiveStreamDataCounters(data);
+    statistician->GetReceiveStreamDataCounters(rtp_counters);
   }
   uint32_t rtx_ssrc = 0;
   if (vie_receiver_.GetRtxSsrc(&rtx_ssrc)) {
     StreamStatistician* statistician =
         vie_receiver_.GetReceiveStatistics()->GetStatistician(rtx_ssrc);
     if (statistician) {
-      statistician->GetReceiveStreamDataCounters(rtx_data);
+      statistician->GetReceiveStreamDataCounters(rtx_counters);
     }
   }
 }
@@ -1341,10 +1393,12 @@
     rtp_rtcp->SetSendingMediaStatus(true);
     rtp_rtcp->SetSendingStatus(true);
   }
+  start_send_ms_ = Clock::GetRealTimeClock()->TimeInMilliseconds();
   return 0;
 }
 
 int32_t ViEChannel::StopSend() {
+  UpdateHistogramsAtStopSend();
   CriticalSectionScoped cs(rtp_rtcp_cs_.get());
   rtp_rtcp_->SetSendingMediaStatus(false);
   for (std::list<RtpRtcp*>::iterator it = simulcast_rtp_rtcp_.begin();
diff --git a/video_engine/vie_channel.h b/video_engine/vie_channel.h
index ffb754c..88d4a94 100644
--- a/video_engine/vie_channel.h
+++ b/video_engine/vie_channel.h
@@ -194,9 +194,13 @@
                            size_t* bytes_received,
                            uint32_t* packets_received) const;
 
+  // Gets send statistics for the rtp and rtx stream.
+  void GetSendStreamDataCounters(StreamDataCounters* rtp_counters,
+                                 StreamDataCounters* rtx_counters) const;
+
   // Gets received stream data counters.
-  void GetReceiveStreamDataCounters(StreamDataCounters* data,
-                                    StreamDataCounters* rtx_data) const;
+  void GetReceiveStreamDataCounters(StreamDataCounters* rtp_counters,
+                                    StreamDataCounters* rtx_counters) const;
 
   // Called on update of RTP statistics.
   void RegisterSendChannelRtpStatisticsCallback(
@@ -379,6 +383,7 @@
   void SetRtxSendStatus(bool enable);
 
   void UpdateHistograms();
+  void UpdateHistogramsAtStopSend();
 
   // ViEChannel exposes methods that allow to modify observers and callbacks
   // to be modified. Such an API-style is cumbersome to implement and maintain
@@ -498,6 +503,7 @@
   int max_nack_reordering_threshold_;
   I420FrameCallback* pre_render_callback_;
   const int64_t start_ms_;
+  int64_t start_send_ms_;
 
   std::map<uint32_t, RTCPReportBlock> prev_report_blocks_;
 };