Update stable to r5277.

git-svn-id: http://webrtc.googlecode.com/svn/stable/talk@5278 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/app/webrtc/java/jni/peerconnection_jni.cc b/app/webrtc/java/jni/peerconnection_jni.cc
index 75f230f..9bb84ac 100644
--- a/app/webrtc/java/jni/peerconnection_jni.cc
+++ b/app/webrtc/java/jni/peerconnection_jni.cc
@@ -195,22 +195,18 @@
   return jni;
 }
 
-// Return a |jlong| that will automatically convert back to |ptr| when assigned
-// to a |uint64|
+// Return a |jlong| that will correctly convert back to |ptr|.  This is needed
+// because the alternative (of silently passing a 32-bit pointer to a vararg
+// function expecting a 64-bit param) picks up garbage in the high 32 bits.
 static jlong jlongFromPointer(void* ptr) {
-  COMPILE_ASSERT(sizeof(intptr_t) <= sizeof(uint64),
+  COMPILE_ASSERT(sizeof(intptr_t) <= sizeof(jlong),
                  Time_to_rethink_the_use_of_jlongs);
-  // Guaranteed to fit by the COMPILE_ASSERT above.
-  uint64 u64 = reinterpret_cast<intptr_t>(ptr);
-  // If the unsigned value fits in the signed type, return it directly.
-  if (u64 <= std::numeric_limits<int64>::max())
-    return u64;
-  // Otherwise, we need to get move u64 into the range of [int64min, -1] subject
-  // to the constraints of remaining equal to |u64| modulo |2^64|.
-  u64 = std::numeric_limits<uint64>::max() - u64;  // In [0,int64max].
-  int64 i64 = -u64; // In [-int64max, 0].
-  i64 -= 1; // In [int64min, -1], and i64+2^64==u64.
-  return i64;
+  // Going through intptr_t to be obvious about the definedness of the
+  // conversion from pointer to integral type.  intptr_t to jlong is a standard
+  // widening by the COMPILE_ASSERT above.
+  jlong ret = reinterpret_cast<intptr_t>(ptr);
+  assert(reinterpret_cast<void*>(ret) == ptr);
+  return ret;
 }
 
 // Android's FindClass() is trickier than usual because the app-specific
@@ -1104,7 +1100,7 @@
   talk_base::scoped_ptr<DataChannelObserverWrapper> observer(
       new DataChannelObserverWrapper(jni, j_observer));
   ExtractNativeDC(jni, j_dc)->RegisterObserver(observer.get());
-  return reinterpret_cast<jlong>(observer.release());
+  return jlongFromPointer(observer.release());
 }
 
 JOW(void, DataChannel_unregisterObserverNative)(
diff --git a/app/webrtc/javatests/src/org/webrtc/PeerConnectionTest.java b/app/webrtc/javatests/src/org/webrtc/PeerConnectionTest.java
index d7380e0..dfed4bf 100644
--- a/app/webrtc/javatests/src/org/webrtc/PeerConnectionTest.java
+++ b/app/webrtc/javatests/src/org/webrtc/PeerConnectionTest.java
@@ -533,9 +533,11 @@
         factory, offeringPC, videoSource, "oLMS", "oLMSv0", "oLMSa0",
         offeringExpectations);
 
-    offeringExpectations.setDataChannel(offeringPC.createDataChannel(
-        "offeringDC", new DataChannel.Init()));
+    DataChannel offeringDC = offeringPC.createDataChannel(
+        "offeringDC", new DataChannel.Init());
+    assertEquals("offeringDC", offeringDC.label());
 
+    offeringExpectations.setDataChannel(offeringDC);
     SdpObserverLatch sdpLatch = new SdpObserverLatch();
     offeringPC.createOffer(sdpLatch, new MediaConstraints());
     assertTrue(sdpLatch.await());
diff --git a/app/webrtc/localaudiosource.cc b/app/webrtc/localaudiosource.cc
index 2cd472a..3dc5c6c 100644
--- a/app/webrtc/localaudiosource.cc
+++ b/app/webrtc/localaudiosource.cc
@@ -54,8 +54,6 @@
 const char MediaConstraintsInterface::kTypingNoiseDetection[] =
     "googTypingNoiseDetection";
 const char MediaConstraintsInterface::kAudioMirroring[] = "googAudioMirroring";
-// TODO(perkj): Remove kInternalAecDump once its not used by Chrome.
-const char MediaConstraintsInterface::kInternalAecDump[] = "deprecatedAecDump";
 
 namespace {
 
@@ -129,8 +127,6 @@
     return;
   }
   options_.SetAll(audio_options);
-  if (options.enable_aec_dump)
-    options_.aec_dump.Set(true);
   source_state_ = kLive;
 }
 
diff --git a/app/webrtc/mediaconstraintsinterface.h b/app/webrtc/mediaconstraintsinterface.h
index ba6b09b..5cf2184 100644
--- a/app/webrtc/mediaconstraintsinterface.h
+++ b/app/webrtc/mediaconstraintsinterface.h
@@ -117,13 +117,6 @@
   // stripped by Chrome before passed down to Libjingle.
   static const char kInternalConstraintPrefix[];
 
-  // These constraints are for internal use only, representing Chrome command
-  // line flags. So they are prefixed with "internal" so JS values will be
-  // removed.
-  // Used by a local audio source.
-  // TODO(perkj): Remove once Chrome use PeerConnectionFactory::SetOptions.
-  static const char kInternalAecDump[];  // internalAecDump
-
  protected:
   // Dtor protected as objects shouldn't be deleted via this interface
   virtual ~MediaConstraintsInterface() {}
diff --git a/app/webrtc/peerconnectionfactory.cc b/app/webrtc/peerconnectionfactory.cc
index e8b8f63..ee15b5d 100644
--- a/app/webrtc/peerconnectionfactory.cc
+++ b/app/webrtc/peerconnectionfactory.cc
@@ -105,12 +105,21 @@
   scoped_refptr<webrtc::VideoSourceInterface> source;
 };
 
+struct StartAecDumpParams : public talk_base::MessageData {
+  explicit StartAecDumpParams(FILE* aec_dump_file)
+      : aec_dump_file(aec_dump_file) {
+  }
+  FILE* aec_dump_file;
+  bool result;
+};
+
 enum {
   MSG_INIT_FACTORY = 1,
   MSG_TERMINATE_FACTORY,
   MSG_CREATE_PEERCONNECTION,
   MSG_CREATE_AUDIOSOURCE,
   MSG_CREATE_VIDEOSOURCE,
+  MSG_START_AEC_DUMP,
 };
 
 }  // namespace
@@ -223,6 +232,12 @@
       pdata->source = CreateVideoSource_s(pdata->capturer, pdata->constraints);
       break;
     }
+    case MSG_START_AEC_DUMP: {
+      StartAecDumpParams* pdata =
+          static_cast<StartAecDumpParams*>(msg->pdata);
+      pdata->result = StartAecDump_s(pdata->aec_dump_file);
+      break;
+    }
   }
 }
 
@@ -274,6 +289,10 @@
   return VideoSourceProxy::Create(signaling_thread_, source);
 }
 
+bool PeerConnectionFactory::StartAecDump_s(FILE* file) {
+  return channel_manager_->StartAecDump(file);
+}
+
 scoped_refptr<PeerConnectionInterface>
 PeerConnectionFactory::CreatePeerConnection(
     const PeerConnectionInterface::IceServers& configuration,
@@ -361,6 +380,12 @@
   return AudioTrackProxy::Create(signaling_thread_, track);
 }
 
+bool PeerConnectionFactory::StartAecDump(FILE* file) {
+  StartAecDumpParams params(file);
+  signaling_thread_->Send(this, MSG_START_AEC_DUMP, &params);
+  return params.result;
+}
+
 cricket::ChannelManager* PeerConnectionFactory::channel_manager() {
   return channel_manager_.get();
 }
diff --git a/app/webrtc/peerconnectionfactory.h b/app/webrtc/peerconnectionfactory.h
index dff885d..63d37f0 100644
--- a/app/webrtc/peerconnectionfactory.h
+++ b/app/webrtc/peerconnectionfactory.h
@@ -78,6 +78,8 @@
       CreateAudioTrack(const std::string& id,
                        AudioSourceInterface* audio_source);
 
+  virtual bool StartAecDump(FILE* file);
+
   virtual cricket::ChannelManager* channel_manager();
   virtual talk_base::Thread* signaling_thread();
   virtual talk_base::Thread* worker_thread();
@@ -93,7 +95,6 @@
       cricket::WebRtcVideoDecoderFactory* video_decoder_factory);
   virtual ~PeerConnectionFactory();
 
-
  private:
   bool Initialize_s();
   void Terminate_s();
@@ -108,6 +109,8 @@
       PortAllocatorFactoryInterface* allocator_factory,
       DTLSIdentityServiceInterface* dtls_identity_service,
       PeerConnectionObserver* observer);
+  bool StartAecDump_s(FILE* file);
+
   // Implements talk_base::MessageHandler.
   void OnMessage(talk_base::Message* msg);
 
diff --git a/app/webrtc/peerconnectioninterface.h b/app/webrtc/peerconnectioninterface.h
index a127dad..01f1e1c 100644
--- a/app/webrtc/peerconnectioninterface.h
+++ b/app/webrtc/peerconnectioninterface.h
@@ -393,11 +393,9 @@
   class Options {
    public:
     Options() :
-      enable_aec_dump(false),
       disable_encryption(false),
       disable_sctp_data_channels(false) {
     }
-    bool enable_aec_dump;
     bool disable_encryption;
     bool disable_sctp_data_channels;
   };
@@ -442,6 +440,12 @@
       CreateAudioTrack(const std::string& label,
                        AudioSourceInterface* source) = 0;
 
+  // Starts AEC dump using existing file. Takes ownership of |file| and passes
+  // it on to VoiceEngine (via other objects) immediately, which will take
+  // the ownerhip.
+  // TODO(grunell): Remove when Chromium has started to use AEC in each source.
+  virtual bool StartAecDump(FILE* file) = 0;
+
  protected:
   // Dtor and ctor protected as objects shouldn't be created or deleted via
   // this interface.
diff --git a/base/asyncpacketsocket.h b/base/asyncpacketsocket.h
index 3b4748f..8ee0a36 100644
--- a/base/asyncpacketsocket.h
+++ b/base/asyncpacketsocket.h
@@ -31,9 +31,30 @@
 #include "talk/base/dscp.h"
 #include "talk/base/sigslot.h"
 #include "talk/base/socket.h"
