Add support for transport wide sequence numbers

Also refactor packet router to use a map rather than iterate over all
rtp modules for each packet sent.

BUG=webrtc:4311

Review URL: https://codereview.webrtc.org/1247293002

Cr-Commit-Position: refs/heads/master@{#9670}
diff --git a/webrtc/config.cc b/webrtc/config.cc
index c5d29d4..ddff931 100644
--- a/webrtc/config.cc
+++ b/webrtc/config.cc
@@ -35,16 +35,20 @@
 const char* RtpExtension::kVideoRotation = "urn:3gpp:video-orientation";
 const char* RtpExtension::kAudioLevel =
     "urn:ietf:params:rtp-hdrext:ssrc-audio-level";
+const char* RtpExtension::kTransportSequenceNumber =
+    "http://www.webrtc.org/experiments/rtp-hdrext/transport-sequence-number";
 
 bool RtpExtension::IsSupportedForAudio(const std::string& name) {
   return name == webrtc::RtpExtension::kAbsSendTime ||
-         name == webrtc::RtpExtension::kAudioLevel;
+         name == webrtc::RtpExtension::kAudioLevel ||
+         name == webrtc::RtpExtension::kTransportSequenceNumber;
 }
 
 bool RtpExtension::IsSupportedForVideo(const std::string& name) {
   return name == webrtc::RtpExtension::kTOffset ||
          name == webrtc::RtpExtension::kAbsSendTime ||
-         name == webrtc::RtpExtension::kVideoRotation;
+         name == webrtc::RtpExtension::kVideoRotation ||
+         name == webrtc::RtpExtension::kTransportSequenceNumber;
 }
 
 VideoStream::VideoStream()
diff --git a/webrtc/config.h b/webrtc/config.h
index 804f54d..5271163 100644
--- a/webrtc/config.h
+++ b/webrtc/config.h
@@ -60,6 +60,7 @@
   static const char* kAbsSendTime;
   static const char* kVideoRotation;
   static const char* kAudioLevel;
+  static const char* kTransportSequenceNumber;
   std::string name;
   int id;
 };
diff --git a/webrtc/modules/bitrate_controller/BUILD.gn b/webrtc/modules/bitrate_controller/BUILD.gn
index 9280f03..4ef536b 100644
--- a/webrtc/modules/bitrate_controller/BUILD.gn
+++ b/webrtc/modules/bitrate_controller/BUILD.gn
@@ -17,8 +17,6 @@
     "include/bitrate_controller.h",
     "send_side_bandwidth_estimation.cc",
     "send_side_bandwidth_estimation.h",
-    "send_time_history.cc",
-    "send_time_history.h",
   ]
 
   if (is_win) {
diff --git a/webrtc/modules/bitrate_controller/bitrate_controller.gypi b/webrtc/modules/bitrate_controller/bitrate_controller.gypi
index a0c2fc9..44c1b89 100644
--- a/webrtc/modules/bitrate_controller/bitrate_controller.gypi
+++ b/webrtc/modules/bitrate_controller/bitrate_controller.gypi
@@ -22,8 +22,6 @@
         'include/bitrate_allocator.h',
         'send_side_bandwidth_estimation.cc',
         'send_side_bandwidth_estimation.h',
-        'send_time_history.cc',
-        'send_time_history.h',
       ],
       # TODO(jschuh): Bug 1348: fix size_t to int truncations.
       'msvs_disabled_warnings': [ 4267, ],
diff --git a/webrtc/modules/bitrate_controller/bitrate_controller_impl.cc b/webrtc/modules/bitrate_controller/bitrate_controller_impl.cc
index d54da99..4a045d9 100644
--- a/webrtc/modules/bitrate_controller/bitrate_controller_impl.cc
+++ b/webrtc/modules/bitrate_controller/bitrate_controller_impl.cc
@@ -87,7 +87,6 @@
     : clock_(clock),
       observer_(observer),
       last_bitrate_update_ms_(clock_->TimeInMilliseconds()),
-      critsect_(CriticalSectionWrapper::CreateCriticalSection()),
       bandwidth_estimation_(),
       reserved_bitrate_bps_(0),
       last_bitrate_bps_(0),
