Wire up packet_id / send time callbacks to webrtc via libjingle.

BUG=webrtc:4173

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

Cr-Commit-Position: refs/heads/master@{#10289}
diff --git a/talk/app/webrtc/fakemediacontroller.h b/talk/app/webrtc/fakemediacontroller.h
new file mode 100644
index 0000000..5bf3e5f
--- /dev/null
+++ b/talk/app/webrtc/fakemediacontroller.h
@@ -0,0 +1,55 @@
+/*
+ * libjingle
+ * Copyright 2015 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_APP_WEBRTC_FAKEMEDIACONTROLLER_H_
+#define TALK_APP_WEBRTC_FAKEMEDIACONTROLLER_H_
+
+#include "talk/app/webrtc/mediacontroller.h"
+#include "webrtc/base/checks.h"
+
+namespace cricket {
+
+class FakeMediaController : public webrtc::MediaControllerInterface {
+ public:
+  explicit FakeMediaController(cricket::ChannelManager* channel_manager,
+                               webrtc::Call* call)
+      : channel_manager_(channel_manager), call_(call) {
+    RTC_DCHECK(nullptr != channel_manager_);
+    RTC_DCHECK(nullptr != call_);
+  }
+  ~FakeMediaController() override {}
+  webrtc::Call* call_w() override { return call_; }
+  cricket::ChannelManager* channel_manager() const override {
+    return channel_manager_;
+  }
+
+ private:
+  cricket::ChannelManager* channel_manager_;
+  webrtc::Call* call_;
+};
+}  // namespace cricket
+#endif  // TALK_APP_WEBRTC_FAKEMEDIACONTROLLER_H_
diff --git a/talk/app/webrtc/mediacontroller.cc b/talk/app/webrtc/mediacontroller.cc
index 28b007e..f7d8511 100644
--- a/talk/app/webrtc/mediacontroller.cc
+++ b/talk/app/webrtc/mediacontroller.cc
@@ -27,6 +27,7 @@
 
 #include "talk/app/webrtc/mediacontroller.h"
 
+#include "talk/session/media/channelmanager.h"
 #include "webrtc/base/bind.h"
 #include "webrtc/base/checks.h"
 #include "webrtc/call.h"
@@ -37,14 +38,16 @@
 const int kStartBandwidthBps = 300000;
 const int kMaxBandwidthBps = 2000000;
 
-class MediaController : public webrtc::MediaControllerInterface {
+class MediaController : public webrtc::MediaControllerInterface,
+                        public sigslot::has_slots<> {
  public:
   MediaController(rtc::Thread* worker_thread,
-                  webrtc::VoiceEngine* voice_engine)
-      : worker_thread_(worker_thread) {
+                  cricket::ChannelManager* channel_manager)
+      : worker_thread_(worker_thread), channel_manager_(channel_manager) {
     RTC_DCHECK(nullptr != worker_thread);
     worker_thread_->Invoke<void>(
-        rtc::Bind(&MediaController::Construct_w, this, voice_engine));
+        rtc::Bind(&MediaController::Construct_w, this,
+                  channel_manager_->media_engine()->GetVoE()));
   }
   ~MediaController() override {
     worker_thread_->Invoke<void>(
@@ -56,6 +59,10 @@
     return call_.get();
   }
 
+  cricket::ChannelManager* channel_manager() const override {
+    return channel_manager_;
+  }
+
  private:
   void Construct_w(webrtc::VoiceEngine* voice_engine)  {
     RTC_DCHECK(worker_thread_->IsCurrent());
@@ -68,10 +75,11 @@
   }
   void Destruct_w() {
     RTC_DCHECK(worker_thread_->IsCurrent());
-    call_.reset(nullptr);
+    call_.reset();
   }
 
-  rtc::Thread* worker_thread_;
+  rtc::Thread* const worker_thread_;
+  cricket::ChannelManager* const channel_manager_;
   rtc::scoped_ptr<webrtc::Call> call_;
 
   RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(MediaController);
@@ -81,7 +89,8 @@
 namespace webrtc {
 
 MediaControllerInterface* MediaControllerInterface::Create(
-    rtc::Thread* worker_thread, webrtc::VoiceEngine* voice_engine) {
-  return new MediaController(worker_thread, voice_engine);
+    rtc::Thread* worker_thread,
+    cricket::ChannelManager* channel_manager) {
+  return new MediaController(worker_thread, channel_manager);
 }
 } // namespace webrtc
diff --git a/talk/app/webrtc/mediacontroller.h b/talk/app/webrtc/mediacontroller.h
index 6879851..1b51be7 100644
--- a/talk/app/webrtc/mediacontroller.h
+++ b/talk/app/webrtc/mediacontroller.h
@@ -30,6 +30,10 @@
 
 #include "webrtc/base/thread.h"
 
+namespace cricket {
+class ChannelManager;
+}  // namespace cricket
+
 namespace webrtc {
 class Call;
 class VoiceEngine;
@@ -38,11 +42,13 @@
 // in the future will create and own RtpSenders and RtpReceivers.
 class MediaControllerInterface {
  public:
-  static MediaControllerInterface* Create(rtc::Thread* worker_thread,
-                                          webrtc::VoiceEngine* voice_engine);
+  static MediaControllerInterface* Create(
+      rtc::Thread* worker_thread,
+      cricket::ChannelManager* channel_manager);
 
   virtual ~MediaControllerInterface() {}
   virtual webrtc::Call* call_w() = 0;
+  virtual cricket::ChannelManager* channel_manager() const = 0;
 };
 }  // namespace webrtc
 
diff --git a/talk/app/webrtc/peerconnection.cc b/talk/app/webrtc/peerconnection.cc
index e7b33c4..3c0dc83 100644
--- a/talk/app/webrtc/peerconnection.cc
+++ b/talk/app/webrtc/peerconnection.cc
@@ -630,12 +630,14 @@
   // No step delay is used while allocating ports.
   port_allocator_->set_step_delay(cricket::kMinimumStepDelay);
 
-  remote_stream_factory_.reset(new RemoteMediaStreamFactory(
-      factory_->signaling_thread(), factory_->channel_manager()));
+  media_controller_.reset(factory_->CreateMediaController());
 
-  session_.reset(new WebRtcSession(
-      factory_->channel_manager(), factory_->signaling_thread(),
-      factory_->worker_thread(), port_allocator_.get()));
+  remote_stream_factory_.reset(new RemoteMediaStreamFactory(
+      factory_->signaling_thread(), media_controller_->channel_manager()));
+
+  session_.reset(
+      new WebRtcSession(media_controller_.get(), factory_->signaling_thread(),
+                        factory_->worker_thread(), port_allocator_.get()));
   stats_.reset(new StatsCollector(this));
 
   // Initialize the WebRtcSession. It creates transport channels etc.
diff --git a/talk/app/webrtc/peerconnection.h b/talk/app/webrtc/peerconnection.h
index 6a66497..c47f903 100644
--- a/talk/app/webrtc/peerconnection.h
+++ b/talk/app/webrtc/peerconnection.h
@@ -361,6 +361,7 @@
   IceGatheringState ice_gathering_state_;
 
   rtc::scoped_ptr<cricket::PortAllocator> port_allocator_;
+  rtc::scoped_ptr<MediaControllerInterface> media_controller_;
 
   // Streams added via AddStream.
   rtc::scoped_refptr<StreamCollection> local_streams_;
diff --git a/talk/app/webrtc/peerconnectionfactory.cc b/talk/app/webrtc/peerconnectionfactory.cc
index 0887754..6619f31 100644
--- a/talk/app/webrtc/peerconnectionfactory.cc
+++ b/talk/app/webrtc/peerconnectionfactory.cc
@@ -279,9 +279,11 @@
   return AudioTrackProxy::Create(signaling_thread_, track);
 }
 
-cricket::ChannelManager* PeerConnectionFactory::channel_manager() {
+webrtc::MediaControllerInterface* PeerConnectionFactory::CreateMediaController()
+    const {
   RTC_DCHECK(signaling_thread_->IsCurrent());
-  return channel_manager_.get();
+  return MediaControllerInterface::Create(worker_thread_,
+                                          channel_manager_.get());
 }
 
 rtc::Thread* PeerConnectionFactory::signaling_thread() {
diff --git a/talk/app/webrtc/peerconnectionfactory.h b/talk/app/webrtc/peerconnectionfactory.h
index c5855f4..8689199 100644
--- a/talk/app/webrtc/peerconnectionfactory.h
+++ b/talk/app/webrtc/peerconnectionfactory.h
@@ -31,6 +31,7 @@
 #include <string>
 
 #include "talk/app/webrtc/dtlsidentitystore.h"
+#include "talk/app/webrtc/mediacontroller.h"
 #include "talk/app/webrtc/mediastreaminterface.h"
 #include "talk/app/webrtc/peerconnectioninterface.h"
 #include "talk/session/media/channelmanager.h"
@@ -80,7 +81,7 @@
 
   bool StartAecDump(rtc::PlatformFile file) override;
 
-  virtual cricket::ChannelManager* channel_manager();
+  virtual webrtc::MediaControllerInterface* CreateMediaController() const;
   virtual rtc::Thread* signaling_thread();
   virtual rtc::Thread* worker_thread();
   const Options& options() const { return options_; }
diff --git a/talk/app/webrtc/statscollector_unittest.cc b/talk/app/webrtc/statscollector_unittest.cc
index 8b0a9ed..9121c69 100644
--- a/talk/app/webrtc/statscollector_unittest.cc
+++ b/talk/app/webrtc/statscollector_unittest.cc
@@ -84,8 +84,8 @@
 
 class MockWebRtcSession : public webrtc::WebRtcSession {
  public:
-  explicit MockWebRtcSession(cricket::ChannelManager* channel_manager)
-      : WebRtcSession(channel_manager,
+  explicit MockWebRtcSession(webrtc::MediaControllerInterface* media_controller)
+      : WebRtcSession(media_controller,
                       rtc::Thread::Current(),
                       rtc::Thread::Current(),
                       nullptr) {}
@@ -506,7 +506,10 @@
       : media_engine_(new cricket::FakeMediaEngine()),
         channel_manager_(
             new cricket::ChannelManager(media_engine_, rtc::Thread::Current())),
-        session_(channel_manager_.get()) {
+        media_controller_(
+            webrtc::MediaControllerInterface::Create(rtc::Thread::Current(),
+                                                     channel_manager_.get())),
+        session_(media_controller_.get()) {
     // By default, we ignore session GetStats calls.
     EXPECT_CALL(session_, GetTransportStats(_)).WillRepeatedly(Return(false));
     // Add default returns for mock classes.
@@ -760,6 +763,7 @@
 
   cricket::FakeMediaEngine* media_engine_;
   rtc::scoped_ptr<cricket::ChannelManager> channel_manager_;
+  rtc::scoped_ptr<webrtc::MediaControllerInterface> media_controller_;
   MockWebRtcSession session_;
   MockPeerConnection pc_;
   FakeDataChannelProvider data_channel_provider_;
@@ -825,8 +829,8 @@
                             Return(true)));
 
   MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
-  cricket::VideoChannel video_channel(rtc::Thread::Current(),
-      media_channel, NULL, kVideoChannelName, false);
+  cricket::VideoChannel video_channel(rtc::Thread::Current(), media_channel,
+                                      nullptr, kVideoChannelName, false);
   StatsReports reports;  // returned values.
   cricket::VideoSenderInfo video_sender_info;
   cricket::VideoMediaInfo stats_read;
@@ -871,8 +875,8 @@
                             Return(true)));
 
   MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
-  cricket::VideoChannel video_channel(rtc::Thread::Current(),
-      media_channel, NULL, kVideoChannelName, false);
+  cricket::VideoChannel video_channel(rtc::Thread::Current(), media_channel,
+                                      nullptr, kVideoChannelName, false);
 
   StatsReports reports;  // returned values.
   cricket::VideoSenderInfo video_sender_info;
@@ -946,8 +950,8 @@
   StatsCollectorForTest stats(&pc_);
 
   MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
-  cricket::VideoChannel video_channel(rtc::Thread::Current(),
-      media_channel, NULL, "video", false);
+  cricket::VideoChannel video_channel(rtc::Thread::Current(), media_channel,
+                                      nullptr, "video", false);
   AddOutgoingVideoTrackStats();
   stats.AddStream(stream_);
 
@@ -982,8 +986,8 @@
                             Return(true)));
 
   MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
-  cricket::VideoChannel video_channel(rtc::Thread::Current(),
-      media_channel, NULL, kVideoChannelName, false);
+  cricket::VideoChannel video_channel(rtc::Thread::Current(), media_channel,
+                                      nullptr, kVideoChannelName, false);
   AddOutgoingVideoTrackStats();
   stats.AddStream(stream_);
 
@@ -1046,8 +1050,8 @@
   MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
   // The transport_name known by the video channel.
   const std::string kVcName("vcname");
-  cricket::VideoChannel video_channel(rtc::Thread::Current(),
-      media_channel, NULL, kVcName, false);
+  cricket::VideoChannel video_channel(rtc::Thread::Current(), media_channel,
+                                      nullptr, kVcName, false);
   AddOutgoingVideoTrackStats();
   stats.AddStream(stream_);
 
@@ -1104,8 +1108,8 @@
   MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
   // The transport_name known by the video channel.
   const std::string kVcName("vcname");
-  cricket::VideoChannel video_channel(rtc::Thread::Current(),
-      media_channel, NULL, kVcName, false);
+  cricket::VideoChannel video_channel(rtc::Thread::Current(), media_channel,
+                                      nullptr, kVcName, false);
   AddOutgoingVideoTrackStats();
   stats.AddStream(stream_);
 
@@ -1130,8 +1134,8 @@
   MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
   // The transport_name known by the video channel.
   const std::string kVcName("vcname");
-  cricket::VideoChannel video_channel(rtc::Thread::Current(),
-      media_channel, NULL, kVcName, false);
+  cricket::VideoChannel video_channel(rtc::Thread::Current(), media_channel,
+                                      nullptr, kVcName, false);
   AddOutgoingVideoTrackStats();
   stats.AddStream(stream_);
 
@@ -1185,8 +1189,8 @@
                             Return(true)));
 
   MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
-  cricket::VideoChannel video_channel(rtc::Thread::Current(),
-      media_channel, NULL, kVideoChannelName, false);
+  cricket::VideoChannel video_channel(rtc::Thread::Current(), media_channel,
+                                      nullptr, kVideoChannelName, false);
   AddIncomingVideoTrackStats();
   stats.AddStream(stream_);
 
@@ -1494,8 +1498,8 @@
   MockVoiceMediaChannel* media_channel = new MockVoiceMediaChannel();
   // The transport_name known by the voice channel.
   const std::string kVcName("vcname");
-  cricket::VoiceChannel voice_channel(rtc::Thread::Current(),
-      media_engine_, media_channel, NULL, kVcName, false);
+  cricket::VoiceChannel voice_channel(rtc::Thread::Current(), media_engine_,
+                                      media_channel, nullptr, kVcName, false);
   AddOutgoingAudioTrackStats();
   stats.AddStream(stream_);
   stats.AddLocalAudioTrack(audio_track_, kSsrcOfTrack);
@@ -1529,8 +1533,8 @@
   MockVoiceMediaChannel* media_channel = new MockVoiceMediaChannel();
   // The transport_name known by the voice channel.
   const std::string kVcName("vcname");
-  cricket::VoiceChannel voice_channel(rtc::Thread::Current(),
-      media_engine_, media_channel, NULL, kVcName, false);
+  cricket::VoiceChannel voice_channel(rtc::Thread::Current(), media_engine_,
+                                      media_channel, nullptr, kVcName, false);
   AddIncomingAudioTrackStats();
   stats.AddStream(stream_);
 
@@ -1558,8 +1562,8 @@
   MockVoiceMediaChannel* media_channel = new MockVoiceMediaChannel();
   // The transport_name known by the voice channel.
   const std::string kVcName("vcname");
-  cricket::VoiceChannel voice_channel(rtc::Thread::Current(),
-      media_engine_, media_channel, NULL, kVcName, false);
+  cricket::VoiceChannel voice_channel(rtc::Thread::Current(), media_engine_,
+                                      media_channel, nullptr, kVcName, false);
   AddOutgoingAudioTrackStats();
   stats.AddStream(stream_);
   stats.AddLocalAudioTrack(audio_track_.get(), kSsrcOfTrack);
@@ -1619,8 +1623,8 @@
   MockVoiceMediaChannel* media_channel = new MockVoiceMediaChannel();
   // The transport_name known by the voice channel.
   const std::string kVcName("vcname");
-  cricket::VoiceChannel voice_channel(rtc::Thread::Current(),
-      media_engine_, media_channel, NULL, kVcName, false);
+  cricket::VoiceChannel voice_channel(rtc::Thread::Current(), media_engine_,
+                                      media_channel, nullptr, kVcName, false);
 
   // Create a local stream with a local audio track and adds it to the stats.
   AddOutgoingAudioTrackStats();
@@ -1706,8 +1710,8 @@
   MockVoiceMediaChannel* media_channel = new MockVoiceMediaChannel();
   // The transport_name known by the voice channel.
   const std::string kVcName("vcname");
-  cricket::VoiceChannel voice_channel(rtc::Thread::Current(),
-      media_engine_, media_channel, NULL, kVcName, false);
+  cricket::VoiceChannel voice_channel(rtc::Thread::Current(), media_engine_,
+                                      media_channel, nullptr, kVcName, false);
 
   // Create a local stream with a local audio track and adds it to the stats.
   AddOutgoingAudioTrackStats();
diff --git a/talk/app/webrtc/test/fakedatachannelprovider.h b/talk/app/webrtc/test/fakedatachannelprovider.h
index 9a8352e..ff44e58 100644
--- a/talk/app/webrtc/test/fakedatachannelprovider.h
+++ b/talk/app/webrtc/test/fakedatachannelprovider.h
@@ -25,6 +25,9 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#ifndef TALK_APP_WEBRTC_TEST_FAKEDATACHANNELPROVIDER_H_
+#define TALK_APP_WEBRTC_TEST_FAKEDATACHANNELPROVIDER_H_
+
 #include "talk/app/webrtc/datachannel.h"
 
 class FakeDataChannelProvider : public webrtc::DataChannelProviderInterface {
@@ -155,3 +158,4 @@
   std::set<uint32_t> send_ssrcs_;
   std::set<uint32_t> recv_ssrcs_;
 };
+#endif  // TALK_APP_WEBRTC_TEST_FAKEDATACHANNELPROVIDER_H_
diff --git a/talk/app/webrtc/webrtcsession.cc b/talk/app/webrtc/webrtcsession.cc
index f17dd34..af1dc61 100644
--- a/talk/app/webrtc/webrtcsession.cc
+++ b/talk/app/webrtc/webrtcsession.cc
@@ -51,7 +51,9 @@
 #include "webrtc/base/logging.h"
 #include "webrtc/base/stringencode.h"
 #include "webrtc/base/stringutils.h"
+#include "webrtc/call.h"
 #include "webrtc/p2p/base/portallocator.h"
+#include "webrtc/p2p/base/transportchannel.h"
 
 using cricket::ContentInfo;
 using cricket::ContentInfos;
@@ -529,7 +531,7 @@
   bool ice_restart_;
 };
 
-WebRtcSession::WebRtcSession(cricket::ChannelManager* channel_manager,
+WebRtcSession::WebRtcSession(webrtc::MediaControllerInterface* media_controller,
                              rtc::Thread* signaling_thread,
                              rtc::Thread* worker_thread,
                              cricket::PortAllocator* port_allocator)
@@ -543,7 +545,8 @@
       transport_controller_(new cricket::TransportController(signaling_thread,
                                                              worker_thread,
                                                              port_allocator)),
-      channel_manager_(channel_manager),
+      media_controller_(media_controller),
+      channel_manager_(media_controller_->channel_manager()),
       ice_observer_(NULL),
       ice_connection_state_(PeerConnectionInterface::kIceConnectionNew),
       ice_connection_receiving_(true),
@@ -763,9 +766,6 @@
         cricket::PORTALLOCATOR_ENABLE_LOCALHOST_CANDIDATE);
   }
 
-  media_controller_.reset(MediaControllerInterface::Create(
-      worker_thread(), channel_manager_->media_engine()->GetVoE()));
-
   return true;
 }
 
@@ -1844,7 +1844,7 @@
 
 bool WebRtcSession::CreateVoiceChannel(const cricket::ContentInfo* content) {
   voice_channel_.reset(channel_manager_->CreateVoiceChannel(
-      media_controller_.get(), transport_controller_.get(), content->name, true,
+      media_controller_, transport_controller_.get(), content->name, true,
       audio_options_));
   if (!voice_channel_) {
     return false;
@@ -1854,12 +1854,14 @@
       this, &WebRtcSession::OnDtlsSetupFailure);
 
   SignalVoiceChannelCreated();
+  voice_channel_->transport_channel()->SignalSentPacket.connect(
+      this, &WebRtcSession::OnSentPacket_w);
   return true;
 }
 
 bool WebRtcSession::CreateVideoChannel(const cricket::ContentInfo* content) {
   video_channel_.reset(channel_manager_->CreateVideoChannel(
-      media_controller_.get(), transport_controller_.get(), content->name, true,
+      media_controller_, transport_controller_.get(), content->name, true,
       video_options_));
   if (!video_channel_) {
     return false;
@@ -1869,6 +1871,8 @@
       this, &WebRtcSession::OnDtlsSetupFailure);
 
   SignalVideoChannelCreated();
+  video_channel_->transport_channel()->SignalSentPacket.connect(
+      this, &WebRtcSession::OnSentPacket_w);
   return true;
 }
 
@@ -1889,6 +1893,8 @@
       this, &WebRtcSession::OnDtlsSetupFailure);
 
   SignalDataChannelCreated();
+  data_channel_->transport_channel()->SignalSentPacket.connect(
+      this, &WebRtcSession::OnSentPacket_w);
   return true;
 }
 
@@ -2205,4 +2211,10 @@
   }
 }
 
+void WebRtcSession::OnSentPacket_w(cricket::TransportChannel* channel,
+                                   const rtc::SentPacket& sent_packet) {
+  RTC_DCHECK(worker_thread()->IsCurrent());
+  media_controller_->call_w()->OnSentPacket(sent_packet);
+}
+
 }  // namespace webrtc
diff --git a/talk/app/webrtc/webrtcsession.h b/talk/app/webrtc/webrtcsession.h
index f3dd602..d9c40d1 100644
--- a/talk/app/webrtc/webrtcsession.h
+++ b/talk/app/webrtc/webrtcsession.h
@@ -151,7 +151,7 @@
     ERROR_TRANSPORT = 2,  // transport error of some kind
   };
 
-  WebRtcSession(cricket::ChannelManager* channel_manager,
+  WebRtcSession(webrtc::MediaControllerInterface* media_controller,
                 rtc::Thread* signaling_thread,
                 rtc::Thread* worker_thread,
                 cricket::PortAllocator* port_allocator);
@@ -458,6 +458,9 @@
 
   void ReportNegotiatedCiphers(const cricket::TransportStats& stats);
 
+  void OnSentPacket_w(cricket::TransportChannel* channel,
+                      const rtc::SentPacket& sent_packet);
+
   rtc::Thread* const signaling_thread_;
   rtc::Thread* const worker_thread_;
   cricket::PortAllocator* const port_allocator_;
@@ -470,7 +473,7 @@
   bool initial_offerer_ = false;
 
   rtc::scoped_ptr<cricket::TransportController> transport_controller_;
-  rtc::scoped_ptr<MediaControllerInterface> media_controller_;
+  MediaControllerInterface* media_controller_;
   rtc::scoped_ptr<cricket::VoiceChannel> voice_channel_;
   rtc::scoped_ptr<cricket::VideoChannel> video_channel_;
   rtc::scoped_ptr<cricket::DataChannel> data_channel_;
diff --git a/talk/app/webrtc/webrtcsession_unittest.cc b/talk/app/webrtc/webrtcsession_unittest.cc
index f998ca8..9618db9 100644
--- a/talk/app/webrtc/webrtcsession_unittest.cc
+++ b/talk/app/webrtc/webrtcsession_unittest.cc
@@ -28,6 +28,7 @@
 #include <vector>
 
 #include "talk/app/webrtc/audiotrack.h"
+#include "talk/app/webrtc/fakemediacontroller.h"
 #include "talk/app/webrtc/fakemetricsobserver.h"
 #include "talk/app/webrtc/jsepicecandidate.h"
 #include "talk/app/webrtc/jsepsessiondescription.h"
@@ -44,6 +45,7 @@
 #include "talk/media/base/fakemediaengine.h"
 #include "talk/media/base/fakevideorenderer.h"
 #include "talk/media/base/mediachannel.h"
+#include "talk/media/webrtc/fakewebrtccall.h"
 #include "webrtc/p2p/base/stunserver.h"
 #include "webrtc/p2p/base/teststunserver.h"
 #include "webrtc/p2p/base/testturnserver.h"
@@ -245,12 +247,15 @@
 
 class WebRtcSessionForTest : public webrtc::WebRtcSession {
  public:
-  WebRtcSessionForTest(cricket::ChannelManager* cmgr,
+  WebRtcSessionForTest(webrtc::MediaControllerInterface* media_controller,
                        rtc::Thread* signaling_thread,
                        rtc::Thread* worker_thread,
                        cricket::PortAllocator* port_allocator,
                        webrtc::IceObserver* ice_observer)
-      : WebRtcSession(cmgr, signaling_thread, worker_thread, port_allocator) {
+      : WebRtcSession(media_controller,
+                      signaling_thread,
+                      worker_thread,
+                      port_allocator) {
     RegisterIceObserver(ice_observer);
   }
   virtual ~WebRtcSessionForTest() {}
@@ -360,24 +365,31 @@
   // TODO Investigate why ChannelManager crashes, if it's created
   // after stun_server.
   WebRtcSessionTest()
-    : media_engine_(new cricket::FakeMediaEngine()),
-      data_engine_(new cricket::FakeDataEngine()),
-      channel_manager_(new cricket::ChannelManager(
-         media_engine_, data_engine_, new cricket::CaptureManager(),
-         rtc::Thread::Current())),
-      tdesc_factory_(new cricket::TransportDescriptionFactory()),
-      desc_factory_(new cricket::MediaSessionDescriptionFactory(
-          channel_manager_.get(), tdesc_factory_.get())),
-      pss_(new rtc::PhysicalSocketServer),
-      vss_(new rtc::VirtualSocketServer(pss_.get())),
-      fss_(new rtc::FirewallSocketServer(vss_.get())),
-      ss_scope_(fss_.get()),
-      stun_socket_addr_(rtc::SocketAddress(kStunAddrHost,
-                                                 cricket::STUN_SERVER_PORT)),
-      stun_server_(cricket::TestStunServer::Create(Thread::Current(),
-                                                   stun_socket_addr_)),
-      turn_server_(Thread::Current(), kTurnUdpIntAddr, kTurnUdpExtAddr),
-      metrics_observer_(new rtc::RefCountedObject<FakeMetricsObserver>()) {
+      : media_engine_(new cricket::FakeMediaEngine()),
+        data_engine_(new cricket::FakeDataEngine()),
+        channel_manager_(
+            new cricket::ChannelManager(media_engine_,
+                                        data_engine_,
+                                        new cricket::CaptureManager(),
+                                        rtc::Thread::Current())),
+        fake_call_(webrtc::Call::Config()),
+        media_controller_(
+            webrtc::MediaControllerInterface::Create(rtc::Thread::Current(),
+                                                     channel_manager_.get())),
+        tdesc_factory_(new cricket::TransportDescriptionFactory()),
+        desc_factory_(
+            new cricket::MediaSessionDescriptionFactory(channel_manager_.get(),
+                                                        tdesc_factory_.get())),
+        pss_(new rtc::PhysicalSocketServer),
+        vss_(new rtc::VirtualSocketServer(pss_.get())),
+        fss_(new rtc::FirewallSocketServer(vss_.get())),
+        ss_scope_(fss_.get()),
+        stun_socket_addr_(
+            rtc::SocketAddress(kStunAddrHost, cricket::STUN_SERVER_PORT)),
+        stun_server_(cricket::TestStunServer::Create(Thread::Current(),
+                                                     stun_socket_addr_)),
+        turn_server_(Thread::Current(), kTurnUdpIntAddr, kTurnUdpExtAddr),
+        metrics_observer_(new rtc::RefCountedObject<FakeMetricsObserver>()) {
     cricket::ServerAddresses stun_servers;
     stun_servers.insert(stun_socket_addr_);
     allocator_.reset(new cricket::BasicPortAllocator(
@@ -405,7 +417,7 @@
       const PeerConnectionInterface::RTCConfiguration& rtc_configuration) {
     ASSERT_TRUE(session_.get() == NULL);
     session_.reset(new WebRtcSessionForTest(
-        channel_manager_.get(), rtc::Thread::Current(), rtc::Thread::Current(),
+        media_controller_.get(), rtc::Thread::Current(), rtc::Thread::Current(),
         allocator_.get(), &observer_));
     session_->SignalDataChannelOpenMessage.connect(
         this, &WebRtcSessionTest::OnDataChannelOpenMessage);
@@ -1226,8 +1238,7 @@
   //     -> Failed.
   // The Gathering state should go: New -> Gathering -> Completed.
 
-  void TestLoopbackCall(const LoopbackNetworkConfiguration& config) {
-    LoopbackNetworkManager loopback_network_manager(this, config);
+  void SetupLoopbackCall() {
     Init();
     SendAudioVideoStream1();
     SessionDescriptionInterface* offer = CreateOffer();
@@ -1238,30 +1249,29 @@
     EXPECT_EQ(PeerConnectionInterface::kIceConnectionNew,
               observer_.ice_connection_state_);
     EXPECT_EQ_WAIT(PeerConnectionInterface::kIceGatheringGathering,
-                   observer_.ice_gathering_state_,
-                   kIceCandidatesTimeout);
+                   observer_.ice_gathering_state_, kIceCandidatesTimeout);
     EXPECT_TRUE_WAIT(observer_.oncandidatesready_, kIceCandidatesTimeout);
     EXPECT_EQ_WAIT(PeerConnectionInterface::kIceGatheringComplete,
-                   observer_.ice_gathering_state_,
-                   kIceCandidatesTimeout);
+                   observer_.ice_gathering_state_, kIceCandidatesTimeout);
 
     std::string sdp;
     offer->ToString(&sdp);
-    SessionDescriptionInterface* desc =
-        webrtc::CreateSessionDescription(
-            JsepSessionDescription::kAnswer, sdp, nullptr);
+    SessionDescriptionInterface* desc = webrtc::CreateSessionDescription(
+        JsepSessionDescription::kAnswer, sdp, nullptr);
     ASSERT_TRUE(desc != NULL);
     SetRemoteDescriptionWithoutError(desc);
 
     EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionChecking,
-                   observer_.ice_connection_state_,
-                   kIceCandidatesTimeout);
+                   observer_.ice_connection_state_, kIceCandidatesTimeout);
 
     // The ice connection state is "Connected" too briefly to catch in a test.
     EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionCompleted,
-                   observer_.ice_connection_state_,
-                   kIceCandidatesTimeout);
+                   observer_.ice_connection_state_, kIceCandidatesTimeout);
+  }
 
+  void TestLoopbackCall(const LoopbackNetworkConfiguration& config) {
+    LoopbackNetworkManager loopback_network_manager(this, config);
+    SetupLoopbackCall();
     config.VerifyBestConnectionAfterIceConverge(metrics_observer_);
     // Adding firewall rule to block ping requests, which should cause
     // transport channel failure.
@@ -1300,6 +1310,25 @@
     TestLoopbackCall(config);
   }
 
+  void TestPacketOptions() {
+    media_controller_.reset(
+        new cricket::FakeMediaController(channel_manager_.get(), &fake_call_));
+    LoopbackNetworkConfiguration config;
+    LoopbackNetworkManager loopback_network_manager(this, config);
+
+    SetupLoopbackCall();
+
+    uint8_t test_packet[15] = {0};
+    rtc::PacketOptions options;
+    options.packet_id = 10;
+    media_engine_->GetVideoChannel(0)
+        ->SendRtp(test_packet, sizeof(test_packet), options);
+
+    const int kPacketTimeout = 2000;
+    EXPECT_EQ_WAIT(fake_call_.last_sent_packet().packet_id, 10, kPacketTimeout);
+    EXPECT_GT(fake_call_.last_sent_packet().send_time_ms, -1);
+  }
+
   // Adds CN codecs to FakeMediaEngine and MediaDescriptionFactory.
   void AddCNCodecs() {
     const cricket::AudioCodec kCNCodec1(102, "CN", 8000, 0, 1, 0);
@@ -1406,6 +1435,8 @@
   cricket::FakeMediaEngine* media_engine_;
   cricket::FakeDataEngine* data_engine_;
   rtc::scoped_ptr<cricket::ChannelManager> channel_manager_;
+  cricket::FakeCall fake_call_;
+  rtc::scoped_ptr<webrtc::MediaControllerInterface> media_controller_;
   rtc::scoped_ptr<cricket::TransportDescriptionFactory> tdesc_factory_;
   rtc::scoped_ptr<cricket::MediaSessionDescriptionFactory> desc_factory_;
   rtc::scoped_ptr<rtc::PhysicalSocketServer> pss_;
@@ -4154,6 +4185,10 @@
   }
 }
 
+TEST_F(WebRtcSessionTest, TestPacketOptionsAndOnPacketSent) {
+  TestPacketOptions();
+}
+
 // TODO(bemasc): Add a TestIceStatesBundle with BUNDLE enabled.  That test
 // currently fails because upon disconnection and reconnection OnIceComplete is
 // called more than once without returning to IceGatheringGathering.
diff --git a/talk/libjingle.gyp b/talk/libjingle.gyp
index 3268d01..fdf0631 100755
--- a/talk/libjingle.gyp
+++ b/talk/libjingle.gyp
@@ -46,6 +46,7 @@
           'target_name': 'libjingle_peerconnection_so',
           'type': 'shared_library',
           'dependencies': [
+            '<(webrtc_root)/system_wrappers/system_wrappers.gyp:field_trial_default',
             'libjingle_peerconnection',
           ],
           'sources': [
@@ -432,8 +433,8 @@
         '<(webrtc_root)/webrtc.gyp:webrtc',
         '<(webrtc_root)/voice_engine/voice_engine.gyp:voice_engine',
         '<(webrtc_root)/sound/sound.gyp:rtc_sound',
+        '<(webrtc_root)/system_wrappers/system_wrappers.gyp:metrics_default',
         '<(webrtc_root)/system_wrappers/system_wrappers.gyp:system_wrappers',
-        '<(webrtc_root)/system_wrappers/system_wrappers.gyp:system_wrappers_default',
         '<(webrtc_root)/libjingle/xmllite/xmllite.gyp:rtc_xmllite',
         '<(webrtc_root)/libjingle/xmpp/xmpp.gyp:rtc_xmpp',
         '<(webrtc_root)/p2p/p2p.gyp:rtc_p2p',
diff --git a/talk/libjingle_tests.gyp b/talk/libjingle_tests.gyp
index 2e42047..7440e47 100755
--- a/talk/libjingle_tests.gyp
+++ b/talk/libjingle_tests.gyp
@@ -142,6 +142,7 @@
       'dependencies': [
         '<(webrtc_root)/base/base_tests.gyp:rtc_base_tests_utils',
         'libjingle.gyp:libjingle',
+        'libjingle.gyp:libjingle_peerconnection',
         'libjingle.gyp:libjingle_p2p',
         'libjingle_unittest_main',
       ],
@@ -344,6 +345,7 @@
           'includes': [ 'build/objc_app.gypi' ],
           'dependencies': [
             '<(webrtc_root)/base/base_tests.gyp:rtc_base_tests_utils',
+            '<(webrtc_root)/system_wrappers/system_wrappers.gyp:field_trial_default',
             'libjingle.gyp:libjingle_peerconnection_objc',
           ],
           'sources': [
@@ -375,6 +377,7 @@
           'includes': [ 'build/objc_app.gypi' ],
           'dependencies': [
             '<(webrtc_root)/base/base_tests.gyp:rtc_base_tests_utils',
+            '<(webrtc_root)/system_wrappers/system_wrappers.gyp:field_trial_default',
             '<(DEPTH)/third_party/ocmock/ocmock.gyp:ocmock',
             '<(webrtc_root)/libjingle_examples.gyp:apprtc_signaling',
           ],
diff --git a/talk/media/base/constants.cc b/talk/media/base/constants.cc
index 0d0a33c..4063004 100644
--- a/talk/media/base/constants.cc
+++ b/talk/media/base/constants.cc
@@ -124,6 +124,10 @@
 const char kRtpVideoRotation6BitsHeaderExtensionForTesting[] =
     "urn:3gpp:video-orientation:6";
 
+const int kRtpTransportSequenceNumberHeaderExtensionDefaultId = 5;
+const char kRtpTransportSequenceNumberHeaderExtension[] =
+    "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions";
+
 const int kNumDefaultUnsignalledVideoRecvStreams = 0;
 
 const char kVp8CodecName[] = "VP8";
diff --git a/talk/media/base/constants.h b/talk/media/base/constants.h
index d92cb22..b6a9e56 100644
--- a/talk/media/base/constants.h
+++ b/talk/media/base/constants.h
@@ -154,6 +154,11 @@
 // We don't support 6 bit CVO. Added here for testing purpose.
 extern const char kRtpVideoRotation6BitsHeaderExtensionForTesting[];
 
+// Header extension for transport sequence number, see url for details:
+// http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions
+extern const int kRtpTransportSequenceNumberHeaderExtensionDefaultId;
+extern const char kRtpTransportSequenceNumberHeaderExtension[];
+
 extern const int kNumDefaultUnsignalledVideoRecvStreams;
 
 extern const char kVp8CodecName[];
diff --git a/talk/media/base/fakemediaengine.h b/talk/media/base/fakemediaengine.h
index 2728c7d..57d0145 100644
--- a/talk/media/base/fakemediaengine.h
+++ b/talk/media/base/fakemediaengine.h
@@ -69,18 +69,18 @@
   const std::list<std::string>& rtp_packets() const { return rtp_packets_; }
   const std::list<std::string>& rtcp_packets() const { return rtcp_packets_; }
 
-  bool SendRtp(const void* data, int len) {
+  bool SendRtp(const void* data, int len, const rtc::PacketOptions& options) {
     if (!sending_) {
       return false;
     }
     rtc::Buffer packet(reinterpret_cast<const uint8_t*>(data), len,
                        kMaxRtpPacketLen);
-    return Base::SendPacket(&packet);
+    return Base::SendPacket(&packet, options);
   }
   bool SendRtcp(const void* data, int len) {
     rtc::Buffer packet(reinterpret_cast<const uint8_t*>(data), len,
                        kMaxRtpPacketLen);
-    return Base::SendRtcp(&packet);
+    return Base::SendRtcp(&packet, rtc::PacketOptions());
   }
 
   bool CheckRtp(const void* data, int len) {
diff --git a/talk/media/base/fakenetworkinterface.h b/talk/media/base/fakenetworkinterface.h
index 275f598..418dfef 100644
--- a/talk/media/base/fakenetworkinterface.h
+++ b/talk/media/base/fakenetworkinterface.h
@@ -129,7 +129,7 @@
 
  protected:
   virtual bool SendPacket(rtc::Buffer* packet,
-                          rtc::DiffServCodePoint dscp) {
+                          const rtc::PacketOptions& options) {
     rtc::CritScope cs(&crit_);
 
     uint32_t cur_ssrc = 0;
@@ -155,7 +155,7 @@
   }
 
   virtual bool SendRtcp(rtc::Buffer* packet,
-                        rtc::DiffServCodePoint dscp) {
+                        const rtc::PacketOptions& options) {
     rtc::CritScope cs(&crit_);
     rtcp_packets_.push_back(*packet);
     if (!conf_) {
diff --git a/talk/media/base/mediachannel.h b/talk/media/base/mediachannel.h
index 05d56cf..900c51a 100644
--- a/talk/media/base/mediachannel.h
+++ b/talk/media/base/mediachannel.h
@@ -504,12 +504,10 @@
   class NetworkInterface {
    public:
     enum SocketType { ST_RTP, ST_RTCP };
-    virtual bool SendPacket(
-        rtc::Buffer* packet,
-        rtc::DiffServCodePoint dscp = rtc::DSCP_NO_CHANGE) = 0;
-    virtual bool SendRtcp(
-        rtc::Buffer* packet,
-        rtc::DiffServCodePoint dscp = rtc::DSCP_NO_CHANGE) = 0;
+    virtual bool SendPacket(rtc::Buffer* packet,
+                            const rtc::PacketOptions& options) = 0;
+    virtual bool SendRtcp(rtc::Buffer* packet,
+                          const rtc::PacketOptions& options) = 0;
     virtual int SetOption(SocketType type, rtc::Socket::Option opt,
                           int option) = 0;
     virtual ~NetworkInterface() {}
@@ -553,12 +551,12 @@
   }
 
   // Base method to send packet using NetworkInterface.
-  bool SendPacket(rtc::Buffer* packet) {
-    return DoSendPacket(packet, false);
+  bool SendPacket(rtc::Buffer* packet, const rtc::PacketOptions& options) {
+    return DoSendPacket(packet, false, options);
   }
 
-  bool SendRtcp(rtc::Buffer* packet) {
-    return DoSendPacket(packet, true);
+  bool SendRtcp(rtc::Buffer* packet, const rtc::PacketOptions& options) {
+    return DoSendPacket(packet, true, options);
   }
 
   int SetOption(NetworkInterface::SocketType type,
@@ -587,13 +585,15 @@
   }
 
  private:
-  bool DoSendPacket(rtc::Buffer* packet, bool rtcp) {
+  bool DoSendPacket(rtc::Buffer* packet,
+                    bool rtcp,
+                    const rtc::PacketOptions& options) {
     rtc::CritScope cs(&network_interface_crit_);
     if (!network_interface_)
       return false;
 
-    return (!rtcp) ? network_interface_->SendPacket(packet) :
-                     network_interface_->SendRtcp(packet);
+    return (!rtcp) ? network_interface_->SendPacket(packet, options)
+                   : network_interface_->SendRtcp(packet, options);
   }
 
   // |network_interface_| can be accessed from the worker_thread and
diff --git a/talk/media/base/rtpdataengine.cc b/talk/media/base/rtpdataengine.cc
index b2b84b9..9b26280 100644
--- a/talk/media/base/rtpdataengine.cc
+++ b/talk/media/base/rtpdataengine.cc
@@ -359,7 +359,7 @@
                   << ", timestamp=" << header.timestamp
                   << ", len=" << payload.size();
 
-  MediaChannel::SendPacket(&packet);
+  MediaChannel::SendPacket(&packet, rtc::PacketOptions());
   send_limiter_->Use(packet_len, now);
   if (result) {
     *result = SDR_SUCCESS;
diff --git a/talk/media/sctp/sctpdataengine.cc b/talk/media/sctp/sctpdataengine.cc
index 739383d..c88882d 100644
--- a/talk/media/sctp/sctpdataengine.cc
+++ b/talk/media/sctp/sctpdataengine.cc
@@ -984,7 +984,7 @@
                   << " even after adding " << kSctpOverhead
                   << " extra SCTP overhead";
   }
-  MediaChannel::SendPacket(buffer);
+  MediaChannel::SendPacket(buffer, rtc::PacketOptions());
 }
 
 bool SctpDataMediaChannel::SendQueuedStreamResets() {
diff --git a/talk/media/sctp/sctpdataengine_unittest.cc b/talk/media/sctp/sctpdataengine_unittest.cc
index 2cd0302..4706368 100644
--- a/talk/media/sctp/sctpdataengine_unittest.cc
+++ b/talk/media/sctp/sctpdataengine_unittest.cc
@@ -64,7 +64,7 @@
  protected:
   // Called to send raw packet down the wire (e.g. SCTP an packet).
   virtual bool SendPacket(rtc::Buffer* packet,
-                          rtc::DiffServCodePoint dscp) {
+                          const rtc::PacketOptions& options) {
     LOG(LS_VERBOSE) << "SctpFakeNetworkInterface::SendPacket";
 
     // TODO(ldixon): Can/should we use Buffer.TransferTo here?
@@ -93,7 +93,7 @@
   // TODO(ldixon): Refactor parent NetworkInterface class so these are not
   // required. They are RTC specific and should be in an appropriate subclass.
   virtual bool SendRtcp(rtc::Buffer* packet,
-                        rtc::DiffServCodePoint dscp) {
+                        const rtc::PacketOptions& options) {
     LOG(LS_WARNING) << "Unsupported: SctpFakeNetworkInterface::SendRtcp.";
     return false;
   }
diff --git a/talk/media/webrtc/fakewebrtccall.cc b/talk/media/webrtc/fakewebrtccall.cc
index 9f2c8e5..a0386b0 100644
--- a/talk/media/webrtc/fakewebrtccall.cc
+++ b/talk/media/webrtc/fakewebrtccall.cc
@@ -202,8 +202,7 @@
     : config_(config),
       network_state_(webrtc::kNetworkUp),
       num_created_send_streams_(0),
-      num_created_receive_streams_(0) {
-}
+      num_created_receive_streams_(0) {}
 
 FakeCall::~FakeCall() {
   EXPECT_EQ(0u, video_send_streams_.size());
@@ -367,4 +366,8 @@
 void FakeCall::SignalNetworkState(webrtc::NetworkState state) {
   network_state_ = state;
 }
+
+void FakeCall::OnSentPacket(const rtc::SentPacket& sent_packet) {
+  last_sent_packet_ = sent_packet;
+}
 }  // namespace cricket
diff --git a/talk/media/webrtc/fakewebrtccall.h b/talk/media/webrtc/fakewebrtccall.h
index 422848d..fb271f2 100644
--- a/talk/media/webrtc/fakewebrtccall.h
+++ b/talk/media/webrtc/fakewebrtccall.h
@@ -164,6 +164,7 @@
   const std::vector<FakeAudioReceiveStream*>& GetAudioReceiveStreams();
   const FakeAudioReceiveStream* GetAudioReceiveStream(uint32_t ssrc);
 
+  rtc::SentPacket last_sent_packet() const { return last_sent_packet_; }
   webrtc::NetworkState GetNetworkState() const;
   int GetNumCreatedSendStreams() const;
   int GetNumCreatedReceiveStreams() const;
@@ -200,9 +201,11 @@
   void SetBitrateConfig(
       const webrtc::Call::Config::BitrateConfig& bitrate_config) override;
   void SignalNetworkState(webrtc::NetworkState state) override;
+  void OnSentPacket(const rtc::SentPacket& sent_packet) override;
 
   webrtc::Call::Config config_;
   webrtc::NetworkState network_state_;
+  rtc::SentPacket last_sent_packet_;
   webrtc::Call::Stats stats_;
   std::vector<FakeVideoSendStream*> video_send_streams_;
   std::vector<FakeVideoReceiveStream*> video_receive_streams_;
diff --git a/talk/media/webrtc/webrtcvideoengine2.cc b/talk/media/webrtc/webrtcvideoengine2.cc
index 5ee0119..7239d7a 100644
--- a/talk/media/webrtc/webrtcvideoengine2.cc
+++ b/talk/media/webrtc/webrtcvideoengine2.cc
@@ -557,6 +557,11 @@
   rtp_header_extensions_.push_back(
       RtpHeaderExtension(kRtpVideoRotationHeaderExtension,
                          kRtpVideoRotationHeaderExtensionDefaultId));
+  if (webrtc::field_trial::FindFullName("WebRTC-SendSideBwe") == "Enabled") {
+    rtp_header_extensions_.push_back(RtpHeaderExtension(
+        kRtpTransportSequenceNumberHeaderExtension,
+        kRtpTransportSequenceNumberHeaderExtensionDefaultId));
+  }
 }
 
 WebRtcVideoEngine2::~WebRtcVideoEngine2() {
@@ -1651,12 +1656,14 @@
                                   size_t len,
                                   const webrtc::PacketOptions& options) {
   rtc::Buffer packet(data, len, kMaxRtpPacketLen);
-  return MediaChannel::SendPacket(&packet);
+  rtc::PacketOptions rtc_options;
+  rtc_options.packet_id = options.packet_id;
+  return MediaChannel::SendPacket(&packet, rtc_options);
 }
 
 bool WebRtcVideoChannel2::SendRtcp(const uint8_t* data, size_t len) {
   rtc::Buffer packet(data, len, kMaxRtpPacketLen);
-  return MediaChannel::SendRtcp(&packet);
+  return MediaChannel::SendRtcp(&packet, rtc::PacketOptions());
 }
 
 void WebRtcVideoChannel2::StartAllSendStreams() {
diff --git a/talk/media/webrtc/webrtcvideoengine2_unittest.cc b/talk/media/webrtc/webrtcvideoengine2_unittest.cc
index 5dab1d6..558bbe8 100644
--- a/talk/media/webrtc/webrtcvideoengine2_unittest.cc
+++ b/talk/media/webrtc/webrtcvideoengine2_unittest.cc
@@ -40,6 +40,7 @@
 #include "webrtc/base/arraysize.h"
 #include "webrtc/base/gunit.h"
 #include "webrtc/base/stringutils.h"
+#include "webrtc/test/field_trial.h"
 #include "webrtc/video_encoder.h"
 
 namespace {
@@ -108,9 +109,13 @@
 namespace cricket {
 class WebRtcVideoEngine2Test : public ::testing::Test {
  public:
-  WebRtcVideoEngine2Test() : WebRtcVideoEngine2Test(nullptr) {}
-  WebRtcVideoEngine2Test(WebRtcVoiceEngine* voice_engine)
-      : call_(webrtc::Call::Create(webrtc::Call::Config())),
+  WebRtcVideoEngine2Test() : WebRtcVideoEngine2Test("") {}
+  explicit WebRtcVideoEngine2Test(const char* field_trials)
+      : WebRtcVideoEngine2Test(nullptr, field_trials) {}
+  WebRtcVideoEngine2Test(WebRtcVoiceEngine* voice_engine,
+                         const char* field_trials)
+      : override_field_trials_(field_trials),
+        call_(webrtc::Call::Create(webrtc::Call::Config())),
         engine_() {
     std::vector<VideoCodec> engine_codecs = engine_.codecs();
     RTC_DCHECK(!engine_codecs.empty());
@@ -144,6 +149,7 @@
       cricket::WebRtcVideoDecoderFactory* decoder_factory,
       const std::vector<VideoCodec>& codecs);
 
+  webrtc::test::ScopedFieldTrials override_field_trials_;
   // Used in WebRtcVideoEngine2VoiceTest, but defined here so it's properly
   // initialized when the constructor is called.
   rtc::scoped_ptr<webrtc::Call> call_;
@@ -258,6 +264,26 @@
   FAIL() << "Absolute Sender Time extension not in header-extension list.";
 }
 
+class WebRtcVideoEngine2WithSendSideBweTest : public WebRtcVideoEngine2Test {
+ public:
+  WebRtcVideoEngine2WithSendSideBweTest()
+      : WebRtcVideoEngine2Test("WebRTC-SendSideBwe/Enabled/") {}
+};
+
+TEST_F(WebRtcVideoEngine2WithSendSideBweTest,
+       SupportsTransportSequenceNumberHeaderExtension) {
+  std::vector<RtpHeaderExtension> extensions = engine_.rtp_header_extensions();
+  ASSERT_FALSE(extensions.empty());
+  for (size_t i = 0; i < extensions.size(); ++i) {
+    if (extensions[i].uri == kRtpTransportSequenceNumberHeaderExtension) {
+      EXPECT_EQ(kRtpTransportSequenceNumberHeaderExtensionDefaultId,
+                extensions[i].id);
+      return;
+    }
+  }
+  FAIL() << "Transport sequence number extension not in header-extension list.";
+}
+
 TEST_F(WebRtcVideoEngine2Test, SupportsVideoRotationHeaderExtension) {
   std::vector<RtpHeaderExtension> extensions = engine_.rtp_header_extensions();
   ASSERT_FALSE(extensions.empty());
@@ -895,7 +921,9 @@
 
 class WebRtcVideoChannel2Test : public WebRtcVideoEngine2Test {
  public:
-  WebRtcVideoChannel2Test() : last_ssrc_(0) {}
+  WebRtcVideoChannel2Test() : WebRtcVideoChannel2Test("") {}
+  explicit WebRtcVideoChannel2Test(const char* field_trials)
+      : WebRtcVideoEngine2Test(field_trials), last_ssrc_(0) {}
   void SetUp() override {
     fake_call_.reset(new FakeCall(webrtc::Call::Config()));
     engine_.Init();
@@ -1171,6 +1199,26 @@
                                  webrtc::RtpExtension::kAbsSendTime);
 }
 
+class WebRtcVideoChannel2WithSendSideBweTest : public WebRtcVideoChannel2Test {
+ public:
+  WebRtcVideoChannel2WithSendSideBweTest()
+      : WebRtcVideoChannel2Test("WebRTC-SendSideBwe/Enabled/") {}
+};
+
+// Test support for transport sequence number header extension.
+TEST_F(WebRtcVideoChannel2WithSendSideBweTest,
+       SendTransportSequenceNumberHeaderExtensions) {
+  TestSetSendRtpHeaderExtensions(
+      kRtpTransportSequenceNumberHeaderExtension,
+      webrtc::RtpExtension::kTransportSequenceNumber);
+}
+TEST_F(WebRtcVideoChannel2WithSendSideBweTest,
+       RecvTransportSequenceNumberHeaderExtensions) {
+  TestSetRecvRtpHeaderExtensions(
+      kRtpTransportSequenceNumberHeaderExtension,
+      webrtc::RtpExtension::kTransportSequenceNumber);
+}
+
 // Test support for video rotation header extension.
 TEST_F(WebRtcVideoChannel2Test, SendVideoRotationHeaderExtensions) {
   TestSetSendRtpHeaderExtensions(kRtpVideoRotationHeaderExtension,
diff --git a/talk/media/webrtc/webrtcvoiceengine.cc b/talk/media/webrtc/webrtcvoiceengine.cc
index a3ea0f9..caaf87e 100644
--- a/talk/media/webrtc/webrtcvoiceengine.cc
+++ b/talk/media/webrtc/webrtcvoiceengine.cc
@@ -52,6 +52,7 @@
 #include "webrtc/base/stringutils.h"
 #include "webrtc/common.h"
 #include "webrtc/modules/audio_processing/include/audio_processing.h"
+#include "webrtc/system_wrappers/interface/field_trial.h"
 
 namespace cricket {
 namespace {
@@ -431,6 +432,11 @@
   rtp_header_extensions_.push_back(
       RtpHeaderExtension(kRtpAbsoluteSenderTimeHeaderExtension,
                          kRtpAbsoluteSenderTimeHeaderExtensionDefaultId));
+  if (webrtc::field_trial::FindFullName("WebRTC-SendSideBwe") == "Enabled") {
+    rtp_header_extensions_.push_back(RtpHeaderExtension(
+        kRtpTransportSequenceNumberHeaderExtension,
+        kRtpTransportSequenceNumberHeaderExtensionDefaultId));
+  }
   options_ = GetDefaultEngineOptions();
 }
 
diff --git a/talk/media/webrtc/webrtcvoiceengine.h b/talk/media/webrtc/webrtcvoiceengine.h
index 5121c08..cba9456 100644
--- a/talk/media/webrtc/webrtcvoiceengine.h
+++ b/talk/media/webrtc/webrtcvoiceengine.h
@@ -226,13 +226,15 @@
                const webrtc::PacketOptions& options) override {
     rtc::Buffer packet(reinterpret_cast<const uint8_t*>(data), len,
                        kMaxRtpPacketLen);
-    return VoiceMediaChannel::SendPacket(&packet);
+    rtc::PacketOptions rtc_options;
+    rtc_options.packet_id = options.packet_id;
+    return VoiceMediaChannel::SendPacket(&packet, rtc_options);
   }
 
   bool SendRtcp(const uint8_t* data, size_t len) override {
     rtc::Buffer packet(reinterpret_cast<const uint8_t*>(data), len,
                        kMaxRtpPacketLen);
-    return VoiceMediaChannel::SendRtcp(&packet);
+    return VoiceMediaChannel::SendRtcp(&packet, rtc::PacketOptions());
   }
 
   void OnError(int error);
diff --git a/talk/session/media/channel.cc b/talk/session/media/channel.cc
index c0f7f23..91a6d8c 100644
--- a/talk/session/media/channel.cc
+++ b/talk/session/media/channel.cc
@@ -67,7 +67,7 @@
 
 struct PacketMessageData : public rtc::MessageData {
   rtc::Buffer packet;
-  rtc::DiffServCodePoint dscp;
+  rtc::PacketOptions options;
 };
 
 struct ScreencastEventMessageData : public rtc::MessageData {
@@ -423,13 +423,13 @@
 }
 
 bool BaseChannel::SendPacket(rtc::Buffer* packet,
-                             rtc::DiffServCodePoint dscp) {
-  return SendPacket(false, packet, dscp);
+                             const rtc::PacketOptions& options) {
+  return SendPacket(false, packet, options);
 }
 
 bool BaseChannel::SendRtcp(rtc::Buffer* packet,
-                           rtc::DiffServCodePoint dscp) {
-  return SendPacket(true, packet, dscp);
+                           const rtc::PacketOptions& options) {
+  return SendPacket(true, packet, options);
 }
 
 int BaseChannel::SetOption(SocketType type, rtc::Socket::Option opt,
@@ -498,8 +498,9 @@
           rtcp_mux_filter_.DemuxRtcp(data, static_cast<int>(len)));
 }
 
-bool BaseChannel::SendPacket(bool rtcp, rtc::Buffer* packet,
-                             rtc::DiffServCodePoint dscp) {
+bool BaseChannel::SendPacket(bool rtcp,
+                             rtc::Buffer* packet,
+                             const rtc::PacketOptions& options) {
   // SendPacket gets called from MediaEngine, typically on an encoder thread.
   // If the thread is not our worker thread, we will post to our worker
   // so that the real work happens on our worker. This avoids us having to
@@ -512,7 +513,7 @@
     int message_id = (!rtcp) ? MSG_RTPPACKET : MSG_RTCPPACKET;
     PacketMessageData* data = new PacketMessageData;
     data->packet = packet->Pass();
-    data->dscp = dscp;
+    data->options = options;
     worker_thread_->Post(this, message_id, data);
     return true;
   }
@@ -535,7 +536,8 @@
     return false;
   }
 
-  rtc::PacketOptions options(dscp);
+  rtc::PacketOptions updated_options;
+  updated_options = options;
   // Protect if needed.
   if (srtp_filter_.IsActive()) {
     bool res;
@@ -551,21 +553,22 @@
       res = srtp_filter_.ProtectRtp(
           data, len, static_cast<int>(packet->capacity()), &len);
 #else
-      options.packet_time_params.rtp_sendtime_extension_id =
+      updated_options.packet_time_params.rtp_sendtime_extension_id =
           rtp_abs_sendtime_extn_id_;
       res = srtp_filter_.ProtectRtp(
           data, len, static_cast<int>(packet->capacity()), &len,
-          &options.packet_time_params.srtp_packet_index);
+          &updated_options.packet_time_params.srtp_packet_index);
       // If protection succeeds, let's get auth params from srtp.
       if (res) {
         uint8_t* auth_key = NULL;
         int key_len;
         res = srtp_filter_.GetRtpAuthParams(
-            &auth_key, &key_len, &options.packet_time_params.srtp_auth_tag_len);
+            &auth_key, &key_len,
+            &updated_options.packet_time_params.srtp_auth_tag_len);
         if (res) {
-          options.packet_time_params.srtp_auth_key.resize(key_len);
-          options.packet_time_params.srtp_auth_key.assign(auth_key,
-                                                          auth_key + key_len);
+          updated_options.packet_time_params.srtp_auth_key.resize(key_len);
+          updated_options.packet_time_params.srtp_auth_key.assign(
+              auth_key, auth_key + key_len);
         }
       }
 #endif
@@ -605,7 +608,7 @@
 
   // Bon voyage.
   int ret =
-      channel->SendPacket(packet->data<char>(), packet->size(), options,
+      channel->SendPacket(packet->data<char>(), packet->size(), updated_options,
                           (secure() && secure_dtls()) ? PF_SRTP_BYPASS : 0);
   if (ret != static_cast<int>(packet->size())) {
     if (channel->GetError() == EWOULDBLOCK) {
@@ -1143,7 +1146,7 @@
        it != streams.end(); ++it) {
     if (!GetStreamBySsrc(local_streams_, it->first_ssrc())) {
       if (media_channel()->AddSendStream(*it)) {
-        LOG(LS_INFO) << "Add send ssrc: " << it->ssrcs[0];
+        LOG(LS_INFO) << "Add send stream ssrc: " << it->ssrcs[0];
       } else {
         std::ostringstream desc;
         desc << "Failed to add send stream ssrc: " << it->first_ssrc();
@@ -1244,7 +1247,8 @@
     case MSG_RTPPACKET:
     case MSG_RTCPPACKET: {
       PacketMessageData* data = static_cast<PacketMessageData*>(pmsg->pdata);
-      SendPacket(pmsg->message_id == MSG_RTCPPACKET, &data->packet, data->dscp);
+      SendPacket(pmsg->message_id == MSG_RTCPPACKET, &data->packet,
+                 data->options);
       delete data;  // because it is Posted
       break;
     }
diff --git a/talk/session/media/channel.h b/talk/session/media/channel.h
index d7f93c7..27088c9 100644
--- a/talk/session/media/channel.h
+++ b/talk/session/media/channel.h
@@ -199,9 +199,8 @@
 
   // NetworkInterface implementation, called by MediaEngine
   virtual bool SendPacket(rtc::Buffer* packet,
-                          rtc::DiffServCodePoint dscp);
-  virtual bool SendRtcp(rtc::Buffer* packet,
-                        rtc::DiffServCodePoint dscp);
+                          const rtc::PacketOptions& options);
+  virtual bool SendRtcp(rtc::Buffer* packet, const rtc::PacketOptions& options);
 
   // From TransportChannel
   void OnWritableState(TransportChannel* channel);
@@ -214,8 +213,9 @@
 
   bool PacketIsRtcp(const TransportChannel* channel, const char* data,
                     size_t len);
-  bool SendPacket(bool rtcp, rtc::Buffer* packet,
-                  rtc::DiffServCodePoint dscp);
+  bool SendPacket(bool rtcp,
+                  rtc::Buffer* packet,
+                  const rtc::PacketOptions& options);
   virtual bool WantsPacket(bool rtcp, rtc::Buffer* packet);
   void HandlePacket(bool rtcp, rtc::Buffer* packet,
                     const rtc::PacketTime& packet_time);
@@ -261,7 +261,7 @@
   // Helper method to get RTP Absoulute SendTime extension header id if
   // present in remote supported extensions list.
   void MaybeCacheRtpAbsSendTimeHeaderExtension(
-    const std::vector<RtpHeaderExtension>& extensions);
+      const std::vector<RtpHeaderExtension>& extensions);
 
   bool CheckSrtpConfig(const std::vector<CryptoParams>& cryptos,
                        bool* dtls,
@@ -470,8 +470,6 @@
   bool SendIntraFrame();
   bool RequestIntraFrame();
 
-  // Configure sending media on the stream with SSRC |ssrc|
-  // If there is only one sending stream SSRC 0 can be used.
   bool SetVideoSend(uint32_t ssrc, bool enable, const VideoOptions* options);
 
  private:
diff --git a/talk/session/media/channel_unittest.cc b/talk/session/media/channel_unittest.cc
index 1b14cda..1823320 100644
--- a/talk/session/media/channel_unittest.cc
+++ b/talk/session/media/channel_unittest.cc
@@ -294,11 +294,13 @@
 
   bool SendRtp1() {
     return media_channel1_->SendRtp(rtp_packet_.c_str(),
-                                    static_cast<int>(rtp_packet_.size()));
+                                    static_cast<int>(rtp_packet_.size()),
+                                    rtc::PacketOptions());
   }
   bool SendRtp2() {
     return media_channel2_->SendRtp(rtp_packet_.c_str(),
-                                    static_cast<int>(rtp_packet_.size()));
+                                    static_cast<int>(rtp_packet_.size()),
+                                    rtc::PacketOptions());
   }
   bool SendRtcp1() {
     return media_channel1_->SendRtcp(rtcp_packet_.c_str(),
@@ -311,13 +313,13 @@
   // Methods to send custom data.
   bool SendCustomRtp1(uint32_t ssrc, int sequence_number, int pl_type = -1) {
     std::string data(CreateRtpData(ssrc, sequence_number, pl_type));
-    return media_channel1_->SendRtp(data.c_str(),
-                                    static_cast<int>(data.size()));
+    return media_channel1_->SendRtp(data.c_str(), static_cast<int>(data.size()),
+                                    rtc::PacketOptions());
   }
   bool SendCustomRtp2(uint32_t ssrc, int sequence_number, int pl_type = -1) {
     std::string data(CreateRtpData(ssrc, sequence_number, pl_type));
-    return media_channel2_->SendRtp(data.c_str(),
-                                    static_cast<int>(data.size()));
+    return media_channel2_->SendRtp(data.c_str(), static_cast<int>(data.size()),
+                                    rtc::PacketOptions());
   }
   bool SendCustomRtcp1(uint32_t ssrc) {
     std::string data(CreateRtcpData(ssrc));
@@ -957,7 +959,8 @@
      public:
       LastWordMediaChannel() : T::MediaChannel(NULL, typename T::Options()) {}
       ~LastWordMediaChannel() {
-        T::MediaChannel::SendRtp(kPcmuFrame, sizeof(kPcmuFrame));
+        T::MediaChannel::SendRtp(kPcmuFrame, sizeof(kPcmuFrame),
+                                 rtc::PacketOptions());
         T::MediaChannel::SendRtcp(kRtcpReport, sizeof(kRtcpReport));
       }
     };
@@ -1709,21 +1712,24 @@
         &error_handler, &SrtpErrorHandler::OnSrtpError);
 
     // Testing failures in sending packets.
-    EXPECT_FALSE(media_channel2_->SendRtp(kBadPacket, sizeof(kBadPacket)));
+    EXPECT_FALSE(media_channel2_->SendRtp(kBadPacket, sizeof(kBadPacket),
+                                          rtc::PacketOptions()));
     // The first failure will trigger an error.
     EXPECT_EQ_WAIT(cricket::SrtpFilter::ERROR_FAIL, error_handler.error_, 500);
     EXPECT_EQ(cricket::SrtpFilter::PROTECT, error_handler.mode_);
     error_handler.error_ = cricket::SrtpFilter::ERROR_NONE;
     error_handler.mode_ = cricket::SrtpFilter::UNPROTECT;
     // The next 250 ms failures will not trigger an error.
-    EXPECT_FALSE(media_channel2_->SendRtp(kBadPacket, sizeof(kBadPacket)));
+    EXPECT_FALSE(media_channel2_->SendRtp(kBadPacket, sizeof(kBadPacket),
+                                          rtc::PacketOptions()));
     // Wait for a while to ensure no message comes in.
     rtc::Thread::Current()->ProcessMessages(200);
     EXPECT_EQ(cricket::SrtpFilter::ERROR_NONE, error_handler.error_);
     EXPECT_EQ(cricket::SrtpFilter::UNPROTECT, error_handler.mode_);
     // Wait for a little more - the error will be triggered again.
     rtc::Thread::Current()->ProcessMessages(200);
-    EXPECT_FALSE(media_channel2_->SendRtp(kBadPacket, sizeof(kBadPacket)));
+    EXPECT_FALSE(media_channel2_->SendRtp(kBadPacket, sizeof(kBadPacket),
+                                          rtc::PacketOptions()));
     EXPECT_EQ_WAIT(cricket::SrtpFilter::ERROR_FAIL, error_handler.error_, 500);
     EXPECT_EQ(cricket::SrtpFilter::PROTECT, error_handler.mode_);
 
diff --git a/talk/session/media/channelmanager_unittest.cc b/talk/session/media/channelmanager_unittest.cc
index 6074d64..fa6aa2c 100644
--- a/talk/session/media/channelmanager_unittest.cc
+++ b/talk/session/media/channelmanager_unittest.cc
@@ -25,7 +25,7 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "talk/app/webrtc/mediacontroller.h"
+#include "talk/app/webrtc/fakemediacontroller.h"
 #include "talk/media/base/fakecapturemanager.h"
 #include "talk/media/base/fakemediaengine.h"
 #include "talk/media/base/fakevideocapturer.h"
@@ -50,37 +50,24 @@
   VideoCodec(96, "rtx", 100, 200, 300, 0),
 };
 
-class FakeMediaController : public webrtc::MediaControllerInterface {
- public:
-  explicit FakeMediaController(webrtc::Call* call) : call_(call) {
-    RTC_DCHECK(nullptr != call);
-  }
-  ~FakeMediaController() override {}
-  webrtc::Call* call_w() override { return call_; }
-
- private:
-  webrtc::Call* call_;
-};
-
 class ChannelManagerTest : public testing::Test {
  protected:
   ChannelManagerTest()
-      : fake_call_(webrtc::Call::Config()),
-        fake_mc_(&fake_call_),
-        fme_(NULL),
-        fcm_(NULL),
-        cm_(NULL) {}
+      : fme_(new cricket::FakeMediaEngine()),
+        fdme_(new cricket::FakeDataEngine()),
+        fcm_(new cricket::FakeCaptureManager()),
+        cm_(new cricket::ChannelManager(fme_,
+                                        fdme_,
+                                        fcm_,
+                                        rtc::Thread::Current())),
+        fake_call_(webrtc::Call::Config()),
+        fake_mc_(cm_, &fake_call_),
+        transport_controller_(
+            new cricket::FakeTransportController(ICEROLE_CONTROLLING)) {}
 
   virtual void SetUp() {
-    fme_ = new cricket::FakeMediaEngine();
     fme_->SetAudioCodecs(MAKE_VECTOR(kAudioCodecs));
     fme_->SetVideoCodecs(MAKE_VECTOR(kVideoCodecs));
-    fdme_ = new cricket::FakeDataEngine();
-    fcm_ = new cricket::FakeCaptureManager();
-    cm_ = new cricket::ChannelManager(
-        fme_, fdme_, fcm_, rtc::Thread::Current());
-    transport_controller_ =
-        new cricket::FakeTransportController(ICEROLE_CONTROLLING);
   }
 
   virtual void TearDown() {
@@ -93,12 +80,12 @@
   }
 
   rtc::Thread worker_;
-  cricket::FakeCall fake_call_;
-  cricket::FakeMediaController fake_mc_;
   cricket::FakeMediaEngine* fme_;
   cricket::FakeDataEngine* fdme_;
   cricket::FakeCaptureManager* fcm_;
   cricket::ChannelManager* cm_;
+  cricket::FakeCall fake_call_;
+  cricket::FakeMediaController fake_mc_;
   cricket::FakeTransportController* transport_controller_;
 };
 
diff --git a/webrtc/base/asyncpacketsocket.h b/webrtc/base/asyncpacketsocket.h
index 07cacf7..949ec67 100644
--- a/webrtc/base/asyncpacketsocket.h
+++ b/webrtc/base/asyncpacketsocket.h
@@ -34,10 +34,11 @@
 // This structure holds meta information for the packet which is about to send
 // over network.
 struct PacketOptions {
-  PacketOptions() : dscp(DSCP_NO_CHANGE) {}
-  explicit PacketOptions(DiffServCodePoint dscp) : dscp(dscp) {}
+  PacketOptions() : dscp(DSCP_NO_CHANGE), packet_id(-1) {}
+  explicit PacketOptions(DiffServCodePoint dscp) : dscp(dscp), packet_id(-1) {}
 
   DiffServCodePoint dscp;
+  int packet_id;  // 16 bits, -1 represents "not set".
   PacketTimeUpdateParams packet_time_params;
 };
 
@@ -109,6 +110,9 @@
                    const SocketAddress&,
                    const PacketTime&> SignalReadPacket;
 
+  // Emitted each time a packet is sent.
+  sigslot::signal2<AsyncPacketSocket*, const SentPacket&> SignalSentPacket;
+
   // Emitted when the socket is currently able to send.
   sigslot::signal1<AsyncPacketSocket*> SignalReadyToSend;
 
diff --git a/webrtc/base/asynctcpsocket.cc b/webrtc/base/asynctcpsocket.cc
index 66fd3f1..8e83cd1 100644
--- a/webrtc/base/asynctcpsocket.cc
+++ b/webrtc/base/asynctcpsocket.cc
@@ -268,6 +268,9 @@
     return res;
   }
 
+  rtc::SentPacket sent_packet(options.packet_id, rtc::Time());
+  SignalSentPacket(this, sent_packet);
+
   // We claim to have sent the whole thing, even if we only sent partial
   return static_cast<int>(cb);
 }
diff --git a/webrtc/base/asyncudpsocket.cc b/webrtc/base/asyncudpsocket.cc
index 3e2ecc4..51a8fa0 100644
--- a/webrtc/base/asyncudpsocket.cc
+++ b/webrtc/base/asyncudpsocket.cc
@@ -60,13 +60,19 @@
 
 int AsyncUDPSocket::Send(const void *pv, size_t cb,
                          const rtc::PacketOptions& options) {
-  return socket_->Send(pv, cb);
+  rtc::SentPacket sent_packet(options.packet_id, rtc::Time());
+  int ret = socket_->Send(pv, cb);
+  SignalSentPacket(this, sent_packet);
+  return ret;
 }
 
 int AsyncUDPSocket::SendTo(const void *pv, size_t cb,
                            const SocketAddress& addr,
                            const rtc::PacketOptions& options) {
-  return socket_->SendTo(pv, cb, addr);
+  rtc::SentPacket sent_packet(options.packet_id, rtc::Time());
+  int ret = socket_->SendTo(pv, cb, addr);
+  SignalSentPacket(this, sent_packet);
+  return ret;
 }
 
 int AsyncUDPSocket::Close() {
diff --git a/webrtc/base/base_tests.gyp b/webrtc/base/base_tests.gyp
index 4d7d74c..f25f3e7 100644
--- a/webrtc/base/base_tests.gyp
+++ b/webrtc/base/base_tests.gyp
@@ -29,6 +29,7 @@
       'dependencies': [
         'base.gyp:rtc_base',
         '<(DEPTH)/testing/gtest.gyp:gtest',
+        '<(webrtc_root)/test/test.gyp:field_trial',
       ],
       'direct_dependent_settings': {
         'defines': [
diff --git a/webrtc/base/socket.h b/webrtc/base/socket.h
index 8d98d27..22326cb 100644
--- a/webrtc/base/socket.h
+++ b/webrtc/base/socket.h
@@ -124,6 +124,15 @@
   return (e == EWOULDBLOCK) || (e == EAGAIN) || (e == EINPROGRESS);
 }
 
+struct SentPacket {
+  SentPacket() : packet_id(-1), send_time_ms(-1) {}
+  SentPacket(int packet_id, int64_t send_time_ms)
+      : packet_id(packet_id), send_time_ms(send_time_ms) {}
+
+  int packet_id;
+  int64_t send_time_ms;
+};
+
 // General interface for the socket implementations of various networks.  The
 // methods match those of normal UNIX sockets very closely.
 class Socket {
diff --git a/webrtc/base/unittest_main.cc b/webrtc/base/unittest_main.cc
index e243c52..f952b2d 100644
--- a/webrtc/base/unittest_main.cc
+++ b/webrtc/base/unittest_main.cc
@@ -19,9 +19,16 @@
 #include "webrtc/base/gunit.h"
 #include "webrtc/base/logging.h"
 #include "webrtc/base/ssladapter.h"
+#include "webrtc/test/field_trial.h"
 
 DEFINE_bool(help, false, "prints this message");
 DEFINE_string(log, "", "logging options to use");
+DEFINE_string(
+    force_fieldtrials,
+    "",
+    "Field trials control experimental feature code which can be forced. "
+    "E.g. running with --force_fieldtrials=WebRTC-FooFeature/Enable/"
+    " will assign the group Enable to field trial WebRTC-FooFeature.");
 #if defined(WEBRTC_WIN)
 DEFINE_int(crt_break_alloc, -1, "memory allocation to break on");
 DEFINE_bool(default_error_handlers, false,
@@ -61,6 +68,8 @@
     return 0;
   }
 
+  webrtc::test::InitFieldTrialsFromString(FLAG_force_fieldtrials);
+
 #if defined(WEBRTC_WIN)
   if (!FLAG_default_error_handlers) {
     // Make sure any errors don't throw dialogs hanging the test run.
diff --git a/webrtc/call.h b/webrtc/call.h
index 033e1a2..e6e8cde 100644
--- a/webrtc/call.h
+++ b/webrtc/call.h
@@ -16,6 +16,7 @@
 #include "webrtc/common_types.h"
 #include "webrtc/audio_receive_stream.h"
 #include "webrtc/audio_send_stream.h"
+#include "webrtc/base/socket.h"
 #include "webrtc/video_receive_stream.h"
 #include "webrtc/video_send_stream.h"
 
@@ -137,6 +138,8 @@
       const Config::BitrateConfig& bitrate_config) = 0;
   virtual void SignalNetworkState(NetworkState state) = 0;
 
+  virtual void OnSentPacket(const rtc::SentPacket& sent_packet) = 0;
+
   virtual ~Call() {}
 };
 
diff --git a/webrtc/call/call.cc b/webrtc/call/call.cc
index 0ccfb61..a32a823 100644
--- a/webrtc/call/call.cc
+++ b/webrtc/call/call.cc
@@ -77,6 +77,8 @@
       const webrtc::Call::Config::BitrateConfig& bitrate_config) override;
   void SignalNetworkState(NetworkState state) override;
 
+  void OnSentPacket(const rtc::SentPacket& sent_packet) override;
+
  private:
   DeliveryStatus DeliverRtcp(MediaType media_type, const uint8_t* packet,
                              size_t length);
@@ -411,6 +413,10 @@
   }
 }
 
+void Call::OnSentPacket(const rtc::SentPacket& sent_packet) {
+  channel_group_->OnSentPacket(sent_packet);
+}
+
 void Call::ConfigureSync(const std::string& sync_group) {
   // Set sync only if there was no previous one.
   if (config_.voice_engine == nullptr || sync_group.empty())
diff --git a/webrtc/config.cc b/webrtc/config.cc
index ddff931..3a74e52 100644
--- a/webrtc/config.cc
+++ b/webrtc/config.cc
@@ -36,7 +36,7 @@
 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";
+    "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions";
 
 bool RtpExtension::IsSupportedForAudio(const std::string& name) {
   return name == webrtc::RtpExtension::kAbsSendTime ||
diff --git a/webrtc/libjingle_examples.gyp b/webrtc/libjingle_examples.gyp
index abe7e8a..ab88818 100755
--- a/webrtc/libjingle_examples.gyp
+++ b/webrtc/libjingle_examples.gyp
@@ -81,6 +81,7 @@
           ],
           'dependencies': [
             '../talk/libjingle.gyp:libjingle_peerconnection',
+            '<(webrtc_root)/system_wrappers/system_wrappers.gyp:field_trial_default',
             '<@(libjingle_tests_additional_deps)',
           ],
           'conditions': [
@@ -139,6 +140,7 @@
           'target_name': 'apprtc_common',
           'type': 'static_library',
           'dependencies': [
+            '<(webrtc_root)/system_wrappers/system_wrappers.gyp:field_trial_default',
             '../talk/libjingle.gyp:libjingle_peerconnection_objc',
           ],
           'sources': [
diff --git a/webrtc/modules/remote_bitrate_estimator/transport_feedback_adapter.cc b/webrtc/modules/remote_bitrate_estimator/transport_feedback_adapter.cc
index 5f51bc5..11922d2 100644
--- a/webrtc/modules/remote_bitrate_estimator/transport_feedback_adapter.cc
+++ b/webrtc/modules/remote_bitrate_estimator/transport_feedback_adapter.cc
@@ -49,11 +49,17 @@
   }
 }
 
-void TransportFeedbackAdapter::OnPacketSent(const PacketInfo& info) {
+void TransportFeedbackAdapter::OnSentPacket(const PacketInfo& info) {
   rtc::CritScope cs(&lock_);
   send_time_history_.AddAndRemoveOld(info);
 }
 
+void TransportFeedbackAdapter::UpdateSendTime(uint16_t sequence_number,
+                                              int64_t send_time_ms) {
+  rtc::CritScope cs(&lock_);
+  send_time_history_.UpdateSendTime(sequence_number, send_time_ms);
+}
+
 void TransportFeedbackAdapter::OnTransportFeedback(
     const rtcp::TransportFeedback& feedback) {
   int64_t timestamp_us = feedback.GetBaseTimeUs();
diff --git a/webrtc/modules/remote_bitrate_estimator/transport_feedback_adapter.h b/webrtc/modules/remote_bitrate_estimator/transport_feedback_adapter.h
index 56b2c73..7267ca0 100644
--- a/webrtc/modules/remote_bitrate_estimator/transport_feedback_adapter.h
+++ b/webrtc/modules/remote_bitrate_estimator/transport_feedback_adapter.h
@@ -33,7 +33,9 @@
                            ProcessThread* process_thread);
   virtual ~TransportFeedbackAdapter();
 
-  void OnPacketSent(const PacketInfo& info) override;
+  void OnSentPacket(const PacketInfo& info) override;
+
+  void UpdateSendTime(uint16_t sequence_number, int64_t send_time_ms);
 
   void OnTransportFeedback(const rtcp::TransportFeedback& feedback) override;
 
diff --git a/webrtc/modules/remote_bitrate_estimator/transport_feedback_adapter_unittest.cc b/webrtc/modules/remote_bitrate_estimator/transport_feedback_adapter_unittest.cc
index 1bf4b1ec..3504ca7 100644
--- a/webrtc/modules/remote_bitrate_estimator/transport_feedback_adapter_unittest.cc
+++ b/webrtc/modules/remote_bitrate_estimator/transport_feedback_adapter_unittest.cc
@@ -103,9 +103,9 @@
   }
 
   // Utility method, to reset arrival_time_ms before adding send time.
-  void OnPacketSent(PacketInfo info) {
+  void OnSentPacket(PacketInfo info) {
     info.arrival_time_ms = 0;
-    adapter_->OnPacketSent(info);
+    adapter_->OnSentPacket(info);
   }
 
   SimulatedClock clock_;
@@ -125,7 +125,7 @@
   packets.push_back(PacketInfo(140, 240, 4, 1500, true));
 
   for (const PacketInfo& packet : packets)
-    OnPacketSent(packet);
+    OnSentPacket(packet);
 
   rtcp::TransportFeedback feedback;
   feedback.WithBase(packets[0].sequence_number,
@@ -160,7 +160,7 @@
 
   for (const PacketInfo& packet : packets) {
     if (packet.sequence_number >= kSendSideDropBefore)
-      OnPacketSent(packet);
+      OnSentPacket(packet);
   }
 
   rtcp::TransportFeedback feedback;
@@ -199,7 +199,7 @@
   packets.push_back(PacketInfo(kHighArrivalTimeMs, 220, 2, 1500, true));
 
   for (const PacketInfo& packet : packets)
-    OnPacketSent(packet);
+    OnSentPacket(packet);
 
   for (size_t i = 0; i < packets.size(); ++i) {
     rtc::scoped_ptr<rtcp::TransportFeedback> feedback(
@@ -263,8 +263,8 @@
 
   // Packets will be added to send history.
   for (const PacketInfo& packet : sent_packets)
-    OnPacketSent(packet);
-  OnPacketSent(info);
+    OnSentPacket(packet);
+  OnSentPacket(info);
 
   // Create expected feedback and send into adapter.
   rtc::scoped_ptr<rtcp::TransportFeedback> feedback(
diff --git a/webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h b/webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h
index a262a07..7789743 100644
--- a/webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h
+++ b/webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h
@@ -313,7 +313,7 @@
 
   // Note: Transport-wide sequence number as sequence number. Arrival time
   // must be set to 0.
-  virtual void OnPacketSent(const PacketInfo& info) = 0;
+  virtual void OnSentPacket(const PacketInfo& info) = 0;
 
   virtual void OnTransportFeedback(const rtcp::TransportFeedback& feedback) = 0;
 };
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_sender.cc b/webrtc/modules/rtp_rtcp/source/rtp_sender.cc
index 0c67c69..2ddc356 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_sender.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtp_sender.cc
@@ -672,8 +672,8 @@
       break;
 
     if (using_transport_seq && transport_feedback_observer_) {
-      transport_feedback_observer_->OnPacketSent(PacketInfo(
-          0, now_ms, options.packet_id, length, true));
+      transport_feedback_observer_->OnSentPacket(
+          PacketInfo(0, now_ms, options.packet_id, length, true));
     }
 
     bytes_sent += padding_bytes_in_packet;
@@ -934,7 +934,7 @@
     media_has_been_sent_ = true;
   }
   if (using_transport_seq && transport_feedback_observer_) {
-    transport_feedback_observer_->OnPacketSent(
+    transport_feedback_observer_->OnSentPacket(
         PacketInfo(0, now_ms, options.packet_id, length, true));
   }
   UpdateRtpStats(buffer_to_send_ptr, length, rtp_header, send_over_rtx,
diff --git a/webrtc/p2p/base/dtlstransportchannel.cc b/webrtc/p2p/base/dtlstransportchannel.cc
index bba7eb9..148a191 100644
--- a/webrtc/p2p/base/dtlstransportchannel.cc
+++ b/webrtc/p2p/base/dtlstransportchannel.cc
@@ -101,6 +101,8 @@
       &DtlsTransportChannelWrapper::OnWritableState);
   channel_->SignalReadPacket.connect(this,
       &DtlsTransportChannelWrapper::OnReadPacket);
+  channel_->SignalSentPacket.connect(
+      this, &DtlsTransportChannelWrapper::OnSentPacket);
   channel_->SignalReadyToSend.connect(this,
       &DtlsTransportChannelWrapper::OnReadyToSend);
   channel_->SignalGatheringState.connect(
@@ -510,6 +512,14 @@
   }
 }
 
+void DtlsTransportChannelWrapper::OnSentPacket(
+    TransportChannel* channel,
+    const rtc::SentPacket& sent_packet) {
+  ASSERT(rtc::Thread::Current() == worker_thread_);
+
+  SignalSentPacket(this, sent_packet);
+}
+
 void DtlsTransportChannelWrapper::OnReadyToSend(TransportChannel* channel) {
   if (writable()) {
     SignalReadyToSend(this);
diff --git a/webrtc/p2p/base/dtlstransportchannel.h b/webrtc/p2p/base/dtlstransportchannel.h
index 9a2ccde..b445c69 100644
--- a/webrtc/p2p/base/dtlstransportchannel.h
+++ b/webrtc/p2p/base/dtlstransportchannel.h
@@ -209,6 +209,8 @@
   void OnWritableState(TransportChannel* channel);
   void OnReadPacket(TransportChannel* channel, const char* data, size_t size,
                     const rtc::PacketTime& packet_time, int flags);
+  void OnSentPacket(TransportChannel* channel,
+                    const rtc::SentPacket& sent_packet);
   void OnReadyToSend(TransportChannel* channel);
   void OnReceivingState(TransportChannel* channel);
   void OnDtlsEvent(rtc::StreamInterface* stream_, int sig, int err);
@@ -223,7 +225,8 @@
 
   Transport* transport_;  // The transport_ that created us.
   rtc::Thread* worker_thread_;  // Everything should occur on this thread.
-  TransportChannelImpl* channel_;  // Underlying channel, owned by transport_.
+  // Underlying channel, owned by transport_.
+  TransportChannelImpl* const channel_;
   rtc::scoped_ptr<rtc::SSLStreamAdapter> dtls_;  // The DTLS stream
   StreamInterfaceChannel* downward_;  // Wrapper for channel_, owned by dtls_.
   std::vector<std::string> srtp_ciphers_;  // SRTP ciphers to use with DTLS.
diff --git a/webrtc/p2p/base/dtlstransportchannel_unittest.cc b/webrtc/p2p/base/dtlstransportchannel_unittest.cc
index 460e294..07e3b87 100644
--- a/webrtc/p2p/base/dtlstransportchannel_unittest.cc
+++ b/webrtc/p2p/base/dtlstransportchannel_unittest.cc
@@ -33,6 +33,7 @@
 static const char kIcePwd1[] = "TESTICEPWD00000000000001";
 static const size_t kPacketNumOffset = 8;
 static const size_t kPacketHeaderLen = 12;
+static const int kFakePacketId = 0x1234;
 
 static bool IsRtpLeadByte(uint8_t b) {
   return ((b & 0xC0) == 0x80);
@@ -86,6 +87,8 @@
         &DtlsTestClient::OnTransportChannelWritableState);
       channel->SignalReadPacket.connect(this,
         &DtlsTestClient::OnTransportChannelReadPacket);
+      channel->SignalSentPacket.connect(
+          this, &DtlsTestClient::OnTransportChannelSentPacket);
       channels_.push_back(channel);
 
       // Hook the raw packets so that we can verify they are encrypted.
@@ -259,6 +262,7 @@
       // Only set the bypass flag if we've activated DTLS.
       int flags = (certificate_ && srtp) ? cricket::PF_SRTP_BYPASS : 0;
       rtc::PacketOptions packet_options;
+      packet_options.packet_id = kFakePacketId;
       int rv = channels_[channel]->SendPacket(
           packet.get(), size, packet_options, flags);
       ASSERT_GT(rv, 0);
@@ -338,6 +342,13 @@
     ASSERT_EQ(expected_flags, flags);
   }
 
+  void OnTransportChannelSentPacket(cricket::TransportChannel* channel,
+                                    const rtc::SentPacket& sent_packet) {
+    sent_packet_ = sent_packet;
+  }
+
+  rtc::SentPacket sent_packet() const { return sent_packet_; }
+
   // Hook into the raw packet stream to make sure DTLS packets are encrypted.
   void OnFakeTransportChannelReadPacket(cricket::TransportChannel* channel,
                                         const char* data, size_t size,
@@ -378,6 +389,7 @@
   bool negotiated_dtls_;
   bool received_dtls_client_hello_;
   bool received_dtls_server_hello_;
+  rtc::SentPacket sent_packet_;
 };
 
 
@@ -558,6 +570,15 @@
   TestTransfer(0, 1000, 100, false);
 }
 
+// Connect without DTLS, and transfer some data.
+TEST_F(DtlsTransportChannelTest, TestOnSentPacket) {
+  ASSERT_TRUE(Connect());
+  EXPECT_EQ(client1_.sent_packet().send_time_ms, -1);
+  TestTransfer(0, 1000, 100, false);
+  EXPECT_EQ(kFakePacketId, client1_.sent_packet().packet_id);
+  EXPECT_GE(client1_.sent_packet().send_time_ms, 0);
+}
+
 // Create two channels without DTLS, and transfer some data.
 TEST_F(DtlsTransportChannelTest, TestTransferTwoChannels) {
   SetChannelCount(2);
diff --git a/webrtc/p2p/base/faketransportcontroller.h b/webrtc/p2p/base/faketransportcontroller.h
index 7d8e3d7..3e656fa 100644
--- a/webrtc/p2p/base/faketransportcontroller.h
+++ b/webrtc/p2p/base/faketransportcontroller.h
@@ -31,10 +31,12 @@
 
 class FakeTransport;
 
+namespace {
 struct PacketMessageData : public rtc::MessageData {
   PacketMessageData(const char* data, size_t len) : packet(data, len) {}
   rtc::Buffer packet;
 };
+}  // namespace
 
 // Fake transport channel class, which can be passed to anything that needs a
 // transport channel. Can be informed of another FakeTransportChannel via
@@ -208,6 +210,8 @@
     } else {
       rtc::Thread::Current()->Send(this, 0, packet);
     }
+    rtc::SentPacket sent_packet(options.packet_id, rtc::Time());
+    SignalSentPacket(this, sent_packet);
     return static_cast<int>(len);
   }
   int SetOption(rtc::Socket::Option opt, int value) override { return true; }
diff --git a/webrtc/p2p/base/p2ptransportchannel.cc b/webrtc/p2p/base/p2ptransportchannel.cc
index fc72131..9d598f5 100644
--- a/webrtc/p2p/base/p2ptransportchannel.cc
+++ b/webrtc/p2p/base/p2ptransportchannel.cc
@@ -435,6 +435,7 @@
   port->SignalDestroyed.connect(this, &P2PTransportChannel::OnPortDestroyed);
   port->SignalRoleConflict.connect(
       this, &P2PTransportChannel::OnRoleConflict);
+  port->SignalSentPacket.connect(this, &P2PTransportChannel::OnSentPacket);
 
   // Attempt to create a connection from this new port to all of the remote
   // candidates that we were given so far.
@@ -1356,6 +1357,13 @@
   }
 }
 
+void P2PTransportChannel::OnSentPacket(PortInterface* port,
+                                       const rtc::SentPacket& sent_packet) {
+  ASSERT(worker_thread_ == rtc::Thread::Current());
+
+  SignalSentPacket(this, sent_packet);
+}
+
 void P2PTransportChannel::OnReadyToSend(Connection* connection) {
   if (connection == best_connection_ && writable()) {
     SignalReadyToSend(this);
diff --git a/webrtc/p2p/base/p2ptransportchannel.h b/webrtc/p2p/base/p2ptransportchannel.h
index 5249639..51979df 100644
--- a/webrtc/p2p/base/p2ptransportchannel.h
+++ b/webrtc/p2p/base/p2ptransportchannel.h
@@ -207,6 +207,7 @@
   void OnConnectionStateChange(Connection* connection);
   void OnReadPacket(Connection *connection, const char *data, size_t len,
                     const rtc::PacketTime& packet_time);
+  void OnSentPacket(PortInterface* port, const rtc::SentPacket& sent_packet);
   void OnReadyToSend(Connection* connection);
   void OnConnectionDestroyed(Connection *connection);
 
diff --git a/webrtc/p2p/base/port.cc b/webrtc/p2p/base/port.cc
index 39fff5f..d34b05f 100644
--- a/webrtc/p2p/base/port.cc
+++ b/webrtc/p2p/base/port.cc
@@ -310,6 +310,10 @@
   }
 }
 
+void Port::OnSentPacket(const rtc::SentPacket& sent_packet) {
+  PortInterface::SignalSentPacket(this, sent_packet);
+}
+
 void Port::OnReadyToSend() {
   AddressMap::iterator iter = connections_.begin();
   for (; iter != connections_.end(); ++iter) {
diff --git a/webrtc/p2p/base/port.h b/webrtc/p2p/base/port.h
index dc54876..01c45f2 100644
--- a/webrtc/p2p/base/port.h
+++ b/webrtc/p2p/base/port.h
@@ -275,6 +275,9 @@
                             IceMessage* stun_msg,
                             const std::string& remote_ufrag);
 
+  // Called when a packet has been sent to the socket.
+  void OnSentPacket(const rtc::SentPacket& sent_packet);
+
   // Called when the socket is currently able to send.
   void OnReadyToSend();
 
diff --git a/webrtc/p2p/base/portinterface.h b/webrtc/p2p/base/portinterface.h
index 0c5948e..0f77036 100644
--- a/webrtc/p2p/base/portinterface.h
+++ b/webrtc/p2p/base/portinterface.h
@@ -14,6 +14,7 @@
 #include <string>
 
 #include "webrtc/p2p/base/transport.h"
+#include "webrtc/base/asyncpacketsocket.h"
 #include "webrtc/base/socketaddress.h"
 
 namespace rtc {
@@ -112,6 +113,9 @@
   sigslot::signal4<PortInterface*, const char*, size_t,
                    const rtc::SocketAddress&> SignalReadPacket;
 
+  // Emitted each time a packet is sent on this port.
+  sigslot::signal2<PortInterface*, const rtc::SentPacket&> SignalSentPacket;
+
   virtual std::string ToString() const = 0;
 
  protected:
diff --git a/webrtc/p2p/base/relayport.cc b/webrtc/p2p/base/relayport.cc
index ccddab0..88adcf2 100644
--- a/webrtc/p2p/base/relayport.cc
+++ b/webrtc/p2p/base/relayport.cc
@@ -144,6 +144,10 @@
     const char* data, size_t size,
     const rtc::SocketAddress& remote_addr,
     const rtc::PacketTime& packet_time);
+
+  void OnSentPacket(rtc::AsyncPacketSocket* socket,
+                    const rtc::SentPacket& sent_packet);
+
   // Called when the socket is currently able to send.
   void OnReadyToSend(rtc::AsyncPacketSocket* socket);
 
@@ -508,6 +512,7 @@
 
   // Otherwise, create the new connection and configure any socket options.
   socket->SignalReadPacket.connect(this, &RelayEntry::OnReadPacket);
+  socket->SignalSentPacket.connect(this, &RelayEntry::OnSentPacket);
   socket->SignalReadyToSend.connect(this, &RelayEntry::OnReadyToSend);
   current_connection_ = new RelayConnection(ra, socket, port()->thread());
   for (size_t i = 0; i < port_->options().size(); ++i) {
@@ -747,6 +752,11 @@
                       PROTO_UDP, packet_time);
 }
 
+void RelayEntry::OnSentPacket(rtc::AsyncPacketSocket* socket,
+                              const rtc::SentPacket& sent_packet) {
+  port_->OnSentPacket(sent_packet);
+}
+
 void RelayEntry::OnReadyToSend(rtc::AsyncPacketSocket* socket) {
   if (connected()) {
     port_->OnReadyToSend();
diff --git a/webrtc/p2p/base/stunport.cc b/webrtc/p2p/base/stunport.cc
index 615bbfe..1598fe4 100644
--- a/webrtc/p2p/base/stunport.cc
+++ b/webrtc/p2p/base/stunport.cc
@@ -217,6 +217,7 @@
     }
     socket_->SignalReadPacket.connect(this, &UDPPort::OnReadPacket);
   }
+  socket_->SignalSentPacket.connect(this, &UDPPort::OnSentPacket);
   socket_->SignalReadyToSend.connect(this, &UDPPort::OnReadyToSend);
   socket_->SignalAddressReady.connect(this, &UDPPort::OnLocalAddressReady);
   requests_.SignalSendPacket.connect(this, &UDPPort::OnSendPacket);
@@ -329,6 +330,11 @@
   }
 }
 
+void UDPPort::OnSentPacket(rtc::AsyncPacketSocket* socket,
+                           const rtc::SentPacket& sent_packet) {
+  Port::OnSentPacket(sent_packet);
+}
+
 void UDPPort::OnReadyToSend(rtc::AsyncPacketSocket* socket) {
   Port::OnReadyToSend();
 }
diff --git a/webrtc/p2p/base/stunport.h b/webrtc/p2p/base/stunport.h
index 488739c..62b23cf 100644
--- a/webrtc/p2p/base/stunport.h
+++ b/webrtc/p2p/base/stunport.h
@@ -140,6 +140,9 @@
                     const rtc::SocketAddress& remote_addr,
                     const rtc::PacketTime& packet_time);
 
+  void OnSentPacket(rtc::AsyncPacketSocket* socket,
+                    const rtc::SentPacket& sent_packet);
+
   void OnReadyToSend(rtc::AsyncPacketSocket* socket);
 
   // This method will send STUN binding request if STUN server address is set.
diff --git a/webrtc/p2p/base/transportchannel.h b/webrtc/p2p/base/transportchannel.h
index 1223618..9dc9c3a 100644
--- a/webrtc/p2p/base/transportchannel.h
+++ b/webrtc/p2p/base/transportchannel.h
@@ -48,7 +48,7 @@
 // between the two sides of a session.
 class TransportChannel : public sigslot::has_slots<> {
  public:
-  explicit TransportChannel(const std::string& transport_name, int component)
+  TransportChannel(const std::string& transport_name, int component)
       : transport_name_(transport_name),
         component_(component),
         writable_(false),
@@ -134,6 +134,9 @@
   sigslot::signal5<TransportChannel*, const char*,
                    size_t, const rtc::PacketTime&, int> SignalReadPacket;
 
+  // Signalled each time a packet is sent on this channel.
+  sigslot::signal2<TransportChannel*, const rtc::SentPacket&> SignalSentPacket;
+
   // This signal occurs when there is a change in the way that packets are
   // being routed, i.e. to a different remote location. The candidate
   // indicates where and how we are currently sending media.
diff --git a/webrtc/transport.h b/webrtc/transport.h
index 7b62f65..b9df7c3 100644
--- a/webrtc/transport.h
+++ b/webrtc/transport.h
@@ -17,6 +17,8 @@
 
 namespace webrtc {
 
+// TODO(holmer): Look into unifying this with the PacketOptions in
+// asyncpacketsocket.h.
 struct PacketOptions {
   // A 16 bits positive id. Negative ids are invalid and should be interpreted
   // as packet_id not being set.
diff --git a/webrtc/video_engine/vie_channel_group.cc b/webrtc/video_engine/vie_channel_group.cc
index 7ed0341..a76c50a 100644
--- a/webrtc/video_engine/vie_channel_group.cc
+++ b/webrtc/video_engine/vie_channel_group.cc
@@ -445,4 +445,11 @@
       PacedSender::kDefaultPaceMultiplier * target_bitrate_bps / 1000,
       pad_up_to_bitrate_bps / 1000);
 }
+
+void ChannelGroup::OnSentPacket(const rtc::SentPacket& sent_packet) {
+  if (transport_feedback_adapter_) {
+    transport_feedback_adapter_->UpdateSendTime(sent_packet.packet_id,
+                                                sent_packet.send_time_ms);
+  }
+}
 }  // namespace webrtc
diff --git a/webrtc/video_engine/vie_channel_group.h b/webrtc/video_engine/vie_channel_group.h
index c2ae127..bb1a08e 100644
--- a/webrtc/video_engine/vie_channel_group.h
+++ b/webrtc/video_engine/vie_channel_group.h
@@ -18,6 +18,7 @@
 
 #include "webrtc/base/criticalsection.h"
 #include "webrtc/base/scoped_ptr.h"
+#include "webrtc/base/socket.h"
 #include "webrtc/modules/bitrate_controller/include/bitrate_controller.h"
 #include "webrtc/video_receive_stream.h"
 #include "webrtc/video_send_stream.h"
@@ -82,6 +83,8 @@
                         uint8_t fraction_loss,
                         int64_t rtt) override;
 
+  void OnSentPacket(const rtc::SentPacket& sent_packet);
+
  private:
   typedef std::map<int, ViEChannel*> ChannelMap;
   typedef std::map<int, ViEEncoder*> EncoderMap;