Update stable to r5195.

git-svn-id: http://webrtc.googlecode.com/svn/stable/webrtc@5196 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/build/common.gypi b/build/common.gypi
index 73608a4..0c47315 100644
--- a/build/common.gypi
+++ b/build/common.gypi
@@ -183,6 +183,7 @@
               '-Wno-missing-field-initializers',
             ],
             'cflags_cc': [
+              '-Wnon-virtual-dtor',
               # This is enabled for clang; enable for gcc as well.
               '-Woverloaded-virtual',
             ],
diff --git a/call.cc b/call.cc
index 45f06eb..9e23b24 100644
--- a/call.cc
+++ b/call.cc
@@ -38,18 +38,18 @@
 
   virtual VideoSendStream::Config GetDefaultSendConfig() OVERRIDE;
 
-  virtual VideoSendStream* CreateSendStream(
+  virtual VideoSendStream* CreateVideoSendStream(
       const VideoSendStream::Config& config) OVERRIDE;
 
-  virtual SendStreamState* DestroySendStream(
-      webrtc::VideoSendStream* send_stream) OVERRIDE;
+  virtual void DestroyVideoSendStream(webrtc::VideoSendStream* send_stream)
+      OVERRIDE;
 
   virtual VideoReceiveStream::Config GetDefaultReceiveConfig() OVERRIDE;
 
-  virtual VideoReceiveStream* CreateReceiveStream(
+  virtual VideoReceiveStream* CreateVideoReceiveStream(
       const VideoReceiveStream::Config& config) OVERRIDE;
 
-  virtual void DestroyReceiveStream(
+  virtual void DestroyVideoReceiveStream(
       webrtc::VideoReceiveStream* receive_stream) OVERRIDE;
 
   virtual uint32_t SendBitrateEstimate() OVERRIDE;
@@ -212,10 +212,10 @@
   return config;
 }
 
-VideoSendStream* Call::CreateSendStream(const VideoSendStream::Config& config) {
+VideoSendStream* Call::CreateVideoSendStream(
+    const VideoSendStream::Config& config) {
   assert(config.rtp.ssrcs.size() > 0);
-  assert(config.codec.numberOfSimulcastStreams == 0 ||
-         config.codec.numberOfSimulcastStreams == config.rtp.ssrcs.size());
+  assert(config.rtp.ssrcs.size() >= config.codec.numberOfSimulcastStreams);
 
   VideoSendStream* send_stream = new VideoSendStream(
       config_.send_transport, config_.overuse_detection, video_engine_, config);
@@ -228,7 +228,7 @@
   return send_stream;
 }
 
-SendStreamState* Call::DestroySendStream(webrtc::VideoSendStream* send_stream) {
+void Call::DestroyVideoSendStream(webrtc::VideoSendStream* send_stream) {
   assert(send_stream != NULL);
 
   VideoSendStream* send_stream_impl = NULL;
@@ -248,19 +248,16 @@
 
   assert(send_stream_impl != NULL);
   delete send_stream_impl;
-
-  // TODO(pbos): Return its previous state
-  return NULL;
 }
 
 VideoReceiveStream::Config Call::GetDefaultReceiveConfig() {
   return VideoReceiveStream::Config();
 }
 
-VideoReceiveStream* Call::CreateReceiveStream(
+VideoReceiveStream* Call::CreateVideoReceiveStream(
     const VideoReceiveStream::Config& config) {
-  VideoReceiveStream* receive_stream =
-      new VideoReceiveStream(video_engine_, config, config_.send_transport);
+  VideoReceiveStream* receive_stream = new VideoReceiveStream(
+      video_engine_, config, config_.send_transport, config_.voice_engine);
 
   WriteLockScoped write_lock(*receive_lock_);
   assert(receive_ssrcs_.find(config.rtp.ssrc) == receive_ssrcs_.end());
@@ -268,7 +265,8 @@
   return receive_stream;
 }
 
-void Call::DestroyReceiveStream(webrtc::VideoReceiveStream* receive_stream) {
+void Call::DestroyVideoReceiveStream(
+    webrtc::VideoReceiveStream* receive_stream) {
   assert(receive_stream != NULL);
 
   VideoReceiveStream* receive_stream_impl = NULL;
diff --git a/call.h b/call.h
index 5239f5a..186672d 100644
--- a/call.h
+++ b/call.h
@@ -60,19 +60,20 @@
 
   virtual VideoSendStream::Config GetDefaultSendConfig() = 0;
 
-  virtual VideoSendStream* CreateSendStream(
+  virtual VideoSendStream* CreateVideoSendStream(
       const VideoSendStream::Config& config) = 0;
 
   // Returns the internal state of the send stream, for resume sending with a
   // new stream with different settings.
   // Note: Only the last returned send-stream state is valid.
-  virtual SendStreamState* DestroySendStream(VideoSendStream* send_stream) = 0;
+  virtual void DestroyVideoSendStream(VideoSendStream* send_stream) = 0;
 
   virtual VideoReceiveStream::Config GetDefaultReceiveConfig() = 0;
 
-  virtual VideoReceiveStream* CreateReceiveStream(
+  virtual VideoReceiveStream* CreateVideoReceiveStream(
       const VideoReceiveStream::Config& config) = 0;
-  virtual void DestroyReceiveStream(VideoReceiveStream* receive_stream) = 0;
+  virtual void DestroyVideoReceiveStream(
+      VideoReceiveStream* receive_stream) = 0;
 
   // All received RTP and RTCP packets for the call should be inserted to this
   // PacketReceiver. The PacketReceiver pointer is valid as long as the
diff --git a/call_tests.cc b/call_tests.cc
index 6a8f34f..5393286 100644
--- a/call_tests.cc
+++ b/call_tests.cc
@@ -9,28 +9,43 @@
  */
 #include <assert.h>
 
+#include <algorithm>
 #include <map>
+#include <sstream>
+#include <string>
 
 #include "testing/gtest/include/gtest/gtest.h"
 
 #include "webrtc/call.h"
 #include "webrtc/common_video/test/frame_generator.h"
+#include "webrtc/frame_callback.h"
+#include "webrtc/modules/remote_bitrate_estimator/include/rtp_to_ntp.h"
 #include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h"
 #include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h"
 #include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
 #include "webrtc/system_wrappers/interface/event_wrapper.h"
 #include "webrtc/system_wrappers/interface/scoped_ptr.h"
+#include "webrtc/video/transport_adapter.h"
+#include "webrtc/voice_engine/include/voe_base.h"
+#include "webrtc/voice_engine/include/voe_codec.h"
+#include "webrtc/voice_engine/include/voe_network.h"
+#include "webrtc/voice_engine/include/voe_rtp_rtcp.h"
+#include "webrtc/voice_engine/include/voe_video_sync.h"
+#include "webrtc/voice_engine/test/auto_test/resource_manager.h"
 #include "webrtc/test/direct_transport.h"
+#include "webrtc/test/fake_audio_device.h"
 #include "webrtc/test/fake_decoder.h"
 #include "webrtc/test/fake_encoder.h"
 #include "webrtc/test/frame_generator_capturer.h"
 #include "webrtc/test/generate_ssrcs.h"
 #include "webrtc/test/rtp_rtcp_observer.h"
+#include "webrtc/test/testsupport/perf_test.h"
 
 namespace webrtc {
 
 static unsigned int kDefaultTimeoutMs = 30 * 1000;
 static unsigned int kLongTimeoutMs = 120 * 1000;
+static const uint8_t kSendPayloadType = 125;
 
 class CallTest : public ::testing::Test {
  public:
@@ -59,6 +74,7 @@
     send_config_.encoder = &fake_encoder_;
     send_config_.internal_source = false;
     test::FakeEncoder::SetCodecSettings(&send_config_.codec, 1);
+    send_config_.codec.plType = kSendPayloadType;
 
     receive_config_.codecs.clear();
     receive_config_.codecs.push_back(send_config_.codec);
@@ -73,8 +89,8 @@
     assert(send_stream_ == NULL);
     assert(receive_stream_ == NULL);
 
-    send_stream_ = sender_call_->CreateSendStream(send_config_);
-    receive_stream_ = receiver_call_->CreateReceiveStream(receive_config_);
+    send_stream_ = sender_call_->CreateVideoSendStream(send_config_);
+    receive_stream_ = receiver_call_->CreateVideoReceiveStream(receive_config_);
   }
 
   void CreateFrameGenerator() {
@@ -87,8 +103,8 @@
   }
 
   void StartSending() {
-    receive_stream_->StartReceive();
-    send_stream_->StartSend();
+    receive_stream_->StartReceiving();
+    send_stream_->StartSending();
     if (frame_generator_capturer_.get() != NULL)
       frame_generator_capturer_->Start();
   }
@@ -97,22 +113,23 @@
     if (frame_generator_capturer_.get() != NULL)
       frame_generator_capturer_->Stop();
     if (send_stream_ != NULL)
-      send_stream_->StopSend();
+      send_stream_->StopSending();
     if (receive_stream_ != NULL)
-      receive_stream_->StopReceive();
+      receive_stream_->StopReceiving();
   }
 
   void DestroyStreams() {
     if (send_stream_ != NULL)
-      sender_call_->DestroySendStream(send_stream_);
+      sender_call_->DestroyVideoSendStream(send_stream_);
     if (receive_stream_ != NULL)
-      receiver_call_->DestroyReceiveStream(receive_stream_);
+      receiver_call_->DestroyVideoReceiveStream(receive_stream_);
     send_stream_ = NULL;
     receive_stream_ = NULL;
   }
 
   void ReceivesPliAndRecovers(int rtp_history_ms);
   void RespectsRtcpMode(newapi::RtcpMode rtcp_mode);
+  void PlaysOutAudioAndVideoInSync();
 
   scoped_ptr<Call> sender_call_;
   scoped_ptr<Call> receiver_call_;
@@ -607,7 +624,7 @@
   CreateFrameGenerator();
   StartSending();
 
-  receiver_call_->DestroyReceiveStream(receive_stream_);
+  receiver_call_->DestroyVideoReceiveStream(receive_stream_);
   receive_stream_ = NULL;
 
   // Wait() waits for a received packet.
@@ -771,15 +788,16 @@
         receiver_call->GetDefaultReceiveConfig();
     receive_config.renderer = observers[i];
     receive_config.rtp.ssrc = ssrc;
-    receive_streams[i] = receiver_call->CreateReceiveStream(receive_config);
-    receive_streams[i]->StartReceive();
+    receive_streams[i] =
+        receiver_call->CreateVideoReceiveStream(receive_config);
+    receive_streams[i]->StartReceiving();
 
     VideoSendStream::Config send_config = sender_call->GetDefaultSendConfig();
     send_config.rtp.ssrcs.push_back(ssrc);
     send_config.codec.width = width;
     send_config.codec.height = height;
-    send_streams[i] = sender_call->CreateSendStream(send_config);
-    send_streams[i]->StartSend();
+    send_streams[i] = sender_call->CreateVideoSendStream(send_config);
+    send_streams[i]->StartSending();
 
     frame_generators[i] = test::FrameGeneratorCapturer::Create(
         send_streams[i]->Input(), width, height, 30, Clock::GetRealTimeClock());
@@ -793,12 +811,307 @@
   for (size_t i = 0; i < kNumStreams; ++i) {
     frame_generators[i]->Stop();
     delete frame_generators[i];
-    sender_call->DestroySendStream(send_streams[i]);
-    receiver_call->DestroyReceiveStream(receive_streams[i]);
+    sender_call->DestroyVideoSendStream(send_streams[i]);
+    receiver_call->DestroyVideoReceiveStream(receive_streams[i]);
     delete observers[i];
   }
 
   sender_transport.StopSending();
   receiver_transport.StopSending();
 }
+
+class SyncRtcpObserver : public test::RtpRtcpObserver {
+ public:
+  SyncRtcpObserver(int delay_ms)
+      : test::RtpRtcpObserver(kLongTimeoutMs, delay_ms),
+        critical_section_(CriticalSectionWrapper::CreateCriticalSection()) {}
+
+  virtual Action OnSendRtcp(const uint8_t* packet, size_t length) OVERRIDE {
+    RTCPUtility::RTCPParserV2 parser(packet, length, true);
+    EXPECT_TRUE(parser.IsValid());
+
+    for (RTCPUtility::RTCPPacketTypes packet_type = parser.Begin();
+         packet_type != RTCPUtility::kRtcpNotValidCode;
+         packet_type = parser.Iterate()) {
+      if (packet_type == RTCPUtility::kRtcpSrCode) {
+        const RTCPUtility::RTCPPacket& packet = parser.Packet();
+        synchronization::RtcpMeasurement ntp_rtp_pair(
+            packet.SR.NTPMostSignificant,
+            packet.SR.NTPLeastSignificant,
+            packet.SR.RTPTimestamp);
+        StoreNtpRtpPair(ntp_rtp_pair);
+      }
+    }
+    return SEND_PACKET;
+  }
+
+  int64_t RtpTimestampToNtp(uint32_t timestamp) const {
+    CriticalSectionScoped cs(critical_section_.get());
+    int64_t timestamp_in_ms = -1;
+    if (ntp_rtp_pairs_.size() == 2) {
+      // TODO(stefan): We can't EXPECT_TRUE on this call due to a bug in the
+      // RTCP sender where it sends RTCP SR before any RTP packets, which leads
+      // to a bogus NTP/RTP mapping.
+      synchronization::RtpToNtpMs(timestamp, ntp_rtp_pairs_, &timestamp_in_ms);
+      return timestamp_in_ms;
+    }
+    return -1;
+  }
+
+ private:
+  void StoreNtpRtpPair(synchronization::RtcpMeasurement ntp_rtp_pair) {
+    CriticalSectionScoped cs(critical_section_.get());
+    for (synchronization::RtcpList::iterator it = ntp_rtp_pairs_.begin();
+         it != ntp_rtp_pairs_.end();
+         ++it) {
+      if (ntp_rtp_pair.ntp_secs == it->ntp_secs &&
+          ntp_rtp_pair.ntp_frac == it->ntp_frac) {
+        // This RTCP has already been added to the list.
+        return;
+      }
+    }
+    // We need two RTCP SR reports to map between RTP and NTP. More than two
+    // will not improve the mapping.
+    if (ntp_rtp_pairs_.size() == 2) {
+      ntp_rtp_pairs_.pop_back();
+    }
+    ntp_rtp_pairs_.push_front(ntp_rtp_pair);
+  }
+
+  scoped_ptr<CriticalSectionWrapper> critical_section_;
+  synchronization::RtcpList ntp_rtp_pairs_;
+};
+
+class VideoRtcpAndSyncObserver : public SyncRtcpObserver, public VideoRenderer {
+  static const int kInSyncThresholdMs = 50;
+  static const int kStartupTimeMs = 2000;
+  static const int kMinRunTimeMs = 30000;
+
+ public:
+  VideoRtcpAndSyncObserver(Clock* clock,
+                           int voe_channel,
+                           VoEVideoSync* voe_sync,
+                           SyncRtcpObserver* audio_observer)
+      : SyncRtcpObserver(0),
+        clock_(clock),
+        voe_channel_(voe_channel),
+        voe_sync_(voe_sync),
+        audio_observer_(audio_observer),
+        creation_time_ms_(clock_->TimeInMilliseconds()),
+        first_time_in_sync_(-1) {}
+
+  virtual void RenderFrame(const I420VideoFrame& video_frame,
+                           int time_to_render_ms) OVERRIDE {
+    int64_t now_ms = clock_->TimeInMilliseconds();
+    uint32_t playout_timestamp = 0;
+    if (voe_sync_->GetPlayoutTimestamp(voe_channel_, playout_timestamp) != 0)
+      return;
+    int64_t latest_audio_ntp =
+        audio_observer_->RtpTimestampToNtp(playout_timestamp);
+    int64_t latest_video_ntp = RtpTimestampToNtp(video_frame.timestamp());
+    if (latest_audio_ntp < 0 || latest_video_ntp < 0)
+      return;
+    int time_until_render_ms =
+        std::max(0, static_cast<int>(video_frame.render_time_ms() - now_ms));
+    latest_video_ntp += time_until_render_ms;
+    int64_t stream_offset = latest_audio_ntp - latest_video_ntp;
+    std::stringstream ss;
+    ss << stream_offset;
+    webrtc::test::PrintResult(
+        "stream_offset", "", "synchronization", ss.str(), "ms", false);
+    int64_t time_since_creation = now_ms - creation_time_ms_;
+    // During the first couple of seconds audio and video can falsely be
+    // estimated as being synchronized. We don't want to trigger on those.
+    if (time_since_creation < kStartupTimeMs)
+      return;
+    if (abs(latest_audio_ntp - latest_video_ntp) < kInSyncThresholdMs) {
+      if (first_time_in_sync_ == -1) {
+        first_time_in_sync_ = now_ms;
+        webrtc::test::PrintResult("sync_convergence_time",
+                                  "",
+                                  "synchronization",
+                                  time_since_creation,
+                                  "ms",
+                                  false);
+      }
+      if (time_since_creation > kMinRunTimeMs)
+        observation_complete_->Set();
+    }
+  }
+
+ private:
+  Clock* clock_;
+  int voe_channel_;
+  VoEVideoSync* voe_sync_;
+  SyncRtcpObserver* audio_observer_;
+  int64_t creation_time_ms_;
+  int64_t first_time_in_sync_;
+};
+
+TEST_F(CallTest, PlaysOutAudioAndVideoInSync) {
+  VoiceEngine* voice_engine = VoiceEngine::Create();
+  VoEBase* voe_base = VoEBase::GetInterface(voice_engine);
+  VoECodec* voe_codec = VoECodec::GetInterface(voice_engine);
+  VoENetwork* voe_network = VoENetwork::GetInterface(voice_engine);
+  VoEVideoSync* voe_sync = VoEVideoSync::GetInterface(voice_engine);
+  ResourceManager resource_manager;
+  const std::string audio_filename = resource_manager.long_audio_file_path();
+  ASSERT_STRNE("", audio_filename.c_str());
+  test::FakeAudioDevice fake_audio_device(Clock::GetRealTimeClock(),
+                                          audio_filename);
+  EXPECT_EQ(0, voe_base->Init(&fake_audio_device, NULL));
+  int channel = voe_base->CreateChannel();
+
+  const int kVoiceDelayMs = 500;
+  SyncRtcpObserver audio_observer(kVoiceDelayMs);
+  VideoRtcpAndSyncObserver observer(
+      Clock::GetRealTimeClock(), channel, voe_sync, &audio_observer);
+
+  Call::Config receiver_config(observer.ReceiveTransport());
+  receiver_config.voice_engine = voice_engine;
+  CreateCalls(Call::Config(observer.SendTransport()), receiver_config);
+  CodecInst isac = {103, "ISAC", 16000, 480, 1, 32000};
+  EXPECT_EQ(0, voe_codec->SetSendCodec(channel, isac));
+
+  class VoicePacketReceiver : public PacketReceiver {
+   public:
+    VoicePacketReceiver(int channel, VoENetwork* voe_network)
+        : channel_(channel),
+          voe_network_(voe_network),
+          parser_(RtpHeaderParser::Create()) {}
+    virtual bool DeliverPacket(const uint8_t* packet, size_t length) {
+      int ret;
+      if (parser_->IsRtcp(packet, static_cast<int>(length))) {
+        ret = voe_network_->ReceivedRTCPPacket(
+            channel_, packet, static_cast<unsigned int>(length));
+      } else {
+        ret = voe_network_->ReceivedRTPPacket(
+            channel_, packet, static_cast<unsigned int>(length));
+      }
+      return ret == 0;
+    }
+
+   private:
+    int channel_;
+    VoENetwork* voe_network_;
+    scoped_ptr<RtpHeaderParser> parser_;
+  } voe_packet_receiver(channel, voe_network);
+
+  audio_observer.SetReceivers(&voe_packet_receiver, &voe_packet_receiver);
+
+  internal::TransportAdapter transport_adapter(audio_observer.SendTransport());
+  EXPECT_EQ(0,
+            voe_network->RegisterExternalTransport(channel, transport_adapter));
+
+  observer.SetReceivers(receiver_call_->Receiver(), sender_call_->Receiver());
+
+  CreateTestConfigs();
+  send_config_.rtp.nack.rtp_history_ms = 1000;
+  receive_config_.rtp.nack.rtp_history_ms = 1000;
+  receive_config_.renderer = &observer;
+  receive_config_.audio_channel_id = channel;
+
+  CreateStreams();
+  CreateFrameGenerator();
+  StartSending();
+
+  fake_audio_device.Start();
+  EXPECT_EQ(0, voe_base->StartPlayout(channel));
+  EXPECT_EQ(0, voe_base->StartReceive(channel));
+  EXPECT_EQ(0, voe_base->StartSend(channel));
+
+  EXPECT_EQ(kEventSignaled, observer.Wait())
+      << "Timed out while waiting for audio and video to be synchronized.";
+
+  EXPECT_EQ(0, voe_base->StopSend(channel));
+  EXPECT_EQ(0, voe_base->StopReceive(channel));
+  EXPECT_EQ(0, voe_base->StopPlayout(channel));
+  fake_audio_device.Stop();
+
+  StopSending();
+  observer.StopSending();
+  audio_observer.StopSending();
+
+  voe_base->DeleteChannel(channel);
+  voe_base->Release();
+  voe_codec->Release();
+  voe_network->Release();
+  voe_sync->Release();
+  DestroyStreams();
+  VoiceEngine::Delete(voice_engine);
+}
+
+TEST_F(CallTest, ObserversEncodedFrames) {
+  class EncodedFrameTestObserver : public EncodedFrameObserver {
+   public:
+    EncodedFrameTestObserver() : length_(0),
+                                 frame_type_(kFrameEmpty),
+                                 called_(EventWrapper::Create()) {}
+    virtual ~EncodedFrameTestObserver() {}
+
+    virtual void EncodedFrameCallback(const EncodedFrame& encoded_frame) {
+      frame_type_ = encoded_frame.frame_type_;
+      length_ = encoded_frame.length_;
+      buffer_.reset(new uint8_t[length_]);
+      memcpy(buffer_.get(), encoded_frame.data_, length_);
+      called_->Set();
+    }
+
+    EventTypeWrapper Wait() {
+      return called_->Wait(kDefaultTimeoutMs);
+    }
+
+    void ExpectEqualFrames(const EncodedFrameTestObserver& observer) {
+      ASSERT_EQ(length_, observer.length_)
+          << "Observed frames are of different lengths.";
+      EXPECT_EQ(frame_type_, observer.frame_type_)
+          << "Observed frames have different frame types.";
+      EXPECT_EQ(0, memcmp(buffer_.get(), observer.buffer_.get(), length_))
+          << "Observed encoded frames have different content.";
+    }
+
+   private:
+    scoped_ptr<uint8_t[]> buffer_;
+    size_t length_;
+    FrameType frame_type_;
+    scoped_ptr<EventWrapper> called_;
+  };
+
+  EncodedFrameTestObserver post_encode_observer;
+  EncodedFrameTestObserver pre_decode_observer;
+
+  test::DirectTransport sender_transport, receiver_transport;
+
+  CreateCalls(Call::Config(&sender_transport),
+              Call::Config(&receiver_transport));
+
+  sender_transport.SetReceiver(receiver_call_->Receiver());
+  receiver_transport.SetReceiver(sender_call_->Receiver());
+
+  CreateTestConfigs();
+  send_config_.post_encode_callback = &post_encode_observer;
+  receive_config_.pre_decode_callback = &pre_decode_observer;
+
+  CreateStreams();
+  StartSending();
+
+  scoped_ptr<test::FrameGenerator> frame_generator(test::FrameGenerator::Create(
+      send_config_.codec.width, send_config_.codec.height));
+  send_stream_->Input()->PutFrame(frame_generator->NextFrame(), 0);
+
+  EXPECT_EQ(kEventSignaled, post_encode_observer.Wait())
+      << "Timed out while waiting for send-side encoded-frame callback.";
+
+  EXPECT_EQ(kEventSignaled, pre_decode_observer.Wait())
+      << "Timed out while waiting for pre-decode encoded-frame callback.";
+
+  post_encode_observer.ExpectEqualFrames(pre_decode_observer);
+
+  StopSending();
+
+  sender_transport.StopSending();
+  receiver_transport.StopSending();
+
+  DestroyStreams();
+}
 }  // namespace webrtc
diff --git a/common_types.h b/common_types.h
index b736a2f..7138b05 100644
--- a/common_types.h
+++ b/common_types.h
@@ -225,6 +225,7 @@
     Transport() {}
 };
 
+// Statistics for an RTCP channel
 struct RtcpStatistics {
  public:
   RtcpStatistics()
@@ -241,6 +242,71 @@
   uint32_t max_jitter;
 };
 
+// Callback, called whenever a new rtcp report block is transmitted.
+class RtcpStatisticsCallback {
+ public:
+  virtual ~RtcpStatisticsCallback() {}
+
+  virtual void StatisticsUpdated(const RtcpStatistics& statistics,
+                                 uint32_t ssrc) = 0;
+};
+
+// Data usage statistics for a (rtp) stream
+struct StreamDataCounters {
+ public:
+  StreamDataCounters()
+   : bytes(0),
+     padding_bytes(0),
+     packets(0),
+     retransmitted_packets(0),
+     fec_packets(0) {}
+
+  uint32_t bytes;
+  uint32_t padding_bytes;
+  uint32_t packets;
+  uint32_t retransmitted_packets;
+  uint32_t fec_packets;
+};
+
+// Callback, called whenever byte/packet counts have been updated.
+class StreamDataCountersCallback {
+ public:
+  virtual ~StreamDataCountersCallback() {}
+
+  virtual void DataCountersUpdated(const StreamDataCounters& counters,
+                                   uint32_t ssrc) = 0;
+};
+
+// Rate statistics for a stream
+struct BitrateStatistics {
+ public:
+  BitrateStatistics()
+    : bitrate_(0),
+      packet_rate(0),
+      now(0) {}
+
+  uint32_t bitrate_;
+  uint32_t packet_rate;
+  uint64_t now;
+};
+
+// Callback, used to notify an observer whenever new rates have been estimated.
+class BitrateStatisticsObserver {
+ public:
+  virtual ~BitrateStatisticsObserver() {}
+
+  virtual void Notify(const BitrateStatistics& stats, uint32_t ssrc) = 0;
+};
+
+// Callback, used to notify an observer whenever frame counts have been updated
+class FrameCountObserver {
+ public:
+  virtual ~FrameCountObserver() {}
+  virtual void Notify(const unsigned int key_frames,
+                      const unsigned int delta_frames,
+                      const unsigned int ssrc) = 0;
+};
+
 // ==================================================================
 // Voice specific types
 // ==================================================================
diff --git a/config.cc b/config.cc
new file mode 100644
index 0000000..021bbbf
--- /dev/null
+++ b/config.cc
@@ -0,0 +1,17 @@
+/*
+ *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/config.h"
+
+namespace webrtc {
+const char* RtpExtension::kTOffset = "urn:ietf:params:rtp-hdrext:toffset";
+const char* RtpExtension::kAbsSendTime =
+    "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time";
+}  // namespace webrtc
diff --git a/config.h b/config.h
index a00a8f9..3ff3bb8 100644
--- a/config.h
+++ b/config.h
@@ -16,6 +16,8 @@
 #include <string>
 #include <vector>
 
+#include "webrtc/typedefs.h"
+
 namespace webrtc {
 
 struct RtpStatistics {
@@ -67,6 +69,8 @@
 
 // RTP header extension to use for the video stream, see RFC 5285.
 struct RtpExtension {
+  static const char* kTOffset;
+  static const char* kAbsSendTime;
   RtpExtension(const char* name, int id) : name(name), id(id) {}
   // TODO(mflodman) Add API to query supported extensions.
   std::string name;
diff --git a/frame_callback.h b/frame_callback.h
index 7f54245..cfb07d8 100644
--- a/frame_callback.h
+++ b/frame_callback.h
@@ -11,11 +11,22 @@
 #ifndef WEBRTC_VIDEO_ENGINE_NEW_INCLUDE_FRAME_CALLBACK_H_
 #define WEBRTC_VIDEO_ENGINE_NEW_INCLUDE_FRAME_CALLBACK_H_
 
+#include "webrtc/common_types.h"
+
 namespace webrtc {
 
 class I420VideoFrame;
 
-struct EncodedFrame;
+struct EncodedFrame {
+ public:
+  EncodedFrame() : data_(NULL), length_(0), frame_type_(kFrameEmpty) {}
+  EncodedFrame(const uint8_t* data, size_t length, FrameType frame_type)
+    : data_(data), length_(length), frame_type_(frame_type) {}
+
+  const uint8_t* data_;
+  const size_t length_;
+  const FrameType frame_type_;
+};
 
 class I420FrameCallback {
  public:
@@ -34,6 +45,7 @@
  protected:
   virtual ~EncodedFrameObserver() {}
 };
+
 }  // namespace webrtc
 
 #endif  // WEBRTC_VIDEO_ENGINE_NEW_INCLUDE_FRAME_CALLBACK_H_
diff --git a/loopback.cc b/loopback.cc
index 5c5dbce..e07138a 100644
--- a/loopback.cc
+++ b/loopback.cc
@@ -63,7 +63,7 @@
   send_config.codec.maxBitrate =
       static_cast<unsigned int>(test::flags::MaxBitrate());
 
-  VideoSendStream* send_stream = call->CreateSendStream(send_config);
+  VideoSendStream* send_stream = call->CreateVideoSendStream(send_config);
 
   Clock* test_clock = Clock::GetRealTimeClock();
 
@@ -79,20 +79,20 @@
   receive_config.renderer = loopback_video.get();
 
   VideoReceiveStream* receive_stream =
-      call->CreateReceiveStream(receive_config);
+      call->CreateVideoReceiveStream(receive_config);
 
-  receive_stream->StartReceive();
-  send_stream->StartSend();
+  receive_stream->StartReceiving();
+  send_stream->StartSending();
   camera->Start();
 
   test::PressEnterToContinue();
 
   camera->Stop();
-  send_stream->StopSend();
-  receive_stream->StopReceive();
+  send_stream->StopSending();
+  receive_stream->StopReceiving();
 
-  call->DestroyReceiveStream(receive_stream);
-  call->DestroySendStream(send_stream);
+  call->DestroyVideoReceiveStream(receive_stream);
+  call->DestroyVideoSendStream(send_stream);
 
   transport.StopSending();
 }
diff --git a/modules/audio_coding/main/acm2/audio_coding_module.cc b/modules/audio_coding/main/acm2/audio_coding_module.cc
index c7fe9fa..60ed69c 100644
--- a/modules/audio_coding/main/acm2/audio_coding_module.cc
+++ b/modules/audio_coding/main/acm2/audio_coding_module.cc
@@ -19,6 +19,9 @@
 
 namespace webrtc {
 
+const char kLegacyAcmVersion[] = "acm1";
+const char kExperimentalAcmVersion[] = "acm2";
+
 // Create module
 AudioCodingModule* AudioCodingModule::Create(int id) {
   return new acm1::AudioCodingModuleImpl(id, Clock::GetRealTimeClock());
diff --git a/modules/audio_coding/main/acm2/audio_coding_module_impl.cc b/modules/audio_coding/main/acm2/audio_coding_module_impl.cc
index 59d5727..ce05218 100644
--- a/modules/audio_coding/main/acm2/audio_coding_module_impl.cc
+++ b/modules/audio_coding/main/acm2/audio_coding_module_impl.cc
@@ -1975,6 +1975,10 @@
   return receiver_.LeastRequiredDelayMs();
 }
 
+const char* AudioCodingModuleImpl::Version() const {
+  return kExperimentalAcmVersion;
+}
+
 }  // namespace acm2
 
 }  // namespace webrtc
diff --git a/modules/audio_coding/main/acm2/audio_coding_module_impl.h b/modules/audio_coding/main/acm2/audio_coding_module_impl.h
index 6bf90cf..17fab39 100644
--- a/modules/audio_coding/main/acm2/audio_coding_module_impl.h
+++ b/modules/audio_coding/main/acm2/audio_coding_module_impl.h
@@ -32,12 +32,11 @@
 
 class AudioCodingModuleImpl : public AudioCodingModule {
  public:
-  // Constructor
   explicit AudioCodingModuleImpl(int id);
-
-  // Destructor
   ~AudioCodingModuleImpl();
 
+  virtual const char* Version() const;
+
   // Change the unique identifier of this object.
   virtual int32_t ChangeUniqueId(const int32_t id);
 
diff --git a/modules/audio_coding/main/interface/audio_coding_module.h b/modules/audio_coding/main/interface/audio_coding_module.h
index 2f4776d..f8b9690 100644
--- a/modules/audio_coding/main/interface/audio_coding_module.h
+++ b/modules/audio_coding/main/interface/audio_coding_module.h
@@ -73,6 +73,10 @@
       const uint16_t delayMS) = 0;  // average delay in ms
 };
 
+// Version string for testing, to distinguish instances of ACM1 from ACM2.
+extern const char kLegacyAcmVersion[];
+extern const char kExperimentalAcmVersion[];
+
 class AudioCodingModule: public Module {
  protected:
   AudioCodingModule() {}
@@ -174,6 +178,11 @@
   //
   static bool IsCodecValid(const CodecInst& codec);
 
+  // Returns the version of ACM. This facilitates distinguishing instances of
+  // ACM1 from ACM2 while testing. This API will be removed when ACM1 is
+  // completely removed.
+  virtual const char* Version() const = 0;
+
   ///////////////////////////////////////////////////////////////////////////
   //   Sender
   //
diff --git a/modules/audio_coding/main/source/audio_coding_module.gypi b/modules/audio_coding/main/source/audio_coding_module.gypi
index c6c10f6..a0389b0 100644
--- a/modules/audio_coding/main/source/audio_coding_module.gypi
+++ b/modules/audio_coding/main/source/audio_coding_module.gypi
@@ -117,7 +117,7 @@
           'dependencies': [
             'audio_coding_module',
             '<(DEPTH)/testing/gtest.gyp:gtest',
-            '<(webrtc_root)/test/test.gyp:test_support_main',
+            '<(webrtc_root)/test/test.gyp:test_support',
             '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers',
             '<(DEPTH)/third_party/gflags/gflags.gyp:gflags',
           ],
@@ -125,6 +125,7 @@
              '../test/delay_test.cc',
              '../test/Channel.cc',
              '../test/PCMFile.cc',
+             '../test/utility.cc',
            ],
         }, # delay_test
         {
@@ -133,7 +134,7 @@
           'dependencies': [
             'audio_coding_module',
             '<(DEPTH)/testing/gtest.gyp:gtest',
-            '<(webrtc_root)/test/test.gyp:test_support_main',
+            '<(webrtc_root)/test/test.gyp:test_support',
             '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers',
             '<(DEPTH)/third_party/gflags/gflags.gyp:gflags',
           ],
diff --git a/modules/audio_coding/main/source/audio_coding_module_impl.cc b/modules/audio_coding/main/source/audio_coding_module_impl.cc
index 3802733..1e71a04 100644
--- a/modules/audio_coding/main/source/audio_coding_module_impl.cc
+++ b/modules/audio_coding/main/source/audio_coding_module_impl.cc
@@ -3026,6 +3026,10 @@
   nack_enabled_ = false;
 }
 
+const char* AudioCodingModuleImpl::Version() const {
+  return kLegacyAcmVersion;
+}
+
 }  // namespace acm1
 
 }  // namespace webrtc
diff --git a/modules/audio_coding/main/source/audio_coding_module_impl.h b/modules/audio_coding/main/source/audio_coding_module_impl.h
index 7d58f37..7acde17 100644
--- a/modules/audio_coding/main/source/audio_coding_module_impl.h
+++ b/modules/audio_coding/main/source/audio_coding_module_impl.h
@@ -40,12 +40,11 @@
 
 class AudioCodingModuleImpl : public AudioCodingModule {
  public:
-  // Constructor
   AudioCodingModuleImpl(const int32_t id, Clock* clock);
-
-  // Destructor
   ~AudioCodingModuleImpl();
 
+  virtual const char* Version() const;
+
   // Change the unique identifier of this object.
   virtual int32_t ChangeUniqueId(const int32_t id);
 
diff --git a/modules/audio_coding/main/test/delay_test.cc b/modules/audio_coding/main/test/delay_test.cc
index 1a0f8f8..63bfe2b 100644
--- a/modules/audio_coding/main/test/delay_test.cc
+++ b/modules/audio_coding/main/test/delay_test.cc
@@ -8,8 +8,6 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h"
-
 #include <assert.h>
 #include <math.h>
 
@@ -17,8 +15,10 @@
 
 #include "gflags/gflags.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "webrtc/common.h"
 #include "webrtc/common_types.h"
 #include "webrtc/engine_configurations.h"
+#include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h"
 #include "webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h"
 #include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.h"
 #include "webrtc/modules/audio_coding/main/test/Channel.h"
@@ -35,68 +35,76 @@
 DEFINE_int32(delay, 0, "Delay in millisecond.");
 DEFINE_int32(init_delay, 0, "Initial delay in millisecond.");
 DEFINE_bool(dtx, false, "Enable DTX at the sender side.");
+DEFINE_bool(acm2, false, "Run the test with ACM2.");
+DEFINE_bool(packet_loss, false, "Apply packet loss, c.f. Channel{.cc, .h}.");
+DEFINE_bool(fec, false, "Use Forward Error Correction (FEC).");
 
 namespace webrtc {
+
 namespace {
 
-struct CodecConfig {
+struct CodecSettings {
   char name[50];
   int sample_rate_hz;
   int num_channels;
 };
 
-struct AcmConfig {
+struct AcmSettings {
   bool dtx;
   bool fec;
 };
 
-struct Config {
-  CodecConfig codec;
-  AcmConfig acm;
+struct TestSettings {
+  CodecSettings codec;
+  AcmSettings acm;
   bool packet_loss;
 };
 
+}  // namespace
 
 class DelayTest {
  public:
-
-  DelayTest()
-      : acm_a_(AudioCodingModule::Create(0)),
-        acm_b_(AudioCodingModule::Create(1)),
-        channel_a2b_(NULL),
+  explicit DelayTest(const Config& config)
+      : acm_a_(config.Get<AudioCodingModuleFactory>().Create(0)),
+        acm_b_(config.Get<AudioCodingModuleFactory>().Create(1)),
+        channel_a2b_(new Channel),
         test_cntr_(0),
         encoding_sample_rate_hz_(8000) {}
 
-  ~DelayTest() {}
-
-  void TearDown() {
+  ~DelayTest() {
     if (channel_a2b_ != NULL) {
       delete channel_a2b_;
       channel_a2b_ = NULL;
     }
+    in_file_a_.Close();
   }
 
-  void SetUp() {
+  void Initialize() {
     test_cntr_ = 0;
     std::string file_name = webrtc::test::ResourcePath(
         "audio_coding/testfile32kHz", "pcm");
     if (FLAGS_input_file.size() > 0)
       file_name = FLAGS_input_file;
     in_file_a_.Open(file_name, 32000, "rb");
-    acm_a_->InitializeReceiver();
-    acm_b_->InitializeReceiver();
+    ASSERT_EQ(0, acm_a_->InitializeReceiver()) <<
+        "Couldn't initialize receiver.\n";
+    ASSERT_EQ(0, acm_b_->InitializeReceiver()) <<
+        "Couldn't initialize receiver.\n";
     if (FLAGS_init_delay > 0) {
-      ASSERT_EQ(0, acm_b_->SetInitialPlayoutDelay(FLAGS_init_delay));
+      ASSERT_EQ(0, acm_b_->SetInitialPlayoutDelay(FLAGS_init_delay)) <<
+          "Failed to set initial delay.\n";
     }
 
     if (FLAGS_delay > 0) {
-      ASSERT_EQ(0, acm_b_->SetMinimumPlayoutDelay(FLAGS_delay));
+      ASSERT_EQ(0, acm_b_->SetMinimumPlayoutDelay(FLAGS_delay)) <<
+          "Failed to set minimum delay.\n";
     }
 
-    uint8_t num_encoders = acm_a_->NumberOfCodecs();
+    int num_encoders = acm_a_->NumberOfCodecs();
     CodecInst my_codec_param;
     for (int n = 0; n < num_encoders; n++) {
-      acm_b_->Codec(n, &my_codec_param);
+      EXPECT_EQ(0, acm_b_->Codec(n, &my_codec_param)) <<
+          "Failed to get codec.";
       if (STR_CASE_CMP(my_codec_param.plname, "opus") == 0)
         my_codec_param.channels = 1;
       else if (my_codec_param.channels > 1)
@@ -106,16 +114,17 @@
         continue;
       if (STR_CASE_CMP(my_codec_param.plname, "telephone-event") == 0)
         continue;
-      acm_b_->RegisterReceiveCodec(my_codec_param);
+      ASSERT_EQ(0, acm_b_->RegisterReceiveCodec(my_codec_param)) <<
+          "Couldn't register receive codec.\n";
     }
 
     // Create and connect the channel
-    channel_a2b_ = new Channel;
-    acm_a_->RegisterTransportCallback(channel_a2b_);
+    ASSERT_EQ(0, acm_a_->RegisterTransportCallback(channel_a2b_)) <<
+        "Couldn't register Transport callback.\n";
     channel_a2b_->RegisterReceiverACM(acm_b_.get());
   }
 
-  void Perform(const Config* config, size_t num_tests, int duration_sec,
+  void Perform(const TestSettings* config, size_t num_tests, int duration_sec,
                const char* output_prefix) {
     for (size_t n = 0; n < num_tests; ++n) {
       ApplyConfig(config[n]);
@@ -124,8 +133,7 @@
   }
 
  private:
-
-  void ApplyConfig(const Config& config) {
+  void ApplyConfig(const TestSettings& config) {
     printf("====================================\n");
     printf("Test %d \n"
            "Codec: %s, %d kHz, %d channel(s)\n"
@@ -140,19 +148,22 @@
     ConfigChannel(config.packet_loss);
   }
 
-  void SendCodec(const CodecConfig& config) {
+  void SendCodec(const CodecSettings& config) {
     CodecInst my_codec_param;
-    ASSERT_EQ(
-        0,
-        AudioCodingModule::Codec(config.name, &my_codec_param,
-                                 config.sample_rate_hz, config.num_channels));
+    ASSERT_EQ(0, AudioCodingModule::Codec(
+              config.name, &my_codec_param, config.sample_rate_hz,
+              config.num_channels)) << "Specified codec is not supported.\n";
+
     encoding_sample_rate_hz_ = my_codec_param.plfreq;
-    ASSERT_EQ(0, acm_a_->RegisterSendCodec(my_codec_param));
+    ASSERT_EQ(0, acm_a_->RegisterSendCodec(my_codec_param)) <<
+        "Failed to register send-codec.\n";
   }
 
-  void ConfigAcm(const AcmConfig& config) {
-    ASSERT_EQ(0, acm_a_->SetVAD(config.dtx, config.dtx, VADAggr));
-    ASSERT_EQ(0, acm_a_->SetFECStatus(config.fec));
+  void ConfigAcm(const AcmSettings& config) {
+    ASSERT_EQ(0, acm_a_->SetVAD(config.dtx, config.dtx, VADAggr)) <<
+        "Failed to set VAD.\n";
+    ASSERT_EQ(0, acm_a_->SetFECStatus(config.fec)) <<
+        "Failed to set FEC.\n";
   }
 
   void ConfigChannel(bool packet_loss) {
@@ -230,19 +241,39 @@
   int encoding_sample_rate_hz_;
 };
 
-void RunTest() {
-  Config config;
-  strcpy(config.codec.name, FLAGS_codec.c_str());
-  config.codec.sample_rate_hz = FLAGS_sample_rate_hz;
-  config.codec.num_channels = FLAGS_num_channels;
-  config.acm.dtx = FLAGS_dtx;
-  config.acm.fec = false;
-  config.packet_loss = false;
-
-  DelayTest delay_test;
-  delay_test.SetUp();
-  delay_test.Perform(&config, 1, 240, "delay_test");
-  delay_test.TearDown();
-}
-}  // namespace
 }  // namespace webrtc
+
+int main(int argc, char* argv[]) {
+  google::ParseCommandLineFlags(&argc, &argv, true);
+  webrtc::Config config;
+  webrtc::TestSettings test_setting;
+  strcpy(test_setting.codec.name, FLAGS_codec.c_str());
+
+  if (FLAGS_sample_rate_hz != 8000 &&
+      FLAGS_sample_rate_hz != 16000 &&
+      FLAGS_sample_rate_hz != 32000 &&
+      FLAGS_sample_rate_hz != 48000) {
+    std::cout << "Invalid sampling rate.\n";
+    return 1;
+  }
+  test_setting.codec.sample_rate_hz = FLAGS_sample_rate_hz;
+  if (FLAGS_num_channels < 1 || FLAGS_num_channels > 2) {
+    std::cout << "Only mono and stereo are supported.\n";
+    return 1;
+  }
+  test_setting.codec.num_channels = FLAGS_num_channels;
+  test_setting.acm.dtx = FLAGS_dtx;
+  test_setting.acm.fec = FLAGS_fec;
+  test_setting.packet_loss = FLAGS_packet_loss;
+
+  if (FLAGS_acm2) {
+    webrtc::UseNewAcm(&config);
+  } else {
+    webrtc::UseLegacyAcm(&config);
+  }
+
+  webrtc::DelayTest delay_test(config);
+  delay_test.Initialize();
+  delay_test.Perform(&test_setting, 1, 240, "delay_test");
+  return 0;
+}
diff --git a/modules/audio_coding/neteq/automode.c b/modules/audio_coding/neteq/automode.c
index a922448..4dbd81e 100644
--- a/modules/audio_coding/neteq/automode.c
+++ b/modules/audio_coding/neteq/automode.c
@@ -28,6 +28,17 @@
 extern FILE *delay_fid2; /* file pointer to delay log file */
 #endif /* NETEQ_DELAY_LOGGING */
 
+// These two functions are copied from module_common_types.h, but adapted for C.
+int WebRtcNetEQ_IsNewerSequenceNumber(uint16_t sequence_number,
+                                      uint16_t prev_sequence_number) {
+  return sequence_number != prev_sequence_number &&
+         ((uint16_t) (sequence_number - prev_sequence_number)) < 0x8000;
+}
+
+int WebRtcNetEQ_IsNewerTimestamp(uint32_t timestamp, uint32_t prev_timestamp) {
+  return timestamp != prev_timestamp &&
+         ((uint32_t) (timestamp - prev_timestamp)) < 0x80000000;
+}
 
 int WebRtcNetEQ_UpdateIatStatistics(AutomodeInst_t *inst, int maxBufLen,
                                     uint16_t seqNumber, uint32_t timeStamp,
@@ -55,7 +66,8 @@
     /****************************/
 
     /* Try calculating packet length from current and previous timestamps */
-    if ((timeStamp <= inst->lastTimeStamp) || (seqNumber <= inst->lastSeqNo))
+    if (!WebRtcNetEQ_IsNewerTimestamp(timeStamp, inst->lastTimeStamp) ||
+        !WebRtcNetEQ_IsNewerSequenceNumber(seqNumber, inst->lastSeqNo))
     {
         /* Wrong timestamp or sequence order; revert to backup plan */
         packetLenSamp = inst->packetSpeechLenSamp; /* use stored value */
@@ -68,7 +80,7 @@
     }
 
     /* Check that the packet size is positive; if not, the statistics cannot be updated. */
-    if (packetLenSamp > 0)
+    if (inst->firstPacketReceived && packetLenSamp > 0)
     { /* packet size ok */
 
         /* calculate inter-arrival time in integer packets (rounding down) */
@@ -113,19 +125,19 @@
         } /* end of streaming mode */
 
         /* check for discontinuous packet sequence and re-ordering */
-        if (seqNumber > inst->lastSeqNo + 1)
+        if (WebRtcNetEQ_IsNewerSequenceNumber(seqNumber, inst->lastSeqNo + 1))
         {
             /* Compensate for gap in the sequence numbers.
              * Reduce IAT with expected extra time due to lost packets, but ensure that
              * the IAT is not negative.
              */
             timeIat -= WEBRTC_SPL_MIN(timeIat,
-                (uint32_t) (seqNumber - inst->lastSeqNo - 1));
+                (uint16_t) (seqNumber - (uint16_t) (inst->lastSeqNo + 1)));
         }
-        else if (seqNumber < inst->lastSeqNo)
+        else if (!WebRtcNetEQ_IsNewerSequenceNumber(seqNumber, inst->lastSeqNo))
         {
             /* compensate for re-ordering */
-            timeIat += (uint32_t) (inst->lastSeqNo + 1 - seqNumber);
+            timeIat += (uint16_t) (inst->lastSeqNo + 1 - seqNumber);
         }
 
         /* saturate IAT at maximum value */
@@ -316,6 +328,8 @@
 
     inst->lastTimeStamp = timeStamp; /* remember current timestamp */
 
+    inst->firstPacketReceived = 1;
+
     return retval;
 }
 
diff --git a/modules/audio_coding/neteq/automode.h b/modules/audio_coding/neteq/automode.h
index 16d72e8..c5dd829 100644
--- a/modules/audio_coding/neteq/automode.h
+++ b/modules/audio_coding/neteq/automode.h
@@ -80,6 +80,8 @@
      contained special information */
     uint16_t lastSeqNo; /* sequence number for last packet received */
     uint32_t lastTimeStamp; /* timestamp for the last packet received */
+    int firstPacketReceived; /* set to zero implicitly when the instance is
+     filled with zeros */
     int32_t sampleMemory; /* memory position for keeping track of how many
      samples we cut during expand */
     int16_t prevTimeScale; /* indicates that the last mode was an accelerate
diff --git a/modules/audio_coding/neteq/webrtc_neteq_unittest.cc b/modules/audio_coding/neteq/webrtc_neteq_unittest.cc
index 8f1c6ba..c37f899 100644
--- a/modules/audio_coding/neteq/webrtc_neteq_unittest.cc
+++ b/modules/audio_coding/neteq/webrtc_neteq_unittest.cc
@@ -17,6 +17,7 @@
 #include <stdlib.h>
 #include <string.h>  // memset
 
+#include <set>
 #include <sstream>
 #include <string>
 #include <vector>
@@ -193,6 +194,8 @@
                           WebRtcNetEQ_RTPInfo* rtp_info,
                           uint8_t* payload,
                           int* payload_len);
+  void WrapTest(uint16_t start_seq_no, uint32_t start_timestamp,
+                const std::set<uint16_t>& drop_seq_numbers);
 
   NETEQTEST_NetEQClass* neteq_inst_;
   std::vector<NETEQTEST_Decoder*> dec_;
@@ -505,7 +508,7 @@
   WebRtcNetEQ_NetworkStatistics network_stats;
   ASSERT_EQ(0, WebRtcNetEQ_GetNetworkStatistics(neteq_inst_->instance(),
                                                 &network_stats));
-  EXPECT_EQ(-106911, network_stats.clockDriftPPM);
+  EXPECT_EQ(-103196, network_stats.clockDriftPPM);
 }
 
 TEST_F(NetEqDecodingTest, TestAverageInterArrivalTimePositive) {
@@ -536,7 +539,7 @@
   WebRtcNetEQ_NetworkStatistics network_stats;
   ASSERT_EQ(0, WebRtcNetEQ_GetNetworkStatistics(neteq_inst_->instance(),
                                                 &network_stats));
-  EXPECT_EQ(108352, network_stats.clockDriftPPM);
+  EXPECT_EQ(110946, network_stats.clockDriftPPM);
 }
 
 TEST_F(NetEqDecodingTest, LongCngWithClockDrift) {
@@ -700,4 +703,76 @@
   }
 }
 
+void NetEqDecodingTest::WrapTest(uint16_t start_seq_no,
+                                 uint32_t start_timestamp,
+                                 const std::set<uint16_t>& drop_seq_numbers) {
+  uint16_t seq_no = start_seq_no;
+  uint32_t timestamp = start_timestamp;
+  const int kFrameSizeMs = 30;
+  const int kSamples = kFrameSizeMs * 16;
+  const int kPayloadBytes = kSamples * 2;
+  double next_input_time_ms = 0.0;
+
+  // Insert speech for 1 second.
+  const int kSpeechDurationMs = 1000;
+  for (double t_ms = 0; t_ms < kSpeechDurationMs; t_ms += 10) {
+    // Each turn in this for loop is 10 ms.
+    while (next_input_time_ms <= t_ms) {
+      // Insert one 30 ms speech frame.
+      uint8_t payload[kPayloadBytes] = {0};
+      WebRtcNetEQ_RTPInfo rtp_info;
+      PopulateRtpInfo(seq_no, timestamp, &rtp_info);
+      if (drop_seq_numbers.find(seq_no) == drop_seq_numbers.end()) {
+        // This sequence number was not in the set to drop. Insert it.
+        ASSERT_EQ(0,
+                  WebRtcNetEQ_RecInRTPStruct(neteq_inst_->instance(),
+                                             &rtp_info,
+                                             payload,
+                                             kPayloadBytes, 0));
+      }
+      ++seq_no;
+      timestamp += kSamples;
+      next_input_time_ms += static_cast<double>(kFrameSizeMs);
+      WebRtcNetEQ_NetworkStatistics network_stats;
+      ASSERT_EQ(0, WebRtcNetEQ_GetNetworkStatistics(neteq_inst_->instance(),
+                                                    &network_stats));
+      // Expect preferred and actual buffer size to be no more than 2 frames.
+      EXPECT_LE(network_stats.preferredBufferSize, kFrameSizeMs * 2);
+      EXPECT_LE(network_stats.currentBufferSize, kFrameSizeMs * 2);
+    }
+    // Pull out data once.
+    ASSERT_TRUE(kBlockSize16kHz == neteq_inst_->recOut(out_data_));
+    // Expect delay (in samples) to be less than 2 packets.
+    EXPECT_LE(timestamp - neteq_inst_->getSpeechTimeStamp(),
+              static_cast<uint32_t>(kSamples * 2));
+  }
+}
+
+TEST_F(NetEqDecodingTest, SequenceNumberWrap) {
+  // Start with a sequence number that will soon wrap.
+  std::set<uint16_t> drop_seq_numbers;  // Don't drop any packets.
+  WrapTest(0xFFFF - 5, 0, drop_seq_numbers);
+}
+
+TEST_F(NetEqDecodingTest, SequenceNumberWrapAndDrop) {
+  // Start with a sequence number that will soon wrap.
+  std::set<uint16_t> drop_seq_numbers;
+  drop_seq_numbers.insert(0xFFFF);
+  drop_seq_numbers.insert(0x0);
+  WrapTest(0xFFFF - 5, 0, drop_seq_numbers);
+}
+
+TEST_F(NetEqDecodingTest, TimestampWrap) {
+  // Start with a timestamp that will soon wrap.
+  std::set<uint16_t> drop_seq_numbers;
+  WrapTest(0, 0xFFFFFFFF - 1000, drop_seq_numbers);
+}
+
+TEST_F(NetEqDecodingTest, TimestampAndSequenceNumberWrap) {
+  // Start with a timestamp and a sequence number that will wrap at the same
+  // time.
+  std::set<uint16_t> drop_seq_numbers;
+  WrapTest(0xFFFF - 2, 0xFFFFFFFF - 1000, drop_seq_numbers);
+}
+
 }  // namespace
diff --git a/modules/audio_coding/neteq4/audio_decoder_unittest.cc b/modules/audio_coding/neteq4/audio_decoder_unittest.cc
index 0ed13e2..dbd9d12 100644
--- a/modules/audio_coding/neteq4/audio_decoder_unittest.cc
+++ b/modules/audio_coding/neteq4/audio_decoder_unittest.cc
@@ -119,14 +119,16 @@
       encoded_bytes_ += enc_len;
       processed_samples += frame_size_;
     }
-    // This test fails on Win x64, see issue webrtc:1459
-#if !(defined(_WIN32) && defined(WEBRTC_ARCH_64_BITS))
-    EXPECT_EQ(expected_bytes, encoded_bytes_);
+    // For some codecs it doesn't make sense to check expected number of bytes,
+    // since the number can vary for different platforms. Opus and iSAC are
+    // such codecs. In this case expected_bytes is set to 0.
+    if (expected_bytes) {
+      EXPECT_EQ(expected_bytes, encoded_bytes_);
+    }
     CompareInputOutput(processed_samples, tolerance, delay);
     if (channels_ == 2)
       CompareTwoChannels(processed_samples, channel_diff_tolerance);
     EXPECT_LE(MseInputOutput(processed_samples, delay), mse);
-#endif
   }
 
   // The absolute difference between the input and output (the first channel) is
@@ -733,7 +735,7 @@
   double mse = 434951.0;
   int delay = 48;  // Delay from input to output.
   EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderISAC));
-  EncodeDecodeTest(883, tolerance, mse, delay);
+  EncodeDecodeTest(0, tolerance, mse, delay);
   ReInitTest();
   EXPECT_TRUE(decoder_->HasDecodePlc());
   DecodePlcTest();
@@ -744,7 +746,7 @@
   double mse = 8.18e6;
   int delay = 160;  // Delay from input to output.
   EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderISACswb));
-  EncodeDecodeTest(853, tolerance, mse, delay);
+  EncodeDecodeTest(0, tolerance, mse, delay);
   ReInitTest();
   EXPECT_TRUE(decoder_->HasDecodePlc());
   DecodePlcTest();
@@ -755,7 +757,7 @@
   double mse = 8.18e6;
   int delay = 160;  // Delay from input to output.
   EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderISACswb));
-  EncodeDecodeTest(853, tolerance, mse, delay);
+  EncodeDecodeTest(0, tolerance, mse, delay);
   ReInitTest();
   EXPECT_TRUE(decoder_->HasDecodePlc());
   DecodePlcTest();
@@ -801,7 +803,7 @@
   double mse = 238630.0;
   int delay = 22;  // Delay from input to output.
   EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderOpus));
-  EncodeDecodeTest(731, tolerance, mse, delay);
+  EncodeDecodeTest(0, tolerance, mse, delay);
   ReInitTest();
   EXPECT_FALSE(decoder_->HasDecodePlc());
 }
@@ -812,7 +814,7 @@
   double mse = 238630.0;
   int delay = 22;  // Delay from input to output.
   EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderOpus_2ch));
-  EncodeDecodeTest(1383, tolerance, mse, delay, channel_diff_tolerance);
+  EncodeDecodeTest(0, tolerance, mse, delay, channel_diff_tolerance);
   ReInitTest();
   EXPECT_FALSE(decoder_->HasDecodePlc());
 }
diff --git a/modules/audio_coding/neteq4/delay_manager.cc b/modules/audio_coding/neteq4/delay_manager.cc
index 63ed525..e80b9de 100644
--- a/modules/audio_coding/neteq4/delay_manager.cc
+++ b/modules/audio_coding/neteq4/delay_manager.cc
@@ -17,6 +17,7 @@
 
 #include "webrtc/common_audio/signal_processing/include/signal_processing_library.h"
 #include "webrtc/modules/audio_coding/neteq4/delay_peak_detector.h"
+#include "webrtc/modules/interface/module_common_types.h"
 #include "webrtc/system_wrappers/interface/logging.h"
 
 namespace webrtc {
@@ -85,10 +86,9 @@
   }
 
   // Try calculating packet length from current and previous timestamps.
-  // TODO(hlundin): Take care of wrap-around. Not done yet due to legacy
-  // bit-exactness.
   int packet_len_ms;
-  if ((timestamp <= last_timestamp_) || (sequence_number <= last_seq_no_)) {
+  if (!IsNewerTimestamp(timestamp, last_timestamp_) ||
+      !IsNewerSequenceNumber(sequence_number, last_seq_no_)) {
     // Wrong timestamp or sequence order; use stored value.
     packet_len_ms = packet_len_ms_;
   } else {
@@ -111,18 +111,14 @@
     }
 
     // Check for discontinuous packet sequence and re-ordering.
-    if (sequence_number > last_seq_no_ + 1) {
-      // TODO(hlundin): Take care of wrap-around. Not done yet due to legacy
-      // bit-exactness.
+    if (IsNewerSequenceNumber(sequence_number, last_seq_no_ + 1)) {
       // Compensate for gap in the sequence numbers. Reduce IAT with the
       // expected extra time due to lost packets, but ensure that the IAT is
       // not negative.
-      iat_packets -= sequence_number - last_seq_no_ - 1;
+      iat_packets -= static_cast<uint16_t>(sequence_number - last_seq_no_ - 1);
       iat_packets = std::max(iat_packets, 0);
-    } else if (sequence_number < last_seq_no_) {
-      // TODO(hlundin): Take care of wrap-around.
-      // Compensate for re-ordering.
-      iat_packets += last_seq_no_ + 1 - sequence_number;
+    } else if (!IsNewerSequenceNumber(sequence_number, last_seq_no_)) {
+      iat_packets += static_cast<uint16_t>(last_seq_no_ + 1 - sequence_number);
     }
 
     // Saturate IAT at maximum value.
diff --git a/modules/audio_coding/neteq4/neteq_unittest.cc b/modules/audio_coding/neteq4/neteq_unittest.cc
index a8cff5e..965f75f 100644
--- a/modules/audio_coding/neteq4/neteq_unittest.cc
+++ b/modules/audio_coding/neteq4/neteq_unittest.cc
@@ -18,6 +18,7 @@
 #include <string.h>  // memset
 
 #include <cmath>
+#include <set>
 #include <string>
 #include <vector>
 
@@ -214,6 +215,10 @@
 
   void CheckBgnOff(int sampling_rate, NetEqBackgroundNoiseMode bgn_mode);
 
+  void WrapTest(uint16_t start_seq_no, uint32_t start_timestamp,
+                const std::set<uint16_t>& drop_seq_numbers,
+                bool expect_seq_no_wrap, bool expect_timestamp_wrap);
+
   NetEq* neteq_;
   FILE* rtp_fp_;
   unsigned int sim_clock_;
@@ -260,10 +265,12 @@
 #endif  // WEBRTC_ANDROID
   // Load iSAC.
   ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderISAC, 103));
+#ifndef WEBRTC_ANDROID
   // Load iSAC SWB.
   ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderISACswb, 104));
   // Load iSAC FB.
   ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderISACfb, 105));
+#endif  // WEBRTC_ANDROID
   // Load PCM16B nb.
   ASSERT_EQ(0, neteq_->RegisterPayloadType(kDecoderPCM16B, 93));
   // Load PCM16B wb.
@@ -1104,4 +1111,109 @@
   }
 }
 
+void NetEqDecodingTest::WrapTest(uint16_t start_seq_no,
+                                 uint32_t start_timestamp,
+                                 const std::set<uint16_t>& drop_seq_numbers,
+                                 bool expect_seq_no_wrap,
+                                 bool expect_timestamp_wrap) {
+  uint16_t seq_no = start_seq_no;
+  uint32_t timestamp = start_timestamp;
+  const int kBlocksPerFrame = 3;  // Number of 10 ms blocks per frame.
+  const int kFrameSizeMs = kBlocksPerFrame * kTimeStepMs;
+  const int kSamples = kBlockSize16kHz * kBlocksPerFrame;
+  const int kPayloadBytes = kSamples * sizeof(int16_t);
+  double next_input_time_ms = 0.0;
+  int16_t decoded[kBlockSize16kHz];
+  int num_channels;
+  int samples_per_channel;
+  NetEqOutputType output_type;
+  uint32_t receive_timestamp = 0;
+
+  // Insert speech for 1 second.
+  const int kSpeechDurationMs = 2000;
+  int packets_inserted = 0;
+  uint16_t last_seq_no;
+  uint32_t last_timestamp;
+  bool timestamp_wrapped = false;
+  bool seq_no_wrapped = false;
+  for (double t_ms = 0; t_ms < kSpeechDurationMs; t_ms += 10) {
+    // Each turn in this for loop is 10 ms.
+    while (next_input_time_ms <= t_ms) {
+      // Insert one 30 ms speech frame.
+      uint8_t payload[kPayloadBytes] = {0};
+      WebRtcRTPHeader rtp_info;
+      PopulateRtpInfo(seq_no, timestamp, &rtp_info);
+      if (drop_seq_numbers.find(seq_no) == drop_seq_numbers.end()) {
+        // This sequence number was not in the set to drop. Insert it.
+        ASSERT_EQ(0,
+                  neteq_->InsertPacket(rtp_info, payload, kPayloadBytes,
+                                       receive_timestamp));
+        ++packets_inserted;
+      }
+      NetEqNetworkStatistics network_stats;
+      ASSERT_EQ(0, neteq_->NetworkStatistics(&network_stats));
+
+      // Due to internal NetEq logic, preferred buffer-size is about 4 times the
+      // packet size for first few packets. Therefore we refrain from checking
+      // the criteria.
+      if (packets_inserted > 4) {
+        // Expect preferred and actual buffer size to be no more than 2 frames.
+        EXPECT_LE(network_stats.preferred_buffer_size_ms, kFrameSizeMs * 2);
+        EXPECT_LE(network_stats.current_buffer_size_ms, kFrameSizeMs * 2);
+      }
+      last_seq_no = seq_no;
+      last_timestamp = timestamp;
+
+      ++seq_no;
+      timestamp += kSamples;
+      receive_timestamp += kSamples;
+      next_input_time_ms += static_cast<double>(kFrameSizeMs);
+
+      seq_no_wrapped |= seq_no < last_seq_no;
+      timestamp_wrapped |= timestamp < last_timestamp;
+    }
+    // Pull out data once.
+    ASSERT_EQ(0, neteq_->GetAudio(kBlockSize16kHz, decoded,
+                                  &samples_per_channel, &num_channels,
+                                  &output_type));
+    ASSERT_EQ(kBlockSize16kHz, samples_per_channel);
+    ASSERT_EQ(1, num_channels);
+
+    // Expect delay (in samples) to be less than 2 packets.
+    EXPECT_LE(timestamp - neteq_->PlayoutTimestamp(),
+              static_cast<uint32_t>(kSamples * 2));
+
+  }
+  // Make sure we have actually tested wrap-around.
+  ASSERT_EQ(expect_seq_no_wrap, seq_no_wrapped);
+  ASSERT_EQ(expect_timestamp_wrap, timestamp_wrapped);
+}
+
+TEST_F(NetEqDecodingTest, SequenceNumberWrap) {
+  // Start with a sequence number that will soon wrap.
+  std::set<uint16_t> drop_seq_numbers;  // Don't drop any packets.
+  WrapTest(0xFFFF - 10, 0, drop_seq_numbers, true, false);
+}
+
+TEST_F(NetEqDecodingTest, SequenceNumberWrapAndDrop) {
+  // Start with a sequence number that will soon wrap.
+  std::set<uint16_t> drop_seq_numbers;
+  drop_seq_numbers.insert(0xFFFF);
+  drop_seq_numbers.insert(0x0);
+  WrapTest(0xFFFF - 10, 0, drop_seq_numbers, true, false);
+}
+
+TEST_F(NetEqDecodingTest, TimestampWrap) {
+  // Start with a timestamp that will soon wrap.
+  std::set<uint16_t> drop_seq_numbers;
+  WrapTest(0, 0xFFFFFFFF - 3000, drop_seq_numbers, false, true);
+}
+
+TEST_F(NetEqDecodingTest, TimestampAndSequenceNumberWrap) {
+  // Start with a timestamp and a sequence number that will wrap at the same
+  // time.
+  std::set<uint16_t> drop_seq_numbers;
+  WrapTest(0xFFFF - 10, 0xFFFFFFFF - 5000, drop_seq_numbers, true, true);
+}
+
 }  // namespace
diff --git a/modules/audio_device/android/opensles_input.cc b/modules/audio_device/android/opensles_input.cc
index 9f58205..5bce539 100644
--- a/modules/audio_device/android/opensles_input.cc
+++ b/modules/audio_device/android/opensles_input.cc
@@ -126,7 +126,6 @@
 
 int32_t OpenSlesInput::InitRecording() {
   assert(initialized_);
-  assert(!rec_initialized_);
   rec_initialized_ = true;
   return 0;
 }
@@ -165,6 +164,7 @@
 int32_t OpenSlesInput::StopRecording() {
   StopCbThreads();
   DestroyAudioRecorder();
+  recording_ = false;
   return 0;
 }
 
diff --git a/modules/audio_device/android/opensles_output.cc b/modules/audio_device/android/opensles_output.cc
index 882440e..f264f21 100644
--- a/modules/audio_device/android/opensles_output.cc
+++ b/modules/audio_device/android/opensles_output.cc
@@ -139,7 +139,6 @@
 
 int32_t OpenSlesOutput::InitPlayout() {
   assert(initialized_);
-  assert(!play_initialized_);
   play_initialized_ = true;
   return 0;
 }
@@ -176,6 +175,7 @@
 int32_t OpenSlesOutput::StopPlayout() {
   StopCbThreads();
   DestroyAudioPlayer();
+  playing_ = false;
   return 0;
 }
 
diff --git a/modules/audio_device/include/fake_audio_device.h b/modules/audio_device/include/fake_audio_device.h
index 7966716..0248317 100644
--- a/modules/audio_device/include/fake_audio_device.h
+++ b/modules/audio_device/include/fake_audio_device.h
@@ -15,7 +15,7 @@
 class FakeAudioDeviceModule : public AudioDeviceModule {
  public:
   FakeAudioDeviceModule() {}
-  ~FakeAudioDeviceModule() {}
+  virtual ~FakeAudioDeviceModule() {}
   virtual int32_t AddRef() { return 0; }
   virtual int32_t Release() { return 0; }
   virtual int32_t RegisterEventObserver(AudioDeviceObserver* eventCallback) {
@@ -48,283 +48,112 @@
   virtual int32_t Process() { return 0; }
   virtual int32_t Terminate() { return 0; }
 
-  virtual int32_t ActiveAudioLayer(AudioLayer* audioLayer) const {
-    assert(false);
-    return 0;
-  }
-  virtual ErrorCode LastError() const {
-    assert(false);
-    return  kAdmErrNone;
-  }
-  virtual bool Initialized() const {
-    assert(false);
-    return true;
-  }
-  virtual int16_t PlayoutDevices() {
-    assert(false);
-    return 0;
-  }
-  virtual int16_t RecordingDevices() {
-    assert(false);
-    return 0;
-  }
+  virtual int32_t ActiveAudioLayer(AudioLayer* audioLayer) const { return 0; }
+  virtual ErrorCode LastError() const { return kAdmErrNone; }
+  virtual bool Initialized() const { return true; }
+  virtual int16_t PlayoutDevices() { return 0; }
+  virtual int16_t RecordingDevices() { return 0; }
   virtual int32_t PlayoutDeviceName(uint16_t index,
                             char name[kAdmMaxDeviceNameSize],
                             char guid[kAdmMaxGuidSize]) {
-    assert(false);
     return 0;
   }
   virtual int32_t RecordingDeviceName(uint16_t index,
                               char name[kAdmMaxDeviceNameSize],
                               char guid[kAdmMaxGuidSize]) {
-    assert(false);
     return 0;
   }
-  virtual int32_t PlayoutIsAvailable(bool* available) {
-    assert(false);
-    return 0;
-  }
-  virtual int32_t InitPlayout() {
-    assert(false);
-    return 0;
-  }
-  virtual bool PlayoutIsInitialized() const {
-    assert(false);
-    return true;
-  }
-  virtual int32_t RecordingIsAvailable(bool* available) {
-    assert(false);
-    return 0;
-  }
-  virtual int32_t InitRecording() {
-    assert(false);
-    return 0;
-  }
-  virtual bool RecordingIsInitialized() const {
-    assert(false);
-    return true;
-  }
-  virtual int32_t StartPlayout() {
-    assert(false);
-    return 0;
-  }
-  virtual bool Playing() const {
-    assert(false);
-    return false;
-  }
-  virtual int32_t StartRecording() {
-    assert(false);
-    return 0;
-  }
-  virtual bool Recording() const {
-    assert(false);
-    return false;
-  }
-  virtual bool AGC() const {
-    assert(false);
-    return true;
-  }
+  virtual int32_t PlayoutIsAvailable(bool* available) { return 0; }
+  virtual int32_t InitPlayout() { return 0; }
+  virtual bool PlayoutIsInitialized() const { return true; }
+  virtual int32_t RecordingIsAvailable(bool* available) { return 0; }
+  virtual int32_t InitRecording() { return 0; }
+  virtual bool RecordingIsInitialized() const { return true; }
+  virtual int32_t StartPlayout() { return 0; }
+  virtual bool Playing() const { return false; }
+  virtual int32_t StartRecording() { return 0; }
+  virtual bool Recording() const { return false; }
+  virtual bool AGC() const { return true; }
   virtual int32_t SetWaveOutVolume(uint16_t volumeLeft,
                            uint16_t volumeRight) {
-    assert(false);
     return 0;
   }
   virtual int32_t WaveOutVolume(uint16_t* volumeLeft,
                         uint16_t* volumeRight) const {
-    assert(false);
     return 0;
   }
-  virtual bool SpeakerIsInitialized() const {
-    assert(false);
-    return true;
-  }
-  virtual bool MicrophoneIsInitialized() const {
-    assert(false);
-    return true;
-  }
-  virtual int32_t SpeakerVolumeIsAvailable(bool* available) {
-    assert(false);
-    return 0;
-  }
-  virtual int32_t SetSpeakerVolume(uint32_t volume) {
-    assert(false);
-    return 0;
-  }
-  virtual int32_t SpeakerVolume(uint32_t* volume) const {
-    assert(false);
-    return 0;
-  }
-  virtual int32_t MaxSpeakerVolume(uint32_t* maxVolume) const {
-    assert(false);
-    return 0;
-  }
-  virtual int32_t MinSpeakerVolume(uint32_t* minVolume) const {
-    assert(false);
-    return 0;
-  }
-  virtual int32_t SpeakerVolumeStepSize(uint16_t* stepSize) const {
-    assert(false);
-    return 0;
-  }
-  virtual int32_t MicrophoneVolumeIsAvailable(bool* available) {
-    assert(false);
-    return 0;
-  }
-  virtual int32_t SetMicrophoneVolume(uint32_t volume) {
-    assert(false);
-    return 0;
-  }
-  virtual int32_t MicrophoneVolume(uint32_t* volume) const {
-    assert(false);
-    return 0;
-  }
-  virtual int32_t MaxMicrophoneVolume(uint32_t* maxVolume) const {
-    assert(false);
-    return 0;
-  }
-  virtual int32_t MinMicrophoneVolume(uint32_t* minVolume) const {
-    assert(false);
-    return 0;
-  }
+  virtual bool SpeakerIsInitialized() const { return true; }
+  virtual bool MicrophoneIsInitialized() const { return true; }
+  virtual int32_t SpeakerVolumeIsAvailable(bool* available) { return 0; }
+  virtual int32_t SetSpeakerVolume(uint32_t volume) { return 0; }
+  virtual int32_t SpeakerVolume(uint32_t* volume) const { return 0; }
+  virtual int32_t MaxSpeakerVolume(uint32_t* maxVolume) const { return 0; }
+  virtual int32_t MinSpeakerVolume(uint32_t* minVolume) const { return 0; }
+  virtual int32_t SpeakerVolumeStepSize(uint16_t* stepSize) const { return 0; }
+  virtual int32_t MicrophoneVolumeIsAvailable(bool* available) { return 0; }
+  virtual int32_t SetMicrophoneVolume(uint32_t volume) { return 0; }
+  virtual int32_t MicrophoneVolume(uint32_t* volume) const { return 0; }
+  virtual int32_t MaxMicrophoneVolume(uint32_t* maxVolume) const { return 0; }
+  virtual int32_t MinMicrophoneVolume(uint32_t* minVolume) const { return 0; }
   virtual int32_t MicrophoneVolumeStepSize(uint16_t* stepSize) const {
-    assert(false);
     return 0;
   }
-  virtual int32_t SpeakerMuteIsAvailable(bool* available) {
-    assert(false);
-    return 0;
-  }
-  virtual int32_t SetSpeakerMute(bool enable) {
-    assert(false);
-    return 0;
-  }
-  virtual int32_t SpeakerMute(bool* enabled) const {
-    assert(false);
-    return 0;
-  }
-  virtual int32_t MicrophoneMuteIsAvailable(bool* available) {
-    assert(false);
-    return 0;
-  }
-  virtual int32_t SetMicrophoneMute(bool enable) {
-    assert(false);
-    return 0;
-  }
-  virtual int32_t MicrophoneMute(bool* enabled) const {
-    assert(false);
-    return 0;
-  }
-  virtual int32_t MicrophoneBoostIsAvailable(bool* available) {
-    assert(false);
-    return 0;
-  }
-  virtual int32_t SetMicrophoneBoost(bool enable) {
-    assert(false);
-    return 0;
-  }
-  virtual int32_t MicrophoneBoost(bool* enabled) const {
-    assert(false);
-    return 0;
-  }
+  virtual int32_t SpeakerMuteIsAvailable(bool* available) { return 0; }
+  virtual int32_t SetSpeakerMute(bool enable) { return 0; }
+  virtual int32_t SpeakerMute(bool* enabled) const { return 0; }
+  virtual int32_t MicrophoneMuteIsAvailable(bool* available) { return 0; }
+  virtual int32_t SetMicrophoneMute(bool enable) { return 0; }
+  virtual int32_t MicrophoneMute(bool* enabled) const { return 0; }
+  virtual int32_t MicrophoneBoostIsAvailable(bool* available) { return 0; }
+  virtual int32_t SetMicrophoneBoost(bool enable) { return 0; }
+  virtual int32_t MicrophoneBoost(bool* enabled) const { return 0; }
   virtual int32_t StereoPlayoutIsAvailable(bool* available) const {
     *available = false;
     return 0;
   }
-  virtual int32_t StereoPlayout(bool* enabled) const {
-    assert(false);
-    return 0;
-  }
+  virtual int32_t StereoPlayout(bool* enabled) const { return 0; }
   virtual int32_t StereoRecordingIsAvailable(bool* available) const {
     *available = false;
     return 0;
   }
-  virtual int32_t StereoRecording(bool* enabled) const {
-    assert(false);
-    return 0;
-  }
-  virtual int32_t SetRecordingChannel(const ChannelType channel) {
-    assert(false);
-    return 0;
-  }
-  virtual int32_t RecordingChannel(ChannelType* channel) const {
-    assert(false);
-    return 0;
-  }
+  virtual int32_t StereoRecording(bool* enabled) const { return 0; }
+  virtual int32_t SetRecordingChannel(const ChannelType channel) { return 0; }
+  virtual int32_t RecordingChannel(ChannelType* channel) const { return 0; }
   virtual int32_t SetPlayoutBuffer(const BufferType type,
                            uint16_t sizeMS = 0) {
-    assert(false);
     return 0;
   }
   virtual int32_t PlayoutBuffer(BufferType* type, uint16_t* sizeMS) const {
-    assert(false);
     return 0;
   }
-  virtual int32_t PlayoutDelay(uint16_t* delayMS) const {
-    assert(false);
-    return 0;
-  }
-  virtual int32_t RecordingDelay(uint16_t* delayMS) const {
-    assert(false);
-    return 0;
-  }
-  virtual int32_t CPULoad(uint16_t* load) const {
-    assert(false);
-    return 0;
-  }
+  virtual int32_t PlayoutDelay(uint16_t* delayMS) const { return 0; }
+  virtual int32_t RecordingDelay(uint16_t* delayMS) const { return 0; }
+  virtual int32_t CPULoad(uint16_t* load) const { return 0; }
   virtual int32_t StartRawOutputFileRecording(
       const char pcmFileNameUTF8[kAdmMaxFileNameSize]) {
-    assert(false);
     return 0;
   }
-  virtual int32_t StopRawOutputFileRecording() {
-    assert(false);
-    return 0;
-  }
+  virtual int32_t StopRawOutputFileRecording() { return 0; }
   virtual int32_t StartRawInputFileRecording(
       const char pcmFileNameUTF8[kAdmMaxFileNameSize]) {
-    assert(false);
     return 0;
   }
-  virtual int32_t StopRawInputFileRecording() {
-    assert(false);
-    return 0;
-  }
+  virtual int32_t StopRawInputFileRecording() { return 0; }
   virtual int32_t SetRecordingSampleRate(const uint32_t samplesPerSec) {
-    assert(false);
     return 0;
   }
   virtual int32_t RecordingSampleRate(uint32_t* samplesPerSec) const {
-    assert(false);
     return 0;
   }
   virtual int32_t SetPlayoutSampleRate(const uint32_t samplesPerSec) {
-    assert(false);
     return 0;
   }
-  virtual int32_t PlayoutSampleRate(uint32_t* samplesPerSec) const {
-    assert(false);
-    return 0;
-  }
-  virtual int32_t ResetAudioDevice() {
-    assert(false);
-    return 0;
-  }
-  virtual int32_t SetLoudspeakerStatus(bool enable) {
-    assert(false);
-    return 0;
-  }
-  virtual int32_t GetLoudspeakerStatus(bool* enabled) const {
-    assert(false);
-    return 0;
-  }
-  virtual int32_t EnableBuiltInAEC(bool enable) {
-    assert(false);
-    return -1;
-  }
-  virtual bool BuiltInAECIsEnabled() const {
-    assert(false);
-    return false;
-  }
+  virtual int32_t PlayoutSampleRate(uint32_t* samplesPerSec) const { return 0; }
+  virtual int32_t ResetAudioDevice() { return 0; }
+  virtual int32_t SetLoudspeakerStatus(bool enable) { return 0; }
+  virtual int32_t GetLoudspeakerStatus(bool* enabled) const { return 0; }
+  virtual int32_t EnableBuiltInAEC(bool enable) { return -1; }
+  virtual bool BuiltInAECIsEnabled() const { return false; }
 };
 
 }  // namespace webrtc
diff --git a/modules/audio_processing/aec/aec_core.c b/modules/audio_processing/aec/aec_core.c
index 9227f10..bfa087c 100644
--- a/modules/audio_processing/aec/aec_core.c
+++ b/modules/audio_processing/aec/aec_core.c
@@ -1156,9 +1156,11 @@
     memcpy(efw, dfw, sizeof(efw));
   }
 
-  // Reset if error is significantly larger than nearend (13 dB).
-  if (seSum > (19.95f * sdSum)) {
-    memset(aec->wfBuf, 0, sizeof(aec->wfBuf));
+  if (!aec->extended_filter_enabled) {
+    // Reset if error is significantly larger than nearend (13 dB).
+    if (seSum > (19.95f * sdSum)) {
+      memset(aec->wfBuf, 0, sizeof(aec->wfBuf));
+    }
   }
 
   // Subband coherence
diff --git a/modules/audio_processing/audio_processing_impl.cc b/modules/audio_processing/audio_processing_impl.cc
index b33049e..2eb97bf 100644
--- a/modules/audio_processing/audio_processing_impl.cc
+++ b/modules/audio_processing/audio_processing_impl.cc
@@ -186,6 +186,10 @@
     (*it)->SetExtraOptions(config);
 }
 
+int AudioProcessingImpl::EnableExperimentalNs(bool enable) {
+  return kNoError;
+}
+
 int AudioProcessingImpl::set_sample_rate_hz(int rate) {
   CriticalSectionScoped crit_scoped(crit_);
   if (rate == sample_rate_hz_) {
diff --git a/modules/audio_processing/audio_processing_impl.h b/modules/audio_processing/audio_processing_impl.h
index b0afd6d..6c22878 100644
--- a/modules/audio_processing/audio_processing_impl.h
+++ b/modules/audio_processing/audio_processing_impl.h
@@ -59,6 +59,10 @@
   virtual int Initialize() OVERRIDE;
   virtual int InitializeLocked();
   virtual void SetExtraOptions(const Config& config) OVERRIDE;
+  virtual int EnableExperimentalNs(bool enable) OVERRIDE;
+  virtual bool experimental_ns_enabled() const OVERRIDE {
+    return false;
+  }
   virtual int set_sample_rate_hz(int rate) OVERRIDE;
   virtual int sample_rate_hz() const OVERRIDE;
   virtual int set_num_channels(int input_channels,
diff --git a/modules/audio_processing/include/audio_processing.h b/modules/audio_processing/include/audio_processing.h
index 1babe43..8297649 100644
--- a/modules/audio_processing/include/audio_processing.h
+++ b/modules/audio_processing/include/audio_processing.h
@@ -155,6 +155,9 @@
   // ensures the options are applied immediately.
   virtual void SetExtraOptions(const Config& config) = 0;
 
+  virtual int EnableExperimentalNs(bool enable) = 0;
+  virtual bool experimental_ns_enabled() const = 0;
+
   // Sets the sample |rate| in Hz for both the primary and reverse audio
   // streams. 8000, 16000 or 32000 Hz are permitted.
   virtual int set_sample_rate_hz(int rate) = 0;
diff --git a/modules/audio_processing/include/mock_audio_processing.h b/modules/audio_processing/include/mock_audio_processing.h
index 9a36fe8..c4600bd 100644
--- a/modules/audio_processing/include/mock_audio_processing.h
+++ b/modules/audio_processing/include/mock_audio_processing.h
@@ -183,6 +183,10 @@
       int());
   MOCK_METHOD1(SetExtraOptions,
       void(const Config& config));
+  MOCK_METHOD1(EnableExperimentalNs,
+      int(bool enable));
+  MOCK_CONST_METHOD0(experimental_ns_enabled,
+      bool());
   MOCK_METHOD1(set_sample_rate_hz,
       int(int rate));
   MOCK_CONST_METHOD0(sample_rate_hz,
diff --git a/modules/audio_processing/utility/delay_estimator.c b/modules/audio_processing/utility/delay_estimator.c
index 91c1e9b..062874d 100644
--- a/modules/audio_processing/utility/delay_estimator.c
+++ b/modules/audio_processing/utility/delay_estimator.c
@@ -196,15 +196,19 @@
 
   // Default return value if we're unable to estimate. -1 is used for errors.
   self->last_delay = -2;
+
+  self->robust_validation_enabled = 0;  // Disabled by default.
 }
 
 int WebRtc_ProcessBinarySpectrum(BinaryDelayEstimator* self,
                                  uint32_t binary_near_spectrum) {
   int i = 0;
   int candidate_delay = -1;
+  int valid_candidate = 0;
 
   int32_t value_best_candidate = 32 << 9;  // 32 in Q9, (max |mean_bit_counts|).
   int32_t value_worst_candidate = 0;
+  int32_t valley_depth = 0;
 
   assert(self != NULL);
   if (self->near_history_size > 1) {
@@ -249,6 +253,7 @@
       value_worst_candidate = self->mean_bit_counts[i];
     }
   }
+  valley_depth = value_worst_candidate - value_best_candidate;
 
   // The |value_best_candidate| is a good indicator on the probability of
   // |candidate_delay| being an accurate delay (a small |value_best_candidate|
@@ -265,7 +270,7 @@
 
   // Update |minimum_probability|.
   if ((self->minimum_probability > kProbabilityLowerLimit) &&
-      (value_worst_candidate - value_best_candidate > kProbabilityMinSpread)) {
+      (valley_depth > kProbabilityMinSpread)) {
     // The "hard" threshold can't be lower than 17 (in Q9).
     // The valley in the curve also has to be distinct, i.e., the
     // difference between |value_worst_candidate| and |value_best_candidate| has
@@ -281,14 +286,21 @@
   // Update |last_delay_probability|.
   // We use a Markov type model, i.e., a slowly increasing level over time.
   self->last_delay_probability++;
-  if (value_worst_candidate > value_best_candidate + kProbabilityOffset) {
-    // Reliable delay value for usage.
-    if (value_best_candidate < self->minimum_probability) {
-      self->last_delay = candidate_delay;
-    }
+  // Validate |candidate_delay|.  We have a reliable instantaneous delay
+  // estimate if
+  //  1) The valley is distinct enough (|valley_depth| > |kProbabilityOffset|)
+  // and
+  //  2) The depth of the valley is deep enough
+  //      (|value_best_candidate| < |minimum_probability|)
+  //     and deeper than the best estimate so far
+  //      (|value_best_candidate| < |last_delay_probability|)
+  valid_candidate = ((valley_depth > kProbabilityOffset) &&
+      ((value_best_candidate < self->minimum_probability) ||
+          (value_best_candidate < self->last_delay_probability)));
+
+  if (valid_candidate) {
+    self->last_delay = candidate_delay;
     if (value_best_candidate < self->last_delay_probability) {
-      self->last_delay = candidate_delay;
-      // Reset |last_delay_probability|.
       self->last_delay_probability = value_best_candidate;
     }
   }
diff --git a/modules/audio_processing/utility/delay_estimator.h b/modules/audio_processing/utility/delay_estimator.h
index bf2b08a..561514b 100644
--- a/modules/audio_processing/utility/delay_estimator.h
+++ b/modules/audio_processing/utility/delay_estimator.h
@@ -42,6 +42,9 @@
   // Delay memory.
   int last_delay;
 
+  // Robust validation
+  int robust_validation_enabled;
+
   // Far-end binary spectrum history buffer etc.
   BinaryDelayEstimatorFarend* farend;
 } BinaryDelayEstimator;
diff --git a/modules/audio_processing/utility/delay_estimator_unittest.cc b/modules/audio_processing/utility/delay_estimator_unittest.cc
index f4b4711..bdc199c 100644
--- a/modules/audio_processing/utility/delay_estimator_unittest.cc
+++ b/modules/audio_processing/utility/delay_estimator_unittest.cc
@@ -246,6 +246,17 @@
   EXPECT_EQ(-1, WebRtc_AddFarSpectrumFix(farend_handle_, far_u16_,
                                          spectrum_size_, 16));
 
+  // WebRtc_enable_robust_validation() should return -1 if we have:
+  // 1) NULL pointer as |handle|.
+  // 2) Incorrect |enable| value (not 0 or 1).
+  EXPECT_EQ(-1, WebRtc_enable_robust_validation(NULL, 0));
+  EXPECT_EQ(-1, WebRtc_enable_robust_validation(handle_, -1));
+  EXPECT_EQ(-1, WebRtc_enable_robust_validation(handle_, 2));
+
+  // WebRtc_is_robust_validation_enabled() should return -1 if we have NULL
+  // pointer as |handle|.
+  EXPECT_EQ(-1, WebRtc_is_robust_validation_enabled(NULL));
+
   // WebRtc_DelayEstimatorProcessFloat() should return -1 if we have:
   // 1) NULL pointer as |handle|.
   // 2) NULL pointer as near-end spectrum.
@@ -283,6 +294,16 @@
   WebRtc_FreeDelayEstimator(handle);
 }
 
+TEST_F(DelayEstimatorTest, VerifyEnableRobustValidation) {
+  Init();
+  // Disabled by default.
+  EXPECT_EQ(0, WebRtc_is_robust_validation_enabled(handle_));
+  for (int i = 1; i >= 0; i--) {
+    EXPECT_EQ(0, WebRtc_enable_robust_validation(handle_, i));
+    EXPECT_EQ(i, WebRtc_is_robust_validation_enabled(handle_));
+  }
+}
+
 TEST_F(DelayEstimatorTest, InitializedSpectrumAfterProcess) {
   // In this test we verify that the mean spectra are initialized after first
   // time we call WebRtc_AddFarSpectrum() and Process() respectively.
diff --git a/modules/audio_processing/utility/delay_estimator_wrapper.c b/modules/audio_processing/utility/delay_estimator_wrapper.c
index b72b8ff..ce44318 100644
--- a/modules/audio_processing/utility/delay_estimator_wrapper.c
+++ b/modules/audio_processing/utility/delay_estimator_wrapper.c
@@ -312,6 +312,30 @@
   return 0;
 }
 
+int WebRtc_enable_robust_validation(void* handle, int enable) {
+  DelayEstimator* self = (DelayEstimator*) handle;
+
+  if (self == NULL) {
+    return -1;
+  }
+  if ((enable < 0) || (enable > 1)) {
+    return -1;
+  }
+  assert(self->binary_handle != NULL);
+  self->binary_handle->robust_validation_enabled = enable;
+  return 0;
+}
+
+int WebRtc_is_robust_validation_enabled(void* handle) {
+  DelayEstimator* self = (DelayEstimator*) handle;
+
+  if (self == NULL) {
+    return -1;
+  }
+  assert(self->binary_handle != NULL);
+  return self->binary_handle->robust_validation_enabled;
+}
+
 int WebRtc_DelayEstimatorProcessFix(void* handle,
                                     uint16_t* near_spectrum,
                                     int spectrum_size,
diff --git a/modules/audio_processing/utility/delay_estimator_wrapper.h b/modules/audio_processing/utility/delay_estimator_wrapper.h
index 51b9a0a..50bcdde 100644
--- a/modules/audio_processing/utility/delay_estimator_wrapper.h
+++ b/modules/audio_processing/utility/delay_estimator_wrapper.h
@@ -123,6 +123,18 @@
 //
 int WebRtc_InitDelayEstimator(void* handle);
 
+// TODO(bjornv): Implement this functionality.  Currently, enabling it has no
+// impact, hence this is an empty API.
+// Enables/Disables a robust validation functionality in the delay estimation.
+// This is by default disabled upon initialization.
+// Inputs:
+//      - handle        : Pointer to the delay estimation instance.
+//      - enable        : Enable (1) or disable (0) this feature.
+int WebRtc_enable_robust_validation(void* handle, int enable);
+
+// Returns 1 if robust validation is enabled and 0 if disabled.
+int WebRtc_is_robust_validation_enabled(void* handle);
+
 // Estimates and returns the delay between the far-end and near-end blocks. The
 // value will be offset by the lookahead (i.e. the lookahead should be
 // subtracted from the returned value).
diff --git a/modules/desktop_capture/desktop_and_cursor_composer.cc b/modules/desktop_capture/desktop_and_cursor_composer.cc
index 3141fee..6ed3ae8 100644
--- a/modules/desktop_capture/desktop_and_cursor_composer.cc
+++ b/modules/desktop_capture/desktop_and_cursor_composer.cc
@@ -51,6 +51,73 @@
   }
 }
 
+// DesktopFrame wrapper that draws mouse on a frame and restores original
+// content before releasing the underlying frame.
+class DesktopFrameWithCursor : public DesktopFrame {
+ public:
+  // Takes ownership of |frame|.
+  DesktopFrameWithCursor(DesktopFrame* frame,
+                         const MouseCursor& cursor,
+                         const DesktopVector& position);
+  virtual ~DesktopFrameWithCursor();
+
+ private:
+  scoped_ptr<DesktopFrame> original_frame_;
+
+  DesktopVector restore_position_;
+  scoped_ptr<DesktopFrame> restore_frame_;
+
+  DISALLOW_COPY_AND_ASSIGN(DesktopFrameWithCursor);
+};
+
+DesktopFrameWithCursor::DesktopFrameWithCursor(DesktopFrame* frame,
+                                               const MouseCursor& cursor,
+                                               const DesktopVector& position)
+    : DesktopFrame(frame->size(), frame->stride(),
+                   frame->data(), frame->shared_memory()),
+      original_frame_(frame) {
+  set_dpi(frame->dpi());
+  set_capture_time_ms(frame->capture_time_ms());
+  mutable_updated_region()->Swap(frame->mutable_updated_region());
+
+  DesktopVector image_pos = position.subtract(cursor.hotspot());
+  DesktopRect target_rect = DesktopRect::MakeSize(cursor.image().size());
+  target_rect.Translate(image_pos);
+  DesktopVector target_origin = target_rect.top_left();
+  target_rect.IntersectWith(DesktopRect::MakeSize(size()));
+
+  if (target_rect.is_empty())
+    return;
+
+  // Copy original screen content under cursor to |restore_frame_|.
+  restore_position_ = target_rect.top_left();
+  restore_frame_.reset(new BasicDesktopFrame(target_rect.size()));
+  restore_frame_->CopyPixelsFrom(*this, target_rect.top_left(),
+                                 DesktopRect::MakeSize(restore_frame_->size()));
+
+  // Blit the cursor.
+  uint8_t* target_rect_data = reinterpret_cast<uint8_t*>(data()) +
+                              target_rect.top() * stride() +
+                              target_rect.left() * DesktopFrame::kBytesPerPixel;
+  DesktopVector origin_shift = target_rect.top_left().subtract(target_origin);
+  AlphaBlend(target_rect_data, stride(),
+             cursor.image().data() +
+                 origin_shift.y() * cursor.image().stride() +
+                 origin_shift.x() * DesktopFrame::kBytesPerPixel,
+             cursor.image().stride(),
+             target_rect.size());
+}
+
+DesktopFrameWithCursor::~DesktopFrameWithCursor() {
+  // Restore original content of the frame.
+  if (restore_frame_.get()) {
+    DesktopRect target_rect = DesktopRect::MakeSize(restore_frame_->size());
+    target_rect.Translate(restore_position_);
+    CopyPixelsFrom(restore_frame_->data(), restore_frame_->stride(),
+                   target_rect);
+  }
+}
+
 }  // namespace
 
 DesktopAndCursorComposer::DesktopAndCursorComposer(
@@ -81,22 +148,9 @@
 
 void DesktopAndCursorComposer::OnCaptureCompleted(DesktopFrame* frame) {
   if (cursor_.get() && cursor_state_ == MouseCursorMonitor::INSIDE) {
-    DesktopVector image_pos = cursor_position_.subtract(cursor_->hotspot());
-    DesktopRect target_rect = DesktopRect::MakeSize(cursor_->image().size());
-    target_rect.Translate(image_pos);
-    DesktopVector target_origin = target_rect.top_left();
-    target_rect.IntersectWith(DesktopRect::MakeSize(frame->size()));
-    DesktopVector origin_shift = target_rect.top_left().subtract(target_origin);
-    int cursor_width = cursor_->image().size().width();
-    AlphaBlend(reinterpret_cast<uint8_t*>(frame->data()) +
-                   target_rect.top() * frame->stride() +
-                   target_rect.left() * DesktopFrame::kBytesPerPixel,
-               frame->stride(),
-               cursor_->image().data() +
-                   (origin_shift.y() * cursor_width + origin_shift.x()) *
-                       DesktopFrame::kBytesPerPixel,
-               cursor_width * DesktopFrame::kBytesPerPixel,
-               target_rect.size());
+    DesktopFrameWithCursor* frame_with_cursor =
+        new DesktopFrameWithCursor(frame, *cursor_, cursor_position_);
+    frame = frame_with_cursor;
   }
 
   callback_->OnCaptureCompleted(frame);
diff --git a/modules/desktop_capture/desktop_and_cursor_composer_unittest.cc b/modules/desktop_capture/desktop_and_cursor_composer_unittest.cc
index 0bb7237..15d6f54 100644
--- a/modules/desktop_capture/desktop_and_cursor_composer_unittest.cc
+++ b/modules/desktop_capture/desktop_and_cursor_composer_unittest.cc
@@ -14,6 +14,7 @@
 #include "webrtc/modules/desktop_capture/desktop_capture_options.h"
 #include "webrtc/modules/desktop_capture/desktop_frame.h"
 #include "webrtc/modules/desktop_capture/mouse_cursor.h"
+#include "webrtc/modules/desktop_capture/shared_desktop_frame.h"
 #include "webrtc/modules/desktop_capture/window_capturer.h"
 #include "webrtc/system_wrappers/interface/logging.h"
 #include "webrtc/system_wrappers/interface/scoped_ptr.h"
@@ -74,11 +75,19 @@
         *(data++) = GetFakeFramePixelValue(DesktopVector(x, y));
       }
     }
-    callback_->OnCaptureCompleted(frame);
+
+    last_frame_.reset(SharedDesktopFrame::Wrap(frame));
+
+    callback_->OnCaptureCompleted(last_frame_->Share());
   }
 
+  // Returns last fake captured frame.
+  SharedDesktopFrame* last_frame() { return last_frame_.get(); }
+
  private:
   Callback* callback_;
+
+  scoped_ptr<SharedDesktopFrame> last_frame_;
 };
 
 class FakeMouseMonitor : public MouseCursorMonitor {
@@ -155,8 +164,9 @@
                                      public DesktopCapturer::Callback {
  public:
   DesktopAndCursorComposerTest()
-      : fake_cursor_(new FakeMouseMonitor()),
-        blender_(new FakeScreenCapturer(), fake_cursor_) {
+      : fake_screen_(new FakeScreenCapturer()),
+        fake_cursor_(new FakeMouseMonitor()),
+        blender_(fake_screen_, fake_cursor_) {
   }
 
   // DesktopCapturer::Callback interface
@@ -170,7 +180,9 @@
 
  protected:
   // Owned by |blender_|.
+  FakeScreenCapturer* fake_screen_;
   FakeMouseMonitor* fake_cursor_;
+
   DesktopAndCursorComposer blender_;
   scoped_ptr<DesktopFrame> frame_;
 };
@@ -213,6 +225,13 @@
     blender_.Capture(DesktopRegion());
 
     VerifyFrame(*frame_, state, pos);
+
+    // Verify that the cursor is erased before the frame buffer is returned to
+    // the screen capturer.
+    frame_.reset();
+    VerifyFrame(*fake_screen_->last_frame(),
+                MouseCursorMonitor::OUTSIDE,
+                DesktopVector());
   }
 }
 
diff --git a/modules/desktop_capture/desktop_frame.cc b/modules/desktop_capture/desktop_frame.cc
index f293baf..f26dc93 100644
--- a/modules/desktop_capture/desktop_frame.cc
+++ b/modules/desktop_capture/desktop_frame.cc
@@ -10,6 +10,7 @@
 
 #include "webrtc/modules/desktop_capture/desktop_frame.h"
 
+#include <assert.h>
 #include <string.h>
 
 namespace webrtc {
@@ -27,6 +28,30 @@
 
 DesktopFrame::~DesktopFrame() {}
 
+void DesktopFrame::CopyPixelsFrom(uint8_t* src_buffer, int src_stride,
+                                  const DesktopRect& dest_rect) {
+  assert(DesktopRect::MakeSize(size()).ContainsRect(dest_rect));
+
+  uint8_t* dest = data() + stride() * dest_rect.top() +
+                  DesktopFrame::kBytesPerPixel * dest_rect.left();
+  for (int y = 0; y < dest_rect.height(); ++y) {
+    memcpy(dest, src_buffer, DesktopFrame::kBytesPerPixel * dest_rect.width());
+    src_buffer += src_stride;
+    dest += stride();
+  }
+}
+
+void DesktopFrame::CopyPixelsFrom(const DesktopFrame& src_frame,
+                                  const DesktopVector& src_pos,
+                                  const DesktopRect& dest_rect) {
+  assert(DesktopRect::MakeSize(src_frame.size()).ContainsRect(
+      DesktopRect::MakeOriginSize(src_pos, dest_rect.size())));
+
+  CopyPixelsFrom(src_frame.data() + src_frame.stride() * src_pos.y() +
+                     DesktopFrame::kBytesPerPixel * src_pos.x(),
+                 src_frame.stride(), dest_rect);
+}
+
 BasicDesktopFrame::BasicDesktopFrame(DesktopSize size)
     : DesktopFrame(size, kBytesPerPixel * size.width(),
                    new uint8_t[kBytesPerPixel * size.width() * size.height()],
diff --git a/modules/desktop_capture/desktop_frame.h b/modules/desktop_capture/desktop_frame.h
index b420a3c..a38359a 100644
--- a/modules/desktop_capture/desktop_frame.h
+++ b/modules/desktop_capture/desktop_frame.h
@@ -53,6 +53,14 @@
   int32_t capture_time_ms() const { return capture_time_ms_; }
   void set_capture_time_ms(int32_t time_ms) { capture_time_ms_ = time_ms; }
 
+  // Copies pixels from a buffer or another frame. |dest_rect| rect must lay
+  // within bounds of this frame.
+  void CopyPixelsFrom(uint8_t* src_buffer, int src_stride,
+                      const DesktopRect& dest_rect);
+  void CopyPixelsFrom(const DesktopFrame& src_frame,
+                      const DesktopVector& src_pos,
+                      const DesktopRect& dest_rect);
+
  protected:
   DesktopFrame(DesktopSize size,
                int stride,
diff --git a/modules/desktop_capture/desktop_geometry.cc b/modules/desktop_capture/desktop_geometry.cc
index cffaefe..1ff7c68 100644
--- a/modules/desktop_capture/desktop_geometry.cc
+++ b/modules/desktop_capture/desktop_geometry.cc
@@ -19,6 +19,11 @@
          point.y() >= top() && point.y() < bottom();
 }
 
+bool DesktopRect::ContainsRect(const DesktopRect& rect) const {
+  return rect.left() >= left() && rect.right() <= right() &&
+         rect.top() >= top() && rect.bottom() <= bottom();
+}
+
 void DesktopRect::IntersectWith(const DesktopRect& rect) {
   left_ = std::max(left(), rect.left());
   top_ = std::max(top(), rect.top());
diff --git a/modules/desktop_capture/desktop_geometry.h b/modules/desktop_capture/desktop_geometry.h
index 580e0bf..e51273d 100644
--- a/modules/desktop_capture/desktop_geometry.h
+++ b/modules/desktop_capture/desktop_geometry.h
@@ -91,6 +91,10 @@
                               int32_t right, int32_t bottom) {
     return DesktopRect(left, top, right, bottom);
   }
+  static DesktopRect MakeOriginSize(const DesktopVector& origin,
+                                    const DesktopSize& size) {
+    return MakeXYWH(origin.x(), origin.y(), size.width(), size.height());
+  }
 
   DesktopRect() : left_(0), top_(0), right_(0), bottom_(0) {}
 
@@ -114,6 +118,9 @@
   // Returns true if |point| lies within the rectangle boundaries.
   bool Contains(const DesktopVector& point) const;
 
+  // Returns true if |rect| lies within the boundaries of this rectangle.
+  bool ContainsRect(const DesktopRect& rect) const;
+
   // Finds intersection with |rect|.
   void IntersectWith(const DesktopRect& rect);
 
diff --git a/modules/desktop_capture/screen_capturer_win.cc b/modules/desktop_capture/screen_capturer_win.cc
index 42a7192..a9bcd48 100644
--- a/modules/desktop_capture/screen_capturer_win.cc
+++ b/modules/desktop_capture/screen_capturer_win.cc
@@ -337,10 +337,13 @@
   scoped_ptr<MouseCursorShape> cursor(new MouseCursorShape);
   cursor->hotspot = cursor_image->hotspot();
   cursor->size = cursor_image->image().size();
-  cursor->data.assign(
-    cursor_image->image().data(),
-    cursor_image->image().data() +
-        cursor_image->image().stride() * DesktopFrame::kBytesPerPixel);
+  uint8_t* current_row = cursor_image->image().data();
+  for (int y = 0; y < cursor_image->image().size().height(); ++y) {
+    cursor->data.append(current_row,
+                        current_row + cursor_image->image().size().width() *
+                                        DesktopFrame::kBytesPerPixel);
+    current_row += cursor_image->image().stride();
+  }
 
   // Compare the current cursor with the last one we sent to the client. If
   // they're the same, then don't bother sending the cursor again.
diff --git a/modules/desktop_capture/screen_capturer_x11.cc b/modules/desktop_capture/screen_capturer_x11.cc
index 97bdb65..c5a4c8c 100644
--- a/modules/desktop_capture/screen_capturer_x11.cc
+++ b/modules/desktop_capture/screen_capturer_x11.cc
@@ -462,14 +462,7 @@
   DCHECK(current != last);
   for (DesktopRegion::Iterator it(last_invalid_region_);
        !it.IsAtEnd(); it.Advance()) {
-    const DesktopRect& r = it.rect();
-    int offset = r.top() * current->stride() +
-        r.left() * DesktopFrame::kBytesPerPixel;
-    for (int i = 0; i < r.height(); ++i) {
-      memcpy(current->data() + offset, last->data() + offset,
-             r.width() * DesktopFrame::kBytesPerPixel);
-      offset += current->size().width() * DesktopFrame::kBytesPerPixel;
-    }
+    current->CopyPixelsFrom(*last, it.rect().top_left(), it.rect());
   }
 }
 
diff --git a/modules/media_file/source/media_file.gypi b/modules/media_file/source/media_file.gypi
index 0d1b15f..3add36c 100644
--- a/modules/media_file/source/media_file.gypi
+++ b/modules/media_file/source/media_file.gypi
@@ -26,6 +26,9 @@
           '../interface',
           '../../interface',
         ],
+        'defines': [
+          'WEBRTC_MODULE_UTILITY_VIDEO',
+        ],
       },
       'sources': [
         '../interface/media_file.h',
diff --git a/modules/media_file/source/media_file_utility.cc b/modules/media_file/source/media_file_utility.cc
index 04022ad..85df0b3 100644
--- a/modules/media_file/source/media_file_utility.cc
+++ b/modules/media_file/source/media_file_utility.cc
@@ -8,6 +8,8 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
+#include "webrtc/modules/media_file/source/media_file_utility.h"
+
 #include <assert.h>
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -15,7 +17,6 @@
 #include "webrtc/common_types.h"
 #include "webrtc/engine_configurations.h"
 #include "webrtc/modules/interface/module_common_types.h"
-#include "webrtc/modules/media_file/source/media_file_utility.h"
 #include "webrtc/system_wrappers/interface/file_wrapper.h"
 #include "webrtc/system_wrappers/interface/trace.h"
 
diff --git a/modules/modules.gyp b/modules/modules.gyp
index 0888263..65c4827 100644
--- a/modules/modules.gyp
+++ b/modules/modules.gyp
@@ -184,6 +184,8 @@
             'remote_bitrate_estimator/test/bwe_test_framework_unittest.cc',
             'remote_bitrate_estimator/test/bwe_test_logging.cc',
             'remote_bitrate_estimator/test/bwe_test_logging.h',
+            'remote_bitrate_estimator/test/bwe_test.cc',
+            'remote_bitrate_estimator/test/bwe_test.h',
             'rtp_rtcp/source/mock/mock_rtp_payload_strategy.h',
             'rtp_rtcp/source/fec_receiver_unittest.cc',
             'rtp_rtcp/source/fec_test_helper.cc',
diff --git a/modules/pacing/include/paced_sender.h b/modules/pacing/include/paced_sender.h
index a0c1f1d..44e8144 100644
--- a/modules/pacing/include/paced_sender.h
+++ b/modules/pacing/include/paced_sender.h
@@ -52,6 +52,9 @@
    protected:
     virtual ~Callback() {}
   };
+
+  static const int kDefaultMaxQueueLengthMs = 2000;
+
   PacedSender(Callback* callback, int target_bitrate_kbps,
               float pace_multiplier);
 
@@ -85,6 +88,10 @@
                           int bytes,
                           bool retransmission);
 
+  // Sets the max length of the pacer queue in milliseconds.
+  // A negative queue size is interpreted as infinite.
+  virtual void set_max_queue_length_ms(int max_queue_length_ms);
+
   // Returns the time since the oldest queued packet was captured.
   virtual int QueueInMs() const;
 
@@ -105,6 +112,8 @@
       uint32_t* ssrc, uint16_t* sequence_number, int64_t* capture_time_ms,
       bool* retransmission);
 
+  bool SendPacketFromList(paced_sender::PacketList* packet_list);
+
   // Updates the number of bytes that can be sent for the next time interval.
   void UpdateBytesPerInterval(uint32_t delta_time_in_ms);
 
@@ -115,6 +124,7 @@
   const float pace_multiplier_;
   bool enabled_;
   bool paused_;
+  int max_queue_length_ms_;
   scoped_ptr<CriticalSectionWrapper> critsect_;
   // This is the media budget, keeping track of how many bits of media
   // we can pace out during the current interval.
diff --git a/modules/pacing/paced_sender.cc b/modules/pacing/paced_sender.cc
index ed9d29a..7d4e81b 100644
--- a/modules/pacing/paced_sender.cc
+++ b/modules/pacing/paced_sender.cc
@@ -107,7 +107,7 @@
 
   void UseBudget(int bytes) {
     bytes_remaining_ = std::max(bytes_remaining_ - bytes,
-                                -100 * target_rate_kbps_ / 8);
+                                -500 * target_rate_kbps_ / 8);
   }
 
   int bytes_remaining() const { return bytes_remaining_; }
@@ -124,6 +124,7 @@
       pace_multiplier_(pace_multiplier),
       enabled_(false),
       paused_(false),
+      max_queue_length_ms_(kDefaultMaxQueueLengthMs),
       critsect_(CriticalSectionWrapper::CreateCriticalSection()),
       media_budget_(new paced_sender::IntervalBudget(
           pace_multiplier_ * target_bitrate_kbps)),
@@ -206,6 +207,11 @@
   return false;
 }
 
+void PacedSender::set_max_queue_length_ms(int max_queue_length_ms) {
+  CriticalSectionScoped cs(critsect_.get());
+  max_queue_length_ms_ = max_queue_length_ms;
+}
+
 int PacedSender::QueueInMs() const {
   CriticalSectionScoped cs(critsect_.get());
   int64_t now_ms = TickTime::MillisecondTimestamp();
@@ -254,36 +260,10 @@
       uint32_t delta_time_ms = std::min(kMaxIntervalTimeMs, elapsed_time_ms);
       UpdateBytesPerInterval(delta_time_ms);
     }
-    uint32_t ssrc;
-    uint16_t sequence_number;
-    int64_t capture_time_ms;
-    bool retransmission;
     paced_sender::PacketList* packet_list;
     while (ShouldSendNextPacket(&packet_list)) {
-      GetNextPacketFromList(packet_list, &ssrc, &sequence_number,
-                            &capture_time_ms, &retransmission);
-      critsect_->Leave();
-
-      const bool success = callback_->TimeToSendPacket(ssrc, sequence_number,
-                                                       capture_time_ms,
-                                                       retransmission);
-      critsect_->Enter();
-      // If packet cannot be sent then keep it in packet list and exit early.
-      // There's no need to send more packets.
-      if (!success) {
+      if (!SendPacketFromList(packet_list))
         return 0;
-      }
-      packet_list->pop_front();
-      const bool last_packet = packet_list->empty() ||
-          packet_list->front().capture_time_ms_ > capture_time_ms;
-      if (packet_list != high_priority_packets_.get()) {
-        if (capture_time_ms > capture_time_ms_last_sent_) {
-          capture_time_ms_last_sent_ = capture_time_ms;
-        } else if (capture_time_ms == capture_time_ms_last_sent_ &&
-                   last_packet) {
-          TRACE_EVENT_ASYNC_END0("webrtc_rtp", "PacedSend", capture_time_ms);
-        }
-      }
     }
     if (high_priority_packets_->empty() &&
         normal_priority_packets_->empty() &&
@@ -305,6 +285,39 @@
 }
 
 // MUST have critsect_ when calling.
+bool PacedSender::SendPacketFromList(paced_sender::PacketList* packet_list) {
+  uint32_t ssrc;
+  uint16_t sequence_number;
+  int64_t capture_time_ms;
+  bool retransmission;
+  GetNextPacketFromList(packet_list, &ssrc, &sequence_number,
+                        &capture_time_ms, &retransmission);
+  critsect_->Leave();
+
+  const bool success = callback_->TimeToSendPacket(ssrc, sequence_number,
+                                                   capture_time_ms,
+                                                   retransmission);
+  critsect_->Enter();
+  // If packet cannot be sent then keep it in packet list and exit early.
+  // There's no need to send more packets.
+  if (!success) {
+    return false;
+  }
+  packet_list->pop_front();
+  const bool last_packet = packet_list->empty() ||
+      packet_list->front().capture_time_ms_ > capture_time_ms;
+  if (packet_list != high_priority_packets_.get()) {
+    if (capture_time_ms > capture_time_ms_last_sent_) {
+      capture_time_ms_last_sent_ = capture_time_ms;
+    } else if (capture_time_ms == capture_time_ms_last_sent_ &&
+               last_packet) {
+      TRACE_EVENT_ASYNC_END0("webrtc_rtp", "PacedSend", capture_time_ms);
+    }
+  }
+  return true;
+}
+
+// MUST have critsect_ when calling.
 void PacedSender::UpdateBytesPerInterval(uint32_t delta_time_ms) {
   media_budget_->IncreaseBudget(delta_time_ms);
   padding_budget_->IncreaseBudget(delta_time_ms);
@@ -313,6 +326,7 @@
 
 // MUST have critsect_ when calling.
 bool PacedSender::ShouldSendNextPacket(paced_sender::PacketList** packet_list) {
+  *packet_list = NULL;
   if (media_budget_->bytes_remaining() <= 0) {
     // All bytes consumed for this interval.
     // Check if we have not sent in a too long time.
@@ -327,6 +341,22 @@
         return true;
       }
     }
+    // Send any old packets to avoid queuing for too long.
+    if (max_queue_length_ms_ >= 0 && QueueInMs() > max_queue_length_ms_) {
+      int64_t high_priority_capture_time = -1;
+      if (!high_priority_packets_->empty()) {
+        high_priority_capture_time =
+            high_priority_packets_->front().capture_time_ms_;
+        *packet_list = high_priority_packets_.get();
+      }
+      if (!normal_priority_packets_->empty() &&
+          (high_priority_capture_time == -1 || high_priority_capture_time >
+          normal_priority_packets_->front().capture_time_ms_)) {
+        *packet_list = normal_priority_packets_.get();
+      }
+      if (*packet_list)
+        return true;
+    }
     return false;
   }
   if (!high_priority_packets_->empty()) {
diff --git a/modules/pacing/paced_sender_unittest.cc b/modules/pacing/paced_sender_unittest.cc
index 66a3383..655c03d 100644
--- a/modules/pacing/paced_sender_unittest.cc
+++ b/modules/pacing/paced_sender_unittest.cc
@@ -83,53 +83,50 @@
 TEST_F(PacedSenderTest, QueuePacket) {
   uint32_t ssrc = 12345;
   uint16_t sequence_number = 1234;
-  int64_t capture_time_ms = 56789;
   // Due to the multiplicative factor we can send 3 packets not 2 packets.
   SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++,
-                      capture_time_ms, 250, false);
+                      TickTime::MillisecondTimestamp(), 250, false);
   SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++,
-                      capture_time_ms, 250, false);
+                      TickTime::MillisecondTimestamp(), 250, false);
   SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++,
-                      capture_time_ms, 250, false);
+                      TickTime::MillisecondTimestamp(), 250, false);
+  int64_t queued_packet_timestamp = TickTime::MillisecondTimestamp();
   EXPECT_FALSE(send_bucket_->SendPacket(PacedSender::kNormalPriority, ssrc,
-      sequence_number, capture_time_ms, 250, false));
+      sequence_number, queued_packet_timestamp, 250, false));
   send_bucket_->Process();
   EXPECT_EQ(5, send_bucket_->TimeUntilNextProcess());
   EXPECT_CALL(callback_, TimeToSendPadding(_)).Times(0);
-  EXPECT_CALL(callback_,
-      TimeToSendPacket(ssrc, sequence_number, capture_time_ms, false)).Times(0);
   TickTime::AdvanceFakeClock(4);
   EXPECT_EQ(1, send_bucket_->TimeUntilNextProcess());
   TickTime::AdvanceFakeClock(1);
   EXPECT_EQ(0, send_bucket_->TimeUntilNextProcess());
   EXPECT_CALL(callback_, TimeToSendPacket(
-      ssrc, sequence_number++, capture_time_ms, false))
+      ssrc, sequence_number++, queued_packet_timestamp, false))
       .Times(1)
       .WillRepeatedly(Return(true));
   send_bucket_->Process();
   sequence_number++;
   SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++,
-                      capture_time_ms, 250, false);
+                      TickTime::MillisecondTimestamp(), 250, false);
   SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++,
-                      capture_time_ms, 250, false);
+                      TickTime::MillisecondTimestamp(), 250, false);
   EXPECT_FALSE(send_bucket_->SendPacket(PacedSender::kNormalPriority, ssrc,
-      sequence_number++, capture_time_ms, 250, false));
+      sequence_number++, TickTime::MillisecondTimestamp(), 250, false));
   send_bucket_->Process();
 }
 
 TEST_F(PacedSenderTest, PaceQueuedPackets) {
   uint32_t ssrc = 12345;
   uint16_t sequence_number = 1234;
-  int64_t capture_time_ms = 56789;
 
   // Due to the multiplicative factor we can send 3 packets not 2 packets.
   for (int i = 0; i < 3; ++i) {
     SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++,
-                        capture_time_ms, 250, false);
+                        TickTime::MillisecondTimestamp(), 250, false);
   }
   for (int j = 0; j < 30; ++j) {
     EXPECT_FALSE(send_bucket_->SendPacket(PacedSender::kNormalPriority, ssrc,
-        sequence_number++, capture_time_ms, 250, false));
+        sequence_number++, TickTime::MillisecondTimestamp(), 250, false));
   }
   send_bucket_->Process();
   EXPECT_CALL(callback_, TimeToSendPadding(_)).Times(0);
@@ -137,7 +134,7 @@
     EXPECT_EQ(5, send_bucket_->TimeUntilNextProcess());
     TickTime::AdvanceFakeClock(5);
     EXPECT_CALL(callback_,
-        TimeToSendPacket(ssrc, _, capture_time_ms, false))
+        TimeToSendPacket(ssrc, _, _, false))
         .Times(3)
         .WillRepeatedly(Return(true));
     EXPECT_EQ(0, send_bucket_->TimeUntilNextProcess());
@@ -148,35 +145,34 @@
   EXPECT_EQ(0, send_bucket_->TimeUntilNextProcess());
   EXPECT_EQ(0, send_bucket_->Process());
   SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++,
-                      capture_time_ms, 250, false);
+                      TickTime::MillisecondTimestamp(), 250, false);
   SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++,
-                      capture_time_ms, 250, false);
+                      TickTime::MillisecondTimestamp(), 250, false);
   SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++,
-                      capture_time_ms, 250, false);
+                      TickTime::MillisecondTimestamp(), 250, false);
   EXPECT_FALSE(send_bucket_->SendPacket(PacedSender::kNormalPriority, ssrc,
-      sequence_number, capture_time_ms, 250, false));
+      sequence_number, TickTime::MillisecondTimestamp(), 250, false));
   send_bucket_->Process();
 }
 
 TEST_F(PacedSenderTest, PaceQueuedPacketsWithDuplicates) {
   uint32_t ssrc = 12345;
   uint16_t sequence_number = 1234;
-  int64_t capture_time_ms = 56789;
   uint16_t queued_sequence_number;
 
   // Due to the multiplicative factor we can send 3 packets not 2 packets.
   for (int i = 0; i < 3; ++i) {
     SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++,
-                        capture_time_ms, 250, false);
+                        TickTime::MillisecondTimestamp(), 250, false);
   }
   queued_sequence_number = sequence_number;
 
   for (int j = 0; j < 30; ++j) {
     // Send in duplicate packets.
     EXPECT_FALSE(send_bucket_->SendPacket(PacedSender::kNormalPriority, ssrc,
-        sequence_number, capture_time_ms, 250, false));
+        sequence_number, TickTime::MillisecondTimestamp(), 250, false));
     EXPECT_FALSE(send_bucket_->SendPacket(PacedSender::kNormalPriority, ssrc,
-        sequence_number++, capture_time_ms, 250, false));
+        sequence_number++, TickTime::MillisecondTimestamp(), 250, false));
   }
   EXPECT_CALL(callback_, TimeToSendPadding(_)).Times(0);
   send_bucket_->Process();
@@ -186,7 +182,8 @@
 
     for (int i = 0; i < 3; ++i) {
       EXPECT_CALL(callback_, TimeToSendPacket(ssrc, queued_sequence_number++,
-                                              capture_time_ms, false))
+                                              _,
+                                              false))
           .Times(1)
           .WillRepeatedly(Return(true));
    }
@@ -198,29 +195,28 @@
   EXPECT_EQ(0, send_bucket_->TimeUntilNextProcess());
   EXPECT_EQ(0, send_bucket_->Process());
   SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++,
-                      capture_time_ms, 250, false);
+                      TickTime::MillisecondTimestamp(), 250, false);
   SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++,
-                      capture_time_ms, 250, false);
+                      TickTime::MillisecondTimestamp(), 250, false);
   SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++,
-                      capture_time_ms, 250, false);
+                      TickTime::MillisecondTimestamp(), 250, false);
   EXPECT_FALSE(send_bucket_->SendPacket(PacedSender::kNormalPriority, ssrc,
-      sequence_number++, capture_time_ms, 250, false));
+      sequence_number++, TickTime::MillisecondTimestamp(), 250, false));
   send_bucket_->Process();
 }
 
 TEST_F(PacedSenderTest, Padding) {
   uint32_t ssrc = 12345;
   uint16_t sequence_number = 1234;
-  int64_t capture_time_ms = 56789;
 
   send_bucket_->UpdateBitrate(kTargetBitrate, kTargetBitrate, kTargetBitrate);
   // Due to the multiplicative factor we can send 3 packets not 2 packets.
   SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++,
-                      capture_time_ms, 250, false);
+                      TickTime::MillisecondTimestamp(), 250, false);
   SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++,
-                      capture_time_ms, 250, false);
+                      TickTime::MillisecondTimestamp(), 250, false);
   SendAndExpectPacket(PacedSender::kNormalPriority, ssrc, sequence_number++,
-                      capture_time_ms, 250, false);
+                      TickTime::MillisecondTimestamp(), 250, false);
   // No padding is expected since we have sent too much already.
   EXPECT_CALL(callback_, TimeToSendPadding(_)).Times(0);
   EXPECT_EQ(5, send_bucket_->TimeUntilNextProcess());
@@ -492,5 +488,33 @@
   EXPECT_EQ(0, send_bucket_->QueueInMs());
 }
 
+TEST_F(PacedSenderTest, MaxQueueLength) {
+  uint32_t ssrc = 12346;
+  uint16_t sequence_number = 1234;
+  EXPECT_EQ(0, send_bucket_->QueueInMs());
+
+  send_bucket_->UpdateBitrate(30, 0, 0);
+  for (int i = 0; i < 30; ++i) {
+    SendAndExpectPacket(PacedSender::kNormalPriority,
+                        ssrc,
+                        sequence_number++,
+                        TickTime::MillisecondTimestamp(),
+                        1200,
+                        false);
+  }
+
+  TickTime::AdvanceFakeClock(2001);
+  SendAndExpectPacket(PacedSender::kNormalPriority,
+                      ssrc,
+                      sequence_number++,
+                      TickTime::MillisecondTimestamp(),
+                      1200,
+                      false);
+  EXPECT_EQ(2001, send_bucket_->QueueInMs());
+  send_bucket_->Process();
+  EXPECT_EQ(0, send_bucket_->QueueInMs());
+  TickTime::AdvanceFakeClock(31);
+  send_bucket_->Process();
+}
 }  // namespace test
 }  // namespace webrtc
diff --git a/modules/remote_bitrate_estimator/bitrate_estimator.cc b/modules/remote_bitrate_estimator/bitrate_estimator.cc
index 316297f..8934d43 100644
--- a/modules/remote_bitrate_estimator/bitrate_estimator.cc
+++ b/modules/remote_bitrate_estimator/bitrate_estimator.cc
@@ -12,52 +12,73 @@
 
 namespace webrtc {
 
-const float kBitrateAverageWindowMs = 500.0f;
+const int kBitrateAverageWindowMs = 500;
 
 BitRateStats::BitRateStats()
-    : data_samples_(),
-      accumulated_bytes_(0) {
+    : num_buckets_(kBitrateAverageWindowMs + 1),  // N ms in (N+1) buckets.
+      buckets_(new uint32_t[num_buckets_]()),
+      accumulated_bytes_(0),
+      oldest_time_(0),
+      oldest_index_(0),
+      bps_coefficient_(8.f * 1000.f / (num_buckets_ - 1)) {
 }
 
 BitRateStats::~BitRateStats() {
-  Init();
 }
 
 void BitRateStats::Init() {
   accumulated_bytes_ = 0;
-  while (data_samples_.size() > 0) {
-    delete data_samples_.front();
-    data_samples_.pop_front();
+  oldest_time_ = 0;
+  oldest_index_ = 0;
+  for (int i = 0; i < num_buckets_; i++) {
+    buckets_[i] = 0;
   }
 }
 
 void BitRateStats::Update(uint32_t packet_size_bytes, int64_t now_ms) {
-  // Find an empty slot for storing the new sample and at the same time
-  // accumulate the history.
-  data_samples_.push_back(new DataTimeSizeTuple(packet_size_bytes, now_ms));
-  accumulated_bytes_ += packet_size_bytes;
-  EraseOld(now_ms);
-}
-
-void BitRateStats::EraseOld(int64_t now_ms) {
-  while (data_samples_.size() > 0) {
-    if (now_ms - data_samples_.front()->time_complete_ms >
-        kBitrateAverageWindowMs) {
-      // Delete old sample
-      accumulated_bytes_ -= data_samples_.front()->size_bytes;
-      delete data_samples_.front();
-      data_samples_.pop_front();
-    } else {
-      break;
-    }
+  if (now_ms < oldest_time_) {
+    // Too old data is ignored.
+    return;
   }
+
+  EraseOld(now_ms);
+
+  int now_offset = static_cast<int>(now_ms - oldest_time_);
+  assert(now_offset < num_buckets_);
+  int index = oldest_index_ + now_offset;
+  if (index >= num_buckets_) {
+    index -= num_buckets_;
+  }
+  buckets_[index] += packet_size_bytes;
+  accumulated_bytes_ += packet_size_bytes;
 }
 
 uint32_t BitRateStats::BitRate(int64_t now_ms) {
-  // Calculate the average bit rate the past BITRATE_AVERAGE_WINDOW ms.
-  // Removes any old samples from the list.
   EraseOld(now_ms);
-  return static_cast<uint32_t>(accumulated_bytes_ * 8.0f * 1000.0f /
-                     kBitrateAverageWindowMs + 0.5f);
+  return static_cast<uint32_t>(accumulated_bytes_ * bps_coefficient_ + 0.5f);
+}
+
+void BitRateStats::EraseOld(int64_t now_ms) {
+  int64_t new_oldest_time = now_ms - num_buckets_ + 1;
+  if (new_oldest_time <= oldest_time_) {
+    return;
+  }
+
+  while (oldest_time_ < new_oldest_time) {
+    uint32_t num_bytes_in_oldest_bucket = buckets_[oldest_index_];
+    assert(accumulated_bytes_ >= num_bytes_in_oldest_bucket);
+    accumulated_bytes_ -= num_bytes_in_oldest_bucket;
+    buckets_[oldest_index_] = 0;
+    if (++oldest_index_ >= num_buckets_) {
+      oldest_index_ = 0;
+    }
+    ++oldest_time_;
+    if (accumulated_bytes_ == 0) {
+      // This guarantees we go through all the buckets at most once, even if
+      // |new_oldest_time| is far greater than |oldest_time_|.
+      break;
+    }
+  }
+  oldest_time_ = new_oldest_time;
 }
 }  // namespace webrtc
diff --git a/modules/remote_bitrate_estimator/bitrate_estimator.h b/modules/remote_bitrate_estimator/bitrate_estimator.h
index 3d5d51b..9ee31eb 100644
--- a/modules/remote_bitrate_estimator/bitrate_estimator.h
+++ b/modules/remote_bitrate_estimator/bitrate_estimator.h
@@ -11,8 +11,7 @@
 #ifndef WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_BITRATE_ESTIMATOR_H_
 #define WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_BITRATE_ESTIMATOR_H_
 
-#include <list>
-
+#include "webrtc/system_wrappers/interface/scoped_ptr.h"
 #include "webrtc/typedefs.h"
 
 namespace webrtc {
@@ -27,20 +26,24 @@
   uint32_t BitRate(int64_t now_ms);
 
  private:
-  struct DataTimeSizeTuple {
-    DataTimeSizeTuple(uint32_t size_bytes_in, int64_t time_complete_ms_in)
-        : size_bytes(size_bytes_in),
-          time_complete_ms(time_complete_ms_in) {
-    }
-
-    uint32_t size_bytes;
-    int64_t time_complete_ms;
-  };
-
   void EraseOld(int64_t now_ms);
 
-  std::list<DataTimeSizeTuple*> data_samples_;
+  // Numbers of bytes are kept in buckets (circular buffer), with one bucket
+  // per millisecond.
+  const int num_buckets_;
+  scoped_array<uint32_t> buckets_;
+
+  // Total number of bytes recorded in buckets.
   uint32_t accumulated_bytes_;
+
+  // Oldest time recorded in buckets.
+  int64_t oldest_time_;
+
+  // Bucket index of oldest bytes recorded in buckets.
+  int oldest_index_;
+
+  // To convert number of bytes in bits/second.
+  const float bps_coefficient_;
 };
 }  // namespace webrtc
 
diff --git a/modules/remote_bitrate_estimator/bitrate_estimator_unittest.cc b/modules/remote_bitrate_estimator/bitrate_estimator_unittest.cc
index ca9da99..3d49ca4 100644
--- a/modules/remote_bitrate_estimator/bitrate_estimator_unittest.cc
+++ b/modules/remote_bitrate_estimator/bitrate_estimator_unittest.cc
@@ -17,7 +17,7 @@
 
 class BitRateStatsTest : public ::testing::Test {
  protected:
-  BitRateStatsTest() {};
+  BitRateStatsTest() {}
   BitRateStats stats_;
 };
 
@@ -47,4 +47,51 @@
   // the estimate should be 0.
   EXPECT_EQ(0u, stats_.BitRate(now_ms));
 }
+
+TEST_F(BitRateStatsTest, IncreasingThenDecreasingBitrate) {
+  int64_t now_ms = 0;
+  stats_.Init();
+  // Expecting 0 after init.
+  uint32_t bitrate = stats_.BitRate(now_ms);
+  EXPECT_EQ(0u, bitrate);
+  // 1000 bytes per millisecond until plateau is reached.
+  while (++now_ms < 10000) {
+    stats_.Update(1000, now_ms);
+    uint32_t new_bitrate = stats_.BitRate(now_ms);
+    if (new_bitrate != bitrate) {
+      // New bitrate must be higher than previous one.
+      EXPECT_GT(new_bitrate, bitrate);
+    } else {
+      // Plateau reached, 8000 kbps expected.
+      EXPECT_NEAR(8000000u, bitrate, 80000u);
+      break;
+    }
+    bitrate = new_bitrate;
+  }
+  // 1000 bytes per millisecond until 10-second mark, 8000 kbps expected.
+  while (++now_ms < 10000) {
+    stats_.Update(1000, now_ms);
+    bitrate = stats_.BitRate(now_ms);
+    EXPECT_NEAR(8000000u, bitrate, 80000u);
+  }
+  // Zero bytes per millisecond until 0 is reached.
+  while (++now_ms < 20000) {
+    stats_.Update(0, now_ms);
+    uint32_t new_bitrate = stats_.BitRate(now_ms);
+    if (new_bitrate != bitrate) {
+      // New bitrate must be lower than previous one.
+      EXPECT_LT(new_bitrate, bitrate);
+    } else {
+      // 0 kbps expected.
+      EXPECT_EQ(0u, bitrate);
+      break;
+    }
+    bitrate = new_bitrate;
+  }
+  // Zero bytes per millisecond until 20-second mark, 0 kbps expected.
+  while (++now_ms < 20000) {
+    stats_.Update(0, now_ms);
+    EXPECT_EQ(0u, stats_.BitRate(now_ms));
+  }
+}
 }  // namespace
diff --git a/modules/remote_bitrate_estimator/remote_bitrate_estimators_test.cc b/modules/remote_bitrate_estimator/remote_bitrate_estimators_test.cc
index deeeb90..ed8e5c5 100644
--- a/modules/remote_bitrate_estimator/remote_bitrate_estimators_test.cc
+++ b/modules/remote_bitrate_estimator/remote_bitrate_estimators_test.cc
@@ -8,444 +8,225 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#include "gtest/gtest.h"
-#include "webrtc/modules/remote_bitrate_estimator/test/bwe_test_framework.h"
 #include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h"
-#include "webrtc/system_wrappers/interface/clock.h"
-#include "webrtc/system_wrappers/interface/constructor_magic.h"
-#include "webrtc/system_wrappers/interface/scoped_ptr.h"
-
-#define ENABLE_1_SENDER 1
-#define ENABLE_3_SENDERS 1
-#define ENABLE_10_SENDERS 1
-#define ENABLE_BASIC_TESTS 1
-#define ENABLE_LOSS_TESTS 0
-#define ENABLE_DELAY_TESTS 0
-#define ENABLE_JITTER_TESTS 0
-#define ENABLE_REORDER_TESTS 0
-#define ENABLE_CHOKE_TESTS 0
-#define ENABLE_MULTI_TESTS 0
-
-#define ENABLE_TOF_ESTIMATOR 1
-#define ENABLE_AST_ESTIMATOR 1
-
-using std::vector;
+#include "webrtc/modules/remote_bitrate_estimator/test/bwe_test.h"
 
 namespace webrtc {
 namespace testing {
 namespace bwe {
 
-const int64_t kSimulationIntervalMs = 1000;
+std::vector<const PacketSenderFactory*> VideoSenderFactories(uint32_t count) {
+  class VideoPacketSenderFactory : public PacketSenderFactory {
+   public:
+    VideoPacketSenderFactory(float fps, uint32_t kbps, uint32_t ssrc,
+                             float frame_offset)
+        : fps_(fps),
+          kbps_(kbps),
+          ssrc_(ssrc),
+          frame_offset_(frame_offset) {
+    }
+    virtual ~VideoPacketSenderFactory() {}
+    virtual PacketSender* Create() const {
+      return new VideoSender(NULL, fps_, kbps_, ssrc_, frame_offset_);
+    }
+   private:
+    float fps_;
+    uint32_t kbps_;
+    uint32_t ssrc_;
+    float frame_offset_;
+  };
 
-namespace stl_helpers {
-template<typename T> void DeleteElements(T* container) {
-  if (!container) return;
-  for (typename T::iterator it = container->begin(); it != container->end();
-      ++it) {
-    delete *it;
+  static const VideoPacketSenderFactory factories[] = {
+    VideoPacketSenderFactory(30.00f, 150, 0x1234, 0.13f),
+    VideoPacketSenderFactory(15.00f, 500, 0x2345, 0.16f),
+    VideoPacketSenderFactory(30.00f, 1200, 0x3456, 0.26f),
+    VideoPacketSenderFactory(7.49f, 150, 0x4567, 0.05f),
+    VideoPacketSenderFactory(7.50f, 150, 0x5678, 0.15f),
+    VideoPacketSenderFactory(7.51f, 150, 0x6789, 0.25f),
+    VideoPacketSenderFactory(15.02f, 150, 0x7890, 0.27f),
+    VideoPacketSenderFactory(15.03f, 150, 0x8901, 0.38f),
+    VideoPacketSenderFactory(30.02f, 150, 0x9012, 0.39f),
+    VideoPacketSenderFactory(30.03f, 150, 0x0123, 0.52f)
+  };
+  assert(count <= sizeof(factories) / sizeof(factories[0]));
+
+  std::vector<const PacketSenderFactory*> result;
+  for (uint32_t i = 0; i < count; ++i) {
+    result.push_back(&factories[i]);
   }
-  container->clear();
+  return result;
 }
-}  // namespace stl_helpers
 
-class TestedEstimator : public RemoteBitrateObserver {
- public:
-  TestedEstimator(const std::string& debug_name,
-                  const RemoteBitrateEstimatorFactory& factory)
-      : debug_name_(debug_name),
-        clock_(0),
-        stats_(),
-        relative_estimator_stats_(),
-        latest_estimate_kbps_(-1.0),
-        estimator_(factory.Create(this, &clock_)),
-        relative_estimator_(NULL) {
-    assert(estimator_.get());
-    // Default RTT in RemoteRateControl is 200 ms ; 50 ms is more realistic.
-    estimator_->OnRttUpdate(50);
+std::vector<BweTestConfig::EstimatorConfig> EstimatorConfigs() {
+  static const RemoteBitrateEstimatorFactory factories[] = {
+    RemoteBitrateEstimatorFactory(),
+    AbsoluteSendTimeRemoteBitrateEstimatorFactory()
+  };
+
+  std::vector<BweTestConfig::EstimatorConfig> result;
+  result.push_back(BweTestConfig::EstimatorConfig("TOF", &factories[0]));
+  result.push_back(BweTestConfig::EstimatorConfig("AST", &factories[1]));
+  return result;
+}
+
+BweTestConfig MakeBweTestConfig(uint32_t sender_count) {
+  BweTestConfig result = {
+    VideoSenderFactories(sender_count), EstimatorConfigs()
+  };
+  return result;
+}
+
+INSTANTIATE_TEST_CASE_P(VideoSendersTest, BweTest,
+    ::testing::Values(MakeBweTestConfig(1),
+                      MakeBweTestConfig(3)));
+
+TEST_P(BweTest, UnlimitedSpeed) {
+  VerboseLogging(false);
+  RunFor(10 * 60 * 1000);
+}
+
+TEST_P(BweTest, SteadyLoss) {
+  LossFilter loss(this);
+  loss.SetLoss(20.0);
+  RunFor(10 * 60 * 1000);
+}
+
+TEST_P(BweTest, IncreasingLoss1) {
+  LossFilter loss(this);
+  for (int i = 0; i < 76; ++i) {
+    loss.SetLoss(i);
+    RunFor(5000);
   }
+}
 
-  void SetRelativeEstimator(TestedEstimator* relative_estimator) {
-    relative_estimator_ = relative_estimator;
+TEST_P(BweTest, SteadyDelay) {
+  DelayFilter delay(this);
+  delay.SetDelay(1000);
+  RunFor(10 * 60 * 1000);
+}
+
+TEST_P(BweTest, IncreasingDelay1) {
+  DelayFilter delay(this);
+  RunFor(10 * 60 * 1000);
+  for (int i = 0; i < 30 * 2; ++i) {
+    delay.SetDelay(i);
+    RunFor(10 * 1000);
   }
+  RunFor(10 * 60 * 1000);
+}
 
-  void EatPacket(const BwePacket& packet) {
-    BWE_TEST_LOGGING_CONTEXT(debug_name_);
-
-    latest_estimate_kbps_ = -1.0;
-
-    // We're treating the send time (from previous filter) as the arrival
-    // time once packet reaches the estimator.
-    int64_t packet_time_ms = (packet.send_time_us() + 500) / 1000;
-    BWE_TEST_LOGGING_TIME(packet_time_ms);
-
-    int64_t step_ms = estimator_->TimeUntilNextProcess();
-    while ((clock_.TimeInMilliseconds() + step_ms) < packet_time_ms) {
-      clock_.AdvanceTimeMilliseconds(step_ms);
-      estimator_->Process();
-      step_ms = estimator_->TimeUntilNextProcess();
-    }
-
-    estimator_->IncomingPacket(packet_time_ms, packet.payload_size(),
-                               packet.header());
-    clock_.AdvanceTimeMilliseconds(packet_time_ms -
-                                   clock_.TimeInMilliseconds());
-    ASSERT_TRUE(packet_time_ms == clock_.TimeInMilliseconds());
+TEST_P(BweTest, IncreasingDelay2) {
+  DelayFilter delay(this);
+  RateCounterFilter counter(this);
+  RunFor(1 * 60 * 1000);
+  for (int i = 1; i < 51; ++i) {
+    delay.SetDelay(10.0f * i);
+    RunFor(10 * 1000);
   }
+  delay.SetDelay(0.0f);
+  RunFor(10 * 60 * 1000);
+}
 
-  void CheckEstimate() {
-    BWE_TEST_LOGGING_CONTEXT(debug_name_);
-    double estimated_kbps = 0.0;
-    if (LatestEstimate(&estimated_kbps)) {
-      stats_.Push(estimated_kbps);
-      BWE_TEST_LOGGING_PLOT("Estimate", clock_.TimeInMilliseconds(),
-                            estimated_kbps / 100);
-      double relative_estimate_kbps = 0.0;
-      if (relative_estimator_ &&
-          relative_estimator_->LatestEstimate(&relative_estimate_kbps)) {
-        relative_estimator_stats_.Push(estimated_kbps - relative_estimate_kbps);
-      }
-    }
+TEST_P(BweTest, JumpyDelay1) {
+  DelayFilter delay(this);
+  RunFor(10 * 60 * 1000);
+  for (int i = 1; i < 200; ++i) {
+    delay.SetDelay((10 * i) % 500);
+    RunFor(1000);
+    delay.SetDelay(1.0f);
+    RunFor(1000);
   }
+  delay.SetDelay(0.0f);
+  RunFor(10 * 60 * 1000);
+}
 
-  void LogStats() {
-    BWE_TEST_LOGGING_CONTEXT(debug_name_);
-    BWE_TEST_LOGGING_CONTEXT("Mean");
-    stats_.Log("kbps");
-    if (relative_estimator_) {
-      BWE_TEST_LOGGING_CONTEXT("Diff");
-      relative_estimator_stats_.Log("kbps");
-    }
+TEST_P(BweTest, SteadyJitter) {
+  JitterFilter jitter(this);
+  RateCounterFilter counter(this);
+  jitter.SetJitter(20);
+  RunFor(2 * 60 * 1000);
+}
+
+TEST_P(BweTest, IncreasingJitter1) {
+  JitterFilter jitter(this);
+  for (int i = 0; i < 2 * 60 * 2; ++i) {
+    jitter.SetJitter(i);
+    RunFor(10 * 1000);
   }
+  RunFor(10 * 60 * 1000);
+}
 
-  virtual void OnReceiveBitrateChanged(const vector<unsigned int>& ssrcs,
-                                       unsigned int bitrate) {
+TEST_P(BweTest, IncreasingJitter2) {
+  JitterFilter jitter(this);
+  RunFor(30 * 1000);
+  for (int i = 1; i < 51; ++i) {
+    jitter.SetJitter(10.0f * i);
+    RunFor(10 * 1000);
   }
+  jitter.SetJitter(0.0f);
+  RunFor(10 * 60 * 1000);
+}
 
- private:
-  bool LatestEstimate(double* estimate_kbps) {
-    if (latest_estimate_kbps_ < 0.0) {
-      vector<unsigned int> ssrcs;
-      unsigned int bps = 0;
-      if (!estimator_->LatestEstimate(&ssrcs, &bps)) {
-        return false;
-      }
-      latest_estimate_kbps_ = bps / 1000.0;
-    }
-    *estimate_kbps = latest_estimate_kbps_;
-    return true;
+TEST_P(BweTest, SteadyReorder) {
+  ReorderFilter reorder(this);
+  reorder.SetReorder(20.0);
+  RunFor(10 * 60 * 1000);
+}
+
+TEST_P(BweTest, IncreasingReorder1) {
+  ReorderFilter reorder(this);
+  for (int i = 0; i < 76; ++i) {
+    reorder.SetReorder(i);
+    RunFor(5000);
   }
+}
 
-  std::string debug_name_;
-  bool log_estimates_;
-  SimulatedClock clock_;
-  Stats<double> stats_;
-  Stats<double> relative_estimator_stats_;
-  double latest_estimate_kbps_;
-  scoped_ptr<RemoteBitrateEstimator> estimator_;
-  TestedEstimator* relative_estimator_;
+TEST_P(BweTest, SteadyChoke) {
+  ChokeFilter choke(this);
+  choke.SetCapacity(140);
+  RunFor(10 * 60 * 1000);
+}
 
-  DISALLOW_IMPLICIT_CONSTRUCTORS(TestedEstimator);
-};
-
-class RemoteBitrateEstimatorsTest : public ::testing::Test {
- public:
-  RemoteBitrateEstimatorsTest()
-      : run_time_ms_(0),
-        estimators_(),
-        previous_packets_(),
-        processors_(),
-        video_senders_() {
+TEST_P(BweTest, IncreasingChoke1) {
+  ChokeFilter choke(this);
+  for (int i = 1200; i >= 100; i -= 100) {
+    choke.SetCapacity(i);
+    RunFor(5000);
   }
-  virtual ~RemoteBitrateEstimatorsTest() {
-    stl_helpers::DeleteElements(&estimators_);
-    stl_helpers::DeleteElements(&video_senders_);
-  }
+}
 
-  virtual void SetUp() {
-#if ENABLE_TOF_ESTIMATOR
-    estimators_.push_back(new TestedEstimator("TOF",
-        RemoteBitrateEstimatorFactory()));
-#endif
-#if ENABLE_AST_ESTIMATOR
-    estimators_.push_back(new TestedEstimator("AST",
-        AbsoluteSendTimeRemoteBitrateEstimatorFactory()));
-#endif
-    // Set all estimators as relative to the first one.
-    for (uint32_t i = 1; i < estimators_.size(); ++i) {
-      estimators_[i]->SetRelativeEstimator(estimators_[0]);
-    }
+TEST_P(BweTest, IncreasingChoke2) {
+  ChokeFilter choke(this);
+  RunFor(60 * 1000);
+  for (int i = 1200; i >= 100; i -= 20) {
+    choke.SetCapacity(i);
+    RunFor(1000);
   }
+}
 
- protected:
-  void RunFor(int64_t time_ms) {
-    for (run_time_ms_ += time_ms; run_time_ms_ >= kSimulationIntervalMs;
-        run_time_ms_ -= kSimulationIntervalMs) {
-      Packets packets;
-      for (vector<PacketProcessorInterface*>::const_iterator it =
-           processors_.begin(); it != processors_.end(); ++it) {
-        (*it)->RunFor(kSimulationIntervalMs, &packets);
-      }
+TEST_P(BweTest, Multi1) {
+  DelayFilter delay(this);
+  ChokeFilter choke(this);
+  RateCounterFilter counter(this);
+  choke.SetCapacity(1000);
+  RunFor(1 * 60 * 1000);
+  for (int i = 1; i < 51; ++i) {
+    delay.SetDelay(100.0f * i);
+    RunFor(10 * 1000);
+  }
+  RunFor(500 * 1000);
+  delay.SetDelay(0.0f);
+  RunFor(5 * 60 * 1000);
+}
 
-      // Verify packets are in order between batches.
-      if (!packets.empty() && !previous_packets_.empty()) {
-        packets.splice(packets.begin(), previous_packets_,
-                       --previous_packets_.end());
-        ASSERT_TRUE(IsTimeSorted(packets));
-        packets.erase(packets.begin());
-      } else {
-        ASSERT_TRUE(IsTimeSorted(packets));
-      }
-
-      for (PacketsConstIt pit = packets.begin(); pit != packets.end(); ++pit) {
-        for (vector<TestedEstimator*>::iterator eit = estimators_.begin();
-            eit != estimators_.end(); ++eit) {
-          (*eit)->EatPacket(*pit);
-        }
-      }
-
-      previous_packets_.swap(packets);
-
-      for (vector<TestedEstimator*>::iterator eit = estimators_.begin();
-          eit != estimators_.end(); ++eit) {
-        (*eit)->CheckEstimate();
-      }
-    }
-  }
-
-  void AddVideoSenders(uint32_t count) {
-    struct { float fps; uint32_t kbps; uint32_t ssrc; float frame_offset; }
-        configs[] = {
-            { 30.00f, 150, 0x1234, 0.13f },
-            { 15.00f, 500, 0x2345, 0.16f },
-            { 30.00f, 1200, 0x3456, 0.26f },
-            { 7.49f, 150, 0x4567, 0.05f },
-            { 7.50f, 150, 0x5678, 0.15f },
-            { 7.51f, 150, 0x6789, 0.25f },
-            { 15.02f, 150, 0x7890, 0.27f },
-            { 15.03f, 150, 0x8901, 0.38f },
-            { 30.02f, 150, 0x9012, 0.39f },
-            { 30.03f, 150, 0x0123, 0.52f }
-        };
-    assert(count <= sizeof(configs) / sizeof(configs[0]));
-    uint32_t total_capacity = 0;
-    for (uint32_t i = 0; i < count; ++i) {
-      video_senders_.push_back(new VideoSender(configs[i].fps, configs[i].kbps,
-          configs[i].ssrc, configs[i].frame_offset));
-      processors_.push_back(video_senders_.back());
-      total_capacity += configs[i].kbps;
-    }
-    BWE_TEST_LOGGING_LOG1("RequiredLinkCapacity", "%d kbps", total_capacity)
-  }
-
-  void LogStats() {
-    for (vector<TestedEstimator*>::iterator eit = estimators_.begin();
-        eit != estimators_.end(); ++eit) {
-      (*eit)->LogStats();
-    }
-  }
-
-  void UnlimitedSpeedTest() {
-    RunFor(10 * 60 * 1000);
-  }
-
-  void SteadyLossTest() {
-    LossFilter loss;
-    processors_.push_back(&loss);
-    loss.SetLoss(20.0);
-    RunFor(10 * 60 * 1000);
-  }
-  void IncreasingLoss1Test() {
-    LossFilter loss;
-    processors_.push_back(&loss);
-    for (int i = 0; i < 76; ++i) {
-      loss.SetLoss(i);
-      RunFor(5000);
-    }
-  }
-
-  void SteadyDelayTest() {
-    DelayFilter delay;
-    processors_.push_back(&delay);
-    delay.SetDelay(1000);
-    RunFor(10 * 60 * 1000);
-  }
-  void IncreasingDelay1Test() {
-    DelayFilter delay;
-    processors_.push_back(&delay);
-    RunFor(10 * 60 * 1000);
-    for (int i = 0; i < 30 * 2; ++i) {
-      delay.SetDelay(i);
-      RunFor(10 * 1000);
-    }
-    RunFor(10 * 60 * 1000);
-  }
-  void IncreasingDelay2Test() {
-    DelayFilter delay;
-    RateCounterFilter counter;
-    processors_.push_back(&delay);
-    processors_.push_back(&counter);
-    RunFor(1 * 60 * 1000);
-    for (int i = 1; i < 51; ++i) {
-      delay.SetDelay(10.0f * i);
-      RunFor(10 * 1000);
-    }
-    delay.SetDelay(0.0f);
-    RunFor(10 * 60 * 1000);
-  }
-  void JumpyDelay1Test() {
-    DelayFilter delay;
-    processors_.push_back(&delay);
-    RunFor(10 * 60 * 1000);
-    for (int i = 1; i < 200; ++i) {
-      delay.SetDelay((10 * i) % 500);
-      RunFor(1000);
-      delay.SetDelay(1.0f);
-      RunFor(1000);
-    }
-    delay.SetDelay(0.0f);
-    RunFor(10 * 60 * 1000);
-  }
-
-  void SteadyJitterTest() {
-    JitterFilter jitter;
-    RateCounterFilter counter;
-    processors_.push_back(&jitter);
-    processors_.push_back(&counter);
-    jitter.SetJitter(20);
-    RunFor(2 * 60 * 1000);
-  }
-  void IncreasingJitter1Test() {
-    JitterFilter jitter;
-    processors_.push_back(&jitter);
-    for (int i = 0; i < 2 * 60 * 2; ++i) {
-      jitter.SetJitter(i);
-      RunFor(10 * 1000);
-    }
-    RunFor(10 * 60 * 1000);
-  }
-  void IncreasingJitter2Test() {
-    JitterFilter jitter;
-    processors_.push_back(&jitter);
-    RunFor(30 * 1000);
-    for (int i = 1; i < 51; ++i) {
-      jitter.SetJitter(10.0f * i);
-      RunFor(10 * 1000);
-    }
-    jitter.SetJitter(0.0f);
-    RunFor(10 * 60 * 1000);
-  }
-
-  void SteadyReorderTest() {
-    ReorderFilter reorder;
-    processors_.push_back(&reorder);
-    reorder.SetReorder(20.0);
-    RunFor(10 * 60 * 1000);
-  }
-  void IncreasingReorder1Test() {
-    ReorderFilter reorder;
-    processors_.push_back(&reorder);
-    for (int i = 0; i < 76; ++i) {
-      reorder.SetReorder(i);
-      RunFor(5000);
-    }
-  }
-
-  void SteadyChokeTest() {
-    ChokeFilter choke;
-    processors_.push_back(&choke);
-    choke.SetCapacity(140);
-    RunFor(10 * 60 * 1000);
-  }
-  void IncreasingChoke1Test() {
-    ChokeFilter choke;
-    processors_.push_back(&choke);
-    for (int i = 1200; i >= 100; i -= 100) {
-      choke.SetCapacity(i);
-      RunFor(5000);
-    }
-  }
-  void IncreasingChoke2Test() {
-    ChokeFilter choke;
-    processors_.push_back(&choke);
-    RunFor(60 * 1000);
-    for (int i = 1200; i >= 100; i -= 20) {
-      choke.SetCapacity(i);
-      RunFor(1000);
-    }
-  }
-
-  void Multi1Test() {
-    DelayFilter delay;
-    ChokeFilter choke;
-    RateCounterFilter counter;
-    processors_.push_back(&delay);
-    processors_.push_back(&choke);
-    processors_.push_back(&counter);
-    choke.SetCapacity(1000);
-    RunFor(1 * 60 * 1000);
-    for (int i = 1; i < 51; ++i) {
-      delay.SetDelay(100.0f * i);
-      RunFor(10 * 1000);
-    }
-    delay.SetDelay(0.0f);
-    RunFor(5 * 60 * 1000);
-  }
-  void Multi2Test() {
-    ChokeFilter choke;
-    JitterFilter jitter;
-    RateCounterFilter counter;
-    processors_.push_back(&choke);
-    processors_.push_back(&jitter);
-    processors_.push_back(&counter);
-    choke.SetCapacity(2000);
-    jitter.SetJitter(120);
-    RunFor(5 * 60 * 1000);
-  }
-
- private:
-  int64_t run_time_ms_;
-  vector<TestedEstimator*> estimators_;
-  Packets previous_packets_;
-  vector<PacketProcessorInterface*> processors_;
-  vector<VideoSender*> video_senders_;
-
-  DISALLOW_COPY_AND_ASSIGN(RemoteBitrateEstimatorsTest);
-};
-
-#define SINGLE_TEST(enabled, test_name, video_senders, log)\
-    TEST_F(RemoteBitrateEstimatorsTest, test_name##_##video_senders##Sender) {\
-      BWE_TEST_LOGGING_ENABLE(log);\
-      if (enabled) {\
-        BWE_TEST_LOGGING_CONTEXT(#test_name);\
-        AddVideoSenders(video_senders);\
-        test_name##Test();\
-        LogStats();\
-      }\
-    }
-
-#define MULTI_TEST(enabled, test_name, log)\
-    SINGLE_TEST((enabled) && ENABLE_1_SENDER, test_name, 1, log)\
-    SINGLE_TEST((enabled) && ENABLE_3_SENDERS, test_name, 3, log)\
-    SINGLE_TEST((enabled) && ENABLE_10_SENDERS, test_name, 10, log)
-
-MULTI_TEST(ENABLE_BASIC_TESTS, UnlimitedSpeed, true)
-MULTI_TEST(ENABLE_LOSS_TESTS, SteadyLoss, true)
-MULTI_TEST(ENABLE_LOSS_TESTS, IncreasingLoss1, true)
-MULTI_TEST(ENABLE_DELAY_TESTS, SteadyDelay, true)
-MULTI_TEST(ENABLE_DELAY_TESTS, IncreasingDelay1, true)
-MULTI_TEST(ENABLE_DELAY_TESTS, IncreasingDelay2, true)
-MULTI_TEST(ENABLE_DELAY_TESTS, JumpyDelay1, true)
-MULTI_TEST(ENABLE_JITTER_TESTS, SteadyJitter, true)
-MULTI_TEST(ENABLE_JITTER_TESTS, IncreasingJitter1, true)
-MULTI_TEST(ENABLE_JITTER_TESTS, IncreasingJitter2, true)
-MULTI_TEST(ENABLE_REORDER_TESTS, SteadyReorder, true)
-MULTI_TEST(ENABLE_REORDER_TESTS, IncreasingReorder1, true)
-MULTI_TEST(ENABLE_CHOKE_TESTS, SteadyChoke, true)
-MULTI_TEST(ENABLE_CHOKE_TESTS, IncreasingChoke1, true)
-MULTI_TEST(ENABLE_CHOKE_TESTS, IncreasingChoke2, true)
-MULTI_TEST(ENABLE_MULTI_TESTS, Multi1, true)
-MULTI_TEST(ENABLE_MULTI_TESTS, Multi2, true)
-
+TEST_P(BweTest, Multi2) {
+  ChokeFilter choke(this);
+  JitterFilter jitter(this);
+  RateCounterFilter counter(this);
+  choke.SetCapacity(2000);
+  jitter.SetJitter(120);
+  RunFor(5 * 60 * 1000);
+}
 }  // namespace bwe
 }  // namespace testing
 }  // namespace webrtc
diff --git a/modules/remote_bitrate_estimator/test/bwe_test.cc b/modules/remote_bitrate_estimator/test/bwe_test.cc
new file mode 100644
index 0000000..3046a5b
--- /dev/null
+++ b/modules/remote_bitrate_estimator/test/bwe_test.cc
@@ -0,0 +1,263 @@
+/*
+ *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/modules/remote_bitrate_estimator/test/bwe_test.h"
+
+#include "webrtc/modules/remote_bitrate_estimator/test/bwe_test_framework.h"
+#include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h"
+#include "webrtc/system_wrappers/interface/clock.h"
+#include "webrtc/system_wrappers/interface/scoped_ptr.h"
+
+using std::string;
+using std::vector;
+
+namespace webrtc {
+namespace testing {
+namespace bwe {
+
+namespace stl_helpers {
+template<typename T> void DeleteElements(T* container) {
+  if (!container) return;
+  for (typename T::iterator it = container->begin(); it != container->end();
+      ++it) {
+    delete *it;
+  }
+  container->clear();
+}
+}  // namespace stl_helpers
+
+class BweTest::TestedEstimator : public RemoteBitrateObserver {
+ public:
+  explicit TestedEstimator(const BweTestConfig::EstimatorConfig& config)
+      : debug_name_(config.debug_name),
+        clock_(0),
+        stats_(),
+        relative_estimator_stats_(),
+        latest_estimate_kbps_(-1.0),
+        estimator_(config.estimator_factory->Create(this, &clock_)),
+        relative_estimator_(NULL) {
+    assert(estimator_.get());
+    // Default RTT in RemoteRateControl is 200 ms ; 50 ms is more realistic.
+    estimator_->OnRttUpdate(50);
+  }
+
+  void SetRelativeEstimator(TestedEstimator* relative_estimator) {
+    relative_estimator_ = relative_estimator;
+  }
+
+  void EatPacket(const Packet& packet) {
+    BWE_TEST_LOGGING_CONTEXT(debug_name_);
+
+    latest_estimate_kbps_ = -1.0;
+
+    // We're treating the send time (from previous filter) as the arrival
+    // time once packet reaches the estimator.
+    int64_t packet_time_ms = (packet.send_time_us() + 500) / 1000;
+    BWE_TEST_LOGGING_TIME(packet_time_ms);
+
+    int64_t step_ms = estimator_->TimeUntilNextProcess();
+    while ((clock_.TimeInMilliseconds() + step_ms) < packet_time_ms) {
+      clock_.AdvanceTimeMilliseconds(step_ms);
+      estimator_->Process();
+      step_ms = estimator_->TimeUntilNextProcess();
+    }
+
+    estimator_->IncomingPacket(packet_time_ms, packet.payload_size(),
+                               packet.header());
+    clock_.AdvanceTimeMilliseconds(packet_time_ms -
+                                   clock_.TimeInMilliseconds());
+    ASSERT_TRUE(packet_time_ms == clock_.TimeInMilliseconds());
+  }
+
+  bool CheckEstimate(PacketSender::Feedback* feedback) {
+    assert(feedback);
+    BWE_TEST_LOGGING_CONTEXT(debug_name_);
+    double estimated_kbps = 0.0;
+    if (LatestEstimate(&estimated_kbps)) {
+      stats_.Push(estimated_kbps);
+      BWE_TEST_LOGGING_PLOT("Estimate", clock_.TimeInMilliseconds(),
+                            estimated_kbps / 100);
+      double relative_estimate_kbps = 0.0;
+      if (relative_estimator_ &&
+          relative_estimator_->LatestEstimate(&relative_estimate_kbps)) {
+        relative_estimator_stats_.Push(estimated_kbps - relative_estimate_kbps);
+      }
+      feedback->estimated_kbps = estimated_kbps;
+      return true;
+    }
+    return false;
+  }
+
+  void LogStats() {
+    BWE_TEST_LOGGING_CONTEXT(debug_name_);
+    BWE_TEST_LOGGING_CONTEXT("Mean");
+    stats_.Log("kbps");
+    if (relative_estimator_) {
+      BWE_TEST_LOGGING_CONTEXT("Diff");
+      relative_estimator_stats_.Log("kbps");
+    }
+  }
+
+  virtual void OnReceiveBitrateChanged(const vector<unsigned int>& ssrcs,
+                                       unsigned int bitrate) {
+  }
+
+ private:
+  bool LatestEstimate(double* estimate_kbps) {
+    if (latest_estimate_kbps_ < 0.0) {
+      vector<unsigned int> ssrcs;
+      unsigned int bps = 0;
+      if (!estimator_->LatestEstimate(&ssrcs, &bps)) {
+        return false;
+      }
+      latest_estimate_kbps_ = bps / 1000.0;
+    }
+    *estimate_kbps = latest_estimate_kbps_;
+    return true;
+  }
+
+  string debug_name_;
+  bool log_estimates_;
+  SimulatedClock clock_;
+  Stats<double> stats_;
+  Stats<double> relative_estimator_stats_;
+  double latest_estimate_kbps_;
+  scoped_ptr<RemoteBitrateEstimator> estimator_;
+  TestedEstimator* relative_estimator_;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(TestedEstimator);
+};
+
+BweTest::BweTest()
+    : run_time_ms_(0),
+      simulation_interval_ms_(-1),
+      previous_packets_(),
+      packet_senders_(),
+      estimators_(),
+      processors_() {
+}
+
+BweTest::~BweTest() {
+  stl_helpers::DeleteElements(&estimators_);
+  stl_helpers::DeleteElements(&packet_senders_);
+}
+
+void BweTest::SetUp() {
+  BWE_TEST_LOGGING_GLOBAL_CONTEXT(::testing::UnitTest::GetInstance()->
+      current_test_info()->test_case_name());
+
+  const BweTestConfig& config = GetParam();
+
+  uint32_t total_capacity = 0;
+  for (vector<const PacketSenderFactory*>::const_iterator it =
+      config.sender_factories.begin(); it != config.sender_factories.end();
+      ++it) {
+    PacketSender* sender = (*it)->Create();
+    assert(sender);
+    total_capacity += sender->GetCapacityKbps();
+    packet_senders_.push_back(sender);
+    processors_.push_back(sender);
+  }
+  BWE_TEST_LOGGING_LOG1("RequiredLinkCapacity", "%d kbps", total_capacity)
+
+  // Set simulation interval from first packet sender.
+  if (packet_senders_.size() > 0) {
+    simulation_interval_ms_ = packet_senders_[0]->GetFeedbackIntervalMs();
+  }
+
+  for (vector<BweTestConfig::EstimatorConfig>:: const_iterator it =
+      config.estimator_configs.begin(); it != config.estimator_configs.end();
+      ++it) {
+    estimators_.push_back(new TestedEstimator(*it));
+  }
+  if (estimators_.size() > 1) {
+    // Set all estimators as relative to the first one.
+    for (uint32_t i = 1; i < estimators_.size(); ++i) {
+      estimators_[i]->SetRelativeEstimator(estimators_[0]);
+    }
+  }
+
+  BWE_TEST_LOGGING_GLOBAL_ENABLE(false);
+}
+
+void BweTest::TearDown() {
+  BWE_TEST_LOGGING_GLOBAL_ENABLE(true);
+  LogStats();
+  BWE_TEST_LOGGING_GLOBAL_CONTEXT("");
+}
+
+void BweTest::AddPacketProcessor(
+    PacketProcessor* processor) {
+  assert(processor);
+  processors_.push_back(processor);
+}
+
+void BweTest::RemovePacketProcessor(
+    PacketProcessor* processor) {
+  vector<PacketProcessor*>::iterator it =
+      std::find(processors_.begin(), processors_.end(), processor);
+  assert(it != processors_.end());
+  processors_.erase(it);
+}
+
+void BweTest::VerboseLogging(bool enable) {
+  BWE_TEST_LOGGING_GLOBAL_ENABLE(enable);
+}
+
+void BweTest::RunFor(int64_t time_ms) {
+  for (run_time_ms_ += time_ms; run_time_ms_ >= simulation_interval_ms_;
+      run_time_ms_ -= simulation_interval_ms_) {
+    Packets packets;
+    for (vector<PacketProcessor*>::const_iterator it =
+         processors_.begin(); it != processors_.end(); ++it) {
+      (*it)->RunFor(simulation_interval_ms_, &packets);
+    }
+
+    // Verify packets are in order between batches.
+    if (!packets.empty() && !previous_packets_.empty()) {
+      packets.splice(packets.begin(), previous_packets_,
+                     --previous_packets_.end());
+      ASSERT_TRUE(IsTimeSorted(packets));
+      packets.erase(packets.begin());
+    } else {
+      ASSERT_TRUE(IsTimeSorted(packets));
+    }
+
+    for (PacketsConstIt pit = packets.begin(); pit != packets.end(); ++pit) {
+      for (vector<TestedEstimator*>::iterator eit = estimators_.begin();
+          eit != estimators_.end(); ++eit) {
+        (*eit)->EatPacket(*pit);
+      }
+    }
+
+    previous_packets_.swap(packets);
+
+    for (vector<TestedEstimator*>::iterator eit = estimators_.begin();
+        eit != estimators_.end(); ++eit) {
+      PacketSender::Feedback feedback = {0};
+      if ((*eit)->CheckEstimate(&feedback)) {
+        for (vector<PacketSender*>::iterator psit = packet_senders_.begin();
+            psit != packet_senders_.end(); ++psit) {
+          (*psit)->GiveFeedback(feedback);
+        }
+      }
+    }
+  }
+}
+
+void BweTest::LogStats() {
+  for (vector<TestedEstimator*>::iterator eit = estimators_.begin();
+      eit != estimators_.end(); ++eit) {
+    (*eit)->LogStats();
+  }
+}
+}  // namespace bwe
+}  // namespace testing
+}  // namespace webrtc
diff --git a/modules/remote_bitrate_estimator/test/bwe_test.h b/modules/remote_bitrate_estimator/test/bwe_test.h
new file mode 100644
index 0000000..0945ea6
--- /dev/null
+++ b/modules/remote_bitrate_estimator/test/bwe_test.h
@@ -0,0 +1,70 @@
+/*
+ *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <string>
+#include <vector>
+#include "gtest/gtest.h"
+#include "webrtc/modules/remote_bitrate_estimator/test/bwe_test_framework.h"
+#include "webrtc/system_wrappers/interface/constructor_magic.h"
+
+namespace webrtc {
+
+struct RemoteBitrateEstimatorFactory;
+
+namespace testing {
+namespace bwe {
+
+struct BweTestConfig {
+  struct EstimatorConfig {
+    EstimatorConfig() : debug_name(), estimator_factory(NULL) {}
+    EstimatorConfig(std::string debug_name,
+                    const RemoteBitrateEstimatorFactory* estimator_factory)
+        : debug_name(debug_name),
+          estimator_factory(estimator_factory) {
+    }
+    std::string debug_name;
+    const RemoteBitrateEstimatorFactory* estimator_factory;
+  };
+
+  std::vector<const PacketSenderFactory*> sender_factories;
+  std::vector<EstimatorConfig> estimator_configs;
+};
+
+class BweTest : public ::testing::TestWithParam<BweTestConfig>,
+    public PacketProcessorListener {
+ public:
+  BweTest();
+  virtual ~BweTest();
+
+  virtual void SetUp();
+  virtual void TearDown();
+  virtual void AddPacketProcessor(PacketProcessor* processor);
+  virtual void RemovePacketProcessor(PacketProcessor* processor);
+
+ protected:
+  void VerboseLogging(bool enable);
+  void RunFor(int64_t time_ms);
+  void LogStats();
+
+ private:
+  class TestedEstimator;
+
+  int64_t run_time_ms_;
+  int64_t simulation_interval_ms_;
+  Packets previous_packets_;
+  std::vector<PacketSender*> packet_senders_;
+  std::vector<TestedEstimator*> estimators_;
+  std::vector<PacketProcessor*> processors_;
+
+  DISALLOW_COPY_AND_ASSIGN(BweTest);
+};
+}  // namespace bwe
+}  // namespace testing
+}  // namespace webrtc
diff --git a/modules/remote_bitrate_estimator/test/bwe_test_framework.cc b/modules/remote_bitrate_estimator/test/bwe_test_framework.cc
index b90a7f6..9b64eaf 100644
--- a/modules/remote_bitrate_estimator/test/bwe_test_framework.cc
+++ b/modules/remote_bitrate_estimator/test/bwe_test_framework.cc
@@ -14,6 +14,62 @@
 namespace testing {
 namespace bwe {
 
+Random::Random(uint32_t seed)
+    : a_(0x531FDB97 ^ seed),
+      b_(0x6420ECA8 + seed) {
+}
+
+float Random::Rand() {
+  const float kScale = 1.0f / 0xffffffff;
+  float result = kScale * b_;
+  a_ ^= b_;
+  b_ += a_;
+  return result;
+}
+
+int Random::Gaussian(int mean, int standard_deviation) {
+  // Creating a Normal distribution variable from two independent uniform
+  // variables based on the Box-Muller transform, which is defined on the
+  // interval (0, 1], hence the mask+add below.
+  const double kPi = 3.14159265358979323846;
+  const double kScale = 1.0 / 0x80000000ul;
+  double u1 = kScale * ((a_ & 0x7ffffffful) + 1);
+  double u2 = kScale * ((b_ & 0x7ffffffful) + 1);
+  a_ ^= b_;
+  b_ += a_;
+  return static_cast<int>(mean + standard_deviation *
+      std::sqrt(-2 * std::log(u1)) * std::cos(2 * kPi * u2));
+}
+
+Packet::Packet()
+    : send_time_us_(0),
+      payload_size_(0) {
+   memset(&header_, 0, sizeof(header_));
+}
+
+Packet::Packet(int64_t send_time_us, uint32_t payload_size,
+          const RTPHeader& header)
+  : send_time_us_(send_time_us),
+    payload_size_(payload_size),
+    header_(header) {
+}
+
+Packet::Packet(int64_t send_time_us, uint32_t sequence_number)
+    : send_time_us_(send_time_us),
+      payload_size_(0) {
+   memset(&header_, 0, sizeof(header_));
+   header_.sequenceNumber = sequence_number;
+}
+
+bool Packet::operator<(const Packet& rhs) const {
+  return send_time_us_ < rhs.send_time_us_;
+}
+
+void Packet::set_send_time_us(int64_t send_time_us) {
+  assert(send_time_us >= 0);
+  send_time_us_ = send_time_us;
+}
+
 bool IsTimeSorted(const Packets& packets) {
   PacketsConstIt last_it = packets.begin();
   for (PacketsConstIt it = last_it; it != packets.end(); ++it) {
@@ -24,6 +80,261 @@
   }
   return true;
 }
+
+PacketProcessor::PacketProcessor(PacketProcessorListener* listener)
+    : listener_(listener) {
+  if (listener_) {
+    listener_->AddPacketProcessor(this);
+  }
+}
+
+PacketProcessor::~PacketProcessor() {
+  if (listener_) {
+    listener_->RemovePacketProcessor(this);
+  }
+}
+
+RateCounterFilter::RateCounterFilter(PacketProcessorListener* listener)
+    : PacketProcessor(listener),
+      kWindowSizeUs(1000000),
+      packets_per_second_(0),
+      bytes_per_second_(0),
+      last_accumulated_us_(0),
+      window_(),
+      pps_stats_(),
+      kbps_stats_() {
+}
+
+RateCounterFilter::~RateCounterFilter() {
+  LogStats();
+}
+
+void RateCounterFilter::LogStats() {
+  BWE_TEST_LOGGING_CONTEXT("RateCounterFilter");
+  pps_stats_.Log("pps");
+  kbps_stats_.Log("kbps");
+}
+
+void RateCounterFilter::RunFor(int64_t /*time_ms*/, Packets* in_out) {
+  assert(in_out);
+  for (PacketsConstIt it = in_out->begin(); it != in_out->end(); ++it) {
+    packets_per_second_++;
+    bytes_per_second_ += it->payload_size();
+    last_accumulated_us_ = it->send_time_us();
+  }
+  window_.insert(window_.end(), in_out->begin(), in_out->end());
+  while (!window_.empty()) {
+    const Packet& packet = window_.front();
+    if (packet.send_time_us() > (last_accumulated_us_ - kWindowSizeUs)) {
+      break;
+    }
+    assert(packets_per_second_ >= 1);
+    assert(bytes_per_second_ >= packet.payload_size());
+    packets_per_second_--;
+    bytes_per_second_ -= packet.payload_size();
+    window_.pop_front();
+  }
+  pps_stats_.Push(packets_per_second_);
+  kbps_stats_.Push((bytes_per_second_ * 8) / 1000.0);
+}
+
+LossFilter::LossFilter(PacketProcessorListener* listener)
+    : PacketProcessor(listener),
+      random_(0x12345678),
+      loss_fraction_(0.0f) {
+}
+
+void LossFilter::SetLoss(float loss_percent) {
+  BWE_TEST_LOGGING_ENABLE(false);
+  BWE_TEST_LOGGING_LOG1("Loss", "%f%%", loss_percent);
+  assert(loss_percent >= 0.0f);
+  assert(loss_percent <= 100.0f);
+  loss_fraction_ = loss_percent * 0.01f;
+}
+
+void LossFilter::RunFor(int64_t /*time_ms*/, Packets* in_out) {
+  assert(in_out);
+  for (PacketsIt it = in_out->begin(); it != in_out->end(); ) {
+    if (random_.Rand() < loss_fraction_) {
+      it = in_out->erase(it);
+    } else {
+      ++it;
+    }
+  }
+}
+
+DelayFilter::DelayFilter(PacketProcessorListener* listener)
+    : PacketProcessor(listener),
+      delay_us_(0),
+      last_send_time_us_(0) {
+}
+
+void DelayFilter::SetDelay(int64_t delay_ms) {
+  BWE_TEST_LOGGING_ENABLE(false);
+  BWE_TEST_LOGGING_LOG1("Delay", "%d ms", static_cast<int>(delay_ms));
+  assert(delay_ms >= 0);
+  delay_us_ = delay_ms * 1000;
+}
+
+void DelayFilter::RunFor(int64_t /*time_ms*/, Packets* in_out) {
+  assert(in_out);
+  for (PacketsIt it = in_out->begin(); it != in_out->end(); ++it) {
+    int64_t new_send_time_us = it->send_time_us() + delay_us_;
+    last_send_time_us_ = std::max(last_send_time_us_, new_send_time_us);
+    it->set_send_time_us(last_send_time_us_);
+  }
+}
+
+JitterFilter::JitterFilter(PacketProcessorListener* listener)
+    : PacketProcessor(listener),
+      random_(0x89674523),
+      stddev_jitter_us_(0),
+      last_send_time_us_(0) {
+}
+
+void JitterFilter::SetJitter(int64_t stddev_jitter_ms) {
+  BWE_TEST_LOGGING_ENABLE(false);
+  BWE_TEST_LOGGING_LOG1("Jitter", "%d ms",
+                        static_cast<int>(stddev_jitter_ms));
+  assert(stddev_jitter_ms >= 0);
+  stddev_jitter_us_ = stddev_jitter_ms * 1000;
+}
+
+void JitterFilter::RunFor(int64_t /*time_ms*/, Packets* in_out) {
+  assert(in_out);
+  for (PacketsIt it = in_out->begin(); it != in_out->end(); ++it) {
+    int64_t new_send_time_us = it->send_time_us();
+    new_send_time_us += random_.Gaussian(0, stddev_jitter_us_);
+    last_send_time_us_ = std::max(last_send_time_us_, new_send_time_us);
+    it->set_send_time_us(last_send_time_us_);
+  }
+}
+
+ReorderFilter::ReorderFilter(PacketProcessorListener* listener)
+    : PacketProcessor(listener),
+      random_(0x27452389),
+      reorder_fraction_(0.0f) {
+}
+
+void ReorderFilter::SetReorder(float reorder_percent) {
+  BWE_TEST_LOGGING_ENABLE(false);
+  BWE_TEST_LOGGING_LOG1("Reordering", "%f%%", reorder_percent);
+  assert(reorder_percent >= 0.0f);
+  assert(reorder_percent <= 100.0f);
+  reorder_fraction_ = reorder_percent * 0.01f;
+}
+
+void ReorderFilter::RunFor(int64_t /*time_ms*/, Packets* in_out) {
+  assert(in_out);
+  if (in_out->size() >= 2) {
+    PacketsIt last_it = in_out->begin();
+    PacketsIt it = last_it;
+    while (++it != in_out->end()) {
+      if (random_.Rand() < reorder_fraction_) {
+        int64_t t1 = last_it->send_time_us();
+        int64_t t2 = it->send_time_us();
+        std::swap(*last_it, *it);
+        last_it->set_send_time_us(t1);
+        it->set_send_time_us(t2);
+      }
+      last_it = it;
+    }
+  }
+}
+
+ChokeFilter::ChokeFilter(PacketProcessorListener* listener)
+    : PacketProcessor(listener),
+      kbps_(1200),
+      max_delay_us_(0),
+      last_send_time_us_(0) {
+}
+
+void ChokeFilter::SetCapacity(uint32_t kbps) {
+  BWE_TEST_LOGGING_ENABLE(false);
+  BWE_TEST_LOGGING_LOG1("BitrateChoke", "%d kbps", kbps);
+  kbps_ = kbps;
+}
+
+void ChokeFilter::SetMaxDelay(int64_t max_delay_ms) {
+  BWE_TEST_LOGGING_ENABLE(false);
+  BWE_TEST_LOGGING_LOG1("Max Delay", "%d ms", static_cast<int>(max_delay_ms));
+  assert(max_delay_ms >= 0);
+  max_delay_us_ = max_delay_ms * 1000;
+}
+
+void ChokeFilter::RunFor(int64_t /*time_ms*/, Packets* in_out) {
+  assert(in_out);
+  for (PacketsIt it = in_out->begin(); it != in_out->end(); ) {
+    int64_t earliest_send_time_us = last_send_time_us_ +
+        (it->payload_size() * 8 * 1000 + kbps_ / 2) / kbps_;
+    int64_t new_send_time_us = std::max(it->send_time_us(),
+                                        earliest_send_time_us);
+    if (max_delay_us_ == 0 ||
+        max_delay_us_ >= (new_send_time_us - it->send_time_us())) {
+      it->set_send_time_us(new_send_time_us);
+      last_send_time_us_ = new_send_time_us;
+      ++it;
+    } else {
+      it = in_out->erase(it);
+    }
+  }
+}
+
+PacketSender::PacketSender(PacketProcessorListener* listener)
+    : PacketProcessor(listener) {
+}
+
+VideoSender::VideoSender(PacketProcessorListener* listener, float fps,
+                         uint32_t kbps, uint32_t ssrc, float first_frame_offset)
+    : PacketSender(listener),
+      kMaxPayloadSizeBytes(1000),
+      kTimestampBase(0xff80ff00ul),
+      frame_period_ms_(1000.0 / fps),
+      next_frame_ms_(frame_period_ms_ * first_frame_offset),
+      now_ms_(0.0),
+      bytes_per_second_((1000 * kbps) / 8),
+      frame_size_bytes_(bytes_per_second_ / fps),
+      prototype_header_() {
+  assert(first_frame_offset >= 0.0f);
+  assert(first_frame_offset < 1.0f);
+  memset(&prototype_header_, 0, sizeof(prototype_header_));
+  prototype_header_.ssrc = ssrc;
+  prototype_header_.sequenceNumber = 0xf000u;
+}
+
+uint32_t VideoSender::GetCapacityKbps() const {
+  return (bytes_per_second_ * 8) / 1000;
+}
+
+void VideoSender::RunFor(int64_t time_ms, Packets* in_out) {
+  assert(in_out);
+  now_ms_ += time_ms;
+  Packets newPackets;
+  while (now_ms_ >= next_frame_ms_) {
+    prototype_header_.sequenceNumber++;
+    prototype_header_.timestamp = kTimestampBase +
+        static_cast<uint32_t>(next_frame_ms_ * 90.0);
+    prototype_header_.extension.absoluteSendTime = (kTimestampBase +
+        ((static_cast<int64_t>(next_frame_ms_ * (1 << 18)) + 500) / 1000)) &
+            0x00fffffful;
+    prototype_header_.extension.transmissionTimeOffset = 0;
+
+    // Generate new packets for this frame, all with the same timestamp,
+    // but the payload size is capped, so if the whole frame doesn't fit in
+    // one packet, we will see a number of equally sized packets followed by
+    // one smaller at the tail.
+    int64_t send_time_us = next_frame_ms_ * 1000.0;
+    uint32_t payload_size = frame_size_bytes_;
+    while (payload_size > 0) {
+      uint32_t size = std::min(kMaxPayloadSizeBytes, payload_size);
+      newPackets.push_back(Packet(send_time_us, size, prototype_header_));
+      payload_size -= size;
+    }
+
+    next_frame_ms_ += frame_period_ms_;
+  }
+  in_out->merge(newPackets);
+}
 }  // namespace bwe
 }  // namespace testing
 }  // namespace webrtc
diff --git a/modules/remote_bitrate_estimator/test/bwe_test_framework.h b/modules/remote_bitrate_estimator/test/bwe_test_framework.h
index 3b340bc..c19cb2b 100644
--- a/modules/remote_bitrate_estimator/test/bwe_test_framework.h
+++ b/modules/remote_bitrate_estimator/test/bwe_test_framework.h
@@ -26,44 +26,6 @@
 namespace testing {
 namespace bwe {
 
-class Random {
- public:
-  explicit Random(uint32_t seed)
-      : a_(0x531FDB97 ^ seed),
-        b_(0x6420ECA8 + seed) {
-  }
-
-  // Return semi-random number in the interval [0.0, 1.0].
-  float Rand() {
-    const float kScale = 1.0f / 0xffffffff;
-    float result = kScale * b_;
-    a_ ^= b_;
-    b_ += a_;
-    return result;
-  }
-
-  // Normal Distribution.
-  int Gaussian(int mean, int standard_deviation) {
-    // Creating a Normal distribution variable from two independent uniform
-    // variables based on the Box-Muller transform, which is defined on the
-    // interval (0, 1], hence the mask+add below.
-    const double kPi = 3.14159265358979323846;
-    const double kScale = 1.0 / 0x80000000ul;
-    double u1 = kScale * ((a_ & 0x7ffffffful) + 1);
-    double u2 = kScale * ((b_ & 0x7ffffffful) + 1);
-    a_ ^= b_;
-    b_ += a_;
-    return static_cast<int>(mean + standard_deviation *
-        std::sqrt(-2 * std::log(u1)) * std::cos(2 * kPi * u2));
-  }
-
- private:
-  uint32_t a_;
-  uint32_t b_;
-
-  DISALLOW_IMPLICIT_CONSTRUCTORS(Random);
-};
-
 template<typename T> class Stats {
  public:
   Stats()
@@ -149,36 +111,36 @@
   T max_;
 };
 
-class BwePacket {
+class Random {
  public:
-  BwePacket()
-      : send_time_us_(0),
-        payload_size_(0) {
-     memset(&header_, 0, sizeof(header_));
-  }
+  explicit Random(uint32_t seed);
 
-  BwePacket(int64_t send_time_us, uint32_t payload_size,
-            const RTPHeader& header)
-    : send_time_us_(send_time_us),
-      payload_size_(payload_size),
-      header_(header) {
-  }
+  // Return pseudo random number in the interval [0.0, 1.0].
+  float Rand();
 
-  BwePacket(int64_t send_time_us, uint32_t sequence_number)
-      : send_time_us_(send_time_us),
-        payload_size_(0) {
-     memset(&header_, 0, sizeof(header_));
-     header_.sequenceNumber = sequence_number;
-  }
+  // Normal Distribution.
+  int Gaussian(int mean, int standard_deviation);
 
-  bool operator<(const BwePacket& rhs) const {
-    return send_time_us_ < rhs.send_time_us_;
-  }
+  // TODO(solenberg): Random from histogram.
+  // template<typename T> int Distribution(const std::vector<T> histogram) {
 
-  void set_send_time_us(int64_t send_time_us) {
-    assert(send_time_us >= 0);
-    send_time_us_ = send_time_us;
-  }
+ private:
+  uint32_t a_;
+  uint32_t b_;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(Random);
+};
+
+class Packet {
+ public:
+  Packet();
+  Packet(int64_t send_time_us, uint32_t payload_size,
+            const RTPHeader& header);
+  Packet(int64_t send_time_us, uint32_t sequence_number);
+
+  bool operator<(const Packet& rhs) const;
+
+  void set_send_time_us(int64_t send_time_us);
   int64_t send_time_us() const { return send_time_us_; }
   uint32_t payload_size() const { return payload_size_; }
   const RTPHeader& header() const { return header_; }
@@ -189,73 +151,180 @@
   RTPHeader header_;       // Actual contents.
 };
 
-typedef std::list<BwePacket> Packets;
-typedef std::list<BwePacket>::iterator PacketsIt;
-typedef std::list<BwePacket>::const_iterator PacketsConstIt;
+typedef std::list<Packet> Packets;
+typedef std::list<Packet>::iterator PacketsIt;
+typedef std::list<Packet>::const_iterator PacketsConstIt;
 
 bool IsTimeSorted(const Packets& packets);
 
-class PacketProcessorInterface {
+class PacketProcessor;
+
+class PacketProcessorListener {
  public:
-  virtual ~PacketProcessorInterface() {}
+  virtual ~PacketProcessorListener() {}
+
+  virtual void AddPacketProcessor(PacketProcessor* processor) = 0;
+  virtual void RemovePacketProcessor(PacketProcessor* processor) = 0;
+};
+
+class PacketProcessor {
+ public:
+  explicit PacketProcessor(PacketProcessorListener* listener);
+  virtual ~PacketProcessor();
 
   // Run simulation for |time_ms| micro seconds, consuming packets from, and
   // producing packets into in_out. The outgoing packet list must be sorted on
   // |send_time_us_|. The simulation time |time_ms| is optional to use.
   virtual void RunFor(int64_t time_ms, Packets* in_out) = 0;
+
+ private:
+  PacketProcessorListener* listener_;
+
+  DISALLOW_COPY_AND_ASSIGN(PacketProcessor);
 };
 
-class VideoSender : public PacketProcessorInterface {
+class RateCounterFilter : public PacketProcessor {
  public:
-  VideoSender(float fps, uint32_t kbps, uint32_t ssrc, float first_frame_offset)
-      : kMaxPayloadSizeBytes(1000),
-        kTimestampBase(0xff80ff00ul),
-        frame_period_ms_(1000.0 / fps),
-        next_frame_ms_(frame_period_ms_ * first_frame_offset),
-        now_ms_(0.0),
-        bytes_per_second_(1000 * kbps / 8),
-        frame_size_bytes_(bytes_per_second_ / fps),
-        prototype_header_() {
-    assert(first_frame_offset >= 0.0f);
-    assert(first_frame_offset < 1.0f);
-    memset(&prototype_header_, 0, sizeof(prototype_header_));
-    prototype_header_.ssrc = ssrc;
-    prototype_header_.sequenceNumber = 0xf000u;
-  }
+  explicit RateCounterFilter(PacketProcessorListener* listener);
+  virtual ~RateCounterFilter();
+
+  uint32_t packets_per_second() const { return packets_per_second_; }
+  uint32_t bits_per_second() const { return bytes_per_second_ * 8; }
+
+  void LogStats();
+  virtual void RunFor(int64_t time_ms, Packets* in_out);
+
+ private:
+  const int64_t kWindowSizeUs;
+  uint32_t packets_per_second_;
+  uint32_t bytes_per_second_;
+  int64_t last_accumulated_us_;
+  Packets window_;
+  Stats<double> pps_stats_;
+  Stats<double> kbps_stats_;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(RateCounterFilter);
+};
+
+class LossFilter : public PacketProcessor {
+ public:
+  explicit LossFilter(PacketProcessorListener* listener);
+  virtual ~LossFilter() {}
+
+  void SetLoss(float loss_percent);
+  virtual void RunFor(int64_t time_ms, Packets* in_out);
+
+ private:
+  Random random_;
+  float loss_fraction_;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(LossFilter);
+};
+
+class DelayFilter : public PacketProcessor {
+ public:
+  explicit DelayFilter(PacketProcessorListener* listener);
+  virtual ~DelayFilter() {}
+
+  void SetDelay(int64_t delay_ms);
+  virtual void RunFor(int64_t time_ms, Packets* in_out);
+
+ private:
+  int64_t delay_us_;
+  int64_t last_send_time_us_;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(DelayFilter);
+};
+
+class JitterFilter : public PacketProcessor {
+ public:
+  explicit JitterFilter(PacketProcessorListener* listener);
+  virtual ~JitterFilter() {}
+
+  void SetJitter(int64_t stddev_jitter_ms);
+  virtual void RunFor(int64_t time_ms, Packets* in_out);
+
+ private:
+  Random random_;
+  int64_t stddev_jitter_us_;
+  int64_t last_send_time_us_;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(JitterFilter);
+};
+
+class ReorderFilter : public PacketProcessor {
+ public:
+  explicit ReorderFilter(PacketProcessorListener* listener);
+  virtual ~ReorderFilter() {}
+
+  void SetReorder(float reorder_percent);
+  virtual void RunFor(int64_t time_ms, Packets* in_out);
+
+ private:
+  Random random_;
+  float reorder_fraction_;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(ReorderFilter);
+};
+
+// Apply a bitrate choke with an infinite queue on the packet stream.
+class ChokeFilter : public PacketProcessor {
+ public:
+  explicit ChokeFilter(PacketProcessorListener* listener);
+  virtual ~ChokeFilter() {}
+
+  void SetCapacity(uint32_t kbps);
+  void SetMaxDelay(int64_t max_delay_ms);
+  virtual void RunFor(int64_t time_ms, Packets* in_out);
+
+ private:
+  uint32_t kbps_;
+  int64_t max_delay_us_;
+  int64_t last_send_time_us_;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(ChokeFilter);
+};
+
+class PacketSender : public PacketProcessor {
+ public:
+  struct Feedback {
+    double estimated_kbps;
+  };
+
+  explicit PacketSender(PacketProcessorListener* listener);
+  virtual ~PacketSender() {}
+
+  virtual uint32_t GetCapacityKbps() const { return 0; }
+
+  // Call GiveFeedback() with the returned interval in milliseconds, provided
+  // there is a new estimate available.
+  virtual int64_t GetFeedbackIntervalMs() const { return 1000; }
+  virtual void GiveFeedback(const Feedback& feedback) {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PacketSender);
+};
+
+struct PacketSenderFactory {
+  PacketSenderFactory() {}
+  virtual ~PacketSenderFactory() {}
+  virtual PacketSender* Create() const = 0;
+};
+
+class VideoSender : public PacketSender {
+ public:
+  VideoSender(PacketProcessorListener* listener, float fps, uint32_t kbps,
+              uint32_t ssrc, float first_frame_offset);
   virtual ~VideoSender() {}
 
   uint32_t max_payload_size_bytes() const { return kMaxPayloadSizeBytes; }
   uint32_t bytes_per_second() const { return bytes_per_second_; }
 
-  virtual void RunFor(int64_t time_ms, Packets* in_out) {
-    assert(in_out);
-    now_ms_ += time_ms;
-    Packets newPackets;
-    while (now_ms_ >= next_frame_ms_) {
-      prototype_header_.sequenceNumber++;
-      prototype_header_.timestamp = kTimestampBase +
-          static_cast<uint32_t>(next_frame_ms_ * 90.0);
-      prototype_header_.extension.absoluteSendTime = (kTimestampBase +
-          ((static_cast<int64_t>(next_frame_ms_ * (1 << 18)) + 500) / 1000)) &
-              0x00fffffful;
-      prototype_header_.extension.transmissionTimeOffset = 0;
+  virtual uint32_t GetCapacityKbps() const;
 
-      // Generate new packets for this frame, all with the same timestamp,
-      // but the payload size is capped, so if the whole frame doesn't fit in
-      // one packet, we will see a number of equally sized packets followed by
-      // one smaller at the tail.
-      int64_t send_time_us = next_frame_ms_ * 1000.0;
-      uint32_t payload_size = frame_size_bytes_;
-      while (payload_size > 0) {
-        uint32_t size = std::min(kMaxPayloadSizeBytes, payload_size);
-        newPackets.push_back(BwePacket(send_time_us, size, prototype_header_));
-        payload_size -= size;
-      }
-
-      next_frame_ms_ += frame_period_ms_;
-    }
-    in_out->merge(newPackets);
-  }
+  // TODO(solenberg): void SetFrameRate(float fps);
+  // TODO(solenberg): void SetRate(uint32_t kbps);
+  virtual void RunFor(int64_t time_ms, Packets* in_out);
 
  private:
   const uint32_t kMaxPayloadSizeBytes;
@@ -269,242 +338,6 @@
 
   DISALLOW_IMPLICIT_CONSTRUCTORS(VideoSender);
 };
-
-class RateCounterFilter : public PacketProcessorInterface {
- public:
-  RateCounterFilter()
-      : kWindowSizeUs(1000000),
-        packets_per_second_(0),
-        bytes_per_second_(0),
-        last_accumulated_us_(0),
-        window_(),
-        pps_stats_(),
-        kbps_stats_() {
-  }
-  virtual ~RateCounterFilter() {
-    LogStats();
-  }
-
-  uint32_t packets_per_second() const { return packets_per_second_; }
-  uint32_t bits_per_second() const { return bytes_per_second_ * 8; }
-
-  void LogStats() {
-    BWE_TEST_LOGGING_CONTEXT("RateCounterFilter");
-    pps_stats_.Log("pps");
-    kbps_stats_.Log("kbps");
-  }
-
-  virtual void RunFor(int64_t /*time_ms*/, Packets* in_out) {
-    assert(in_out);
-    for (PacketsConstIt it = in_out->begin(); it != in_out->end(); ++it) {
-      packets_per_second_++;
-      bytes_per_second_ += it->payload_size();
-      last_accumulated_us_ = it->send_time_us();
-    }
-    window_.insert(window_.end(), in_out->begin(), in_out->end());
-    while (!window_.empty()) {
-      const BwePacket& packet = window_.front();
-      if (packet.send_time_us() > (last_accumulated_us_ - kWindowSizeUs)) {
-        break;
-      }
-      assert(packets_per_second_ >= 1);
-      assert(bytes_per_second_ >= packet.payload_size());
-      packets_per_second_--;
-      bytes_per_second_ -= packet.payload_size();
-      window_.pop_front();
-    }
-    pps_stats_.Push(packets_per_second_);
-    kbps_stats_.Push((bytes_per_second_ * 8) / 1000.0);
-  }
-
- private:
-  const int64_t kWindowSizeUs;
-  uint32_t packets_per_second_;
-  uint32_t bytes_per_second_;
-  int64_t last_accumulated_us_;
-  Packets window_;
-  Stats<double> pps_stats_;
-  Stats<double> kbps_stats_;
-
-  DISALLOW_COPY_AND_ASSIGN(RateCounterFilter);
-};
-
-class LossFilter : public PacketProcessorInterface {
- public:
-  LossFilter() : random_(0x12345678), loss_fraction_(0.0f) {}
-  virtual ~LossFilter() {}
-
-  void SetLoss(float loss_percent) {
-    BWE_TEST_LOGGING_ENABLE(false);
-    BWE_TEST_LOGGING_LOG1("Loss", "%f%%", loss_percent);
-    assert(loss_percent >= 0.0f);
-    assert(loss_percent <= 100.0f);
-    loss_fraction_ = loss_percent * 0.01f;
-  }
-
-  virtual void RunFor(int64_t /*time_ms*/, Packets* in_out) {
-    assert(in_out);
-    for (PacketsIt it = in_out->begin(); it != in_out->end(); ) {
-      if (random_.Rand() < loss_fraction_) {
-        it = in_out->erase(it);
-      } else {
-        ++it;
-      }
-    }
-  }
-
- private:
-  Random random_;
-  float loss_fraction_;
-
-  DISALLOW_COPY_AND_ASSIGN(LossFilter);
-};
-
-class DelayFilter : public PacketProcessorInterface {
- public:
-  DelayFilter() : delay_us_(0), last_send_time_us_(0) {}
-  virtual ~DelayFilter() {}
-
-  void SetDelay(int64_t delay_ms) {
-    BWE_TEST_LOGGING_ENABLE(false);
-    BWE_TEST_LOGGING_LOG1("Delay", "%d ms", static_cast<int>(delay_ms));
-    assert(delay_ms >= 0);
-    delay_us_ = delay_ms * 1000;
-  }
-
-  virtual void RunFor(int64_t /*time_ms*/, Packets* in_out) {
-    assert(in_out);
-    for (PacketsIt it = in_out->begin(); it != in_out->end(); ++it) {
-      int64_t new_send_time_us = it->send_time_us() + delay_us_;
-      last_send_time_us_ = std::max(last_send_time_us_, new_send_time_us);
-      it->set_send_time_us(last_send_time_us_);
-    }
-  }
-
- private:
-  int64_t delay_us_;
-  int64_t last_send_time_us_;
-
-  DISALLOW_COPY_AND_ASSIGN(DelayFilter);
-};
-
-class JitterFilter : public PacketProcessorInterface {
- public:
-  JitterFilter()
-      : random_(0x89674523),
-        stddev_jitter_us_(0),
-        last_send_time_us_(0) {
-  }
-  virtual ~JitterFilter() {}
-
-  void SetJitter(int64_t stddev_jitter_ms) {
-    BWE_TEST_LOGGING_ENABLE(false);
-    BWE_TEST_LOGGING_LOG1("Jitter", "%d ms",
-                          static_cast<int>(stddev_jitter_ms));
-    assert(stddev_jitter_ms >= 0);
-    stddev_jitter_us_ = stddev_jitter_ms * 1000;
-  }
-
-  virtual void RunFor(int64_t /*time_ms*/, Packets* in_out) {
-    assert(in_out);
-    for (PacketsIt it = in_out->begin(); it != in_out->end(); ++it) {
-      int64_t new_send_time_us = it->send_time_us();
-      new_send_time_us += random_.Gaussian(0, stddev_jitter_us_);
-      last_send_time_us_ = std::max(last_send_time_us_, new_send_time_us);
-      it->set_send_time_us(last_send_time_us_);
-    }
-  }
-
- private:
-  Random random_;
-  int64_t stddev_jitter_us_;
-  int64_t last_send_time_us_;
-
-  DISALLOW_COPY_AND_ASSIGN(JitterFilter);
-};
-
-class ReorderFilter : public PacketProcessorInterface {
- public:
-  ReorderFilter() : random_(0x27452389), reorder_fraction_(0.0f) {}
-  virtual ~ReorderFilter() {}
-
-  void SetReorder(float reorder_percent) {
-    BWE_TEST_LOGGING_ENABLE(false);
-    BWE_TEST_LOGGING_LOG1("Reordering", "%f%%", reorder_percent);
-    assert(reorder_percent >= 0.0f);
-    assert(reorder_percent <= 100.0f);
-    reorder_fraction_ = reorder_percent * 0.01f;
-  }
-
-  virtual void RunFor(int64_t /*time_ms*/, Packets* in_out) {
-    assert(in_out);
-    if (in_out->size() >= 2) {
-      PacketsIt last_it = in_out->begin();
-      PacketsIt it = last_it;
-      while (++it != in_out->end()) {
-        if (random_.Rand() < reorder_fraction_) {
-          int64_t t1 = last_it->send_time_us();
-          int64_t t2 = it->send_time_us();
-          std::swap(*last_it, *it);
-          last_it->set_send_time_us(t1);
-          it->set_send_time_us(t2);
-        }
-        last_it = it;
-      }
-    }
-  }
-
- private:
-  Random random_;
-  float reorder_fraction_;
-
-  DISALLOW_COPY_AND_ASSIGN(ReorderFilter);
-};
-
-// Apply a bitrate choke with an infinite queue on the packet stream.
-class ChokeFilter : public PacketProcessorInterface {
- public:
-  ChokeFilter() : kbps_(1200), max_delay_us_(0), last_send_time_us_(0) {}
-  virtual ~ChokeFilter() {}
-
-  void SetCapacity(uint32_t kbps) {
-    BWE_TEST_LOGGING_ENABLE(false);
-    BWE_TEST_LOGGING_LOG1("BitrateChoke", "%d kbps", kbps);
-    kbps_ = kbps;
-  }
-
-  void SetMaxDelay(int64_t max_delay_ms) {
-    BWE_TEST_LOGGING_ENABLE(false);
-    BWE_TEST_LOGGING_LOG1("Max Delay", "%d ms", static_cast<int>(max_delay_ms));
-    assert(max_delay_ms >= 0);
-    max_delay_us_ = max_delay_ms * 1000;
-  }
-
-  virtual void RunFor(int64_t /*time_ms*/, Packets* in_out) {
-    assert(in_out);
-    for (PacketsIt it = in_out->begin(); it != in_out->end(); ) {
-      int64_t earliest_send_time_us = last_send_time_us_ +
-          (it->payload_size() * 8 * 1000 + kbps_ / 2) / kbps_;
-      int64_t new_send_time_us = std::max(it->send_time_us(),
-                                          earliest_send_time_us);
-      if (max_delay_us_ == 0 ||
-          max_delay_us_ >= (new_send_time_us - it->send_time_us())) {
-        it->set_send_time_us(new_send_time_us);
-        last_send_time_us_ = new_send_time_us;
-        ++it;
-      } else {
-        it = in_out->erase(it);
-      }
-    }
-  }
-
- private:
-  uint32_t kbps_;
-  int64_t max_delay_us_;
-  int64_t last_send_time_us_;
-
-  DISALLOW_COPY_AND_ASSIGN(ChokeFilter);
-};
 }  // namespace bwe
 }  // namespace testing
 }  // namespace webrtc
diff --git a/modules/remote_bitrate_estimator/test/bwe_test_framework_unittest.cc b/modules/remote_bitrate_estimator/test/bwe_test_framework_unittest.cc
index b6a7123..440044a 100644
--- a/modules/remote_bitrate_estimator/test/bwe_test_framework_unittest.cc
+++ b/modules/remote_bitrate_estimator/test/bwe_test_framework_unittest.cc
@@ -66,45 +66,45 @@
   return true;
 }
 
-TEST(BweTestFramework_BwePacketTest, IsTimeSorted) {
+TEST(BweTestFramework_PacketTest, IsTimeSorted) {
   Packets packets;
   // Insert some packets in order...
   EXPECT_TRUE(IsTimeSorted(packets));
 
-  packets.push_back(BwePacket(100, 0));
+  packets.push_back(Packet(100, 0));
   EXPECT_TRUE(IsTimeSorted(packets));
 
-  packets.push_back(BwePacket(110, 0));
+  packets.push_back(Packet(110, 0));
   EXPECT_TRUE(IsTimeSorted(packets));
 
   // ...and one out-of-order...
-  packets.push_back(BwePacket(100, 0));
+  packets.push_back(Packet(100, 0));
   EXPECT_FALSE(IsTimeSorted(packets));
 
   // ...remove the out-of-order packet, insert another in-order packet.
   packets.pop_back();
-  packets.push_back(BwePacket(120, 0));
+  packets.push_back(Packet(120, 0));
   EXPECT_TRUE(IsTimeSorted(packets));
 }
 
-TEST(BweTestFramework_BwePacketTest, IsSequenceNumberSorted) {
+TEST(BweTestFramework_PacketTest, IsSequenceNumberSorted) {
   Packets packets;
   // Insert some packets in order...
   EXPECT_TRUE(IsSequenceNumberSorted(packets));
 
-  packets.push_back(BwePacket(0, 100));
+  packets.push_back(Packet(0, 100));
   EXPECT_TRUE(IsSequenceNumberSorted(packets));
 
-  packets.push_back(BwePacket(0, 110));
+  packets.push_back(Packet(0, 110));
   EXPECT_TRUE(IsSequenceNumberSorted(packets));
 
   // ...and one out-of-order...
-  packets.push_back(BwePacket(0, 100));
+  packets.push_back(Packet(0, 100));
   EXPECT_FALSE(IsSequenceNumberSorted(packets));
 
   // ...remove the out-of-order packet, insert another in-order packet.
   packets.pop_back();
-  packets.push_back(BwePacket(0, 120));
+  packets.push_back(Packet(0, 120));
   EXPECT_TRUE(IsSequenceNumberSorted(packets));
 }
 
@@ -168,167 +168,10 @@
   EXPECT_EQ(3, stats.GetMax());
 }
 
-void TestVideoSender(VideoSender* sender, int64_t run_for_ms,
-                     uint32_t expected_packets,
-                     uint32_t expected_payload_size,
-                     uint32_t expected_total_payload_size) {
-  assert(sender);
-  Packets packets;
-  sender->RunFor(run_for_ms, &packets);
-  ASSERT_TRUE(IsTimeSorted(packets));
-  ASSERT_TRUE(IsSequenceNumberSorted(packets));
-  EXPECT_EQ(expected_packets, packets.size());
-  int64_t send_time_us = -1;
-  uint32_t total_payload_size = 0;
-  uint32_t absolute_send_time = 0;
-  uint32_t absolute_send_time_wraps = 0;
-  uint32_t rtp_timestamp = 0;
-  uint32_t rtp_timestamp_wraps = 0;
-  for (PacketsIt it = packets.begin(); it != packets.end(); ++it) {
-    EXPECT_LE(send_time_us, it->send_time_us());
-    send_time_us = it->send_time_us();
-    if (sender->max_payload_size_bytes() != it->payload_size()) {
-      EXPECT_EQ(expected_payload_size, it->payload_size());
-    }
-    total_payload_size += it->payload_size();
-    if (absolute_send_time > it->header().extension.absoluteSendTime) {
-      absolute_send_time_wraps++;
-    }
-    absolute_send_time = it->header().extension.absoluteSendTime;
-    if (rtp_timestamp > it->header().timestamp) {
-      rtp_timestamp_wraps++;
-    }
-    rtp_timestamp = it->header().timestamp;
-  }
-  EXPECT_EQ(expected_total_payload_size, total_payload_size);
-  EXPECT_GE(1u, absolute_send_time_wraps);
-  EXPECT_GE(1u, rtp_timestamp_wraps);
-}
-
-TEST(BweTestFramework_VideoSenderTest, Fps1Kpbs80_1s) {
-  // 1 fps, 80 kbps
-  VideoSender sender(1.0f, 80, 0x1234, 0);
-  EXPECT_EQ(10000u, sender.bytes_per_second());
-  // We're at 1 fps, so all packets should be generated on first call, giving 10
-  // packets of each 1000 bytes, total 10000 bytes.
-  TestVideoSender(&sender, 1, 10, 1000, 10000);
-  // 999ms, should see no output here.
-  TestVideoSender(&sender, 998, 0, 0, 0);
-  // 1999ms, should get data for one more frame.
-  TestVideoSender(&sender, 1000, 10, 1000, 10000);
-  // 2000ms, one more frame.
-  TestVideoSender(&sender, 1, 10, 1000, 10000);
-  // 2999ms, should see nothing.
-  TestVideoSender(&sender, 999, 0, 0, 0);
-}
-
-TEST(BweTestFramework_VideoSenderTest, Fps1Kpbs80_1s_Offset) {
-  // 1 fps, 80 kbps, offset 0.5 of a frame period, ==0.5s in this case.
-  VideoSender sender(1.0f, 80, 0x1234, 0.5f);
-  EXPECT_EQ(10000u, sender.bytes_per_second());
-  // 499ms, no output.
-  TestVideoSender(&sender, 499, 0, 0, 0);
-  // 500ms, first frame (this is the offset we set), 10 packets of 1000 bytes.
-  TestVideoSender(&sender, 1, 10, 1000, 10000);
-  // 1499ms, nothing.
-  TestVideoSender(&sender, 999, 0, 0, 0);
-  // 1999ms, second frame.
-  TestVideoSender(&sender, 500, 10, 1000, 10000);
-  // 2499ms, nothing.
-  TestVideoSender(&sender, 500, 0, 0, 0);
-  // 2500ms, third frame.
-  TestVideoSender(&sender, 1, 10, 1000, 10000);
-  // 3499ms, nothing.
-  TestVideoSender(&sender, 999, 0, 0, 0);
-}
-
-TEST(BweTestFramework_VideoSenderTest, Fps50Kpbs80_11s) {
-  // 50 fps, 80 kbps.
-  VideoSender sender(50.0f, 80, 0x1234, 0);
-  EXPECT_EQ(10000u, sender.bytes_per_second());
-  // 9998ms, should see 500 frames, 200 byte payloads, total 100000 bytes.
-  TestVideoSender(&sender, 9998, 500, 200, 100000);
-  // 9999ms, nothing.
-  TestVideoSender(&sender, 1, 0, 0, 0);
-  // 10000ms, 501st frame as a single packet.
-  TestVideoSender(&sender, 1, 1, 200, 200);
-  // 10998ms, 49 more frames.
-  TestVideoSender(&sender, 998, 49, 200, 9800);
-  // 10999ms, nothing.
-  TestVideoSender(&sender, 1, 0, 0, 0);
-}
-
-TEST(BweTestFramework_VideoSenderTest, Fps10Kpbs120_1s) {
-  // 20 fps, 120 kbps.
-  VideoSender sender(20.0f, 120, 0x1234, 0);
-  EXPECT_EQ(15000u, sender.bytes_per_second());
-  // 498ms, 10 frames with 750 byte payloads, total 7500 bytes.
-  TestVideoSender(&sender, 498, 10, 750, 7500);
-  // 499ms, nothing.
-  TestVideoSender(&sender, 1, 0, 0, 0);
-  // 500ms, one more frame.
-  TestVideoSender(&sender, 1, 1, 750, 750);
-  // 998ms, 9 more frames.
-  TestVideoSender(&sender, 498, 9, 750, 6750);
-  // 999ms, nothing.
-  TestVideoSender(&sender, 1, 0, 0, 0);
-}
-
-TEST(BweTestFramework_VideoSenderTest, Fps30Kpbs800_20s) {
-  // 20 fps, 820 kbps.
-  VideoSender sender(25.0f, 820, 0x1234, 0);
-  EXPECT_EQ(102500u, sender.bytes_per_second());
-  // 9998ms, 250 frames. 820 kbps = 102500 bytes/s, so total should be 1025000.
-  // Each frame is 102500/25=4100 bytes, or 5 packets (4 @1000 bytes, 1 @100),
-  // so packet count should be 5*250=1250 and last packet of each frame has
-  // 100 bytes of payload.
-  TestVideoSender(&sender, 9998, 1250, 100, 1025000);
-  // 9999ms, nothing.
-  TestVideoSender(&sender, 1, 0, 0, 0);
-  // 19998ms, 250 more frames.
-  TestVideoSender(&sender, 9999, 1250, 100, 1025000);
-  // 19999ms, nothing.
-  TestVideoSender(&sender, 1, 0, 0, 0);
-  // 20038ms, one more frame, as described above (25fps == 40ms/frame).
-  TestVideoSender(&sender, 39, 5, 100, 4100);
-  // 20039ms, nothing.
-  TestVideoSender(&sender, 1, 0, 0, 0);
-}
-
-TEST(BweTestFramework_VideoSenderTest, TestAppendInOrder) {
-  // 1 fps, 80 kbps, 250ms offset.
-  VideoSender sender1(1.0f, 80, 0x1234, 0.25f);
-  EXPECT_EQ(10000u, sender1.bytes_per_second());
-  Packets packets;
-  // Generate some packets, verify they are sorted.
-  sender1.RunFor(999, &packets);
-  ASSERT_TRUE(IsTimeSorted(packets));
-  ASSERT_TRUE(IsSequenceNumberSorted(packets));
-  EXPECT_EQ(10u, packets.size());
-  // Generate some more packets and verify they are appended to end of list.
-  sender1.RunFor(1000, &packets);
-  ASSERT_TRUE(IsTimeSorted(packets));
-  ASSERT_TRUE(IsSequenceNumberSorted(packets));
-  EXPECT_EQ(20u, packets.size());
-
-  // Another sender, 2 fps, 160 kpbs, 150ms offset
-  VideoSender sender2(2.0f, 160, 0x2234, 0.30f);
-  EXPECT_EQ(20000u, sender2.bytes_per_second());
-  // Generate some packets, verify that they are merged with the packets already
-  // on the list.
-  sender2.RunFor(999, &packets);
-  ASSERT_TRUE(IsTimeSorted(packets));
-  EXPECT_EQ(40u, packets.size());
-  // Generate some more.
-  sender2.RunFor(1000, &packets);
-  ASSERT_TRUE(IsTimeSorted(packets));
-  EXPECT_EQ(60u, packets.size());
-}
-
 class BweTestFramework_RateCounterFilterTest : public ::testing::Test {
  public:
   BweTestFramework_RateCounterFilterTest()
-    : filter_(),
+    : filter_(NULL),
       now_ms_(0) {
   }
   virtual ~BweTestFramework_RateCounterFilterTest() {}
@@ -340,7 +183,7 @@
     RTPHeader header = {0};
     // "Send" a packet every 10 ms.
     for (int64_t i = 0; i < run_for_ms; i += 10, now_ms_ += 10) {
-      packets.push_back(BwePacket(now_ms_ * 1000, payload_bits / 8, header));
+      packets.push_back(Packet(now_ms_ * 1000, payload_bits / 8, header));
     }
     filter_.RunFor(run_for_ms, &packets);
     ASSERT_TRUE(IsTimeSorted(packets));
@@ -386,7 +229,7 @@
 }
 
 static void TestLossFilter(float loss_percent, bool zero_tolerance) {
-  LossFilter filter;
+  LossFilter filter(NULL);
   filter.SetLoss(loss_percent);
   Packets::size_type sent_packets = 0;
   Packets::size_type remaining_packets = 0;
@@ -406,7 +249,7 @@
   // Generate and process 10000 packets in different batch sizes (some empty)
   for (int i = 0; i < 2225; ++i) {
     Packets packets;
-    packets.insert(packets.end(), i % 10, BwePacket());
+    packets.insert(packets.end(), i % 10, Packet());
     sent_packets += packets.size();
     filter.RunFor(0, &packets);
     ASSERT_TRUE(IsTimeSorted(packets));
@@ -445,7 +288,7 @@
 class BweTestFramework_DelayFilterTest : public ::testing::Test {
  public:
   BweTestFramework_DelayFilterTest()
-    : filter_(),
+    : filter_(NULL),
       now_ms_(0),
       sequence_number_(0) {
   }
@@ -456,7 +299,7 @@
                        uint32_t out_packets) {
     Packets packets;
     for (uint32_t i = 0; i < in_packets; ++i) {
-      packets.push_back(BwePacket(now_ms_ * 1000 + (sequence_number_ >> 4),
+      packets.push_back(Packet(now_ms_ * 1000 + (sequence_number_ >> 4),
                                   sequence_number_));
       sequence_number_++;
     }
@@ -550,14 +393,14 @@
 }
 
 TEST_F(BweTestFramework_DelayFilterTest, JumpToZeroDelay) {
-  DelayFilter delay;
+  DelayFilter delay(NULL);
   Packets acc;
   Packets packets;
 
   // Delay a bunch of packets, accumulate them to the 'acc' list.
   delay.SetDelay(100.0f);
   for (uint32_t i = 0; i < 10; ++i) {
-    packets.push_back(BwePacket(i * 100, i));
+    packets.push_back(Packet(i * 100, i));
   }
   delay.RunFor(1000, &packets);
   acc.splice(acc.end(), packets);
@@ -568,7 +411,7 @@
   // to the 'acc' list and verify that it is all sorted.
   delay.SetDelay(0.0f);
   for (uint32_t i = 10; i < 50; ++i) {
-    packets.push_back(BwePacket(i * 100, i));
+    packets.push_back(Packet(i * 100, i));
   }
   delay.RunFor(1000, &packets);
   acc.splice(acc.end(), packets);
@@ -595,7 +438,7 @@
 }
 
 static void TestJitterFilter(int64_t stddev_jitter_ms) {
-  JitterFilter filter;
+  JitterFilter filter(NULL);
   filter.SetJitter(stddev_jitter_ms);
 
   int64_t now_ms = 0;
@@ -607,7 +450,7 @@
   for (uint32_t i = 0; i < 1000; ++i) {
     Packets packets;
     for (uint32_t j = 0; j < i % 100; ++j) {
-      packets.push_back(BwePacket(now_ms * 1000, sequence_number++));
+      packets.push_back(Packet(now_ms * 1000, sequence_number++));
       now_ms += 5 * stddev_jitter_ms;
     }
     original.insert(original.end(), packets.begin(), packets.end());
@@ -664,13 +507,13 @@
   int64_t now_ms = 0;
   uint32_t sequence_number = 1;
   for (uint32_t i = 0; i < kPacketCount; ++i, now_ms += 10) {
-    packets.push_back(BwePacket(now_ms * 1000, sequence_number++));
+    packets.push_back(Packet(now_ms * 1000, sequence_number++));
   }
   ASSERT_TRUE(IsTimeSorted(packets));
   ASSERT_TRUE(IsSequenceNumberSorted(packets));
 
   // Reorder packets, verify that send times are still in order.
-  ReorderFilter filter;
+  ReorderFilter filter(NULL);
   filter.SetReorder(reorder_percent);
   filter.RunFor(now_ms, &packets);
   ASSERT_TRUE(IsTimeSorted(packets));
@@ -724,7 +567,7 @@
 class BweTestFramework_ChokeFilterTest : public ::testing::Test {
  public:
   BweTestFramework_ChokeFilterTest()
-    : filter_(),
+    : filter_(NULL),
       now_ms_(0),
       sequence_number_(0),
       output_packets_(),
@@ -744,7 +587,7 @@
       int64_t send_time_ms = now_ms_ + (i * run_for_ms) / packets_to_generate;
       header.sequenceNumber = sequence_number_++;
       // Payload is 1000 bits.
-      packets.push_back(BwePacket(send_time_ms * 1000, 125, header));
+      packets.push_back(Packet(send_time_ms * 1000, 125, header));
       send_times_us_.push_back(send_time_ms * 1000);
     }
     ASSERT_TRUE(IsTimeSorted(packets));
@@ -757,7 +600,7 @@
     // Sum up the transmitted bytes up until the current time.
     uint32_t bytes_transmitted = 0;
     while (!output_packets_.empty()) {
-      const BwePacket& packet = output_packets_.front();
+      const Packet& packet = output_packets_.front();
       if (packet.send_time_us() > now_ms_ * 1000) {
         break;
       }
@@ -770,7 +613,7 @@
   void CheckMaxDelay(int64_t max_delay_ms) {
     for (PacketsIt it = output_packets_.begin(); it != output_packets_.end();
         ++it) {
-      const BwePacket& packet = *it;
+      const Packet& packet = *it;
       int64_t delay_us = packet.send_time_us() -
           send_times_us_[packet.header().sequenceNumber];
       EXPECT_GE(max_delay_ms * 1000, delay_us);
@@ -853,6 +696,163 @@
   TestChoke(100, 100, 2);
   TestChoke(9900, 0, 98);
 }
+
+void TestVideoSender(VideoSender* sender, int64_t run_for_ms,
+                     uint32_t expected_packets,
+                     uint32_t expected_payload_size,
+                     uint32_t expected_total_payload_size) {
+  assert(sender);
+  Packets packets;
+  sender->RunFor(run_for_ms, &packets);
+  ASSERT_TRUE(IsTimeSorted(packets));
+  ASSERT_TRUE(IsSequenceNumberSorted(packets));
+  EXPECT_EQ(expected_packets, packets.size());
+  int64_t send_time_us = -1;
+  uint32_t total_payload_size = 0;
+  uint32_t absolute_send_time = 0;
+  uint32_t absolute_send_time_wraps = 0;
+  uint32_t rtp_timestamp = 0;
+  uint32_t rtp_timestamp_wraps = 0;
+  for (PacketsIt it = packets.begin(); it != packets.end(); ++it) {
+    EXPECT_LE(send_time_us, it->send_time_us());
+    send_time_us = it->send_time_us();
+    if (sender->max_payload_size_bytes() != it->payload_size()) {
+      EXPECT_EQ(expected_payload_size, it->payload_size());
+    }
+    total_payload_size += it->payload_size();
+    if (absolute_send_time > it->header().extension.absoluteSendTime) {
+      absolute_send_time_wraps++;
+    }
+    absolute_send_time = it->header().extension.absoluteSendTime;
+    if (rtp_timestamp > it->header().timestamp) {
+      rtp_timestamp_wraps++;
+    }
+    rtp_timestamp = it->header().timestamp;
+  }
+  EXPECT_EQ(expected_total_payload_size, total_payload_size);
+  EXPECT_GE(1u, absolute_send_time_wraps);
+  EXPECT_GE(1u, rtp_timestamp_wraps);
+}
+
+TEST(BweTestFramework_VideoSenderTest, Fps1Kpbs80_1s) {
+  // 1 fps, 80 kbps
+  VideoSender sender(NULL, 1.0f, 80, 0x1234, 0);
+  EXPECT_EQ(10000u, sender.bytes_per_second());
+  // We're at 1 fps, so all packets should be generated on first call, giving 10
+  // packets of each 1000 bytes, total 10000 bytes.
+  TestVideoSender(&sender, 1, 10, 1000, 10000);
+  // 999ms, should see no output here.
+  TestVideoSender(&sender, 998, 0, 0, 0);
+  // 1999ms, should get data for one more frame.
+  TestVideoSender(&sender, 1000, 10, 1000, 10000);
+  // 2000ms, one more frame.
+  TestVideoSender(&sender, 1, 10, 1000, 10000);
+  // 2999ms, should see nothing.
+  TestVideoSender(&sender, 999, 0, 0, 0);
+}
+
+TEST(BweTestFramework_VideoSenderTest, Fps1Kpbs80_1s_Offset) {
+  // 1 fps, 80 kbps, offset 0.5 of a frame period, ==0.5s in this case.
+  VideoSender sender(NULL, 1.0f, 80, 0x1234, 0.5f);
+  EXPECT_EQ(10000u, sender.bytes_per_second());
+  // 499ms, no output.
+  TestVideoSender(&sender, 499, 0, 0, 0);
+  // 500ms, first frame (this is the offset we set), 10 packets of 1000 bytes.
+  TestVideoSender(&sender, 1, 10, 1000, 10000);
+  // 1499ms, nothing.
+  TestVideoSender(&sender, 999, 0, 0, 0);
+  // 1999ms, second frame.
+  TestVideoSender(&sender, 500, 10, 1000, 10000);
+  // 2499ms, nothing.
+  TestVideoSender(&sender, 500, 0, 0, 0);
+  // 2500ms, third frame.
+  TestVideoSender(&sender, 1, 10, 1000, 10000);
+  // 3499ms, nothing.
+  TestVideoSender(&sender, 999, 0, 0, 0);
+}
+
+TEST(BweTestFramework_VideoSenderTest, Fps50Kpbs80_11s) {
+  // 50 fps, 80 kbps.
+  VideoSender sender(NULL, 50.0f, 80, 0x1234, 0);
+  EXPECT_EQ(10000u, sender.bytes_per_second());
+  // 9998ms, should see 500 frames, 200 byte payloads, total 100000 bytes.
+  TestVideoSender(&sender, 9998, 500, 200, 100000);
+  // 9999ms, nothing.
+  TestVideoSender(&sender, 1, 0, 0, 0);
+  // 10000ms, 501st frame as a single packet.
+  TestVideoSender(&sender, 1, 1, 200, 200);
+  // 10998ms, 49 more frames.
+  TestVideoSender(&sender, 998, 49, 200, 9800);
+  // 10999ms, nothing.
+  TestVideoSender(&sender, 1, 0, 0, 0);
+}
+
+TEST(BweTestFramework_VideoSenderTest, Fps10Kpbs120_1s) {
+  // 20 fps, 120 kbps.
+  VideoSender sender(NULL, 20.0f, 120, 0x1234, 0);
+  EXPECT_EQ(15000u, sender.bytes_per_second());
+  // 498ms, 10 frames with 750 byte payloads, total 7500 bytes.
+  TestVideoSender(&sender, 498, 10, 750, 7500);
+  // 499ms, nothing.
+  TestVideoSender(&sender, 1, 0, 0, 0);
+  // 500ms, one more frame.
+  TestVideoSender(&sender, 1, 1, 750, 750);
+  // 998ms, 9 more frames.
+  TestVideoSender(&sender, 498, 9, 750, 6750);
+  // 999ms, nothing.
+  TestVideoSender(&sender, 1, 0, 0, 0);
+}
+
+TEST(BweTestFramework_VideoSenderTest, Fps30Kpbs800_20s) {
+  // 20 fps, 820 kbps.
+  VideoSender sender(NULL, 25.0f, 820, 0x1234, 0);
+  EXPECT_EQ(102500u, sender.bytes_per_second());
+  // 9998ms, 250 frames. 820 kbps = 102500 bytes/s, so total should be 1025000.
+  // Each frame is 102500/25=4100 bytes, or 5 packets (4 @1000 bytes, 1 @100),
+  // so packet count should be 5*250=1250 and last packet of each frame has
+  // 100 bytes of payload.
+  TestVideoSender(&sender, 9998, 1250, 100, 1025000);
+  // 9999ms, nothing.
+  TestVideoSender(&sender, 1, 0, 0, 0);
+  // 19998ms, 250 more frames.
+  TestVideoSender(&sender, 9999, 1250, 100, 1025000);
+  // 19999ms, nothing.
+  TestVideoSender(&sender, 1, 0, 0, 0);
+  // 20038ms, one more frame, as described above (25fps == 40ms/frame).
+  TestVideoSender(&sender, 39, 5, 100, 4100);
+  // 20039ms, nothing.
+  TestVideoSender(&sender, 1, 0, 0, 0);
+}
+
+TEST(BweTestFramework_VideoSenderTest, TestAppendInOrder) {
+  // 1 fps, 80 kbps, 250ms offset.
+  VideoSender sender1(NULL, 1.0f, 80, 0x1234, 0.25f);
+  EXPECT_EQ(10000u, sender1.bytes_per_second());
+  Packets packets;
+  // Generate some packets, verify they are sorted.
+  sender1.RunFor(999, &packets);
+  ASSERT_TRUE(IsTimeSorted(packets));
+  ASSERT_TRUE(IsSequenceNumberSorted(packets));
+  EXPECT_EQ(10u, packets.size());
+  // Generate some more packets and verify they are appended to end of list.
+  sender1.RunFor(1000, &packets);
+  ASSERT_TRUE(IsTimeSorted(packets));
+  ASSERT_TRUE(IsSequenceNumberSorted(packets));
+  EXPECT_EQ(20u, packets.size());
+
+  // Another sender, 2 fps, 160 kpbs, 150ms offset
+  VideoSender sender2(NULL, 2.0f, 160, 0x2234, 0.30f);
+  EXPECT_EQ(20000u, sender2.bytes_per_second());
+  // Generate some packets, verify that they are merged with the packets already
+  // on the list.
+  sender2.RunFor(999, &packets);
+  ASSERT_TRUE(IsTimeSorted(packets));
+  EXPECT_EQ(40u, packets.size());
+  // Generate some more.
+  sender2.RunFor(1000, &packets);
+  ASSERT_TRUE(IsTimeSorted(packets));
+  EXPECT_EQ(60u, packets.size());
+}
 }  // namespace bwe
 }  // namespace testing
 }  // namespace webrtc
diff --git a/modules/remote_bitrate_estimator/test/bwe_test_logging.cc b/modules/remote_bitrate_estimator/test/bwe_test_logging.cc
index ae1d42b..1a43f09 100644
--- a/modules/remote_bitrate_estimator/test/bwe_test_logging.cc
+++ b/modules/remote_bitrate_estimator/test/bwe_test_logging.cc
@@ -25,20 +25,24 @@
 
 Logging Logging::g_Logging;
 
-Logging::Context::Context(uint32_t name, int64_t timestamp_ms, bool enabled) {
+static std::string ToString(uint32_t v) {
   const size_t kBufferSize = 16;
   char string_buffer[kBufferSize] = {0};
 #if defined(_MSC_VER) && defined(_WIN32)
-  _snprintf(string_buffer, kBufferSize - 1, "%08x", name);
+  _snprintf(string_buffer, kBufferSize - 1, "%08x", v);
 #else
-  snprintf(string_buffer, kBufferSize, "%08x", name);
+  snprintf(string_buffer, kBufferSize, "%08x", v);
 #endif
-  Logging::GetInstance()->PushState(string_buffer, timestamp_ms, enabled);
+  return string_buffer;
+}
+
+Logging::Context::Context(uint32_t name, int64_t timestamp_ms, bool enabled) {
+  Logging::GetInstance()->PushState(ToString(name), timestamp_ms, enabled);
 }
 
 Logging::Context::Context(const std::string& name, int64_t timestamp_ms,
                           bool enabled) {
-  Logging::GetInstance()->PushState(name.c_str(), timestamp_ms, enabled);
+  Logging::GetInstance()->PushState(name, timestamp_ms, enabled);
 }
 
 Logging::Context::Context(const char* name, int64_t timestamp_ms,
@@ -54,11 +58,31 @@
   return &g_Logging;
 }
 
+void Logging::SetGlobalContext(uint32_t name) {
+  CriticalSectionScoped cs(crit_sect_.get());
+  thread_map_[ThreadWrapper::GetThreadId()].global_state.tag = ToString(name);
+}
+
+void Logging::SetGlobalContext(const std::string& name) {
+  CriticalSectionScoped cs(crit_sect_.get());
+  thread_map_[ThreadWrapper::GetThreadId()].global_state.tag = name;
+}
+
+void Logging::SetGlobalContext(const char* name) {
+  CriticalSectionScoped cs(crit_sect_.get());
+  thread_map_[ThreadWrapper::GetThreadId()].global_state.tag = name;
+}
+
+void Logging::SetGlobalEnable(bool enabled) {
+  CriticalSectionScoped cs(crit_sect_.get());
+  thread_map_[ThreadWrapper::GetThreadId()].global_state.enabled = enabled;
+}
+
 void Logging::Log(const char format[], ...) {
   CriticalSectionScoped cs(crit_sect_.get());
   ThreadMap::iterator it = thread_map_.find(ThreadWrapper::GetThreadId());
   assert(it != thread_map_.end());
-  const State& state = it->second.top();
+  const State& state = it->second.stack.top();
   if (state.enabled) {
     printf("%s\t", state.tag.c_str());
     va_list args;
@@ -73,7 +97,7 @@
   CriticalSectionScoped cs(crit_sect_.get());
   ThreadMap::iterator it = thread_map_.find(ThreadWrapper::GetThreadId());
   assert(it != thread_map_.end());
-  const State& state = it->second.top();
+  const State& state = it->second.stack.top();
   if (state.enabled) {
     printf("PLOT\t%s\t%f\t%f\n", state.tag.c_str(), state.timestamp_ms * 0.001,
            value);
@@ -85,37 +109,48 @@
       thread_map_() {
 }
 
-void Logging::PushState(const char append_to_tag[], int64_t timestamp_ms,
-                        bool enabled) {
-  assert(append_to_tag);
-  CriticalSectionScoped cs(crit_sect_.get());
-  std::stack<State>* stack = &thread_map_[ThreadWrapper::GetThreadId()];
-  if (stack->empty()) {
-    State new_state(append_to_tag, std::max(static_cast<int64_t>(0),
-                                            timestamp_ms), enabled);
-    stack->push(new_state);
-  } else {
-    stack->push(stack->top());
-    State* state = &stack->top();
-    if (state->tag != "" && std::string(append_to_tag) != "") {
-      state->tag.append("_");
-    }
-    state->tag.append(append_to_tag);
-    state->timestamp_ms = std::max(timestamp_ms, state->timestamp_ms);
-    state->enabled = enabled && state->enabled;
+Logging::State::State() : tag(""), timestamp_ms(0), enabled(true) {}
+
+Logging::State::State(const std::string& tag, int64_t timestamp_ms,
+                      bool enabled)
+    : tag(tag),
+      timestamp_ms(timestamp_ms),
+      enabled(enabled) {
+}
+
+void Logging::State::MergePrevious(const State& previous) {
+  if (tag == "") {
+    tag = previous.tag;
+  } else if (previous.tag != "") {
+    tag = previous.tag + "_" + tag;
   }
+  timestamp_ms = std::max(previous.timestamp_ms, timestamp_ms);
+  enabled = previous.enabled && enabled;
+}
+
+void Logging::PushState(const std::string& append_to_tag, int64_t timestamp_ms,
+                        bool enabled) {
+  CriticalSectionScoped cs(crit_sect_.get());
+  State new_state(append_to_tag, timestamp_ms, enabled);
+  ThreadState* thread_state = &thread_map_[ThreadWrapper::GetThreadId()];
+  std::stack<State>* stack = &thread_state->stack;
+  if (stack->empty()) {
+    new_state.MergePrevious(thread_state->global_state);
+  } else {
+    new_state.MergePrevious(stack->top());
+  }
+  stack->push(new_state);
 }
 
 void Logging::PopState() {
   CriticalSectionScoped cs(crit_sect_.get());
   ThreadMap::iterator it = thread_map_.find(ThreadWrapper::GetThreadId());
   assert(it != thread_map_.end());
-  int64_t newest_timestamp_ms = it->second.top().timestamp_ms;
-  it->second.pop();
-  if (it->second.empty()) {
-    thread_map_.erase(it);
-  } else {
-    State* state = &it->second.top();
+  std::stack<State>* stack = &it->second.stack;
+  int64_t newest_timestamp_ms = stack->top().timestamp_ms;
+  stack->pop();
+  if (!stack->empty()) {
+    State* state = &stack->top();
     // Update time so that next log/plot will use the latest time seen so far
     // in this call tree.
     state->timestamp_ms = std::max(state->timestamp_ms, newest_timestamp_ms);
diff --git a/modules/remote_bitrate_estimator/test/bwe_test_logging.h b/modules/remote_bitrate_estimator/test/bwe_test_logging.h
index 0ff5825..c949763 100644
--- a/modules/remote_bitrate_estimator/test/bwe_test_logging.h
+++ b/modules/remote_bitrate_estimator/test/bwe_test_logging.h
@@ -51,6 +51,15 @@
 
 #if !(BWE_TEST_LOGGING_COMPILE_TIME_ENABLE)
 
+// Set a thread-global base logging context. This name will be prepended to all
+// hierarchical contexts.
+// |name| is a char*, std::string or uint32_t to name the context.
+#define BWE_TEST_LOGGING_GLOBAL_CONTEXT(name)
+
+// Thread-globally allow/disallow logging.
+// |enable| is expected to be a bool.
+#define BWE_TEST_LOGGING_GLOBAL_ENABLE(enabled)
+
 // Insert a (hierarchical) logging context.
 // |name| is a char*, std::string or uint32_t to name the context.
 #define BWE_TEST_LOGGING_CONTEXT(name)
@@ -93,6 +102,16 @@
 #include "webrtc/system_wrappers/interface/constructor_magic.h"
 #include "webrtc/system_wrappers/interface/scoped_ptr.h"
 
+#define BWE_TEST_LOGGING_GLOBAL_CONTEXT(name) \
+    do { \
+      webrtc::testing::bwe::Logging::GetInstance()->SetGlobalContext(name); \
+    } while (0);
+
+#define BWE_TEST_LOGGING_GLOBAL_ENABLE(enabled) \
+    do { \
+      webrtc::testing::bwe::Logging::GetInstance()->SetGlobalEnable(enabled); \
+    } while (0);
+
 #define __BWE_TEST_LOGGING_CONTEXT_NAME(ctx, line) ctx ## line
 #define __BWE_TEST_LOGGING_CONTEXT_DECLARE(ctx, line, name, time, enabled) \
     webrtc::testing::bwe::Logging::Context \
@@ -162,24 +181,33 @@
   };
 
   static Logging* GetInstance();
+
+  void SetGlobalContext(uint32_t name);
+  void SetGlobalContext(const std::string& name);
+  void SetGlobalContext(const char* name);
+  void SetGlobalEnable(bool enabled);
+
   void Log(const char format[], ...);
   void Plot(double value);
 
  private:
   struct State {
-    State(const char new_tag[], int64_t timestamp_ms, bool enabled)
-        : tag(new_tag),
-          timestamp_ms(timestamp_ms),
-          enabled(enabled) {
-    }
+    State();
+    State(const std::string& new_tag, int64_t timestamp_ms, bool enabled);
+    void MergePrevious(const State& previous);
+
     std::string tag;
     int64_t timestamp_ms;
     bool enabled;
   };
-  typedef std::map<uint32_t, std::stack<State> > ThreadMap;
+  struct ThreadState {
+    State global_state;
+    std::stack<State> stack;
+  };
+  typedef std::map<uint32_t, ThreadState> ThreadMap;
 
   Logging();
-  void PushState(const char append_to_tag[], int64_t timestamp_ms,
+  void PushState(const std::string& append_to_tag, int64_t timestamp_ms,
                  bool enabled);
   void PopState();
 
diff --git a/modules/rtp_rtcp/interface/rtp_rtcp.h b/modules/rtp_rtcp/interface/rtp_rtcp.h
index c0e6183..54aadf5 100644
--- a/modules/rtp_rtcp/interface/rtp_rtcp.h
+++ b/modules/rtp_rtcp/interface/rtp_rtcp.h
@@ -63,7 +63,7 @@
     RtcpFeedback* rtcp_feedback;
     RtcpIntraFrameObserver* intra_frame_callback;
     RtcpBandwidthObserver* bandwidth_callback;
-    RtcpRttObserver* rtt_observer;
+    RtcpRttStats* rtt_stats;
     RtpAudioFeedback* audio_messages;
     RemoteBitrateEstimator* remote_bitrate_estimator;
     PacedSender* paced_sender;
@@ -510,6 +510,8 @@
     */
     virtual void SetRtcpXrRrtrStatus(bool enable) = 0;
 
+    virtual bool RtcpXrRrtrStatus() const = 0;
+
     /*
     *  (REMB) Receiver Estimated Max Bitrate
     */
diff --git a/modules/rtp_rtcp/interface/rtp_rtcp_defines.h b/modules/rtp_rtcp/interface/rtp_rtcp_defines.h
index 21dcc9f..29a1bbc 100644
--- a/modules/rtp_rtcp/interface/rtp_rtcp_defines.h
+++ b/modules/rtp_rtcp/interface/rtp_rtcp_defines.h
@@ -288,11 +288,13 @@
   virtual ~RtcpBandwidthObserver() {}
 };
 
-class RtcpRttObserver {
+class RtcpRttStats {
  public:
   virtual void OnRttUpdate(uint32_t rtt) = 0;
 
-  virtual ~RtcpRttObserver() {};
+  virtual uint32_t LastProcessedRtt() const = 0;
+
+  virtual ~RtcpRttStats() {};
 };
 
 // Null object version of RtpFeedback.
diff --git a/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h b/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h
index 09c3ca6..90ea4f8 100644
--- a/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h
+++ b/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h
@@ -170,6 +170,8 @@
       int32_t(const RTCPVoIPMetric* VoIPMetric));
   MOCK_METHOD1(SetRtcpXrRrtrStatus,
       void(bool enable));
+  MOCK_CONST_METHOD0(RtcpXrRrtrStatus,
+      bool());
   MOCK_CONST_METHOD0(REMB,
       bool());
   MOCK_METHOD1(SetREMBStatus,
diff --git a/modules/rtp_rtcp/source/forward_error_correction.cc b/modules/rtp_rtcp/source/forward_error_correction.cc
index a892386..189e1b0 100644
--- a/modules/rtp_rtcp/source/forward_error_correction.cc
+++ b/modules/rtp_rtcp/source/forward_error_correction.cc
@@ -11,6 +11,7 @@
 #include "webrtc/modules/rtp_rtcp/source/forward_error_correction.h"
 
 #include <assert.h>
+#include <stdlib.h>
 #include <string.h>
 
 #include <algorithm>
diff --git a/modules/rtp_rtcp/source/rtcp_sender.cc b/modules/rtp_rtcp/source/rtcp_sender.cc
index 7962e97..a407a42 100644
--- a/modules/rtp_rtcp/source/rtcp_sender.cc
+++ b/modules/rtp_rtcp/source/rtcp_sender.cc
@@ -2182,6 +2182,11 @@
   xrSendReceiverReferenceTimeEnabled_ = enable;
 }
 
+bool RTCPSender::RtcpXrReceiverReferenceTime() const {
+  CriticalSectionScoped lock(_criticalSectionRTCPSender);
+  return xrSendReceiverReferenceTimeEnabled_;
+}
+
 // called under critsect _criticalSectionRTCPSender
 int32_t RTCPSender::WriteAllReportBlocksToBuffer(
     uint8_t* rtcpbuffer,
diff --git a/modules/rtp_rtcp/source/rtcp_sender.h b/modules/rtp_rtcp/source/rtcp_sender.h
index 106a78d..9ed5824 100644
--- a/modules/rtp_rtcp/source/rtcp_sender.h
+++ b/modules/rtp_rtcp/source/rtcp_sender.h
@@ -171,6 +171,8 @@
 
     void SendRtcpXrReceiverReferenceTime(bool enable);
 
+    bool RtcpXrReceiverReferenceTime() const;
+
     int32_t SetCSRCs(const uint32_t arrOfCSRC[kRtpCsrcSize],
                      const uint8_t arrLength);
 
diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl.cc b/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
index f910a18..44b33a8 100644
--- a/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
+++ b/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
@@ -39,7 +39,7 @@
       rtcp_feedback(NULL),
       intra_frame_callback(NULL),
       bandwidth_callback(NULL),
-      rtt_observer(NULL),
+      rtt_stats(NULL),
       audio_messages(NullObjectRtpAudioFeedback()),
       remote_bitrate_estimator(NULL),
       paced_sender(NULL) {
@@ -92,7 +92,9 @@
 #ifdef MATLAB
       , plot1_(NULL),
 #endif
-      rtt_observer_(configuration.rtt_observer) {
+      rtt_stats_(configuration.rtt_stats),
+      critical_section_rtt_(CriticalSectionWrapper::CreateCriticalSection()),
+      rtt_ms_(0) {
   send_video_codec_.codecType = kVideoCodecUnknown;
 
   if (default_module_) {
@@ -212,8 +214,8 @@
           max_rtt = (rtt > max_rtt) ? rtt : max_rtt;
         }
         // Report the rtt.
-        if (rtt_observer_ && max_rtt != 0)
-          rtt_observer_->OnRttUpdate(max_rtt);
+        if (rtt_stats_ && max_rtt != 0)
+          rtt_stats_->OnRttUpdate(max_rtt);
       }
 
       // Verify receiver reports are delivered and the reported sequence number
@@ -240,14 +242,18 @@
       // Report rtt from receiver.
       if (process_rtt) {
          uint16_t rtt_ms;
-         if (rtt_observer_ && rtcp_receiver_.GetAndResetXrRrRtt(&rtt_ms)) {
-           rtt_observer_->OnRttUpdate(rtt_ms);
+         if (rtt_stats_ && rtcp_receiver_.GetAndResetXrRrRtt(&rtt_ms)) {
+           rtt_stats_->OnRttUpdate(rtt_ms);
          }
       }
     }
 
+    // Get processed rtt.
     if (process_rtt) {
       last_rtt_process_time_ = now;
+      if (rtt_stats_) {
+        set_rtt_ms(rtt_stats_->LastProcessedRtt());
+      }
     }
 
     if (rtcp_sender_.TimeToSendRTCPReport()) {
@@ -346,7 +352,12 @@
                video_codec.plType);
 
   send_video_codec_ = video_codec;
-  simulcast_ = (video_codec.numberOfSimulcastStreams > 1) ? true : false;
+  {
+    // simulcast_ is accessed when accessing child_modules_, so this write needs
+    // to be protected by the same lock.
+    CriticalSectionScoped lock(critical_section_module_ptrs_.get());
+    simulcast_ = video_codec.numberOfSimulcastStreams > 1;
+  }
   return rtp_sender_.RegisterPayload(video_codec.plName,
                                      video_codec.plType,
                                      90000,
@@ -600,12 +611,12 @@
                                         &(rtp_video_hdr->codecHeader));
   }
   int32_t ret_val = -1;
+  CriticalSectionScoped lock(critical_section_module_ptrs_.get());
   if (simulcast_) {
     if (rtp_video_hdr == NULL) {
       return -1;
     }
     int idx = 0;
-    CriticalSectionScoped lock(critical_section_module_ptrs_.get());
     std::list<ModuleRtpRtcpImpl*>::iterator it = child_modules_.begin();
     for (; idx < rtp_video_hdr->simulcastIdx; ++it) {
       if (it == child_modules_.end()) {
@@ -638,7 +649,6 @@
                                    fragmentation,
                                    rtp_video_hdr);
   } else {
-    CriticalSectionScoped lock(critical_section_module_ptrs_.get());
     std::list<ModuleRtpRtcpImpl*>::iterator it = child_modules_.begin();
     // Send to all "child" modules
     while (it != child_modules_.end()) {
@@ -957,6 +967,10 @@
   return rtcp_sender_.SendRtcpXrReceiverReferenceTime(enable);
 }
 
+bool ModuleRtpRtcpImpl::RtcpXrRrtrStatus() const {
+  return rtcp_sender_.RtcpXrReceiverReferenceTime();
+}
+
 int32_t ModuleRtpRtcpImpl::DataCountersRTP(
     uint32_t* bytes_sent,
     uint32_t* packets_sent) const {
@@ -1623,4 +1637,14 @@
   rtcp_receiver_.SetSsrcs(main_ssrc, ssrcs);
 }
 
+void ModuleRtpRtcpImpl::set_rtt_ms(uint32_t rtt_ms) {
+  CriticalSectionScoped cs(critical_section_rtt_.get());
+  rtt_ms_ = rtt_ms;
+}
+
+uint32_t ModuleRtpRtcpImpl::rtt_ms() const {
+  CriticalSectionScoped cs(critical_section_rtt_.get());
+  return rtt_ms_;
+}
+
 }  // Namespace webrtc
diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl.h b/modules/rtp_rtcp/source/rtp_rtcp_impl.h
index b29ec57..940a656 100644
--- a/modules/rtp_rtcp/source/rtp_rtcp_impl.h
+++ b/modules/rtp_rtcp/source/rtp_rtcp_impl.h
@@ -19,6 +19,7 @@
 #include "webrtc/modules/rtp_rtcp/source/rtcp_sender.h"
 #include "webrtc/modules/rtp_rtcp/source/rtp_sender.h"
 #include "webrtc/system_wrappers/interface/scoped_ptr.h"
+#include "webrtc/test/testsupport/gtest_prod_util.h"
 
 #ifdef MATLAB
 class MatlabPlot;
@@ -258,6 +259,8 @@
   // (XR) Receiver reference time report.
   virtual void SetRtcpXrRrtrStatus(bool enable) OVERRIDE;
 
+  virtual bool RtcpXrRrtrStatus() const OVERRIDE;
+
   // Audio part.
 
   // Set audio packet size, used to determine when it's time to send a DTMF
@@ -383,9 +386,13 @@
   Clock*                    clock_;
 
  private:
+  FRIEND_TEST_ALL_PREFIXES(RtpRtcpImplTest, RttForReceiverOnly);
   int64_t RtcpReportInterval();
   void SetRtcpReceiverSsrcs(uint32_t main_ssrc);
 
+  void set_rtt_ms(uint32_t rtt_ms);
+  uint32_t rtt_ms() const;
+
   int32_t             id_;
   const bool                audio_;
   bool                      collision_detected_;
@@ -414,7 +421,11 @@
   MatlabPlot*           plot1_;
 #endif
 
-  RtcpRttObserver* rtt_observer_;
+  RtcpRttStats* rtt_stats_;
+
+  // The processed RTT from RtcpRttStats.
+  scoped_ptr<CriticalSectionWrapper> critical_section_rtt_;
+  uint32_t rtt_ms_;
 };
 
 }  // namespace webrtc
diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl_unittest.cc b/modules/rtp_rtcp/source/rtp_rtcp_impl_unittest.cc
index ff3c32e..6248f49 100644
--- a/modules/rtp_rtcp/source/rtp_rtcp_impl_unittest.cc
+++ b/modules/rtp_rtcp/source/rtp_rtcp_impl_unittest.cc
@@ -18,7 +18,7 @@
 namespace webrtc {
 namespace {
 
-class RtcpRttStatsTestImpl : public RtcpRttObserver {
+class RtcpRttStatsTestImpl : public RtcpRttStats {
  public:
   RtcpRttStatsTestImpl() : rtt_ms_(0) {}
   virtual ~RtcpRttStatsTestImpl() {}
@@ -26,6 +26,9 @@
   virtual void OnRttUpdate(uint32_t rtt_ms) {
     rtt_ms_ = rtt_ms;
   }
+  virtual uint32_t LastProcessedRtt() const {
+    return rtt_ms_;
+  }
   uint32_t rtt_ms_;
 };
 
@@ -70,7 +73,7 @@
     configuration.clock = &clock_;
     configuration.outgoing_transport = &transport_;
     configuration.receive_statistics = receive_statistics_.get();
-    configuration.rtt_observer = &rtt_stats_;
+    configuration.rtt_stats = &rtt_stats_;
 
     rtp_rtcp_impl_.reset(new ModuleRtpRtcpImpl(configuration));
     transport_.SetRtpRtcpModule(rtp_rtcp_impl_.get());
@@ -121,6 +124,12 @@
       rtp_rtcp_impl_->RTT(kSsrc + 1, &rtt, &avg_rtt, &min_rtt, &max_rtt));
 }
 
+TEST_F(RtpRtcpImplTest, SetRtcpXrRrtrStatus) {
+  EXPECT_FALSE(rtp_rtcp_impl_->RtcpXrRrtrStatus());
+  rtp_rtcp_impl_->SetRtcpXrRrtrStatus(true);
+  EXPECT_TRUE(rtp_rtcp_impl_->RtcpXrRrtrStatus());
+}
+
 TEST_F(RtpRtcpImplTest, RttForReceiverOnly) {
   rtp_rtcp_impl_->SetRtcpXrRrtrStatus(true);
   EXPECT_EQ(0, rtp_rtcp_impl_->SetSendingStatus(false));
@@ -136,10 +145,12 @@
   EXPECT_EQ(0, rtp_rtcp_impl_->SendRTCP(kRtcpReport));
 
   // Verify RTT.
-  EXPECT_EQ(0U, rtt_stats_.rtt_ms_);
+  EXPECT_EQ(0U, rtt_stats_.LastProcessedRtt());
+  EXPECT_EQ(0U, rtp_rtcp_impl_->rtt_ms());
 
   rtp_rtcp_impl_->Process();
-  EXPECT_EQ(100U, rtt_stats_.rtt_ms_);
+  EXPECT_EQ(100U, rtt_stats_.LastProcessedRtt());
+  EXPECT_EQ(100U, rtp_rtcp_impl_->rtt_ms());
 }
 
 }  // namespace webrtc
diff --git a/modules/rtp_rtcp/source/rtp_sender.cc b/modules/rtp_rtcp/source/rtp_sender.cc
index 5c63b58..0031a0e 100644
--- a/modules/rtp_rtcp/source/rtp_sender.cc
+++ b/modules/rtp_rtcp/source/rtp_sender.cc
@@ -60,9 +60,9 @@
       packets_sent_(0), payload_bytes_sent_(0), 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_packet_marker_bit_(false),
-      num_csrcs_(0), csrcs_(), include_csrcs_(true),
-      rtx_(kRtxOff), payload_type_rtx_(-1) {
+      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_));
@@ -419,6 +419,7 @@
     timestamp = start_time_stamp_ + capture_timestamp;
     timestamp_ = timestamp;
     capture_time_ms_ = capture_time_ms;
+    last_timestamp_time_ms_ = clock_->TimeInMilliseconds();
   }
   int bytes_sent = SendPadData(payload_type, timestamp, capture_time_ms,
                                bytes, kDontRetransmit, false, false);
@@ -771,6 +772,12 @@
     payload_type = (rtx_ == kRtxOff) ? payload_type_ : payload_type_rtx_;
     timestamp = timestamp_;
     capture_time_ms = capture_time_ms_;
+    if (last_timestamp_time_ms_ > 0) {
+      timestamp +=
+          (clock_->TimeInMilliseconds() - last_timestamp_time_ms_) * 90;
+      capture_time_ms +=
+          (clock_->TimeInMilliseconds() - last_timestamp_time_ms_);
+    }
   }
   return SendPadData(payload_type, timestamp, capture_time_ms, bytes,
                      kDontStore, true, true);
@@ -941,6 +948,7 @@
     // timing.
     timestamp_++;
   }
+  last_timestamp_time_ms_ = clock_->TimeInMilliseconds();
   uint32_t sequence_number = sequence_number_++;
   capture_time_ms_ = capture_time_ms;
   last_packet_marker_bit_ = marker_bit;
diff --git a/modules/rtp_rtcp/source/rtp_sender.h b/modules/rtp_rtcp/source/rtp_sender.h
index afa57f1..6a3bacc 100644
--- a/modules/rtp_rtcp/source/rtp_sender.h
+++ b/modules/rtp_rtcp/source/rtp_sender.h
@@ -331,6 +331,7 @@
   uint32_t ssrc_;
   uint32_t timestamp_;
   int64_t capture_time_ms_;
+  int64_t last_timestamp_time_ms_;
   bool last_packet_marker_bit_;
   uint8_t num_csrcs_;
   uint32_t csrcs_[kRtpCsrcSize];
diff --git a/modules/rtp_rtcp/source/rtp_sender_unittest.cc b/modules/rtp_rtcp/source/rtp_sender_unittest.cc
index b1f1b50..4320a0d 100644
--- a/modules/rtp_rtcp/source/rtp_sender_unittest.cc
+++ b/modules/rtp_rtcp/source/rtp_sender_unittest.cc
@@ -15,6 +15,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 #include "webrtc/modules/pacing/include/mock/mock_paced_sender.h"
+#include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h"
 #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h"
 #include "webrtc/modules/rtp_rtcp/source/rtp_format_video_generic.h"
 #include "webrtc/modules/rtp_rtcp/source/rtp_header_extension.h"
@@ -52,6 +53,10 @@
   return static_cast<uint16_t>(length);
 }
 
+uint64_t ConvertMsToAbsSendTime(int64_t time_ms) {
+  return 0x00fffffful & ((time_ms << 18) / 1000);
+}
+
 class LoopbackTransportTest : public webrtc::Transport {
  public:
   LoopbackTransportTest()
@@ -392,7 +397,7 @@
   // Verify transmission time offset.
   EXPECT_EQ(kStoredTimeInMs * 90, rtp_header.extension.transmissionTimeOffset);
   uint64_t expected_send_time =
-      0x00fffffful & ((fake_clock_.TimeInMilliseconds() << 18) / 1000);
+      ConvertMsToAbsSendTime(fake_clock_.TimeInMilliseconds());
   EXPECT_EQ(expected_send_time, rtp_header.extension.absoluteSendTime);
 }
 
@@ -454,7 +459,126 @@
   // Verify transmission time offset.
   EXPECT_EQ(kStoredTimeInMs * 90, rtp_header.extension.transmissionTimeOffset);
   uint64_t expected_send_time =
-      0x00fffffful & ((fake_clock_.TimeInMilliseconds() << 18) / 1000);
+      ConvertMsToAbsSendTime(fake_clock_.TimeInMilliseconds());
+  EXPECT_EQ(expected_send_time, rtp_header.extension.absoluteSendTime);
+}
+
+// This test sends 1 regular video packet, then 4 padding packets, and then
+// 1 more regular packet.
+TEST_F(RtpSenderTest, SendPadding) {
+  // Make all (non-padding) packets go to send queue.
+  EXPECT_CALL(mock_paced_sender_,
+              SendPacket(PacedSender::kNormalPriority, _, _, _, _, _)).
+                  WillRepeatedly(testing::Return(false));
+
+  uint16_t seq_num = kSeqNum;
+  uint32_t timestamp = kTimestamp;
+  rtp_sender_->SetStorePacketsStatus(true, 10);
+  int rtp_header_len = 12;
+  EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension(
+      kRtpExtensionTransmissionTimeOffset, kTransmissionTimeOffsetExtensionId));
+  rtp_header_len += 4;  // 4 bytes extension.
+  EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension(
+      kRtpExtensionAbsoluteSendTime, kAbsoluteSendTimeExtensionId));
+  rtp_header_len += 4;  // 4 bytes extension.
+  rtp_header_len += 4;  // 4 extra bytes common to all extension headers.
+
+  // Create and set up parser.
+  scoped_ptr<webrtc::RtpHeaderParser> rtp_parser(
+      webrtc::RtpHeaderParser::Create());
+  ASSERT_TRUE(rtp_parser.get() != NULL);
+  rtp_parser->RegisterRtpHeaderExtension(kRtpExtensionTransmissionTimeOffset,
+                                         kTransmissionTimeOffsetExtensionId);
+  rtp_parser->RegisterRtpHeaderExtension(kRtpExtensionAbsoluteSendTime,
+                                         kAbsoluteSendTimeExtensionId);
+  webrtc::RTPHeader rtp_header;
+
+  rtp_sender_->SetTargetSendBitrate(300000);
+  int64_t capture_time_ms = fake_clock_.TimeInMilliseconds();
+  int32_t rtp_length = rtp_sender_->BuildRTPheader(packet_,
+                                                   kPayload,
+                                                   kMarkerBit,
+                                                   timestamp,
+                                                   capture_time_ms);
+
+  // Packet should be stored in a send bucket.
+  EXPECT_EQ(0, rtp_sender_->SendToNetwork(packet_,
+                                          0,
+                                          rtp_length,
+                                          capture_time_ms,
+                                          kAllowRetransmission,
+                                          PacedSender::kNormalPriority));
+
+  int total_packets_sent = 0;
+  EXPECT_EQ(total_packets_sent, transport_.packets_sent_);
+
+  const int kStoredTimeInMs = 100;
+  fake_clock_.AdvanceTimeMilliseconds(kStoredTimeInMs);
+  rtp_sender_->TimeToSendPacket(seq_num++, capture_time_ms, false);
+  // Packet should now be sent. This test doesn't verify the regular video
+  // packet, since it is tested in another test.
+  EXPECT_EQ(++total_packets_sent, transport_.packets_sent_);
+  timestamp += 90 * kStoredTimeInMs;
+
+  // Send padding 4 times, waiting 50 ms between each.
+  for (int i = 0; i < 4; ++i) {
+    const int kPaddingPeriodMs = 50;
+    const int kPaddingBytes = 100;
+    const int kMaxPaddingLength = 224;  // Value taken from rtp_sender.cc.
+    // Padding will be forced to full packets.
+    EXPECT_EQ(kMaxPaddingLength, rtp_sender_->TimeToSendPadding(kPaddingBytes));
+
+    // Process send bucket. Padding should now be sent.
+    EXPECT_EQ(++total_packets_sent, transport_.packets_sent_);
+    EXPECT_EQ(kMaxPaddingLength + rtp_header_len,
+              transport_.last_sent_packet_len_);
+    // Parse sent packet.
+    ASSERT_TRUE(rtp_parser->Parse(transport_.last_sent_packet_, kPaddingBytes,
+                                  &rtp_header));
+
+    // Verify sequence number and timestamp.
+    EXPECT_EQ(seq_num++, rtp_header.sequenceNumber);
+    EXPECT_EQ(timestamp, rtp_header.timestamp);
+    // Verify transmission time offset.
+    EXPECT_EQ(0, rtp_header.extension.transmissionTimeOffset);
+    uint64_t expected_send_time =
+        ConvertMsToAbsSendTime(fake_clock_.TimeInMilliseconds());
+    EXPECT_EQ(expected_send_time, rtp_header.extension.absoluteSendTime);
+    fake_clock_.AdvanceTimeMilliseconds(kPaddingPeriodMs);
+    timestamp += 90 * kPaddingPeriodMs;
+  }
+
+  // Send a regular video packet again.
+  capture_time_ms = fake_clock_.TimeInMilliseconds();
+  rtp_length = rtp_sender_->BuildRTPheader(packet_,
+                                           kPayload,
+                                           kMarkerBit,
+                                           timestamp,
+                                           capture_time_ms);
+
+  // Packet should be stored in a send bucket.
+  EXPECT_EQ(0, rtp_sender_->SendToNetwork(packet_,
+                                          0,
+                                          rtp_length,
+                                          capture_time_ms,
+                                          kAllowRetransmission,
+                                          PacedSender::kNormalPriority));
+
+  rtp_sender_->TimeToSendPacket(seq_num, capture_time_ms, false);
+  // Process send bucket.
+  EXPECT_EQ(++total_packets_sent, transport_.packets_sent_);
+  EXPECT_EQ(rtp_length, transport_.last_sent_packet_len_);
+  // Parse sent packet.
+  ASSERT_TRUE(rtp_parser->Parse(transport_.last_sent_packet_, rtp_length,
+                                &rtp_header));
+
+  // Verify sequence number and timestamp.
+  EXPECT_EQ(seq_num, rtp_header.sequenceNumber);
+  EXPECT_EQ(timestamp, rtp_header.timestamp);
+  // Verify transmission time offset. This packet is sent without delay.
+  EXPECT_EQ(0, rtp_header.extension.transmissionTimeOffset);
+  uint64_t expected_send_time =
+      ConvertMsToAbsSendTime(fake_clock_.TimeInMilliseconds());
   EXPECT_EQ(expected_send_time, rtp_header.extension.absoluteSendTime);
 }
 
diff --git a/modules/video_capture/include/video_capture_defines.h b/modules/video_capture/include/video_capture_defines.h
index c592633..330bfc7 100644
--- a/modules/video_capture/include/video_capture_defines.h
+++ b/modules/video_capture/include/video_capture_defines.h
@@ -84,33 +84,6 @@
     Cleared = 1
 };
 
-// VideoFrameI420 doesn't take the ownership of the buffer.
-// It's mostly used to group the parameters for external capture.
-struct VideoFrameI420
-{
-  VideoFrameI420() {
-    y_plane = NULL;
-    u_plane = NULL;
-    v_plane = NULL;
-    y_pitch = 0;
-    u_pitch = 0;
-    v_pitch = 0;
-    width = 0;
-    height = 0;
-  }
-
-  unsigned char* y_plane;
-  unsigned char* u_plane;
-  unsigned char* v_plane;
-
-  int y_pitch;
-  int u_pitch;
-  int v_pitch;
-
-  unsigned short width;
-  unsigned short height;
-};
-
 /* External Capture interface. Returned by Create
  and implemented by the capture module.
  */
@@ -122,8 +95,9 @@
                                   int32_t videoFrameLength,
                                   const VideoCaptureCapability& frameInfo,
                                   int64_t captureTime = 0) = 0;
-    virtual int32_t IncomingFrameI420(const VideoFrameI420& video_frame,
-                                      int64_t captureTime = 0) = 0;
+    virtual int32_t IncomingI420VideoFrame(I420VideoFrame* video_frame,
+                                           int64_t captureTime = 0) = 0;
+
 protected:
     ~VideoCaptureExternal() {}
 };
diff --git a/modules/video_capture/test/video_capture_unittest.cc b/modules/video_capture/test/video_capture_unittest.cc
index b047bee..0f8052e 100644
--- a/modules/video_capture/test/video_capture_unittest.cc
+++ b/modules/video_capture/test/video_capture_unittest.cc
@@ -84,60 +84,6 @@
     return true;
 }
 
-// Compares the content of a I420 frame in planar form and the new video frame.
-static bool CompareFrames(const webrtc::VideoFrameI420& frame1,
-                          const webrtc::I420VideoFrame& frame2) {
-  if (frame1.width != frame2.width() ||
-      frame1.height != frame2.height()) {
-      return false;
-  }
-
-  // Compare Y
-  const unsigned char* y_plane = frame1.y_plane;
-  const unsigned char* y_plane2 = frame2.buffer(webrtc::kYPlane);
-  for (int i = 0; i < frame2.height(); ++i) {
-    for (int j = 0; j < frame2.width(); ++j) {
-      if (*y_plane != *y_plane2)
-        return false;
-      ++y_plane;
-      ++y_plane2;
-    }
-    y_plane += frame1.y_pitch - frame1.width;
-    y_plane2 += frame2.stride(webrtc::kYPlane) - frame2.width();
-  }
-
-  // Compare U
-  const unsigned char* u_plane = frame1.u_plane;
-  const unsigned char* u_plane2 = frame2.buffer(webrtc::kUPlane);
-  for (int i = 0; i < (frame2.height() + 1) / 2; ++i) {
-    for (int j = 0; j < (frame2.width() + 1) / 2; ++j) {
-      if (*u_plane != *u_plane2)
-        return false;
-      ++u_plane;
-      ++u_plane2;
-    }
-    u_plane += frame1.u_pitch - (frame1.width + 1) / 2;
-    u_plane2+= frame2.stride(webrtc::kUPlane) - (frame2.width() + 1) / 2;
-  }
-
-  // Compare V
-  unsigned char* v_plane = frame1.v_plane;
-  const unsigned char* v_plane2 = frame2.buffer(webrtc::kVPlane);
-  for (int i = 0; i < frame2.height() /2; ++i) {
-    for (int j = 0; j < frame2.width() /2; ++j) {
-      if (*u_plane != *u_plane2) {
-        return false;
-      }
-      ++v_plane;
-      ++v_plane2;
-    }
-    v_plane += frame1.v_pitch - (frame1.width + 1) / 2;
-    u_plane2+= frame2.stride(webrtc::kVPlane) - (frame2.width() + 1) / 2;
-  }
-  return true;
-}
-
-
 class TestVideoCaptureCallback : public VideoCaptureDataCallback {
  public:
   TestVideoCaptureCallback()
@@ -229,11 +175,6 @@
     return CompareFrames(last_frame_, frame);
   }
 
-  bool CompareLastFrame(const webrtc::VideoFrameI420& frame) {
-    CriticalSectionScoped cs(capture_cs_.get());
-    return CompareFrames(frame, last_frame_);
-  }
-
   void SetExpectedCaptureRotation(webrtc::VideoCaptureRotation rotation) {
     CriticalSectionScoped cs(capture_cs_.get());
     rotate_frame_ = rotation;
@@ -503,16 +444,11 @@
 // NOTE: flaky, sometimes fails on the last CompareLastFrame.
 // http://code.google.com/p/webrtc/issues/detail?id=777
 TEST_F(VideoCaptureExternalTest, DISABLED_TestExternalCaptureI420) {
-  webrtc::VideoFrameI420 frame_i420;
-  frame_i420.width = kTestWidth;
-  frame_i420.height = kTestHeight;
-  frame_i420.y_plane = test_frame_.buffer(webrtc::kYPlane);
-  frame_i420.u_plane = frame_i420.y_plane + (kTestWidth * kTestHeight);
-  frame_i420.v_plane = frame_i420.u_plane + ((kTestWidth * kTestHeight) >> 2);
-  frame_i420.y_pitch = kTestWidth;
-  frame_i420.u_pitch = kTestWidth / 2;
-  frame_i420.v_pitch = kTestWidth / 2;
-  EXPECT_EQ(0, capture_input_interface_->IncomingFrameI420(frame_i420, 0));
+  webrtc::I420VideoFrame frame_i420;
+  frame_i420.CopyFrame(test_frame_);
+
+  EXPECT_EQ(0,
+            capture_input_interface_->IncomingI420VideoFrame(&frame_i420, 0));
   EXPECT_TRUE(capture_callback_.CompareLastFrame(frame_i420));
 
   // Test with a frame with pitch not equal to width
@@ -566,16 +502,10 @@
     current_pointer += v_pitch;
     v_plane += uv_width;
   }
-  frame_i420.width = kTestWidth;
-  frame_i420.height = kTestHeight;
-  frame_i420.y_plane = aligned_test_frame.buffer(webrtc::kYPlane);
-  frame_i420.u_plane = aligned_test_frame.buffer(webrtc::kYPlane);
-  frame_i420.v_plane = aligned_test_frame.buffer(webrtc::kVPlane);
-  frame_i420.y_pitch = y_pitch;
-  frame_i420.u_pitch = u_pitch;
-  frame_i420.v_pitch = v_pitch;
+  frame_i420.CopyFrame(aligned_test_frame);
 
-  EXPECT_EQ(0, capture_input_interface_->IncomingFrameI420(frame_i420, 0));
+  EXPECT_EQ(0,
+            capture_input_interface_->IncomingI420VideoFrame(&frame_i420, 0));
   EXPECT_TRUE(capture_callback_.CompareLastFrame(test_frame_));
 }
 
diff --git a/modules/video_capture/video_capture_impl.cc b/modules/video_capture/video_capture_impl.cc
index a23d22b..6689fd1 100644
--- a/modules/video_capture/video_capture_impl.cc
+++ b/modules/video_capture/video_capture_impl.cc
@@ -358,27 +358,11 @@
     return 0;
 }
 
-int32_t VideoCaptureImpl::IncomingFrameI420(
-    const VideoFrameI420& video_frame, int64_t captureTime) {
+int32_t VideoCaptureImpl::IncomingI420VideoFrame(I420VideoFrame* video_frame,
+                                                 int64_t captureTime) {
 
   CriticalSectionScoped cs(&_callBackCs);
-  int size_y = video_frame.height * video_frame.y_pitch;
-  int size_u = video_frame.u_pitch * ((video_frame.height + 1) / 2);
-  int size_v =  video_frame.v_pitch * ((video_frame.height + 1) / 2);
-  // TODO(mikhal): Can we use Swap here? This will do a memcpy.
-  int ret = _captureFrame.CreateFrame(size_y, video_frame.y_plane,
-                                      size_u, video_frame.u_plane,
-                                      size_v, video_frame.v_plane,
-                                      video_frame.width, video_frame.height,
-                                      video_frame.y_pitch, video_frame.u_pitch,
-                                      video_frame.v_pitch);
-  if (ret < 0) {
-    WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
-                 "Failed to create I420VideoFrame");
-    return -1;
-  }
-
-  DeliverCapturedFrame(_captureFrame, captureTime);
+  DeliverCapturedFrame(*video_frame, captureTime);
 
   return 0;
 }
diff --git a/modules/video_capture/video_capture_impl.h b/modules/video_capture/video_capture_impl.h
index 41f555c..80d5e67 100644
--- a/modules/video_capture/video_capture_impl.h
+++ b/modules/video_capture/video_capture_impl.h
@@ -86,9 +86,9 @@
                                   int32_t videoFrameLength,
                                   const VideoCaptureCapability& frameInfo,
                                   int64_t captureTime = 0);
-    virtual int32_t IncomingFrameI420(
-        const VideoFrameI420& video_frame,
-        int64_t captureTime = 0);
+
+    virtual int32_t IncomingI420VideoFrame(I420VideoFrame* video_frame,
+                                           int64_t captureTime = 0);
 
     // Platform dependent
     virtual int32_t StartCapture(const VideoCaptureCapability& capability)
diff --git a/modules/video_coding/main/interface/video_coding.h b/modules/video_coding/main/interface/video_coding.h
index 5a206e9..c016676 100644
--- a/modules/video_coding/main/interface/video_coding.h
+++ b/modules/video_coding/main/interface/video_coding.h
@@ -21,6 +21,7 @@
 {
 
 class Clock;
+class EncodedImageCallback;
 class VideoEncoder;
 class VideoDecoder;
 struct CodecSpecificInfo;
@@ -592,17 +593,19 @@
     // Disables recording of debugging information.
     virtual int StopDebugRecording() = 0;
 
-    // Enables AutoMuter to turn off video when the rate drops below
+    // Lets the sender suspend video when the rate drops below
     // |threshold_bps|, and turns back on when the rate goes back up above
     // |threshold_bps| + |window_bps|.
-    virtual void EnableAutoMuting() = 0;
+    virtual void SuspendBelowMinBitrate() = 0;
 
-    // Disables AutoMuter.
-    virtual void DisableAutoMuting() = 0;
+    // Returns true if SuspendBelowMinBitrate is engaged and the video has been
+    // suspended due to bandwidth limitations; otherwise false.
+    virtual bool VideoSuspended() const = 0;
 
-    // Returns true if AutoMuter is engaged and the video has been muted due to
-    // bandwidth limitations; otherwise false.
-    virtual bool VideoMuted() const = 0;
+    virtual void RegisterPreDecodeImageCallback(
+        EncodedImageCallback* observer) = 0;
+    virtual void RegisterPostEncodeImageCallback(
+        EncodedImageCallback* post_encode_callback) = 0;
 };
 
 }  // namespace webrtc
diff --git a/modules/video_coding/main/source/generic_encoder.cc b/modules/video_coding/main/source/generic_encoder.cc
index c6ab9fb..064470b 100644
--- a/modules/video_coding/main/source/generic_encoder.cc
+++ b/modules/video_coding/main/source/generic_encoder.cc
@@ -12,6 +12,7 @@
 #include "webrtc/modules/video_coding/main/source/encoded_frame.h"
 #include "webrtc/modules/video_coding/main/source/generic_encoder.h"
 #include "webrtc/modules/video_coding/main/source/media_optimization.h"
+#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
 
 namespace webrtc {
 
@@ -147,7 +148,9 @@
 _encodedBytes(0),
 _payloadType(0),
 _codecType(kVideoCodecUnknown),
-_internalSource(false)
+_internalSource(false),
+post_encode_callback_lock_(CriticalSectionWrapper::CreateCriticalSection()),
+post_encode_callback_(NULL)
 #ifdef DEBUG_ENCODER_BIT_STREAM
 , _bitStreamAfterEncoder(NULL)
 #endif
@@ -177,6 +180,12 @@
     const CodecSpecificInfo* codecSpecificInfo,
     const RTPFragmentationHeader* fragmentationHeader)
 {
+    {
+      CriticalSectionScoped cs(post_encode_callback_lock_.get());
+      if (post_encode_callback_) {
+        post_encode_callback_->Encoded(encodedImage);
+      }
+    }
     FrameType frameType = VCMEncodedFrame::ConvertFrameType(encodedImage._frameType);
 
     uint32_t encodedBytes = 0;
@@ -271,4 +280,10 @@
       return;
   }
 }
+
+void VCMEncodedFrameCallback::RegisterPostEncodeImageCallback(
+    EncodedImageCallback* callback) {
+  CriticalSectionScoped cs(post_encode_callback_lock_.get());
+  post_encode_callback_ = callback;
+}
 }  // namespace webrtc
diff --git a/modules/video_coding/main/source/generic_encoder.h b/modules/video_coding/main/source/generic_encoder.h
index 0c2d287..c5cfeab 100644
--- a/modules/video_coding/main/source/generic_encoder.h
+++ b/modules/video_coding/main/source/generic_encoder.h
@@ -15,9 +15,13 @@
 
 #include <stdio.h>
 
+#include "webrtc/system_wrappers/interface/scoped_ptr.h"
+
 namespace webrtc
 {
 
+class CriticalSectionWrapper;
+
 namespace media_optimization {
 class MediaOptimization;
 }  // namespace media_optimization
@@ -55,6 +59,8 @@
     void SetCodecType(VideoCodecType codecType) {_codecType = codecType;};
     void SetInternalSource(bool internalSource) { _internalSource = internalSource; };
 
+    void RegisterPostEncodeImageCallback(EncodedImageCallback* callback);
+
 private:
     /*
      * Map information from info into rtp. If no relevant information is found
@@ -69,6 +75,10 @@
     uint8_t _payloadType;
     VideoCodecType _codecType;
     bool _internalSource;
+
+    scoped_ptr<CriticalSectionWrapper> post_encode_callback_lock_;
+    EncodedImageCallback* post_encode_callback_;
+
 #ifdef DEBUG_ENCODER_BIT_STREAM
     FILE* _bitStreamAfterEncoder;
 #endif
diff --git a/modules/video_coding/main/source/media_optimization.cc b/modules/video_coding/main/source/media_optimization.cc
index 815be36..37dff6c 100644
--- a/modules/video_coding/main/source/media_optimization.cc
+++ b/modules/video_coding/main/source/media_optimization.cc
@@ -47,10 +47,10 @@
       last_qm_update_time_(0),
       last_change_time_(0),
       num_layers_(0),
-      muting_enabled_(false),
-      video_muted_(false),
-      muter_threshold_bps_(0),
-      muter_window_bps_(0) {
+      suspension_enabled_(false),
+      video_suspended_(false),
+      suspension_threshold_bps_(0),
+      suspension_window_bps_(0) {
   memset(send_statistics_, 0, sizeof(send_statistics_));
   memset(incoming_frame_times_, -1, sizeof(incoming_frame_times_));
 }
@@ -193,7 +193,7 @@
     content_->ResetShortTermAvgData();
   }
 
-  CheckAutoMuteConditions();
+  CheckSuspendConditions();
 
   return target_bit_rate_;
 }
@@ -351,7 +351,7 @@
 bool MediaOptimization::DropFrame() {
   // Leak appropriate number of bytes.
   frame_dropper_->Leak((uint32_t)(InputFrameRate() + 0.5f));
-  if (video_muted_) {
+  if (video_suspended_) {
     return true;  // Drop all frames when muted.
   }
   return frame_dropper_->DropFrame();
@@ -418,17 +418,13 @@
   return VCM_OK;
 }
 
-void MediaOptimization::EnableAutoMuting(int threshold_bps, int window_bps) {
+void MediaOptimization::SuspendBelowMinBitrate(int threshold_bps,
+                                               int window_bps) {
   assert(threshold_bps > 0 && window_bps >= 0);
-  muter_threshold_bps_ = threshold_bps;
-  muter_window_bps_ = window_bps;
-  muting_enabled_ = true;
-  video_muted_ = false;
-}
-
-void MediaOptimization::DisableAutoMuting() {
-  muting_enabled_ = false;
-  video_muted_ = false;
+  suspension_threshold_bps_ = threshold_bps;
+  suspension_window_bps_ = window_bps;
+  suspension_enabled_ = true;
+  video_suspended_ = false;
 }
 
 // Private methods below this line.
@@ -605,19 +601,20 @@
   }
 }
 
-void MediaOptimization::CheckAutoMuteConditions() {
-  // Check conditions for AutoMute. |target_bit_rate_| is in bps.
-  if (muting_enabled_) {
-    if (!video_muted_) {
+void MediaOptimization::CheckSuspendConditions() {
+  // Check conditions for SuspendBelowMinBitrate. |target_bit_rate_| is in bps.
+  if (suspension_enabled_) {
+    if (!video_suspended_) {
       // Check if we just went below the threshold.
-      if (target_bit_rate_ < muter_threshold_bps_) {
-        video_muted_ = true;
+      if (target_bit_rate_ < suspension_threshold_bps_) {
+        video_suspended_ = true;
       }
     } else {
-      // Video is already muted. Check if we just went over the threshold
+      // Video is already suspended. Check if we just went over the threshold
       // with a margin.
-      if (target_bit_rate_ > muter_threshold_bps_ + muter_window_bps_) {
-        video_muted_ = false;
+      if (target_bit_rate_ >
+          suspension_threshold_bps_ + suspension_window_bps_) {
+        video_suspended_ = false;
       }
     }
   }
diff --git a/modules/video_coding/main/source/media_optimization.h b/modules/video_coding/main/source/media_optimization.h
index ca383bf..cde28d2 100644
--- a/modules/video_coding/main/source/media_optimization.h
+++ b/modules/video_coding/main/source/media_optimization.h
@@ -120,18 +120,15 @@
   // Computes new Quality Mode.
   int32_t SelectQuality();
 
-  // Enables AutoMuter to turn off video when the rate drops below
+  // Lets the sender suspend video when the rate drops below
   // |threshold_bps|, and turns back on when the rate goes back up above
   // |threshold_bps| + |window_bps|.
-  void EnableAutoMuting(int threshold_bps, int window_bps);
-
-  // Disables AutoMuter.
-  void DisableAutoMuting();
+  void SuspendBelowMinBitrate(int threshold_bps, int window_bps);
 
   // Accessors and mutators.
   int32_t max_bit_rate() const { return max_bit_rate_; }
   void set_max_payload_size(int32_t mtu) { max_payload_size_ = mtu; }
-  bool video_muted() const { return video_muted_; }
+  bool video_suspended() const { return video_suspended_; }
 
  private:
   typedef std::list<EncodedFrameSample> FrameSampleList;
@@ -161,10 +158,10 @@
 
   void ProcessIncomingFrameRate(int64_t now);
 
-  // Checks conditions for AutoMute. The method compares |target_bit_rate_|
-  // with the threshold values for AutoMute, and changes the state of
-  // |video_muted_| accordingly.
-  void CheckAutoMuteConditions();
+  // Checks conditions for suspending the video. The method compares
+  // |target_bit_rate_| with the threshold values for suspension, and changes
+  // the state of |video_suspended_| accordingly.
+  void CheckSuspendConditions();
 
   int32_t id_;
   Clock* clock_;
@@ -195,10 +192,10 @@
   int64_t last_qm_update_time_;
   int64_t last_change_time_;  // Content/user triggered.
   int num_layers_;
-  bool muting_enabled_;
-  bool video_muted_;
-  int muter_threshold_bps_;
-  int muter_window_bps_;
+  bool suspension_enabled_;
+  bool video_suspended_;
+  int suspension_threshold_bps_;
+  int suspension_window_bps_;
 };  // End of MediaOptimization class declaration.
 
 }  // namespace media_optimization
diff --git a/modules/video_coding/main/source/media_optimization_unittest.cc b/modules/video_coding/main/source/media_optimization_unittest.cc
index fc4fd75..1425dad 100644
--- a/modules/video_coding/main/source/media_optimization_unittest.cc
+++ b/modules/video_coding/main/source/media_optimization_unittest.cc
@@ -55,15 +55,15 @@
 
 
 TEST_F(TestMediaOptimization, VerifyMuting) {
-  // Enable video muter with these limits.
-  // Mute the video when the rate is below 50 kbps and unmute when it gets above
-  // 50 + 10 kbps again.
+  // Enable video suspension with these limits.
+  // Suspend the video when the rate is below 50 kbps and resume when it gets
+  // above 50 + 10 kbps again.
   const int kThresholdBps = 50000;
   const int kWindowBps = 10000;
-  media_opt_.EnableAutoMuting(kThresholdBps, kWindowBps);
+  media_opt_.SuspendBelowMinBitrate(kThresholdBps, kWindowBps);
 
-  // The video should not be muted from the start.
-  EXPECT_FALSE(media_opt_.video_muted());
+  // The video should not be suspended from the start.
+  EXPECT_FALSE(media_opt_.video_suspended());
 
   int target_bitrate_kbps = 100;
   media_opt_.SetTargetRates(target_bitrate_kbps * 1000,
@@ -81,7 +81,7 @@
   // Expect the muter to engage immediately and stay muted.
   // Test during 2 seconds.
   for (int time = 0; time < 2000; time += frame_time_ms_) {
-    EXPECT_TRUE(media_opt_.video_muted());
+    EXPECT_TRUE(media_opt_.video_suspended());
     ASSERT_NO_FATAL_FAILURE(AddFrameAndAdvanceTime(target_bitrate_kbps, true));
   }
 
@@ -93,7 +93,7 @@
   // Expect the muter to stay muted.
   // Test during 2 seconds.
   for (int time = 0; time < 2000; time += frame_time_ms_) {
-    EXPECT_TRUE(media_opt_.video_muted());
+    EXPECT_TRUE(media_opt_.video_suspended());
     ASSERT_NO_FATAL_FAILURE(AddFrameAndAdvanceTime(target_bitrate_kbps, true));
   }
 
@@ -104,7 +104,7 @@
   // Expect the muter to disengage immediately.
   // Test during 2 seconds.
   for (int time = 0; time < 2000; time += frame_time_ms_) {
-    EXPECT_FALSE(media_opt_.video_muted());
+    EXPECT_FALSE(media_opt_.video_suspended());
     ASSERT_NO_FATAL_FAILURE(
         AddFrameAndAdvanceTime((kThresholdBps + kWindowBps) / 1000, false));
   }
diff --git a/modules/video_coding/main/source/video_coding_impl.cc b/modules/video_coding/main/source/video_coding_impl.cc
index 9524984..1decc2f 100644
--- a/modules/video_coding/main/source/video_coding_impl.cc
+++ b/modules/video_coding/main/source/video_coding_impl.cc
@@ -197,16 +197,12 @@
     return sender_->StopDebugRecording();
   }
 
-  virtual void EnableAutoMuting() {
-    return sender_->EnableAutoMuting();
+  virtual void SuspendBelowMinBitrate() {
+    return sender_->SuspendBelowMinBitrate();
   }
 
-  virtual void DisableAutoMuting() {
-    return sender_->DisableAutoMuting();
-  }
-
-  virtual bool VideoMuted() const {
-    return sender_->VideoMuted();
+  virtual bool VideoSuspended() const {
+    return sender_->VideoSuspended();
   }
 
   virtual int32_t InitializeReceiver() OVERRIDE {
@@ -323,6 +319,16 @@
     return receiver_->SetReceiveChannelParameters(rtt);
   }
 
+  virtual void RegisterPreDecodeImageCallback(
+      EncodedImageCallback* observer) OVERRIDE {
+    receiver_->RegisterPreDecodeImageCallback(observer);
+  }
+
+  virtual void RegisterPostEncodeImageCallback(
+      EncodedImageCallback* observer) OVERRIDE {
+    sender_->RegisterPostEncodeImageCallback(observer);
+  }
+
  private:
   scoped_ptr<vcm::VideoSender> sender_;
   scoped_ptr<vcm::VideoReceiver> receiver_;
diff --git a/modules/video_coding/main/source/video_coding_impl.h b/modules/video_coding/main/source/video_coding_impl.h
index f9a7973..d9564c0 100644
--- a/modules/video_coding/main/source/video_coding_impl.h
+++ b/modules/video_coding/main/source/video_coding_impl.h
@@ -27,6 +27,9 @@
 #include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
 
 namespace webrtc {
+
+class EncodedFrameObserver;
+
 namespace vcm {
 
 class VCMProcessTimer {
@@ -95,9 +98,11 @@
   int StartDebugRecording(const char* file_name_utf8);
   int StopDebugRecording();
 
-  void EnableAutoMuting();
-  void DisableAutoMuting();
-  bool VideoMuted() const;
+  void SuspendBelowMinBitrate();
+  bool VideoSuspended() const;
+
+  void RegisterPostEncodeImageCallback(
+      EncodedImageCallback* post_encode_callback);
 
   int32_t TimeUntilNextProcess();
   int32_t Process();
@@ -174,6 +179,8 @@
   int32_t TimeUntilNextProcess();
   int32_t Process();
 
+  void RegisterPreDecodeImageCallback(EncodedImageCallback* observer);
+
  protected:
   int32_t Decode(const webrtc::VCMEncodedFrame& frame);
   int32_t RequestKeyFrame();
@@ -214,6 +221,7 @@
   VCMKeyRequestMode _keyRequestMode;
   bool _scheduleKeyRequest;
   size_t max_nack_list_size_;
+  EncodedImageCallback* pre_decode_image_callback_;
 
   VCMCodecDataBase _codecDataBase;
   VCMProcessTimer _receiveStatsTimer;
diff --git a/modules/video_coding/main/source/video_receiver.cc b/modules/video_coding/main/source/video_receiver.cc
index cea8b44..68668ea 100644
--- a/modules/video_coding/main/source/video_receiver.cc
+++ b/modules/video_coding/main/source/video_receiver.cc
@@ -52,6 +52,7 @@
       _keyRequestMode(kKeyOnError),
       _scheduleKeyRequest(false),
       max_nack_list_size_(0),
+      pre_decode_image_callback_(NULL),
       _codecDataBase(id),
       _receiveStatsTimer(1000, clock_),
       _retransmissionTimer(10, clock_),
@@ -400,6 +401,11 @@
     _timing.UpdateCurrentDelay(frame->RenderTimeMs(),
                                clock_->TimeInMilliseconds());
 
+    if (pre_decode_image_callback_) {
+      EncodedImage encoded_image(frame->EncodedImage());
+      pre_decode_image_callback_->Encoded(encoded_image);
+    }
+
 #ifdef DEBUG_DECODER_BIT_STREAM
     if (_bitStreamBeforeDecoder != NULL) {
       // Write bit stream to file for debugging purposes
@@ -815,5 +821,11 @@
   return _receiver.SetMinReceiverDelay(desired_delay_ms);
 }
 
+void VideoReceiver::RegisterPreDecodeImageCallback(
+    EncodedImageCallback* observer) {
+  CriticalSectionScoped cs(_receiveCritSect);
+  pre_decode_image_callback_ = observer;
+}
+
 }  // namespace vcm
 }  // namespace webrtc
diff --git a/modules/video_coding/main/source/video_sender.cc b/modules/video_coding/main/source/video_sender.cc
index 4cbcc89..948218b 100644
--- a/modules/video_coding/main/source/video_sender.cc
+++ b/modules/video_coding/main/source/video_sender.cc
@@ -422,11 +422,11 @@
   return VCM_OK;
 }
 
-void VideoSender::EnableAutoMuting() {
+void VideoSender::SuspendBelowMinBitrate() {
   CriticalSectionScoped cs(_sendCritSect);
   VideoCodec current_send_codec;
   if (SendCodec(&current_send_codec) != 0) {
-    assert(false);  // Must set a send codec before enabling auto-mute.
+    assert(false);  // Must set a send codec before SuspendBelowMinBitrate.
     return;
   }
   int threshold_bps;
@@ -438,17 +438,18 @@
   // Set the hysteresis window to be at 10% of the threshold, but at least
   // 10 kbps.
   int window_bps = std::max(threshold_bps / 10, 10000);
-  _mediaOpt.EnableAutoMuting(threshold_bps, window_bps);
+  _mediaOpt.SuspendBelowMinBitrate(threshold_bps, window_bps);
 }
 
-void VideoSender::DisableAutoMuting() {
+bool VideoSender::VideoSuspended() const {
   CriticalSectionScoped cs(_sendCritSect);
-  _mediaOpt.DisableAutoMuting();
+  return _mediaOpt.video_suspended();
 }
 
-bool VideoSender::VideoMuted() const {
+void VideoSender::RegisterPostEncodeImageCallback(
+    EncodedImageCallback* observer) {
   CriticalSectionScoped cs(_sendCritSect);
-  return _mediaOpt.video_muted();
+  _encodedFrameCallback.RegisterPostEncodeImageCallback(observer);
 }
 
 }  // namespace vcm
diff --git a/modules/video_processing/main/test/unit_test/video_processing_unittest.cc b/modules/video_processing/main/test/unit_test/video_processing_unittest.cc
index 89c59ec..6e54923 100644
--- a/modules/video_processing/main/test/unit_test/video_processing_unittest.cc
+++ b/modules/video_processing/main/test/unit_test/video_processing_unittest.cc
@@ -45,7 +45,10 @@
 
   ASSERT_EQ(0, video_frame_.CreateEmptyFrame(width_, height_, width_,
                                             half_width_, half_width_));
-
+  // Clear video frame so DrMemory/Valgrind will allow reads of the buffer.
+  memset(video_frame_.buffer(kYPlane), 0, video_frame_.allocated_size(kYPlane));
+  memset(video_frame_.buffer(kUPlane), 0, video_frame_.allocated_size(kUPlane));
+  memset(video_frame_.buffer(kVPlane), 0, video_frame_.allocated_size(kVPlane));
   const std::string video_file =
       webrtc::test::ResourcePath("foreman_cif", "yuv");
   source_file_  = fopen(video_file.c_str(),"rb");
diff --git a/test/direct_transport.cc b/test/direct_transport.cc
index 8c7e351..7b3ff22 100644
--- a/test/direct_transport.cc
+++ b/test/direct_transport.cc
@@ -12,6 +12,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 #include "webrtc/call.h"
+#include "webrtc/system_wrappers/interface/clock.h"
 
 namespace webrtc {
 namespace test {
@@ -20,8 +21,22 @@
     : lock_(CriticalSectionWrapper::CreateCriticalSection()),
       packet_event_(EventWrapper::Create()),
       thread_(ThreadWrapper::CreateThread(NetworkProcess, this)),
+      clock_(Clock::GetRealTimeClock()),
       shutting_down_(false),
-      receiver_(NULL) {
+      receiver_(NULL),
+      delay_ms_(0) {
+  unsigned int thread_id;
+  EXPECT_TRUE(thread_->Start(thread_id));
+}
+
+DirectTransport::DirectTransport(int delay_ms)
+    : lock_(CriticalSectionWrapper::CreateCriticalSection()),
+      packet_event_(EventWrapper::Create()),
+      thread_(ThreadWrapper::CreateThread(NetworkProcess, this)),
+      clock_(Clock::GetRealTimeClock()),
+      shutting_down_(false),
+      receiver_(NULL),
+      delay_ms_(delay_ms) {
   unsigned int thread_id;
   EXPECT_TRUE(thread_->Start(thread_id));
 }
@@ -42,28 +57,33 @@
   receiver_ = receiver;
 }
 
-bool DirectTransport::SendRTP(const uint8_t* data, size_t length) {
-  QueuePacket(data, length);
+bool DirectTransport::SendRtp(const uint8_t* data, size_t length) {
+  QueuePacket(data, length, clock_->TimeInMilliseconds() + delay_ms_);
   return true;
 }
 
-bool DirectTransport::SendRTCP(const uint8_t* data, size_t length) {
-  QueuePacket(data, length);
+bool DirectTransport::SendRtcp(const uint8_t* data, size_t length) {
+  QueuePacket(data, length, clock_->TimeInMilliseconds() + delay_ms_);
   return true;
 }
 
-DirectTransport::Packet::Packet() : length(0) {}
+DirectTransport::Packet::Packet() : length(0), delivery_time_ms(0) {}
 
-DirectTransport::Packet::Packet(const uint8_t* data, size_t length)
-    : length(length) {
+DirectTransport::Packet::Packet(const uint8_t* data,
+                                size_t length,
+                                int64_t delivery_time_ms)
+    : length(length), delivery_time_ms(delivery_time_ms) {
   EXPECT_LE(length, sizeof(this->data));
   memcpy(this->data, data, length);
 }
 
-void DirectTransport::QueuePacket(const uint8_t* data, size_t length) {
+void DirectTransport::QueuePacket(const uint8_t* data,
+                                  size_t length,
+                                  int64_t delivery_time_ms) {
   CriticalSectionScoped crit(lock_.get());
-  EXPECT_TRUE(receiver_ != NULL);
-  packet_queue_.push_back(Packet(data, length));
+  if (receiver_ == NULL)
+    return;
+  packet_queue_.push_back(Packet(data, length, delivery_time_ms));
   packet_event_->Set();
 }
 
@@ -79,12 +99,27 @@
       if (packet_queue_.empty())
         break;
       p = packet_queue_.front();
+      if (p.delivery_time_ms > clock_->TimeInMilliseconds())
+        break;
       packet_queue_.pop_front();
     }
     receiver_->DeliverPacket(p.data, p.length);
   }
+  uint32_t time_until_next_delivery = WEBRTC_EVENT_INFINITE;
+  {
+    CriticalSectionScoped crit(lock_.get());
+    if (!packet_queue_.empty()) {
+      int64_t now_ms = clock_->TimeInMilliseconds();
+      const int64_t delivery_time_ms = packet_queue_.front().delivery_time_ms;
+      if (delivery_time_ms > now_ms) {
+        time_until_next_delivery = delivery_time_ms - now_ms;
+      } else {
+        time_until_next_delivery = 0;
+      }
+    }
+  }
 
-  switch (packet_event_->Wait(WEBRTC_EVENT_INFINITE)) {
+  switch (packet_event_->Wait(time_until_next_delivery)) {
     case kEventSignaled:
       packet_event_->Reset();
       break;
diff --git a/test/direct_transport.h b/test/direct_transport.h
index d4cb45a..b6021cb 100644
--- a/test/direct_transport.h
+++ b/test/direct_transport.h
@@ -22,6 +22,7 @@
 
 namespace webrtc {
 
+class Clock;
 class PacketReceiver;
 
 namespace test {
@@ -29,24 +30,28 @@
 class DirectTransport : public newapi::Transport {
  public:
   DirectTransport();
+  explicit DirectTransport(int delay_ms);
   ~DirectTransport();
 
   virtual void StopSending();
   virtual void SetReceiver(PacketReceiver* receiver);
 
-  virtual bool SendRTP(const uint8_t* data, size_t length) OVERRIDE;
-  virtual bool SendRTCP(const uint8_t* data, size_t length) OVERRIDE;
+  virtual bool SendRtp(const uint8_t* data, size_t length) OVERRIDE;
+  virtual bool SendRtcp(const uint8_t* data, size_t length) OVERRIDE;
 
  private:
   struct Packet {
     Packet();
-    Packet(const uint8_t* data, size_t length);
+    Packet(const uint8_t* data, size_t length, int64_t delivery_time_ms);
 
     uint8_t data[1500];
     size_t length;
+    int64_t delivery_time_ms;
   };
 
-  void QueuePacket(const uint8_t* data, size_t length);
+  void QueuePacket(const uint8_t* data,
+                   size_t length,
+                   int64_t delivery_time_ms);
 
   static bool NetworkProcess(void* transport);
   bool SendPackets();
@@ -54,11 +59,14 @@
   scoped_ptr<CriticalSectionWrapper> lock_;
   scoped_ptr<EventWrapper> packet_event_;
   scoped_ptr<ThreadWrapper> thread_;
+  Clock* clock_;
 
   bool shutting_down_;
 
   std::deque<Packet> packet_queue_;
   PacketReceiver* receiver_;
+  // TODO(stefan): Replace this with FakeNetworkPipe.
+  const int delay_ms_;
 };
 }  // namespace test
 }  // namespace webrtc
diff --git a/test/fake_audio_device.cc b/test/fake_audio_device.cc
new file mode 100644
index 0000000..a6fe165
--- /dev/null
+++ b/test/fake_audio_device.cc
@@ -0,0 +1,146 @@
+/*
+ *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/test/fake_audio_device.h"
+
+#include <algorithm>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webrtc/modules/media_file/source/media_file_utility.h"
+#include "webrtc/system_wrappers/interface/clock.h"
+#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
+#include "webrtc/system_wrappers/interface/event_wrapper.h"
+#include "webrtc/system_wrappers/interface/file_wrapper.h"
+#include "webrtc/system_wrappers/interface/thread_wrapper.h"
+
+namespace webrtc {
+namespace test {
+
+FakeAudioDevice::FakeAudioDevice(Clock* clock, const std::string& filename)
+    : audio_callback_(NULL),
+      capturing_(false),
+      captured_audio_(),
+      playout_buffer_(),
+      last_playout_ms_(-1),
+      clock_(clock),
+      tick_(EventWrapper::Create()),
+      lock_(CriticalSectionWrapper::CreateCriticalSection()),
+      file_utility_(new ModuleFileUtility(0)),
+      input_stream_(FileWrapper::Create()) {
+  memset(captured_audio_, 0, sizeof(captured_audio_));
+  memset(playout_buffer_, 0, sizeof(playout_buffer_));
+  // Open audio input file as read-only and looping.
+  EXPECT_EQ(0, input_stream_->OpenFile(filename.c_str(), true, true))
+      << filename;
+}
+
+FakeAudioDevice::~FakeAudioDevice() {
+  Stop();
+
+  if (thread_.get() != NULL)
+    thread_->Stop();
+}
+
+int32_t FakeAudioDevice::Init() {
+  CriticalSectionScoped cs(lock_.get());
+  if (file_utility_->InitPCMReading(*input_stream_.get()) != 0)
+    return -1;
+
+  if (!tick_->StartTimer(true, 10))
+    return -1;
+  thread_.reset(ThreadWrapper::CreateThread(
+      FakeAudioDevice::Run, this, webrtc::kHighPriority, "FakeAudioDevice"));
+  if (thread_.get() == NULL)
+    return -1;
+  unsigned int thread_id;
+  if (!thread_->Start(thread_id)) {
+    thread_.reset();
+    return -1;
+  }
+  return 0;
+}
+
+int32_t FakeAudioDevice::RegisterAudioCallback(AudioTransport* callback) {
+  CriticalSectionScoped cs(lock_.get());
+  audio_callback_ = callback;
+  return 0;
+}
+
+bool FakeAudioDevice::Playing() const {
+  CriticalSectionScoped cs(lock_.get());
+  return capturing_;
+}
+
+int32_t FakeAudioDevice::PlayoutDelay(uint16_t* delay_ms) const {
+  *delay_ms = 0;
+  return 0;
+}
+
+bool FakeAudioDevice::Recording() const {
+  CriticalSectionScoped cs(lock_.get());
+  return capturing_;
+}
+
+bool FakeAudioDevice::Run(void* obj) {
+  static_cast<FakeAudioDevice*>(obj)->CaptureAudio();
+  return true;
+}
+
+void FakeAudioDevice::CaptureAudio() {
+  {
+    CriticalSectionScoped cs(lock_.get());
+    if (capturing_) {
+      int bytes_read = file_utility_->ReadPCMData(
+          *input_stream_.get(), captured_audio_, kBufferSizeBytes);
+      if (bytes_read <= 0)
+        return;
+      int num_samples = bytes_read / 2;  // 2 bytes per sample.
+      uint32_t new_mic_level;
+      EXPECT_EQ(0,
+                audio_callback_->RecordedDataIsAvailable(captured_audio_,
+                                                         num_samples,
+                                                         2,
+                                                         1,
+                                                         kFrequencyHz,
+                                                         0,
+                                                         0,
+                                                         0,
+                                                         false,
+                                                         new_mic_level));
+      uint32_t samples_needed = kFrequencyHz / 100;
+      int64_t now_ms = clock_->TimeInMilliseconds();
+      uint32_t time_since_last_playout_ms = now_ms - last_playout_ms_;
+      if (last_playout_ms_ > 0 && time_since_last_playout_ms > 0)
+        samples_needed = std::min(kFrequencyHz / time_since_last_playout_ms,
+                                  kBufferSizeBytes / 2);
+      uint32_t samples_out = 0;
+      EXPECT_EQ(0,
+                audio_callback_->NeedMorePlayData(samples_needed,
+                                                  2,
+                                                  1,
+                                                  kFrequencyHz,
+                                                  playout_buffer_,
+                                                  samples_out));
+    }
+  }
+  tick_->Wait(WEBRTC_EVENT_INFINITE);
+}
+
+void FakeAudioDevice::Start() {
+  CriticalSectionScoped cs(lock_.get());
+  capturing_ = true;
+}
+
+void FakeAudioDevice::Stop() {
+  CriticalSectionScoped cs(lock_.get());
+  capturing_ = false;
+}
+}  // namespace test
+}  // namespace webrtc
diff --git a/test/fake_audio_device.h b/test/fake_audio_device.h
new file mode 100644
index 0000000..40a7547
--- /dev/null
+++ b/test/fake_audio_device.h
@@ -0,0 +1,69 @@
+/*
+ *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+#ifndef WEBRTC_TEST_FAKE_AUDIO_DEVICE_H_
+#define WEBRTC_TEST_FAKE_AUDIO_DEVICE_H_
+
+#include <string>
+
+#include "webrtc/modules/audio_device/include/fake_audio_device.h"
+#include "webrtc/system_wrappers/interface/scoped_ptr.h"
+#include "webrtc/typedefs.h"
+
+namespace webrtc {
+
+class Clock;
+class CriticalSectionWrapper;
+class EventWrapper;
+class FileWrapper;
+class ModuleFileUtility;
+class ThreadWrapper;
+
+namespace test {
+
+class FakeAudioDevice : public FakeAudioDeviceModule {
+ public:
+  FakeAudioDevice(Clock* clock, const std::string& filename);
+
+  virtual ~FakeAudioDevice();
+
+  virtual int32_t Init() OVERRIDE;
+  virtual int32_t RegisterAudioCallback(AudioTransport* callback) OVERRIDE;
+
+  virtual bool Playing() const OVERRIDE;
+  virtual int32_t PlayoutDelay(uint16_t* delay_ms) const OVERRIDE;
+  virtual bool Recording() const OVERRIDE;
+
+  void Start();
+  void Stop();
+
+ private:
+  static bool Run(void* obj);
+  void CaptureAudio();
+
+  static const uint32_t kFrequencyHz = 16000;
+  static const uint32_t kBufferSizeBytes = 2 * kFrequencyHz;
+
+  AudioTransport* audio_callback_;
+  bool capturing_;
+  int8_t captured_audio_[kBufferSizeBytes];
+  int8_t playout_buffer_[kBufferSizeBytes];
+  int64_t last_playout_ms_;
+
+  Clock* clock_;
+  scoped_ptr<EventWrapper> tick_;
+  scoped_ptr<CriticalSectionWrapper> lock_;
+  scoped_ptr<ThreadWrapper> thread_;
+  scoped_ptr<ModuleFileUtility> file_utility_;
+  scoped_ptr<FileWrapper> input_stream_;
+};
+}  // namespace test
+}  // namespace webrtc
+
+#endif  // WEBRTC_TEST_FAKE_AUDIO_DEVICE_H_
diff --git a/test/fake_encoder.cc b/test/fake_encoder.cc
index 454be16..f4e5227 100644
--- a/test/fake_encoder.cc
+++ b/test/fake_encoder.cc
@@ -20,13 +20,16 @@
       callback_(NULL),
       target_bitrate_kbps_(0),
       last_encode_time_ms_(0) {
-  memset(encoded_buffer_, 0, sizeof(encoded_buffer_));
+  // Generate some arbitrary not-all-zero data
+  for (size_t i = 0; i < sizeof(encoded_buffer_); ++i) {
+    encoded_buffer_[i] = static_cast<uint8_t>(i);
+  }
 }
 
 FakeEncoder::~FakeEncoder() {}
 
 void FakeEncoder::SetCodecSettings(VideoCodec* codec,
-                                         size_t num_streams) {
+                                   size_t num_streams) {
   assert(num_streams > 0);
   assert(num_streams <= kMaxSimulcastStreams);
 
@@ -57,7 +60,6 @@
 
   codec->codecType = kVideoCodecGeneric;
   strcpy(codec->plName, "FAKE");
-  codec->plType = 125;
 }
 
 int32_t FakeEncoder::InitEncode(const VideoCodec* config,
diff --git a/test/null_transport.cc b/test/null_transport.cc
index 1baeedf..3cba638 100644
--- a/test/null_transport.cc
+++ b/test/null_transport.cc
@@ -12,11 +12,11 @@
 namespace webrtc {
 namespace test {
 
-bool NullTransport::SendRTP(const uint8_t* packet, size_t length) {
+bool NullTransport::SendRtp(const uint8_t* packet, size_t length) {
   return true;
 }
 
-bool NullTransport::SendRTCP(const uint8_t* packet, size_t length) {
+bool NullTransport::SendRtcp(const uint8_t* packet, size_t length) {
   return true;
 }
 
diff --git a/test/null_transport.h b/test/null_transport.h
index f9839b9..e8d4d10 100644
--- a/test/null_transport.h
+++ b/test/null_transport.h
@@ -19,8 +19,8 @@
 namespace test {
 class NullTransport : public newapi::Transport {
  public:
-  virtual bool SendRTP(const uint8_t* packet, size_t length) OVERRIDE;
-  virtual bool SendRTCP(const uint8_t* packet, size_t length) OVERRIDE;
+  virtual bool SendRtp(const uint8_t* packet, size_t length) OVERRIDE;
+  virtual bool SendRtcp(const uint8_t* packet, size_t length) OVERRIDE;
 };
 }  // namespace test
 }  // namespace webrtc
diff --git a/test/rtp_rtcp_observer.h b/test/rtp_rtcp_observer.h
index ee06e2a..56c96fa 100644
--- a/test/rtp_rtcp_observer.h
+++ b/test/rtp_rtcp_observer.h
@@ -13,6 +13,7 @@
 #include <map>
 #include <vector>
 
+#include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h"
 #include "webrtc/typedefs.h"
 #include "webrtc/video_send_stream.h"
 
@@ -21,6 +22,7 @@
 
 class RtpRtcpObserver {
  public:
+  virtual ~RtpRtcpObserver() {}
   newapi::Transport* SendTransport() {
     return &send_transport_;
   }
@@ -40,20 +42,43 @@
     receive_transport_.StopSending();
   }
 
-  EventTypeWrapper Wait() { return observation_complete_->Wait(timeout_ms_); }
+  virtual EventTypeWrapper Wait() {
+    EventTypeWrapper result = observation_complete_->Wait(timeout_ms_);
+    observation_complete_->Reset();
+    return result;
+  }
 
  protected:
-  RtpRtcpObserver(unsigned int event_timeout_ms)
+  RtpRtcpObserver(unsigned int event_timeout_ms, int delay_ms)
       : lock_(CriticalSectionWrapper::CreateCriticalSection()),
         observation_complete_(EventWrapper::Create()),
+        parser_(RtpHeaderParser::Create()),
         send_transport_(lock_.get(),
                         this,
                         &RtpRtcpObserver::OnSendRtp,
-                        &RtpRtcpObserver::OnSendRtcp),
+                        &RtpRtcpObserver::OnSendRtcp,
+                        delay_ms),
         receive_transport_(lock_.get(),
                            this,
                            &RtpRtcpObserver::OnReceiveRtp,
-                           &RtpRtcpObserver::OnReceiveRtcp),
+                           &RtpRtcpObserver::OnReceiveRtcp,
+                           delay_ms),
+        timeout_ms_(event_timeout_ms) {}
+
+  explicit RtpRtcpObserver(unsigned int event_timeout_ms)
+      : lock_(CriticalSectionWrapper::CreateCriticalSection()),
+        observation_complete_(EventWrapper::Create()),
+        parser_(RtpHeaderParser::Create()),
+        send_transport_(lock_.get(),
+                        this,
+                        &RtpRtcpObserver::OnSendRtp,
+                        &RtpRtcpObserver::OnSendRtcp,
+                        0),
+        receive_transport_(lock_.get(),
+                           this,
+                           &RtpRtcpObserver::OnReceiveRtp,
+                           &RtpRtcpObserver::OnReceiveRtcp,
+                           0),
         timeout_ms_(event_timeout_ms) {}
 
   enum Action {
@@ -83,17 +108,20 @@
    public:
     typedef Action (RtpRtcpObserver::*PacketTransportAction)(const uint8_t*,
                                                              size_t);
+
     PacketTransport(CriticalSectionWrapper* lock,
                     RtpRtcpObserver* observer,
                     PacketTransportAction on_rtp,
-                    PacketTransportAction on_rtcp)
-        : lock_(lock),
+                    PacketTransportAction on_rtcp,
+                    int delay_ms)
+        : test::DirectTransport(delay_ms),
+          lock_(lock),
           observer_(observer),
           on_rtp_(on_rtp),
           on_rtcp_(on_rtcp) {}
 
   private:
-    virtual bool SendRTP(const uint8_t* packet, size_t length) OVERRIDE {
+    virtual bool SendRtp(const uint8_t* packet, size_t length) OVERRIDE {
       Action action;
       {
         CriticalSectionScoped crit_(lock_);
@@ -104,12 +132,12 @@
           // Drop packet silently.
           return true;
         case SEND_PACKET:
-          return test::DirectTransport::SendRTP(packet, length);
+          return test::DirectTransport::SendRtp(packet, length);
       }
       return true;  // Will never happen, makes compiler happy.
     }
 
-    virtual bool SendRTCP(const uint8_t* packet, size_t length) OVERRIDE {
+    virtual bool SendRtcp(const uint8_t* packet, size_t length) OVERRIDE {
       Action action;
       {
         CriticalSectionScoped crit_(lock_);
@@ -120,7 +148,7 @@
           // Drop packet silently.
           return true;
         case SEND_PACKET:
-          return test::DirectTransport::SendRTCP(packet, length);
+          return test::DirectTransport::SendRtcp(packet, length);
       }
       return true;  // Will never happen, makes compiler happy.
     }
@@ -135,6 +163,7 @@
  protected:
   scoped_ptr<CriticalSectionWrapper> lock_;
   scoped_ptr<EventWrapper> observation_complete_;
+  scoped_ptr<RtpHeaderParser> parser_;
 
  private:
   PacketTransport send_transport_, receive_transport_;
diff --git a/test/webrtc_test_common.gyp b/test/webrtc_test_common.gyp
index 1a7e579..5b546c7 100644
--- a/test/webrtc_test_common.gyp
+++ b/test/webrtc_test_common.gyp
@@ -16,6 +16,8 @@
       'sources': [
         'direct_transport.cc',
         'direct_transport.h',
+        'fake_audio_device.cc',
+        'fake_audio_device.h',
         'fake_decoder.cc',
         'fake_decoder.h',
         'fake_encoder.cc',
@@ -115,6 +117,7 @@
         '<(DEPTH)/testing/gtest.gyp:gtest',
         '<(DEPTH)/third_party/gflags/gflags.gyp:gflags',
         '<(webrtc_root)/modules/modules.gyp:video_capture_module',
+        '<(webrtc_root)/modules/modules.gyp:media_file',
         '<(webrtc_root)/test/test.gyp:test_support',
         '<(webrtc_root)/common_video/common_video.gyp:frame_generator',
       ],
diff --git a/tools/barcode_tools/barcode_decoder.py b/tools/barcode_tools/barcode_decoder.py
index 0bf8b5b..7a36668 100755
--- a/tools/barcode_tools/barcode_decoder.py
+++ b/tools/barcode_tools/barcode_decoder.py
@@ -11,15 +11,18 @@
 import os
 import sys
 
-import helper_functions
+if __name__ == '__main__':
+  # Make sure we always can import helper_functions.
+  sys.path.append(os.path.dirname(__file__))
 
+import helper_functions
 
 # Chrome browsertests will throw away stderr; avoid that output gets lost.
 sys.stderr = sys.stdout
 
 
 def convert_yuv_to_png_files(yuv_file_name, yuv_frame_width, yuv_frame_height,
-                             output_directory, ffmpeg_dir=None):
+                             output_directory, ffmpeg_path):
   """Converts a YUV video file into PNG frames.
 
   The function uses ffmpeg to convert the YUV file. The output of ffmpeg is in
@@ -31,18 +34,17 @@
     yuv_frame_height(int): The height of one YUV frame.
     output_directory(string): The output directory where the PNG frames will be
       stored.
-    ffmpeg_dir(string): The directory containing the ffmpeg executable. If
-      omitted, the PATH will be searched for it.
+    ffmpeg_path(string): The path to the ffmpeg executable. If None, the PATH
+      will be searched for it.
 
   Return:
     (bool): True if the conversion was OK.
   """
   size_string = str(yuv_frame_width) + 'x' + str(yuv_frame_height)
   output_files_pattern = os.path.join(output_directory, 'frame_%04d.png')
-  ffmpeg_executable = 'ffmpeg.exe' if sys.platform == 'win32' else 'ffmpeg'
-  if ffmpeg_dir:
-    ffmpeg_executable = os.path.join(ffmpeg_dir, ffmpeg_executable)
-  command = [ffmpeg_executable, '-s', '%s' % size_string, '-i', '%s'
+  if not ffmpeg_path:
+    ffmpeg_path = 'ffmpeg.exe' if sys.platform == 'win32' else 'ffmpeg'
+  command = [ffmpeg_path, '-s', '%s' % size_string, '-i', '%s'
              % yuv_file_name, '-f', 'image2', '-vcodec', 'png',
              '%s' % output_files_pattern]
   try:
@@ -54,12 +56,12 @@
     print 'Error executing command: %s. Error: %s' % (command, err)
     return False
   except OSError:
-    print ('Did not find %s. Have you installed it?' % ffmpeg_executable)
+    print ('Did not find %s. Have you installed it?' % ffmpeg_path)
     return False
   return True
 
 
-def decode_frames(input_directory, zxing_dir=None):
+def decode_frames(input_directory, zxing_path):
   """Decodes the barcodes overlaid in each frame.
 
   The function uses the Zxing command-line tool from the Zxing C++ distribution
@@ -73,19 +75,18 @@
   Args:
     input_directory(string): The input directory from where the PNG frames are
       read.
-    zxing_dir(string): The directory containing the zxing executable. If
-      omitted, the PATH will be searched for it.
+    zxing_path(string): The path to the zxing binary. If specified as None,
+      the PATH will be searched for it.
   Return:
-    (bool): True if the decoding went without errors.
+    (bool): True if the decoding succeeded.
   """
-  zxing_executable = 'zxing.exe' if sys.platform == 'win32' else 'zxing'
-  if zxing_dir:
-    zxing_executable = os.path.join(zxing_dir, zxing_executable)
-  print 'Decoding barcodes from PNG files with %s...' % zxing_executable
+  if not zxing_path:
+    zxing_path = 'zxing.exe' if sys.platform == 'win32' else 'zxing'
+  print 'Decoding barcodes from PNG files with %s...' % zxing_path
   return helper_functions.perform_action_on_all_files(
       directory=input_directory, file_pattern='frame_',
       file_extension='png', start_number=1, action=_decode_barcode_in_file,
-      command_line_decoder=zxing_executable)
+      command_line_decoder=zxing_path)
 
 
 def _decode_barcode_in_file(file_name, command_line_decoder):
@@ -230,14 +231,14 @@
   usage = "usage: %prog [options]"
   parser = optparse.OptionParser(usage=usage)
 
-  parser.add_option('--zxing_dir', type='string',
-                    help=('The path to the directory where the zxing executable'
-                          'is located. If omitted, it will be assumed to be '
-                          'present in the PATH.'))
-  parser.add_option('--ffmpeg_dir', type='string', default=None,
-                    help=('The path to the directory where the ffmpeg '
-                          'executable is located. If omitted, it will be '
-                          'assumed to be present in the PATH.'))
+  parser.add_option('--zxing_path', type='string',
+                    help=('The path to where the zxing executable is located. '
+                          'If omitted, it will be assumed to be present in the '
+                          'PATH with the name zxing[.exe].'))
+  parser.add_option('--ffmpeg_path', type='string',
+                    help=('The path to where the ffmpeg executable is located. '
+                          'If omitted, it will be assumed to be present in the '
+                          'PATH with the name ffmpeg[.exe].'))
   parser.add_option('--yuv_frame_width', type='int', default=640,
                     help='Width of the YUV file\'s frames. Default: %default')
   parser.add_option('--yuv_frame_height', type='int', default=480,
@@ -271,13 +272,13 @@
   if not convert_yuv_to_png_files(options.yuv_file, options.yuv_frame_width,
                                   options.yuv_frame_height,
                                   output_directory=options.png_working_dir,
-                                  ffmpeg_dir=options.ffmpeg_dir):
+                                  ffmpeg_path=options.ffmpeg_path):
     print 'An error occurred converting from YUV to PNG frames.'
     return -1
 
   # Decode the barcodes from the PNG frames.
   if not decode_frames(input_directory=options.png_working_dir,
-                       zxing_dir=options.zxing_dir):
+                       zxing_path=options.zxing_path):
     print 'An error occurred decoding barcodes from PNG frames.'
     return -2
 
diff --git a/tools/compare_videos.py b/tools/compare_videos.py
index ee8e6d5..f6275a6 100755
--- a/tools/compare_videos.py
+++ b/tools/compare_videos.py
@@ -9,8 +9,10 @@
 
 import optparse
 import os
+import shutil
 import subprocess
 import sys
+import tempfile
 
 
 SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
@@ -24,7 +26,7 @@
   usage = 'usage: %prog [options]'
   parser = optparse.OptionParser(usage=usage)
 
-  parser.add_option('--label', type='string', default="MY_TEST",
+  parser.add_option('--label', type='string', default='MY_TEST',
                     help=('Label of the test, used to identify different '
                           'tests. Default: %default'))
   parser.add_option('--ref_video', type='string',
@@ -34,6 +36,18 @@
                           'video (YUV).'))
   parser.add_option('--frame_analyzer', type='string',
                     help='Path to the frame analyzer executable.')
+  parser.add_option('--barcode_decoder', type='string',
+                    help=('Path to the barcode decoder script. By default, we '
+                          'will assume we can find it in barcode_tools/'
+                          'relative to this directory.'))
+  parser.add_option('--ffmpeg_path', type='string',
+                    help=('The path to where the ffmpeg executable is located. '
+                          'If omitted, it will be assumed to be present in the '
+                          'PATH with the name ffmpeg[.exe].'))
+  parser.add_option('--zxing_path', type='string',
+                    help=('The path to where the zxing executable is located. '
+                          'If omitted, it will be assumed to be present in the '
+                          'PATH with the name zxing[.exe].'))
   parser.add_option('--stats_file', type='string', default='stats.txt',
                     help=('Path to the temporary stats file to be created and '
                           'used. Default: %default'))
@@ -77,14 +91,18 @@
   """
   options = _ParseArgs()
 
-  # Run barcode decoder on the test video to identify frame numbers.
-  path_to_decoder = os.path.join(SCRIPT_DIR, 'barcode_tools',
-                                 'barcode_decoder.py')
+  if options.barcode_decoder:
+    path_to_decoder = options.barcode_decoder
+  else:
+    path_to_decoder = os.path.join(SCRIPT_DIR, 'barcode_tools',
+                                   'barcode_decoder.py')
 
   # On Windows, sometimes the inherited stdin handle from the parent process
   # fails. Work around this by passing null to stdin to the subprocesses.
   null_filehandle = open(os.devnull, 'r')
 
+  # Run barcode decoder on the test video to identify frame numbers.
+  png_working_directory = tempfile.mkdtemp()
   cmd = [
     sys.executable,
     path_to_decoder,
@@ -92,10 +110,17 @@
     '--yuv_frame_width=%d' % options.yuv_frame_width,
     '--yuv_frame_height=%d' % options.yuv_frame_height,
     '--stats_file=%s' % options.stats_file,
+    '--png_working_dir=%s' % png_working_directory,
   ]
+  if options.zxing_path:
+    cmd.append('--zxing_path=%s' % options.zxing_path)
+  if options.ffmpeg_path:
+    cmd.append('--ffmpeg_path=%s' % options.ffmpeg_path)
   barcode_decoder = subprocess.Popen(cmd, stdin=null_filehandle,
                                      stdout=sys.stdout, stderr=sys.stderr)
   barcode_decoder.wait()
+
+  shutil.rmtree(png_working_directory)
   if barcode_decoder.returncode != 0:
     print 'Failed to run barcode decoder script.'
     return 1
diff --git a/transport.h b/transport.h
index f83e1e7..c44c5b2 100644
--- a/transport.h
+++ b/transport.h
@@ -20,8 +20,8 @@
 
 class Transport {
  public:
-  virtual bool SendRTP(const uint8_t* packet, size_t length) = 0;
-  virtual bool SendRTCP(const uint8_t* packet, size_t length) = 0;
+  virtual bool SendRtp(const uint8_t* packet, size_t length) = 0;
+  virtual bool SendRtcp(const uint8_t* packet, size_t length) = 0;
 
  protected:
   virtual ~Transport() {}
diff --git a/video/encoded_frame_callback_adapter.cc b/video/encoded_frame_callback_adapter.cc
new file mode 100644
index 0000000..f5eca7c
--- /dev/null
+++ b/video/encoded_frame_callback_adapter.cc
@@ -0,0 +1,39 @@
+/*
+ *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/video/encoded_frame_callback_adapter.h"
+
+#include "webrtc/modules/video_coding/main/source/encoded_frame.h"
+
+namespace webrtc {
+namespace internal {
+
+EncodedFrameCallbackAdapter::EncodedFrameCallbackAdapter(
+    EncodedFrameObserver* observer) : observer_(observer) {
+}
+
+EncodedFrameCallbackAdapter::~EncodedFrameCallbackAdapter() {}
+
+int32_t EncodedFrameCallbackAdapter::Encoded(
+    EncodedImage& encodedImage,
+    const CodecSpecificInfo* codecSpecificInfo,
+    const RTPFragmentationHeader* fragmentation) {
+  assert(observer_ != NULL);
+  FrameType frame_type =
+        VCMEncodedFrame::ConvertFrameType(encodedImage._frameType);
+  const EncodedFrame frame(encodedImage._buffer,
+                           encodedImage._length,
+                           frame_type);
+  observer_->EncodedFrameCallback(frame);
+  return 0;
+}
+
+}  // namespace internal
+}  // namespace webrtc
diff --git a/video/encoded_frame_callback_adapter.h b/video/encoded_frame_callback_adapter.h
new file mode 100644
index 0000000..d381479
--- /dev/null
+++ b/video/encoded_frame_callback_adapter.h
@@ -0,0 +1,36 @@
+/*
+ *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_VIDEO_ENCODED_FRAME_CALLBACK_ADAPTER_H_
+#define WEBRTC_VIDEO_ENCODED_FRAME_CALLBACK_ADAPTER_H_
+
+#include "webrtc/modules/video_coding/codecs/interface/video_codec_interface.h"
+#include "webrtc/frame_callback.h"
+
+namespace webrtc {
+namespace internal {
+
+class EncodedFrameCallbackAdapter : public EncodedImageCallback {
+ public:
+  explicit EncodedFrameCallbackAdapter(EncodedFrameObserver* observer);
+  virtual ~EncodedFrameCallbackAdapter();
+
+  virtual int32_t Encoded(EncodedImage& encodedImage,
+                          const CodecSpecificInfo* codecSpecificInfo,
+                          const RTPFragmentationHeader* fragmentation);
+
+ private:
+  EncodedFrameObserver* observer_;
+};
+
+}  // namespace internal
+}  // namespace webrtc
+
+#endif  // WEBRTC_VIDEO_ENCODED_FRAME_CALLBACK_ADAPTER_H_
diff --git a/video/full_stack.cc b/video/full_stack.cc
index b154df3..17e7686 100644
--- a/video/full_stack.cc
+++ b/video/full_stack.cc
@@ -149,7 +149,7 @@
     input_->PutFrame(video_frame, delta_capture_ms);
   }
 
-  virtual bool SendRTP(const uint8_t* packet, size_t length) OVERRIDE {
+  virtual bool SendRtp(const uint8_t* packet, size_t length) OVERRIDE {
     scoped_ptr<RtpHeaderParser> parser(RtpHeaderParser::Create());
     RTPHeader header;
     parser->Parse(packet, static_cast<int>(length), &header);
@@ -165,11 +165,11 @@
           Clock::GetRealTimeClock()->CurrentNtpInMilliseconds();
     }
 
-    return transport_->SendRTP(packet, length);
+    return transport_->SendRtp(packet, length);
   }
 
-  virtual bool SendRTCP(const uint8_t* packet, size_t length) OVERRIDE {
-    return transport_->SendRTCP(packet, length);
+  virtual bool SendRtcp(const uint8_t* packet, size_t length) OVERRIDE {
+    return transport_->SendRtcp(packet, length);
   }
 
   virtual void RenderFrame(const I420VideoFrame& video_frame,
@@ -402,7 +402,7 @@
   send_config.codec.startBitrate = params.bitrate;
   send_config.codec.maxBitrate = params.bitrate;
 
-  VideoSendStream* send_stream = call->CreateSendStream(send_config);
+  VideoSendStream* send_stream = call->CreateVideoSendStream(send_config);
   analyzer.input_ = send_stream->Input();
 
   scoped_ptr<test::FrameGeneratorCapturer> file_capturer(
@@ -422,21 +422,21 @@
   receive_config.renderer = &analyzer;
 
   VideoReceiveStream* receive_stream =
-      call->CreateReceiveStream(receive_config);
+      call->CreateVideoReceiveStream(receive_config);
 
-  receive_stream->StartReceive();
-  send_stream->StartSend();
+  receive_stream->StartReceiving();
+  send_stream->StartSending();
 
   file_capturer->Start();
 
   analyzer.Wait();
 
   file_capturer->Stop();
-  send_stream->StopSend();
-  receive_stream->StopReceive();
+  send_stream->StopSending();
+  receive_stream->StopReceiving();
 
-  call->DestroyReceiveStream(receive_stream);
-  call->DestroySendStream(send_stream);
+  call->DestroyVideoReceiveStream(receive_stream);
+  call->DestroyVideoSendStream(send_stream);
 
   transport.StopSending();
 }
diff --git a/video/rampup_tests.cc b/video/rampup_tests.cc
index c668458..b294c70 100644
--- a/video/rampup_tests.cc
+++ b/video/rampup_tests.cc
@@ -27,6 +27,7 @@
 #include "webrtc/test/fake_encoder.h"
 #include "webrtc/test/frame_generator_capturer.h"
 #include "webrtc/test/generate_ssrcs.h"
+#include "webrtc/video/transport_adapter.h"
 
 namespace webrtc {
 
@@ -43,7 +44,7 @@
       : critical_section_(CriticalSectionWrapper::CreateCriticalSection()),
         all_ssrcs_sent_(EventWrapper::Create()),
         rtp_parser_(RtpHeaderParser::Create()),
-        feedback_transport_(new TransportWrapper(feedback_transport)),
+        feedback_transport_(feedback_transport),
         receive_stats_(ReceiveStatistics::Create(clock)),
         clock_(clock),
         num_expected_ssrcs_(num_expected_ssrcs) {
@@ -53,7 +54,7 @@
     // be able to produce an RTCP with REMB.
     RtpRtcp::Configuration config;
     config.receive_statistics = receive_stats_.get();
-    config.outgoing_transport = feedback_transport_.get();
+    config.outgoing_transport = &feedback_transport_;
     rtp_rtcp_.reset(RtpRtcp::CreateRtpRtcp(config));
     rtp_rtcp_->SetREMBStatus(true);
     rtp_rtcp_->SetRTCPStatus(kRtcpNonCompound);
@@ -73,7 +74,7 @@
     rtp_rtcp_->Process();
   }
 
-  virtual bool SendRTP(const uint8_t* packet, size_t length) OVERRIDE {
+  virtual bool SendRtp(const uint8_t* packet, size_t length) OVERRIDE {
     CriticalSectionScoped lock(critical_section_.get());
     RTPHeader header;
     EXPECT_TRUE(rtp_parser_->Parse(packet, static_cast<int>(length), &header));
@@ -87,43 +88,20 @@
     return true;
   }
 
-  virtual bool SendRTCP(const uint8_t* packet, size_t length) OVERRIDE {
+  virtual bool SendRtcp(const uint8_t* packet, size_t length) OVERRIDE {
     return true;
   }
 
   EventTypeWrapper Wait() { return all_ssrcs_sent_->Wait(120 * 1000); }
 
  private:
-  class TransportWrapper : public webrtc::Transport {
-   public:
-    explicit TransportWrapper(newapi::Transport* new_transport)
-        : new_transport_(new_transport) {}
-
-    virtual int SendPacket(int channel, const void* data, int len) OVERRIDE {
-      return new_transport_->SendRTP(static_cast<const uint8_t*>(data), len)
-                 ? len
-                 : -1;
-    }
-
-    virtual int SendRTCPPacket(int channel,
-                               const void* data,
-                               int len) OVERRIDE {
-      return new_transport_->SendRTCP(static_cast<const uint8_t*>(data), len)
-                 ? len
-                 : -1;
-    }
-
-   private:
-    newapi::Transport* new_transport_;
-  };
-
   static const unsigned int kExpectedBitrateBps = 1200000;
 
   scoped_ptr<CriticalSectionWrapper> critical_section_;
   scoped_ptr<EventWrapper> all_ssrcs_sent_;
   scoped_ptr<RtpHeaderParser> rtp_parser_;
   scoped_ptr<RtpRtcp> rtp_rtcp_;
-  scoped_ptr<TransportWrapper> feedback_transport_;
+  internal::TransportAdapter feedback_transport_;
   scoped_ptr<ReceiveStatistics> receive_stats_;
   scoped_ptr<RemoteBitrateEstimator> remote_bitrate_estimator_;
   Clock* clock_;
@@ -152,19 +130,20 @@
   send_config.encoder = &encoder;
   send_config.internal_source = false;
   test::FakeEncoder::SetCodecSettings(&send_config.codec, 3);
+  send_config.codec.plType = 125;
   send_config.pacing = GetParam();
   send_config.rtp.extensions.push_back(
-      RtpExtension("toffset", kTOffsetExtensionId));
+      RtpExtension(RtpExtension::kTOffset, kTOffsetExtensionId));
 
   test::GenerateRandomSsrcs(&send_config, &reserved_ssrcs_);
 
-  VideoSendStream* send_stream = call->CreateSendStream(send_config);
+  VideoSendStream* send_stream = call->CreateVideoSendStream(send_config);
 
   VideoReceiveStream::Config receive_config;
   receive_config.rtp.ssrc = send_config.rtp.ssrcs[0];
   receive_config.rtp.nack.rtp_history_ms = send_config.rtp.nack.rtp_history_ms;
   VideoReceiveStream* receive_stream =
-      call->CreateReceiveStream(receive_config);
+      call->CreateVideoReceiveStream(receive_config);
 
   scoped_ptr<test::FrameGeneratorCapturer> frame_generator_capturer(
       test::FrameGeneratorCapturer::Create(send_stream->Input(),
@@ -173,18 +152,18 @@
                                            30,
                                            Clock::GetRealTimeClock()));
 
-  receive_stream->StartReceive();
-  send_stream->StartSend();
+  receive_stream->StartReceiving();
+  send_stream->StartSending();
   frame_generator_capturer->Start();
 
   EXPECT_EQ(kEventSignaled, stream_observer.Wait());
 
   frame_generator_capturer->Stop();
-  send_stream->StopSend();
-  receive_stream->StopReceive();
+  send_stream->StopSending();
+  receive_stream->StopReceiving();
 
-  call->DestroyReceiveStream(receive_stream);
-  call->DestroySendStream(send_stream);
+  call->DestroyVideoReceiveStream(receive_stream);
+  call->DestroyVideoSendStream(send_stream);
 }
 
 INSTANTIATE_TEST_CASE_P(RampUpTest, RampUpTest, ::testing::Bool());
diff --git a/video/transport_adapter.cc b/video/transport_adapter.cc
index 1d32504..7cc6a0a 100644
--- a/video/transport_adapter.cc
+++ b/video/transport_adapter.cc
@@ -19,7 +19,7 @@
 int TransportAdapter::SendPacket(int /*channel*/,
                                  const void* packet,
                                  int length) {
-  bool success = transport_->SendRTP(static_cast<const uint8_t*>(packet),
+  bool success = transport_->SendRtp(static_cast<const uint8_t*>(packet),
                                      static_cast<size_t>(length));
   return success ? length : -1;
 }
@@ -27,7 +27,7 @@
 int TransportAdapter::SendRTCPPacket(int /*channel*/,
                                      const void* packet,
                                      int length) {
-  bool success = transport_->SendRTCP(static_cast<const uint8_t*>(packet),
+  bool success = transport_->SendRtcp(static_cast<const uint8_t*>(packet),
                                       static_cast<size_t>(length));
   return success ? length : -1;
 }
diff --git a/video/video_receive_stream.cc b/video/video_receive_stream.cc
index a84c6d2..5157237 100644
--- a/video/video_receive_stream.cc
+++ b/video/video_receive_stream.cc
@@ -30,8 +30,12 @@
 
 VideoReceiveStream::VideoReceiveStream(webrtc::VideoEngine* video_engine,
                                        const VideoReceiveStream::Config& config,
-                                       newapi::Transport* transport)
-    : transport_adapter_(transport), config_(config), channel_(-1) {
+                                       newapi::Transport* transport,
+                                       webrtc::VoiceEngine* voice_engine)
+    : transport_adapter_(transport),
+      encoded_frame_proxy_(config.pre_decode_callback),
+      config_(config),
+      channel_(-1) {
   video_engine_base_ = ViEBase::GetInterface(video_engine);
   // TODO(mflodman): Use the other CreateChannel method.
   video_engine_base_->CreateChannel(channel_);
@@ -84,12 +88,21 @@
     }
   }
 
-  render_ = webrtc::ViERender::GetInterface(video_engine);
+  render_ = ViERender::GetInterface(video_engine);
   assert(render_ != NULL);
 
-  render_->AddRenderer(channel_, kVideoI420, this);
+  render_->AddRenderCallback(channel_, this);
+
+  if (voice_engine) {
+    video_engine_base_->SetVoiceEngine(voice_engine);
+    video_engine_base_->ConnectAudioChannel(channel_, config_.audio_channel_id);
+  }
 
   image_process_ = ViEImageProcess::GetInterface(video_engine);
+  if (config.pre_decode_callback) {
+    image_process_->RegisterPreDecodeImageCallback(channel_,
+                                                   &encoded_frame_proxy_);
+  }
   image_process_->RegisterPreRenderCallback(channel_,
                                             config_.pre_render_callback);
 
@@ -97,7 +110,8 @@
 }
 
 VideoReceiveStream::~VideoReceiveStream() {
-  image_process_->DeRegisterPreEncodeCallback(channel_);
+  image_process_->DeRegisterPreRenderCallback(channel_);
+  image_process_->DeRegisterPreDecodeCallback(channel_);
 
   render_->RemoveRenderer(channel_);
 
@@ -108,6 +122,7 @@
 
   network_->DeregisterSendTransport(channel_);
 
+  video_engine_base_->SetVoiceEngine(NULL);
   image_process_->Release();
   video_engine_base_->Release();
   external_codec_->Release();
@@ -117,22 +132,18 @@
   rtp_rtcp_->Release();
 }
 
-void VideoReceiveStream::StartReceive() {
-  if (render_->StartRender(channel_)) {
+void VideoReceiveStream::StartReceiving() {
+  if (render_->StartRender(channel_) != 0)
     abort();
-  }
-  if (video_engine_base_->StartReceive(channel_) != 0) {
+  if (video_engine_base_->StartReceive(channel_) != 0)
     abort();
-  }
 }
 
-void VideoReceiveStream::StopReceive() {
-  if (render_->StopRender(channel_)) {
+void VideoReceiveStream::StopReceiving() {
+  if (render_->StopRender(channel_) != 0)
     abort();
-  }
-  if (video_engine_base_->StopReceive(channel_) != 0) {
+  if (video_engine_base_->StopReceive(channel_) != 0)
     abort();
-  }
 }
 
 void VideoReceiveStream::GetCurrentReceiveCodec(VideoCodec* receive_codec) {
@@ -149,44 +160,14 @@
              channel_, packet, static_cast<int>(length)) == 0;
 }
 
-int VideoReceiveStream::FrameSizeChange(unsigned int width,
-                                        unsigned int height,
-                                        unsigned int /*number_of_streams*/) {
-  width_ = width;
-  height_ = height;
-  return 0;
-}
-
-int VideoReceiveStream::DeliverFrame(uint8_t* frame,
-                                     int buffer_size,
-                                     uint32_t timestamp,
-                                     int64_t render_time,
-                                     void* /*handle*/) {
-  if (config_.renderer == NULL) {
+int32_t VideoReceiveStream::RenderFrame(const uint32_t stream_id,
+                                        I420VideoFrame& video_frame) {
+  if (config_.renderer == NULL)
     return 0;
-  }
 
-  I420VideoFrame video_frame;
-  video_frame.CreateEmptyFrame(width_, height_, width_, height_, height_);
-  ConvertToI420(kI420,
-                frame,
-                0,
-                0,
-                width_,
-                height_,
-                buffer_size,
-                webrtc::kRotateNone,
-                &video_frame);
-  video_frame.set_timestamp(timestamp);
-  video_frame.set_render_time_ms(render_time);
-
-  config_.renderer->RenderFrame(video_frame,
-                                render_time - clock_->TimeInMilliseconds());
-
+  config_.renderer->RenderFrame(
+      video_frame, video_frame.render_time_ms() - clock_->TimeInMilliseconds());
   return 0;
 }
-
-bool VideoReceiveStream::IsTextureSupported() { return false; }
-
 }  // internal
 }  // webrtc
diff --git a/video/video_receive_stream.h b/video/video_receive_stream.h
index c2352f4..8a23a10 100644
--- a/video/video_receive_stream.h
+++ b/video/video_receive_stream.h
@@ -14,7 +14,9 @@
 #include <vector>
 
 #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
+#include "webrtc/modules/video_render/include/video_render_defines.h"
 #include "webrtc/system_wrappers/interface/clock.h"
+#include "webrtc/video/encoded_frame_callback_adapter.h"
 #include "webrtc/video/transport_adapter.h"
 #include "webrtc/video_engine/include/vie_render.h"
 #include "webrtc/video_receive_stream.h"
@@ -29,28 +31,26 @@
 class ViENetwork;
 class ViERender;
 class ViERTP_RTCP;
+class VoiceEngine;
 
 namespace internal {
 
 class VideoReceiveStream : public webrtc::VideoReceiveStream,
-                           public webrtc::ExternalRenderer {
+                           public VideoRenderCallback {
  public:
   VideoReceiveStream(webrtc::VideoEngine* video_engine,
                      const VideoReceiveStream::Config& config,
-                     newapi::Transport* transport);
+                     newapi::Transport* transport,
+                     webrtc::VoiceEngine* voice_engine);
   virtual ~VideoReceiveStream();
 
-  virtual void StartReceive() OVERRIDE;
-  virtual void StopReceive() OVERRIDE;
+  virtual void StartReceiving() OVERRIDE;
+  virtual void StopReceiving() OVERRIDE;
 
   virtual void GetCurrentReceiveCodec(VideoCodec* receive_codec) OVERRIDE;
 
-  virtual int FrameSizeChange(unsigned int width, unsigned int height,
-                              unsigned int /*number_of_streams*/) OVERRIDE;
-  virtual int DeliverFrame(uint8_t* frame, int buffer_size, uint32_t timestamp,
-                           int64_t render_time, void* /*handle*/) OVERRIDE;
-
-  virtual bool IsTextureSupported() OVERRIDE;
+  virtual int32_t RenderFrame(const uint32_t stream_id,
+                              I420VideoFrame& video_frame) OVERRIDE;
 
  public:
   virtual bool DeliverRtcp(const uint8_t* packet, size_t length);
@@ -58,6 +58,7 @@
 
  private:
   TransportAdapter transport_adapter_;
+  EncodedFrameCallbackAdapter encoded_frame_proxy_;
   VideoReceiveStream::Config config_;
   Clock* clock_;
 
@@ -70,10 +71,6 @@
   ViEImageProcess* image_process_;
 
   int channel_;
-
-  // TODO(pbos): Remove VideoReceiveStream can operate on I420 frames directly.
-  unsigned int height_;
-  unsigned int width_;
 };
 }  // internal
 }  // webrtc
diff --git a/video/video_send_stream.cc b/video/video_send_stream.cc
index 8814c35..d90c733 100644
--- a/video/video_send_stream.cc
+++ b/video/video_send_stream.cc
@@ -82,14 +82,11 @@
                                  bool overuse_detection,
                                  webrtc::VideoEngine* video_engine,
                                  const VideoSendStream::Config& config)
-    : transport_adapter_(transport), config_(config), external_codec_(NULL) {
-
-  if (config_.codec.numberOfSimulcastStreams > 0) {
-    assert(config_.rtp.ssrcs.size() == config_.codec.numberOfSimulcastStreams);
-  } else {
-    assert(config_.rtp.ssrcs.size() == 1);
-  }
-
+    : transport_adapter_(transport),
+      encoded_frame_proxy_(config.post_encode_callback),
+      codec_lock_(CriticalSectionWrapper::CreateCriticalSection()),
+      config_(config),
+      external_codec_(NULL) {
   video_engine_base_ = ViEBase::GetInterface(video_engine);
   video_engine_base_->CreateChannel(channel_);
   assert(channel_ != -1);
@@ -97,39 +94,18 @@
   rtp_rtcp_ = ViERTP_RTCP::GetInterface(video_engine);
   assert(rtp_rtcp_ != NULL);
 
-  if (config_.rtp.ssrcs.size() == 1) {
-    rtp_rtcp_->SetLocalSSRC(channel_, config_.rtp.ssrcs[0]);
-  } else {
-    for (size_t i = 0; i < config_.rtp.ssrcs.size(); ++i) {
-      rtp_rtcp_->SetLocalSSRC(channel_,
-                              config_.rtp.ssrcs[i],
-                              kViEStreamTypeNormal,
-                              static_cast<unsigned char>(i));
-    }
-  }
+  assert(config_.rtp.ssrcs.size() > 0);
+  if (config_.suspend_below_min_bitrate)
+    config_.pacing = true;
   rtp_rtcp_->SetTransmissionSmoothingStatus(channel_, config_.pacing);
-  if (!config_.rtp.rtx.ssrcs.empty()) {
-    assert(config_.rtp.rtx.ssrcs.size() == config_.rtp.ssrcs.size());
-    for (size_t i = 0; i < config_.rtp.rtx.ssrcs.size(); ++i) {
-      rtp_rtcp_->SetLocalSSRC(channel_,
-                              config_.rtp.rtx.ssrcs[i],
-                              kViEStreamTypeRtx,
-                              static_cast<unsigned char>(i));
-    }
-
-    if (config_.rtp.rtx.rtx_payload_type != 0) {
-      rtp_rtcp_->SetRtxSendPayloadType(channel_,
-                                       config_.rtp.rtx.rtx_payload_type);
-    }
-  }
 
   for (size_t i = 0; i < config_.rtp.extensions.size(); ++i) {
     const std::string& extension = config_.rtp.extensions[i].name;
     int id = config_.rtp.extensions[i].id;
-    if (extension == "toffset") {
+    if (extension == RtpExtension::kTOffset) {
       if (rtp_rtcp_->SetSendTimestampOffsetStatus(channel_, true, id) != 0)
         abort();
-    } else if (extension == "abs-send-time") {
+    } else if (extension == RtpExtension::kAbsSendTime) {
       if (rtp_rtcp_->SetSendAbsoluteSendTimeStatus(channel_, true, id) != 0)
         abort();
     } else {
@@ -186,9 +162,8 @@
   }
 
   codec_ = ViECodec::GetInterface(video_engine);
-  if (codec_->SetSendCodec(channel_, config_.codec) != 0) {
+  if (!SetCodec(config_.codec))
     abort();
-  }
 
   if (overuse_detection) {
     overuse_observer_.reset(
@@ -201,9 +176,13 @@
   image_process_ = ViEImageProcess::GetInterface(video_engine);
   image_process_->RegisterPreEncodeCallback(channel_,
                                             config_.pre_encode_callback);
+  if (config_.post_encode_callback) {
+    image_process_->RegisterPostEncodeImageCallback(channel_,
+                                                    &encoded_frame_proxy_);
+  }
 
-  if (config.auto_mute) {
-    codec_->EnableAutoMuting(channel_);
+  if (config.suspend_below_min_bitrate) {
+    codec_->SuspendBelowMinBitrate(channel_);
   }
 }
 
@@ -261,35 +240,63 @@
 
 VideoSendStreamInput* VideoSendStream::Input() { return this; }
 
-void VideoSendStream::StartSend() {
+void VideoSendStream::StartSending() {
   if (video_engine_base_->StartSend(channel_) != 0)
     abort();
   if (video_engine_base_->StartReceive(channel_) != 0)
     abort();
 }
 
-void VideoSendStream::StopSend() {
+void VideoSendStream::StopSending() {
   if (video_engine_base_->StopSend(channel_) != 0)
     abort();
   if (video_engine_base_->StopReceive(channel_) != 0)
     abort();
 }
 
-bool VideoSendStream::SetTargetBitrate(
-    int min_bitrate,
-    int max_bitrate,
-    const std::vector<SimulcastStream>& streams) {
-  return false;
+bool VideoSendStream::SetCodec(const VideoCodec& codec) {
+  assert(config_.rtp.ssrcs.size() >= codec.numberOfSimulcastStreams);
+
+  CriticalSectionScoped crit(codec_lock_.get());
+  if (codec_->SetSendCodec(channel_, codec) != 0)
+    return false;
+
+  for (size_t i = 0; i < config_.rtp.ssrcs.size(); ++i) {
+    rtp_rtcp_->SetLocalSSRC(channel_,
+                            config_.rtp.ssrcs[i],
+                            kViEStreamTypeNormal,
+                            static_cast<unsigned char>(i));
+  }
+
+  config_.codec = codec;
+  if (config_.rtp.rtx.ssrcs.empty())
+    return true;
+
+  // Set up RTX.
+  assert(config_.rtp.rtx.ssrcs.size() == config_.rtp.ssrcs.size());
+  for (size_t i = 0; i < config_.rtp.ssrcs.size(); ++i) {
+    rtp_rtcp_->SetLocalSSRC(channel_,
+                            config_.rtp.rtx.ssrcs[i],
+                            kViEStreamTypeRtx,
+                            static_cast<unsigned char>(i));
+  }
+
+  if (config_.rtp.rtx.rtx_payload_type != 0) {
+    rtp_rtcp_->SetRtxSendPayloadType(channel_,
+                                     config_.rtp.rtx.rtx_payload_type);
+  }
+
+  return true;
 }
 
-void VideoSendStream::GetSendCodec(VideoCodec* send_codec) {
-  *send_codec = config_.codec;
+VideoCodec VideoSendStream::GetCodec() {
+  CriticalSectionScoped crit(codec_lock_.get());
+  return config_.codec;
 }
 
 bool VideoSendStream::DeliverRtcp(const uint8_t* packet, size_t length) {
   return network_->ReceivedRTCPPacket(
              channel_, packet, static_cast<int>(length)) == 0;
 }
-
 }  // namespace internal
 }  // namespace webrtc
diff --git a/video/video_send_stream.h b/video/video_send_stream.h
index 304d825..0881d91 100644
--- a/video/video_send_stream.h
+++ b/video/video_send_stream.h
@@ -14,9 +14,11 @@
 #include <vector>
 
 #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
+#include "webrtc/video/encoded_frame_callback_adapter.h"
 #include "webrtc/video/transport_adapter.h"
 #include "webrtc/video_receive_stream.h"
 #include "webrtc/video_send_stream.h"
+#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
 
 namespace webrtc {
 
@@ -49,21 +51,20 @@
 
   virtual VideoSendStreamInput* Input() OVERRIDE;
 
-  virtual void StartSend() OVERRIDE;
+  virtual void StartSending() OVERRIDE;
 
-  virtual void StopSend() OVERRIDE;
+  virtual void StopSending() OVERRIDE;
 
-  virtual bool SetTargetBitrate(int min_bitrate, int max_bitrate,
-                                const std::vector<SimulcastStream>& streams)
-      OVERRIDE;
-
-  virtual void GetSendCodec(VideoCodec* send_codec) OVERRIDE;
+  virtual bool SetCodec(const VideoCodec& codec) OVERRIDE;
+  virtual VideoCodec GetCodec() OVERRIDE;
 
  public:
   bool DeliverRtcp(const uint8_t* packet, size_t length);
 
  private:
   TransportAdapter transport_adapter_;
+  EncodedFrameCallbackAdapter encoded_frame_proxy_;
+  scoped_ptr<CriticalSectionWrapper> codec_lock_;
   VideoSendStream::Config config_;
 
   ViEBase* video_engine_base_;
diff --git a/video/video_send_stream_tests.cc b/video/video_send_stream_tests.cc
index 358ecf3..9507bf0 100644
--- a/video/video_send_stream_tests.cc
+++ b/video/video_send_stream_tests.cc
@@ -25,31 +25,14 @@
 #include "webrtc/test/direct_transport.h"
 #include "webrtc/test/fake_encoder.h"
 #include "webrtc/test/frame_generator_capturer.h"
+#include "webrtc/test/generate_ssrcs.h"
 #include "webrtc/test/null_transport.h"
+#include "webrtc/test/rtp_rtcp_observer.h"
 #include "webrtc/video/transport_adapter.h"
 #include "webrtc/video_send_stream.h"
 
 namespace webrtc {
 
-class SendTransportObserver : public test::NullTransport {
- public:
-  explicit SendTransportObserver(unsigned long timeout_ms)
-      : rtp_header_parser_(RtpHeaderParser::Create()),
-        send_test_complete_(EventWrapper::Create()),
-        timeout_ms_(timeout_ms) {}
-
-  EventTypeWrapper Wait() { return send_test_complete_->Wait(timeout_ms_); }
-
-  virtual void Stop() {}
-
- protected:
-  scoped_ptr<RtpHeaderParser> rtp_header_parser_;
-  scoped_ptr<EventWrapper> send_test_complete_;
-
- private:
-  unsigned long timeout_ms_;
-};
-
 class VideoSendStreamTest : public ::testing::Test {
  public:
   VideoSendStreamTest() : fake_encoder_(Clock::GetRealTimeClock()) {}
@@ -57,75 +40,175 @@
  protected:
   void RunSendTest(Call* call,
                    const VideoSendStream::Config& config,
-                   SendTransportObserver* observer) {
-    VideoSendStream* send_stream = call->CreateSendStream(config);
+                   test::RtpRtcpObserver* observer) {
+    send_stream_ = call->CreateVideoSendStream(config);
     scoped_ptr<test::FrameGeneratorCapturer> frame_generator_capturer(
         test::FrameGeneratorCapturer::Create(
-            send_stream->Input(), 320, 240, 30, Clock::GetRealTimeClock()));
-    send_stream->StartSend();
+            send_stream_->Input(), 320, 240, 30, Clock::GetRealTimeClock()));
+    send_stream_->StartSending();
     frame_generator_capturer->Start();
 
     EXPECT_EQ(kEventSignaled, observer->Wait());
 
-    observer->Stop();
+    observer->StopSending();
     frame_generator_capturer->Stop();
-    send_stream->StopSend();
-    call->DestroySendStream(send_stream);
+    send_stream_->StopSending();
+    call->DestroyVideoSendStream(send_stream_);
   }
 
-  VideoSendStream::Config GetSendTestConfig(Call* call) {
+  VideoSendStream::Config GetSendTestConfig(Call* call,
+                                            size_t number_of_streams) {
+    assert(number_of_streams <= kNumSendSsrcs);
     VideoSendStream::Config config = call->GetDefaultSendConfig();
     config.encoder = &fake_encoder_;
     config.internal_source = false;
-    config.rtp.ssrcs.push_back(kSendSsrc);
-    test::FakeEncoder::SetCodecSettings(&config.codec, 1);
+    for (size_t i = 0; i < number_of_streams; ++i)
+      config.rtp.ssrcs.push_back(kSendSsrcs[i]);
+    config.pacing = true;
+    test::FakeEncoder::SetCodecSettings(&config.codec, number_of_streams);
+    config.codec.plType = kFakeSendPayloadType;
     return config;
   }
 
-  void TestNackRetransmission(uint32_t retransmit_ssrc);
+  void TestNackRetransmission(uint32_t retransmit_ssrc,
+                              uint8_t retransmit_payload_type,
+                              bool enable_pacing);
 
+  void SendsSetSsrcs(size_t num_ssrcs, bool send_single_ssrc_first);
+
+  enum { kNumSendSsrcs = 3 };
+  static const uint8_t kSendPayloadType;
+  static const uint8_t kSendRtxPayloadType;
+  static const uint8_t kFakeSendPayloadType;
   static const uint32_t kSendSsrc;
   static const uint32_t kSendRtxSsrc;
+  static const uint32_t kSendSsrcs[kNumSendSsrcs];
 
+  VideoSendStream* send_stream_;
   test::FakeEncoder fake_encoder_;
 };
 
-const uint32_t VideoSendStreamTest::kSendSsrc = 0xC0FFEE;
+const uint8_t VideoSendStreamTest::kSendPayloadType = 100;
+const uint8_t VideoSendStreamTest::kFakeSendPayloadType = 125;
+const uint8_t VideoSendStreamTest::kSendRtxPayloadType = 98;
 const uint32_t VideoSendStreamTest::kSendRtxSsrc = 0xBADCAFE;
+const uint32_t VideoSendStreamTest::kSendSsrcs[kNumSendSsrcs] = { 0xC0FFED,
+    0xC0FFEE, 0xC0FFEF };
+const uint32_t VideoSendStreamTest::kSendSsrc =
+    VideoSendStreamTest::kSendSsrcs[0];
 
-TEST_F(VideoSendStreamTest, SendsSetSsrc) {
-  class SendSsrcObserver : public SendTransportObserver {
+void VideoSendStreamTest::SendsSetSsrcs(size_t num_ssrcs,
+                                        bool send_single_ssrc_first) {
+  class SendSsrcObserver : public test::RtpRtcpObserver {
    public:
-    SendSsrcObserver() : SendTransportObserver(30 * 1000) {}
-
-    virtual bool SendRTP(const uint8_t* packet, size_t length) OVERRIDE {
-      RTPHeader header;
-      EXPECT_TRUE(
-          rtp_header_parser_->Parse(packet, static_cast<int>(length), &header));
-
-      if (header.ssrc == kSendSsrc)
-        send_test_complete_->Set();
-
-      return true;
+    SendSsrcObserver(const uint32_t* ssrcs,
+                     size_t num_ssrcs,
+                     bool send_single_ssrc_first)
+        : RtpRtcpObserver(30 * 1000),
+          ssrcs_to_observe_(num_ssrcs),
+          expect_single_ssrc_(send_single_ssrc_first) {
+      for (size_t i = 0; i < num_ssrcs; ++i)
+        valid_ssrcs_[ssrcs[i]] = true;
     }
-  } observer;
 
-  Call::Config call_config(&observer);
+    virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE {
+      RTPHeader header;
+      EXPECT_TRUE(parser_->Parse(packet, static_cast<int>(length), &header));
+
+      // TODO(pbos): Reenable this part of the test when #1695 is resolved and
+      //             all SSRCs are allocated on startup. This test was observed
+      //             to fail on TSan as the codec gets set before the SSRCs are
+      //             set up and some frames are sent on a random-generated SSRC
+      //             before the correct SSRC gets set.
+      //EXPECT_TRUE(valid_ssrcs_[header.ssrc])
+      //    << "Received unknown SSRC: " << header.ssrc;
+      //
+      //if (!valid_ssrcs_[header.ssrc])
+      //  observation_complete_->Set();
+
+      if (!is_observed_[header.ssrc]) {
+        is_observed_[header.ssrc] = true;
+        --ssrcs_to_observe_;
+        if (expect_single_ssrc_) {
+          expect_single_ssrc_ = false;
+          observation_complete_->Set();
+        }
+      }
+
+      if (ssrcs_to_observe_ == 0)
+        observation_complete_->Set();
+
+      return SEND_PACKET;
+    }
+
+   private:
+    std::map<uint32_t, bool> valid_ssrcs_;
+    std::map<uint32_t, bool> is_observed_;
+    size_t ssrcs_to_observe_;
+    bool expect_single_ssrc_;
+  } observer(kSendSsrcs, num_ssrcs, send_single_ssrc_first);
+
+  Call::Config call_config(observer.SendTransport());
   scoped_ptr<Call> call(Call::Create(call_config));
 
-  VideoSendStream::Config send_config = GetSendTestConfig(call.get());
-  send_config.rtp.max_packet_size = 128;
+  VideoSendStream::Config send_config =
+      GetSendTestConfig(call.get(), num_ssrcs);
 
-  RunSendTest(call.get(), send_config, &observer);
+  if (num_ssrcs > 1) {
+    // Set low simulcast bitrates to not have to wait for bandwidth ramp-up.
+    for (size_t i = 0; i < num_ssrcs; ++i) {
+      send_config.codec.simulcastStream[i].minBitrate = 10;
+      send_config.codec.simulcastStream[i].targetBitrate = 10;
+      send_config.codec.simulcastStream[i].maxBitrate = 10;
+    }
+  }
+
+  if (send_single_ssrc_first)
+    send_config.codec.numberOfSimulcastStreams = 1;
+
+  send_stream_ = call->CreateVideoSendStream(send_config);
+  scoped_ptr<test::FrameGeneratorCapturer> frame_generator_capturer(
+      test::FrameGeneratorCapturer::Create(
+          send_stream_->Input(), 320, 240, 30, Clock::GetRealTimeClock()));
+  send_stream_->StartSending();
+  frame_generator_capturer->Start();
+
+  EXPECT_EQ(kEventSignaled, observer.Wait())
+      << "Timed out while waiting for "
+      << (send_single_ssrc_first ? "first SSRC." : "SSRCs.");
+
+  if (send_single_ssrc_first) {
+    // Set full simulcast and continue with the rest of the SSRCs.
+    send_config.codec.numberOfSimulcastStreams =
+        static_cast<unsigned char>(num_ssrcs);
+    send_stream_->SetCodec(send_config.codec);
+    EXPECT_EQ(kEventSignaled, observer.Wait())
+        << "Timed out while waiting on additional SSRCs.";
+  }
+
+  observer.StopSending();
+  frame_generator_capturer->Stop();
+  send_stream_->StopSending();
+  call->DestroyVideoSendStream(send_stream_);
+}
+
+TEST_F(VideoSendStreamTest, SendsSetSsrc) { SendsSetSsrcs(1, false); }
+
+TEST_F(VideoSendStreamTest, SendsSetSimulcastSsrcs) {
+  SendsSetSsrcs(kNumSendSsrcs, false);
+}
+
+TEST_F(VideoSendStreamTest, CanSwitchToUseAllSsrcs) {
+  SendsSetSsrcs(kNumSendSsrcs, true);
 }
 
 TEST_F(VideoSendStreamTest, SupportsCName) {
   static std::string kCName = "PjQatC14dGfbVwGPUOA9IH7RlsFDbWl4AhXEiDsBizo=";
-  class CNameObserver : public SendTransportObserver {
+  class CNameObserver : public test::RtpRtcpObserver {
    public:
-    CNameObserver() : SendTransportObserver(30 * 1000) {}
+    CNameObserver() : RtpRtcpObserver(30 * 1000) {}
 
-    virtual bool SendRTCP(const uint8_t* packet, size_t length) OVERRIDE {
+    virtual Action OnSendRtcp(const uint8_t* packet, size_t length) OVERRIDE {
       RTCPUtility::RTCPParserV2 parser(packet, length, true);
       EXPECT_TRUE(parser.IsValid());
 
@@ -133,20 +216,20 @@
       while (packet_type != RTCPUtility::kRtcpNotValidCode) {
         if (packet_type == RTCPUtility::kRtcpSdesChunkCode) {
           EXPECT_EQ(parser.Packet().CName.CName, kCName);
-          send_test_complete_->Set();
+          observation_complete_->Set();
         }
 
         packet_type = parser.Iterate();
       }
 
-      return true;
+      return SEND_PACKET;
     }
   } observer;
 
-  Call::Config call_config(&observer);
+  Call::Config call_config(observer.SendTransport());
   scoped_ptr<Call> call(Call::Create(call_config));
 
-  VideoSendStream::Config send_config = GetSendTestConfig(call.get());
+  VideoSendStream::Config send_config = GetSendTestConfig(call.get(), 1);
   send_config.rtp.c_name = kCName;
 
   RunSendTest(call.get(), send_config, &observer);
@@ -154,31 +237,31 @@
 
 TEST_F(VideoSendStreamTest, SupportsAbsoluteSendTime) {
   static const uint8_t kAbsSendTimeExtensionId = 13;
-  class AbsoluteSendTimeObserver : public SendTransportObserver {
+  class AbsoluteSendTimeObserver : public test::RtpRtcpObserver {
    public:
-    AbsoluteSendTimeObserver() : SendTransportObserver(30 * 1000) {
-      EXPECT_TRUE(rtp_header_parser_->RegisterRtpHeaderExtension(
+    AbsoluteSendTimeObserver() : RtpRtcpObserver(30 * 1000) {
+      EXPECT_TRUE(parser_->RegisterRtpHeaderExtension(
           kRtpExtensionAbsoluteSendTime, kAbsSendTimeExtensionId));
     }
 
-    virtual bool SendRTP(const uint8_t* packet, size_t length) OVERRIDE {
+    virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE {
       RTPHeader header;
       EXPECT_TRUE(
-          rtp_header_parser_->Parse(packet, static_cast<int>(length), &header));
+          parser_->Parse(packet, static_cast<int>(length), &header));
 
       if (header.extension.absoluteSendTime > 0)
-        send_test_complete_->Set();
+        observation_complete_->Set();
 
-      return true;
+      return SEND_PACKET;
     }
   } observer;
 
-  Call::Config call_config(&observer);
+  Call::Config call_config(observer.SendTransport());
   scoped_ptr<Call> call(Call::Create(call_config));
 
-  VideoSendStream::Config send_config = GetSendTestConfig(call.get());
+  VideoSendStream::Config send_config = GetSendTestConfig(call.get(), 1);
   send_config.rtp.extensions.push_back(
-      RtpExtension("abs-send-time", kAbsSendTimeExtensionId));
+      RtpExtension(RtpExtension::kAbsSendTime, kAbsSendTimeExtensionId));
 
   RunSendTest(call.get(), send_config, &observer);
 }
@@ -199,32 +282,32 @@
     }
   } encoder(Clock::GetRealTimeClock());
 
-  class TransmissionTimeOffsetObserver : public SendTransportObserver {
+  class TransmissionTimeOffsetObserver : public test::RtpRtcpObserver {
    public:
-    TransmissionTimeOffsetObserver() : SendTransportObserver(30 * 1000) {
-      EXPECT_TRUE(rtp_header_parser_->RegisterRtpHeaderExtension(
+    TransmissionTimeOffsetObserver() : RtpRtcpObserver(30 * 1000) {
+      EXPECT_TRUE(parser_->RegisterRtpHeaderExtension(
           kRtpExtensionTransmissionTimeOffset, kTOffsetExtensionId));
     }
 
-    virtual bool SendRTP(const uint8_t* packet, size_t length) OVERRIDE {
+    virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE {
       RTPHeader header;
       EXPECT_TRUE(
-          rtp_header_parser_->Parse(packet, static_cast<int>(length), &header));
+          parser_->Parse(packet, static_cast<int>(length), &header));
 
       EXPECT_GT(header.extension.transmissionTimeOffset, 0);
-      send_test_complete_->Set();
+      observation_complete_->Set();
 
-      return true;
+      return SEND_PACKET;
     }
   } observer;
 
-  Call::Config call_config(&observer);
+  Call::Config call_config(observer.SendTransport());
   scoped_ptr<Call> call(Call::Create(call_config));
 
-  VideoSendStream::Config send_config = GetSendTestConfig(call.get());
+  VideoSendStream::Config send_config = GetSendTestConfig(call.get(), 1);
   send_config.encoder = &encoder;
   send_config.rtp.extensions.push_back(
-      RtpExtension("toffset", kTOffsetExtensionId));
+      RtpExtension(RtpExtension::kTOffset, kTOffsetExtensionId));
 
   RunSendTest(call.get(), send_config, &observer);
 }
@@ -288,23 +371,19 @@
 TEST_F(VideoSendStreamTest, SupportsFec) {
   static const int kRedPayloadType = 118;
   static const int kUlpfecPayloadType = 119;
-  class FecObserver : public SendTransportObserver {
+  class FecObserver : public test::RtpRtcpObserver {
    public:
     FecObserver()
-        : SendTransportObserver(30 * 1000),
-          transport_adapter_(&transport_),
+        : RtpRtcpObserver(30 * 1000),
+          transport_adapter_(SendTransport()),
           send_count_(0),
           received_media_(false),
           received_fec_(false) {}
 
-    void SetReceiver(PacketReceiver* receiver) {
-      transport_.SetReceiver(receiver);
-    }
-
-    virtual bool SendRTP(const uint8_t* packet, size_t length) OVERRIDE {
+    virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE {
       RTPHeader header;
       EXPECT_TRUE(
-          rtp_header_parser_->Parse(packet, static_cast<int>(length), &header));
+          parser_->Parse(packet, static_cast<int>(length), &header));
 
       // Send lossy receive reports to trigger FEC enabling.
       if (send_count_++ % 2 != 0) {
@@ -334,51 +413,49 @@
       }
 
       if (received_media_ && received_fec_)
-        send_test_complete_->Set();
+        observation_complete_->Set();
 
-      return true;
+      return SEND_PACKET;
     }
 
-    virtual void Stop() OVERRIDE { transport_.StopSending(); }
-
    private:
     internal::TransportAdapter transport_adapter_;
-    test::DirectTransport transport_;
     int send_count_;
     bool received_media_;
     bool received_fec_;
   } observer;
 
-  Call::Config call_config(&observer);
+  Call::Config call_config(observer.SendTransport());
   scoped_ptr<Call> call(Call::Create(call_config));
 
-  observer.SetReceiver(call->Receiver());
+  observer.SetReceivers(call->Receiver(), NULL);
 
-  VideoSendStream::Config send_config = GetSendTestConfig(call.get());
+  VideoSendStream::Config send_config = GetSendTestConfig(call.get(), 1);
   send_config.rtp.fec.red_payload_type = kRedPayloadType;
   send_config.rtp.fec.ulpfec_payload_type = kUlpfecPayloadType;
 
   RunSendTest(call.get(), send_config, &observer);
 }
 
-void VideoSendStreamTest::TestNackRetransmission(uint32_t retransmit_ssrc) {
-  class NackObserver : public SendTransportObserver {
+void VideoSendStreamTest::TestNackRetransmission(
+    uint32_t retransmit_ssrc,
+    uint8_t retransmit_payload_type,
+    bool enable_pacing) {
+  class NackObserver : public test::RtpRtcpObserver {
    public:
-    explicit NackObserver(uint32_t retransmit_ssrc)
-        : SendTransportObserver(30 * 1000),
-          transport_adapter_(&transport_),
+    explicit NackObserver(uint32_t retransmit_ssrc,
+                          uint8_t retransmit_payload_type)
+        : RtpRtcpObserver(30 * 1000),
+          transport_adapter_(SendTransport()),
           send_count_(0),
           retransmit_ssrc_(retransmit_ssrc),
+          retransmit_payload_type_(retransmit_payload_type),
           nacked_sequence_number_(0) {}
 
-    void SetReceiver(PacketReceiver* receiver) {
-      transport_.SetReceiver(receiver);
-    }
-
-    virtual bool SendRTP(const uint8_t* packet, size_t length) OVERRIDE {
+    virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE {
       RTPHeader header;
       EXPECT_TRUE(
-          rtp_header_parser_->Parse(packet, static_cast<int>(length), &header));
+          parser_->Parse(packet, static_cast<int>(length), &header));
 
       // Nack second packet after receiving the third one.
       if (++send_count_ == 3) {
@@ -408,28 +485,29 @@
 
       if (sequence_number == nacked_sequence_number_) {
         EXPECT_EQ(retransmit_ssrc_, header.ssrc);
-        send_test_complete_->Set();
+        EXPECT_EQ(retransmit_payload_type_, header.payloadType);
+        observation_complete_->Set();
       }
 
-      return true;
+      return SEND_PACKET;
     }
 
-    virtual void Stop() OVERRIDE { transport_.StopSending(); }
-
    private:
     internal::TransportAdapter transport_adapter_;
-    test::DirectTransport transport_;
     int send_count_;
     uint32_t retransmit_ssrc_;
+    uint8_t retransmit_payload_type_;
     uint16_t nacked_sequence_number_;
-  } observer(retransmit_ssrc);
+  } observer(retransmit_ssrc, retransmit_payload_type);
 
-  Call::Config call_config(&observer);
+  Call::Config call_config(observer.SendTransport());
   scoped_ptr<Call> call(Call::Create(call_config));
-  observer.SetReceiver(call->Receiver());
+  observer.SetReceivers(call->Receiver(), NULL);
 
-  VideoSendStream::Config send_config = GetSendTestConfig(call.get());
+  VideoSendStream::Config send_config = GetSendTestConfig(call.get(), 1);
   send_config.rtp.nack.rtp_history_ms = 1000;
+  send_config.rtp.rtx.rtx_payload_type = retransmit_payload_type;
+  send_config.pacing = enable_pacing;
   if (retransmit_ssrc != kSendSsrc)
     send_config.rtp.rtx.ssrcs.push_back(retransmit_ssrc);
 
@@ -438,24 +516,29 @@
 
 TEST_F(VideoSendStreamTest, RetransmitsNack) {
   // Normal NACKs should use the send SSRC.
-  TestNackRetransmission(kSendSsrc);
+  TestNackRetransmission(kSendSsrc, kFakeSendPayloadType, false);
 }
 
 TEST_F(VideoSendStreamTest, RetransmitsNackOverRtx) {
   // NACKs over RTX should use a separate SSRC.
-  TestNackRetransmission(kSendRtxSsrc);
+  TestNackRetransmission(kSendRtxSsrc, kSendRtxPayloadType, false);
+}
+
+TEST_F(VideoSendStreamTest, RetransmitsNackOverRtxWithPacing) {
+  // NACKs over RTX should use a separate SSRC.
+  TestNackRetransmission(kSendRtxSsrc, kSendRtxPayloadType, true);
 }
 
 TEST_F(VideoSendStreamTest, MaxPacketSize) {
-  class PacketSizeObserver : public SendTransportObserver {
+  class PacketSizeObserver : public test::RtpRtcpObserver {
    public:
-    PacketSizeObserver(size_t max_length) : SendTransportObserver(30 * 1000),
+    PacketSizeObserver(size_t max_length) : RtpRtcpObserver(30 * 1000),
       max_length_(max_length), accumulated_size_(0) {}
 
-    virtual bool SendRTP(const uint8_t* packet, size_t length) OVERRIDE {
+    virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE {
       RTPHeader header;
       EXPECT_TRUE(
-          rtp_header_parser_->Parse(packet, static_cast<int>(length), &header));
+          parser_->Parse(packet, static_cast<int>(length), &header));
 
       EXPECT_LE(length, max_length_);
 
@@ -466,12 +549,12 @@
         if (accumulated_size_ + length > max_length_) {
           // The packet was fragmented, total size was larger than max size,
           // but size of individual fragments were within size limit => pass!
-          send_test_complete_->Set();
+          observation_complete_->Set();
         }
         accumulated_size_ = 0; // Last fragment, reset packet size
       }
 
-      return true;
+      return SEND_PACKET;
     }
 
    private:
@@ -482,36 +565,100 @@
   static const uint32_t kMaxPacketSize = 128;
 
   PacketSizeObserver observer(kMaxPacketSize);
-  Call::Config call_config(&observer);
+  Call::Config call_config(observer.SendTransport());
   scoped_ptr<Call> call(Call::Create(call_config));
 
-  VideoSendStream::Config send_config = GetSendTestConfig(call.get());
+  VideoSendStream::Config send_config = GetSendTestConfig(call.get(), 1);
   send_config.rtp.max_packet_size = kMaxPacketSize;
 
   RunSendTest(call.get(), send_config, &observer);
 }
 
+TEST_F(VideoSendStreamTest, CanChangeSendCodec) {
+  static const uint8_t kFirstPayloadType = 121;
+  static const uint8_t kSecondPayloadType = 122;
+
+  class CodecChangeObserver : public test::RtpRtcpObserver {
+   public:
+    CodecChangeObserver(VideoSendStream** send_stream_ptr)
+        : RtpRtcpObserver(30 * 1000),
+          received_first_payload_(EventWrapper::Create()),
+          send_stream_ptr_(send_stream_ptr) {}
+
+    virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE {
+      RTPHeader header;
+      EXPECT_TRUE(parser_->Parse(packet, static_cast<int>(length), &header));
+
+      if (header.payloadType == kFirstPayloadType) {
+        received_first_payload_->Set();
+      } else if (header.payloadType == kSecondPayloadType) {
+        observation_complete_->Set();
+      }
+
+      return SEND_PACKET;
+    }
+
+    virtual EventTypeWrapper Wait() OVERRIDE {
+      EXPECT_EQ(kEventSignaled, received_first_payload_->Wait(30 * 1000))
+          << "Timed out while waiting for first payload.";
+
+      EXPECT_TRUE((*send_stream_ptr_)->SetCodec(second_codec_));
+
+      EXPECT_EQ(kEventSignaled, RtpRtcpObserver::Wait())
+          << "Timed out while waiting for second payload type.";
+
+      // Return OK regardless, prevents double error reporting.
+      return kEventSignaled;
+    }
+
+    void SetSecondCodec(const VideoCodec& codec) {
+      second_codec_ = codec;
+    }
+
+   private:
+    scoped_ptr<EventWrapper> received_first_payload_;
+    VideoSendStream** send_stream_ptr_;
+    VideoCodec second_codec_;
+  } observer(&send_stream_);
+
+  Call::Config call_config(observer.SendTransport());
+  scoped_ptr<Call> call(Call::Create(call_config));
+
+  std::vector<VideoCodec> codecs = call->GetVideoCodecs();
+  ASSERT_GE(codecs.size(), 2u)
+      << "Test needs at least 2 separate codecs to work.";
+  codecs[0].plType = kFirstPayloadType;
+  codecs[1].plType = kSecondPayloadType;
+  observer.SetSecondCodec(codecs[1]);
+
+  VideoSendStream::Config send_config = GetSendTestConfig(call.get(), 1);
+  send_config.codec = codecs[0];
+  send_config.encoder = NULL;
+
+  RunSendTest(call.get(), send_config, &observer);
+}
+
 // The test will go through a number of phases.
 // 1. Start sending packets.
 // 2. As soon as the RTP stream has been detected, signal a low REMB value to
-//    activate the auto muter.
-// 3. Wait until |kMuteTimeFrames| have been captured without seeing any RTP
+//    suspend the stream.
+// 3. Wait until |kSuspendTimeFrames| have been captured without seeing any RTP
 //    packets.
-// 4. Signal a high REMB and the wait for the RTP stream to start again.
+// 4. Signal a high REMB and then wait for the RTP stream to start again.
 //    When the stream is detected again, the test ends.
-TEST_F(VideoSendStreamTest, AutoMute) {
-  static const int kMuteTimeFrames = 60;  // Mute for 2 seconds @ 30 fps.
+TEST_F(VideoSendStreamTest, SuspendBelowMinBitrate) {
+  static const int kSuspendTimeFrames = 60;  // Suspend for 2 seconds @ 30 fps.
 
-  class RembObserver : public SendTransportObserver, public I420FrameCallback {
+  class RembObserver : public test::RtpRtcpObserver, public I420FrameCallback {
    public:
     RembObserver()
-        : SendTransportObserver(30 * 1000),  // Timeout after 30 seconds.
+        : RtpRtcpObserver(30 * 1000),  // Timeout after 30 seconds.
           transport_adapter_(&transport_),
           clock_(Clock::GetRealTimeClock()),
-          test_state_(kBeforeMute),
+          test_state_(kBeforeSuspend),
           rtp_count_(0),
           last_sequence_number_(0),
-          mute_frame_count_(0),
+          suspended_frame_count_(0),
           low_remb_bps_(0),
           high_remb_bps_(0),
           crit_sect_(CriticalSectionWrapper::CreateCriticalSection()) {}
@@ -520,39 +667,50 @@
       transport_.SetReceiver(receiver);
     }
 
-    virtual bool SendRTCP(const uint8_t* packet, size_t length) OVERRIDE {
+    virtual Action OnSendRtcp(const uint8_t* packet, size_t length) OVERRIDE {
       // Receive statistics reporting having lost 0% of the packets.
       // This is needed for the send-side bitrate controller to work properly.
       CriticalSectionScoped lock(crit_sect_.get());
       SendRtcpFeedback(0);  // REMB is only sent if value is > 0.
-      return true;
+      return SEND_PACKET;
     }
 
-    virtual bool SendRTP(const uint8_t* packet, size_t length) OVERRIDE {
+    virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE {
       CriticalSectionScoped lock(crit_sect_.get());
       ++rtp_count_;
       RTPHeader header;
-      EXPECT_TRUE(
-          rtp_header_parser_->Parse(packet, static_cast<int>(length), &header));
+      EXPECT_TRUE(parser_->Parse(packet, static_cast<int>(length), &header));
       last_sequence_number_ = header.sequenceNumber;
 
-      if (test_state_ == kBeforeMute) {
-        // The stream has started. Try to mute it.
+      if (test_state_ == kBeforeSuspend) {
+        // The stream has started. Try to suspend it.
         SendRtcpFeedback(low_remb_bps_);
-        test_state_ = kDuringMute;
-      } else if (test_state_ == kDuringMute) {
-        mute_frame_count_ = 0;
+        test_state_ = kDuringSuspend;
+      } else if (test_state_ == kDuringSuspend) {
+        if (header.paddingLength == 0) {
+          // Received non-padding packet during suspension period. Reset the
+          // counter.
+          // TODO(hlundin): We should probably make this test more advanced in
+          // the future, so that it verifies that the bitrate can go below the
+          // min_bitrate. This requires that the fake encoder sees the
+          // min_bitrate, and never goes below it. See WebRTC Issue 2655.
+          suspended_frame_count_ = 0;
+        }
       } else if (test_state_ == kWaitingForPacket) {
-        send_test_complete_->Set();
+        if (header.paddingLength == 0) {
+          // Non-padding packet observed. Test is complete.
+          observation_complete_->Set();
+        }
       }
 
-      return true;
+      return SEND_PACKET;
     }
 
     // This method implements the I420FrameCallback.
     void FrameCallback(I420VideoFrame* video_frame) OVERRIDE {
       CriticalSectionScoped lock(crit_sect_.get());
-      if (test_state_ == kDuringMute && ++mute_frame_count_ > kMuteTimeFrames) {
+      if (test_state_ == kDuringSuspend &&
+          ++suspended_frame_count_ > kSuspendTimeFrames) {
         SendRtcpFeedback(high_remb_bps_);
         test_state_ = kWaitingForPacket;
       }
@@ -562,14 +720,14 @@
 
     void set_high_remb_bps(int value) { high_remb_bps_ = value; }
 
-    virtual void Stop() OVERRIDE { transport_.StopSending(); }
+    virtual void Stop() { transport_.StopSending(); }
 
    private:
     enum TestState {
-      kBeforeMute,
-      kDuringMute,
+      kBeforeSuspend,
+      kDuringSuspend,
       kWaitingForPacket,
-      kAfterMute
+      kAfterSuspend
     };
 
     virtual void SendRtcpFeedback(int remb_value) {
@@ -594,20 +752,20 @@
     TestState test_state_;
     int rtp_count_;
     int last_sequence_number_;
-    int mute_frame_count_;
+    int suspended_frame_count_;
     int low_remb_bps_;
     int high_remb_bps_;
     scoped_ptr<CriticalSectionWrapper> crit_sect_;
   } observer;
 
-  Call::Config call_config(&observer);
+  Call::Config call_config(observer.SendTransport());
   scoped_ptr<Call> call(Call::Create(call_config));
   observer.SetReceiver(call->Receiver());
 
-  VideoSendStream::Config send_config = GetSendTestConfig(call.get());
+  VideoSendStream::Config send_config = GetSendTestConfig(call.get(), 1);
   send_config.rtp.nack.rtp_history_ms = 1000;
   send_config.pre_encode_callback = &observer;
-  send_config.auto_mute = true;
+  send_config.suspend_below_min_bitrate = true;
   unsigned int min_bitrate_bps =
       send_config.codec.simulcastStream[0].minBitrate * 1000;
   observer.set_low_remb_bps(min_bitrate_bps - 10000);
@@ -619,4 +777,78 @@
   RunSendTest(call.get(), send_config, &observer);
 }
 
+TEST_F(VideoSendStreamTest, NoPaddingWhenVideoIsMuted) {
+  class PacketObserver : public test::RtpRtcpObserver {
+   public:
+    PacketObserver()
+        : RtpRtcpObserver(30 * 1000),  // Timeout after 30 seconds.
+          clock_(Clock::GetRealTimeClock()),
+          last_packet_time_ms_(-1),
+          transport_adapter_(ReceiveTransport()),
+          capturer_(NULL),
+          crit_sect_(CriticalSectionWrapper::CreateCriticalSection()) {}
+
+    void SetCapturer(test::FrameGeneratorCapturer* capturer) {
+      capturer_ = capturer;
+    }
+
+    virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE {
+      CriticalSectionScoped lock(crit_sect_.get());
+      last_packet_time_ms_ = clock_->TimeInMilliseconds();
+      capturer_->Stop();
+      return SEND_PACKET;
+    }
+
+    virtual Action OnSendRtcp(const uint8_t* packet, size_t length) OVERRIDE {
+      CriticalSectionScoped lock(crit_sect_.get());
+      const int kVideoMutedThresholdMs = 10000;
+      if (last_packet_time_ms_ > 0 && clock_->TimeInMilliseconds() -
+          last_packet_time_ms_ > kVideoMutedThresholdMs)
+        observation_complete_->Set();
+      // Receive statistics reporting having lost 50% of the packets.
+      FakeReceiveStatistics receive_stats(kSendSsrcs[0], 1, 1, 0);
+      RTCPSender rtcp_sender(
+          0, false, Clock::GetRealTimeClock(), &receive_stats);
+      EXPECT_EQ(0, rtcp_sender.RegisterSendTransport(&transport_adapter_));
+
+      rtcp_sender.SetRTCPStatus(kRtcpNonCompound);
+      rtcp_sender.SetRemoteSSRC(kSendSsrcs[0]);
+
+      RTCPSender::FeedbackState feedback_state;
+
+      EXPECT_EQ(0, rtcp_sender.SendRTCP(feedback_state, kRtcpRr));
+      return SEND_PACKET;
+    }
+
+   private:
+    Clock* clock_;
+    int64_t last_packet_time_ms_;
+    internal::TransportAdapter transport_adapter_;
+    test::FrameGeneratorCapturer* capturer_;
+    scoped_ptr<CriticalSectionWrapper> crit_sect_;
+  } observer;
+
+  Call::Config call_config(observer.SendTransport());
+  scoped_ptr<Call> call(Call::Create(call_config));
+  observer.SetReceivers(call->Receiver(), call->Receiver());
+
+  VideoSendStream::Config send_config = GetSendTestConfig(call.get(), 3);
+
+  send_stream_ = call->CreateVideoSendStream(send_config);
+  scoped_ptr<test::FrameGeneratorCapturer> frame_generator_capturer(
+      test::FrameGeneratorCapturer::Create(
+          send_stream_->Input(), 320, 240, 30, Clock::GetRealTimeClock()));
+  observer.SetCapturer(frame_generator_capturer.get());
+  send_stream_->StartSending();
+  frame_generator_capturer->Start();
+
+  EXPECT_EQ(kEventSignaled, observer.Wait())
+      << "Timed out while waiting for RTP packets to stop being sent.";
+
+  observer.StopSending();
+  frame_generator_capturer->Stop();
+  send_stream_->StopSending();
+  call->DestroyVideoSendStream(send_stream_);
+}
+
 }  // namespace webrtc
diff --git a/video/webrtc_video.gypi b/video/webrtc_video.gypi
index e26db0b..82e17f4 100644
--- a/video/webrtc_video.gypi
+++ b/video/webrtc_video.gypi
@@ -11,6 +11,8 @@
       '<(webrtc_root)/video_engine/video_engine.gyp:video_engine_core',
     ],
     'webrtc_video_sources': [
+      'video/encoded_frame_callback_adapter.cc',
+      'video/encoded_frame_callback_adapter.h',
       'video/transport_adapter.cc',
       'video/transport_adapter.h',
       'video/video_receive_stream.cc',
diff --git a/video_engine/call_stats.cc b/video_engine/call_stats.cc
index 0184e5b..a79a7d2 100644
--- a/video_engine/call_stats.cc
+++ b/video_engine/call_stats.cc
@@ -24,7 +24,7 @@
 // Time interval for updating the observers.
 const int kUpdateIntervalMs = 1000;
 
-class RtcpObserver : public RtcpRttObserver {
+class RtcpObserver : public RtcpRttStats {
  public:
   explicit RtcpObserver(CallStats* owner) : owner_(owner) {}
   virtual ~RtcpObserver() {}
@@ -33,6 +33,10 @@
     owner_->OnRttUpdate(rtt);
   }
 
+  virtual uint32_t LastProcessedRtt() const {
+    return owner_->last_processed_rtt_ms();
+  }
+
  private:
   CallStats* owner_;
 
@@ -41,8 +45,9 @@
 
 CallStats::CallStats()
     : crit_(CriticalSectionWrapper::CreateCriticalSection()),
-      rtcp_rtt_observer_(new RtcpObserver(this)),
-      last_process_time_(TickTime::MillisecondTimestamp()) {
+      rtcp_rtt_stats_(new RtcpObserver(this)),
+      last_process_time_(TickTime::MillisecondTimestamp()),
+      last_processed_rtt_ms_(0) {
 }
 
 CallStats::~CallStats() {
@@ -81,12 +86,18 @@
       (*it)->OnRttUpdate(max_rtt);
     }
   }
+  last_processed_rtt_ms_ = max_rtt;
   last_process_time_ = time_now;
   return 0;
 }
 
-RtcpRttObserver* CallStats::rtcp_rtt_observer() const {
-  return rtcp_rtt_observer_.get();
+uint32_t CallStats::last_processed_rtt_ms() const {
+  CriticalSectionScoped cs(crit_.get());
+  return last_processed_rtt_ms_;
+}
+
+RtcpRttStats* CallStats::rtcp_rtt_stats() const {
+  return rtcp_rtt_stats_.get();
 }
 
 void CallStats::RegisterStatsObserver(CallStatsObserver* observer) {
diff --git a/video_engine/call_stats.h b/video_engine/call_stats.h
index 5fd93a7..5c021a4 100644
--- a/video_engine/call_stats.h
+++ b/video_engine/call_stats.h
@@ -19,9 +19,9 @@
 
 namespace webrtc {
 
-class CriticalSectionWrapper;
-class RtcpRttObserver;
 class CallStatsObserver;
+class CriticalSectionWrapper;
+class RtcpRttStats;
 
 // CallStats keeps track of statistics for a call.
 class CallStats : public Module {
@@ -35,9 +35,9 @@
   virtual int32_t TimeUntilNextProcess();
   virtual int32_t Process();
 
-  // Returns a RtcpRttObserver to register at a statistics provider. The object
+  // Returns a RtcpRttStats to register at a statistics provider. The object
   // has the same lifetime as the CallStats instance.
-  RtcpRttObserver* rtcp_rtt_observer() const;
+  RtcpRttStats* rtcp_rtt_stats() const;
 
   // Registers/deregisters a new observer to receive statistics updates.
   void RegisterStatsObserver(CallStatsObserver* observer);
@@ -46,6 +46,8 @@
  protected:
   void OnRttUpdate(uint32_t rtt);
 
+  uint32_t last_processed_rtt_ms() const;
+
  private:
   // Helper struct keeping track of the time a rtt value is reported.
   struct RttTime {
@@ -58,9 +60,11 @@
   // Protecting all members.
   scoped_ptr<CriticalSectionWrapper> crit_;
   // Observer receiving statistics updates.
-  scoped_ptr<RtcpRttObserver> rtcp_rtt_observer_;
+  scoped_ptr<RtcpRttStats> rtcp_rtt_stats_;
   // The last time 'Process' resulted in statistic update.
   int64_t last_process_time_;
+  // The last RTT in the statistics update (zero if there is no valid estimate).
+  uint32_t last_processed_rtt_ms_;
 
   // All Rtt reports within valid time interval, oldest first.
   std::list<RttTime> reports_;
diff --git a/video_engine/call_stats_unittest.cc b/video_engine/call_stats_unittest.cc
index e5b2361..f504094 100644
--- a/video_engine/call_stats_unittest.cc
+++ b/video_engine/call_stats_unittest.cc
@@ -41,15 +41,24 @@
 
 TEST_F(CallStatsTest, AddAndTriggerCallback) {
   MockStatsObserver stats_observer;
-  RtcpRttObserver* rtcp_observer = call_stats_->rtcp_rtt_observer();
+  RtcpRttStats* rtcp_rtt_stats = call_stats_->rtcp_rtt_stats();
   call_stats_->RegisterStatsObserver(&stats_observer);
   TickTime::AdvanceFakeClock(1000);
+  EXPECT_EQ(0U, rtcp_rtt_stats->LastProcessedRtt());
 
-  uint32_t rtt = 25;
-  rtcp_observer->OnRttUpdate(rtt);
-  EXPECT_CALL(stats_observer, OnRttUpdate(rtt))
+  const uint32_t kRtt = 25;
+  rtcp_rtt_stats->OnRttUpdate(kRtt);
+  EXPECT_CALL(stats_observer, OnRttUpdate(kRtt))
       .Times(1);
   call_stats_->Process();
+  EXPECT_EQ(kRtt, rtcp_rtt_stats->LastProcessedRtt());
+
+  const int kRttTimeOutMs = 1500 + 10;
+  TickTime::AdvanceFakeClock(kRttTimeOutMs);
+  EXPECT_CALL(stats_observer, OnRttUpdate(_))
+      .Times(0);
+  call_stats_->Process();
+  EXPECT_EQ(0U, rtcp_rtt_stats->LastProcessedRtt());
 
   call_stats_->DeregisterStatsObserver(&stats_observer);
 }
@@ -57,8 +66,8 @@
 TEST_F(CallStatsTest, ProcessTime) {
   MockStatsObserver stats_observer;
   call_stats_->RegisterStatsObserver(&stats_observer);
-  RtcpRttObserver* rtcp_observer = call_stats_->rtcp_rtt_observer();
-  rtcp_observer->OnRttUpdate(100);
+  RtcpRttStats* rtcp_rtt_stats = call_stats_->rtcp_rtt_stats();
+  rtcp_rtt_stats->OnRttUpdate(100);
 
   // Time isn't updated yet.
   EXPECT_CALL(stats_observer, OnRttUpdate(_))
@@ -73,7 +82,7 @@
 
   // Advance clock just too little to get an update.
   TickTime::AdvanceFakeClock(999);
-  rtcp_observer->OnRttUpdate(100);
+  rtcp_rtt_stats->OnRttUpdate(100);
   EXPECT_CALL(stats_observer, OnRttUpdate(_))
       .Times(0);
   call_stats_->Process();
@@ -98,9 +107,9 @@
   call_stats_->RegisterStatsObserver(&stats_observer_2);
   call_stats_->RegisterStatsObserver(&stats_observer_2);
 
-  RtcpRttObserver* rtcp_observer = call_stats_->rtcp_rtt_observer();
+  RtcpRttStats* rtcp_rtt_stats = call_stats_->rtcp_rtt_stats();
   uint32_t rtt = 100;
-  rtcp_observer->OnRttUpdate(rtt);
+  rtcp_rtt_stats->OnRttUpdate(rtt);
 
   // Verify both observers are updated.
   TickTime::AdvanceFakeClock(1000);
@@ -113,7 +122,7 @@
   // Deregister the second observer and verify update is only sent to the first
   // observer.
   call_stats_->DeregisterStatsObserver(&stats_observer_2);
-  rtcp_observer->OnRttUpdate(rtt);
+  rtcp_rtt_stats->OnRttUpdate(rtt);
   TickTime::AdvanceFakeClock(1000);
   EXPECT_CALL(stats_observer_1, OnRttUpdate(rtt))
       .Times(1);
@@ -123,7 +132,7 @@
 
   // Deregister the first observer.
   call_stats_->DeregisterStatsObserver(&stats_observer_1);
-  rtcp_observer->OnRttUpdate(rtt);
+  rtcp_rtt_stats->OnRttUpdate(rtt);
   TickTime::AdvanceFakeClock(1000);
   EXPECT_CALL(stats_observer_1, OnRttUpdate(rtt))
       .Times(0);
@@ -136,14 +145,14 @@
 TEST_F(CallStatsTest, ChangeRtt) {
   MockStatsObserver stats_observer;
   call_stats_->RegisterStatsObserver(&stats_observer);
-  RtcpRttObserver* rtcp_observer = call_stats_->rtcp_rtt_observer();
+  RtcpRttStats* rtcp_rtt_stats = call_stats_->rtcp_rtt_stats();
 
   // Advance clock to be ready for an update.
   TickTime::AdvanceFakeClock(1000);
 
   // Set a first value and verify the callback is triggered.
   const uint32_t first_rtt = 100;
-  rtcp_observer->OnRttUpdate(first_rtt);
+  rtcp_rtt_stats->OnRttUpdate(first_rtt);
   EXPECT_CALL(stats_observer, OnRttUpdate(first_rtt))
       .Times(1);
   call_stats_->Process();
@@ -151,7 +160,7 @@
   // Increase rtt and verify the new value is reported.
   TickTime::AdvanceFakeClock(1000);
   const uint32_t high_rtt = first_rtt + 20;
-  rtcp_observer->OnRttUpdate(high_rtt);
+  rtcp_rtt_stats->OnRttUpdate(high_rtt);
   EXPECT_CALL(stats_observer, OnRttUpdate(high_rtt))
       .Times(1);
   call_stats_->Process();
@@ -161,13 +170,13 @@
   // in the callback.
   TickTime::AdvanceFakeClock(1000);
   const uint32_t low_rtt = first_rtt - 20;
-  rtcp_observer->OnRttUpdate(low_rtt);
+  rtcp_rtt_stats->OnRttUpdate(low_rtt);
   EXPECT_CALL(stats_observer, OnRttUpdate(high_rtt))
       .Times(1);
   call_stats_->Process();
 
-  // Advance time to make the high report invalid, the lower rtt should no be in
-  // the callback.
+  // Advance time to make the high report invalid, the lower rtt should now be
+  // in the callback.
   TickTime::AdvanceFakeClock(1000);
   EXPECT_CALL(stats_observer, OnRttUpdate(low_rtt))
       .Times(1);
diff --git a/video_engine/include/vie_base.h b/video_engine/include/vie_base.h
index 1ac3fd1..1801e7b 100644
--- a/video_engine/include/vie_base.h
+++ b/video_engine/include/vie_base.h
@@ -120,6 +120,12 @@
   virtual int RegisterCpuOveruseObserver(int channel,
                                          CpuOveruseObserver* observer) = 0;
 
+  // Gets the last cpu overuse measure.
+  // TODO(asapersson): Remove default implementation.
+  virtual int CpuOveruseMeasures(int channel,
+                                 int* capture_jitter_ms,
+                                 int* avg_encode_time_ms) { return -1; }
+
   // Specifies the VoiceEngine and VideoEngine channel pair to use for
   // audio/video synchronization.
   virtual int ConnectAudioChannel(const int video_channel,
diff --git a/video_engine/include/vie_codec.h b/video_engine/include/vie_codec.h
index f470438..bddcc3a 100644
--- a/video_engine/include/vie_codec.h
+++ b/video_engine/include/vie_codec.h
@@ -36,10 +36,10 @@
                             const unsigned int framerate,
                             const unsigned int bitrate) = 0;
 
-  // This method is called whenever the state of the AutoMuter changes, i.e.,
-  // when |is_muted| toggles.
+  // This method is called whenever the state of the SuspendBelowMinBitrate
+  // changes, i.e., when |is_suspended| toggles.
   // TODO(hlundin): Remove the default implementation when possible.
-  virtual void VideoAutoMuted(int video_channel, bool is_muted) {}
+  virtual void SuspendChange(int video_channel, bool is_suspended) {}
 
  protected:
   virtual ~ViEEncoderObserver() {}
@@ -193,12 +193,12 @@
   // Disables recording of debugging information.
   virtual int StopDebugRecording(int video_channel) = 0;
 
-  // Enables AutoMuter to turn off video when the rate drops below
+  // Lets the sender suspend video when the rate drops below
   // |threshold_bps|, and turns back on when the rate goes back up above
   // |threshold_bps| + |window_bps|.
   // This is under development; not tested.
   // TODO(hlundin): Remove the default implementation when possible.
-  virtual void EnableAutoMuting(int video_channel) {}
+  virtual void SuspendBelowMinBitrate(int video_channel) {}
 
  protected:
   ViECodec() {}
diff --git a/video_engine/include/vie_image_process.h b/video_engine/include/vie_image_process.h
index 9a12748..aff2d61 100644
--- a/video_engine/include/vie_image_process.h
+++ b/video_engine/include/vie_image_process.h
@@ -18,12 +18,11 @@
 #define WEBRTC_VIDEO_ENGINE_INCLUDE_VIE_IMAGE_PROCESS_H_
 
 #include "webrtc/common_types.h"
-#include "webrtc/common_video/interface/i420_video_frame.h"
 
 namespace webrtc {
 
+class EncodedImageCallback;
 class I420FrameCallback;
-
 class VideoEngine;
 
 // This class declares an abstract interface for a user defined effect filter.
@@ -98,6 +97,16 @@
       I420FrameCallback* pre_encode_callback) = 0;
   virtual void DeRegisterPreEncodeCallback(int video_channel) = 0;
 
+  virtual void RegisterPostEncodeImageCallback(
+      int video_channel,
+      EncodedImageCallback* post_encode_callback) {}
+  virtual void DeRegisterPostEncodeCallback(int video_channel) {}
+
+  virtual void RegisterPreDecodeImageCallback(
+      int video_channel,
+      EncodedImageCallback* pre_decode_callback) {}
+  virtual void DeRegisterPreDecodeCallback(int video_channel) {}
+
   virtual void RegisterPreRenderCallback(
       int video_channel,
       I420FrameCallback* pre_render_callback) = 0;
diff --git a/video_engine/include/vie_render.h b/video_engine/include/vie_render.h
index 48afc1a..ab61d7a 100644
--- a/video_engine/include/vie_render.h
+++ b/video_engine/include/vie_render.h
@@ -22,10 +22,11 @@
 
 class VideoEngine;
 class VideoRender;
+class VideoRenderCallback;
 
 // This class declares an abstract interface to be used for external renderers.
 // The user implemented derived class is registered using AddRenderer().
-class WEBRTC_DLLEXPORT ExternalRenderer {
+class ExternalRenderer {
  public:
   // This method will be called when the stream to be rendered changes in
   // resolution or number of streams mixed in the image.
@@ -51,7 +52,7 @@
   virtual ~ExternalRenderer() {}
 };
 
-class WEBRTC_DLLEXPORT ViERender {
+class ViERender {
  public:
   // Factory for the ViERender sub‐API and increases an internal reference
   // counter if successful. Returns NULL if the API is not supported or if
@@ -111,6 +112,13 @@
                           RawVideoType video_input_format,
                           ExternalRenderer* renderer) = 0;
 
+  // Propagating VideoRenderCallback down to the VideoRender module for new API.
+  // Contains default-implementation not to break code mocking this interface.
+  // (Ugly, but temporary.)
+  virtual int AddRenderCallback(int render_id, VideoRenderCallback* callback) {
+    return 0;
+  }
+
  protected:
   ViERender() {}
   virtual ~ViERender() {}
diff --git a/video_engine/include/vie_rtp_rtcp.h b/video_engine/include/vie_rtp_rtcp.h
index 135f7f8..9d899ad 100644
--- a/video_engine/include/vie_rtp_rtcp.h
+++ b/video_engine/include/vie_rtp_rtcp.h
@@ -254,6 +254,11 @@
                                                bool enable,
                                                int id) = 0;
 
+  // Enables/disables RTCP Receiver Reference Time Report Block extension/
+  // DLRR Report Block extension (RFC 3611).
+  // TODO(asapersson): Remove default implementation.
+  virtual int SetRtcpXrRrtrStatus(int video_channel, bool enable) { return -1; }
+
   // Enables transmission smoothening, i.e. packets belonging to the same frame
   // will be sent over a longer period of time instead of sending them
   // back-to-back.
@@ -262,29 +267,97 @@
 
   // This function returns our locally created statistics of the received RTP
   // stream.
-  virtual int GetReceivedRTCPStatistics(
-      const int video_channel,
-      unsigned short& fraction_lost,
-      unsigned int& cumulative_lost,
-      unsigned int& extended_max,
-      unsigned int& jitter,
-      int& rtt_ms) const = 0;
+  virtual int GetReceiveChannelRtcpStatistics(const int video_channel,
+                                              RtcpStatistics& basic_stats,
+                                              int& rtt_ms) const = 0;
 
   // This function returns statistics reported by the remote client in a RTCP
   // packet.
+  virtual int GetSendChannelRtcpStatistics(const int video_channel,
+                                           RtcpStatistics& basic_stats,
+                                           int& rtt_ms) const = 0;
+
+  // TODO(sprang): Temporary hacks to prevent libjingle build from failing,
+  // remove when libjingle has been lifted to support webrtc issue 2589
+  virtual int GetReceivedRTCPStatistics(const int video_channel,
+                                unsigned short& fraction_lost,
+                                unsigned int& cumulative_lost,
+                                unsigned int& extended_max,
+                                unsigned int& jitter,
+                                int& rtt_ms) const {
+    RtcpStatistics stats;
+    int ret_code = GetReceiveChannelRtcpStatistics(video_channel,
+                                             stats,
+                                             rtt_ms);
+    fraction_lost = stats.fraction_lost;
+    cumulative_lost = stats.cumulative_lost;
+    extended_max = stats.extended_max_sequence_number;
+    jitter = stats.jitter;
+    return ret_code;
+  }
   virtual int GetSentRTCPStatistics(const int video_channel,
-                                    unsigned short& fraction_lost,
-                                    unsigned int& cumulative_lost,
-                                    unsigned int& extended_max,
-                                    unsigned int& jitter,
-                                    int& rtt_ms) const = 0;
+                            unsigned short& fraction_lost,
+                            unsigned int& cumulative_lost,
+                            unsigned int& extended_max,
+                            unsigned int& jitter,
+                            int& rtt_ms) const {
+    RtcpStatistics stats;
+    int ret_code = GetSendChannelRtcpStatistics(video_channel,
+                                                stats,
+                                                rtt_ms);
+    fraction_lost = stats.fraction_lost;
+    cumulative_lost = stats.cumulative_lost;
+    extended_max = stats.extended_max_sequence_number;
+    jitter = stats.jitter;
+    return ret_code;
+  }
+
+
+  virtual int RegisterSendChannelRtcpStatisticsCallback(
+      int video_channel, RtcpStatisticsCallback* callback) = 0;
+
+  virtual int DeregisterSendChannelRtcpStatisticsCallback(
+      int video_channel, RtcpStatisticsCallback* callback) = 0;
+
+  virtual int RegisterReceiveChannelRtcpStatisticsCallback(
+      int video_channel, RtcpStatisticsCallback* callback) = 0;
+
+  virtual int DeregisterReceiveChannelRtcpStatisticsCallback(
+      int video_channel, RtcpStatisticsCallback* callback) = 0;
 
   // The function gets statistics from the sent and received RTP streams.
+  virtual int GetRtpStatistics(const int video_channel,
+                               StreamDataCounters& sent,
+                               StreamDataCounters& received) const = 0;
+
+  // TODO(sprang): Temporary hacks to prevent libjingle build from failing,
+  // remove when libjingle has been lifted to support webrtc issue 2589
   virtual int GetRTPStatistics(const int video_channel,
-                               unsigned int& bytes_sent,
-                               unsigned int& packets_sent,
-                               unsigned int& bytes_received,
-                               unsigned int& packets_received) const = 0;
+                       unsigned int& bytes_sent,
+                       unsigned int& packets_sent,
+                       unsigned int& bytes_received,
+                       unsigned int& packets_received) const {
+    StreamDataCounters sent;
+    StreamDataCounters received;
+    int ret_code = GetRtpStatistics(video_channel, sent, received);
+    bytes_sent = sent.bytes;
+    packets_sent = sent.packets;
+    bytes_received = received.bytes;
+    packets_received = received.packets;
+    return ret_code;
+  }
+
+  virtual int RegisterSendChannelRtpStatisticsCallback(
+      int video_channel, StreamDataCountersCallback* callback) = 0;
+
+  virtual int DeregisterSendChannelRtpStatisticsCallback(
+      int video_channel, StreamDataCountersCallback* callback) = 0;
+
+  virtual int RegisterReceiveChannelRtpStatisticsCallback(
+      int video_channel, StreamDataCountersCallback* callback) = 0;
+
+  virtual int DeregisterReceiveChannelRtpStatisticsCallback(
+      int video_channel, StreamDataCountersCallback* callback) = 0;
 
   // The function gets bandwidth usage statistics from the sent RTP streams in
   // bits/s.
@@ -294,6 +367,15 @@
                                 unsigned int& fec_bitrate_sent,
                                 unsigned int& nackBitrateSent) const = 0;
 
+  // (De)Register an observer, called whenever the send bitrate is updated
+  virtual int RegisterSendBitrateObserver(
+      int video_channel,
+      BitrateStatisticsObserver* observer) = 0;
+
+  virtual int DeregisterSendBitrateObserver(
+      int video_channel,
+      BitrateStatisticsObserver* observer) = 0;
+
   // This function gets the send-side estimated bandwidth available for video,
   // including overhead, in bits/s.
   virtual int GetEstimatedSendBandwidth(
@@ -334,8 +416,15 @@
   // Removes a registered instance of ViERTCPObserver.
   virtual int DeregisterRTCPObserver(const int video_channel) = 0;
 
+  // Registers and instance of a user implementation of ViEFrameCountObserver
+  virtual int RegisterSendFrameCountObserver(
+      int video_channel, FrameCountObserver* observer) = 0;
+
+  // Removes a registered instance of a ViEFrameCountObserver
+  virtual int DeregisterSendFrameCountObserver(
+      int video_channel, FrameCountObserver* observer) = 0;
+
  protected:
-  ViERTP_RTCP() {}
   virtual ~ViERTP_RTCP() {}
 };
 
diff --git a/video_engine/overuse_frame_detector.cc b/video_engine/overuse_frame_detector.cc
index 96cea17..c284f64 100644
--- a/video_engine/overuse_frame_detector.cc
+++ b/video_engine/overuse_frame_detector.cc
@@ -47,6 +47,8 @@
 // Expontential back-off factor, to prevent annoying up-down behaviour.
 const double kRampUpBackoffFactor = 2.0;
 
+// The initial average encode time (set to a fairly small value).
+const float kInitialAvgEncodeTimeMs = 5.0f;
 }  // namespace
 
 Statistics::Statistics() :
@@ -116,7 +118,8 @@
       last_rampup_time_(0),
       in_quick_rampup_(false),
       current_rampup_delay_ms_(kStandardRampUpDelayMs),
-      num_pixels_(0) {}
+      num_pixels_(0),
+      avg_encode_time_ms_(kInitialAvgEncodeTimeMs) {}
 
 OveruseFrameDetector::~OveruseFrameDetector() {
 }
@@ -144,6 +147,23 @@
   last_capture_time_ = time;
 }
 
+void OveruseFrameDetector::FrameEncoded(int encode_time_ms) {
+  CriticalSectionScoped cs(crit_.get());
+  const float kWeight = 0.1f;
+  avg_encode_time_ms_ = kWeight * encode_time_ms +
+                        (1.0f - kWeight) * avg_encode_time_ms_;
+}
+
+int OveruseFrameDetector::last_capture_jitter_ms() const {
+  CriticalSectionScoped cs(crit_.get());
+  return static_cast<int>(capture_deltas_.StdDev() + 0.5);
+}
+
+int OveruseFrameDetector::avg_encode_time_ms() const {
+  CriticalSectionScoped cs(crit_.get());
+  return static_cast<int>(avg_encode_time_ms_ + 0.5);
+}
+
 int32_t OveruseFrameDetector::TimeUntilNextProcess() {
   CriticalSectionScoped cs(crit_.get());
   return next_process_time_ - clock_->TimeInMilliseconds();
diff --git a/video_engine/overuse_frame_detector.h b/video_engine/overuse_frame_detector.h
index 4717a60..356e27f 100644
--- a/video_engine/overuse_frame_detector.h
+++ b/video_engine/overuse_frame_detector.h
@@ -71,6 +71,14 @@
   // Called for each captured frame.
   void FrameCaptured(int width, int height);
 
+  void FrameEncoded(int encode_time_ms);
+
+  int last_capture_jitter_ms() const;
+
+  // Running average of reported encode time (FrameEncoded()).
+  // Only used for stats.
+  int avg_encode_time_ms() const;
+
   // Implements Module.
   virtual int32_t TimeUntilNextProcess() OVERRIDE;
   virtual int32_t Process() OVERRIDE;
@@ -105,6 +113,8 @@
   // Number of pixels of last captured frame.
   int num_pixels_;
 
+  float avg_encode_time_ms_;
+
   DISALLOW_COPY_AND_ASSIGN(OveruseFrameDetector);
 };
 
diff --git a/video_engine/overuse_frame_detector_unittest.cc b/video_engine/overuse_frame_detector_unittest.cc
index d199569..131bbfd 100644
--- a/video_engine/overuse_frame_detector_unittest.cc
+++ b/video_engine/overuse_frame_detector_unittest.cc
@@ -95,4 +95,18 @@
     TriggerOveruse();
 }
 
+TEST_F(OveruseFrameDetectorTest, LastCaptureJitter) {
+  EXPECT_EQ(0, overuse_detector_->last_capture_jitter_ms());
+  TriggerOveruse();
+  EXPECT_GT(overuse_detector_->last_capture_jitter_ms(), 0);
+}
+
+TEST_F(OveruseFrameDetectorTest, EncodedFrame) {
+  const int kInitialAvgEncodeTimeInMs = 5;
+  EXPECT_EQ(kInitialAvgEncodeTimeInMs, overuse_detector_->avg_encode_time_ms());
+  for (int i = 0; i < 30; i++)
+    overuse_detector_->FrameEncoded(2);
+  EXPECT_EQ(2, overuse_detector_->avg_encode_time_ms());
+}
+
 }  // namespace webrtc
diff --git a/video_engine/stream_synchronization.cc b/video_engine/stream_synchronization.cc
index fcff782..6192dfa 100644
--- a/video_engine/stream_synchronization.cc
+++ b/video_engine/stream_synchronization.cc
@@ -12,6 +12,7 @@
 
 #include <assert.h>
 #include <math.h>
+#include <stdlib.h>
 
 #include <algorithm>
 
diff --git a/video_engine/test/android/jni/android_media_codec_decoder.cc b/video_engine/test/android/jni/android_media_codec_decoder.cc
index 9e45c8e..b9712ea 100644
--- a/video_engine/test/android/jni/android_media_codec_decoder.cc
+++ b/video_engine/test/android/jni/android_media_codec_decoder.cc
@@ -58,7 +58,7 @@
       __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
                           "Could not attach thread to JVM (%d, %p)", ret,
                           env_);
-      return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
+      return WEBRTC_VIDEO_CODEC_ERROR;
   } else {
     vm_attached_ = true;
   }
@@ -68,9 +68,13 @@
   mediaCodecDecoder_ = env_->NewGlobalRef(env_->NewObject(decoderClass_, mid));
 
   mid = env_->GetMethodID(
-      decoderClass_, "configure", "(Landroid/view/SurfaceView;II)V");
-  env_->CallVoidMethod(mediaCodecDecoder_, mid, surface_,
-                       codecSettings->width, codecSettings->height);
+      decoderClass_, "configure", "(Landroid/view/SurfaceView;II)Z");
+  bool success = env_->CallBooleanMethod(
+      mediaCodecDecoder_, mid, surface_, codecSettings->width,
+      codecSettings->height);
+  if (!success) {
+      return WEBRTC_VIDEO_CODEC_ERROR;
+  }
 
   setEncodedImageID_ = env_->GetMethodID(
       decoderClass_, "setEncodedImage", "(Ljava/nio/ByteBuffer;J)V");
diff --git a/video_engine/test/android/src/org/webrtc/videoengine/ViEMediaCodecDecoder.java b/video_engine/test/android/src/org/webrtc/videoengine/ViEMediaCodecDecoder.java
index 9ca4125..1d5bb39 100644
--- a/video_engine/test/android/src/org/webrtc/videoengine/ViEMediaCodecDecoder.java
+++ b/video_engine/test/android/src/org/webrtc/videoengine/ViEMediaCodecDecoder.java
@@ -20,6 +20,7 @@
 import android.view.Surface;
 import android.view.SurfaceView;
 
+import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.LinkedList;
 
@@ -245,7 +246,7 @@
 
     private Thread mLooperThread;
 
-    public void configure(SurfaceView surfaceView, int width, int height) {
+    public boolean configure(SurfaceView surfaceView, int width, int height) {
         mSurfaceView = surfaceView;
         Log.d(TAG, "configure " + "width" + width + "height" + height + mSurfaceView.toString());
 
@@ -253,18 +254,30 @@
         format.setString(MediaFormat.KEY_MIME, "video/x-vnd.on2.vp8");
         format.setInteger(MediaFormat.KEY_WIDTH, width);
         format.setInteger(MediaFormat.KEY_HEIGHT, height);
-        MediaCodec codec = MediaCodec.createDecoderByType("video/x-vnd.on2.vp8");
-        // SW VP8 decoder
-        // MediaCodec codec = MediaCodec.createByCodecName("OMX.google.vpx.decoder");
-        // Nexus10 HW VP8 decoder
-        // MediaCodec codec = MediaCodec.createByCodecName("OMX.Exynos.VP8.Decoder");
+
         Surface surface = mSurfaceView.getHolder().getSurface();
         Log.d(TAG, "Surface " + surface.isValid());
-        codec.configure(
-                format, surface, null, 0);
-        mCodecState = new CodecState(this, format, codec);
+        MediaCodec codec;
+        try {
+            codec = MediaCodec.createDecoderByType("video/x-vnd.on2.vp8");
+            // SW VP8 decoder
+            // codec = MediaCodec.createByCodecName("OMX.google.vpx.decoder");
+            // Nexus10 HW VP8 decoder
+            // codec = MediaCodec.createByCodecName("OMX.Exynos.VP8.Decoder");
+        } catch (Exception e) {
+            // TODO(dwkang): replace this instanceof/throw with a narrower catch clause
+            // once the SDK advances.
+            if (e instanceof IOException) {
+                Log.e(TAG, "Failed to create MediaCodec for VP8.", e);
+                return false;
+            }
+            throw new RuntimeException(e);
+        }
 
+        codec.configure(format, surface, null, 0);
+        mCodecState = new CodecState(this, format, codec);
         initMediaCodecView();
+        return true;
     }
 
     public void setEncodedImage(ByteBuffer buffer, long renderTimeMs) {
diff --git a/video_engine/test/auto_test/interface/vie_autotest_defines.h b/video_engine/test/auto_test/interface/vie_autotest_defines.h
index 83476e0..3e8d762 100644
--- a/video_engine/test/auto_test/interface/vie_autotest_defines.h
+++ b/video_engine/test/auto_test/interface/vie_autotest_defines.h
@@ -49,9 +49,8 @@
 #undef RGB
 #define RGB(r,g,b) r|g<<8|b<<16
 
-enum {
-  kAutoTestSleepTimeMs = 5000
-};
+enum { kAutoTestSleepTimeMs = 5000 };
+enum { kAutoTestFullStackSleepTimeMs = 20000 };
 
 struct AutoTestSize {
   unsigned int width;
diff --git a/video_engine/test/auto_test/primitives/framedrop_primitives.cc b/video_engine/test/auto_test/primitives/framedrop_primitives.cc
index a79f7d5..99531d5 100644
--- a/video_engine/test/auto_test/primitives/framedrop_primitives.cc
+++ b/video_engine/test/auto_test/primitives/framedrop_primitives.cc
@@ -218,7 +218,7 @@
                                                          decode_filter));
   // Send video.
   EXPECT_EQ(0, base_interface->StartSend(video_channel));
-  AutoTestSleep(kAutoTestSleepTimeMs);
+  AutoTestSleep(kAutoTestFullStackSleepTimeMs);
 
   ViETest::Log("Done!");
 
diff --git a/video_engine/test/auto_test/source/vie_autotest_codec.cc b/video_engine/test/auto_test/source/vie_autotest_codec.cc
index 3fa2bc4..0ebfbec 100644
--- a/video_engine/test/auto_test/source/vie_autotest_codec.cc
+++ b/video_engine/test/auto_test/source/vie_autotest_codec.cc
@@ -44,7 +44,7 @@
   unsigned int last_outgoing_bitrate_;
   unsigned int last_incoming_framerate_;
   unsigned int last_incoming_bitrate_;
-  unsigned int video_auto_muted_called_;
+  unsigned int suspend_change_called_;
 
   webrtc::VideoCodec incoming_codec_;
 
@@ -60,7 +60,7 @@
         last_outgoing_bitrate_(0),
         last_incoming_framerate_(0),
         last_incoming_bitrate_(0),
-        video_auto_muted_called_(0) {
+        suspend_change_called_(0) {
     memset(&incoming_codec_, 0, sizeof(incoming_codec_));
   }
   virtual void IncomingCodecChanged(const int video_channel,
@@ -100,8 +100,8 @@
     last_outgoing_bitrate_ += bitrate;
   }
 
-  virtual void VideoAutoMuted(int video_channel, bool is_muted) {
-    video_auto_muted_called_++;
+  virtual void SuspendChange(int video_channel, bool is_suspended) OVERRIDE {
+    suspend_change_called_++;
   }
 
   virtual void RequestNewKeyFrame(const int video_channel) {
diff --git a/video_engine/test/auto_test/source/vie_autotest_custom_call.cc b/video_engine/test/auto_test/source/vie_autotest_custom_call.cc
index 8d7613d..f9a5c22 100644
--- a/video_engine/test/auto_test/source/vie_autotest_custom_call.cc
+++ b/video_engine/test/auto_test/source/vie_autotest_custom_call.cc
@@ -73,8 +73,8 @@
               << " BR: " << bitrate << std::endl;
   }
 
-  virtual void VideoAutoMuted(int video_channel, bool is_muted) {
-    std::cout << "VideoAutoMuted: " << is_muted << std::endl;
+  virtual void SuspendChange(int video_channel, bool is_suspended) OVERRIDE {
+    std::cout << "SuspendChange: " << is_suspended << std::endl;
   }
 };
 
@@ -1509,10 +1509,7 @@
                           StatisticsType stat_type) {
   int error = 0;
   int number_of_errors = 0;
-  uint16_t fraction_lost = 0;
-  unsigned int cumulative_lost = 0;
-  unsigned int extended_max = 0;
-  unsigned int jitter = 0;
+  webrtc::RtcpStatistics rtcp_stats;
   int rtt_ms = 0;
 
   switch (stat_type) {
@@ -1520,11 +1517,9 @@
       std::cout << "RTCP Received statistics"
                 << std::endl;
       // Get and print the Received RTCP Statistics
-      error = vie_rtp_rtcp->GetReceivedRTCPStatistics(video_channel,
-                                                      fraction_lost,
-                                                      cumulative_lost,
-                                                      extended_max,
-                                                      jitter, rtt_ms);
+      error = vie_rtp_rtcp->GetReceiveChannelRtcpStatistics(video_channel,
+                                                      rtcp_stats,
+                                                      rtt_ms);
       number_of_errors += ViETest::TestError(error == 0,
                                              "ERROR: %s at line %d",
                                              __FUNCTION__, __LINE__);
@@ -1533,22 +1528,22 @@
       std::cout << "RTCP Sent statistics"
                 << std::endl;
       // Get and print the Sent RTCP Statistics
-      error = vie_rtp_rtcp->GetSentRTCPStatistics(video_channel, fraction_lost,
-                                                  cumulative_lost, extended_max,
-                                                  jitter, rtt_ms);
+      error = vie_rtp_rtcp->GetSendChannelRtcpStatistics(video_channel,
+                                                  rtcp_stats,
+                                                  rtt_ms);
       number_of_errors += ViETest::TestError(error == 0,
                                              "ERROR: %s at line %d",
                                              __FUNCTION__, __LINE__);
       break;
   }
   std::cout << "\tRTCP fraction of lost packets: "
-            << fraction_lost << std::endl;
+            << rtcp_stats.fraction_lost << std::endl;
   std::cout << "\tRTCP cumulative number of lost packets: "
-            << cumulative_lost << std::endl;
+            << rtcp_stats.cumulative_lost << std::endl;
   std::cout << "\tRTCP max received sequence number "
-            << extended_max << std::endl;
+            << rtcp_stats.extended_max_sequence_number << std::endl;
   std::cout << "\tRTCP jitter: "
-            << jitter << std::endl;
+            << rtcp_stats.jitter << std::endl;
   std::cout << "\tRTCP round trip (ms): "
             << rtt_ms << std::endl;
 }
@@ -1557,29 +1552,25 @@
                         int video_channel) {
   int error = 0;
   int number_of_errors = 0;
-  unsigned int bytes_sent = 0;
-  unsigned int packets_sent = 0;
-  unsigned int bytes_received = 0;
-  unsigned int packets_received = 0;
+  webrtc::StreamDataCounters sent;
+  webrtc::StreamDataCounters received;
 
   std::cout << "RTP statistics"
             << std::endl;
 
   // Get and print the RTP Statistics
-  error = vie_rtp_rtcp->GetRTPStatistics(video_channel, bytes_sent,
-                                         packets_sent, bytes_received,
-                                         packets_received);
+  error = vie_rtp_rtcp->GetRtpStatistics(video_channel, sent, received);
   number_of_errors += ViETest::TestError(error == 0,
                                          "ERROR: %s at line %d",
                                          __FUNCTION__, __LINE__);
   std::cout << "\tRTP bytes sent: "
-            << bytes_sent << std::endl;
+            << sent.bytes << std::endl;
   std::cout << "\tRTP packets sent: "
-            << packets_sent << std::endl;
+            << sent.packets << std::endl;
   std::cout << "\tRTP bytes received: "
-            << bytes_received << std::endl;
+            << received.bytes << std::endl;
   std::cout << "\tRTP packets received: "
-            << packets_received << std::endl;
+            << received.packets << std::endl;
 }
 
 void PrintBandwidthUsage(webrtc::ViERTP_RTCP* vie_rtp_rtcp,
diff --git a/video_engine/test/auto_test/source/vie_autotest_rtp_rtcp.cc b/video_engine/test/auto_test/source/vie_autotest_rtp_rtcp.cc
index 3b4753e..56a6a6a 100644
--- a/video_engine/test/auto_test/source/vie_autotest_rtp_rtcp.cc
+++ b/video_engine/test/auto_test/source/vie_autotest_rtp_rtcp.cc
@@ -12,6 +12,7 @@
 
 #include "webrtc/engine_configurations.h"
 #include "webrtc/test/testsupport/fileutils.h"
+#include "webrtc/video_engine/include/vie_rtp_rtcp.h"
 #include "webrtc/video_engine/test/auto_test/interface/vie_autotest.h"
 #include "webrtc/video_engine/test/auto_test/interface/vie_autotest_defines.h"
 #include "webrtc/video_engine/test/libvietest/include/tb_capture_device.h"
@@ -164,10 +165,7 @@
     //
     // Pacing
     //
-    unsigned short recFractionsLost = 0;
-    unsigned int recCumulativeLost = 0;
-    unsigned int recExtendedMax = 0;
-    unsigned int recJitter = 0;
+    webrtc::RtcpStatistics received;
     int recRttMs = 0;
     unsigned int sentTotalBitrate = 0;
     unsigned int sentVideoBitrate = 0;
@@ -193,9 +191,8 @@
 
     AutoTestSleep(kAutoTestSleepTimeMs);
 
-    EXPECT_EQ(0, ViE.rtp_rtcp->GetReceivedRTCPStatistics(
-        tbChannel.videoChannel, recFractionsLost, recCumulativeLost,
-        recExtendedMax, recJitter, recRttMs));
+    EXPECT_EQ(0, ViE.rtp_rtcp->GetReceiveChannelRtcpStatistics(
+        tbChannel.videoChannel, received, recRttMs));
     EXPECT_EQ(0, ViE.rtp_rtcp->GetBandwidthUsage(
         tbChannel.videoChannel, sentTotalBitrate, sentVideoBitrate,
         sentFecBitrate, sentNackBitrate));
@@ -211,7 +208,7 @@
     EXPECT_GT(num_rtcp_packets, 0);
     EXPECT_GT(sentTotalBitrate, 0u);
     EXPECT_EQ(sentNackBitrate, 0u);
-    EXPECT_EQ(recCumulativeLost, 0u);
+    EXPECT_EQ(received.cumulative_lost, 0u);
 
     //
     // RTX
@@ -248,9 +245,8 @@
     myTransport.SetNetworkParameters(network);
     AutoTestSleep(kAutoTestSleepTimeMs);
 
-    EXPECT_EQ(0, ViE.rtp_rtcp->GetReceivedRTCPStatistics(
-        tbChannel.videoChannel, recFractionsLost, recCumulativeLost,
-        recExtendedMax, recJitter, recRttMs));
+    EXPECT_EQ(0, ViE.rtp_rtcp->GetReceiveChannelRtcpStatistics(
+        tbChannel.videoChannel, received, recRttMs));
     EXPECT_EQ(0, ViE.rtp_rtcp->GetBandwidthUsage(
         tbChannel.videoChannel, sentTotalBitrate, sentVideoBitrate,
         sentFecBitrate, sentNackBitrate));
@@ -292,10 +288,7 @@
 
     AutoTestSleep(kAutoTestSleepTimeMs);
 
-    unsigned short sentFractionsLost = 0;
-    unsigned int sentCumulativeLost = 0;
-    unsigned int sentExtendedMax = 0;
-    unsigned int sentJitter = 0;
+    webrtc::RtcpStatistics sent;
     int sentRttMs = 0;
 
     EXPECT_EQ(0, ViE.rtp_rtcp->GetBandwidthUsage(
@@ -310,21 +303,19 @@
 
     AutoTestSleep(2000);
 
-    EXPECT_EQ(0, ViE.rtp_rtcp->GetSentRTCPStatistics(
-        tbChannel.videoChannel, sentFractionsLost, sentCumulativeLost,
-        sentExtendedMax, sentJitter, sentRttMs));
-    EXPECT_GT(sentCumulativeLost, 0u);
-    EXPECT_GT(sentExtendedMax, startSequenceNumber);
-    EXPECT_GT(sentJitter, 0u);
+    EXPECT_EQ(0, ViE.rtp_rtcp->GetSendChannelRtcpStatistics(
+        tbChannel.videoChannel, sent, sentRttMs));
+    EXPECT_GT(sent.cumulative_lost, 0u);
+    EXPECT_GT(sent.extended_max_sequence_number, startSequenceNumber);
+    EXPECT_GT(sent.jitter, 0u);
     EXPECT_GT(sentRttMs, 0);
 
-    EXPECT_EQ(0, ViE.rtp_rtcp->GetReceivedRTCPStatistics(
-        tbChannel.videoChannel, recFractionsLost, recCumulativeLost,
-        recExtendedMax, recJitter, recRttMs));
+    EXPECT_EQ(0, ViE.rtp_rtcp->GetReceiveChannelRtcpStatistics(
+        tbChannel.videoChannel, received, recRttMs));
 
-    EXPECT_GT(recCumulativeLost, 0u);
-    EXPECT_GT(recExtendedMax, startSequenceNumber);
-    EXPECT_GT(recJitter, 0u);
+    EXPECT_GT(received.cumulative_lost, 0u);
+    EXPECT_GT(received.extended_max_sequence_number, startSequenceNumber);
+    EXPECT_GT(received.jitter, 0u);
     EXPECT_GT(recRttMs, 0);
 
     unsigned int estimated_bandwidth = 0;
@@ -351,7 +342,8 @@
     }
 
     // Check that rec stats extended max is greater than what we've sent.
-    EXPECT_GE(recExtendedMax, sentExtendedMax);
+    EXPECT_GE(received.extended_max_sequence_number,
+              sent.extended_max_sequence_number);
     EXPECT_EQ(0, ViE.base->StopSend(tbChannel.videoChannel));
 
     //
@@ -486,62 +478,51 @@
     EXPECT_EQ(0, ViE.rtp_rtcp->SetNACKStatus(tbChannel.videoChannel, true));
     EXPECT_EQ(0, ViE.rtp_rtcp->SetTransmissionSmoothingStatus(
         tbChannel.videoChannel, true));
-    unsigned int bytes_sent_before = 0;
-    unsigned int packets_sent_before = 0;
-    unsigned int bytes_received_before = 0;
-    unsigned int packets_received_before = 0;
-    unsigned int bytes_sent_after = 0;
-    unsigned int packets_sent_after = 0;
-    unsigned int bytes_received_after = 0;
-    unsigned int packets_received_after = 0;
-    EXPECT_EQ(0, ViE.rtp_rtcp->GetRTPStatistics(tbChannel.videoChannel,
-                                                bytes_sent_before,
-                                                packets_sent_before,
-                                                bytes_received_before,
-                                                packets_received_before));
+
+    webrtc::StreamDataCounters sent_before;
+    webrtc::StreamDataCounters received_before;
+    webrtc::StreamDataCounters sent_after;
+    webrtc::StreamDataCounters received_after;
+
+    EXPECT_EQ(0, ViE.rtp_rtcp->GetRtpStatistics(tbChannel.videoChannel,
+                                                sent_before,
+                                                received_before));
     EXPECT_EQ(0, ViE.base->StartReceive(tbChannel.videoChannel));
     EXPECT_EQ(0, ViE.base->StartSend(tbChannel.videoChannel));
 
     // Real-time mode.
     AutoTestSleep(kAutoTestSleepTimeMs);
-    EXPECT_EQ(0, ViE.rtp_rtcp->GetRTPStatistics(tbChannel.videoChannel,
-                                                bytes_sent_after,
-                                                packets_sent_after,
-                                                bytes_received_after,
-                                                packets_received_after));
+    EXPECT_EQ(0, ViE.rtp_rtcp->GetRtpStatistics(tbChannel.videoChannel,
+                                                sent_after, received_after));
     if (FLAGS_include_timing_dependent_tests) {
-      EXPECT_GT(bytes_received_after, bytes_received_before);
+      EXPECT_GT(received_after.bytes, received_before.bytes);
     }
     // Simulate lost reception and verify that nothing is sent during that time.
     ViE.network->SetNetworkTransmissionState(tbChannel.videoChannel, false);
     // Allow the encoder to finish the current frame before we expect that no
     // additional packets will be sent.
     AutoTestSleep(kAutoTestSleepTimeMs);
-    bytes_received_before = bytes_received_after;
+    received_before.bytes = received_after.bytes;
     ViETest::Log("Network Down...\n");
     AutoTestSleep(kAutoTestSleepTimeMs);
-    EXPECT_EQ(0, ViE.rtp_rtcp->GetRTPStatistics(tbChannel.videoChannel,
-                                                bytes_sent_after,
-                                                packets_sent_after,
-                                                bytes_received_after,
-                                                packets_received_after));
+    EXPECT_EQ(0, ViE.rtp_rtcp->GetRtpStatistics(tbChannel.videoChannel,
+                                                sent_before,
+                                                received_before));
     if (FLAGS_include_timing_dependent_tests) {
-      EXPECT_EQ(bytes_received_before, bytes_received_after);
+      EXPECT_EQ(received_before.bytes, received_after.bytes);
     }
 
     // Network reception back. Video should now be sent.
     ViE.network->SetNetworkTransmissionState(tbChannel.videoChannel, true);
     ViETest::Log("Network Up...\n");
     AutoTestSleep(kAutoTestSleepTimeMs);
-    EXPECT_EQ(0, ViE.rtp_rtcp->GetRTPStatistics(tbChannel.videoChannel,
-                                                bytes_sent_before,
-                                                packets_sent_before,
-                                                bytes_received_before,
-                                                packets_received_before));
+    EXPECT_EQ(0, ViE.rtp_rtcp->GetRtpStatistics(tbChannel.videoChannel,
+                                                sent_before,
+                                                received_before));
     if (FLAGS_include_timing_dependent_tests) {
-      EXPECT_GT(bytes_received_before, bytes_received_after);
+      EXPECT_GT(received_before.bytes, received_after.bytes);
     }
-    bytes_received_after = bytes_received_before;
+    received_after.bytes = received_before.bytes;
     // Buffering mode.
     EXPECT_EQ(0, ViE.base->StopSend(tbChannel.videoChannel));
     EXPECT_EQ(0, ViE.base->StopReceive(tbChannel.videoChannel));
@@ -561,35 +542,29 @@
     // Allow the encoder to finish the current frame before we expect that no
     // additional packets will be sent.
     AutoTestSleep(kAutoTestSleepTimeMs);
-    EXPECT_EQ(0, ViE.rtp_rtcp->GetRTPStatistics(tbChannel.videoChannel,
-                                                bytes_sent_before,
-                                                packets_sent_before,
-                                                bytes_received_before,
-                                                packets_received_before));
+    EXPECT_EQ(0, ViE.rtp_rtcp->GetRtpStatistics(tbChannel.videoChannel,
+                                                sent_before,
+                                                received_before));
     if (FLAGS_include_timing_dependent_tests) {
-      EXPECT_GT(bytes_received_before, bytes_received_after);
+      EXPECT_GT(received_before.bytes, received_after.bytes);
     }
-    bytes_received_after = bytes_received_before;
+    received_after.bytes = received_before.bytes;
     AutoTestSleep(kAutoTestSleepTimeMs);
-    EXPECT_EQ(0, ViE.rtp_rtcp->GetRTPStatistics(tbChannel.videoChannel,
-                                                bytes_sent_after,
-                                                packets_sent_after,
-                                                bytes_received_after,
-                                                packets_received_after));
+    EXPECT_EQ(0, ViE.rtp_rtcp->GetRtpStatistics(tbChannel.videoChannel,
+                                                sent_before,
+                                                received_before));
     if (FLAGS_include_timing_dependent_tests) {
-      EXPECT_EQ(bytes_received_after, bytes_received_before);
+      EXPECT_EQ(received_after.bytes, received_before.bytes);
     }
     // Network reception back. Video should now be sent.
     ViETest::Log("Network Up...\n");
     ViE.network->SetNetworkTransmissionState(tbChannel.videoChannel, true);
     AutoTestSleep(kAutoTestSleepTimeMs);
-    EXPECT_EQ(0, ViE.rtp_rtcp->GetRTPStatistics(tbChannel.videoChannel,
-                                                bytes_sent_before,
-                                                packets_sent_before,
-                                                bytes_received_before,
-                                                packets_received_before));
+    EXPECT_EQ(0, ViE.rtp_rtcp->GetRtpStatistics(tbChannel.videoChannel,
+                                                sent_before,
+                                                received_before));
     if (FLAGS_include_timing_dependent_tests) {
-      EXPECT_GT(bytes_received_before, bytes_received_after);
+      EXPECT_GT(received_before.bytes, received_after.bytes);
     }
     // TODO(holmer): Verify that the decoded framerate doesn't decrease on an
     // outage when in buffering mode. This isn't currently possible because we
diff --git a/video_engine/test/auto_test/source/vie_autotest_win.cc b/video_engine/test/auto_test/source/vie_autotest_win.cc
index 8d5cb49..98e37a4 100755
--- a/video_engine/test/auto_test/source/vie_autotest_win.cc
+++ b/video_engine/test/auto_test/source/vie_autotest_win.cc
@@ -64,7 +64,7 @@
     ViEDestroyWindow(_hwnd1);
   }
   if (_hwnd2) {
-    ViEDestroyWindow(_hwnd1);
+    ViEDestroyWindow(_hwnd2);
   }
   delete &_crit;
 }
diff --git a/video_engine/vie_base_impl.cc b/video_engine/vie_base_impl.cc
index 53d2f99..8d6aeb2 100644
--- a/video_engine/vie_base_impl.cc
+++ b/video_engine/vie_base_impl.cc
@@ -117,6 +117,36 @@
   return 0;
 }
 
+int ViEBaseImpl::CpuOveruseMeasures(int video_channel,
+                                    int* capture_jitter_ms,
+                                    int* avg_encode_time_ms) {
+  ViEChannelManagerScoped cs(*(shared_data_.channel_manager()));
+  ViEChannel* vie_channel = cs.Channel(video_channel);
+  if (!vie_channel) {
+    WEBRTC_TRACE(kTraceError,
+                 kTraceVideo,
+                 ViEId(shared_data_.instance_id()),
+                 "%s: channel %d doesn't exist",
+                 __FUNCTION__,
+                 video_channel);
+    shared_data_.SetLastError(kViEBaseInvalidChannelId);
+    return -1;
+  }
+  ViEEncoder* vie_encoder = cs.Encoder(video_channel);
+  assert(vie_encoder);
+
+  ViEInputManagerScoped is(*(shared_data_.input_manager()));
+  ViEFrameProviderBase* provider = is.FrameProvider(vie_encoder);
+  if (provider) {
+    ViECapturer* capturer = is.Capture(provider->Id());
+    if (capturer) {
+      capturer->CpuOveruseMeasures(capture_jitter_ms, avg_encode_time_ms);
+      return 0;
+    }
+  }
+  return -1;
+}
+
 int ViEBaseImpl::CreateChannel(int& video_channel) {  // NOLINT
   WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(shared_data_.instance_id()),
                "%s", __FUNCTION__);
@@ -347,7 +377,7 @@
 
   // Add WebRTC Version.
   std::stringstream version_stream;
-  version_stream << "VideoEngine 3.46.0" << std::endl;
+  version_stream << "VideoEngine 3.47.0" << std::endl;
 
   // Add build info.
   version_stream << "Build: svn:" << WEBRTC_SVNREVISION << " " << BUILDINFO
diff --git a/video_engine/vie_base_impl.h b/video_engine/vie_base_impl.h
index fca63f4..1411caf 100644
--- a/video_engine/vie_base_impl.h
+++ b/video_engine/vie_base_impl.h
@@ -33,6 +33,9 @@
   virtual int SetVoiceEngine(VoiceEngine* voice_engine);
   virtual int RegisterCpuOveruseObserver(int channel,
                                          CpuOveruseObserver* observer);
+  virtual int CpuOveruseMeasures(int channel,
+                                 int* capture_jitter_ms,
+                                 int* avg_encode_time_ms);
   virtual int CreateChannel(int& video_channel);  // NOLINT
   virtual int CreateChannel(int& video_channel,  // NOLINT
                             int original_channel);
diff --git a/video_engine/vie_capturer.cc b/video_engine/vie_capturer.cc
index e380bc3..d7e33e7 100644
--- a/video_engine/vie_capturer.cc
+++ b/video_engine/vie_capturer.cc
@@ -43,6 +43,7 @@
       external_capture_module_(NULL),
       module_process_thread_(module_process_thread),
       capture_id_(capture_id),
+      incoming_frame_cs_(CriticalSectionWrapper::CreateCriticalSection()),
       capture_thread_(*ThreadWrapper::CreateThread(ViECaptureThreadFunction,
                                                    this, kHighPriority,
                                                    "ViECaptureThread")),
@@ -266,6 +267,12 @@
   overuse_detector_->SetObserver(observer);
 }
 
+void ViECapturer::CpuOveruseMeasures(int* capture_jitter_ms,
+                                     int* avg_encode_time_ms) const {
+  *capture_jitter_ms = overuse_detector_->last_capture_jitter_ms();
+  *avg_encode_time_ms = overuse_detector_->avg_encode_time_ms();
+}
+
 int32_t ViECapturer::SetCaptureDelay(int32_t delay_ms) {
   return capture_module_->SetCaptureDelay(delay_ms);
 }
@@ -323,17 +330,32 @@
     return -1;
   }
 
-  VideoFrameI420 frame;
-  frame.width = video_frame.width;
-  frame.height = video_frame.height;
-  frame.y_plane = video_frame.y_plane;
-  frame.u_plane = video_frame.u_plane;
-  frame.v_plane = video_frame.v_plane;
-  frame.y_pitch = video_frame.y_pitch;
-  frame.u_pitch = video_frame.u_pitch;
-  frame.v_pitch = video_frame.v_pitch;
+  int size_y = video_frame.height * video_frame.y_pitch;
+  int size_u = video_frame.u_pitch * ((video_frame.height + 1) / 2);
+  int size_v = video_frame.v_pitch * ((video_frame.height + 1) / 2);
+  CriticalSectionScoped cs(incoming_frame_cs_.get());
+  int ret = incoming_frame_.CreateFrame(size_y,
+                                       video_frame.y_plane,
+                                       size_u,
+                                       video_frame.u_plane,
+                                       size_v,
+                                       video_frame.v_plane,
+                                       video_frame.width,
+                                       video_frame.height,
+                                       video_frame.y_pitch,
+                                       video_frame.u_pitch,
+                                       video_frame.v_pitch);
 
-  return external_capture_module_->IncomingFrameI420(frame, capture_time);
+  if (ret < 0) {
+    WEBRTC_TRACE(kTraceError,
+                 kTraceVideo,
+                 ViEId(engine_id_, capture_id_),
+                 "Failed to create I420VideoFrame");
+    return -1;
+  }
+
+  return external_capture_module_->IncomingI420VideoFrame(&incoming_frame_,
+                                                          capture_time);
 }
 
 void ViECapturer::OnIncomingCapturedFrame(const int32_t capture_id,
@@ -512,8 +534,10 @@
 
 bool ViECapturer::ViECaptureProcess() {
   if (capture_event_.Wait(kThreadWaitTimeMs) == kEventSignaled) {
+    int64_t encode_start_time = -1;
     deliver_cs_->Enter();
     if (SwapCapturedAndDeliverFrameIfAvailable()) {
+      encode_start_time = Clock::GetRealTimeClock()->TimeInMilliseconds();
       DeliverI420Frame(&deliver_frame_);
     }
     deliver_cs_->Leave();
@@ -524,6 +548,11 @@
         reported_brightness_level_ = current_brightness_level_;
       }
     }
+    // Update the overuse detector with the duration.
+    if (encode_start_time != -1) {
+      overuse_detector_->FrameEncoded(
+          Clock::GetRealTimeClock()->TimeInMilliseconds() - encode_start_time);
+    }
   }
   // We're done!
   return true;
diff --git a/video_engine/vie_capturer.h b/video_engine/vie_capturer.h
index 59c59b9..e47700f 100644
--- a/video_engine/vie_capturer.h
+++ b/video_engine/vie_capturer.h
@@ -104,6 +104,9 @@
 
   void RegisterCpuOveruseObserver(CpuOveruseObserver* observer);
 
+  void CpuOveruseMeasures(int* capture_jitter_ms,
+                          int* avg_encode_time_ms) const;
+
  protected:
   ViECapturer(int capture_id,
               int engine_id,
@@ -154,6 +157,10 @@
   ProcessThread& module_process_thread_;
   const int capture_id_;
 
+  // Frame used in IncomingFrameI420.
+  scoped_ptr<CriticalSectionWrapper> incoming_frame_cs_;
+  I420VideoFrame incoming_frame_;
+
   // Capture thread.
   ThreadWrapper& capture_thread_;
   EventWrapper& capture_event_;
@@ -178,8 +185,6 @@
 
   CaptureCapability requested_capability_;
 
-  I420VideoFrame capture_device_image_;
-
   scoped_ptr<OveruseFrameDetector> overuse_detector_;
 };
 
diff --git a/video_engine/vie_channel.cc b/video_engine/vie_channel.cc
index fcb15e1..4a85a18 100644
--- a/video_engine/vie_channel.cc
+++ b/video_engine/vie_channel.cc
@@ -62,7 +62,7 @@
                        RtcpIntraFrameObserver* intra_frame_observer,
                        RtcpBandwidthObserver* bandwidth_observer,
                        RemoteBitrateEstimator* remote_bitrate_estimator,
-                       RtcpRttObserver* rtt_observer,
+                       RtcpRttStats* rtt_stats,
                        PacedSender* paced_sender,
                        RtpRtcp* default_rtp_rtcp,
                        bool sender)
@@ -85,7 +85,7 @@
       rtp_observer_(NULL),
       rtcp_observer_(NULL),
       intra_frame_observer_(intra_frame_observer),
-      rtt_observer_(rtt_observer),
+      rtt_stats_(rtt_stats),
       paced_sender_(paced_sender),
       bandwidth_observer_(bandwidth_observer),
       send_timestamp_extension_id_(kInvalidRtpExtensionId),
@@ -115,7 +115,7 @@
   configuration.rtcp_feedback = this;
   configuration.intra_frame_callback = intra_frame_observer;
   configuration.bandwidth_callback = bandwidth_observer;
-  configuration.rtt_observer = rtt_observer;
+  configuration.rtt_stats = rtt_stats;
   configuration.remote_bitrate_estimator = remote_bitrate_estimator;
   configuration.paced_sender = paced_sender;
   configuration.receive_statistics = vie_receiver_.GetReceiveStatistics();
@@ -319,7 +319,7 @@
       configuration.outgoing_transport = &vie_sender_;
       configuration.intra_frame_callback = intra_frame_observer_;
       configuration.bandwidth_callback = bandwidth_observer_.get();
-      configuration.rtt_observer = rtt_observer_;
+      configuration.rtt_stats = rtt_stats_;
       configuration.paced_sender = paced_sender_;
 
       RtpRtcp* rtp_rtcp = RtpRtcp::CreateRtpRtcp(configuration);
@@ -406,6 +406,7 @@
         rtp_rtcp->DeregisterSendRtpHeaderExtension(
             kRtpExtensionAbsoluteSendTime);
       }
+      rtp_rtcp->SetRtcpXrRrtrStatus(rtp_rtcp_->RtcpXrRrtrStatus());
     }
     // |RegisterSimulcastRtpRtcpModules| resets all old weak pointers and old
     // modules can be deleted after this step.
@@ -922,6 +923,15 @@
   return receive_absolute_send_time_enabled_;
 }
 
+void ViEChannel::SetRtcpXrRrtrStatus(bool enable) {
+  CriticalSectionScoped cs(rtp_rtcp_cs_.get());
+  rtp_rtcp_->SetRtcpXrRrtrStatus(enable);
+  for (std::list<RtpRtcp*>::iterator it = simulcast_rtp_rtcp_.begin();
+       it != simulcast_rtp_rtcp_.end(); it++) {
+    (*it)->SetRtcpXrRrtrStatus(enable);
+  }
+}
+
 void ViEChannel::SetTransmissionSmoothingStatus(bool enable) {
   assert(paced_sender_ && "No paced sender registered.");
   paced_sender_->SetStatus(enable);
@@ -1860,6 +1870,12 @@
   pre_render_callback_ = pre_render_callback;
 }
 
+void ViEChannel::RegisterPreDecodeImageCallback(
+    EncodedImageCallback* pre_decode_callback) {
+  CriticalSectionScoped cs(callback_cs_.get());
+  vcm_.RegisterPreDecodeImageCallback(pre_decode_callback);
+}
+
 void ViEChannel::OnApplicationDataReceived(const int32_t id,
                                            const uint8_t sub_type,
                                            const uint32_t name,
diff --git a/video_engine/vie_channel.h b/video_engine/vie_channel.h
index f774311..f6bc3fc 100644
--- a/video_engine/vie_channel.h
+++ b/video_engine/vie_channel.h
@@ -33,11 +33,12 @@
 class ChannelStatsObserver;
 class Config;
 class CriticalSectionWrapper;
+class EncodedImageCallback;
 class Encryption;
 class I420FrameCallback;
 class PacedSender;
 class ProcessThread;
-class RtcpRttObserver;
+class RtcpRttStats;
 class RtpRtcp;
 class ThreadWrapper;
 class ViEDecoderObserver;
@@ -69,7 +70,7 @@
              RtcpIntraFrameObserver* intra_frame_observer,
              RtcpBandwidthObserver* bandwidth_observer,
              RemoteBitrateEstimator* remote_bitrate_estimator,
-             RtcpRttObserver* rtt_observer,
+             RtcpRttStats* rtt_stats,
              PacedSender* paced_sender,
              RtpRtcp* default_rtp_rtcp,
              bool sender);
@@ -125,6 +126,7 @@
   int SetSendAbsoluteSendTimeStatus(bool enable, int id);
   int SetReceiveAbsoluteSendTimeStatus(bool enable, int id);
   bool GetReceiveAbsoluteSendTimeStatus() const;
+  void SetRtcpXrRrtrStatus(bool enable);
   void SetTransmissionSmoothingStatus(bool enable);
   int32_t EnableTMMBR(const bool enable);
   int32_t EnableKeyFrameRequestCallback(const bool enable);
@@ -319,8 +321,10 @@
 
   int32_t RegisterEffectFilter(ViEEffectFilter* effect_filter);
 
-  // New-style callback, used by VideoReceiveStream.
+  // New-style callbacks, used by VideoReceiveStream.
   void RegisterPreRenderCallback(I420FrameCallback* pre_render_callback);
+  void RegisterPreDecodeImageCallback(
+      EncodedImageCallback* pre_decode_callback);
 
  protected:
   static bool ChannelDecodeThreadFunction(void* obj);
@@ -370,7 +374,7 @@
   ViERTPObserver* rtp_observer_;
   ViERTCPObserver* rtcp_observer_;
   RtcpIntraFrameObserver* intra_frame_observer_;
-  RtcpRttObserver* rtt_observer_;
+  RtcpRttStats* rtt_stats_;
   PacedSender* paced_sender_;
 
   scoped_ptr<RtcpBandwidthObserver> bandwidth_observer_;
diff --git a/video_engine/vie_channel_manager.cc b/video_engine/vie_channel_manager.cc
index 02b991c..5fdbde5 100644
--- a/video_engine/vie_channel_manager.cc
+++ b/video_engine/vie_channel_manager.cc
@@ -104,12 +104,12 @@
       group->GetRemoteBitrateEstimator();
   EncoderStateFeedback* encoder_state_feedback =
       group->GetEncoderStateFeedback();
-  RtcpRttObserver* rtcp_rtt_observer =
-      group->GetCallStats()->rtcp_rtt_observer();
+  RtcpRttStats* rtcp_rtt_stats =
+      group->GetCallStats()->rtcp_rtt_stats();
 
   if (!(vie_encoder->Init() &&
         CreateChannelObject(new_channel_id, vie_encoder, bandwidth_observer,
-                            remote_bitrate_estimator, rtcp_rtt_observer,
+                            remote_bitrate_estimator, rtcp_rtt_stats,
                             encoder_state_feedback->GetRtcpIntraFrameObserver(),
                             true))) {
     delete vie_encoder;
@@ -156,8 +156,8 @@
       channel_group->GetRemoteBitrateEstimator();
   EncoderStateFeedback* encoder_state_feedback =
       channel_group->GetEncoderStateFeedback();
-    RtcpRttObserver* rtcp_rtt_observer =
-        channel_group->GetCallStats()->rtcp_rtt_observer();
+    RtcpRttStats* rtcp_rtt_stats =
+        channel_group->GetCallStats()->rtcp_rtt_stats();
 
   ViEEncoder* vie_encoder = NULL;
   if (sender) {
@@ -172,7 +172,7 @@
             vie_encoder,
             bandwidth_observer,
             remote_bitrate_estimator,
-            rtcp_rtt_observer,
+            rtcp_rtt_stats,
             encoder_state_feedback->GetRtcpIntraFrameObserver(),
             sender))) {
       delete vie_encoder;
@@ -191,7 +191,7 @@
         vie_encoder,
         bandwidth_observer,
         remote_bitrate_estimator,
-        rtcp_rtt_observer,
+        rtcp_rtt_stats,
         encoder_state_feedback->GetRtcpIntraFrameObserver(),
         sender)) {
       vie_encoder = NULL;
@@ -421,7 +421,7 @@
     ViEEncoder* vie_encoder,
     RtcpBandwidthObserver* bandwidth_observer,
     RemoteBitrateEstimator* remote_bitrate_estimator,
-    RtcpRttObserver* rtcp_rtt_observer,
+    RtcpRttStats* rtcp_rtt_stats,
     RtcpIntraFrameObserver* intra_frame_observer,
     bool sender) {
   PacedSender* paced_sender = vie_encoder->GetPacedSender();
@@ -436,7 +436,7 @@
                                            intra_frame_observer,
                                            bandwidth_observer,
                                            remote_bitrate_estimator,
-                                           rtcp_rtt_observer,
+                                           rtcp_rtt_stats,
                                            paced_sender,
                                            send_rtp_rtcp_module,
                                            sender);
diff --git a/video_engine/vie_channel_manager.h b/video_engine/vie_channel_manager.h
index 5ded956..9776435 100644
--- a/video_engine/vie_channel_manager.h
+++ b/video_engine/vie_channel_manager.h
@@ -28,7 +28,7 @@
 class Config;
 class CriticalSectionWrapper;
 class ProcessThread;
-class RtcpRttObserver;
+class RtcpRttStats;
 class ViEChannel;
 class ViEEncoder;
 class VoEVideoSync;
@@ -89,7 +89,7 @@
                            ViEEncoder* vie_encoder,
                            RtcpBandwidthObserver* bandwidth_observer,
                            RemoteBitrateEstimator* remote_bitrate_estimator,
-                           RtcpRttObserver* rtcp_rtt_observer,
+                           RtcpRttStats* rtcp_rtt_stats,
                            RtcpIntraFrameObserver* intra_frame_observer,
                            bool sender);
 
diff --git a/video_engine/vie_codec_impl.cc b/video_engine/vie_codec_impl.cc
index 4d927b5..364862f 100644
--- a/video_engine/vie_codec_impl.cc
+++ b/video_engine/vie_codec_impl.cc
@@ -715,7 +715,7 @@
   return vie_encoder->StopDebugRecording();
 }
 
-void ViECodecImpl::EnableAutoMuting(int video_channel) {
+void ViECodecImpl::SuspendBelowMinBitrate(int video_channel) {
   ViEChannelManagerScoped cs(*(shared_data_->channel_manager()));
   ViEEncoder* vie_encoder = cs.Encoder(video_channel);
   if (!vie_encoder) {
@@ -724,7 +724,18 @@
                  "%s: No encoder %d", __FUNCTION__, video_channel);
     return;
   }
-  return vie_encoder->EnableAutoMuting();
+  vie_encoder->SuspendBelowMinBitrate();
+  ViEChannel* vie_channel = cs.Channel(video_channel);
+  if (!vie_channel) {
+    WEBRTC_TRACE(kTraceError, kTraceVideo,
+                 ViEId(shared_data_->instance_id(), video_channel),
+                 "%s: No channel %d", __FUNCTION__, video_channel);
+    return;
+  }
+  // Must enable pacing when enabling SuspendBelowMinBitrate. Otherwise, no
+  // padding will be sent when the video is suspended so the video will be
+  // unable to recover.
+  vie_channel->SetTransmissionSmoothingStatus(true);
 }
 
 bool ViECodecImpl::CodecValid(const VideoCodec& video_codec) {
diff --git a/video_engine/vie_codec_impl.h b/video_engine/vie_codec_impl.h
index 8bcac41..372ffc9 100644
--- a/video_engine/vie_codec_impl.h
+++ b/video_engine/vie_codec_impl.h
@@ -70,7 +70,7 @@
   virtual int StartDebugRecording(int video_channel,
                                   const char* file_name_utf8);
   virtual int StopDebugRecording(int video_channel);
-  virtual void EnableAutoMuting(int video_channel);
+  virtual void SuspendBelowMinBitrate(int video_channel);
 
  protected:
   explicit ViECodecImpl(ViESharedData* shared_data);
diff --git a/video_engine/vie_encoder.cc b/video_engine/vie_encoder.cc
index 889842e..f226ada 100644
--- a/video_engine/vie_encoder.cc
+++ b/video_engine/vie_encoder.cc
@@ -14,6 +14,7 @@
 
 #include <algorithm>
 
+#include "webrtc/common_video/interface/video_image.h"
 #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
 #include "webrtc/modules/pacing/include/paced_sender.h"
 #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h"
@@ -21,6 +22,7 @@
 #include "webrtc/modules/video_coding/codecs/interface/video_codec_interface.h"
 #include "webrtc/modules/video_coding/main/interface/video_coding.h"
 #include "webrtc/modules/video_coding/main/interface/video_coding_defines.h"
+#include "webrtc/modules/video_coding/main/source/encoded_frame.h"
 #include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
 #include "webrtc/system_wrappers/interface/logging.h"
 #include "webrtc/system_wrappers/interface/tick_util.h"
@@ -163,7 +165,7 @@
     has_received_rpsi_(false),
     picture_id_rpsi_(0),
     qm_callback_(NULL),
-    video_auto_muted_(false),
+    video_suspended_(false),
     pre_encode_callback_(NULL) {
   WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceVideo,
                ViEId(engine_id, channel_id),
@@ -525,7 +527,12 @@
 }
 
 int ViEEncoder::TimeToSendPadding(int bytes) {
-  if (send_padding_) {
+  bool send_padding;
+  {
+    CriticalSectionScoped cs(data_cs_.get());
+    send_padding = send_padding_ || video_suspended_;
+  }
+  if (send_padding) {
     return default_rtp_rtcp_->TimeToSendPadding(bytes);
   }
   return 0;
@@ -839,10 +846,15 @@
     // Disable external frame-droppers.
     vcm_.EnableFrameDropper(false);
     vpm_.EnableTemporalDecimation(false);
+    // We don't put any limits on the pacer queue when running in buffered mode
+    // since the encoder will be paused if the queue grow too large.
+    paced_sender_->set_max_queue_length_ms(-1);
   } else {
     // Real-time mode - enable frame droppers.
     vpm_.EnableTemporalDecimation(true);
     vcm_.EnableFrameDropper(true);
+    paced_sender_->set_max_queue_length_ms(
+        PacedSender::kDefaultMaxQueueLengthMs);
   }
 }
 
@@ -1050,7 +1062,7 @@
                __FUNCTION__, bitrate_bps, fraction_lost, round_trip_time_ms);
 
   vcm_.SetChannelParameters(bitrate_bps, fraction_lost, round_trip_time_ms);
-  bool video_is_muted = vcm_.VideoMuted();
+  bool video_is_suspended = vcm_.VideoSuspended();
   int bitrate_kbps = bitrate_bps / 1000;
   VideoCodec send_codec;
   if (vcm_.SendCodec(&send_codec) != 0) {
@@ -1087,10 +1099,10 @@
       pad_up_to_bitrate_kbps += stream_configs[i].targetBitrate;
     }
   }
-  if (video_is_muted || send_codec.numberOfSimulcastStreams > 1) {
+  if (video_is_suspended || send_codec.numberOfSimulcastStreams > 1) {
     pad_up_to_bitrate_kbps = std::min(bitrate_kbps, pad_up_to_bitrate_kbps);
   } else {
-    // Disable padding if only sending one stream and video isn't muted.
+    // Disable padding if only sending one stream and video isn't suspended.
     pad_up_to_bitrate_kbps = 0;
   }
 
@@ -1107,16 +1119,19 @@
                                max_padding_bitrate_kbps,
                                pad_up_to_bitrate_kbps);
   default_rtp_rtcp_->SetTargetSendBitrate(stream_bitrates);
-  if (video_is_muted != video_auto_muted_) {
-    // State changed now. Send callback to inform about that.
-    video_auto_muted_ = video_is_muted;
-    if (codec_observer_) {
-      WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo,
-                   ViEId(engine_id_, channel_id_),
-                   "%s: video_auto_muted_ changed to %i",
-                   __FUNCTION__, video_auto_muted_);
-      codec_observer_->VideoAutoMuted(channel_id_, video_auto_muted_);
-    }
+  {
+    CriticalSectionScoped cs(data_cs_.get());
+    if (video_suspended_ == video_is_suspended)
+      return;
+    video_suspended_ = video_is_suspended;
+  }
+  // State changed, inform codec observer.
+  if (codec_observer_) {
+    WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo,
+                 ViEId(engine_id_, channel_id_),
+                 "%s: video_suspended_ changed to %i",
+                 __FUNCTION__, video_is_suspended);
+    codec_observer_->SuspendChange(channel_id_, video_is_suspended);
   }
 }
 
@@ -1159,8 +1174,8 @@
   return vcm_.StopDebugRecording();
 }
 
-void ViEEncoder::EnableAutoMuting() {
-  vcm_.EnableAutoMuting();
+void ViEEncoder::SuspendBelowMinBitrate() {
+  vcm_.SuspendBelowMinBitrate();
   bitrate_controller_->EnforceMinBitrate(false);
 }
 
@@ -1175,6 +1190,15 @@
   pre_encode_callback_ = NULL;
 }
 
+void ViEEncoder::RegisterPostEncodeImageCallback(
+      EncodedImageCallback* post_encode_callback) {
+  vcm_.RegisterPostEncodeImageCallback(post_encode_callback);
+}
+
+void ViEEncoder::DeRegisterPostEncodeImageCallback() {
+  vcm_.RegisterPostEncodeImageCallback(NULL);
+}
+
 QMVideoSettingsCallback::QMVideoSettingsCallback(VideoProcessingModule* vpm)
     : vpm_(vpm) {
 }
diff --git a/video_engine/vie_encoder.h b/video_engine/vie_encoder.h
index f80699c..cd8328e 100644
--- a/video_engine/vie_encoder.h
+++ b/video_engine/vie_encoder.h
@@ -29,6 +29,7 @@
 
 class Config;
 class CriticalSectionWrapper;
+class EncodedImageCallback;
 class PacedSender;
 class ProcessThread;
 class QMVideoSettingsCallback;
@@ -163,16 +164,20 @@
   // Disables recording of debugging information.
   virtual int StopDebugRecording();
 
-  // Enables AutoMuter to turn off video when the rate drops below
+  // Lets the sender suspend video when the rate drops below
   // |threshold_bps|, and turns back on when the rate goes back up above
   // |threshold_bps| + |window_bps|.
-  virtual void EnableAutoMuting();
+  virtual void SuspendBelowMinBitrate();
 
-  // New-style callback, used by VideoSendStream.
+  // New-style callbacks, used by VideoSendStream.
   void RegisterPreEncodeCallback(I420FrameCallback* pre_encode_callback);
   void DeRegisterPreEncodeCallback();
+  void RegisterPostEncodeImageCallback(
+        EncodedImageCallback* post_encode_callback);
+  void DeRegisterPostEncodeImageCallback();
 
   int channel_id() const { return channel_id_; }
+
  protected:
   // Called by BitrateObserver.
   void OnNetworkChanged(const uint32_t bitrate_bps,
@@ -226,7 +231,7 @@
 
   // Quality modes callback
   QMVideoSettingsCallback* qm_callback_;
-  bool video_auto_muted_;
+  bool video_suspended_;
   I420FrameCallback* pre_encode_callback_;
 };
 
diff --git a/video_engine/vie_image_process_impl.cc b/video_engine/vie_image_process_impl.cc
index 92fe697..b10e774 100644
--- a/video_engine/vie_image_process_impl.cc
+++ b/video_engine/vie_image_process_impl.cc
@@ -284,6 +284,36 @@
   vie_encoder->DeRegisterPreEncodeCallback();
 }
 
+void ViEImageProcessImpl::RegisterPostEncodeImageCallback(
+    int video_channel,
+    EncodedImageCallback* post_encode_callback) {
+  ViEChannelManagerScoped cs(*(shared_data_->channel_manager()));
+  ViEEncoder* vie_encoder = cs.Encoder(video_channel);
+  assert(vie_encoder != NULL);
+  vie_encoder->RegisterPostEncodeImageCallback(post_encode_callback);
+}
+
+void ViEImageProcessImpl::DeRegisterPostEncodeCallback(int video_channel) {
+  ViEChannelManagerScoped cs(*(shared_data_->channel_manager()));
+  ViEEncoder* vie_encoder = cs.Encoder(video_channel);
+  assert(vie_encoder != NULL);
+  vie_encoder->DeRegisterPostEncodeImageCallback();
+}
+
+void ViEImageProcessImpl::RegisterPreDecodeImageCallback(
+    int video_channel,
+    EncodedImageCallback* pre_decode_callback) {
+  ViEChannelManagerScoped cs(*(shared_data_->channel_manager()));
+  ViEChannel* channel = cs.Channel(video_channel);
+  channel->RegisterPreDecodeImageCallback(pre_decode_callback);
+}
+
+void ViEImageProcessImpl::DeRegisterPreDecodeCallback(int video_channel) {
+  ViEChannelManagerScoped cs(*(shared_data_->channel_manager()));
+  ViEChannel* channel = cs.Channel(video_channel);
+  channel->RegisterPreDecodeImageCallback(NULL);
+}
+
 void ViEImageProcessImpl::RegisterPreRenderCallback(
     int video_channel,
     I420FrameCallback* pre_render_callback) {
diff --git a/video_engine/vie_image_process_impl.h b/video_engine/vie_image_process_impl.h
index 9b43e7e..74a7ff0 100644
--- a/video_engine/vie_image_process_impl.h
+++ b/video_engine/vie_image_process_impl.h
@@ -43,6 +43,16 @@
       I420FrameCallback* pre_encode_callback) OVERRIDE;
   virtual void DeRegisterPreEncodeCallback(int video_channel) OVERRIDE;
 
+  virtual void RegisterPostEncodeImageCallback(
+      int video_channel,
+      EncodedImageCallback* post_encode_callback) OVERRIDE;
+  virtual void DeRegisterPostEncodeCallback(int video_channel) OVERRIDE;
+
+  virtual void RegisterPreDecodeImageCallback(
+        int video_channel,
+        EncodedImageCallback* post_encode_callback) OVERRIDE;
+  virtual void DeRegisterPreDecodeCallback(int video_channel) OVERRIDE;
+
   virtual void RegisterPreRenderCallback(
       int video_channel,
       I420FrameCallback* pre_render_callback) OVERRIDE;
diff --git a/video_engine/vie_render_impl.cc b/video_engine/vie_render_impl.cc
index 507e05a..b7b7c94 100644
--- a/video_engine/vie_render_impl.cc
+++ b/video_engine/vie_render_impl.cc
@@ -394,4 +394,35 @@
   }
 }
 
+int ViERenderImpl::AddRenderCallback(int render_id,
+                                     VideoRenderCallback* callback) {
+  if (render_id < kViEChannelIdBase || render_id > kViEChannelIdMax)
+    return -1;
+  // This is a channel.
+  ViEChannelManagerScoped cm(*(shared_data_->channel_manager()));
+  ViEFrameProviderBase* frame_provider = cm.Channel(render_id);
+  if (!frame_provider) {
+    WEBRTC_TRACE(kTraceError,
+                 kTraceVideo,
+                 ViEId(shared_data_->instance_id()),
+                 "%s: FrameProvider id %d doesn't exist",
+                 __FUNCTION__,
+                 render_id);
+    shared_data_->SetLastError(kViERenderInvalidRenderId);
+    return -1;
+  }
+  ViERenderer* renderer = shared_data_->render_manager()->AddRenderStream(
+      render_id, NULL, 0, 0.0f, 0.0f, 1.0f, 1.0f);
+  if (!renderer) {
+    shared_data_->SetLastError(kViERenderUnknownError);
+    return -1;
+  }
+  if (renderer->SetVideoRenderCallback(render_id, callback) != 0) {
+    shared_data_->SetLastError(kViERenderUnknownError);
+    return -1;
+  }
+
+  return frame_provider->RegisterFrameCallback(render_id, renderer);
+}
+
 }  // namespace webrtc
diff --git a/video_engine/vie_render_impl.h b/video_engine/vie_render_impl.h
index c419794..370a7a3 100644
--- a/video_engine/vie_render_impl.h
+++ b/video_engine/vie_render_impl.h
@@ -45,6 +45,8 @@
                                  const bool mirror_yaxis);
   virtual int AddRenderer(const int render_id, RawVideoType video_input_format,
                           ExternalRenderer* renderer);
+  virtual int AddRenderCallback(int render_id,
+                                VideoRenderCallback* callback) OVERRIDE;
 
  protected:
   explicit ViERenderImpl(ViESharedData* shared_data);
diff --git a/video_engine/vie_renderer.cc b/video_engine/vie_renderer.cc
index 35c68aa..e18ebe9 100644
--- a/video_engine/vie_renderer.cc
+++ b/video_engine/vie_renderer.cc
@@ -104,6 +104,11 @@
                                                   incoming_external_callback_);
 }
 
+int32_t ViERenderer::SetVideoRenderCallback(int32_t render_id,
+                                            VideoRenderCallback* callback) {
+  return render_module_.AddExternalRenderCallback(render_id, callback);
+}
+
 ViERenderer::ViERenderer(const int32_t render_id,
                          const int32_t engine_id,
                          VideoRender& render_module,
diff --git a/video_engine/vie_renderer.h b/video_engine/vie_renderer.h
index 4f73e51..907735c 100644
--- a/video_engine/vie_renderer.h
+++ b/video_engine/vie_renderer.h
@@ -86,6 +86,9 @@
                               RawVideoType video_input_format,
                               ExternalRenderer* external_renderer);
 
+  int32_t SetVideoRenderCallback(const int32_t render_id,
+                                 VideoRenderCallback* callback);
+
  private:
   ViERenderer(const int32_t render_id, const int32_t engine_id,
                 VideoRender& render_module,
diff --git a/video_engine/vie_rtp_rtcp_impl.cc b/video_engine/vie_rtp_rtcp_impl.cc
index ecfa1b6..7d3c8ad 100644
--- a/video_engine/vie_rtp_rtcp_impl.cc
+++ b/video_engine/vie_rtp_rtcp_impl.cc
@@ -804,6 +804,25 @@
   return 0;
 }
 
+int ViERTP_RTCPImpl::SetRtcpXrRrtrStatus(int video_channel, bool enable) {
+  WEBRTC_TRACE(kTraceApiCall, kTraceVideo,
+               ViEId(shared_data_->instance_id(), video_channel),
+               "ViERTP_RTCPImpl::SetRtcpXrRrtrStatus(%d, %d)",
+               video_channel, enable);
+
+  ViEChannelManagerScoped cs(*(shared_data_->channel_manager()));
+  ViEChannel* vie_channel = cs.Channel(video_channel);
+  if (!vie_channel) {
+    WEBRTC_TRACE(kTraceError, kTraceVideo,
+                 ViEId(shared_data_->instance_id(), video_channel),
+                 "%s: Channel %d doesn't exist", __FUNCTION__, video_channel);
+    shared_data_->SetLastError(kViERtpRtcpInvalidChannelId);
+    return -1;
+  }
+  vie_channel->SetRtcpXrRrtrStatus(enable);
+  return 0;
+}
+
 int ViERTP_RTCPImpl::SetTransmissionSmoothingStatus(int video_channel,
                                                     bool enable) {
   WEBRTC_TRACE(kTraceApiCall, kTraceVideo,
@@ -823,11 +842,8 @@
   return 0;
 }
 
-int ViERTP_RTCPImpl::GetReceivedRTCPStatistics(const int video_channel,
-                                               uint16_t& fraction_lost,
-                                               unsigned int& cumulative_lost,
-                                               unsigned int& extended_max,
-                                               unsigned int& jitter,
+int ViERTP_RTCPImpl::GetReceiveChannelRtcpStatistics(const int video_channel,
+                                               RtcpStatistics& basic_stats,
                                                int& rtt_ms) const {
   WEBRTC_TRACE(kTraceApiCall, kTraceVideo,
                ViEId(shared_data_->instance_id(), video_channel),
@@ -841,22 +857,22 @@
     shared_data_->SetLastError(kViERtpRtcpInvalidChannelId);
     return -1;
   }
-  if (vie_channel->GetReceivedRtcpStatistics(&fraction_lost,
-                                             &cumulative_lost,
-                                             &extended_max,
-                                             &jitter,
-                                             &rtt_ms) != 0) {
+
+  // TODO(sprang): Clean this up when stats struct is propagated all the way.
+  uint16_t frac_loss;
+  if (vie_channel->GetReceivedRtcpStatistics(
+      &frac_loss, &basic_stats.cumulative_lost,
+      &basic_stats.extended_max_sequence_number, &basic_stats.jitter,
+      &rtt_ms) != 0) {
+    basic_stats.fraction_lost = frac_loss;
     shared_data_->SetLastError(kViERtpRtcpUnknownError);
     return -1;
   }
   return 0;
 }
 
-int ViERTP_RTCPImpl::GetSentRTCPStatistics(const int video_channel,
-                                           uint16_t& fraction_lost,
-                                           unsigned int& cumulative_lost,
-                                           unsigned int& extended_max,
-                                           unsigned int& jitter,
+int ViERTP_RTCPImpl::GetSendChannelRtcpStatistics(const int video_channel,
+                                           RtcpStatistics& basic_stats,
                                            int& rtt_ms) const {
   WEBRTC_TRACE(kTraceApiCall, kTraceVideo,
                ViEId(shared_data_->instance_id(), video_channel),
@@ -871,20 +887,22 @@
     return -1;
   }
 
-  if (vie_channel->GetSendRtcpStatistics(&fraction_lost, &cumulative_lost,
-                                         &extended_max, &jitter,
-                                         &rtt_ms) != 0) {
+  // TODO(sprang): Clean this up when stats struct is propagated all the way.
+  uint16_t frac_loss;
+  if (vie_channel->GetSendRtcpStatistics(
+      &frac_loss, &basic_stats.cumulative_lost,
+      &basic_stats.extended_max_sequence_number, &basic_stats.jitter,
+      &rtt_ms) != 0) {
+    basic_stats.fraction_lost = frac_loss;
     shared_data_->SetLastError(kViERtpRtcpUnknownError);
     return -1;
   }
   return 0;
 }
 
-int ViERTP_RTCPImpl::GetRTPStatistics(const int video_channel,
-                                      unsigned int& bytes_sent,
-                                      unsigned int& packets_sent,
-                                      unsigned int& bytes_received,
-                                      unsigned int& packets_received) const {
+int ViERTP_RTCPImpl::GetRtpStatistics(const int video_channel,
+                                      StreamDataCounters& sent,
+                                      StreamDataCounters& received) const {
   WEBRTC_TRACE(kTraceApiCall, kTraceVideo,
                ViEId(shared_data_->instance_id(), video_channel),
                "%s(channel: %d)", __FUNCTION__, video_channel);
@@ -897,10 +915,10 @@
     shared_data_->SetLastError(kViERtpRtcpInvalidChannelId);
     return -1;
   }
-  if (vie_channel->GetRtpStatistics(&bytes_sent,
-                                    &packets_sent,
-                                    &bytes_received,
-                                    &packets_received) != 0) {
+  if (vie_channel->GetRtpStatistics(&sent.bytes,
+                                    &sent.packets,
+                                    &received.bytes,
+                                    &received.packets) != 0) {
     shared_data_->SetLastError(kViERtpRtcpUnknownError);
     return -1;
   }
@@ -1100,4 +1118,76 @@
   return 0;
 }
 
+int ViERTP_RTCPImpl::RegisterSendChannelRtcpStatisticsCallback(
+    int channel, RtcpStatisticsCallback* callback) {
+  // TODO(sprang): Implement
+  return -1;
+}
+
+int ViERTP_RTCPImpl::DeregisterSendChannelRtcpStatisticsCallback(
+    int channel, RtcpStatisticsCallback* callback) {
+  // TODO(sprang): Implement
+  return -1;
+}
+
+int ViERTP_RTCPImpl::RegisterReceiveChannelRtcpStatisticsCallback(
+    int channel, RtcpStatisticsCallback* callback) {
+  // TODO(sprang): Implement
+  return -1;
+}
+
+int ViERTP_RTCPImpl::DeregisterReceiveChannelRtcpStatisticsCallback(
+    int channel, RtcpStatisticsCallback* callback) {
+  // TODO(sprang): Implement
+  return -1;
+}
+
+int ViERTP_RTCPImpl::RegisterSendChannelRtpStatisticsCallback(
+    int channel, StreamDataCountersCallback* callback) {
+  // TODO(sprang): Implement
+  return -1;
+}
+
+int ViERTP_RTCPImpl::DeregisterSendChannelRtpStatisticsCallback(
+    int channel, StreamDataCountersCallback* callback) {
+  // TODO(sprang): Implement
+  return -1;
+}
+
+int ViERTP_RTCPImpl::RegisterReceiveChannelRtpStatisticsCallback(
+    int channel, StreamDataCountersCallback* callback) {
+  // TODO(sprang): Implement
+  return -1;
+}
+
+int ViERTP_RTCPImpl::DeregisterReceiveChannelRtpStatisticsCallback(
+    int channel, StreamDataCountersCallback* callback) {
+  // TODO(sprang): Implement
+  return -1;
+}
+int ViERTP_RTCPImpl::RegisterSendBitrateObserver(
+    int channel, BitrateStatisticsObserver* callback) {
+  // TODO(sprang): Implement
+  return -1;
+}
+
+int ViERTP_RTCPImpl::DeregisterSendBitrateObserver(
+    int channel, BitrateStatisticsObserver* callback) {
+  // TODO(sprang): Implement
+  return -1;
+}
+
+int ViERTP_RTCPImpl::RegisterSendFrameCountObserver(
+    int channel, FrameCountObserver* callback) {
+  // TODO(sprang): Implement
+  return -1;
+}
+
+int ViERTP_RTCPImpl::DeregisterSendFrameCountObserver(
+    int channel, FrameCountObserver* callback) {
+  // TODO(sprang): Implement
+  return -1;
+}
+
+
 }  // namespace webrtc
diff --git a/video_engine/vie_rtp_rtcp_impl.h b/video_engine/vie_rtp_rtcp_impl.h
index 9af4b0c..caabd2c 100644
--- a/video_engine/vie_rtp_rtcp_impl.h
+++ b/video_engine/vie_rtp_rtcp_impl.h
@@ -88,23 +88,17 @@
   virtual int SetReceiveAbsoluteSendTimeStatus(int video_channel,
                                                bool enable,
                                                int id);
+  virtual int SetRtcpXrRrtrStatus(int video_channel, bool enable);
   virtual int SetTransmissionSmoothingStatus(int video_channel, bool enable);
-  virtual int GetReceivedRTCPStatistics(const int video_channel,
-                                        uint16_t& fraction_lost,
-                                        unsigned int& cumulative_lost,
-                                        unsigned int& extended_max,
-                                        unsigned int& jitter,
-                                        int& rtt_ms) const;
-  virtual int GetSentRTCPStatistics(const int video_channel,
-                                    uint16_t& fraction_lost,
-                                    unsigned int& cumulative_lost,
-                                    unsigned int& extended_max,
-                                    unsigned int& jitter, int& rtt_ms) const;
-  virtual int GetRTPStatistics(const int video_channel,
-                               unsigned int& bytes_sent,
-                               unsigned int& packets_sent,
-                               unsigned int& bytes_received,
-                               unsigned int& packets_received) const;
+  virtual int GetReceiveChannelRtcpStatistics(const int video_channel,
+                                              RtcpStatistics& basic_stats,
+                                              int& rtt_ms) const;
+  virtual int GetSendChannelRtcpStatistics(const int video_channel,
+                                           RtcpStatistics& basic_stats,
+                                           int& rtt_ms) const;
+  virtual int GetRtpStatistics(const int video_channel,
+                               StreamDataCounters& sent,
+                               StreamDataCounters& received) const;
   virtual int GetBandwidthUsage(const int video_channel,
                                 unsigned int& total_bitrate_sent,
                                 unsigned int& video_bitrate_sent,
@@ -127,6 +121,31 @@
                                    ViERTCPObserver& observer);
   virtual int DeregisterRTCPObserver(const int video_channel);
 
+  virtual int RegisterSendChannelRtcpStatisticsCallback(
+      int channel, RtcpStatisticsCallback* callback);
+  virtual int DeregisterSendChannelRtcpStatisticsCallback(
+      int channel, RtcpStatisticsCallback* callback);
+  virtual int RegisterReceiveChannelRtcpStatisticsCallback(
+        int channel, RtcpStatisticsCallback* callback);
+    virtual int DeregisterReceiveChannelRtcpStatisticsCallback(
+        int channel, RtcpStatisticsCallback* callback);
+  virtual int RegisterSendChannelRtpStatisticsCallback(
+      int channel, StreamDataCountersCallback* callback);
+  virtual int DeregisterSendChannelRtpStatisticsCallback(
+      int channel, StreamDataCountersCallback* callback);
+  virtual int RegisterReceiveChannelRtpStatisticsCallback(
+      int channel, StreamDataCountersCallback* callback);
+  virtual int DeregisterReceiveChannelRtpStatisticsCallback(
+      int channel, StreamDataCountersCallback* callback);
+  virtual int RegisterSendBitrateObserver(
+      int channel, BitrateStatisticsObserver* callback);
+  virtual int DeregisterSendBitrateObserver(
+      int channel, BitrateStatisticsObserver* callback);
+  virtual int RegisterSendFrameCountObserver(
+      int channel, FrameCountObserver* callback);
+  virtual int DeregisterSendFrameCountObserver(
+      int channel, FrameCountObserver* callback);
+
  protected:
   explicit ViERTP_RTCPImpl(ViESharedData* shared_data);
   virtual ~ViERTP_RTCPImpl();
diff --git a/video_engine_tests.isolate b/video_engine_tests.isolate
index e3d2381..af98afd 100644
--- a/video_engine_tests.isolate
+++ b/video_engine_tests.isolate
@@ -26,6 +26,7 @@
         ],
         'isolate_dependency_tracked': [
           '../DEPS',
+          '../data/voice_engine/audio_long16.pcm',
           '../resources/foreman_cif.yuv',
           '../resources/paris_qcif.yuv',
           '../testing/test_env.py',
diff --git a/video_receive_stream.h b/video_receive_stream.h
index f968c66..8b4f643 100644
--- a/video_receive_stream.h
+++ b/video_receive_stream.h
@@ -164,8 +164,8 @@
     StatsCallback* stats_callback;
   };
 
-  virtual void StartReceive() = 0;
-  virtual void StopReceive() = 0;
+  virtual void StartReceiving() = 0;
+  virtual void StopReceiving() = 0;
 
   // TODO(mflodman) Replace this with callback.
   virtual void GetCurrentReceiveCodec(VideoCodec* receive_codec) = 0;
diff --git a/video_send_stream.h b/video_send_stream.h
index af33b7c..2358042 100644
--- a/video_send_stream.h
+++ b/video_send_stream.h
@@ -23,8 +23,6 @@
 
 class VideoEncoder;
 
-struct SendStreamState;
-
 // Class to deliver captured frame to the video send stream.
 class VideoSendStreamInput {
  public:
@@ -73,7 +71,7 @@
   struct Config {
     Config()
         : pre_encode_callback(NULL),
-          encoded_callback(NULL),
+          post_encode_callback(NULL),
           local_renderer(NULL),
           render_delay_ms(0),
           encoder(NULL),
@@ -81,8 +79,7 @@
           target_delay_ms(0),
           pacing(false),
           stats_callback(NULL),
-          start_state(NULL),
-          auto_mute(false) {}
+          suspend_below_min_bitrate(false) {}
     VideoCodec codec;
 
     static const size_t kDefaultMaxPacketSize = 1500 - 40;  // TCP over IPv4.
@@ -116,7 +113,7 @@
 
     // Called for each encoded frame, e.g. used for file storage. 'NULL'
     // disables the callback.
-    EncodedFrameObserver* encoded_callback;
+    EncodedFrameObserver* post_encode_callback;
 
     // Renderer for local preview. The local renderer will be called even if
     // sending hasn't started. 'NULL' disables local rendering.
@@ -145,28 +142,23 @@
     // Callback for periodically receiving send stats.
     StatsCallback* stats_callback;
 
-    // Set to resume a previously destroyed send stream.
-    SendStreamState* start_state;
-
-    // True if video should be muted when video goes under the minimum video
-    // bitrate. Unless muted, video will be sent at a bitrate higher than
-    // estimated available.
-    bool auto_mute;
+    // True if the stream should be suspended when the available bitrate fall
+    // below the minimum configured bitrate. If this variable is false, the
+    // stream may send at a rate higher than the estimated available bitrate.
+    // Enabling suspend_below_min_bitrate will also enable pacing and padding,
+    // otherwise, the video will be unable to recover from suspension.
+    bool suspend_below_min_bitrate;
   };
 
   // Gets interface used to insert captured frames. Valid as long as the
   // VideoSendStream is valid.
   virtual VideoSendStreamInput* Input() = 0;
 
-  virtual void StartSend() = 0;
-  virtual void StopSend() = 0;
+  virtual void StartSending() = 0;
+  virtual void StopSending() = 0;
 
-  // TODO(mflodman) Change VideoCodec struct and use here.
-  virtual bool SetTargetBitrate(
-      int min_bitrate, int max_bitrate,
-      const std::vector<SimulcastStream>& streams) = 0;
-
-  virtual void GetSendCodec(VideoCodec* send_codec) = 0;
+  virtual bool SetCodec(const VideoCodec& codec) = 0;
+  virtual VideoCodec GetCodec() = 0;
 
  protected:
   virtual ~VideoSendStream() {}
diff --git a/voice_engine/test/cmd_test/voe_cmd_test.cc b/voice_engine/test/cmd_test/voe_cmd_test.cc
index 10fc198..68d096d 100644
--- a/voice_engine/test/cmd_test/voe_cmd_test.cc
+++ b/voice_engine/test/cmd_test/voe_cmd_test.cc
@@ -23,6 +23,7 @@
 #include "webrtc/common_types.h"
 #include "webrtc/engine_configurations.h"
 #include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h"
+#include "webrtc/modules/audio_processing/include/audio_processing.h"
 #include "webrtc/system_wrappers/interface/scoped_ptr.h"
 #include "webrtc/test/channel_transport/include/channel_transport.h"
 #include "webrtc/test/testsupport/fileutils.h"
@@ -242,6 +243,7 @@
   bool muted = false;
   bool on_hold = false;
   bool opus_stereo = false;
+  bool experimental_ns_enabled = false;
 
 #if defined(WEBRTC_ANDROID)
   std::string resource_path = "/sdcard/";
@@ -432,6 +434,7 @@
       printf("%i. Toggle CNG\n", option_index++);
       printf("%i. Toggle AGC\n", option_index++);
       printf("%i. Toggle NS\n", option_index++);
+      printf("%i. Toggle experimental NS\n", option_index++);
       printf("%i. Toggle EC\n", option_index++);
       printf("%i. Select AEC\n", option_index++);
       printf("%i. Select AECM\n", option_index++);
@@ -497,6 +500,16 @@
         else
           printf("\n NS is now off! \n");
       } else if (option_selection == option_index++) {
+        experimental_ns_enabled = !experimental_ns_enabled;
+        res = base1->audio_processing()->EnableExperimentalNs(
+            experimental_ns_enabled);
+        VALIDATE;
+        if (experimental_ns_enabled) {
+          printf("\n Experimental NS is now on!\n");
+        } else {
+          printf("\n Experimental NS is now off!\n");
+        }
+      } else if (option_selection == option_index++) {
         enable_aec = !enable_aec;
         res = apm->SetEcStatus(enable_aec, kEcUnchanged);
         VALIDATE;
diff --git a/voice_engine/voice_engine_impl.cc b/voice_engine/voice_engine_impl.cc
index 703b6a4..4af15f9 100644
--- a/voice_engine/voice_engine_impl.cc
+++ b/voice_engine/voice_engine_impl.cc
@@ -149,7 +149,11 @@
 {
 #ifdef WEBRTC_ANDROID
 #ifdef WEBRTC_ANDROID_OPENSLES
-  AudioManagerJni::SetAndroidAudioDeviceObjects(javaVM, env, context);
+  if (javaVM && env && context) {
+    AudioManagerJni::SetAndroidAudioDeviceObjects(javaVM, env, context);
+  } else {
+    AudioManagerJni::ClearAndroidAudioDeviceObjects();
+  }
   return 0;
 #else
   return AudioDeviceAndroidJni::SetAndroidAudioDeviceObjects(
diff --git a/webrtc.gyp b/webrtc.gyp
index 9fe514c..a223007 100644
--- a/webrtc.gyp
+++ b/webrtc.gyp
@@ -61,6 +61,7 @@
       'sources': [
         'call.cc',
         'call.h',
+        'config.cc',
         'config.h',
         'frame_callback.h',
         'transport.h',
diff --git a/webrtc_tests.gypi b/webrtc_tests.gypi
index bcf3b13..6ea74f4 100644
--- a/webrtc_tests.gypi
+++ b/webrtc_tests.gypi
@@ -36,6 +36,8 @@
         'video/full_stack.cc',
         'video/rampup_tests.cc',
         'video/video_send_stream_tests.cc',
+        'voice_engine/test/auto_test/resource_manager.cc',
+        'voice_engine/test/auto_test/resource_manager.h',
         'test/test_main.cc',
       ],
       'dependencies': [