@@ -107,7 +106,7 @@
 
 void BitrateControllerImpl::SetStartBitrate(int start_bitrate_bps) {
   {
-    CriticalSectionScoped cs(critsect_.get());
+    rtc::CritScope cs(&critsect_);
     bandwidth_estimation_.SetSendBitrate(start_bitrate_bps);
   }
   MaybeTriggerOnNetworkChanged();
@@ -116,7 +115,7 @@
 void BitrateControllerImpl::SetMinMaxBitrate(int min_bitrate_bps,
                                              int max_bitrate_bps) {
   {
-    CriticalSectionScoped cs(critsect_.get());
+    rtc::CritScope cs(&critsect_);
     bandwidth_estimation_.SetMinMaxBitrate(min_bitrate_bps, max_bitrate_bps);
   }
   MaybeTriggerOnNetworkChanged();
@@ -124,7 +123,7 @@
 
 void BitrateControllerImpl::SetReservedBitrate(uint32_t reserved_bitrate_bps) {
   {
-    CriticalSectionScoped cs(critsect_.get());
+    rtc::CritScope cs(&critsect_);
     reserved_bitrate_bps_ = reserved_bitrate_bps;
   }
   MaybeTriggerOnNetworkChanged();
@@ -132,7 +131,7 @@
 
 void BitrateControllerImpl::OnReceivedEstimatedBitrate(uint32_t bitrate) {
   {
-    CriticalSectionScoped cs(critsect_.get());
+    rtc::CritScope cs(&critsect_);
     bandwidth_estimation_.UpdateReceiverEstimate(bitrate);
   }
   MaybeTriggerOnNetworkChanged();
@@ -140,7 +139,7 @@
 
 int64_t BitrateControllerImpl::TimeUntilNextProcess() {
   const int64_t kBitrateControllerUpdateIntervalMs = 25;
-  CriticalSectionScoped cs(critsect_.get());
+  rtc::CritScope cs(&critsect_);
   int64_t time_since_update_ms =
       clock_->TimeInMilliseconds() - last_bitrate_update_ms_;
   return std::max<int64_t>(
@@ -151,7 +150,7 @@
   if (TimeUntilNextProcess() > 0)
     return 0;
   {
-    CriticalSectionScoped cs(critsect_.get());
+    rtc::CritScope cs(&critsect_);
     bandwidth_estimation_.UpdateEstimate(clock_->TimeInMilliseconds());
   }
   MaybeTriggerOnNetworkChanged();
@@ -165,7 +164,7 @@
     int number_of_packets,
     int64_t now_ms) {
   {
-    CriticalSectionScoped cs(critsect_.get());
+    rtc::CritScope cs(&critsect_);
     bandwidth_estimation_.UpdateReceiverBlock(fraction_loss, rtt,
                                               number_of_packets, now_ms);
   }
@@ -183,7 +182,7 @@
 bool BitrateControllerImpl::GetNetworkParameters(uint32_t* bitrate,
                                                  uint8_t* fraction_loss,
                                                  int64_t* rtt) {
-  CriticalSectionScoped cs(critsect_.get());
+  rtc::CritScope cs(&critsect_);
   int current_bitrate;
   bandwidth_estimation_.CurrentEstimate(&current_bitrate, fraction_loss, rtt);
   *bitrate = current_bitrate;
@@ -205,7 +204,7 @@
 }
 
 bool BitrateControllerImpl::AvailableBandwidth(uint32_t* bandwidth) const {
-  CriticalSectionScoped cs(critsect_.get());
+  rtc::CritScope cs(&critsect_);
   int bitrate;
   uint8_t fraction_loss;
   int64_t rtt;
@@ -218,5 +217,4 @@
   }
   return false;
 }
-
 }  // namespace webrtc
diff --git a/webrtc/modules/bitrate_controller/bitrate_controller_impl.h b/webrtc/modules/bitrate_controller/bitrate_controller_impl.h
index 3d38a54..a0131e2 100644
--- a/webrtc/modules/bitrate_controller/bitrate_controller_impl.h
+++ b/webrtc/modules/bitrate_controller/bitrate_controller_impl.h
@@ -20,9 +20,9 @@
 #include <list>
 #include <utility>
 
+#include "webrtc/base/criticalsection.h"
 #include "webrtc/base/scoped_ptr.h"
 #include "webrtc/modules/bitrate_controller/send_side_bandwidth_estimation.h"
-#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
 
 namespace webrtc {
 
@@ -64,22 +64,21 @@
 
   void OnNetworkChanged(uint32_t bitrate,
                         uint8_t fraction_loss,  // 0 - 255.
-                        int64_t rtt)
-      EXCLUSIVE_LOCKS_REQUIRED(*critsect_);
+                        int64_t rtt) EXCLUSIVE_LOCKS_REQUIRED(critsect_);
 
   // Used by process thread.
   Clock* clock_;
   BitrateObserver* observer_;
   int64_t last_bitrate_update_ms_;
 
-  const rtc::scoped_ptr<CriticalSectionWrapper> critsect_;
-  SendSideBandwidthEstimation bandwidth_estimation_ GUARDED_BY(*critsect_);
-  uint32_t reserved_bitrate_bps_ GUARDED_BY(*critsect_);
+  mutable rtc::CriticalSection critsect_;
+  SendSideBandwidthEstimation bandwidth_estimation_ GUARDED_BY(critsect_);
+  uint32_t reserved_bitrate_bps_ GUARDED_BY(critsect_);
 
-  uint32_t last_bitrate_bps_ GUARDED_BY(*critsect_);
-  uint8_t last_fraction_loss_ GUARDED_BY(*critsect_);
-  int64_t last_rtt_ms_ GUARDED_BY(*critsect_);
-  uint32_t last_reserved_bitrate_bps_ GUARDED_BY(*critsect_);
+  uint32_t last_bitrate_bps_ GUARDED_BY(critsect_);
+  uint8_t last_fraction_loss_ GUARDED_BY(critsect_);
+  int64_t last_rtt_ms_ GUARDED_BY(critsect_);
+  uint32_t last_reserved_bitrate_bps_ GUARDED_BY(critsect_);
 
   DISALLOW_IMPLICIT_CONSTRUCTORS(BitrateControllerImpl);
 };
diff --git a/webrtc/modules/bitrate_controller/include/bitrate_controller.h b/webrtc/modules/bitrate_controller/include/bitrate_controller.h
index 7303d06..bb53288 100644
--- a/webrtc/modules/bitrate_controller/include/bitrate_controller.h
+++ b/webrtc/modules/bitrate_controller/include/bitrate_controller.h
@@ -23,6 +23,7 @@
 namespace webrtc {
 
 class CriticalSectionWrapper;
+struct PacketInfo;
 
 class BitrateObserver {
   // Observer class for bitrate changes announced due to change in bandwidth
diff --git a/webrtc/modules/modules.gyp b/webrtc/modules/modules.gyp
index a769aa9..f114fea 100644
--- a/webrtc/modules/modules.gyp
+++ b/webrtc/modules/modules.gyp
@@ -194,7 +194,6 @@
             'bitrate_controller/bitrate_allocator_unittest.cc',
             'bitrate_controller/bitrate_controller_unittest.cc',
             'bitrate_controller/send_side_bandwidth_estimation_unittest.cc',
-            'bitrate_controller/send_time_history_unittest.cc',
             'desktop_capture/desktop_and_cursor_composer_unittest.cc',
             'desktop_capture/desktop_region_unittest.cc',
             'desktop_capture/differ_block_unittest.cc',
@@ -222,6 +221,7 @@
             'remote_bitrate_estimator/remote_bitrate_estimator_single_stream_unittest.cc',
             'remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.cc',
             'remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.h',
+            'remote_bitrate_estimator/send_time_history_unittest.cc',
             'remote_bitrate_estimator/test/bwe_test_framework_unittest.cc',
             'remote_bitrate_estimator/test/bwe_unittest.cc',
             'remote_bitrate_estimator/test/metric_recorder_unittest.cc',
diff --git a/webrtc/modules/pacing/BUILD.gn b/webrtc/modules/pacing/BUILD.gn
index 296bf17..69cd6db 100644
--- a/webrtc/modules/pacing/BUILD.gn
+++ b/webrtc/modules/pacing/BUILD.gn
@@ -27,5 +27,6 @@
 
   deps = [
     "../../system_wrappers",
+    "../bitrate_controller",
   ]
 }
diff --git a/webrtc/modules/pacing/include/packet_router.h b/webrtc/modules/pacing/include/packet_router.h
index c1b332a..e7d630e 100644
--- a/webrtc/modules/pacing/include/packet_router.h
+++ b/webrtc/modules/pacing/include/packet_router.h
@@ -14,6 +14,7 @@
 #include <list>
 
 #include "webrtc/base/constructormagic.h"
+#include "webrtc/base/criticalsection.h"
 #include "webrtc/base/scoped_ptr.h"
 #include "webrtc/base/thread_annotations.h"
 #include "webrtc/common_types.h"
@@ -21,10 +22,7 @@
 
 namespace webrtc {
 
-class CriticalSectionWrapper;
-class RTPFragmentationHeader;
 class RtpRtcp;
-struct RTPVideoHeader;
 
 // PacketRouter routes outgoing data to the correct sending RTP module, based
 // on the simulcast layer in RTPVideoHeader.
@@ -44,14 +42,15 @@
 
   size_t TimeToSendPadding(size_t bytes) override;
 
- private:
-  // TODO(holmer): When the new video API has launched, remove crit_ and
-  // assume rtp_modules_ will never change during a call. We should then also
-  // switch rtp_modules_ to a map from ssrc to rtp module.
-  rtc::scoped_ptr<CriticalSectionWrapper> crit_;
+  void SetTransportWideSequenceNumber(uint16_t sequence_number);
+  uint16_t AllocateSequenceNumber();
 
+ private:
+  rtc::CriticalSection modules_lock_;
   // Map from ssrc to sending rtp module.
-  std::list<RtpRtcp*> rtp_modules_ GUARDED_BY(crit_.get());
+  std::list<RtpRtcp*> rtp_modules_ GUARDED_BY(modules_lock_);
+
+  volatile int transport_seq_;
 
   DISALLOW_COPY_AND_ASSIGN(PacketRouter);
 };
diff --git a/webrtc/modules/pacing/pacing.gypi b/webrtc/modules/pacing/pacing.gypi
index 09be38f..29d9508 100644
--- a/webrtc/modules/pacing/pacing.gypi
+++ b/webrtc/modules/pacing/pacing.gypi
@@ -13,6 +13,7 @@
       'type': 'static_library',
       'dependencies': [
         '<(webrtc_root)/system_wrappers/system_wrappers.gyp:system_wrappers',
+        '<(webrtc_root)/modules/modules.gyp:bitrate_controller',
       ],
       'sources': [
         'include/paced_sender.h',
diff --git a/webrtc/modules/pacing/packet_router.cc b/webrtc/modules/pacing/packet_router.cc
index 9e15a71..1b12498 100644
--- a/webrtc/modules/pacing/packet_router.cc
+++ b/webrtc/modules/pacing/packet_router.cc
@@ -10,37 +10,39 @@
 
 #include "webrtc/modules/pacing/include/packet_router.h"
 
+#include "webrtc/base/atomicops.h"
 #include "webrtc/base/checks.h"
 #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h"
 #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h"
-#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
 
 namespace webrtc {
 
-PacketRouter::PacketRouter()
-    : crit_(CriticalSectionWrapper::CreateCriticalSection()) {
+PacketRouter::PacketRouter() : transport_seq_(0) {
 }
 
 PacketRouter::~PacketRouter() {
+  DCHECK(rtp_modules_.empty());
 }
 
 void PacketRouter::AddRtpModule(RtpRtcp* rtp_module) {
-  CriticalSectionScoped cs(crit_.get());
+  rtc::CritScope cs(&modules_lock_);
   DCHECK(std::find(rtp_modules_.begin(), rtp_modules_.end(), rtp_module) ==
          rtp_modules_.end());
   rtp_modules_.push_back(rtp_module);
 }
 
 void PacketRouter::RemoveRtpModule(RtpRtcp* rtp_module) {
-  CriticalSectionScoped cs(crit_.get());
-  rtp_modules_.remove(rtp_module);
+  rtc::CritScope cs(&modules_lock_);
+  auto it = std::find(rtp_modules_.begin(), rtp_modules_.end(), rtp_module);
+  DCHECK(it != rtp_modules_.end());
+  rtp_modules_.erase(it);
 }
 
 bool PacketRouter::TimeToSendPacket(uint32_t ssrc,
                                     uint16_t sequence_number,
                                     int64_t capture_timestamp,
                                     bool retransmission) {
-  CriticalSectionScoped cs(crit_.get());
+  rtc::CritScope cs(&modules_lock_);
   for (auto* rtp_module : rtp_modules_) {
     if (rtp_module->SendingMedia() && ssrc == rtp_module->SSRC()) {
       return rtp_module->TimeToSendPacket(ssrc, sequence_number,
@@ -50,12 +52,41 @@
   return true;
 }
 
-size_t PacketRouter::TimeToSendPadding(size_t bytes) {
-  CriticalSectionScoped cs(crit_.get());
-  for (auto* rtp_module : rtp_modules_) {
-    if (rtp_module->SendingMedia())
-      return rtp_module->TimeToSendPadding(bytes);
+size_t PacketRouter::TimeToSendPadding(size_t bytes_to_send) {
+  size_t total_bytes_sent = 0;
+  rtc::CritScope cs(&modules_lock_);
+  for (RtpRtcp* module : rtp_modules_) {
+    if (module->SendingMedia()) {
+      size_t bytes_sent =
+          module->TimeToSendPadding(bytes_to_send - total_bytes_sent);
+      total_bytes_sent += bytes_sent;
+      if (total_bytes_sent >= bytes_to_send)
+        break;
+    }
   }
-  return 0;
+  return total_bytes_sent;
 }
+
+void PacketRouter::SetTransportWideSequenceNumber(uint16_t sequence_number) {
+  rtc::AtomicOps::ReleaseStore(&transport_seq_, sequence_number);
+}
+
+uint16_t PacketRouter::AllocateSequenceNumber() {
+  int prev_seq = rtc::AtomicOps::AcquireLoad(&transport_seq_);
+  int desired_prev_seq;
+  int new_seq;
+  do {
+    desired_prev_seq = prev_seq;
+    new_seq = (desired_prev_seq + 1) & 0xFFFF;
+    // Note: CompareAndSwap returns the actual value of transport_seq at the
+    // time the CAS operation was executed. Thus, if prev_seq is returned, the
+    // operation was successful - otherwise we need to retry. Saving the
+    // return value saves us a load on retry.
+    prev_seq = rtc::AtomicOps::CompareAndSwap(&transport_seq_, desired_prev_seq,
+                                              new_seq);
+  } while (prev_seq != desired_prev_seq);
+
+  return new_seq;
+}
+
 }  // namespace webrtc
diff --git a/webrtc/modules/pacing/packet_router_unittest.cc b/webrtc/modules/pacing/packet_router_unittest.cc
index f7fdf7b..eecb137 100644
--- a/webrtc/modules/pacing/packet_router_unittest.cc
+++ b/webrtc/modules/pacing/packet_router_unittest.cc
@@ -102,20 +102,30 @@
 }
 
 TEST_F(PacketRouterTest, TimeToSendPadding) {
+  const uint16_t kSsrc1 = 1234;
+  const uint16_t kSsrc2 = 4567;
+
   MockRtpRtcp rtp_1;
+  EXPECT_CALL(rtp_1, SSRC()).WillRepeatedly(Return(kSsrc1));
   MockRtpRtcp rtp_2;
+  EXPECT_CALL(rtp_2, SSRC()).WillRepeatedly(Return(kSsrc2));
   packet_router_->AddRtpModule(&rtp_1);
   packet_router_->AddRtpModule(&rtp_2);
 
-  // Default configuration, sending padding on the first sending module.
+  // Default configuration, sending padding on all modules sending media,
+  // ordered by SSRC.
   const size_t requested_padding_bytes = 1000;
   const size_t sent_padding_bytes = 890;
   EXPECT_CALL(rtp_1, SendingMedia()).Times(1).WillOnce(Return(true));
   EXPECT_CALL(rtp_1, TimeToSendPadding(requested_padding_bytes))
       .Times(1)
       .WillOnce(Return(sent_padding_bytes));
-  EXPECT_CALL(rtp_2, TimeToSendPadding(_)).Times(0);
-  EXPECT_EQ(sent_padding_bytes,
+  EXPECT_CALL(rtp_2, SendingMedia()).Times(1).WillOnce(Return(true));
+  EXPECT_CALL(rtp_2,
+              TimeToSendPadding(requested_padding_bytes - sent_padding_bytes))
+      .Times(1)
+      .WillOnce(Return(requested_padding_bytes - sent_padding_bytes));
+  EXPECT_EQ(requested_padding_bytes,
             packet_router_->TimeToSendPadding(requested_padding_bytes));
 
   // Let only the second module be sending and verify the padding request is
@@ -134,8 +144,7 @@
   EXPECT_CALL(rtp_1, TimeToSendPadding(requested_padding_bytes)).Times(0);
   EXPECT_CALL(rtp_2, SendingMedia()).Times(1).WillOnce(Return(false));
   EXPECT_CALL(rtp_2, TimeToSendPadding(_)).Times(0);
-  EXPECT_EQ(static_cast<size_t>(0),
-            packet_router_->TimeToSendPadding(requested_padding_bytes));
+  EXPECT_EQ(0u, packet_router_->TimeToSendPadding(requested_padding_bytes));
 
   packet_router_->RemoveRtpModule(&rtp_1);
 
@@ -143,9 +152,21 @@
   // to send by not expecting any calls. Instead verify rtp_2 is called.
   EXPECT_CALL(rtp_2, SendingMedia()).Times(1).WillOnce(Return(true));
   EXPECT_CALL(rtp_2, TimeToSendPadding(requested_padding_bytes)).Times(1);
-  EXPECT_EQ(static_cast<size_t>(0),
-            packet_router_->TimeToSendPadding(requested_padding_bytes));
+  EXPECT_EQ(0u, packet_router_->TimeToSendPadding(requested_padding_bytes));
 
   packet_router_->RemoveRtpModule(&rtp_2);
 }
+
+TEST_F(PacketRouterTest, AllocateSequenceNumbers) {
+  const uint16_t kStartSeq = 0xFFF0;
+  const size_t kNumPackets = 32;
+
+  packet_router_->SetTransportWideSequenceNumber(kStartSeq - 1);
+
+  for (size_t i = 0; i < kNumPackets; ++i) {
+    uint16_t seq = packet_router_->AllocateSequenceNumber();
+    uint32_t expected_unwrapped_seq = static_cast<uint32_t>(kStartSeq) + i;
+    EXPECT_EQ(static_cast<uint16_t>(expected_unwrapped_seq & 0xFFFF), seq);
+  }
+}
 }  // namespace webrtc
diff --git a/webrtc/modules/remote_bitrate_estimator/BUILD.gn b/webrtc/modules/remote_bitrate_estimator/BUILD.gn
index b4d4af9..938777e 100644
--- a/webrtc/modules/remote_bitrate_estimator/BUILD.gn
+++ b/webrtc/modules/remote_bitrate_estimator/BUILD.gn
@@ -10,8 +10,10 @@
   sources = [
     "include/bwe_defines.h",
     "include/remote_bitrate_estimator.h",
+    "include/send_time_history.h",
     "rate_statistics.cc",
     "rate_statistics.h",
+    "send_time_history.cc",
   ]
 
   configs += [ "../../:common_inherited_config" ]
diff --git a/webrtc/modules/bitrate_controller/send_time_history.h b/webrtc/modules/remote_bitrate_estimator/include/send_time_history.h
similarity index 100%
rename from webrtc/modules/bitrate_controller/send_time_history.h
rename to webrtc/modules/remote_bitrate_estimator/include/send_time_history.h
diff --git a/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator.gypi b/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator.gypi
index f318433..3bbd503 100644
--- a/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator.gypi
+++ b/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator.gypi
@@ -21,6 +21,7 @@
       'sources': [
         'include/bwe_defines.h',
         'include/remote_bitrate_estimator.h',
+        'include/send_time_history.h',
         'aimd_rate_control.cc',
         'aimd_rate_control.h',
         'inter_arrival.cc',
@@ -35,6 +36,7 @@
         'remote_bitrate_estimator_abs_send_time.h',
         'remote_bitrate_estimator_single_stream.cc',
         'remote_bitrate_estimator_single_stream.h',
+        'send_time_history.cc',
         'test/bwe_test_logging.cc',
         'test/bwe_test_logging.h',
       ], # source
diff --git a/webrtc/modules/bitrate_controller/send_time_history.cc b/webrtc/modules/remote_bitrate_estimator/send_time_history.cc
similarity index 96%
rename from webrtc/modules/bitrate_controller/send_time_history.cc
rename to webrtc/modules/remote_bitrate_estimator/send_time_history.cc
index 7e0c89e..f564573 100644
--- a/webrtc/modules/bitrate_controller/send_time_history.cc
+++ b/webrtc/modules/remote_bitrate_estimator/send_time_history.cc
@@ -10,7 +10,7 @@
 
 #include <assert.h>
 
-#include "webrtc/modules/bitrate_controller/send_time_history.h"
+#include "webrtc/modules/remote_bitrate_estimator/include/send_time_history.h"
 
 namespace webrtc {
 
diff --git a/webrtc/modules/bitrate_controller/send_time_history_unittest.cc b/webrtc/modules/remote_bitrate_estimator/send_time_history_unittest.cc
similarity index 98%
rename from webrtc/modules/bitrate_controller/send_time_history_unittest.cc
rename to webrtc/modules/remote_bitrate_estimator/send_time_history_unittest.cc
index fc7099d..8d70852 100644
--- a/webrtc/modules/bitrate_controller/send_time_history_unittest.cc
+++ b/webrtc/modules/remote_bitrate_estimator/send_time_history_unittest.cc
@@ -13,7 +13,7 @@
 #include <vector>
 
 #include "testing/gtest/include/gtest/gtest.h"
-#include "webrtc/modules/bitrate_controller/send_time_history.h"
+#include "webrtc/modules/remote_bitrate_estimator/include/send_time_history.h"
 #include "webrtc/system_wrappers/interface/clock.h"
 
 namespace webrtc {
diff --git a/webrtc/modules/remote_bitrate_estimator/test/estimators/send_side.h b/webrtc/modules/remote_bitrate_estimator/test/estimators/send_side.h
index c76e360..322dced 100644
--- a/webrtc/modules/remote_bitrate_estimator/test/estimators/send_side.h
+++ b/webrtc/modules/remote_bitrate_estimator/test/estimators/send_side.h
@@ -13,7 +13,7 @@
 
 #include <vector>
 
-#include "webrtc/modules/bitrate_controller/send_time_history.h"
+#include "webrtc/modules/remote_bitrate_estimator/include/send_time_history.h"
 #include "webrtc/modules/remote_bitrate_estimator/test/bwe.h"
 
 namespace webrtc {
diff --git a/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h b/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h
index cddaa21..d8f9a94 100644
--- a/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h
+++ b/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h
@@ -20,6 +20,7 @@
 namespace webrtc {
 // Forward declarations.
 class PacedSender;
+class PacketRouter;
 class ReceiveStatistics;
 class RemoteBitrateEstimator;
 class RtpReceiver;
@@ -61,11 +62,13 @@
     Transport* outgoing_transport;
     RtcpIntraFrameObserver* intra_frame_callback;
     RtcpBandwidthObserver* bandwidth_callback;
+    SendTimeObserver* send_time_callback;
     RtcpRttStats* rtt_stats;
     RtcpPacketTypeCounterObserver* rtcp_packet_type_counter_observer;
     RtpAudioFeedback* audio_messages;
     RemoteBitrateEstimator* remote_bitrate_estimator;
     PacedSender* paced_sender;
+    PacketRouter* packet_router;
     BitrateStatisticsObserver* send_bitrate_observer;
     FrameCountObserver* send_frame_count_observer;
     SendSideDelayObserver* send_side_delay_observer;
diff --git a/webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h b/webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h
index ea36687..7642285 100644
--- a/webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h
+++ b/webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h
@@ -293,6 +293,17 @@
   virtual ~RtcpBandwidthObserver() {}
 };
 
+class SendTimeObserver {
+ public:
+  SendTimeObserver() {}
+  virtual ~SendTimeObserver() {}
+
+  // Transport-wide sequence number and timestamp (system time in milliseconds),
+  // of when the packet was put on the wire.
+  virtual void OnPacketSent(uint16_t transport_sequence_number,
+                            int64_t send_time) = 0;
+};
+
 class RtcpRttStats {
  public:
   virtual void OnRttUpdate(int64_t rtt) = 0;
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
index c8b4b33..df5fb65 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
@@ -30,19 +30,20 @@
     : id(-1),
       audio(false),
       receiver_only(false),
-      clock(NULL),
+      clock(nullptr),
       receive_statistics(NullObjectReceiveStatistics()),
-      outgoing_transport(NULL),
-      intra_frame_callback(NULL),
-      bandwidth_callback(NULL),
-      rtt_stats(NULL),
-      rtcp_packet_type_counter_observer(NULL),
+      outgoing_transport(nullptr),
+      intra_frame_callback(nullptr),
+      bandwidth_callback(nullptr),
+      rtt_stats(nullptr),
+      rtcp_packet_type_counter_observer(nullptr),
       audio_messages(NullObjectRtpAudioFeedback()),
-      remote_bitrate_estimator(NULL),
-      paced_sender(NULL),
-      send_bitrate_observer(NULL),
-      send_frame_count_observer(NULL),
-      send_side_delay_observer(NULL) {
+      remote_bitrate_estimator(nullptr),
+      paced_sender(nullptr),
+      packet_router(nullptr),
+      send_bitrate_observer(nullptr),
+      send_frame_count_observer(nullptr),
+      send_side_delay_observer(nullptr) {
 }
 
 RtpRtcp* RtpRtcp::CreateRtpRtcp(const RtpRtcp::Configuration& configuration) {
@@ -65,6 +66,8 @@
                   configuration.outgoing_transport,
                   configuration.audio_messages,
                   configuration.paced_sender,
+                  configuration.packet_router,
+                  configuration.send_time_callback,
                   configuration.send_bitrate_observer,
                   configuration.send_frame_count_observer,
                   configuration.send_side_delay_observer),
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_sender.cc b/webrtc/modules/rtp_rtcp/source/rtp_sender.cc
index e7306a1..cec7e17 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_sender.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtp_sender.cc
@@ -103,6 +103,8 @@
                      Transport* transport,
                      RtpAudioFeedback* audio_feedback,
                      PacedSender* paced_sender,
+                     PacketRouter* packet_router,
+                     SendTimeObserver* send_time_observer,
                      BitrateStatisticsObserver* bitrate_callback,
                      FrameCountObserver* frame_count_observer,
                      SendSideDelayObserver* send_side_delay_observer)
@@ -119,6 +121,8 @@
                    : nullptr),
       video_(audio ? nullptr : new RTPSenderVideo(clock, this)),
       paced_sender_(paced_sender),
+      packet_router_(packet_router),
+      send_time_observer_(send_time_observer),
       last_capture_time_ms_sent_(0),
       send_critsect_(CriticalSectionWrapper::CreateCriticalSection()),
       transport_(transport),
@@ -603,6 +607,9 @@
                               size_t bytes) {
   size_t padding_bytes_in_packet = 0;
   size_t bytes_sent = 0;
+  bool using_transport_seq = rtp_header_extension_map_.IsRegistered(
+                                 kRtpExtensionTransportSequenceNumber) &&
+                             packet_router_;
   for (; bytes > 0; bytes -= padding_bytes_in_packet) {
     // Always send full padding packets.
     if (bytes < kMaxPaddingLength)
@@ -659,8 +666,19 @@
     }
 
     UpdateAbsoluteSendTime(padding_packet, length, rtp_header, now_ms);
+
+    uint16_t transport_seq = 0;
+    if (using_transport_seq) {
+      transport_seq =
+          UpdateTransportSequenceNumber(padding_packet, length, rtp_header);
+    }
+
     if (!SendPacketToNetwork(padding_packet, length))
       break;
+
+    if (using_transport_seq)
+      send_time_observer_->OnPacketSent(transport_seq, now_ms);
+
     bytes_sent += padding_bytes_in_packet;
     UpdateRtpStats(padding_packet, length, rtp_header, over_rtx, false);
   }
@@ -710,9 +728,11 @@
     CriticalSectionScoped lock(send_critsect_.get());
     rtx = rtx_;
   }
-  return PrepareAndSendPacket(data_buffer, length, capture_time_ms,
-                              (rtx & kRtxRetransmitted) > 0, true) ?
-      static_cast<int32_t>(length) : -1;
+  if (!PrepareAndSendPacket(data_buffer, length, capture_time_ms,
+                            (rtx & kRtxRetransmitted) > 0, true)) {
+    return -1;
+  }
+  return static_cast<int32_t>(length);
 }
 
 bool RTPSender::SendPacketToNetwork(const uint8_t *packet, size_t size) {
@@ -897,11 +917,23 @@
   UpdateTransmissionTimeOffset(buffer_to_send_ptr, length, rtp_header,
                                diff_ms);
   UpdateAbsoluteSendTime(buffer_to_send_ptr, length, rtp_header, now_ms);
+
+  uint16_t transport_seq = 0;
+  bool using_transport_seq = rtp_header_extension_map_.IsRegistered(
+                                 kRtpExtensionTransportSequenceNumber) &&
+                             packet_router_;
+  if (using_transport_seq) {
+    transport_seq =
+        UpdateTransportSequenceNumber(buffer_to_send_ptr, length, rtp_header);
+  }
+
   bool ret = SendPacketToNetwork(buffer_to_send_ptr, length);
   if (ret) {
     CriticalSectionScoped lock(send_critsect_.get());
     media_has_been_sent_ = true;
   }
+  if (using_transport_seq)
+    send_time_observer_->OnPacketSent(transport_seq, now_ms);
   UpdateRtpStats(buffer_to_send_ptr, length, rtp_header, send_over_rtx,
                  is_retransmit);
   return ret;
@@ -960,7 +992,8 @@
     return 0;
   {
     CriticalSectionScoped cs(send_critsect_.get());
-    if (!sending_media_) return 0;
+    if (!sending_media_)
+      return 0;
   }
   size_t bytes_sent = TrySendRedundantPayloads(bytes);
   if (bytes_sent < bytes)
@@ -1212,7 +1245,8 @@
         block_length = BuildVideoRotationExtension(extension_data);
         break;
       case kRtpExtensionTransportSequenceNumber:
-        block_length = BuildTransportSequenceNumberExtension(extension_data);
+        block_length = BuildTransportSequenceNumberExtension(
+            extension_data, transport_sequence_number_);
         break;
       default:
         assert(false);
@@ -1365,7 +1399,8 @@
 }
 
 uint8_t RTPSender::BuildTransportSequenceNumberExtension(
-    uint8_t* data_buffer) const {
+    uint8_t* data_buffer,
+    uint16_t sequence_number) const {
   //   0                   1                   2
   //   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
   //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -1382,8 +1417,7 @@
   size_t pos = 0;
   const uint8_t len = 1;
   data_buffer[pos++] = (id << 4) + len;
-  ByteWriter<uint16_t>::WriteBigEndian(data_buffer + pos,
-                                       transport_sequence_number_);
+  ByteWriter<uint16_t>::WriteBigEndian(data_buffer + pos, sequence_number);
   pos += 2;
   assert(pos == kTransportSequenceNumberLength);
   return kTransportSequenceNumberLength;
@@ -1426,35 +1460,62 @@
   return true;
 }
 
+RTPSender::ExtensionStatus RTPSender::VerifyExtension(
+    RTPExtensionType extension_type,
+    uint8_t* rtp_packet,
+    size_t rtp_packet_length,
+    const RTPHeader& rtp_header,
+    size_t extension_length_bytes,
+    size_t* extension_offset) const {
+  // Get id.
+  uint8_t id = 0;
+  if (rtp_header_extension_map_.GetId(extension_type, &id) != 0)
+    return ExtensionStatus::kNotRegistered;
+
+  size_t block_pos = 0;
+  if (!FindHeaderExtensionPosition(extension_type, rtp_packet,
+                                   rtp_packet_length, rtp_header, &block_pos))
+    return ExtensionStatus::kError;
+
+  // Verify that header contains extension.
+  if (!((rtp_packet[kRtpHeaderLength + rtp_header.numCSRCs] == 0xBE) &&
+        (rtp_packet[kRtpHeaderLength + rtp_header.numCSRCs + 1] == 0xDE))) {
+    LOG(LS_WARNING)
+        << "Failed to update absolute send time, hdr extension not found.";
+    return ExtensionStatus::kError;
+  }
+
+  // Verify first byte in block.
+  const uint8_t first_block_byte = (id << 4) + (extension_length_bytes - 2);
+  if (rtp_packet[block_pos] != first_block_byte)
+    return ExtensionStatus::kError;
+
+  *extension_offset = block_pos;
+  return ExtensionStatus::kOk;
+}
+
 void RTPSender::UpdateTransmissionTimeOffset(uint8_t* rtp_packet,
                                              size_t rtp_packet_length,
                                              const RTPHeader& rtp_header,
                                              int64_t time_diff_ms) const {
+  size_t offset;
   CriticalSectionScoped cs(send_critsect_.get());
-  // Get id.
-  uint8_t id = 0;
-  if (rtp_header_extension_map_.GetId(kRtpExtensionTransmissionTimeOffset,
-                                      &id) != 0) {
-    // Not registered.
-    return;
+  switch (VerifyExtension(kRtpExtensionTransmissionTimeOffset, rtp_packet,
+                          rtp_packet_length, rtp_header,
+                          kTransmissionTimeOffsetLength, &offset)) {
+    case ExtensionStatus::kNotRegistered:
+      return;
+    case ExtensionStatus::kError:
+      LOG(LS_WARNING) << "Failed to update transmission time offset.";
+      return;
+    case ExtensionStatus::kOk:
+      break;
+    default:
+      RTC_NOTREACHED();
   }
 
-  size_t block_pos = 0;
-  if (!FindHeaderExtensionPosition(kRtpExtensionTransmissionTimeOffset,
-                                   rtp_packet, rtp_packet_length, rtp_header,
-                                   &block_pos)) {
-    LOG(LS_WARNING) << "Failed to update transmission time offset.";
-    return;
-  }
-
-  // Verify first byte in block.
-  const uint8_t first_block_byte = (id << 4) + 2;
-  if (rtp_packet[block_pos] != first_block_byte) {
-    LOG(LS_WARNING) << "Failed to update transmission time offset.";
-    return;
-  }
   // Update transmission offset field (converting to a 90 kHz timestamp).
-  ByteWriter<int32_t, 3>::WriteBigEndian(rtp_packet + block_pos + 1,
+  ByteWriter<int32_t, 3>::WriteBigEndian(rtp_packet + offset + 1,
                                          time_diff_ms * 90);  // RTP timestamp.
 }
 
@@ -1463,29 +1524,24 @@
                                  const RTPHeader& rtp_header,
                                  bool is_voiced,
                                  uint8_t dBov) const {
+  size_t offset;
   CriticalSectionScoped cs(send_critsect_.get());
 
-  // Get id.
-  uint8_t id = 0;
-  if (rtp_header_extension_map_.GetId(kRtpExtensionAudioLevel, &id) != 0) {
-    // Not registered.
-    return false;
+  switch (VerifyExtension(kRtpExtensionAudioLevel, rtp_packet,
+                          rtp_packet_length, rtp_header, kAudioLevelLength,
+                          &offset)) {
+    case ExtensionStatus::kNotRegistered:
+      return false;
+    case ExtensionStatus::kError:
+      LOG(LS_WARNING) << "Failed to update audio level.";
+      return false;
+    case ExtensionStatus::kOk:
+      break;
+    default:
+      RTC_NOTREACHED();
   }
 
-  size_t block_pos = 0;
-  if (!FindHeaderExtensionPosition(kRtpExtensionAudioLevel, rtp_packet,
-                                   rtp_packet_length, rtp_header, &block_pos)) {
-    LOG(LS_WARNING) << "Failed to update audio level.";
-    return false;
-  }
-
-  // Verify first byte in block.
-  const uint8_t first_block_byte = (id << 4) + 0;
-  if (rtp_packet[block_pos] != first_block_byte) {
-    LOG(LS_WARNING) << "Failed to update audio level.";
-    return false;
-  }
-  rtp_packet[block_pos + 1] = (is_voiced ? 0x80 : 0x00) + (dBov & 0x7f);
+  rtp_packet[offset + 1] = (is_voiced ? 0x80 : 0x00) + (dBov & 0x7f);
   return true;
 }
 
@@ -1493,37 +1549,24 @@
                                     size_t rtp_packet_length,
                                     const RTPHeader& rtp_header,
                                     VideoRotation rotation) const {
+  size_t offset;
   CriticalSectionScoped cs(send_critsect_.get());
 
-  // Get id.
-  uint8_t id = 0;
-  if (rtp_header_extension_map_.GetId(kRtpExtensionVideoRotation, &id) != 0) {
-    // Not registered.
-    return false;
+  switch (VerifyExtension(kRtpExtensionVideoRotation, rtp_packet,
+                          rtp_packet_length, rtp_header, kVideoRotationLength,
+                          &offset)) {
+    case ExtensionStatus::kNotRegistered:
+      return false;
+    case ExtensionStatus::kError:
+      LOG(LS_WARNING) << "Failed to update CVO.";
+      return false;
+    case ExtensionStatus::kOk:
+      break;
+    default:
+      RTC_NOTREACHED();
   }
 
-  size_t block_pos = 0;
-  if (!FindHeaderExtensionPosition(kRtpExtensionVideoRotation, rtp_packet,
-                                   rtp_packet_length, rtp_header, &block_pos)) {
-    LOG(LS_WARNING) << "Failed to update video rotation (CVO).";
-    return false;
-  }
-  // Get length until start of header extension block.
-  int extension_block_pos =
-      rtp_header_extension_map_.GetLengthUntilBlockStartInBytes(
-          kRtpExtensionVideoRotation);
-  if (extension_block_pos < 0) {
-    // The feature is not enabled.
-    return false;
-  }
-
-  // Verify first byte in block.
-  const uint8_t first_block_byte = (id << 4) + 0;
-  if (rtp_packet[block_pos] != first_block_byte) {
-    LOG(LS_WARNING) << "Failed to update CVO.";
-    return false;
-  }
-  rtp_packet[block_pos + 1] = ConvertVideoRotationToCVOByte(rotation);
+  rtp_packet[offset + 1] = ConvertVideoRotationToCVOByte(rotation);
   return true;
 }
 
@@ -1531,49 +1574,55 @@
                                        size_t rtp_packet_length,
                                        const RTPHeader& rtp_header,
                                        int64_t now_ms) const {
+  size_t offset;
   CriticalSectionScoped cs(send_critsect_.get());
 
-  // Get id.
-  uint8_t id = 0;
-  if (rtp_header_extension_map_.GetId(kRtpExtensionAbsoluteSendTime,
-                                      &id) != 0) {
-    // Not registered.
-    return;
+  switch (VerifyExtension(kRtpExtensionAbsoluteSendTime, rtp_packet,
+                          rtp_packet_length, rtp_header,
+                          kAbsoluteSendTimeLength, &offset)) {
+    case ExtensionStatus::kNotRegistered:
+      return;
+    case ExtensionStatus::kError:
+      LOG(LS_WARNING) << "Failed to update absolute send time";
+      return;
+    case ExtensionStatus::kOk:
+      break;
+    default:
+      RTC_NOTREACHED();
   }
-  // Get length until start of header extension block.
-  int extension_block_pos =
-      rtp_header_extension_map_.GetLengthUntilBlockStartInBytes(
-          kRtpExtensionAbsoluteSendTime);
-  if (extension_block_pos < 0) {
-    // The feature is not enabled.
-    return;
-  }
-  size_t block_pos =
-      kRtpHeaderLength + rtp_header.numCSRCs + extension_block_pos;
-  if (rtp_packet_length < block_pos + kAbsoluteSendTimeLength ||
-      rtp_header.headerLength < block_pos + kAbsoluteSendTimeLength) {
-    LOG(LS_WARNING) << "Failed to update absolute send time, invalid length.";
-    return;
-  }
-  // Verify that header contains extension.
-  if (!((rtp_packet[kRtpHeaderLength + rtp_header.numCSRCs] == 0xBE) &&
-        (rtp_packet[kRtpHeaderLength + rtp_header.numCSRCs + 1] == 0xDE))) {
-    LOG(LS_WARNING)
-        << "Failed to update absolute send time, hdr extension not found.";
-    return;
-  }
-  // Verify first byte in block.
-  const uint8_t first_block_byte = (id << 4) + 2;
-  if (rtp_packet[block_pos] != first_block_byte) {
-    LOG(LS_WARNING) << "Failed to update absolute send time.";
-    return;
-  }
+
   // Update absolute send time field (convert ms to 24-bit unsigned with 18 bit
   // fractional part).
-  ByteWriter<uint32_t, 3>::WriteBigEndian(rtp_packet + block_pos + 1,
+  ByteWriter<uint32_t, 3>::WriteBigEndian(rtp_packet + offset + 1,
                                           ((now_ms << 18) / 1000) & 0x00ffffff);
 }
 
+uint16_t RTPSender::UpdateTransportSequenceNumber(
+    uint8_t* rtp_packet,
+    size_t rtp_packet_length,
+    const RTPHeader& rtp_header) const {
+  size_t offset;
+  CriticalSectionScoped cs(send_critsect_.get());
+
+  switch (VerifyExtension(kRtpExtensionTransportSequenceNumber, rtp_packet,
+                          rtp_packet_length, rtp_header,
+                          kTransportSequenceNumberLength, &offset)) {
+    case ExtensionStatus::kNotRegistered:
+      return 0;
+    case ExtensionStatus::kError:
+      LOG(LS_WARNING) << "Failed to update transport sequence number";
+      return 0;
+    case ExtensionStatus::kOk:
+      break;
+    default:
+      RTC_NOTREACHED();
+  }
+
+  uint16_t seq = packet_router_->AllocateSequenceNumber();
+  BuildTransportSequenceNumberExtension(rtp_packet + offset, seq);
+  return seq;
+}
+
 void RTPSender::SetSendingStatus(bool enabled) {
   if (enabled) {
     uint32_t frequency_hz = SendPayloadFrequency();
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_sender.h b/webrtc/modules/rtp_rtcp/source/rtp_sender.h
index a470398..3b93ae4 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_sender.h
+++ b/webrtc/modules/rtp_rtcp/source/rtp_sender.h
@@ -18,6 +18,7 @@
 #include "webrtc/base/thread_annotations.h"
 #include "webrtc/common_types.h"
 #include "webrtc/modules/pacing/include/paced_sender.h"
+#include "webrtc/modules/pacing/include/packet_router.h"
 #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h"
 #include "webrtc/modules/rtp_rtcp/source/bitrate.h"
 #include "webrtc/modules/rtp_rtcp/source/rtp_header_extension.h"
@@ -91,6 +92,8 @@
             Transport* transport,
             RtpAudioFeedback* audio_feedback,
             PacedSender* paced_sender,
+            PacketRouter* packet_router,
+            SendTimeObserver* send_time_observer,
             BitrateStatisticsObserver* bitrate_callback,
             FrameCountObserver* frame_count_observer,
             SendSideDelayObserver* send_side_delay_observer);
@@ -171,7 +174,27 @@
   uint8_t BuildAudioLevelExtension(uint8_t* data_buffer) const;
   uint8_t BuildAbsoluteSendTimeExtension(uint8_t* data_buffer) const;
   uint8_t BuildVideoRotationExtension(uint8_t* data_buffer) const;
-  uint8_t BuildTransportSequenceNumberExtension(uint8_t* data_buffer) const;
+  uint8_t BuildTransportSequenceNumberExtension(uint8_t* data_buffer,
+                                                uint16_t sequence_number) const;
+
+  // Verifies that the specified extension is registered, and that it is
+  // present in rtp packet. If extension is not registered kNotRegistered is
+  // returned. If extension cannot be found in the rtp header, or if it is
+  // malformed, kError is returned. Otherwise *extension_offset is set to the
+  // offset of the extension from the beginning of the rtp packet and kOk is
+  // returned.
+  enum class ExtensionStatus {
+    kNotRegistered,
+    kOk,
+    kError,
+  };
+  ExtensionStatus VerifyExtension(RTPExtensionType extension_type,
+                                  uint8_t* rtp_packet,
+                                  size_t rtp_packet_length,
+                                  const RTPHeader& rtp_header,
+                                  size_t extension_length_bytes,
+                                  size_t* extension_offset) const
+      EXCLUSIVE_LOCKS_REQUIRED(send_critsect_.get());
 
   bool UpdateAudioLevel(uint8_t* rtp_packet,
                         size_t rtp_packet_length,
@@ -345,6 +368,12 @@
                               size_t rtp_packet_length,
                               const RTPHeader& rtp_header,
                               int64_t now_ms) const;
+  // Update the transport sequence number of the packet using a new sequence
+  // number allocated by PacketRouter. Returns the assigned sequence number,
+  // or 0 if extension could not be updated.
+  uint16_t UpdateTransportSequenceNumber(uint8_t* rtp_packet,
+                                         size_t rtp_packet_length,
+                                         const RTPHeader& rtp_header) const;
 
   void UpdateRtpStats(const uint8_t* buffer,
                       size_t packet_length,
@@ -365,7 +394,9 @@
   rtc::scoped_ptr<RTPSenderAudio> audio_;
   rtc::scoped_ptr<RTPSenderVideo> video_;
 
-  PacedSender *paced_sender_;
+  PacedSender* const paced_sender_;
+  PacketRouter* const packet_router_;
+  SendTimeObserver* const send_time_observer_;
   int64_t last_capture_time_ms_sent_;
   rtc::scoped_ptr<CriticalSectionWrapper> send_critsect_;
 
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_sender_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtp_sender_unittest.cc
index 23300bb..ea6fb6a 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_sender_unittest.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtp_sender_unittest.cc
@@ -73,7 +73,7 @@
       : packets_sent_(0),
         last_sent_packet_len_(0),
         total_bytes_sent_(0),
-        last_sent_packet_(NULL) {}
+        last_sent_packet_(nullptr) {}
 
   ~LoopbackTransportTest() {
     STLDeleteContainerPointers(sent_packets_.begin(), sent_packets_.end());
@@ -114,8 +114,9 @@
   }
 
   void SetUp() override {
-    rtp_sender_.reset(new RTPSender(0, false, &fake_clock_, &transport_, NULL,
-                                    &mock_paced_sender_, NULL, NULL, NULL));
+    rtp_sender_.reset(new RTPSender(0, false, &fake_clock_, &transport_,
+                                    nullptr, &mock_paced_sender_, nullptr,
+                                    nullptr, nullptr, nullptr, nullptr));
     rtp_sender_->SetSequenceNumber(kSeqNum);
   }
 
@@ -308,7 +309,7 @@
   webrtc::RtpUtility::RtpHeaderParser rtp_parser(packet_, length);
   webrtc::RTPHeader rtp_header;
 
-  const bool valid_rtp_header = rtp_parser.Parse(rtp_header, NULL);
+  const bool valid_rtp_header = rtp_parser.Parse(rtp_header, nullptr);
 
   ASSERT_TRUE(valid_rtp_header);
   ASSERT_FALSE(rtp_parser.RTCP());
@@ -351,7 +352,7 @@
 
   // Parse without map extension
   webrtc::RTPHeader rtp_header2;
-  const bool valid_rtp_header2 = rtp_parser.Parse(rtp_header2, NULL);
+  const bool valid_rtp_header2 = rtp_parser.Parse(rtp_header2, nullptr);
 
   ASSERT_TRUE(valid_rtp_header2);
   VerifyRTPHeaderCommon(rtp_header2);
@@ -415,7 +416,7 @@
 
   // Parse without map extension
   webrtc::RTPHeader rtp_header2;
-  const bool valid_rtp_header2 = rtp_parser.Parse(rtp_header2, NULL);
+  const bool valid_rtp_header2 = rtp_parser.Parse(rtp_header2, nullptr);
 
   ASSERT_TRUE(valid_rtp_header2);
   VerifyRTPHeaderCommon(rtp_header2);
@@ -509,7 +510,7 @@
 
   // Parse without map extension
   webrtc::RTPHeader rtp_header2;
-  const bool valid_rtp_header2 = rtp_parser.Parse(rtp_header2, NULL);
+  const bool valid_rtp_header2 = rtp_parser.Parse(rtp_header2, nullptr);
 
   ASSERT_TRUE(valid_rtp_header2);
   VerifyRTPHeaderCommon(rtp_header2);
@@ -571,7 +572,7 @@
 
   // Parse without map extension
   webrtc::RTPHeader rtp_header2;
-  const bool valid_rtp_header2 = rtp_parser.Parse(rtp_header2, NULL);
+  const bool valid_rtp_header2 = rtp_parser.Parse(rtp_header2, nullptr);
 
   ASSERT_TRUE(valid_rtp_header2);
   VerifyRTPHeaderCommon(rtp_header2);
@@ -724,7 +725,7 @@
   // Create and set up parser.
   rtc::scoped_ptr<webrtc::RtpHeaderParser> rtp_parser(
       webrtc::RtpHeaderParser::Create());
-  ASSERT_TRUE(rtp_parser.get() != NULL);
+  ASSERT_TRUE(rtp_parser.get() != nullptr);
   rtp_parser->RegisterRtpHeaderExtension(kRtpExtensionTransmissionTimeOffset,
                                          kTransmissionTimeOffsetExtensionId);
   rtp_parser->RegisterRtpHeaderExtension(kRtpExtensionAbsoluteSendTime,
@@ -822,8 +823,9 @@
 
 TEST_F(RtpSenderTest, SendRedundantPayloads) {
   MockTransport transport;
-  rtp_sender_.reset(new RTPSender(0, false, &fake_clock_, &transport, NULL,
-                                  &mock_paced_sender_, NULL, NULL, NULL));
+  rtp_sender_.reset(new RTPSender(0, false, &fake_clock_, &transport, nullptr,
+                                  &mock_paced_sender_, nullptr, nullptr,
+                                  nullptr, nullptr, nullptr));
   rtp_sender_->SetSequenceNumber(kSeqNum);
   rtp_sender_->SetRtxPayloadType(kRtxPayload, kPayload);
   // Make all packets go through the pacer.
@@ -845,7 +847,7 @@
   // Create and set up parser.
   rtc::scoped_ptr<webrtc::RtpHeaderParser> rtp_parser(
       webrtc::RtpHeaderParser::Create());
-  ASSERT_TRUE(rtp_parser.get() != NULL);
+  ASSERT_TRUE(rtp_parser.get() != nullptr);
   rtp_parser->RegisterRtpHeaderExtension(kRtpExtensionTransmissionTimeOffset,
                                          kTransmissionTimeOffsetExtensionId);
   rtp_parser->RegisterRtpHeaderExtension(kRtpExtensionAbsoluteSendTime,
@@ -891,9 +893,9 @@
   uint8_t payload[] = {47, 11, 32, 93, 89};
 
   // Send keyframe
-  ASSERT_EQ(0, rtp_sender_->SendOutgoingData(kVideoFrameKey, payload_type, 1234,
-                                             4321, payload, sizeof(payload),
-                                             NULL));
+  ASSERT_EQ(
+      0, rtp_sender_->SendOutgoingData(kVideoFrameKey, payload_type, 1234, 4321,
+                                       payload, sizeof(payload), nullptr));
 
   RtpUtility::RtpHeaderParser rtp_parser(transport_.last_sent_packet_,
                                          transport_.last_sent_packet_len_);
@@ -919,7 +921,7 @@
 
   ASSERT_EQ(0, rtp_sender_->SendOutgoingData(kVideoFrameDelta, payload_type,
                                              1234, 4321, payload,
-                                             sizeof(payload), NULL));
+                                             sizeof(payload), nullptr));
 
   RtpUtility::RtpHeaderParser rtp_parser2(transport_.last_sent_packet_,
                                           transport_.last_sent_packet_len_);
@@ -955,8 +957,9 @@
     FrameCounts frame_counts_;
   } callback;
 
-  rtp_sender_.reset(new RTPSender(0, false, &fake_clock_, &transport_, NULL,
-                                  &mock_paced_sender_, NULL, &callback, NULL));
+  rtp_sender_.reset(new RTPSender(0, false, &fake_clock_, &transport_, nullptr,
+                                  &mock_paced_sender_, nullptr, nullptr,
+                                  nullptr, &callback, nullptr));
 
   char payload_name[RTP_PAYLOAD_NAME_SIZE] = "GENERIC";
   const uint8_t payload_type = 127;
@@ -966,18 +969,18 @@
   rtp_sender_->SetStorePacketsStatus(true, 1);
   uint32_t ssrc = rtp_sender_->SSRC();
 
-  ASSERT_EQ(0, rtp_sender_->SendOutgoingData(kVideoFrameKey, payload_type, 1234,
-                                             4321, payload, sizeof(payload),
-                                             NULL));
+  ASSERT_EQ(
+      0, rtp_sender_->SendOutgoingData(kVideoFrameKey, payload_type, 1234, 4321,
+                                       payload, sizeof(payload), nullptr));
 
   EXPECT_EQ(1U, callback.num_calls_);
   EXPECT_EQ(ssrc, callback.ssrc_);
   EXPECT_EQ(1, callback.frame_counts_.key_frames);
   EXPECT_EQ(0, callback.frame_counts_.delta_frames);
 
-  ASSERT_EQ(0, rtp_sender_->SendOutgoingData(kVideoFrameDelta,
-                                             payload_type, 1234, 4321, payload,
-                                             sizeof(payload), NULL));
+  ASSERT_EQ(0, rtp_sender_->SendOutgoingData(kVideoFrameDelta, payload_type,
+                                             1234, 4321, payload,
+                                             sizeof(payload), nullptr));
 
   EXPECT_EQ(2U, callback.num_calls_);
   EXPECT_EQ(ssrc, callback.ssrc_);
@@ -1007,8 +1010,9 @@
     BitrateStatistics total_stats_;
     BitrateStatistics retransmit_stats_;
   } callback;
-  rtp_sender_.reset(new RTPSender(0, false, &fake_clock_, &transport_, NULL,
-                                  &mock_paced_sender_, &callback, NULL, NULL));
+  rtp_sender_.reset(new RTPSender(0, false, &fake_clock_, &transport_, nullptr,
+                                  &mock_paced_sender_, nullptr, nullptr,
+                                  &callback, nullptr, nullptr));
 
   // Simulate kNumPackets sent with kPacketInterval ms intervals.
   const uint32_t kNumPackets = 15;
@@ -1065,8 +1069,9 @@
 
   void SetUp() override {
     payload_ = kAudioPayload;
-    rtp_sender_.reset(new RTPSender(0, true, &fake_clock_, &transport_, NULL,
-                                    &mock_paced_sender_, NULL, NULL, NULL));
+    rtp_sender_.reset(new RTPSender(0, true, &fake_clock_, &transport_, nullptr,
+                                    &mock_paced_sender_, nullptr, nullptr,
+                                    nullptr, nullptr, nullptr));
     rtp_sender_->SetSequenceNumber(kSeqNum);
   }
 };
@@ -1117,9 +1122,9 @@
   rtp_sender_->RegisterRtpStatisticsCallback(&callback);
 
   // Send a frame.
-  ASSERT_EQ(0, rtp_sender_->SendOutgoingData(kVideoFrameKey, payload_type, 1234,
-                                             4321, payload, sizeof(payload),
-                                             NULL));
+  ASSERT_EQ(
+      0, rtp_sender_->SendOutgoingData(kVideoFrameKey, payload_type, 1234, 4321,
+                                       payload, sizeof(payload), nullptr));
   StreamDataCounters expected;
   expected.transmitted.payload_bytes = 6;
   expected.transmitted.header_bytes = 12;
@@ -1162,14 +1167,14 @@
   rtp_sender_->SetFecParameters(&fec_params, &fec_params);
   ASSERT_EQ(0, rtp_sender_->SendOutgoingData(kVideoFrameDelta, payload_type,
                                              1234, 4321, payload,
-                                             sizeof(payload), NULL));
+                                             sizeof(payload), nullptr));
   expected.transmitted.payload_bytes = 40;
   expected.transmitted.header_bytes = 60;
   expected.transmitted.packets = 5;
   expected.fec.packets = 1;
   callback.Matches(ssrc, expected);
 
-  rtp_sender_->RegisterRtpStatisticsCallback(NULL);
+  rtp_sender_->RegisterRtpStatisticsCallback(nullptr);
 }
 
 TEST_F(RtpSenderAudioTest, SendAudio) {
@@ -1179,9 +1184,9 @@
                                             0, 1500));
   uint8_t payload[] = {47, 11, 32, 93, 89};
 
-  ASSERT_EQ(0, rtp_sender_->SendOutgoingData(kAudioFrameCN, payload_type, 1234,
-                                             4321, payload, sizeof(payload),
-                                             NULL));
+  ASSERT_EQ(
+      0, rtp_sender_->SendOutgoingData(kAudioFrameCN, payload_type, 1234, 4321,
+                                       payload, sizeof(payload), nullptr));
 
   RtpUtility::RtpHeaderParser rtp_parser(transport_.last_sent_packet_,
                                          transport_.last_sent_packet_len_);
@@ -1208,9 +1213,9 @@
                                             0, 1500));
   uint8_t payload[] = {47, 11, 32, 93, 89};
 
-  ASSERT_EQ(0, rtp_sender_->SendOutgoingData(kAudioFrameCN, payload_type, 1234,
-                                             4321, payload, sizeof(payload),
-                                             NULL));
+  ASSERT_EQ(
+      0, rtp_sender_->SendOutgoingData(kAudioFrameCN, payload_type, 1234, 4321,
+                                       payload, sizeof(payload), nullptr));
 
   RtpUtility::RtpHeaderParser rtp_parser(transport_.last_sent_packet_,
                                          transport_.last_sent_packet_len_);
@@ -1259,19 +1264,17 @@
   // The duration is calculated as the difference of current and last sent
   // timestamp. So for first call it will skip since the duration is zero.
   ASSERT_EQ(0, rtp_sender_->SendOutgoingData(kFrameEmpty, payload_type,
-                                             capture_time_ms,
-                                             0, NULL, 0,
-                                             NULL));
+                                             capture_time_ms, 0, nullptr, 0,
+                                             nullptr));
   // DTMF Sample Length is (Frequency/1000) * Duration.
   // So in this case, it is (8000/1000) * 500 = 4000.
   // Sending it as two packets.
   ASSERT_EQ(0, rtp_sender_->SendOutgoingData(kFrameEmpty, payload_type,
-                                             capture_time_ms+2000,
-                                             0, NULL, 0,
-                                             NULL));
+                                             capture_time_ms + 2000, 0, nullptr,
+                                             0, nullptr));
   rtc::scoped_ptr<webrtc::RtpHeaderParser> rtp_parser(
       webrtc::RtpHeaderParser::Create());
-  ASSERT_TRUE(rtp_parser.get() != NULL);
+  ASSERT_TRUE(rtp_parser.get() != nullptr);
   webrtc::RTPHeader rtp_header;
   ASSERT_TRUE(rtp_parser->Parse(transport_.last_sent_packet_,
                                 transport_.last_sent_packet_len_,
@@ -1280,9 +1283,8 @@
   EXPECT_TRUE(rtp_header.markerBit);
 
   ASSERT_EQ(0, rtp_sender_->SendOutgoingData(kFrameEmpty, payload_type,
-                                             capture_time_ms+4000,
-                                             0, NULL, 0,
-                                             NULL));
+                                             capture_time_ms + 4000, 0, nullptr,
+                                             0, nullptr));
   ASSERT_TRUE(rtp_parser->Parse(transport_.last_sent_packet_,
                                 transport_.last_sent_packet_len_,
                                 &rtp_header));
@@ -1357,7 +1359,7 @@
       rtp_sender_->RtpHeaderExtensionTotalLength());
 
   rtp_sender_video_->SendVideo(kRtpVideoGeneric, kVideoFrameKey, kPayload,
-                               kTimestamp, 0, packet_, sizeof(packet_), NULL,
+                               kTimestamp, 0, packet_, sizeof(packet_), nullptr,
                                &hdr);
 
   RtpHeaderExtensionMap map;
diff --git a/webrtc/test/frame_generator_capturer.cc b/webrtc/test/frame_generator_capturer.cc
index fcab602..b991414 100644
--- a/webrtc/test/frame_generator_capturer.cc
+++ b/webrtc/test/frame_generator_capturer.cc
@@ -129,5 +129,9 @@
   rtc::CritScope cs(&lock_);
   sending_ = false;
 }
+
+void FrameGeneratorCapturer::ForceFrame() {
+  tick_->Set();
+}
 }  // test
 }  // webrtc