+#include "talk/base/timeutils.h"
 
 namespace talk_base {
 
+// This structure will have the information about when packet is actually
+// received by socket.
+struct PacketTime {
+  PacketTime() : timestamp(0), not_before(0) {}
+  PacketTime(int64 timestamp, int64 not_before)
+      : timestamp(timestamp), not_before(not_before) {
+  }
+
+  int64 timestamp;  // Receive time after socket delivers the data.
+  int64 not_before; // Earliest possible time the data could have arrived,
+                    // indicating the potential error in the |timestamp| value,
+                    // in case the system, is busy. For example, the time of
+                    // the last select() call.
+                    // If unknown, this value will be set to zero.
+};
+
+inline PacketTime CreatePacketTime(int64 not_before) {
+  return PacketTime(TimeMicros(), not_before);
+}
+
 // Provides the ability to receive packets asynchronously. Sends are not
 // buffered since it is acceptable to drop packets under high load.
 class AsyncPacketSocket : public sigslot::has_slots<> {
@@ -78,8 +99,9 @@
 
   // Emitted each time a packet is read. Used only for UDP and
   // connected TCP sockets.
-  sigslot::signal4<AsyncPacketSocket*, const char*, size_t,
-                   const SocketAddress&> SignalReadPacket;
+  sigslot::signal5<AsyncPacketSocket*, const char*, size_t,
+                   const SocketAddress&,
+                   const PacketTime&> SignalReadPacket;
 
   // Emitted when the socket is currently able to send.
   sigslot::signal1<AsyncPacketSocket*> SignalReadyToSend;
diff --git a/base/asynctcpsocket.cc b/base/asynctcpsocket.cc
index 517e799..d2ae513 100644
--- a/base/asynctcpsocket.cc
+++ b/base/asynctcpsocket.cc
@@ -300,7 +300,8 @@
     if (*len < kPacketLenSize + pkt_len)
       return;
 
-    SignalReadPacket(this, data + kPacketLenSize, pkt_len, remote_addr);
+    SignalReadPacket(this, data + kPacketLenSize, pkt_len, remote_addr,
+                     CreatePacketTime(0));
 
     *len -= kPacketLenSize + pkt_len;
     if (*len > 0) {
diff --git a/base/asyncudpsocket.cc b/base/asyncudpsocket.cc
index 97e5dff..5005263 100644
--- a/base/asyncudpsocket.cc
+++ b/base/asyncudpsocket.cc
@@ -128,7 +128,8 @@
 
   // TODO: Make sure that we got all of the packet.
   // If we did not, then we should resize our buffer to be large enough.
-  SignalReadPacket(this, buf_, (size_t)len, remote_addr);
+  SignalReadPacket(this, buf_, static_cast<size_t>(len), remote_addr,
+                   CreatePacketTime(0));
 }
 
 void AsyncUDPSocket::OnWriteEvent(AsyncSocket* socket) {
diff --git a/base/natserver.cc b/base/natserver.cc
index 3ad378c..4698048 100644
--- a/base/natserver.cc
+++ b/base/natserver.cc
@@ -107,7 +107,7 @@
 
 void NATServer::OnInternalPacket(
     AsyncPacketSocket* socket, const char* buf, size_t size,
-    const SocketAddress& addr) {
+    const SocketAddress& addr, const PacketTime& packet_time) {
 
   // Read the intended destination from the wire.
   SocketAddress dest_addr;
@@ -132,7 +132,7 @@
 
 void NATServer::OnExternalPacket(
     AsyncPacketSocket* socket, const char* buf, size_t size,
-    const SocketAddress& remote_addr) {
+    const SocketAddress& remote_addr, const PacketTime& packet_time) {
 
   SocketAddress local_addr = socket->GetLocalAddress();
 
diff --git a/base/natserver.h b/base/natserver.h
index ed3b0b6..05d3475 100644
--- a/base/natserver.h
+++ b/base/natserver.h
@@ -79,9 +79,11 @@
 
   // Packets received on one of the networks.
   void OnInternalPacket(AsyncPacketSocket* socket, const char* buf,
-                        size_t size, const SocketAddress& addr);
+                        size_t size, const SocketAddress& addr,
+                        const PacketTime& packet_time);
   void OnExternalPacket(AsyncPacketSocket* socket, const char* buf,
-                        size_t size, const SocketAddress& remote_addr);
+                        size_t size, const SocketAddress& remote_addr,
+                        const PacketTime& packet_time);
 
  private:
   typedef std::set<SocketAddress, AddrCmp> AddressSet;
diff --git a/base/testclient.cc b/base/testclient.cc
index 1a12761..04d6030 100644
--- a/base/testclient.cc
+++ b/base/testclient.cc
@@ -135,7 +135,8 @@
 }
 
 void TestClient::OnPacket(AsyncPacketSocket* socket, const char* buf,
-                          size_t size, const SocketAddress& remote_addr) {
+                          size_t size, const SocketAddress& remote_addr,
+                          const PacketTime& packet_time) {
   CritScope cs(&crit_);
   packets_->push_back(new Packet(remote_addr, buf, size));
 }
diff --git a/base/testclient.h b/base/testclient.h
index 1e1780a..87e32df 100644
--- a/base/testclient.h
+++ b/base/testclient.h
@@ -94,7 +94,8 @@
   Socket::ConnState GetState();
   // Slot for packets read on the socket.
   void OnPacket(AsyncPacketSocket* socket, const char* buf, size_t len,
-                const SocketAddress& remote_addr);
+                const SocketAddress& remote_addr,
+                const PacketTime& packet_time);
   void OnReadyToSend(AsyncPacketSocket* socket);
 
   CriticalSection crit_;
diff --git a/base/testechoserver.h b/base/testechoserver.h
index 10466fa..5c10454 100644
--- a/base/testechoserver.h
+++ b/base/testechoserver.h
@@ -67,7 +67,8 @@
     }
   }
   void OnPacket(AsyncPacketSocket* socket, const char* buf, size_t size,
-                const SocketAddress& remote_addr) {
+                const SocketAddress& remote_addr,
+                const PacketTime& packet_time) {
     socket->Send(buf, size, DSCP_NO_CHANGE);
   }
   void OnClose(AsyncPacketSocket* socket, int err) {
diff --git a/base/thread_unittest.cc b/base/thread_unittest.cc
index d6af17a..896fbab 100644
--- a/base/thread_unittest.cc
+++ b/base/thread_unittest.cc
@@ -81,7 +81,8 @@
   SocketAddress address() const { return socket_->GetLocalAddress(); }
 
   void OnPacket(AsyncPacketSocket* socket, const char* buf, size_t size,
-                const SocketAddress& remote_addr) {
+                const SocketAddress& remote_addr,
+                const PacketTime& packet_time) {
     EXPECT_EQ(size, sizeof(uint32));
     uint32 prev = reinterpret_cast<const uint32*>(buf)[0];
     uint32 result = Next(prev);
diff --git a/base/timeutils.cc b/base/timeutils.cc
index 66b9bf2..54db341 100644
--- a/base/timeutils.cc
+++ b/base/timeutils.cc
@@ -94,6 +94,10 @@
   return static_cast<uint32>(TimeNanos() / kNumNanosecsPerMillisec);
 }
 
+uint64 TimeMicros() {
+  return static_cast<uint64>(TimeNanos() / kNumNanosecsPerMicrosec);
+}
+
 #if defined(WIN32)
 static const uint64 kFileTimeToUnixTimeEpochOffset = 116444736000000000ULL;
 
diff --git a/base/timeutils.h b/base/timeutils.h
index 545e86a..f13c3f2 100644
--- a/base/timeutils.h
+++ b/base/timeutils.h
@@ -42,6 +42,8 @@
     kNumMillisecsPerSec;
 static const int64 kNumNanosecsPerMillisec =  kNumNanosecsPerSec /
     kNumMillisecsPerSec;
+static const int64 kNumNanosecsPerMicrosec =  kNumNanosecsPerSec /
+    kNumMicrosecsPerSec;
 
 // January 1970, in NTP milliseconds.
 static const int64 kJan1970AsNtpMillisecs = INT64_C(2208988800000);
@@ -50,6 +52,8 @@
 
 // Returns the current time in milliseconds.
 uint32 Time();
+// Returns the current time in microseconds.
+uint64 TimeMicros();
 // Returns the current time in nanoseconds.
 uint64 TimeNanos();
 
diff --git a/base/virtualsocket_unittest.cc b/base/virtualsocket_unittest.cc
index 7bbb5f0..b31b8c8 100644
--- a/base/virtualsocket_unittest.cc
+++ b/base/virtualsocket_unittest.cc
@@ -97,7 +97,8 @@
   }
 
   void OnReadPacket(AsyncPacketSocket* s, const char* data, size_t size,
-                    const SocketAddress& remote_addr) {
+                    const SocketAddress& remote_addr,
+                    const PacketTime& packet_time) {
     ASSERT_EQ(socket.get(), s);
     ASSERT_GE(size, 4U);
 
diff --git a/examples/android/src/org/appspot/apprtc/AppRTCDemoActivity.java b/examples/android/src/org/appspot/apprtc/AppRTCDemoActivity.java
index c1dff74..6d864e4 100644
--- a/examples/android/src/org/appspot/apprtc/AppRTCDemoActivity.java
+++ b/examples/android/src/org/appspot/apprtc/AppRTCDemoActivity.java
@@ -171,11 +171,26 @@
     }
   }
 
+
+  // Just for fun (and to regression-test bug 2302) make sure that DataChannels
+  // can be created, queried, and disposed.
+  private static void createDataChannelToRegressionTestBug2302(
+      PeerConnection pc) {
+    DataChannel dc = pc.createDataChannel("dcLabel", new DataChannel.Init());
+    abortUnless("dcLabel".equals(dc.label()), "WTF?");
+    dc.dispose();
+  }
+
   @Override
   public void onIceServers(List<PeerConnection.IceServer> iceServers) {
     factory = new PeerConnectionFactory();
-    pc = factory.createPeerConnection(
-        iceServers, appRtcClient.pcConstraints(), pcObserver);
+
+    MediaConstraints pcConstraints = appRtcClient.pcConstraints();
+    pcConstraints.optional.add(
+        new MediaConstraints.KeyValuePair("RtpDataChannels", "true"));
+    pc = factory.createPeerConnection(iceServers, pcConstraints, pcObserver);
+
+    createDataChannelToRegressionTestBug2302(pc);  // See method comment.
 
     // Uncomment to get ALL WebRTC tracing and SENSITIVE libjingle logging.
     // NOTE: this _must_ happen while |factory| is alive!
diff --git a/libjingle.gyp b/libjingle.gyp
index 4cbeaa3..dced3d9 100755
--- a/libjingle.gyp
+++ b/libjingle.gyp
@@ -105,13 +105,14 @@
                 # included here, or better yet, build a proper .jar in webrtc
                 # and include it here.
                 'android_java_files': [
-                  '<(webrtc_modules_dir)/audio_device/android/java/src/org/webrtc/voiceengine/WebRTCAudioDevice.java',
                   '<(webrtc_modules_dir)/audio_device/android/java/src/org/webrtc/voiceengine/AudioManagerAndroid.java',
                   '<(webrtc_modules_dir)/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureAndroid.java',
                   '<(webrtc_modules_dir)/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureDeviceInfoAndroid.java',
                   '<(webrtc_modules_dir)/video_render/android/java/src/org/webrtc/videoengine/ViEAndroidGLES20.java',
                   '<(webrtc_modules_dir)/video_render/android/java/src/org/webrtc/videoengine/ViERenderer.java',
                   '<(webrtc_modules_dir)/video_render/android/java/src/org/webrtc/videoengine/ViESurfaceRenderer.java',
+                  '<(webrtc_modules_dir)/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioRecord.java',
+                  '<(webrtc_modules_dir)/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioTrack.java',
                 ],
               },
               'action_name': 'create_jar',
diff --git a/libjingle_examples.gyp b/libjingle_examples.gyp
index c69aa9e..1e2eeb0 100755
--- a/libjingle_examples.gyp
+++ b/libjingle_examples.gyp
@@ -304,6 +304,9 @@
               'outputs': [
                 '<(PRODUCT_DIR)/AppRTCDemo-debug.apk',
               ],
+              'variables': {
+                'ant_log': '<(INTERMEDIATE_DIR)/ant.log',
+              },
               'action': [
                 'bash', '-ec',
                 'rm -fr <(_outputs) examples/android/{bin,libs} && '
@@ -311,8 +314,10 @@
                 'cp <(PRODUCT_DIR)/libjingle_peerconnection.jar examples/android/libs/ &&'
                 '<(android_strip) -o examples/android/libs/<(android_app_abi)/libjingle_peerconnection_so.so  <(PRODUCT_DIR)/libjingle_peerconnection_so.so &&'
                 'cd examples/android && '
-                'ant debug && '
-                'cd - && '
+                'mkdir -p <(INTERMEDIATE_DIR) && '
+                '{ ant -q -l <(ant_log) debug || '
+                '  { cat <(ant_log) ; exit 1; } } && '
+                'cd - > /dev/null && '
                 'cp examples/android/bin/AppRTCDemo-debug.apk <(_outputs)'
               ],
             },
diff --git a/media/base/fakemediaengine.h b/media/base/fakemediaengine.h
index c44db68..d71c660 100644
--- a/media/base/fakemediaengine.h
+++ b/media/base/fakemediaengine.h
@@ -191,10 +191,12 @@
     return true;
   }
   void set_playout(bool playout) { playout_ = playout; }
