Callback for send bitrate estimates

BUG=2235
R=mflodman@webrtc.org, pbos@webrtc.org, stefan@webrtc.org

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

git-svn-id: http://webrtc.googlecode.com/svn/trunk@5259 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/webrtc/common_types.h b/webrtc/common_types.h
index 55ba552..49f3a76 100644
--- a/webrtc/common_types.h
+++ b/webrtc/common_types.h
@@ -285,14 +285,11 @@
 
 // Rate statistics for a stream
 struct BitrateStatistics {
-  BitrateStatistics()
-    : bitrate_(0),
-      packet_rate(0),
-      now(0) {}
+  BitrateStatistics() : bitrate_bps(0), packet_rate(0), timestamp_ms(0) {}
 
-  uint32_t bitrate_;
-  uint32_t packet_rate;
-  uint64_t now;
+  uint32_t bitrate_bps;   // Bitrate in bits per second.
+  uint32_t packet_rate;   // Packet rate in packets per second.
+  uint64_t timestamp_ms;  // Ntp timestamp in ms at time of rate estimation.
 };
 
 // Callback, used to notify an observer whenever new rates have been estimated.
diff --git a/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h b/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h
index 19bc470..67dad0d 100644
--- a/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h
+++ b/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h
@@ -301,6 +301,13 @@
                              uint32_t* nackRate) const = 0;
 
     /*
+    *   Called on any new send bitrate estimate.
+    */
+    virtual void RegisterVideoBitrateObserver(
+        BitrateStatisticsObserver* observer) = 0;
+    virtual BitrateStatisticsObserver* GetVideoBitrateObserver() const = 0;
+
+    /*
     *   Used by the codec module to deliver a video or audio frame for
     *   packetization.
     *
diff --git a/webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h b/webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h
index 8edb548..42c7b4e 100644
--- a/webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h
+++ b/webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h
@@ -100,6 +100,8 @@
       bool());
   MOCK_CONST_METHOD4(BitrateSent,
       void(uint32_t* totalRate, uint32_t* videoRate, uint32_t* fecRate, uint32_t* nackRate));
+  MOCK_METHOD1(RegisterVideoBitrateObserver, void(BitrateStatisticsObserver*));
+  MOCK_CONST_METHOD0(GetVideoBitrateObserver, BitrateStatisticsObserver*(void));
   MOCK_CONST_METHOD1(EstimatedReceiveBandwidth,
       int(uint32_t* available_bandwidth));
   MOCK_METHOD8(SendOutgoingData,
diff --git a/webrtc/modules/rtp_rtcp/source/bitrate.cc b/webrtc/modules/rtp_rtcp/source/bitrate.cc
index d8145d1..42e331b 100644
--- a/webrtc/modules/rtp_rtcp/source/bitrate.cc
+++ b/webrtc/modules/rtp_rtcp/source/bitrate.cc
@@ -15,7 +15,7 @@
 
 namespace webrtc {
 
-Bitrate::Bitrate(Clock* clock)
+Bitrate::Bitrate(Clock* clock, Observer* observer)
     : clock_(clock),
       crit_(CriticalSectionWrapper::CreateCriticalSection()),
       packet_rate_(0),
@@ -23,12 +23,15 @@
       bitrate_next_idx_(0),
       time_last_rate_update_(0),
       bytes_count_(0),
-      packet_count_(0) {
+      packet_count_(0),
+      observer_(observer) {
   memset(packet_rate_array_, 0, sizeof(packet_rate_array_));
   memset(bitrate_diff_ms_, 0, sizeof(bitrate_diff_ms_));
   memset(bitrate_array_, 0, sizeof(bitrate_array_));
 }
 
+Bitrate::~Bitrate() {}
+
 void Bitrate::Update(const int32_t bytes) {
   CriticalSectionScoped cs(crit_.get());
   bytes_count_ += bytes;
@@ -71,7 +74,7 @@
 void Bitrate::Process() {
   // Triggered by timer.
   CriticalSectionScoped cs(crit_.get());
-  int64_t now = clock_->TimeInMilliseconds();
+  int64_t now = clock_->CurrentNtpInMilliseconds();
   int64_t diff_ms = now - time_last_rate_update_;
 
   if (diff_ms < 100) {
@@ -105,6 +108,14 @@
   packet_count_ = 0;
   packet_rate_ = static_cast<uint32_t>(sum_packetrateMS / sum_diffMS);
   bitrate_ = static_cast<uint32_t>(sum_bitrateMS / sum_diffMS);
+
+  if (observer_) {
+    BitrateStatistics stats;
+    stats.bitrate_bps = bitrate_;
+    stats.packet_rate = packet_rate_;
+    stats.timestamp_ms = now;
+    observer_->BitrateUpdated(stats);
+  }
 }
 
 }  // namespace webrtc
diff --git a/webrtc/modules/rtp_rtcp/source/bitrate.h b/webrtc/modules/rtp_rtcp/source/bitrate.h
index 17a099a..36fa1d3 100644
--- a/webrtc/modules/rtp_rtcp/source/bitrate.h
+++ b/webrtc/modules/rtp_rtcp/source/bitrate.h
@@ -27,7 +27,9 @@
 
 class Bitrate {
  public:
-  explicit Bitrate(Clock* clock);
+  class Observer;
+  Bitrate(Clock* clock, Observer* observer);
+  virtual ~Bitrate();
 
   // Calculates rates.
   void Process();
@@ -46,6 +48,14 @@
 
   int64_t time_last_rate_update() const;
 
+  class Observer {
+   public:
+    Observer() {}
+    virtual ~Observer() {}
+
+    virtual void BitrateUpdated(const BitrateStatistics& stats) = 0;
+  };
+
  protected:
   Clock* clock_;
 
@@ -60,6 +70,7 @@
   int64_t time_last_rate_update_;
   uint32_t bytes_count_;
   uint32_t packet_count_;
+  Observer* const observer_;
 };
 
 }  // namespace webrtc
diff --git a/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.cc b/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.cc
index cf18990..3ed44b8 100644
--- a/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.cc
+++ b/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.cc
@@ -27,7 +27,7 @@
 StreamStatisticianImpl::StreamStatisticianImpl(Clock* clock)
     : clock_(clock),
       crit_sect_(CriticalSectionWrapper::CreateCriticalSection()),
-      incoming_bitrate_(clock),
+      incoming_bitrate_(clock, NULL),
       ssrc_(0),
       max_reordering_threshold_(kDefaultMaxReorderingThreshold),
       jitter_q4_(0),
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
index acc2c26..e9deb3d 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
@@ -1559,7 +1559,7 @@
     return;
   }
   if (total_rate != NULL)
-    *total_rate = rtp_sender_.BitrateLast();
+    *total_rate = rtp_sender_.BitrateSent();
   if (video_rate != NULL)
     *video_rate = rtp_sender_.VideoBitrateSent();
   if (fec_rate != NULL)
@@ -1568,6 +1568,31 @@
     *nack_rate = rtp_sender_.NackOverheadRate();
 }
 
+void ModuleRtpRtcpImpl::RegisterVideoBitrateObserver(
+    BitrateStatisticsObserver* observer) {
+  {
+    CriticalSectionScoped cs(critical_section_module_ptrs_.get());
+    if (!child_modules_.empty()) {
+      for (std::list<ModuleRtpRtcpImpl*>::const_iterator it =
+               child_modules_.begin();
+           it != child_modules_.end();
+           ++it) {
+        RtpRtcp* module = *it;
+        if (module)
+          module->RegisterVideoBitrateObserver(observer);
+        ++it;
+      }
+      return;
+    }
+  }
+
+  rtp_sender_.RegisterBitrateObserver(observer);
+}
+
+BitrateStatisticsObserver* ModuleRtpRtcpImpl::GetVideoBitrateObserver() const {
+  return rtp_sender_.GetBitrateObserver();
+}
+
 // Bad state of RTP receiver request a keyframe.
 void ModuleRtpRtcpImpl::OnRequestIntraFrame() {
   RequestKeyFrame();
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h
index 7e0425f..075770d 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h
+++ b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h
@@ -349,6 +349,11 @@
                            uint32_t* fec_rate,
                            uint32_t* nackRate) const OVERRIDE;
 
+  virtual void RegisterVideoBitrateObserver(BitrateStatisticsObserver* observer)
+      OVERRIDE;
+
+  virtual BitrateStatisticsObserver* GetVideoBitrateObserver() const OVERRIDE;
+
   virtual uint32_t SendTimeOfSendReport(const uint32_t send_report);
 
   virtual bool SendTimeOfXrRrReport(uint32_t mid_ntp, int64_t* time_ms) const;
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_sender.cc b/webrtc/modules/rtp_rtcp/source/rtp_sender.cc
index 20ebd44..fd32032 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_sender.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtp_sender.cc
@@ -39,30 +39,56 @@
 
 }  // namespace
 
-RTPSender::RTPSender(const int32_t id, const bool audio, Clock *clock,
-                     Transport *transport, RtpAudioFeedback *audio_feedback,
-                     PacedSender *paced_sender)
-    : Bitrate(clock), id_(id), audio_configured_(audio), audio_(NULL),
-      video_(NULL), paced_sender_(paced_sender),
+RTPSender::RTPSender(const int32_t id,
+                     const bool audio,
+                     Clock* clock,
+                     Transport* transport,
+                     RtpAudioFeedback* audio_feedback,
+                     PacedSender* paced_sender)
+    : clock_(clock),
+      bitrate_sent_(clock, this),
+      id_(id),
+      audio_configured_(audio),
+      audio_(NULL),
+      video_(NULL),
+      paced_sender_(paced_sender),
       send_critsect_(CriticalSectionWrapper::CreateCriticalSection()),
-      transport_(transport), sending_media_(true),  // Default to sending media.
-      max_payload_length_(IP_PACKET_SIZE - 28),     // Default is IP-v4/UDP.
-      target_send_bitrate_(0), packet_over_head_(28), payload_type_(-1),
-      payload_type_map_(), rtp_header_extension_map_(),
-      transmission_time_offset_(0), absolute_send_time_(0),
+      transport_(transport),
+      sending_media_(true),                      // Default to sending media.
+      max_payload_length_(IP_PACKET_SIZE - 28),  // Default is IP-v4/UDP.
+      target_send_bitrate_(0),
+      packet_over_head_(28),
+      payload_type_(-1),
+      payload_type_map_(),
+      rtp_header_extension_map_(),
+      transmission_time_offset_(0),
+      absolute_send_time_(0),
       // NACK.
-      nack_byte_count_times_(), nack_byte_count_(), nack_bitrate_(clock),
+      nack_byte_count_times_(),
+      nack_byte_count_(),
+      nack_bitrate_(clock, NULL),
       packet_history_(clock),
       // Statistics
       statistics_crit_(CriticalSectionWrapper::CreateCriticalSection()),
-      frame_count_observer_(NULL), rtp_stats_callback_(NULL),
+      frame_count_observer_(NULL),
+      rtp_stats_callback_(NULL),
+      bitrate_callback_(NULL),
       // RTP variables
       start_time_stamp_forced_(false),
-      start_time_stamp_(0), ssrc_db_(*SSRCDatabase::GetSSRCDatabase()),
-      remote_ssrc_(0), sequence_number_forced_(false), ssrc_forced_(false),
-      timestamp_(0), capture_time_ms_(0), last_timestamp_time_ms_(0),
-      last_packet_marker_bit_(false), num_csrcs_(0), csrcs_(),
-      include_csrcs_(true), rtx_(kRtxOff), payload_type_rtx_(-1) {
+      start_time_stamp_(0),
+      ssrc_db_(*SSRCDatabase::GetSSRCDatabase()),
+      remote_ssrc_(0),
+      sequence_number_forced_(false),
+      ssrc_forced_(false),
+      timestamp_(0),
+      capture_time_ms_(0),
+      last_timestamp_time_ms_(0),
+      last_packet_marker_bit_(false),
+      num_csrcs_(0),
+      csrcs_(),
+      include_csrcs_(true),
+      rtx_(kRtxOff),
+      payload_type_rtx_(-1) {
   memset(nack_byte_count_times_, 0, sizeof(nack_byte_count_times_));
   memset(nack_byte_count_, 0, sizeof(nack_byte_count_));
   memset(csrcs_, 0, sizeof(csrcs_));
@@ -108,7 +134,7 @@
 }
 
 uint16_t RTPSender::ActualSendBitrateKbit() const {
-  return (uint16_t)(Bitrate::BitrateNow() / 1000);
+  return (uint16_t)(bitrate_sent_.BitrateNow() / 1000);
 }
 
 uint32_t RTPSender::VideoBitrateSent() const {
@@ -445,7 +471,7 @@
     int64_t capture_time_ms) {
   // Current bitrate since last estimate(1 second) averaged with the
   // estimate since then, to get the most up to date bitrate.
-  uint32_t current_bitrate = BitrateNow();
+  uint32_t current_bitrate = bitrate_sent_.BitrateNow();
   int bitrate_diff = target_send_bitrate_ * 1000 - current_bitrate;
   if (bitrate_diff <= 0) {
     return true;
@@ -827,7 +853,7 @@
     ssrc = ssrc_;
   }
 
-  Bitrate::Update(size);
+  bitrate_sent_.Update(size);
   ++counters->packets;
   if (IsFecPacket(buffer, header)) {
     ++counters->fec_packets;
@@ -948,7 +974,7 @@
 
 void RTPSender::ProcessBitrate() {
   CriticalSectionScoped cs(send_critsect_);
-  Bitrate::Process();
+  bitrate_sent_.Process();
   nack_bitrate_.Process();
   if (audio_configured_) {
     return;
@@ -1588,4 +1614,24 @@
   return rtp_stats_callback_;
 }
 
+void RTPSender::RegisterBitrateObserver(BitrateStatisticsObserver* observer) {
+  CriticalSectionScoped cs(statistics_crit_.get());
+  if (observer != NULL)
+    assert(bitrate_callback_ == NULL);
+  bitrate_callback_ = observer;
+}
+
+BitrateStatisticsObserver* RTPSender::GetBitrateObserver() const {
+  CriticalSectionScoped cs(statistics_crit_.get());
+  return bitrate_callback_;
+}
+
+uint32_t RTPSender::BitrateSent() const { return bitrate_sent_.BitrateLast(); }
+
+void RTPSender::BitrateUpdated(const BitrateStatistics& stats) {
+  CriticalSectionScoped cs(statistics_crit_.get());
+  if (bitrate_callback_) {
+    bitrate_callback_->Notify(stats, ssrc_);
+  }
+}
 }  // namespace webrtc
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_sender.h b/webrtc/modules/rtp_rtcp/source/rtp_sender.h
index d0aeb64..e1cc3a18 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_sender.h
+++ b/webrtc/modules/rtp_rtcp/source/rtp_sender.h
@@ -63,7 +63,7 @@
       PacedSender::Priority priority) = 0;
 };
 
-class RTPSender : public Bitrate, public RTPSenderInterface {
+class RTPSender : public RTPSenderInterface, public Bitrate::Observer {
  public:
   RTPSender(const int32_t id, const bool audio, Clock *clock,
             Transport *transport, RtpAudioFeedback *audio_feedback,
@@ -275,6 +275,14 @@
   void RegisterRtpStatisticsCallback(StreamDataCountersCallback* callback);
   StreamDataCountersCallback* GetRtpStatisticsCallback() const;
 
+  // Called on new send bitrate estimate.
+  void RegisterBitrateObserver(BitrateStatisticsObserver* observer);
+  BitrateStatisticsObserver* GetBitrateObserver() const;
+
+  uint32_t BitrateSent() const;
+
+  virtual void BitrateUpdated(const BitrateStatistics& stats) OVERRIDE;
+
  protected:
   int32_t CheckPayloadType(const int8_t payload_type,
                            RtpVideoCodecTypes *video_type);
@@ -318,6 +326,9 @@
                       bool is_retransmit);
   bool IsFecPacket(const uint8_t* buffer, const RTPHeader& header) const;
 
+  Clock* clock_;
+  Bitrate bitrate_sent_;
+
   int32_t id_;
   const bool audio_configured_;
   RTPSenderAudio *audio_;
@@ -355,6 +366,7 @@
   StreamDataCounters rtp_stats_;
   StreamDataCounters rtx_rtp_stats_;
   StreamDataCountersCallback* rtp_stats_callback_;
+  BitrateStatisticsObserver* bitrate_callback_;
 
   // RTP variables
   bool start_time_stamp_forced_;
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_sender_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtp_sender_unittest.cc
index 863d868..ce615be 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_sender_unittest.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtp_sender_unittest.cc
@@ -38,6 +38,7 @@
 const uint8_t kAudioLevel = 0x5a;
 const uint8_t kAudioLevelExtensionId = 9;
 const int kAudioPayload = 103;
+const uint64_t kStartTime = 123456789;
 }  // namespace
 
 using testing::_;
@@ -81,12 +82,12 @@
 class RtpSenderTest : public ::testing::Test {
  protected:
   RtpSenderTest()
-    : fake_clock_(123456789),
-      mock_paced_sender_(),
-      rtp_sender_(),
-      payload_(kPayload),
-      transport_(),
-      kMarkerBit(true) {
+      : fake_clock_(kStartTime),
+        mock_paced_sender_(),
+        rtp_sender_(),
+        payload_(kPayload),
+        transport_(),
+        kMarkerBit(true) {
     EXPECT_CALL(mock_paced_sender_,
         SendPacket(_, _, _, _, _, _)).WillRepeatedly(testing::Return(true));
   }
@@ -779,6 +780,73 @@
   rtp_sender_->RegisterFrameCountObserver(NULL);
 }
 
+TEST_F(RtpSenderTest, BitrateCallbacks) {
+  class TestCallback : public BitrateStatisticsObserver {
+   public:
+    TestCallback()
+        : BitrateStatisticsObserver(), num_calls_(0), ssrc_(0), bitrate_() {}
+    virtual ~TestCallback() {}
+
+    virtual void Notify(const BitrateStatistics& stats, uint32_t ssrc) {
+      ++num_calls_;
+      ssrc_ = ssrc;
+      bitrate_ = stats;
+    }
+
+    uint32_t num_calls_;
+    uint32_t ssrc_;
+    BitrateStatistics bitrate_;
+  } callback;
+
+  // Simulate kNumPackets sent with kPacketInterval ms intervals.
+  const uint32_t kNumPackets = 15;
+  const uint32_t kPacketInterval = 20;
+  // Overhead = 12 bytes RTP header + 1 byte generic header.
+  const uint32_t kPacketOverhead = 13;
+
+  char payload_name[RTP_PAYLOAD_NAME_SIZE] = "GENERIC";
+  const uint8_t payload_type = 127;
+  ASSERT_EQ(
+      0,
+      rtp_sender_->RegisterPayload(payload_name, payload_type, 90000, 0, 1500));
+  uint8_t payload[] = {47, 11, 32, 93, 89};
+  rtp_sender_->SetStorePacketsStatus(true, 1);
+  uint32_t ssrc = rtp_sender_->SSRC();
+
+  rtp_sender_->RegisterBitrateObserver(&callback);
+
+  // Initial process call so we get a new time window.
+  rtp_sender_->ProcessBitrate();
+  uint64_t start_time = fake_clock_.CurrentNtpInMilliseconds();
+
+  // Send a few frames.
+  for (uint32_t i = 0; i < kNumPackets; ++i) {
+    ASSERT_EQ(0,
+              rtp_sender_->SendOutgoingData(kVideoFrameKey,
+                                            payload_type,
+                                            1234,
+                                            4321,
+                                            payload,
+                                            sizeof(payload),
+                                            0));
+    fake_clock_.AdvanceTimeMilliseconds(kPacketInterval);
+  }
+
+  rtp_sender_->ProcessBitrate();
+
+  const uint32_t expected_packet_rate = 1000 / kPacketInterval;
+
+  EXPECT_EQ(1U, callback.num_calls_);
+  EXPECT_EQ(ssrc, callback.ssrc_);
+  EXPECT_EQ(start_time + (kNumPackets * kPacketInterval),
+            callback.bitrate_.timestamp_ms);
+  EXPECT_EQ(expected_packet_rate, callback.bitrate_.packet_rate);
+  EXPECT_EQ((kPacketOverhead + sizeof(payload)) * 8 * expected_packet_rate,
+            callback.bitrate_.bitrate_bps);
+
+  rtp_sender_->RegisterBitrateObserver(NULL);
+}
+
 class RtpSenderAudioTest : public RtpSenderTest {
  protected:
   RtpSenderAudioTest() {}
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_sender_video.cc b/webrtc/modules/rtp_rtcp/source/rtp_sender_video.cc
index 4d0da73..7b36f7c 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_sender_video.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtp_sender_video.cc
@@ -32,27 +32,26 @@
 
 RTPSenderVideo::RTPSenderVideo(const int32_t id,
                                Clock* clock,
-                               RTPSenderInterface* rtpSender) :
-    _id(id),
-    _rtpSender(*rtpSender),
-    _sendVideoCritsect(CriticalSectionWrapper::CreateCriticalSection()),
+                               RTPSenderInterface* rtpSender)
+    : _id(id),
+      _rtpSender(*rtpSender),
+      _sendVideoCritsect(CriticalSectionWrapper::CreateCriticalSection()),
+      _videoType(kRtpVideoGeneric),
+      _videoCodecInformation(NULL),
+      _maxBitrate(0),
+      _retransmissionSettings(kRetransmitBaseLayer),
 
-    _videoType(kRtpVideoGeneric),
-    _videoCodecInformation(NULL),
-    _maxBitrate(0),
-    _retransmissionSettings(kRetransmitBaseLayer),
-
-    // Generic FEC
-    _fec(id),
-    _fecEnabled(false),
-    _payloadTypeRED(-1),
-    _payloadTypeFEC(-1),
-    _numberFirstPartition(0),
-    delta_fec_params_(),
-    key_fec_params_(),
-    producer_fec_(&_fec),
-    _fecOverheadRate(clock),
-    _videoBitrate(clock) {
+      // Generic FEC
+      _fec(id),
+      _fecEnabled(false),
+      _payloadTypeRED(-1),
+      _payloadTypeFEC(-1),
+      _numberFirstPartition(0),
+      delta_fec_params_(),
+      key_fec_params_(),
+      producer_fec_(&_fec),
+      _fecOverheadRate(clock, NULL),
+      _videoBitrate(clock, NULL) {
   memset(&delta_fec_params_, 0, sizeof(delta_fec_params_));
   memset(&key_fec_params_, 0, sizeof(key_fec_params_));
   delta_fec_params_.max_fec_frames = key_fec_params_.max_fec_frames = 1;
diff --git a/webrtc/video_engine/vie_channel.cc b/webrtc/video_engine/vie_channel.cc
index b6d9be1..9d93b3e 100644
--- a/webrtc/video_engine/vie_channel.cc
+++ b/webrtc/video_engine/vie_channel.cc
@@ -359,6 +359,7 @@
       rtp_rtcp->RegisterSendFrameCountObserver(NULL);
       rtp_rtcp->RegisterSendChannelRtcpStatisticsCallback(NULL);
       rtp_rtcp->RegisterSendChannelRtpStatisticsCallback(NULL);
+      rtp_rtcp->RegisterVideoBitrateObserver(NULL);
       simulcast_rtp_rtcp_.pop_back();
       removed_rtp_rtcp_.push_front(rtp_rtcp);
     }
@@ -419,6 +420,8 @@
           rtp_rtcp_->GetSendChannelRtcpStatisticsCallback());
       rtp_rtcp->RegisterSendChannelRtpStatisticsCallback(
           rtp_rtcp_->GetSendChannelRtpStatisticsCallback());
+      rtp_rtcp->RegisterVideoBitrateObserver(
+          rtp_rtcp_->GetVideoBitrateObserver());
     }
     // |RegisterSimulcastRtpRtcpModules| resets all old weak pointers and old
     // modules can be deleted after this step.
@@ -432,6 +435,7 @@
       rtp_rtcp->RegisterSendFrameCountObserver(NULL);
       rtp_rtcp->RegisterSendChannelRtcpStatisticsCallback(NULL);
       rtp_rtcp->RegisterSendChannelRtpStatisticsCallback(NULL);
+      rtp_rtcp->RegisterVideoBitrateObserver(NULL);
       simulcast_rtp_rtcp_.pop_back();
       removed_rtp_rtcp_.push_front(rtp_rtcp);
     }
@@ -1427,6 +1431,17 @@
   return valid_estimate;
 }
 
+void ViEChannel::RegisterSendBitrateObserver(
+    BitrateStatisticsObserver* observer) {
+  rtp_rtcp_->RegisterVideoBitrateObserver(observer);
+  CriticalSectionScoped cs(rtp_rtcp_cs_.get());
+  for (std::list<RtpRtcp*>::const_iterator it = simulcast_rtp_rtcp_.begin();
+       it != simulcast_rtp_rtcp_.end();
+       it++) {
+    (*it)->RegisterVideoBitrateObserver(observer);
+  }
+}
+
 void ViEChannel::GetEstimatedReceiveBandwidth(
     uint32_t* estimated_bandwidth) const {
   vie_receiver_.EstimatedReceiveBandwidth(estimated_bandwidth);
diff --git a/webrtc/video_engine/vie_channel.h b/webrtc/video_engine/vie_channel.h
index 0ee677e..a4b3dd0 100644
--- a/webrtc/video_engine/vie_channel.h
+++ b/webrtc/video_engine/vie_channel.h
@@ -202,6 +202,9 @@
   bool GetSendSideDelay(int* avg_send_delay, int* max_send_delay) const;
   void GetEstimatedReceiveBandwidth(uint32_t* estimated_bandwidth) const;
 
+  // Called on any new send bitrate estimate.
+  void RegisterSendBitrateObserver(BitrateStatisticsObserver* observer);
+
   int32_t StartRTPDump(const char file_nameUTF8[1024],
                        RTPDirections direction);
   int32_t StopRTPDump(RTPDirections direction);
diff --git a/webrtc/video_engine/vie_rtp_rtcp_impl.cc b/webrtc/video_engine/vie_rtp_rtcp_impl.cc
index ce2f434..e07ab6c 100644
--- a/webrtc/video_engine/vie_rtp_rtcp_impl.cc
+++ b/webrtc/video_engine/vie_rtp_rtcp_impl.cc
@@ -1206,16 +1206,38 @@
   // TODO(sprang): Implement
   return -1;
 }
+
+// Called whenever the send bitrate is updated.
 int ViERTP_RTCPImpl::RegisterSendBitrateObserver(
-    int channel, BitrateStatisticsObserver* callback) {
-  // TODO(sprang): Implement
-  return -1;
+    const int video_channel,
+    BitrateStatisticsObserver* observer) {
+  WEBRTC_TRACE(kTraceApiCall,
+               kTraceVideo,
+               ViEId(shared_data_->instance_id(), video_channel),
+               "%s(channel: %d)",
+               __FUNCTION__,
+               video_channel);
+  ViEChannelManagerScoped cs(*(shared_data_->channel_manager()));
+  ViEChannel* vie_channel = cs.Channel(video_channel);
+  assert(vie_channel != NULL);
+  vie_channel->RegisterSendBitrateObserver(observer);
+  return 0;
 }
 
 int ViERTP_RTCPImpl::DeregisterSendBitrateObserver(
-    int channel, BitrateStatisticsObserver* callback) {
-  // TODO(sprang): Implement
-  return -1;
+    const int video_channel,
+    BitrateStatisticsObserver* observer) {
+  WEBRTC_TRACE(kTraceApiCall,
+               kTraceVideo,
+               ViEId(shared_data_->instance_id(), video_channel),
+               "%s(channel: %d)",
+               __FUNCTION__,
+               video_channel);
+  ViEChannelManagerScoped cs(*(shared_data_->channel_manager()));
+  ViEChannel* vie_channel = cs.Channel(video_channel);
+  assert(vie_channel != NULL);
+  vie_channel->RegisterSendBitrateObserver(NULL);
+  return 0;
 }
 
 int ViERTP_RTCPImpl::RegisterSendFrameCountObserver(