diff --git a/webrtc/test/frame_generator_capturer.h b/webrtc/test/frame_generator_capturer.h
index 86df689..aff906d 100644
--- a/webrtc/test/frame_generator_capturer.h
+++ b/webrtc/test/frame_generator_capturer.h
@@ -45,6 +45,7 @@
 
   void Start() override;
   void Stop() override;
+  void ForceFrame();
 
   int64_t first_frame_capture_time() const { return first_frame_capture_time_; }
 
diff --git a/webrtc/video/audio_receive_stream.cc b/webrtc/video/audio_receive_stream.cc
index 9f8bebe..6731ea4 100644
--- a/webrtc/video/audio_receive_stream.cc
+++ b/webrtc/video/audio_receive_stream.cc
@@ -61,6 +61,9 @@
     } else if (ext.name == RtpExtension::kAbsSendTime) {
       CHECK(rtp_header_parser_->RegisterRtpHeaderExtension(
           kRtpExtensionAbsoluteSendTime, ext.id));
+    } else if (ext.name == RtpExtension::kTransportSequenceNumber) {
+      CHECK(rtp_header_parser_->RegisterRtpHeaderExtension(
+          kRtpExtensionTransportSequenceNumber, ext.id));
     } else {
       RTC_NOTREACHED() << "Unsupported RTP extension.";
     }