-  virtual void OnPacketReceived(talk_base::Buffer* packet) {
+  virtual void OnPacketReceived(talk_base::Buffer* packet,
+                                const talk_base::PacketTime& packet_time) {
     rtp_packets_.push_back(std::string(packet->data(), packet->length()));
   }
-  virtual void OnRtcpReceived(talk_base::Buffer* packet) {
+  virtual void OnRtcpReceived(talk_base::Buffer* packet,
+                              const talk_base::PacketTime& packet_time) {
     rtcp_packets_.push_back(std::string(packet->data(), packet->length()));
   }
   virtual void OnReadyToSend(bool ready) {
@@ -776,6 +778,8 @@
 
   bool SetLocalMonitor(bool enable) { return true; }
 
+  bool StartAecDump(FILE* file) { return false; }
+
   bool RegisterProcessor(uint32 ssrc, VoiceProcessor* voice_processor,
                          MediaProcessorDirection direction) {
     if (direction == MPD_RX) {
diff --git a/media/base/fakenetworkinterface.h b/media/base/fakenetworkinterface.h
index 37679eb..eb0175b 100644
--- a/media/base/fakenetworkinterface.h
+++ b/media/base/fakenetworkinterface.h
@@ -201,9 +201,11 @@
             msg->pdata);
     if (dest_) {
       if (msg->message_id == ST_RTP) {
-        dest_->OnPacketReceived(&msg_data->data());
+        dest_->OnPacketReceived(&msg_data->data(),
+                                talk_base::CreatePacketTime(0));
       } else {
-        dest_->OnRtcpReceived(&msg_data->data());
+        dest_->OnRtcpReceived(&msg_data->data(),
+                              talk_base::CreatePacketTime(0));
       }
     }
     delete msg_data;
diff --git a/media/base/filemediaengine.cc b/media/base/filemediaengine.cc
index 80e9729..dfec607 100644
--- a/media/base/filemediaengine.cc
+++ b/media/base/filemediaengine.cc
@@ -315,7 +315,8 @@
   return true;
 }
 
-void FileVoiceChannel::OnPacketReceived(talk_base::Buffer* packet) {
+void FileVoiceChannel::OnPacketReceived(
+    talk_base::Buffer* packet, const talk_base::PacketTime& packet_time) {
   rtp_sender_receiver_->OnPacketReceived(packet);
 }
 
@@ -360,7 +361,8 @@
   return true;
 }
 
-void FileVideoChannel::OnPacketReceived(talk_base::Buffer* packet) {
+void FileVideoChannel::OnPacketReceived(
+    talk_base::Buffer* packet, const talk_base::PacketTime& packet_time) {
   rtp_sender_receiver_->OnPacketReceived(packet);
 }
 
diff --git a/media/base/filemediaengine.h b/media/base/filemediaengine.h
index 129af9c..843806b 100644
--- a/media/base/filemediaengine.h
+++ b/media/base/filemediaengine.h
@@ -133,6 +133,7 @@
   virtual bool FindVideoCodec(const VideoCodec& codec) { return true; }
   virtual void SetVoiceLogging(int min_sev, const char* filter) {}
   virtual void SetVideoLogging(int min_sev, const char* filter) {}
+  virtual bool StartAecDump(FILE* file) { return false; }
 
   virtual bool RegisterVideoProcessor(VideoProcessor* processor) {
     return true;
@@ -232,8 +233,10 @@
   virtual bool GetStats(VoiceMediaInfo* info) { return true; }
 
   // Implement pure virtual methods of MediaChannel.
-  virtual void OnPacketReceived(talk_base::Buffer* packet);
-  virtual void OnRtcpReceived(talk_base::Buffer* packet) {}
+  virtual void OnPacketReceived(talk_base::Buffer* packet,
+                                const talk_base::PacketTime& packet_time);
+  virtual void OnRtcpReceived(talk_base::Buffer* packet,
+                              const talk_base::PacketTime& packet_time) {}
   virtual void OnReadyToSend(bool ready) {}
   virtual bool AddSendStream(const StreamParams& sp);
   virtual bool RemoveSendStream(uint32 ssrc);
@@ -298,8 +301,10 @@
   virtual bool RequestIntraFrame() { return false; }
 
   // Implement pure virtual methods of MediaChannel.
-  virtual void OnPacketReceived(talk_base::Buffer* packet);
-  virtual void OnRtcpReceived(talk_base::Buffer* packet) {}
+  virtual void OnPacketReceived(talk_base::Buffer* packet,
+                                const talk_base::PacketTime& packet_time);
+  virtual void OnRtcpReceived(talk_base::Buffer* packet,
+                              const talk_base::PacketTime& packet_time) {}
   virtual void OnReadyToSend(bool ready) {}
   virtual bool AddSendStream(const StreamParams& sp);
   virtual bool RemoveSendStream(uint32 ssrc);
diff --git a/media/base/filemediaengine_unittest.cc b/media/base/filemediaengine_unittest.cc
index 7ba96a2..b1b021d 100644
--- a/media/base/filemediaengine_unittest.cc
+++ b/media/base/filemediaengine_unittest.cc
@@ -63,7 +63,7 @@
     if (!packet) return false;
 
     if (media_channel_) {
-      media_channel_->OnPacketReceived(packet);
+      media_channel_->OnPacketReceived(packet, talk_base::PacketTime());
     }
     if (dump_writer_.get() &&
         talk_base::SR_SUCCESS != dump_writer_->WriteRtpPacket(
diff --git a/media/base/hybridvideoengine.cc b/media/base/hybridvideoengine.cc
index a405f8d..6863311 100644
--- a/media/base/hybridvideoengine.cc
+++ b/media/base/hybridvideoengine.cc
@@ -276,19 +276,21 @@
       active_channel_->GetStats(info);
 }
 
-void HybridVideoMediaChannel::OnPacketReceived(talk_base::Buffer* packet) {
+void HybridVideoMediaChannel::OnPacketReceived(
+    talk_base::Buffer* packet, const talk_base::PacketTime& packet_time) {
   // Eat packets until we have an active channel;
   if (active_channel_) {
-    active_channel_->OnPacketReceived(packet);
+    active_channel_->OnPacketReceived(packet, packet_time);
   } else {
     LOG(LS_INFO) << "HybridVideoChannel: Eating early RTP packet";
   }
 }
 
-void HybridVideoMediaChannel::OnRtcpReceived(talk_base::Buffer* packet) {
+void HybridVideoMediaChannel::OnRtcpReceived(
+    talk_base::Buffer* packet, const talk_base::PacketTime& packet_time) {
   // Eat packets until we have an active channel;
   if (active_channel_) {
-    active_channel_->OnRtcpReceived(packet);
+    active_channel_->OnRtcpReceived(packet, packet_time);
   } else {
     LOG(LS_INFO) << "HybridVideoChannel: Eating early RTCP packet";
   }
diff --git a/media/base/hybridvideoengine.h b/media/base/hybridvideoengine.h
index ab62cc7..a49a1aa 100644
--- a/media/base/hybridvideoengine.h
+++ b/media/base/hybridvideoengine.h
@@ -87,8 +87,10 @@
 
   virtual bool GetStats(VideoMediaInfo* info);
 
-  virtual void OnPacketReceived(talk_base::Buffer* packet);
-  virtual void OnRtcpReceived(talk_base::Buffer* packet);
+  virtual void OnPacketReceived(talk_base::Buffer* packet,
+                                const talk_base::PacketTime& packet_time);
+  virtual void OnRtcpReceived(talk_base::Buffer* packet,
+                              const talk_base::PacketTime& packet_time);
   virtual void OnReadyToSend(bool ready);
 
   virtual void UpdateAspectRatio(int ratio_w, int ratio_h);
diff --git a/media/base/mediachannel.h b/media/base/mediachannel.h
index 9e1ea9d..94ae03f 100644
--- a/media/base/mediachannel.h
+++ b/media/base/mediachannel.h
@@ -509,9 +509,11 @@
   }
 
   // Called when a RTP packet is received.
-  virtual void OnPacketReceived(talk_base::Buffer* packet) = 0;
+  virtual void OnPacketReceived(talk_base::Buffer* packet,
+                                const talk_base::PacketTime& packet_time) = 0;
   // Called when a RTCP packet is received.
-  virtual void OnRtcpReceived(talk_base::Buffer* packet) = 0;
+  virtual void OnRtcpReceived(talk_base::Buffer* packet,
+                              const talk_base::PacketTime& packet_time) = 0;
   // Called when the socket's ability to send has changed.
   virtual void OnReadyToSend(bool ready) = 0;
   // Creates a new outgoing media stream with SSRCs and CNAME as described
@@ -1131,25 +1133,15 @@
 
   virtual ~DataMediaChannel() {}
 
-  virtual bool SetSendBandwidth(bool autobw, int bps) = 0;
   virtual bool SetSendCodecs(const std::vector<DataCodec>& codecs) = 0;
   virtual bool SetRecvCodecs(const std::vector<DataCodec>& codecs) = 0;
-  virtual bool SetRecvRtpHeaderExtensions(
-      const std::vector<RtpHeaderExtension>& extensions) = 0;
-  virtual bool SetSendRtpHeaderExtensions(
-      const std::vector<RtpHeaderExtension>& extensions) = 0;
-  virtual bool AddSendStream(const StreamParams& sp) = 0;
-  virtual bool RemoveSendStream(uint32 ssrc) = 0;
-  virtual bool AddRecvStream(const StreamParams& sp) = 0;
-  virtual bool RemoveRecvStream(uint32 ssrc) = 0;
+
   virtual bool MuteStream(uint32 ssrc, bool on) { return false; }
   // TODO(pthatcher): Implement this.
   virtual bool GetStats(DataMediaInfo* info) { return true; }
 
   virtual bool SetSend(bool send) = 0;
   virtual bool SetReceive(bool receive) = 0;
-  virtual void OnPacketReceived(talk_base::Buffer* packet) = 0;
-  virtual void OnRtcpReceived(talk_base::Buffer* packet) = 0;
 
   virtual bool SendData(
       const SendDataParams& params,
diff --git a/media/base/mediaengine.h b/media/base/mediaengine.h
index f916572..c04df9f 100644
--- a/media/base/mediaengine.h
+++ b/media/base/mediaengine.h
@@ -135,6 +135,9 @@
   virtual void SetVoiceLogging(int min_sev, const char* filter) = 0;
   virtual void SetVideoLogging(int min_sev, const char* filter) = 0;
 
+  // Starts AEC dump using existing file.
+  virtual bool StartAecDump(FILE* file) = 0;
+
   // Voice processors for effects.
   virtual bool RegisterVoiceProcessor(uint32 ssrc,
                                       VoiceProcessor* video_processor,
@@ -253,6 +256,10 @@
     video_.SetLogging(min_sev, filter);
   }
 
+  virtual bool StartAecDump(FILE* file) {
+    return voice_.StartAecDump(file);
+  }
+
   virtual bool RegisterVoiceProcessor(uint32 ssrc,
                                       VoiceProcessor* processor,
                                       MediaProcessorDirection direction) {
diff --git a/media/base/rtpdataengine.cc b/media/base/rtpdataengine.cc
index 3a9228a..0f84c83 100644
--- a/media/base/rtpdataengine.cc
+++ b/media/base/rtpdataengine.cc
@@ -230,7 +230,8 @@
   return true;
 }
 
-void RtpDataMediaChannel::OnPacketReceived(talk_base::Buffer* packet) {
+void RtpDataMediaChannel::OnPacketReceived(
+    talk_base::Buffer* packet, const talk_base::PacketTime& packet_time) {
   RtpHeader header;
   if (!GetRtpHeader(packet->data(), packet->length(), &header)) {
     // Don't want to log for every corrupt packet.
diff --git a/media/base/rtpdataengine.h b/media/base/rtpdataengine.h
index bc7b667..59e6589 100644
--- a/media/base/rtpdataengine.h
+++ b/media/base/rtpdataengine.h
@@ -115,8 +115,10 @@
     receiving_ = receive;
     return true;
   }
-  virtual void OnPacketReceived(talk_base::Buffer* packet);
-  virtual void OnRtcpReceived(talk_base::Buffer* packet) {}
+  virtual void OnPacketReceived(talk_base::Buffer* packet,
+                                const talk_base::PacketTime& packet_time);
+  virtual void OnRtcpReceived(talk_base::Buffer* packet,
+                              const talk_base::PacketTime& packet_time) {}
   virtual void OnReadyToSend(bool ready) {}
   virtual bool SendData(
     const SendDataParams& params,
diff --git a/media/base/rtpdataengine_unittest.cc b/media/base/rtpdataengine_unittest.cc
index bc46818..a86ab3b 100644
--- a/media/base/rtpdataengine_unittest.cc
+++ b/media/base/rtpdataengine_unittest.cc
@@ -423,13 +423,13 @@
   talk_base::scoped_ptr<cricket::RtpDataMediaChannel> dmc(CreateChannel());
 
   // SetReceived not called.
-  dmc->OnPacketReceived(&packet);
+  dmc->OnPacketReceived(&packet, talk_base::PacketTime());
   EXPECT_FALSE(HasReceivedData());
 
   dmc->SetReceive(true);
 
   // Unknown payload id
-  dmc->OnPacketReceived(&packet);
+  dmc->OnPacketReceived(&packet, talk_base::PacketTime());
   EXPECT_FALSE(HasReceivedData());
 
   cricket::DataCodec codec;
@@ -440,7 +440,7 @@
   ASSERT_TRUE(dmc->SetRecvCodecs(codecs));
 
   // Unknown stream
-  dmc->OnPacketReceived(&packet);
+  dmc->OnPacketReceived(&packet, talk_base::PacketTime());
   EXPECT_FALSE(HasReceivedData());
 
   cricket::StreamParams stream;
@@ -448,7 +448,7 @@
   ASSERT_TRUE(dmc->AddRecvStream(stream));
 
   // Finally works!
-  dmc->OnPacketReceived(&packet);
+  dmc->OnPacketReceived(&packet, talk_base::PacketTime());
   EXPECT_TRUE(HasReceivedData());
   EXPECT_EQ("abcde", GetReceivedData());
   EXPECT_EQ(5U, GetReceivedDataLen());
@@ -463,6 +463,6 @@
   talk_base::scoped_ptr<cricket::RtpDataMediaChannel> dmc(CreateChannel());
 
   // Too short
-  dmc->OnPacketReceived(&packet);
+  dmc->OnPacketReceived(&packet, talk_base::PacketTime());
   EXPECT_FALSE(HasReceivedData());
 }
diff --git a/media/base/videoengine_unittest.h b/media/base/videoengine_unittest.h
index 95d8f6c..d9266f2 100644
--- a/media/base/videoengine_unittest.h
+++ b/media/base/videoengine_unittest.h
@@ -981,7 +981,7 @@
     EXPECT_TRUE(SetSend(true));
     EXPECT_TRUE(channel_->SetRender(true));
     EXPECT_EQ(0, renderer_.num_rendered_frames());
-    channel_->OnPacketReceived(&packet1);
+    channel_->OnPacketReceived(&packet1, talk_base::PacketTime());
     SetRendererAsDefault();
     EXPECT_TRUE(SendFrame());
     EXPECT_FRAME_WAIT(1, DefaultCodec().width, DefaultCodec().height, kTimeout);
diff --git a/media/sctp/sctpdataengine.cc b/media/sctp/sctpdataengine.cc
index 2b86001..653273b 100644
--- a/media/sctp/sctpdataengine.cc
+++ b/media/sctp/sctpdataengine.cc
@@ -542,7 +542,8 @@
 }
 
 // Called by network interface when a packet has been received.
-void SctpDataMediaChannel::OnPacketReceived(talk_base::Buffer* packet) {
+void SctpDataMediaChannel::OnPacketReceived(
+    talk_base::Buffer* packet, const talk_base::PacketTime& packet_time) {
   LOG(LS_VERBOSE) << debug_name_ << "->OnPacketReceived(...): " << " length="
                   << packet->length() << ", sending: " << sending_;
   // Only give receiving packets to usrsctp after if connected. This enables two
diff --git a/media/sctp/sctpdataengine.h b/media/sctp/sctpdataengine.h
index d09b152..4d05cf3 100644
--- a/media/sctp/sctpdataengine.h
+++ b/media/sctp/sctpdataengine.h
@@ -149,7 +149,8 @@
                         const talk_base::Buffer& payload,
                         SendDataResult* result = NULL);
   // A packet is received from the network interface. Posted to OnMessage.
-  virtual void OnPacketReceived(talk_base::Buffer* packet);
+  virtual void OnPacketReceived(talk_base::Buffer* packet,
+                                const talk_base::PacketTime& packet_time);
 
   // Exposed to allow Post call from c-callbacks.
   talk_base::Thread* worker_thread() const { return worker_thread_; }
@@ -170,7 +171,8 @@
       const std::vector<RtpHeaderExtension>& extensions) { return true; }
   virtual bool SetSendCodecs(const std::vector<DataCodec>& codecs);
   virtual bool SetRecvCodecs(const std::vector<DataCodec>& codecs);
-  virtual void OnRtcpReceived(talk_base::Buffer* packet) {}
+  virtual void OnRtcpReceived(talk_base::Buffer* packet,
+                              const talk_base::PacketTime& packet_time) {}
   virtual void OnReadyToSend(bool ready) {}
 
   // Helper for debugging.
diff --git a/media/sctp/sctpdataengine_unittest.cc b/media/sctp/sctpdataengine_unittest.cc
index cab60b0..b4ad6ce 100644
--- a/media/sctp/sctpdataengine_unittest.cc
+++ b/media/sctp/sctpdataengine_unittest.cc
@@ -84,7 +84,7 @@
         static_cast<talk_base::TypedMessageData<talk_base::Buffer*>*>(
             msg->pdata)->data();
     if (dest_) {
-      dest_->OnPacketReceived(buffer);
+      dest_->OnPacketReceived(buffer, talk_base::PacketTime());
     }
     delete buffer;
   }
diff --git a/media/webrtc/fakewebrtcvideoengine.h b/media/webrtc/fakewebrtcvideoengine.h
index 850451b..070c731 100644
--- a/media/webrtc/fakewebrtcvideoengine.h
+++ b/media/webrtc/fakewebrtcvideoengine.h
@@ -339,14 +339,12 @@
   };
   class Capturer : public webrtc::ViEExternalCapture {
    public:
-    Capturer() : channel_id_(-1), denoising_(false),
-                 last_capture_time_(0), incoming_frame_num_(0) { }
+    Capturer() : channel_id_(-1), denoising_(false), last_capture_time_(0) { }
     int channel_id() const { return channel_id_; }
     void set_channel_id(int channel_id) { channel_id_ = channel_id; }
     bool denoising() const { return denoising_; }
     void set_denoising(bool denoising) { denoising_ = denoising; }
-    int64 last_capture_time() const { return last_capture_time_; }
-    int incoming_frame_num() const { return incoming_frame_num_; }
+    int64 last_capture_time() { return last_capture_time_; }
 
     // From ViEExternalCapture
     virtual int IncomingFrame(unsigned char* videoFrame,
@@ -361,7 +359,6 @@
         const webrtc::ViEVideoFrameI420& video_frame,
         unsigned long long captureTime) {
       last_capture_time_ = captureTime;
-      ++incoming_frame_num_;
       return 0;
     }
 
@@ -369,7 +366,6 @@
     int channel_id_;
     bool denoising_;
     int64 last_capture_time_;
-    int incoming_frame_num_;
   };
 
   FakeWebRtcVideoEngine(const cricket::VideoCodec* const* codecs,
@@ -412,16 +408,6 @@
 
   int GetLastCapturer() const { return last_capturer_; }
   int GetNumCapturers() const { return static_cast<int>(capturers_.size()); }
-  int GetIncomingFrameNum(int channel_id) const {
-    for (std::map<int, Capturer*>::const_iterator iter = capturers_.begin();
-         iter != capturers_.end(); ++iter) {
-      Capturer* capturer = iter->second;
-      if (capturer->channel_id() == channel_id) {
-        return capturer->incoming_frame_num();
-      }
-    }
-    return -1;
-  }
   void set_fail_alloc_capturer(bool fail_alloc_capturer) {
     fail_alloc_capturer_ = fail_alloc_capturer;
   }
@@ -827,7 +813,12 @@
   }
   WEBRTC_STUB(RegisterSendTransport, (const int, webrtc::Transport&));
   WEBRTC_STUB(DeregisterSendTransport, (const int));
+#ifdef USE_WEBRTC_DEV_BRANCH
+  WEBRTC_STUB(ReceivedRTPPacket, (const int, const void*, const int,
+      const webrtc::PacketTime&));
+#else
   WEBRTC_STUB(ReceivedRTPPacket, (const int, const void*, const int));
+#endif
   WEBRTC_STUB(ReceivedRTCPPacket, (const int, const void*, const int));
   // Not using WEBRTC_STUB due to bool return value
   virtual bool IsIPv6Enabled(int channel) { return true; }
diff --git a/media/webrtc/webrtcvideoengine.cc b/media/webrtc/webrtcvideoengine.cc
index d6fabdb..1c1ccc3 100644
--- a/media/webrtc/webrtcvideoengine.cc
+++ b/media/webrtc/webrtcvideoengine.cc
@@ -2119,6 +2119,18 @@
 }
 
 WebRtcVideoChannelSendInfo* WebRtcVideoMediaChannel::GetSendChannel(
+    VideoCapturer* video_capturer) {
+  for (SendChannelMap::iterator iter = send_channels_.begin();
+       iter != send_channels_.end(); ++iter) {
+    WebRtcVideoChannelSendInfo* send_channel = iter->second;
+    if (send_channel->video_capturer() == video_capturer) {
+      return send_channel;
+    }
+  }
+  return NULL;
+}
+
+WebRtcVideoChannelSendInfo* WebRtcVideoMediaChannel::GetSendChannel(
     uint32 local_ssrc) {
   uint32 key;
   if (!GetSendChannelKey(local_ssrc, &key)) {
@@ -2480,7 +2492,8 @@
   return false;
 }
 
-void WebRtcVideoMediaChannel::OnPacketReceived(talk_base::Buffer* packet) {
+void WebRtcVideoMediaChannel::OnPacketReceived(
+    talk_base::Buffer* packet, const talk_base::PacketTime& packet_time) {
   // Pick which channel to send this packet to. If this packet doesn't match
   // any multiplexed streams, just send it to the default channel. Otherwise,
   // send it to the specific decoder instance for that stream.
@@ -2495,10 +2508,16 @@
   engine()->vie()->network()->ReceivedRTPPacket(
       which_channel,
       packet->data(),
+#ifdef USE_WEBRTC_DEV_BRANCH
+      static_cast<int>(packet->length()),
+      webrtc::PacketTime(packet_time.timestamp, packet_time.not_before));
+#else
       static_cast<int>(packet->length()));
+#endif
 }
 
-void WebRtcVideoMediaChannel::OnRtcpReceived(talk_base::Buffer* packet) {
+void WebRtcVideoMediaChannel::OnRtcpReceived(
+    talk_base::Buffer* packet, const talk_base::PacketTime& packet_time) {
 // Sending channels need all RTCP packets with feedback information.
 // Even sender reports can contain attached report blocks.
 // Receiving channels need sender reports in order to create
@@ -2846,23 +2865,20 @@
   return true;
 }
 
+// TODO(zhurunz): Add unittests to test this function.
+// TODO(thorcarpenter): This is broken. One capturer registered on two ssrc
+// will not send any video to the second ssrc send channel. We should remove
+// GetSendChannel(capturer) and pass in an ssrc here.
 void WebRtcVideoMediaChannel::SendFrame(VideoCapturer* capturer,
                                         const VideoFrame* frame) {
-  // If the |capturer| is registered to any send channel, then send the frame
-  // to those send channels.
-  bool capturer_is_channel_owned = false;
-  for (SendChannelMap::iterator iter = send_channels_.begin();
-       iter != send_channels_.end(); ++iter) {
-    WebRtcVideoChannelSendInfo* send_channel = iter->second;
-    if (send_channel->video_capturer() == capturer) {
-      SendFrame(send_channel, frame, capturer->IsScreencast());
-      capturer_is_channel_owned = true;
-    }
-  }
-  if (capturer_is_channel_owned) {
+  // If there's send channel registers to the |capturer|, then only send the
+  // frame to that channel and return. Otherwise send the frame to the default
+  // channel, which currently taking frames from the engine.
+  WebRtcVideoChannelSendInfo* send_channel = GetSendChannel(capturer);
+  if (send_channel) {
+    SendFrame(send_channel, frame, capturer->IsScreencast());
     return;
   }
-
   // TODO(hellner): Remove below for loop once the captured frame no longer
   // come from the engine, i.e. the engine no longer owns a capturer.
   for (SendChannelMap::iterator iter = send_channels_.begin();
diff --git a/media/webrtc/webrtcvideoengine.h b/media/webrtc/webrtcvideoengine.h
index d5f0e57..6278461 100644
--- a/media/webrtc/webrtcvideoengine.h
+++ b/media/webrtc/webrtcvideoengine.h
@@ -266,8 +266,10 @@
   virtual bool SendIntraFrame();
   virtual bool RequestIntraFrame();
 
-  virtual void OnPacketReceived(talk_base::Buffer* packet);
-  virtual void OnRtcpReceived(talk_base::Buffer* packet);
+  virtual void OnPacketReceived(talk_base::Buffer* packet,
+                                const talk_base::PacketTime& packet_time);
+  virtual void OnRtcpReceived(talk_base::Buffer* packet,
+                              const talk_base::PacketTime& packet_time);
   virtual void OnReadyToSend(bool ready);
   virtual bool MuteStream(uint32 ssrc, bool on);
   virtual bool SetRecvRtpHeaderExtensions(
@@ -364,6 +366,7 @@
   // If the local ssrc correspond to that of the default channel the key is 0.
   // For all other channels the returned key will be the same as the local ssrc.
   bool GetSendChannelKey(uint32 local_ssrc, uint32* key);
+  WebRtcVideoChannelSendInfo* GetSendChannel(VideoCapturer* video_capturer);
   WebRtcVideoChannelSendInfo* GetSendChannel(uint32 local_ssrc);
   // Creates a new unique key that can be used for inserting a new send channel
   // into |send_channels_|
diff --git a/media/webrtc/webrtcvideoengine_unittest.cc b/media/webrtc/webrtcvideoengine_unittest.cc
index 93ca9ad..2b83cce 100644
--- a/media/webrtc/webrtcvideoengine_unittest.cc
+++ b/media/webrtc/webrtcvideoengine_unittest.cc
@@ -1216,41 +1216,6 @@
   EXPECT_FALSE(vie_.GetCaptureDenoising(capture_id));
 }
 
-TEST_F(WebRtcVideoEngineTestFake, MultipleSendStreamsWithOneCapturer) {
-  EXPECT_TRUE(SetupEngine());
-  cricket::FakeVideoCapturer capturer;
-  for (unsigned int i = 0; i < sizeof(kSsrcs2)/sizeof(kSsrcs2[0]); ++i) {
-    EXPECT_TRUE(channel_->AddSendStream(
-        cricket::StreamParams::CreateLegacy(kSsrcs2[i])));
-    // Register the capturer to the ssrc.
-    EXPECT_TRUE(channel_->SetCapturer(kSsrcs2[i], &capturer));
-  }
-
-  const int channel0 = vie_.GetChannelFromLocalSsrc(kSsrcs2[0]);
-  ASSERT_NE(-1, channel0);
-  const int channel1 = vie_.GetChannelFromLocalSsrc(kSsrcs2[1]);
-  ASSERT_NE(-1, channel1);
-  ASSERT_NE(channel0, channel1);
-
-  std::vector<cricket::VideoCodec> codecs;
-  codecs.push_back(kVP8Codec);
-  EXPECT_TRUE(channel_->SetSendCodecs(codecs));
-
-  cricket::WebRtcVideoFrame frame;
-  const size_t pixel_width = 1;
-  const size_t pixel_height = 1;
-  const int64 elapsed_time = 0;
-  const int64 time_stamp = 0;
-  EXPECT_TRUE(frame.InitToBlack(kVP8Codec.width, kVP8Codec.height,
-                                pixel_width, pixel_height,
-                                elapsed_time, time_stamp));
-  channel_->SendFrame(&capturer, &frame);
-
-  // Both channels should have received the frame.
-  EXPECT_EQ(1, vie_.GetIncomingFrameNum(channel0));
-  EXPECT_EQ(1, vie_.GetIncomingFrameNum(channel1));
-}
-
 
 // Disabled since its flaky: b/11288120
 TEST_F(WebRtcVideoEngineTestFake, DISABLED_SendReceiveBitratesStats) {
diff --git a/media/webrtc/webrtcvoiceengine.cc b/media/webrtc/webrtcvoiceengine.cc
index 745a1e0..2aa6b8c 100644
--- a/media/webrtc/webrtcvoiceengine.cc
+++ b/media/webrtc/webrtcvoiceengine.cc
@@ -1433,6 +1433,22 @@
   return true;
 }
 
+bool WebRtcVoiceEngine::StartAecDump(FILE* file) {
+#ifdef USE_WEBRTC_DEV_BRANCH
+  StopAecDump();
+  if (voe_wrapper_->processing()->StartDebugRecording(file) !=
+      webrtc::AudioProcessing::kNoError) {
+    LOG_RTCERR1(StartDebugRecording, "FILE*");
+    fclose(file);
+    return false;
+  }
+  is_dumping_aec_ = true;
+  return true;
+#else
+  return false;
+#endif
+}
+
 bool WebRtcVoiceEngine::RegisterProcessor(
     uint32 ssrc,
     VoiceProcessor* voice_processor,
@@ -1590,7 +1606,7 @@
     // Start dumping AEC when we are not dumping.
     if (voe_wrapper_->processing()->StartDebugRecording(
         filename.c_str()) != webrtc::AudioProcessing::kNoError) {
-      LOG_RTCERR0(StartDebugRecording);
+      LOG_RTCERR1(StartDebugRecording, filename.c_str());
     } else {
       is_dumping_aec_ = true;
     }
@@ -2821,7 +2837,8 @@
   return true;
 }
 
-void WebRtcVoiceMediaChannel::OnPacketReceived(talk_base::Buffer* packet) {
+void WebRtcVoiceMediaChannel::OnPacketReceived(
+    talk_base::Buffer* packet, const talk_base::PacketTime& packet_time) {
   // Pick which channel to send this packet to. If this packet doesn't match
   // any multiplexed streams, just send it to the default channel. Otherwise,
   // send it to the specific decoder instance for that stream.
@@ -2854,7 +2871,8 @@
       static_cast<unsigned int>(packet->length()));
 }
 
-void WebRtcVoiceMediaChannel::OnRtcpReceived(talk_base::Buffer* packet) {
+void WebRtcVoiceMediaChannel::OnRtcpReceived(
+    talk_base::Buffer* packet, const talk_base::PacketTime& packet_time) {
   // Sending channels need all RTCP packets with feedback information.
   // Even sender reports can contain attached report blocks.
   // Receiving channels need sender reports in order to create
diff --git a/media/webrtc/webrtcvoiceengine.h b/media/webrtc/webrtcvoiceengine.h
index 29807ef..adf4853 100644
--- a/media/webrtc/webrtcvoiceengine.h
+++ b/media/webrtc/webrtcvoiceengine.h
@@ -174,6 +174,9 @@
   bool SetAudioDeviceModule(webrtc::AudioDeviceModule* adm,
                             webrtc::AudioDeviceModule* adm_sc);
 
+  // Starts AEC dump using existing file.
+  bool StartAecDump(FILE* file);
+
   // Check whether the supplied trace should be ignored.
   bool ShouldIgnoreTrace(const std::string& trace);
 
@@ -356,8 +359,10 @@
   virtual bool CanInsertDtmf();
   virtual bool InsertDtmf(uint32 ssrc, int event, int duration, int flags);
 
-  virtual void OnPacketReceived(talk_base::Buffer* packet);
-  virtual void OnRtcpReceived(talk_base::Buffer* packet);
+  virtual void OnPacketReceived(talk_base::Buffer* packet,
+                                const talk_base::PacketTime& packet_time);
+  virtual void OnRtcpReceived(talk_base::Buffer* packet,
+                              const talk_base::PacketTime& packet_time);
   virtual void OnReadyToSend(bool ready) {}
   virtual bool MuteStream(uint32 ssrc, bool on);
   virtual bool SetSendBandwidth(bool autobw, int bps);
diff --git a/media/webrtc/webrtcvoiceengine_unittest.cc b/media/webrtc/webrtcvoiceengine_unittest.cc
index 4c3ba2a..9bb681a 100644
--- a/media/webrtc/webrtcvoiceengine_unittest.cc
+++ b/media/webrtc/webrtcvoiceengine_unittest.cc
@@ -139,7 +139,7 @@
   }
   void DeliverPacket(const void* data, int len) {
     talk_base::Buffer packet(data, len);
-    channel_->OnPacketReceived(&packet);
+    channel_->OnPacketReceived(&packet, talk_base::PacketTime());
   }
   virtual void TearDown() {
     delete soundclip_;
diff --git a/p2p/base/asyncstuntcpsocket.cc b/p2p/base/asyncstuntcpsocket.cc
index ec00c04..67178f4 100644
--- a/p2p/base/asyncstuntcpsocket.cc
+++ b/p2p/base/asyncstuntcpsocket.cc
@@ -126,7 +126,8 @@
       return;
     }
 
-    SignalReadPacket(this, data, expected_pkt_len, remote_addr);
+    SignalReadPacket(this, data, expected_pkt_len, remote_addr,
+                     talk_base::CreatePacketTime(0));
 
     *len -= actual_length;
     if (*len > 0) {
diff --git a/p2p/base/asyncstuntcpsocket_unittest.cc b/p2p/base/asyncstuntcpsocket_unittest.cc
index 7cb380b..c6a7b1b 100644
--- a/p2p/base/asyncstuntcpsocket_unittest.cc
+++ b/p2p/base/asyncstuntcpsocket_unittest.cc
@@ -109,7 +109,8 @@
   }
 
   void OnReadPacket(talk_base::AsyncPacketSocket* socket, const char* data,
-                    size_t len, const talk_base::SocketAddress& remote_addr) {
+                    size_t len, const talk_base::SocketAddress& remote_addr,
+                    const talk_base::PacketTime& packet_time) {
     recv_packets_.push_back(std::string(data, len));
   }
 
diff --git a/p2p/base/dtlstransportchannel.cc b/p2p/base/dtlstransportchannel.cc
index 7412e5e..4722999 100644
--- a/p2p/base/dtlstransportchannel.cc
+++ b/p2p/base/dtlstransportchannel.cc
@@ -446,9 +446,9 @@
   }
 }
 
-void DtlsTransportChannelWrapper::OnReadPacket(TransportChannel* channel,
-                                               const char* data, size_t size,
-                                               int flags) {
+void DtlsTransportChannelWrapper::OnReadPacket(
+    TransportChannel* channel, const char* data, size_t size,
+    const talk_base::PacketTime& packet_time, int flags) {
   ASSERT(talk_base::Thread::Current() == worker_thread_);
   ASSERT(channel == channel_);
   ASSERT(flags == 0);
@@ -456,7 +456,7 @@
   switch (dtls_state_) {
     case STATE_NONE:
       // We are not doing DTLS
-      SignalReadPacket(this, data, size, 0);
+      SignalReadPacket(this, data, size, packet_time, 0);
       break;
 
     case STATE_OFFERED:
@@ -500,7 +500,7 @@
         ASSERT(!srtp_ciphers_.empty());
 
         // Signal this upwards as a bypass packet.
-        SignalReadPacket(this, data, size, PF_SRTP_BYPASS);
+        SignalReadPacket(this, data, size, packet_time, PF_SRTP_BYPASS);
       }
       break;
     case STATE_CLOSED:
@@ -535,7 +535,7 @@
     char buf[kMaxDtlsPacketLen];
     size_t read;
     if (dtls_->Read(buf, sizeof(buf), &read, NULL) == talk_base::SR_SUCCESS) {
-      SignalReadPacket(this, buf, read, 0);
+      SignalReadPacket(this, buf, read, talk_base::CreatePacketTime(0), 0);
     }
   }
   if (sig & talk_base::SE_CLOSE) {
diff --git a/p2p/base/dtlstransportchannel.h b/p2p/base/dtlstransportchannel.h
index 29d97a2..d6b7346 100644
--- a/p2p/base/dtlstransportchannel.h
+++ b/p2p/base/dtlstransportchannel.h
@@ -225,7 +225,7 @@
   void OnReadableState(TransportChannel* channel);
   void OnWritableState(TransportChannel* channel);
   void OnReadPacket(TransportChannel* channel, const char* data, size_t size,
-                    int flags);
+                    const talk_base::PacketTime& packet_time, int flags);
   void OnReadyToSend(TransportChannel* channel);
   void OnDtlsEvent(talk_base::StreamInterface* stream_, int sig, int err);
   bool SetupDtls();
diff --git a/p2p/base/dtlstransportchannel_unittest.cc b/p2p/base/dtlstransportchannel_unittest.cc
index c6e2804..1fd82d7 100644
--- a/p2p/base/dtlstransportchannel_unittest.cc
+++ b/p2p/base/dtlstransportchannel_unittest.cc
@@ -307,6 +307,7 @@
 
   void OnTransportChannelReadPacket(cricket::TransportChannel* channel,
                                     const char* data, size_t size,
+                                    const talk_base::PacketTime& packet_time,
                                     int flags) {
     uint32 packet_num = 0;
     ASSERT_TRUE(VerifyPacket(data, size, &packet_num));
@@ -320,6 +321,7 @@
   // Hook into the raw packet stream to make sure DTLS packets are encrypted.
   void OnFakeTransportChannelReadPacket(cricket::TransportChannel* channel,
                                         const char* data, size_t size,
+                                        const talk_base::PacketTime& time,
                                         int flags) {
     // Flags shouldn't be set on the underlying TransportChannel packets.
     ASSERT_EQ(0, flags);
diff --git a/p2p/base/fakesession.h b/p2p/base/fakesession.h
index 6a8ab4b..2615f50 100644
--- a/p2p/base/fakesession.h
+++ b/p2p/base/fakesession.h
@@ -204,7 +204,8 @@
     PacketMessageData* data = static_cast<PacketMessageData*>(
         msg->pdata);
     dest_->SignalReadPacket(dest_, data->packet.data(),
-                            data->packet.length(), 0);
+                            data->packet.length(),
+                            talk_base::CreatePacketTime(0), 0);
     delete data;
   }
 
diff --git a/p2p/base/p2ptransportchannel.cc b/p2p/base/p2ptransportchannel.cc
index e8f53ad..38cc354 100644
--- a/p2p/base/p2ptransportchannel.cc
+++ b/p2p/base/p2ptransportchannel.cc
@@ -1227,8 +1227,9 @@
 }
 
 // We data is available, let listeners know
-void P2PTransportChannel::OnReadPacket(Connection *connection, const char *data,
-                                       size_t len) {
+void P2PTransportChannel::OnReadPacket(
+    Connection *connection, const char *data, size_t len,
+    const talk_base::PacketTime& packet_time) {
   ASSERT(worker_thread_ == talk_base::Thread::Current());
 
   // Do not deliver, if packet doesn't belong to the correct transport channel.
@@ -1236,7 +1237,7 @@
     return;
 
   // Let the client know of an incoming packet
-  SignalReadPacket(this, data, len, 0);
+  SignalReadPacket(this, data, len, packet_time, 0);
 }
 
 void P2PTransportChannel::OnReadyToSend(Connection* connection) {
diff --git a/p2p/base/p2ptransportchannel.h b/p2p/base/p2ptransportchannel.h
index 63ec6aa..6f287f3 100644
--- a/p2p/base/p2ptransportchannel.h
+++ b/p2p/base/p2ptransportchannel.h
@@ -40,6 +40,7 @@
 #include <map>
 #include <vector>
 #include <string>
+#include "talk/base/asyncpacketsocket.h"
 #include "talk/base/sigslot.h"
 #include "talk/p2p/base/candidate.h"
 #include "talk/p2p/base/portinterface.h"
@@ -207,8 +208,9 @@
   void OnPortDestroyed(PortInterface* port);
   void OnRoleConflict(PortInterface* port);
 
-  void OnConnectionStateChange(Connection *connection);
-  void OnReadPacket(Connection *connection, const char *data, size_t len);
+  void OnConnectionStateChange(Connection* connection);
+  void OnReadPacket(Connection *connection, const char *data, size_t len,
+                    const talk_base::PacketTime& packet_time);
   void OnReadyToSend(Connection* connection);
   void OnConnectionDestroyed(Connection *connection);
 
diff --git a/p2p/base/p2ptransportchannel_unittest.cc b/p2p/base/p2ptransportchannel_unittest.cc
index 07cfeaa..3c24ded 100644
--- a/p2p/base/p2ptransportchannel_unittest.cc
+++ b/p2p/base/p2ptransportchannel_unittest.cc
@@ -613,7 +613,8 @@
     rch->OnCandidate(c);
   }
   void OnReadPacket(cricket::TransportChannel* channel, const char* data,
-                    size_t len, int flags) {
+                    size_t len, const talk_base::PacketTime& packet_time,
+                    int flags) {
     std::list<std::string>& packets = GetPacketList(channel);
     packets.push_front(std::string(data, len));
   }
diff --git a/p2p/base/port.cc b/p2p/base/port.cc
index 7d52386..24ef427 100644
--- a/p2p/base/port.cc
+++ b/p2p/base/port.cc
@@ -924,7 +924,8 @@
   }
 }
 
-void Connection::OnReadPacket(const char* data, size_t size) {
+void Connection::OnReadPacket(
+  const char* data, size_t size, const talk_base::PacketTime& packet_time) {
   talk_base::scoped_ptr<IceMessage> msg;
   std::string remote_ufrag;
   const talk_base::SocketAddress& addr(remote_candidate_.address());
@@ -938,7 +939,7 @@
 
       last_data_received_ = talk_base::Time();
       recv_rate_tracker_.Update(size);
-      SignalReadPacket(this, data, size);
+      SignalReadPacket(this, data, size, packet_time);
 
       // If timed out sending writability checks, start up again
       if (!pruned_ && (write_state_ == STATE_WRITE_TIMEOUT)) {
diff --git a/p2p/base/port.h b/p2p/base/port.h
index ab7fded..9ea3f0c 100644
--- a/p2p/base/port.h
+++ b/p2p/base/port.h
@@ -32,6 +32,7 @@
 #include <vector>
 #include <map>
 
+#include "talk/base/asyncpacketsocket.h"
 #include "talk/base/network.h"
 #include "talk/base/proxyinfo.h"
 #include "talk/base/ratetracker.h"
@@ -45,10 +46,6 @@
 #include "talk/p2p/base/stunrequest.h"
 #include "talk/p2p/base/transport.h"
 
-namespace talk_base {
-class AsyncPacketSocket;
-}
-
 namespace cricket {
 
 class Connection;
@@ -240,7 +237,8 @@
   // TODO(mallinath) - Make it pure virtual.
   virtual bool HandleIncomingPacket(
       talk_base::AsyncPacketSocket* socket, const char* data, size_t size,
-      const talk_base::SocketAddress& remote_addr) {
+      const talk_base::SocketAddress& remote_addr,
+      const talk_base::PacketTime& packet_time) {
     ASSERT(false);
     return false;
   }
@@ -470,12 +468,14 @@
   // Error if Send() returns < 0
   virtual int GetError() = 0;
 
-  sigslot::signal3<Connection*, const char*, size_t> SignalReadPacket;
+  sigslot::signal4<Connection*, const char*, size_t,
+                   const talk_base::PacketTime&> SignalReadPacket;
 
   sigslot::signal1<Connection*> SignalReadyToSend;
 
   // Called when a packet is received on this connection.
-  void OnReadPacket(const char* data, size_t size);
+  void OnReadPacket(const char* data, size_t size,
+                    const talk_base::PacketTime& packet_time);
 
   // Called when the socket is currently able to send.
   void OnReadyToSend();
diff --git a/p2p/base/port_unittest.cc b/p2p/base/port_unittest.cc
index a527155..1122d8a 100644
--- a/p2p/base/port_unittest.cc
+++ b/p2p/base/port_unittest.cc
@@ -1049,7 +1049,8 @@
   IceMessage* msg = lport->last_stun_msg();
   EXPECT_EQ(STUN_BINDING_REQUEST, msg->type());
   conn->OnReadPacket(lport->last_stun_buf()->Data(),
-                     lport->last_stun_buf()->Length());
+                     lport->last_stun_buf()->Length(),
+                     talk_base::PacketTime());
   ASSERT_TRUE_WAIT(lport->last_stun_msg() != NULL, 1000);
   msg = lport->last_stun_msg();
   EXPECT_EQ(STUN_BINDING_RESPONSE, msg->type());
@@ -1082,7 +1083,7 @@
   lport->Reset();
   talk_base::scoped_ptr<ByteBuffer> buf(new ByteBuffer());
   WriteStunMessage(modified_req.get(), buf.get());
-  conn1->OnReadPacket(buf->Data(), buf->Length());
+  conn1->OnReadPacket(buf->Data(), buf->Length(), talk_base::PacketTime());
   ASSERT_TRUE_WAIT(lport->last_stun_msg() != NULL, 1000);
   msg = lport->last_stun_msg();
   EXPECT_EQ(STUN_BINDING_ERROR_RESPONSE, msg->type());
@@ -1120,7 +1121,8 @@
   EXPECT_EQ(STUN_BINDING_REQUEST, msg->type());
   // Send rport binding request to lport.
   lconn->OnReadPacket(rport->last_stun_buf()->Data(),
-                      rport->last_stun_buf()->Length());
+                      rport->last_stun_buf()->Length(),
+                      talk_base::PacketTime());
 
   ASSERT_TRUE_WAIT(lport->last_stun_msg() != NULL, 1000);
   EXPECT_EQ(STUN_BINDING_RESPONSE, lport->last_stun_msg()->type());
@@ -1902,7 +1904,8 @@
   EXPECT_EQ(STUN_BINDING_REQUEST, msg->type());
   // Send rport binding request to lport.
   lconn->OnReadPacket(rport->last_stun_buf()->Data(),
-                      rport->last_stun_buf()->Length());
+                      rport->last_stun_buf()->Length(),
+                      talk_base::PacketTime());
   ASSERT_TRUE_WAIT(lport->last_stun_msg() != NULL, 1000);
   EXPECT_EQ(STUN_BINDING_RESPONSE, lport->last_stun_msg()->type());
   uint32 last_ping_received1 = lconn->last_ping_received();
@@ -1910,7 +1913,7 @@
   // Adding a delay of 100ms.
   talk_base::Thread::Current()->ProcessMessages(100);
   // Pinging lconn using stun indication message.
-  lconn->OnReadPacket(buf->Data(), buf->Length());
+  lconn->OnReadPacket(buf->Data(), buf->Length(), talk_base::PacketTime());
   uint32 last_ping_received2 = lconn->last_ping_received();
   EXPECT_GT(last_ping_received2, last_ping_received1);
 }
@@ -2272,7 +2275,8 @@
 
   // Feeding the respone message from litemode to the full mode connection.
   ch1.conn()->OnReadPacket(ice_lite_port->last_stun_buf()->Data(),
-                           ice_lite_port->last_stun_buf()->Length());
+                           ice_lite_port->last_stun_buf()->Length(),
+                           talk_base::PacketTime());
   // Verifying full mode connection becomes writable from the response.
   EXPECT_EQ_WAIT(Connection::STATE_WRITABLE, ch1.conn()->write_state(),
                  kTimeout);
diff --git a/p2p/base/rawtransportchannel.cc b/p2p/base/rawtransportchannel.cc
index ec22502..2baef42 100644
--- a/p2p/base/rawtransportchannel.cc
+++ b/p2p/base/rawtransportchannel.cc
@@ -257,7 +257,7 @@
     PortInterface* port, const char* data, size_t size,
     const talk_base::SocketAddress& addr) {
   ASSERT(port_ == port);
-  SignalReadPacket(this, data, size, 0);
+  SignalReadPacket(this, data, size, talk_base::CreatePacketTime(0), 0);
 }
 
 void RawTransportChannel::OnMessage(talk_base::Message* msg) {
diff --git a/p2p/base/relayport.cc b/p2p/base/relayport.cc
index ff8c07c..ddfca71 100644
--- a/p2p/base/relayport.cc
+++ b/p2p/base/relayport.cc
@@ -155,10 +155,11 @@
   void OnSocketClose(talk_base::AsyncPacketSocket* socket, int error);
 
   // Called when a packet is received on this socket.
-  void OnReadPacket(talk_base::AsyncPacketSocket* socket,
-                    const char* data, size_t size,
-                    const talk_base::SocketAddress& remote_addr);
-
+  void OnReadPacket(
+    talk_base::AsyncPacketSocket* socket,
+    const char* data, size_t size,
+    const talk_base::SocketAddress& remote_addr,
+    const talk_base::PacketTime& packet_time);
   // Called when the socket is currently able to send.
   void OnReadyToSend(talk_base::AsyncPacketSocket* socket);
 
@@ -393,9 +394,11 @@
 
 void RelayPort::OnReadPacket(
     const char* data, size_t size,
-    const talk_base::SocketAddress& remote_addr, ProtocolType proto) {
+    const talk_base::SocketAddress& remote_addr,
+    ProtocolType proto,
+    const talk_base::PacketTime& packet_time) {
   if (Connection* conn = GetConnection(remote_addr)) {
-    conn->OnReadPacket(data, size);
+    conn->OnReadPacket(data, size, packet_time);
   } else {
     Port::OnReadPacket(data, size, remote_addr, proto);
   }
@@ -682,9 +685,11 @@
   HandleConnectFailure(socket);
 }
 
-void RelayEntry::OnReadPacket(talk_base::AsyncPacketSocket* socket,
-                              const char* data, size_t size,
-                              const talk_base::SocketAddress& remote_addr) {
+void RelayEntry::OnReadPacket(
+    talk_base::AsyncPacketSocket* socket,
+    const char* data, size_t size,
+    const talk_base::SocketAddress& remote_addr,
+    const talk_base::PacketTime& packet_time) {
   // ASSERT(remote_addr == port_->server_addr());
   // TODO: are we worried about this?
 
@@ -698,7 +703,7 @@
   // by the server,  The actual remote address is the one we recorded.
   if (!port_->HasMagicCookie(data, size)) {
     if (locked_) {
-      port_->OnReadPacket(data, size, ext_addr_, PROTO_UDP);
+      port_->OnReadPacket(data, size, ext_addr_, PROTO_UDP, packet_time);
     } else {
       LOG(WARNING) << "Dropping packet: entry not locked";
     }
@@ -751,7 +756,7 @@
 
   // Process the actual data and remote address in the normal manner.
   port_->OnReadPacket(data_attr->bytes(), data_attr->length(), remote_addr2,
-                      PROTO_UDP);
+                      PROTO_UDP, packet_time);
 }
 
 void RelayEntry::OnReadyToSend(talk_base::AsyncPacketSocket* socket) {
diff --git a/p2p/base/relayport.h b/p2p/base/relayport.h
index c15e7e0..08df12f 100644
--- a/p2p/base/relayport.h
+++ b/p2p/base/relayport.h
@@ -99,7 +99,8 @@
   // Dispatches the given packet to the port or connection as appropriate.
   void OnReadPacket(const char* data, size_t size,
                     const talk_base::SocketAddress& remote_addr,
-                    ProtocolType proto);
+                    ProtocolType proto,
+                    const talk_base::PacketTime& packet_time);
 
  private:
   friend class RelayEntry;
diff --git a/p2p/base/relayport_unittest.cc b/p2p/base/relayport_unittest.cc
index ced8c58..bd00af8 100644
--- a/p2p/base/relayport_unittest.cc
+++ b/p2p/base/relayport_unittest.cc
@@ -78,7 +78,8 @@
 
   void OnReadPacket(talk_base::AsyncPacketSocket* socket,
                     const char* data, size_t size,
-                    const talk_base::SocketAddress& remote_addr) {
+                    const talk_base::SocketAddress& remote_addr,
+                    const talk_base::PacketTime& packet_time) {
     received_packet_count_[socket]++;
   }
 
diff --git a/p2p/base/relayserver.cc b/p2p/base/relayserver.cc
index c2cf472..c2619c0 100644
--- a/p2p/base/relayserver.cc
+++ b/p2p/base/relayserver.cc
@@ -198,7 +198,8 @@
 
 void RelayServer::OnInternalPacket(
     talk_base::AsyncPacketSocket* socket, const char* bytes, size_t size,
-    const talk_base::SocketAddress& remote_addr) {
+    const talk_base::SocketAddress& remote_addr,
+    const talk_base::PacketTime& packet_time) {
 
   // Get the address of the connection we just received on.
   talk_base::SocketAddressPair ap(remote_addr, socket->GetLocalAddress());
@@ -242,7 +243,8 @@
 
 void RelayServer::OnExternalPacket(
     talk_base::AsyncPacketSocket* socket, const char* bytes, size_t size,
-    const talk_base::SocketAddress& remote_addr) {
+    const talk_base::SocketAddress& remote_addr,
+    const talk_base::PacketTime& packet_time) {
 
   // Get the address of the connection we just received on.
   talk_base::SocketAddressPair ap(remote_addr, socket->GetLocalAddress());
diff --git a/p2p/base/relayserver.h b/p2p/base/relayserver.h
index f3bee7e..922a256 100644
--- a/p2p/base/relayserver.h
+++ b/p2p/base/relayserver.h
@@ -104,10 +104,12 @@
   // Called when a packet is received by the server on one of its sockets.
   void OnInternalPacket(talk_base::AsyncPacketSocket* socket,
                         const char* bytes, size_t size,
-                        const talk_base::SocketAddress& remote_addr);
+                        const talk_base::SocketAddress& remote_addr,
+                        const talk_base::PacketTime& packet_time);
   void OnExternalPacket(talk_base::AsyncPacketSocket* socket,
                         const char* bytes, size_t size,
-                        const talk_base::SocketAddress& remote_addr);
+                        const talk_base::SocketAddress& remote_addr,
+                        const talk_base::PacketTime& packet_time);
 
   void OnReadEvent(talk_base::AsyncSocket* socket);
 
diff --git a/p2p/base/session_unittest.cc b/p2p/base/session_unittest.cc
index b64e737..ab4620f 100644
--- a/p2p/base/session_unittest.cc
+++ b/p2p/base/session_unittest.cc
@@ -814,7 +814,7 @@
   }
 
   void OnReadPacket(cricket::TransportChannel* p, const char* buf,
-                    size_t size, int flags) {
+                    size_t size, const talk_base::PacketTime& time, int flags) {
     if (memcmp(buf, name.c_str(), name.size()) != 0)
       return;  // drop packet if packet doesn't belong to this channel. This
                // can happen when transport channels are muxed together.
diff --git a/p2p/base/stunport.cc b/p2p/base/stunport.cc
index 283eade..913f9af 100644
--- a/p2p/base/stunport.cc
+++ b/p2p/base/stunport.cc
@@ -254,9 +254,10 @@
   MaybePrepareStunCandidate();
 }
 
-void UDPPort::OnReadPacket(talk_base::AsyncPacketSocket* socket,
-                           const char* data, size_t size,
-                           const talk_base::SocketAddress& remote_addr) {
+void UDPPort::OnReadPacket(
+  talk_base::AsyncPacketSocket* socket, const char* data, size_t size,
+  const talk_base::SocketAddress& remote_addr,
+  const talk_base::PacketTime& packet_time) {
   ASSERT(socket == socket_);
 
   // Look for a response from the STUN server.
@@ -269,7 +270,7 @@
   }
 
   if (Connection* conn = GetConnection(remote_addr)) {
-    conn->OnReadPacket(data, size);
+    conn->OnReadPacket(data, size, packet_time);
   } else {
     Port::OnReadPacket(data, size, remote_addr, PROTO_UDP);
   }
diff --git a/p2p/base/stunport.h b/p2p/base/stunport.h
index 8f72556..a8b89c3 100644
--- a/p2p/base/stunport.h
+++ b/p2p/base/stunport.h
@@ -97,9 +97,10 @@
 
   virtual bool HandleIncomingPacket(
       talk_base::AsyncPacketSocket* socket, const char* data, size_t size,
-      const talk_base::SocketAddress& remote_addr) {
+      const talk_base::SocketAddress& remote_addr,
+      const talk_base::PacketTime& packet_time) {
     // All packets given to UDP port will be consumed.
-    OnReadPacket(socket, data, size, remote_addr);
+    OnReadPacket(socket, data, size, remote_addr, packet_time);
     return true;
   }
 
@@ -131,7 +132,9 @@
                            const talk_base::SocketAddress& address);
   void OnReadPacket(talk_base::AsyncPacketSocket* socket,
                     const char* data, size_t size,
-                    const talk_base::SocketAddress& remote_addr);
+                    const talk_base::SocketAddress& remote_addr,
+                    const talk_base::PacketTime& packet_time);
+
   void OnReadyToSend(talk_base::AsyncPacketSocket* socket);
 
   // This method will send STUN binding request if STUN server address is set.
diff --git a/p2p/base/stunport_unittest.cc b/p2p/base/stunport_unittest.cc
index 12b32db..2a98a9f 100644
--- a/p2p/base/stunport_unittest.cc
+++ b/p2p/base/stunport_unittest.cc
@@ -99,13 +99,16 @@
   }
 
   void OnReadPacket(talk_base::AsyncPacketSocket* socket, const char* data,
-                    size_t size, const talk_base::SocketAddress& remote_addr) {
-    stun_port_->HandleIncomingPacket(socket, data, size, remote_addr);
+                    size_t size, const talk_base::SocketAddress& remote_addr,
+                    const talk_base::PacketTime& packet_time) {
+    stun_port_->HandleIncomingPacket(
+        socket, data, size, remote_addr, talk_base::PacketTime());
   }
 
   void SendData(const char* data, size_t len) {
     stun_port_->HandleIncomingPacket(
-        socket_.get(), data, len, talk_base::SocketAddress("22.22.22.22", 0));
+        socket_.get(), data, len, talk_base::SocketAddress("22.22.22.22", 0),
+        talk_base::PacketTime());
   }
 
  protected:
diff --git a/p2p/base/stunserver.cc b/p2p/base/stunserver.cc
index 80719b4..062be20 100644
--- a/p2p/base/stunserver.cc
+++ b/p2p/base/stunserver.cc
@@ -42,7 +42,8 @@
 
 void StunServer::OnPacket(
     talk_base::AsyncPacketSocket* socket, const char* buf, size_t size,
-    const talk_base::SocketAddress& remote_addr) {
+    const talk_base::SocketAddress& remote_addr,
+    const talk_base::PacketTime& packet_time) {
   // Parse the STUN message; eat any messages that fail to parse.
   talk_base::ByteBuffer bbuf(buf, size);
   StunMessage msg;
diff --git a/p2p/base/stunserver.h b/p2p/base/stunserver.h
index 6e51ad1..c5d12e1 100644
--- a/p2p/base/stunserver.h
+++ b/p2p/base/stunserver.h
@@ -47,7 +47,8 @@
   // Slot for AsyncSocket.PacketRead:
   void OnPacket(
       talk_base::AsyncPacketSocket* socket, const char* buf, size_t size,
-      const talk_base::SocketAddress& remote_addr);
+      const talk_base::SocketAddress& remote_addr,
+      const talk_base::PacketTime& packet_time);
 
   // Handlers for the different types of STUN/TURN requests:
   void OnBindingRequest(StunMessage* msg,
diff --git a/p2p/base/tcpport.cc b/p2p/base/tcpport.cc
index 11334c6..2cca82f 100644
--- a/p2p/base/tcpport.cc
+++ b/p2p/base/tcpport.cc
@@ -218,7 +218,8 @@
 
 void TCPPort::OnReadPacket(talk_base::AsyncPacketSocket* socket,
                            const char* data, size_t size,
-                           const talk_base::SocketAddress& remote_addr) {
+                           const talk_base::SocketAddress& remote_addr,
+                           const talk_base::PacketTime& packet_time) {
   Port::OnReadPacket(data, size, remote_addr, PROTO_TCP);
 }
 
@@ -310,11 +311,12 @@
   set_write_state(STATE_WRITE_TIMEOUT);
 }
 
-void TCPConnection::OnReadPacket(talk_base::AsyncPacketSocket* socket,
-                                 const char* data, size_t size,
-                                 const talk_base::SocketAddress& remote_addr) {
+void TCPConnection::OnReadPacket(
+  talk_base::AsyncPacketSocket* socket, const char* data, size_t size,
+  const talk_base::SocketAddress& remote_addr,
+  const talk_base::PacketTime& packet_time) {
   ASSERT(socket == socket_);
-  Connection::OnReadPacket(data, size);
+  Connection::OnReadPacket(data, size, packet_time);
 }
 
 void TCPConnection::OnReadyToSend(talk_base::AsyncPacketSocket* socket) {
diff --git a/p2p/base/tcpport.h b/p2p/base/tcpport.h
index 599d3c6..77b177a 100644
--- a/p2p/base/tcpport.h
+++ b/p2p/base/tcpport.h
@@ -102,7 +102,8 @@
   // Receives packet signal from the local TCP Socket.
   void OnReadPacket(talk_base::AsyncPacketSocket* socket,
                     const char* data, size_t size,
-                    const talk_base::SocketAddress& remote_addr);
+                    const talk_base::SocketAddress& remote_addr,
+                    const talk_base::PacketTime& packet_time);
 
   void OnReadyToSend(talk_base::AsyncPacketSocket* socket);
 
@@ -137,7 +138,8 @@
   void OnClose(talk_base::AsyncPacketSocket* socket, int error);
   void OnReadPacket(talk_base::AsyncPacketSocket* socket,
                     const char* data, size_t size,
-                    const talk_base::SocketAddress& remote_addr);
+                    const talk_base::SocketAddress& remote_addr,
+                    const talk_base::PacketTime& packet_time);
   void OnReadyToSend(talk_base::AsyncPacketSocket* socket);
 
   talk_base::AsyncPacketSocket* socket_;
diff --git a/p2p/base/transportchannel.h b/p2p/base/transportchannel.h
index c48e1a5..47ba990 100644
--- a/p2p/base/transportchannel.h
+++ b/p2p/base/transportchannel.h
@@ -31,6 +31,7 @@
 #include <string>
 #include <vector>
 
+#include "talk/base/asyncpacketsocket.h"
 #include "talk/base/basictypes.h"
 #include "talk/base/dscp.h"
 #include "talk/base/sigslot.h"
@@ -122,8 +123,8 @@
       size_t result_len) = 0;
 
   // Signalled each time a packet is received on this channel.
-  sigslot::signal4<TransportChannel*, const char*,
-                   size_t, int> SignalReadPacket;
+  sigslot::signal5<TransportChannel*, const char*,
+                   size_t, const talk_base::PacketTime&, int> SignalReadPacket;
 
   // 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
diff --git a/p2p/base/transportchannelproxy.cc b/p2p/base/transportchannelproxy.cc
index 9a10603..0d8cace 100644
--- a/p2p/base/transportchannelproxy.cc
+++ b/p2p/base/transportchannelproxy.cc
@@ -234,12 +234,12 @@
   // Note: SignalWritableState fired by set_readable.
 }
 
-void TransportChannelProxy::OnReadPacket(TransportChannel* channel,
-                                         const char* data, size_t size,
-                                         int flags) {
+void TransportChannelProxy::OnReadPacket(
+    TransportChannel* channel, const char* data, size_t size,
+    const talk_base::PacketTime& packet_time, int flags) {
   ASSERT(talk_base::Thread::Current() == worker_thread_);
   ASSERT(channel == impl_);
-  SignalReadPacket(this, data, size, flags);
+  SignalReadPacket(this, data, size, packet_time, flags);
 }
 
 void TransportChannelProxy::OnReadyToSend(TransportChannel* channel) {
diff --git a/p2p/base/transportchannelproxy.h b/p2p/base/transportchannelproxy.h
index 3559ed5..196d0f6 100644
--- a/p2p/base/transportchannelproxy.h
+++ b/p2p/base/transportchannelproxy.h
@@ -90,7 +90,7 @@
   void OnReadableState(TransportChannel* channel);
   void OnWritableState(TransportChannel* channel);
   void OnReadPacket(TransportChannel* channel, const char* data, size_t size,
-                    int flags);
+                    const talk_base::PacketTime& packet_time, int flags);
   void OnReadyToSend(TransportChannel* channel);
   void OnRouteChange(TransportChannel* channel, const Candidate& candidate);
 
diff --git a/p2p/base/turnport.cc b/p2p/base/turnport.cc
index 92f62c8..01d7f9c 100644
--- a/p2p/base/turnport.cc
+++ b/p2p/base/turnport.cc
@@ -356,9 +356,10 @@
   return static_cast<int>(size);
 }
 
-void TurnPort::OnReadPacket(talk_base::AsyncPacketSocket* socket,
-                           const char* data, size_t size,
-                           const talk_base::SocketAddress& remote_addr) {
+void TurnPort::OnReadPacket(
+    talk_base::AsyncPacketSocket* socket, const char* data, size_t size,
+    const talk_base::SocketAddress& remote_addr,
+    const talk_base::PacketTime& packet_time) {
   ASSERT(socket == socket_.get());
   ASSERT(remote_addr == server_address_.address);
 
@@ -373,9 +374,9 @@
   // a response to a previous request.
   uint16 msg_type = talk_base::GetBE16(data);
   if (IsTurnChannelData(msg_type)) {
-    HandleChannelData(msg_type, data, size);
+    HandleChannelData(msg_type, data, size, packet_time);
   } else if (msg_type == TURN_DATA_INDICATION) {
-    HandleDataIndication(data, size);
+    HandleDataIndication(data, size, packet_time);
   } else {
     // This must be a response for one of our requests.
     // Check success responses, but not errors, for MESSAGE-INTEGRITY.
@@ -460,7 +461,8 @@
   OnAllocateError();
 }
 
-void TurnPort::HandleDataIndication(const char* data, size_t size) {
+void TurnPort::HandleDataIndication(const char* data, size_t size,
+                                    const talk_base::PacketTime& packet_time) {
   // Read in the message, and process according to RFC5766, Section 10.4.
   talk_base::ByteBuffer buf(data, size);
   TurnMessage msg;
@@ -495,11 +497,13 @@
     return;
   }
 
-  DispatchPacket(data_attr->bytes(), data_attr->length(), ext_addr, PROTO_UDP);
+  DispatchPacket(data_attr->bytes(), data_attr->length(), ext_addr,
+                 PROTO_UDP, packet_time);
 }
 
 void TurnPort::HandleChannelData(int channel_id, const char* data,
-                                 size_t size) {
+                                 size_t size,
+                                 const talk_base::PacketTime& packet_time) {
   // Read the message, and process according to RFC5766, Section 11.6.
   //    0                   1                   2                   3
   //    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
@@ -531,13 +535,14 @@
   }
 
   DispatchPacket(data + TURN_CHANNEL_HEADER_SIZE, len, entry->address(),
-                 PROTO_UDP);
+                 PROTO_UDP, packet_time);
 }
 
 void TurnPort::DispatchPacket(const char* data, size_t size,
-    const talk_base::SocketAddress& remote_addr, ProtocolType proto) {
+    const talk_base::SocketAddress& remote_addr,
+    ProtocolType proto, const talk_base::PacketTime& packet_time) {
   if (Connection* conn = GetConnection(remote_addr)) {
-    conn->OnReadPacket(data, size);
+    conn->OnReadPacket(data, size, packet_time);
   } else {
     Port::OnReadPacket(data, size, remote_addr, proto);
   }
diff --git a/p2p/base/turnport.h b/p2p/base/turnport.h
index e5c03da..e380a89 100644
--- a/p2p/base/turnport.h
+++ b/p2p/base/turnport.h
@@ -32,11 +32,11 @@
 #include <string>
 #include <list>
 
+#include "talk/base/asyncpacketsocket.h"
 #include "talk/p2p/base/port.h"
 #include "talk/p2p/client/basicportallocator.h"
 
 namespace talk_base {
-class AsyncPacketSocket;
 class AsyncResolver;
 class SignalThread;
 }
@@ -79,9 +79,10 @@
   virtual int SetOption(talk_base::Socket::Option opt, int value);
   virtual int GetOption(talk_base::Socket::Option opt, int* value);
   virtual int GetError();
-  virtual void OnReadPacket(talk_base::AsyncPacketSocket* socket,
-                            const char* data, size_t size,
-                            const talk_base::SocketAddress& remote_addr);
+  virtual void OnReadPacket(
+      talk_base::AsyncPacketSocket* socket, const char* data, size_t size,
+      const talk_base::SocketAddress& remote_addr,
+      const talk_base::PacketTime& packet_time);
   virtual void OnReadyToSend(talk_base::AsyncPacketSocket* socket);
 
   void OnSocketConnect(talk_base::AsyncPacketSocket* socket);
@@ -134,10 +135,13 @@
   void OnAllocateError();
   void OnAllocateRequestTimeout();
 
-  void HandleDataIndication(const char* data, size_t size);
-  void HandleChannelData(int channel_id, const char* data, size_t size);
+  void HandleDataIndication(const char* data, size_t size,
+                            const talk_base::PacketTime& packet_time);
+  void HandleChannelData(int channel_id, const char* data, size_t size,
+                         const talk_base::PacketTime& packet_time);
   void DispatchPacket(const char* data, size_t size,
-      const talk_base::SocketAddress& remote_addr, ProtocolType proto);
+      const talk_base::SocketAddress& remote_addr,
+      ProtocolType proto, const talk_base::PacketTime& packet_time);
 
   bool ScheduleRefresh(int lifetime);
   void SendRequest(StunRequest* request, int delay);
diff --git a/p2p/base/turnport_unittest.cc b/p2p/base/turnport_unittest.cc
index 11e2213..d559894 100644
--- a/p2p/base/turnport_unittest.cc
+++ b/p2p/base/turnport_unittest.cc
@@ -118,13 +118,15 @@
       turn_create_permission_success_ = true;
     }
   }
-  void OnTurnReadPacket(Connection* conn, const char* data, size_t size) {
+  void OnTurnReadPacket(Connection* conn, const char* data, size_t size,
+                        const talk_base::PacketTime& packet_time) {
     turn_packets_.push_back(talk_base::Buffer(data, size));
   }
   void OnUdpPortComplete(Port* port) {
     udp_ready_ = true;
   }
-  void OnUdpReadPacket(Connection* conn, const char* data, size_t size) {
+  void OnUdpReadPacket(Connection* conn, const char* data, size_t size,
+                       const talk_base::PacketTime& packet_time) {
     udp_packets_.push_back(talk_base::Buffer(data, size));
   }
 
diff --git a/p2p/base/turnserver.cc b/p2p/base/turnserver.cc
index 17ecf35..0bd903a 100644
--- a/p2p/base/turnserver.cc
+++ b/p2p/base/turnserver.cc
@@ -109,7 +109,8 @@
 
   void OnExternalPacket(talk_base::AsyncPacketSocket* socket,
                         const char* data, size_t size,
-                        const talk_base::SocketAddress& addr);
+                        const talk_base::SocketAddress& addr,
+                        const talk_base::PacketTime& packet_time);
 
   static int ComputeLifetime(const TurnMessage* msg);
   bool HasPermission(const talk_base::IPAddress& addr);
@@ -280,7 +281,8 @@
 
 void TurnServer::OnInternalPacket(talk_base::AsyncPacketSocket* socket,
                                   const char* data, size_t size,
-                                  const talk_base::SocketAddress& addr) {
+                                  const talk_base::SocketAddress& addr,
+                                  const talk_base::PacketTime& packet_time) {
   // Fail if the packet is too small to even contain a channel header.
   if (size < TURN_CHANNEL_HEADER_SIZE) {
    return;
@@ -838,7 +840,8 @@
 void TurnServer::Allocation::OnExternalPacket(
     talk_base::AsyncPacketSocket* socket,
     const char* data, size_t size,
-    const talk_base::SocketAddress& addr) {
+    const talk_base::SocketAddress& addr,
+    const talk_base::PacketTime& packet_time) {
   ASSERT(external_socket_.get() == socket);
   Channel* channel = FindChannel(addr);
   if (channel) {
diff --git a/p2p/base/turnserver.h b/p2p/base/turnserver.h
index 56ce2fc..2c33cdb 100644
--- a/p2p/base/turnserver.h
+++ b/p2p/base/turnserver.h
@@ -33,13 +33,13 @@
 #include <set>
 #include <string>
 
+#include "talk/base/asyncpacketsocket.h"
 #include "talk/base/messagequeue.h"
 #include "talk/base/sigslot.h"
 #include "talk/base/socketaddress.h"
 #include "talk/p2p/base/portinterface.h"
 
 namespace talk_base {
-class AsyncPacketSocket;
 class ByteBuffer;
 class PacketSocketFactory;
 class Thread;
@@ -123,7 +123,8 @@
   typedef std::map<Connection, Allocation*> AllocationMap;
 
   void OnInternalPacket(talk_base::AsyncPacketSocket* socket, const char* data,
-                        size_t size, const talk_base::SocketAddress& address);
+                        size_t size, const talk_base::SocketAddress& address,
+                        const talk_base::PacketTime& packet_time);
 
   void OnNewInternalConnection(talk_base::AsyncSocket* socket);
 
diff --git a/p2p/client/basicportallocator.cc b/p2p/client/basicportallocator.cc
index a5310b7..dbc2e33 100644
--- a/p2p/client/basicportallocator.cc
+++ b/p2p/client/basicportallocator.cc
@@ -149,7 +149,9 @@
 
   void OnReadPacket(talk_base::AsyncPacketSocket* socket,
                     const char* data, size_t size,
-                    const talk_base::SocketAddress& remote_addr);
+                    const talk_base::SocketAddress& remote_addr,
+                    const talk_base::PacketTime& packet_time);
+
   void OnPortDestroyed(PortInterface* port);
 
   BasicPortAllocatorSession* session_;
@@ -1024,13 +1026,15 @@
 
 void AllocationSequence::OnReadPacket(
     talk_base::AsyncPacketSocket* socket, const char* data, size_t size,
-    const talk_base::SocketAddress& remote_addr) {
+    const talk_base::SocketAddress& remote_addr,
+    const talk_base::PacketTime& packet_time) {
   ASSERT(socket == udp_socket_.get());
   for (std::deque<Port*>::iterator iter = ports.begin();
        iter != ports.end(); ++iter) {
     // We have only one port in the queue.
     // TODO(mallinath) - Add shared socket support to Relay and Turn ports.
-    if ((*iter)->HandleIncomingPacket(socket, data, size, remote_addr)) {
+    if ((*iter)->HandleIncomingPacket(
+        socket, data, size, remote_addr, packet_time)) {
       break;
     }
   }
diff --git a/session/media/channel.cc b/session/media/channel.cc
index 8579db2..9a8559a 100644
--- a/session/media/channel.cc
+++ b/session/media/channel.cc
@@ -610,7 +610,9 @@
 }
 
 void BaseChannel::OnChannelRead(TransportChannel* channel,
-                                const char* data, size_t len, int flags) {
+                                const char* data, size_t len,
+                                const talk_base::PacketTime& packet_time,
+                                int flags) {
   // OnChannelRead gets called from P2PSocket; now pass data to MediaEngine
   ASSERT(worker_thread_ == talk_base::Thread::Current());
 
@@ -618,7 +620,7 @@
   // transport. We feed RTP traffic into the demuxer to determine if it is RTCP.
   bool rtcp = PacketIsRtcp(channel, data, len);
   talk_base::Buffer packet(data, len);
-  HandlePacket(rtcp, &packet);
+  HandlePacket(rtcp, &packet, packet_time);
 }
 
 void BaseChannel::OnReadyToSend(TransportChannel* channel) {
@@ -774,7 +776,8 @@
   return true;
 }
 
-void BaseChannel::HandlePacket(bool rtcp, talk_base::Buffer* packet) {
+void BaseChannel::HandlePacket(bool rtcp, talk_base::Buffer* packet,
+                               const talk_base::PacketTime& packet_time) {
   if (!WantsPacket(rtcp, packet)) {
     return;
   }
@@ -843,9 +846,9 @@
 
   // Push it down to the media channel.
   if (!rtcp) {
-    media_channel_->OnPacketReceived(packet);
+    media_channel_->OnPacketReceived(packet, packet_time);
   } else {
-    media_channel_->OnRtcpReceived(packet);
+    media_channel_->OnRtcpReceived(packet, packet_time);
   }
 }
 
@@ -1645,8 +1648,10 @@
 }
 
 void VoiceChannel::OnChannelRead(TransportChannel* channel,
-                                 const char* data, size_t len, int flags) {
-  BaseChannel::OnChannelRead(channel, data, len, flags);
+                                 const char* data, size_t len,
+                                 const talk_base::PacketTime& packet_time,
+                                int flags) {
+  BaseChannel::OnChannelRead(channel, data, len, packet_time, flags);
 
   // Set a flag when we've received an RTP packet. If we're waiting for early
   // media, this will disable the timeout.
diff --git a/session/media/channel.h b/session/media/channel.h
index 27a81a6..d297ee4 100644
--- a/session/media/channel.h
+++ b/session/media/channel.h
@@ -265,8 +265,11 @@
 
   // From TransportChannel
   void OnWritableState(TransportChannel* channel);
-  virtual void OnChannelRead(TransportChannel* channel, const char* data,
-                             size_t len, int flags);
+  virtual void OnChannelRead(TransportChannel* channel,
+                             const char* data,
+                             size_t len,
+                             const talk_base::PacketTime& packet_time,
+                             int flags);
   void OnReadyToSend(TransportChannel* channel);
 
   bool PacketIsRtcp(const TransportChannel* channel, const char* data,
@@ -274,7 +277,8 @@
   bool SendPacket(bool rtcp, talk_base::Buffer* packet,
                   talk_base::DiffServCodePoint dscp);
   virtual bool WantsPacket(bool rtcp, talk_base::Buffer* packet);
-  void HandlePacket(bool rtcp, talk_base::Buffer* packet);
+  void HandlePacket(bool rtcp, talk_base::Buffer* packet,
+                    const talk_base::PacketTime& packet_time);
 
   // Apply the new local/remote session description.
   void OnNewLocalDescription(BaseSession* session, ContentAction action);
@@ -441,7 +445,9 @@
  private:
   // overrides from BaseChannel
   virtual void OnChannelRead(TransportChannel* channel,
-                             const char* data, size_t len, int flags);
+                             const char* data, size_t len,
+                             const talk_base::PacketTime& packet_time,
+                             int flags);
   virtual void ChangeState();
   virtual const ContentInfo* GetFirstContent(const SessionDescription* sdesc);
   virtual bool SetLocalContent_w(const MediaContentDescription* content,
diff --git a/session/media/channel_unittest.cc b/session/media/channel_unittest.cc
index 0273907..48a9bde 100644
--- a/session/media/channel_unittest.cc
+++ b/session/media/channel_unittest.cc
@@ -1775,7 +1775,7 @@
         channel2_->transport_channel();
     transport_channel->SignalReadPacket(
         transport_channel, reinterpret_cast<const char*>(kBadPacket),
-        sizeof(kBadPacket), 0);
+        sizeof(kBadPacket), talk_base::PacketTime(), 0);
     EXPECT_EQ_WAIT(T::MediaChannel::ERROR_PLAY_SRTP_ERROR, error_, 500);
   }
 
diff --git a/session/media/channelmanager.cc b/session/media/channelmanager.cc
index d4fcc79..4d5d8fc 100644
--- a/session/media/channelmanager.cc
+++ b/session/media/channelmanager.cc
@@ -947,4 +947,9 @@
   return true;
 }
 
+bool ChannelManager::StartAecDump(FILE* file) {
+  return worker_thread_->Invoke<bool>(
+      Bind(&MediaEngineInterface::StartAecDump, media_engine_.get(), file));
+}
+
 }  // namespace cricket
diff --git a/session/media/channelmanager.h b/session/media/channelmanager.h
index fdb8f73..f19d3d0 100644
--- a/session/media/channelmanager.h
+++ b/session/media/channelmanager.h
@@ -214,6 +214,9 @@
   void SetVideoCaptureDeviceMaxFormat(const std::string& usb_id,
                                       const VideoFormat& max_format);
 
+  // Starts AEC dump using existing file.
+  bool StartAecDump(FILE* file);
+
   sigslot::repeater0<> SignalDevicesChange;
   sigslot::signal2<VideoCapturer*, CaptureState> SignalVideoCaptureStateChange;
 
diff --git a/session/tunnel/pseudotcpchannel.cc b/session/tunnel/pseudotcpchannel.cc
index 92e9e0e..ee88797 100644
--- a/session/tunnel/pseudotcpchannel.cc
+++ b/session/tunnel/pseudotcpchannel.cc
@@ -340,7 +340,9 @@
 }
 
 void PseudoTcpChannel::OnChannelRead(TransportChannel* channel,
-                                     const char* data, size_t size, int flags) {
+                                     const char* data, size_t size,
+                                     const talk_base::PacketTime& packet_time,
+                                     int flags) {
   //LOG_F(LS_VERBOSE) << "(" << size << ")";
   ASSERT(worker_thread_->IsCurrent());
   CritScope lock(&cs_);
diff --git a/session/tunnel/pseudotcpchannel.h b/session/tunnel/pseudotcpchannel.h
index a540699..31cd9a1 100644
--- a/session/tunnel/pseudotcpchannel.h
+++ b/session/tunnel/pseudotcpchannel.h
@@ -111,7 +111,7 @@
   // Worker thread methods
   void OnChannelWritableState(TransportChannel* channel);
   void OnChannelRead(TransportChannel* channel, const char* data, size_t size,
-                     int flags);
+                     const talk_base::PacketTime& packet_time, int flags);
   void OnChannelConnectionChanged(TransportChannel* channel,
                                   const Candidate& candidate);