diff --git a/webrtc/video/call_perf_tests.cc b/webrtc/video/call_perf_tests.cc
index fd159f5..cf65ea0 100644
--- a/webrtc/video/call_perf_tests.cc
+++ b/webrtc/video/call_perf_tests.cc
@@ -528,6 +528,7 @@
   static const int kMinAcceptableTransmitBitrate = 130;
   static const int kMaxAcceptableTransmitBitrate = 170;
   static const int kNumBitrateObservationsInRange = 100;
+  static const int kAcceptableBitrateErrorMargin = 15;  // +- 7
   class BitrateObserver : public test::EndToEndTest, public PacketReceiver {
    public:
     explicit BitrateObserver(bool using_min_transmit_bitrate)
@@ -567,8 +568,10 @@
             }
           } else {
             // Expect bitrate stats to roughly match the max encode bitrate.
-            if (bitrate_kbps > kMaxEncodeBitrateKbps - 5 &&
-                bitrate_kbps < kMaxEncodeBitrateKbps + 5) {
+            if (bitrate_kbps > (kMaxEncodeBitrateKbps -
+                                kAcceptableBitrateErrorMargin / 2) &&
+                bitrate_kbps < (kMaxEncodeBitrateKbps +
+                                kAcceptableBitrateErrorMargin / 2)) {
               ++num_bitrate_observations_in_range_;
             }
           }
@@ -629,7 +632,9 @@
         : EndToEndTest(kDefaultTimeoutMs),
           FakeEncoder(Clock::GetRealTimeClock()),
           time_to_reconfigure_(webrtc::EventWrapper::Create()),
-          encoder_inits_(0) {}
+          encoder_inits_(0),
+          last_set_bitrate_(0),
+          send_stream_(nullptr) {}
 
     int32_t InitEncode(const VideoCodec* config,
                        int32_t number_of_cores,
diff --git a/webrtc/video/end_to_end_tests.cc b/webrtc/video/end_to_end_tests.cc
index 416c8dd..36206fa 100644
--- a/webrtc/video/end_to_end_tests.cc
+++ b/webrtc/video/end_to_end_tests.cc
@@ -1133,120 +1133,304 @@
 
 // Test sets up a Call multiple senders with different resolutions and SSRCs.
 // Another is set up to receive all three of these with different renderers.
+class MultiStreamTest {
+ public:
+  static const size_t kNumStreams = 3;
+  struct CodecSettings {
+    uint32_t ssrc;
+    int width;
+    int height;
+  } codec_settings[kNumStreams];
+
+  MultiStreamTest() {
+    // TODO(sprang): Cleanup when msvc supports explicit initializers for array.
+    codec_settings[0] = {1, 640, 480};
+    codec_settings[1] = {2, 320, 240};
+    codec_settings[2] = {3, 240, 160};
+  }
+
+  virtual ~MultiStreamTest() {}
+
+  void RunTest() {
+    rtc::scoped_ptr<test::DirectTransport> sender_transport(
+        CreateSendTransport());
+    rtc::scoped_ptr<test::DirectTransport> receiver_transport(
+        CreateReceiveTransport());
+
+    rtc::scoped_ptr<Call> sender_call(
+        Call::Create(Call::Config(sender_transport.get())));
+    rtc::scoped_ptr<Call> receiver_call(
+        Call::Create(Call::Config(receiver_transport.get())));
+    sender_transport->SetReceiver(receiver_call->Receiver());
+    receiver_transport->SetReceiver(sender_call->Receiver());
+
+    rtc::scoped_ptr<VideoEncoder> encoders[kNumStreams];
+    for (size_t i = 0; i < kNumStreams; ++i)
+      encoders[i].reset(VideoEncoder::Create(VideoEncoder::kVp8));
+
+    VideoSendStream* send_streams[kNumStreams];
+    VideoReceiveStream* receive_streams[kNumStreams];
+
+    test::FrameGeneratorCapturer* frame_generators[kNumStreams];
+    ScopedVector<VideoDecoder> allocated_decoders;
+    for (size_t i = 0; i < kNumStreams; ++i) {
+      uint32_t ssrc = codec_settings[i].ssrc;
+      int width = codec_settings[i].width;
+      int height = codec_settings[i].height;
+
+      VideoSendStream::Config send_config;
+      send_config.rtp.ssrcs.push_back(ssrc);
+      send_config.encoder_settings.encoder = encoders[i].get();
+      send_config.encoder_settings.payload_name = "VP8";
+      send_config.encoder_settings.payload_type = 124;
+      VideoEncoderConfig encoder_config;
+      encoder_config.streams = test::CreateVideoStreams(1);
+      VideoStream* stream = &encoder_config.streams[0];
+      stream->width = width;
+      stream->height = height;
+      stream->max_framerate = 5;
+      stream->min_bitrate_bps = stream->target_bitrate_bps =
+          stream->max_bitrate_bps = 100000;
+
+      UpdateSendConfig(i, &send_config, &encoder_config, &frame_generators[i]);
+
+      send_streams[i] =
+          sender_call->CreateVideoSendStream(send_config, encoder_config);
+      send_streams[i]->Start();
+
+      VideoReceiveStream::Config receive_config;
+      receive_config.rtp.remote_ssrc = ssrc;
+      receive_config.rtp.local_ssrc = test::CallTest::kReceiverLocalSsrc;
+      VideoReceiveStream::Decoder decoder =
+          test::CreateMatchingDecoder(send_config.encoder_settings);
+      allocated_decoders.push_back(decoder.decoder);
+      receive_config.decoders.push_back(decoder);
+
+      UpdateReceiveConfig(i, &receive_config);
+
+      receive_streams[i] =
+          receiver_call->CreateVideoReceiveStream(receive_config);
+      receive_streams[i]->Start();
+
+      frame_generators[i] = test::FrameGeneratorCapturer::Create(
+          send_streams[i]->Input(), width, height, 30,
+          Clock::GetRealTimeClock());
+      frame_generators[i]->Start();
+    }
+
+    Wait();
+
+    for (size_t i = 0; i < kNumStreams; ++i) {
+      frame_generators[i]->Stop();
+      sender_call->DestroyVideoSendStream(send_streams[i]);
+      receiver_call->DestroyVideoReceiveStream(receive_streams[i]);
+      delete frame_generators[i];
+    }
+
+    sender_transport->StopSending();
+    receiver_transport->StopSending();
+  }
+
+ protected:
+  virtual void Wait() = 0;
+  // Note: frame_generator is a point-to-pointer, since the actual instance
+  // hasn't been created at the time of this call. Only when packets/frames
+  // start flowing should this be dereferenced.
+  virtual void UpdateSendConfig(
+      size_t stream_index,
+      VideoSendStream::Config* send_config,
+      VideoEncoderConfig* encoder_config,
+      test::FrameGeneratorCapturer** frame_generator) {}
+  virtual void UpdateReceiveConfig(size_t stream_index,
+                                   VideoReceiveStream::Config* receive_config) {
+  }
+  virtual test::DirectTransport* CreateSendTransport() {
+    return new test::DirectTransport();
+  }
+  virtual test::DirectTransport* CreateReceiveTransport() {
+    return new test::DirectTransport();
+  }
+};
+
 // Each renderer verifies that it receives the expected resolution, and as soon
 // as every renderer has received a frame, the test finishes.
 TEST_F(EndToEndTest, SendsAndReceivesMultipleStreams) {
-  static const size_t kNumStreams = 3;
-
   class VideoOutputObserver : public VideoRenderer {
    public:
-    VideoOutputObserver(test::FrameGeneratorCapturer** capturer,
-                        int width,
-                        int height)
-        : capturer_(capturer),
-          width_(width),
-          height_(height),
+    VideoOutputObserver(const MultiStreamTest::CodecSettings& settings,
+                        uint32_t ssrc,
+                        test::FrameGeneratorCapturer** frame_generator)
+        : settings_(settings),
+          ssrc_(ssrc),
+          frame_generator_(frame_generator),
           done_(EventWrapper::Create()) {}
 
     void RenderFrame(const VideoFrame& video_frame,
                      int time_to_render_ms) override {
-      EXPECT_EQ(width_, video_frame.width());
-      EXPECT_EQ(height_, video_frame.height());
-      (*capturer_)->Stop();
+      EXPECT_EQ(settings_.width, video_frame.width());
+      EXPECT_EQ(settings_.height, video_frame.height());
+      (*frame_generator_)->Stop();
       done_->Set();
     }
 
+    uint32_t Ssrc() { return ssrc_; }
+
     bool IsTextureSupported() const override { return false; }
 
     EventTypeWrapper Wait() { return done_->Wait(kDefaultTimeoutMs); }
 
    private:
-    test::FrameGeneratorCapturer** capturer_;
-    int width_;
-    int height_;
+    const MultiStreamTest::CodecSettings& settings_;
+    const uint32_t ssrc_;
+    test::FrameGeneratorCapturer** const frame_generator_;
     rtc::scoped_ptr<EventWrapper> done_;
   };
 
-  struct {
-    uint32_t ssrc;
-    int width;
-    int height;
-  } codec_settings[kNumStreams] = {{1, 640, 480}, {2, 320, 240}, {3, 240, 160}};
+  class Tester : public MultiStreamTest {
+   public:
+    Tester() {}
+    virtual ~Tester() {}
 
-  test::DirectTransport sender_transport, receiver_transport;
-  rtc::scoped_ptr<Call> sender_call(
-      Call::Create(Call::Config(&sender_transport)));
-  rtc::scoped_ptr<Call> receiver_call(
-      Call::Create(Call::Config(&receiver_transport)));
-  sender_transport.SetReceiver(receiver_call->Receiver());
-  receiver_transport.SetReceiver(sender_call->Receiver());
+   protected:
+    void Wait() override {
+      for (const auto& observer : observers_) {
+        EXPECT_EQ(EventTypeWrapper::kEventSignaled, observer->Wait())
+            << "Time out waiting for from on ssrc " << observer->Ssrc();
+      }
+    }
 
-  VideoSendStream* send_streams[kNumStreams];
-  VideoReceiveStream* receive_streams[kNumStreams];
+    void UpdateSendConfig(
+        size_t stream_index,
+        VideoSendStream::Config* send_config,
+        VideoEncoderConfig* encoder_config,
+        test::FrameGeneratorCapturer** frame_generator) override {
+      observers_[stream_index].reset(new VideoOutputObserver(
+          codec_settings[stream_index], send_config->rtp.ssrcs.front(),
+          frame_generator));
+    }
 
-  VideoOutputObserver* observers[kNumStreams];
-  test::FrameGeneratorCapturer* frame_generators[kNumStreams];
+    void UpdateReceiveConfig(
+        size_t stream_index,
+        VideoReceiveStream::Config* receive_config) override {
+      receive_config->renderer = observers_[stream_index].get();
+    }
 
-  rtc::scoped_ptr<VideoEncoder> encoders[kNumStreams];
-  for (size_t i = 0; i < kNumStreams; ++i)
-    encoders[i].reset(VideoEncoder::Create(VideoEncoder::kVp8));
+   private:
+    rtc::scoped_ptr<VideoOutputObserver> observers_[kNumStreams];
+  } tester;
 
-  ScopedVector<VideoDecoder> allocated_decoders;
-  for (size_t i = 0; i < kNumStreams; ++i) {
-    uint32_t ssrc = codec_settings[i].ssrc;
-    int width = codec_settings[i].width;
-    int height = codec_settings[i].height;
-    observers[i] = new VideoOutputObserver(&frame_generators[i], width, height);
+  tester.RunTest();
+}
 
-    VideoSendStream::Config send_config;
-    send_config.rtp.ssrcs.push_back(ssrc);
-    send_config.encoder_settings.encoder = encoders[i].get();
-    send_config.encoder_settings.payload_name = "VP8";
-    send_config.encoder_settings.payload_type = 124;
-    VideoEncoderConfig encoder_config;
-    encoder_config.streams = test::CreateVideoStreams(1);
-    VideoStream* stream = &encoder_config.streams[0];
-    stream->width = width;
-    stream->height = height;
-    stream->max_framerate = 5;
-    stream->min_bitrate_bps = stream->target_bitrate_bps =
-        stream->max_bitrate_bps = 100000;
-    send_streams[i] =
-        sender_call->CreateVideoSendStream(send_config, encoder_config);
-    send_streams[i]->Start();
+TEST_F(EndToEndTest, AssignsTransportSequenceNumbers) {
+  // TODO(sprang): Extend this to verify received values once send-side BWE
+  // is in place.
 
-    VideoReceiveStream::Config receive_config;
-    receive_config.renderer = observers[i];
-    receive_config.rtp.remote_ssrc = ssrc;
-    receive_config.rtp.local_ssrc = kReceiverLocalSsrc;
-    VideoReceiveStream::Decoder decoder =
-        test::CreateMatchingDecoder(send_config.encoder_settings);
-    allocated_decoders.push_back(decoder.decoder);
-    receive_config.decoders.push_back(decoder);
-    receive_streams[i] =
-        receiver_call->CreateVideoReceiveStream(receive_config);
-    receive_streams[i]->Start();
+  static const int kExtensionId = 5;
 
-    frame_generators[i] = test::FrameGeneratorCapturer::Create(
-        send_streams[i]->Input(), width, height, 30, Clock::GetRealTimeClock());
-    frame_generators[i]->Start();
-  }
+  class RtpExtensionHeaderObserver : public test::DirectTransport {
+   public:
+    RtpExtensionHeaderObserver()
+        : done_(EventWrapper::Create()),
+          parser_(RtpHeaderParser::Create()),
+          last_seq_(0),
+          padding_observed_(false),
+          rtx_padding_observed_(false) {
+      parser_->RegisterRtpHeaderExtension(kRtpExtensionTransportSequenceNumber,
+                                          kExtensionId);
+    }
+    virtual ~RtpExtensionHeaderObserver() {}
 
-  for (size_t i = 0; i < kNumStreams; ++i) {
-    EXPECT_EQ(kEventSignaled, observers[i]->Wait())
-        << "Timed out while waiting for observer " << i << " to render.";
-  }
+    bool SendRtp(const uint8_t* data, size_t length) override {
+      RTPHeader header;
+      EXPECT_TRUE(parser_->Parse(data, length, &header));
+      if (header.extension.hasTransportSequenceNumber) {
+        if (!streams_observed_.empty()) {
+          EXPECT_EQ(static_cast<uint16_t>(last_seq_ + 1),
+                    header.extension.transportSequenceNumber);
+        }
+        last_seq_ = header.extension.transportSequenceNumber;
 
-  for (size_t i = 0; i < kNumStreams; ++i) {
-    frame_generators[i]->Stop();
-    sender_call->DestroyVideoSendStream(send_streams[i]);
-    receiver_call->DestroyVideoReceiveStream(receive_streams[i]);
-    delete frame_generators[i];
-    delete observers[i];
-  }
+        size_t payload_length =
+            length - (header.headerLength + header.paddingLength);
+        if (payload_length == 0) {
+          padding_observed_ = true;
+        } else if (header.payloadType == kSendRtxPayloadType) {
+          rtx_padding_observed_ = true;
+        } else {
+          streams_observed_.insert(header.ssrc);
+        }
 
-  sender_transport.StopSending();
-  receiver_transport.StopSending();
+        if (streams_observed_.size() == MultiStreamTest::kNumStreams &&
+            padding_observed_ && rtx_padding_observed_) {
+          done_->Set();
+        }
+      }
+      return test::DirectTransport::SendRtp(data, length);
+    }
+
+    EventTypeWrapper Wait() { return done_->Wait(kDefaultTimeoutMs); }
+
+    rtc::scoped_ptr<EventWrapper> done_;
+    rtc::scoped_ptr<RtpHeaderParser> parser_;
+    uint16_t last_seq_;
+    std::set<uint32_t> streams_observed_;
+    bool padding_observed_;
+    bool rtx_padding_observed_;
+  };
+
+  class TransportSequenceNumberTester : public MultiStreamTest {
+   public:
+    TransportSequenceNumberTester() : observer_(nullptr) {}
+    virtual ~TransportSequenceNumberTester() {}
+
+   protected:
+    void Wait() override {
+      DCHECK(observer_ != nullptr);
+      EXPECT_EQ(EventTypeWrapper::kEventSignaled, observer_->Wait());
+    }
+
+    void UpdateSendConfig(
+        size_t stream_index,
+        VideoSendStream::Config* send_config,
+        VideoEncoderConfig* encoder_config,
+        test::FrameGeneratorCapturer** frame_generator) override {
+      send_config->rtp.extensions.clear();
+      send_config->rtp.extensions.push_back(
+          RtpExtension(RtpExtension::kTransportSequenceNumber, kExtensionId));
+
+      // Force some padding to be sent.
+      const int kPaddingBitrateBps = 50000;
+      int total_target_bitrate = 0;
+      for (const VideoStream& stream : encoder_config->streams)
+        total_target_bitrate += stream.target_bitrate_bps;
+      encoder_config->min_transmit_bitrate_bps =
+          total_target_bitrate + kPaddingBitrateBps;
+
+      // Configure RTX for redundant payload padding.
+      send_config->rtp.nack.rtp_history_ms = kNackRtpHistoryMs;
+      send_config->rtp.rtx.ssrcs.push_back(kSendRtxSsrcs[0]);
+      send_config->rtp.rtx.payload_type = kSendRtxPayloadType;
+    }
+
+    void UpdateReceiveConfig(
+        size_t stream_index,
+        VideoReceiveStream::Config* receive_config) override {
+      receive_config->rtp.extensions.clear();
+      receive_config->rtp.extensions.push_back(
+          RtpExtension(RtpExtension::kTransportSequenceNumber, kExtensionId));
+    }
+
+    virtual test::DirectTransport* CreateSendTransport() {
+      observer_ = new RtpExtensionHeaderObserver();
+      return observer_;
+    }
+
+   private:
+    RtpExtensionHeaderObserver* observer_;
+  } tester;
+
+  tester.RunTest();
 }
 
 TEST_F(EndToEndTest, ObserversEncodedFrames) {
@@ -1806,7 +1990,8 @@
           num_ssrcs_(num_ssrcs),
           send_single_ssrc_first_(send_single_ssrc_first),
           ssrcs_to_observe_(num_ssrcs),
-          expect_single_ssrc_(send_single_ssrc_first) {
+          expect_single_ssrc_(send_single_ssrc_first),
+          send_stream_(nullptr) {
       for (size_t i = 0; i < num_ssrcs; ++i)
         valid_ssrcs_[ssrcs[i]] = true;
     }
@@ -1898,7 +2083,9 @@
    public:
     EncoderRateStatsTest()
         : EndToEndTest(kDefaultTimeoutMs),
-          FakeEncoder(Clock::GetRealTimeClock()) {}
+          FakeEncoder(Clock::GetRealTimeClock()),
+          send_stream_(nullptr),
+          bitrate_kbps_(0) {}
 
     void OnStreamsCreated(
         VideoSendStream* send_stream,
@@ -2534,6 +2721,8 @@
           FakeEncoder(Clock::GetRealTimeClock()),
           encoded_frames_(EventWrapper::Create()),
           packet_event_(EventWrapper::Create()),
+          sender_call_(nullptr),
+          receiver_call_(nullptr),
           sender_state_(kNetworkUp),
           sender_rtp_(0),
           sender_rtcp_(0),
diff --git a/webrtc/video/video_receive_stream.cc b/webrtc/video/video_receive_stream.cc
index 57ddee0..f91eac5 100644
--- a/webrtc/video/video_receive_stream.cc
+++ b/webrtc/video/video_receive_stream.cc
@@ -179,6 +179,8 @@
       CHECK_EQ(0, vie_channel_->SetReceiveAbsoluteSendTimeStatus(true, id));
     } else if (extension == RtpExtension::kVideoRotation) {
       CHECK_EQ(0, vie_channel_->SetReceiveVideoRotationStatus(true, id));
+    } else if (extension == RtpExtension::kTransportSequenceNumber) {
+      CHECK_EQ(0, vie_channel_->SetReceiveTransportSequenceNumber(true, id));
     } else {
       RTC_NOTREACHED() << "Unsupported RTP extension.";
     }
diff --git a/webrtc/video/video_send_stream.cc b/webrtc/video/video_send_stream.cc
index f51e0e2..3bf07c9 100644
--- a/webrtc/video/video_send_stream.cc
+++ b/webrtc/video/video_send_stream.cc
@@ -17,6 +17,7 @@
 
 #include "webrtc/base/checks.h"
 #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
+#include "webrtc/modules/pacing/include/packet_router.h"
 #include "webrtc/system_wrappers/interface/logging.h"
 #include "webrtc/system_wrappers/interface/trace_event.h"
 #include "webrtc/video/video_capture_input.h"
@@ -136,6 +137,8 @@
       CHECK_EQ(0, vie_channel_->SetSendAbsoluteSendTimeStatus(true, id));
     } else if (extension == RtpExtension::kVideoRotation) {
       CHECK_EQ(0, vie_channel_->SetSendVideoRotationStatus(true, id));
+    } else if (extension == RtpExtension::kTransportSequenceNumber) {
+      CHECK_EQ(0, vie_channel_->SetSendTransportSequenceNumber(true, id));
     } else {
       RTC_NOTREACHED() << "Registering unsupported RTP extension.";
     }
diff --git a/webrtc/video/video_send_stream_tests.cc b/webrtc/video/video_send_stream_tests.cc
index 83ce92b..8b18622 100644
--- a/webrtc/video/video_send_stream_tests.cc
+++ b/webrtc/video/video_send_stream_tests.cc
@@ -205,6 +205,49 @@
   RunBaseTest(&test);
 }
 
+TEST_F(VideoSendStreamTest, SupportsTransportWideSequenceNumbers) {
+  static const uint8_t kExtensionId = 13;
+  class TransportWideSequenceNumberObserver : public test::SendTest {
+   public:
+    TransportWideSequenceNumberObserver()
+        : SendTest(kDefaultTimeoutMs), encoder_(Clock::GetRealTimeClock()) {
+      EXPECT_TRUE(parser_->RegisterRtpHeaderExtension(
+          kRtpExtensionTransportSequenceNumber, kExtensionId));
+    }
+
+   private:
+    Action OnSendRtp(const uint8_t* packet, size_t length) override {
+      RTPHeader header;
+      EXPECT_TRUE(parser_->Parse(packet, length, &header));
+
+      EXPECT_TRUE(header.extension.hasTransportSequenceNumber);
+      EXPECT_FALSE(header.extension.hasTransmissionTimeOffset);
+      EXPECT_FALSE(header.extension.hasAbsoluteSendTime);
+
+      observation_complete_->Set();
+
+      return SEND_PACKET;
+    }
+
+    void ModifyConfigs(VideoSendStream::Config* send_config,
+                       std::vector<VideoReceiveStream::Config>* receive_configs,
+                       VideoEncoderConfig* encoder_config) override {
+      send_config->encoder_settings.encoder = &encoder_;
+      send_config->rtp.extensions.push_back(
+          RtpExtension(RtpExtension::kTransportSequenceNumber, kExtensionId));
+    }
+
+    void PerformTest() override {
+      EXPECT_EQ(kEventSignaled, Wait())
+          << "Timed out while waiting for a single RTP packet.";
+    }
+
+    test::FakeEncoder encoder_;
+  } test;
+
+  RunBaseTest(&test);
+}
+
 class FakeReceiveStatistics : public NullReceiveStatistics {
  public:
   FakeReceiveStatistics(uint32_t send_ssrc,
diff --git a/webrtc/video_engine/vie_channel.cc b/webrtc/video_engine/vie_channel.cc
index a01a408..df371ed 100644
--- a/webrtc/video_engine/vie_channel.cc
+++ b/webrtc/video_engine/vie_channel.cc
@@ -84,6 +84,7 @@
                        ProcessThread* module_process_thread,
                        RtcpIntraFrameObserver* intra_frame_observer,
                        RtcpBandwidthObserver* bandwidth_observer,
+                       SendTimeObserver* send_time_observer,
                        RemoteBitrateEstimator* remote_bitrate_estimator,
                        RtcpRttStats* rtt_stats,
                        PacedSender* paced_sender,
@@ -112,6 +113,7 @@
       paced_sender_(paced_sender),
       packet_router_(packet_router),
       bandwidth_observer_(bandwidth_observer),
+      send_time_observer_(send_time_observer),
       decoder_reset_(true),
       nack_history_size_sender_(kSendSidePacketHistorySize),
       max_nack_reordering_threshold_(kMaxPacketAgeToNack),
@@ -124,10 +126,12 @@
                                transport,
                                sender ? intra_frame_observer_ : nullptr,
                                sender ? bandwidth_observer_.get() : nullptr,
+                               sender ? send_time_observer_ : nullptr,
                                rtt_stats_,
                                &rtcp_packet_type_counter_observer_,
                                remote_bitrate_estimator,
                                paced_sender_,
+                               sender_ ? packet_router_ : nullptr,
                                &send_bitrate_observer_,
                                &send_frame_count_observer_,
                                &send_side_delay_observer_,
@@ -176,8 +180,11 @@
   module_process_thread_->DeRegisterModule(vcm_);
   module_process_thread_->DeRegisterModule(&vie_sync_);
   send_payload_router_->SetSendingRtpModules(std::list<RtpRtcp*>());
+  if (sender_ && packet_router_) {
+    for (size_t i = 0; i < num_active_rtp_rtcp_modules_; ++i)
+      packet_router_->RemoveRtpModule(rtp_rtcp_modules_[i]);
+  }
   for (RtpRtcp* rtp_rtcp : rtp_rtcp_modules_) {
-    packet_router_->RemoveRtpModule(rtp_rtcp);
     module_process_thread_->DeRegisterModule(rtp_rtcp);
     delete rtp_rtcp;
   }
@@ -334,8 +341,6 @@
   bool router_was_active = send_payload_router_->active();
   send_payload_router_->set_active(false);
   send_payload_router_->SetSendingRtpModules(std::list<RtpRtcp*>());
-  for (RtpRtcp* module : rtp_rtcp_modules_)
-    packet_router_->RemoveRtpModule(module);
 
   std::vector<RtpRtcp*> registered_modules;
   std::vector<RtpRtcp*> deregistered_modules;
@@ -376,22 +381,28 @@
   vie_receiver_.RegisterRtpRtcpModules(registered_modules);
 
   // Update the packet and payload routers with the sending RtpRtcp modules.
-  std::list<RtpRtcp*> active_send_modules;
-  for (RtpRtcp* rtp_rtcp : registered_modules) {
-    packet_router_->AddRtpModule(rtp_rtcp);
-    active_send_modules.push_back(rtp_rtcp);
+  if (sender_) {
+    std::list<RtpRtcp*> active_send_modules;
+    for (RtpRtcp* rtp_rtcp : registered_modules)
+      active_send_modules.push_back(rtp_rtcp);
+    send_payload_router_->SetSendingRtpModules(active_send_modules);
   }
-  send_payload_router_->SetSendingRtpModules(active_send_modules);
 
   if (router_was_active)
     send_payload_router_->set_active(true);
 
   // Deregister previously registered modules.
-  for (size_t i = num_active_modules; i < num_prev_active_modules; ++i)
+  for (size_t i = num_active_modules; i < num_prev_active_modules; ++i) {
     module_process_thread_->DeRegisterModule(rtp_rtcp_modules_[i]);
+    if (sender_ && packet_router_)
+      packet_router_->RemoveRtpModule(rtp_rtcp_modules_[i]);
+  }
   // Register new active modules.
-  for (size_t i = num_prev_active_modules; i < num_active_modules; ++i)
+  for (size_t i = num_prev_active_modules; i < num_active_modules; ++i) {
     module_process_thread_->RegisterModule(rtp_rtcp_modules_[i]);
+    if (sender_ && packet_router_)
+      packet_router_->AddRtpModule(rtp_rtcp_modules_[i]);
+  }
   return 0;
 }
 
@@ -675,6 +686,27 @@
   return vie_receiver_.SetReceiveVideoRotationStatus(enable, id) ? 0 : -1;
 }
 
+int ViEChannel::SetSendTransportSequenceNumber(bool enable, int id) {
+  // Disable any previous registrations of this extension to avoid errors.
+  for (RtpRtcp* rtp_rtcp : rtp_rtcp_modules_) {
+    rtp_rtcp->DeregisterSendRtpHeaderExtension(
+        kRtpExtensionTransportSequenceNumber);
+  }
+  if (!enable)
+    return 0;
+  // Enable the extension.
+  int error = 0;
+  for (RtpRtcp* rtp_rtcp : rtp_rtcp_modules_) {
+    error |= rtp_rtcp->RegisterSendRtpHeaderExtension(
+        kRtpExtensionTransportSequenceNumber, id);
+  }
+  return error;
+}
+
+int ViEChannel::SetReceiveTransportSequenceNumber(bool enable, int id) {
+  return vie_receiver_.SetReceiveTransportSequenceNumber(enable, id) ? 0 : -1;
+}
+
 void ViEChannel::SetRtcpXrRrtrStatus(bool enable) {
   rtp_rtcp_modules_[0]->SetRtcpXrRrtrStatus(enable);
 }
@@ -1121,10 +1153,12 @@
     Transport* outgoing_transport,
     RtcpIntraFrameObserver* intra_frame_callback,
     RtcpBandwidthObserver* bandwidth_callback,
+    SendTimeObserver* send_time_callback,
     RtcpRttStats* rtt_stats,
     RtcpPacketTypeCounterObserver* rtcp_packet_type_counter_observer,
     RemoteBitrateEstimator* remote_bitrate_estimator,
     PacedSender* paced_sender,
+    PacketRouter* packet_router,
     BitrateStatisticsObserver* send_bitrate_observer,
     FrameCountObserver* send_frame_count_observer,
     SendSideDelayObserver* send_side_delay_observer,
@@ -1142,10 +1176,12 @@
   configuration.rtcp_packet_type_counter_observer =
       rtcp_packet_type_counter_observer;
   configuration.paced_sender = paced_sender;
+  configuration.packet_router = packet_router;
   configuration.send_bitrate_observer = send_bitrate_observer;
   configuration.send_frame_count_observer = send_frame_count_observer;
   configuration.send_side_delay_observer = send_side_delay_observer;
   configuration.bandwidth_callback = bandwidth_callback;
+  configuration.send_time_callback = send_time_callback;
 
   std::vector<RtpRtcp*> modules;
   for (size_t i = 0; i < num_modules; ++i) {
diff --git a/webrtc/video_engine/vie_channel.h b/webrtc/video_engine/vie_channel.h
index 86969e7..6c5782e 100644
--- a/webrtc/video_engine/vie_channel.h
+++ b/webrtc/video_engine/vie_channel.h
@@ -103,6 +103,7 @@
              ProcessThread* module_process_thread,
              RtcpIntraFrameObserver* intra_frame_observer,
              RtcpBandwidthObserver* bandwidth_observer,
+             SendTimeObserver* send_time_observer,
              RemoteBitrateEstimator* remote_bitrate_estimator,
              RtcpRttStats* rtt_stats,
              PacedSender* paced_sender,
@@ -150,6 +151,8 @@
   int SetReceiveAbsoluteSendTimeStatus(bool enable, int id);
   int SetSendVideoRotationStatus(bool enable, int id);
   int SetReceiveVideoRotationStatus(bool enable, int id);
+  int SetSendTransportSequenceNumber(bool enable, int id);
+  int SetReceiveTransportSequenceNumber(bool enable, int id);
   void SetRtcpXrRrtrStatus(bool enable);
   void SetTransmissionSmoothingStatus(bool enable);
   void EnableTMMBR(bool enable);
@@ -331,10 +334,12 @@
       Transport* outgoing_transport,
       RtcpIntraFrameObserver* intra_frame_callback,
       RtcpBandwidthObserver* bandwidth_callback,
+      SendTimeObserver* send_time_observer,
       RtcpRttStats* rtt_stats,
       RtcpPacketTypeCounterObserver* rtcp_packet_type_counter_observer,
       RemoteBitrateEstimator* remote_bitrate_estimator,
       PacedSender* paced_sender,
+      PacketRouter* packet_router,
       BitrateStatisticsObserver* send_bitrate_observer,
       FrameCountObserver* send_frame_count_observer,
       SendSideDelayObserver* send_side_delay_observer,
@@ -470,6 +475,7 @@
   PacketRouter* const packet_router_;
 
   const rtc::scoped_ptr<RtcpBandwidthObserver> bandwidth_observer_;
+  SendTimeObserver* const send_time_observer_;
 
   bool decoder_reset_ GUARDED_BY(crit_);
   // Current receive codec used for codec change callback.
diff --git a/webrtc/video_engine/vie_channel_group.cc b/webrtc/video_engine/vie_channel_group.cc
index 084ffef..cf6b19c 100644
--- a/webrtc/video_engine/vie_channel_group.cc
+++ b/webrtc/video_engine/vie_channel_group.cc
@@ -15,6 +15,7 @@
 #include "webrtc/common.h"
 #include "webrtc/modules/pacing/include/paced_sender.h"
 #include "webrtc/modules/pacing/include/packet_router.h"
+#include "webrtc/modules/remote_bitrate_estimator/include/send_time_history.h"
 #include "webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_abs_send_time.h"
 #include "webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream.h"
 #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h"
@@ -137,8 +138,21 @@
 
   DISALLOW_IMPLICIT_CONSTRUCTORS(WrappingBitrateEstimator);
 };
+
+static const int64_t kSendTimeHistoryWindowMs = 2000;
+
 }  // namespace
 
+class AdaptedSendTimeHistory : public SendTimeHistory, public SendTimeObserver {
+ public:
+  AdaptedSendTimeHistory() : SendTimeHistory(kSendTimeHistoryWindowMs) {}
+  virtual ~AdaptedSendTimeHistory() {}
+
+  void OnPacketSent(uint16_t sequence_number, int64_t send_time) override {
+    SendTimeHistory::AddAndRemoveOldSendTimes(sequence_number, send_time);
+  }
+};
+
 ChannelGroup::ChannelGroup(ProcessThread* process_thread)
     : remb_(new VieRemb()),
       bitrate_allocator_(new BitrateAllocator()),
@@ -157,7 +171,8 @@
       // construction.
       bitrate_controller_(
           BitrateController::CreateBitrateController(Clock::GetRealTimeClock(),
-                                                     this)) {
+                                                     this)),
+      send_time_history_(new AdaptedSendTimeHistory()) {
   remote_bitrate_estimator_.reset(new WrappingBitrateEstimator(
       remb_.get(), Clock::GetRealTimeClock()));
 
@@ -235,8 +250,9 @@
       channel_id, engine_id, number_of_cores, transport, process_thread_,
       encoder_state_feedback_->GetRtcpIntraFrameObserver(),
       bitrate_controller_->CreateRtcpBandwidthObserver(),
-      remote_bitrate_estimator_.get(), call_stats_->rtcp_rtt_stats(),
-      pacer_.get(), packet_router_.get(), max_rtp_streams, sender));
+      send_time_history_.get(), remote_bitrate_estimator_.get(),
+      call_stats_->rtcp_rtt_stats(), pacer_.get(), packet_router_.get(),
+      max_rtp_streams, sender));
   if (channel->Init() != 0) {
     return false;
   }
diff --git a/webrtc/video_engine/vie_channel_group.h b/webrtc/video_engine/vie_channel_group.h
index 5d4d9af..3c8008f 100644
--- a/webrtc/video_engine/vie_channel_group.h
+++ b/webrtc/video_engine/vie_channel_group.h
@@ -22,6 +22,7 @@
 
 namespace webrtc {
 
+class AdaptedSendTimeHistory;
 class BitrateAllocator;
 class CallStats;
 class Config;
@@ -100,6 +101,7 @@
   rtc::scoped_ptr<ProcessThread> pacer_thread_;
 
   rtc::scoped_ptr<BitrateController> bitrate_controller_;
+  rtc::scoped_ptr<AdaptedSendTimeHistory> send_time_history_;
 };
 
 }  // namespace webrtc
diff --git a/webrtc/video_engine/vie_receiver.cc b/webrtc/video_engine/vie_receiver.cc
index 9e25080..a3ef2cf 100644
--- a/webrtc/video_engine/vie_receiver.cc
+++ b/webrtc/video_engine/vie_receiver.cc
@@ -58,6 +58,7 @@
       restored_packet_in_use_(false),
       receiving_ast_enabled_(false),
       receiving_cvo_enabled_(false),
+      receiving_tsn_enabled_(false),
       last_packet_log_ms_(-1) {
   assert(remote_bitrate_estimator);
 }
@@ -199,6 +200,22 @@
   }
 }
 
+bool ViEReceiver::SetReceiveTransportSequenceNumber(bool enable, int id) {
+  if (enable) {
+    if (rtp_header_parser_->RegisterRtpHeaderExtension(
+            kRtpExtensionTransportSequenceNumber, id)) {
+      receiving_tsn_enabled_ = true;
+      return true;
+    } else {
+      return false;
+    }
+  } else {
+    receiving_tsn_enabled_ = false;
+    return rtp_header_parser_->DeregisterRtpHeaderExtension(
+        kRtpExtensionTransportSequenceNumber);
+  }
+}
+
 int ViEReceiver::ReceivedRTPPacket(const void* rtp_packet,
                                    size_t rtp_packet_length,
                                    const PacketTime& packet_time) {
diff --git a/webrtc/video_engine/vie_receiver.h b/webrtc/video_engine/vie_receiver.h
index 62fb225..781f14f 100644
--- a/webrtc/video_engine/vie_receiver.h
+++ b/webrtc/video_engine/vie_receiver.h
@@ -63,6 +63,7 @@
   bool SetReceiveTimestampOffsetStatus(bool enable, int id);
   bool SetReceiveAbsoluteSendTimeStatus(bool enable, int id);
   bool SetReceiveVideoRotationStatus(bool enable, int id);
+  bool SetReceiveTransportSequenceNumber(bool enable, int id);
 
   void StartReceive();
   void StopReceive();
@@ -116,6 +117,7 @@
   bool restored_packet_in_use_;
   bool receiving_ast_enabled_;
   bool receiving_cvo_enabled_;
+  bool receiving_tsn_enabled_;
   int64_t last_packet_log_ms_;
 };