Fixed GetStats when local and remote track are using the same ssrc.

R=hta@chromium.org, kjellander@webrtc.org, tommi@webrtc.org

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

git-svn-id: http://webrtc.googlecode.com/svn/trunk@6414 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/talk/app/webrtc/mediastream_unittest.cc b/talk/app/webrtc/mediastream_unittest.cc
index bb2d50e..113242f 100644
--- a/talk/app/webrtc/mediastream_unittest.cc
+++ b/talk/app/webrtc/mediastream_unittest.cc
@@ -33,7 +33,8 @@
 #include "talk/base/refcount.h"
 #include "talk/base/scoped_ptr.h"
 #include "talk/base/gunit.h"
-#include "testing/base/public/gmock.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
 
 static const char kStreamLabel1[] = "local_stream_1";
 static const char kVideoTrackId[] = "dummy_video_cam_1";
diff --git a/talk/app/webrtc/mediastreamhandler_unittest.cc b/talk/app/webrtc/mediastreamhandler_unittest.cc
index 6eedb7e..9a53f35 100644
--- a/talk/app/webrtc/mediastreamhandler_unittest.cc
+++ b/talk/app/webrtc/mediastreamhandler_unittest.cc
@@ -38,7 +38,8 @@
 #include "talk/base/gunit.h"
 #include "talk/media/base/fakevideocapturer.h"
 #include "talk/media/base/mediachannel.h"
-#include "testing/base/public/gmock.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
 
 using ::testing::_;
 using ::testing::Exactly;
diff --git a/talk/app/webrtc/statscollector.cc b/talk/app/webrtc/statscollector.cc
index bc8b4f8..149ad5c 100644
--- a/talk/app/webrtc/statscollector.cc
+++ b/talk/app/webrtc/statscollector.cc
@@ -187,7 +187,6 @@
 
 const char StatsReport::kStatsReportVideoBweId[] = "bweforvideo";
 
-
 // Implementations of functions in statstypes.h
 void StatsReport::AddValue(const std::string& name, const std::string& value) {
   Value temp;
@@ -238,6 +237,20 @@
   return type + "_" + id;
 }
 
+std::string StatsId(const std::string& type, const std::string& id,
+                    StatsCollector::TrackDirection direction) {
+  ASSERT(direction == StatsCollector::kSending ||
+         direction == StatsCollector::kReceiving);
+
+  // Strings for the direction of the track.
+  const char kSendDirection[] = "send";
+  const char kRecvDirection[] = "recv";
+
+  const std::string direction_id = (direction == StatsCollector::kSending) ?
+      kSendDirection : kRecvDirection;
+  return type + "_" + id + "_" + direction_id;
+}
+
 bool ExtractValueFromReport(
     const StatsReport& report,
     const std::string& name,
@@ -476,20 +489,22 @@
 template<typename T>
 void ExtractStatsFromList(const std::vector<T>& data,
                           const std::string& transport_id,
-                          StatsCollector* collector) {
+                          StatsCollector* collector,
+                          StatsCollector::TrackDirection direction) {
   typename std::vector<T>::const_iterator it = data.begin();
   for (; it != data.end(); ++it) {
     std::string id;
     uint32 ssrc = it->ssrc();
-    // Each object can result in 2 objects, a local and a remote object.
+    // Each track can have stats for both local and remote objects.
     // TODO(hta): Handle the case of multiple SSRCs per object.
-    StatsReport* report = collector->PrepareLocalReport(ssrc, transport_id);
-    if (!report) {
-      continue;
-    }
-    ExtractStats(*it, report);
+    StatsReport* report = collector->PrepareLocalReport(ssrc, transport_id,
+                                                        direction);
+    if (report)
+      ExtractStats(*it, report);
+
     if (it->remote_stats.size() > 0) {
-      report = collector->PrepareRemoteReport(ssrc, transport_id);
+      report = collector->PrepareRemoteReport(ssrc, transport_id,
+                                              direction);
       if (!report) {
         continue;
       }
@@ -606,18 +621,16 @@
 
 StatsReport* StatsCollector::PrepareLocalReport(
     uint32 ssrc,
-    const std::string& transport_id) {
-  std::string ssrc_id = talk_base::ToString<uint32>(ssrc);
+    const std::string& transport_id,
+    TrackDirection direction) {
+  const std::string ssrc_id = talk_base::ToString<uint32>(ssrc);
   StatsMap::iterator it = reports_.find(StatsId(
-      StatsReport::kStatsReportTypeSsrc, ssrc_id));
+      StatsReport::kStatsReportTypeSsrc, ssrc_id, direction));
 
   std::string track_id;
   if (it == reports_.end()) {
-    if (!session()->GetTrackIdBySsrc(ssrc, &track_id)) {
-      LOG(LS_WARNING) << "The SSRC " << ssrc
-                      << " is not associated with a track";
+    if (!GetTrackIdBySsrc(ssrc, &track_id, direction))
       return NULL;
-    }
   } else {
     // Keeps the old track id since we want to report the stats for inactive
     // tracks.
@@ -627,7 +640,7 @@
   }
 
   StatsReport* report = GetOrCreateReport(StatsReport::kStatsReportTypeSsrc,
-                                          ssrc_id);
+                                          ssrc_id, direction);
 
   // Clear out stats from previous GatherStats calls if any.
   if (report->timestamp != stats_gathering_started_) {
@@ -645,18 +658,16 @@
 
 StatsReport* StatsCollector::PrepareRemoteReport(
     uint32 ssrc,
-    const std::string& transport_id) {
-  std::string ssrc_id = talk_base::ToString<uint32>(ssrc);
+    const std::string& transport_id,
+    TrackDirection direction) {
+  const std::string ssrc_id = talk_base::ToString<uint32>(ssrc);
   StatsMap::iterator it = reports_.find(StatsId(
-      StatsReport::kStatsReportTypeRemoteSsrc, ssrc_id));
+      StatsReport::kStatsReportTypeRemoteSsrc, ssrc_id, direction));
 
   std::string track_id;
   if (it == reports_.end()) {
-    if (!session()->GetTrackIdBySsrc(ssrc, &track_id)) {
-      LOG(LS_WARNING) << "The SSRC " << ssrc
-                      << " is not associated with a track";
+    if (!GetTrackIdBySsrc(ssrc, &track_id, direction))
       return NULL;
-    }
   } else {
     // Keeps the old track id since we want to report the stats for inactive
     // tracks.
@@ -666,7 +677,7 @@
   }
 
   StatsReport* report = GetOrCreateReport(
-      StatsReport::kStatsReportTypeRemoteSsrc, ssrc_id);
+      StatsReport::kStatsReportTypeRemoteSsrc, ssrc_id, direction);
 
   // Clear out stats from previous GatherStats calls if any.
   // The timestamp will be added later. Zero it for debugging.
@@ -865,8 +876,8 @@
                   << session_->voice_channel()->content_name();
     return;
   }
-  ExtractStatsFromList(voice_info.receivers, transport_id, this);
-  ExtractStatsFromList(voice_info.senders, transport_id, this);
+  ExtractStatsFromList(voice_info.receivers, transport_id, this, kReceiving);
+  ExtractStatsFromList(voice_info.senders, transport_id, this, kSending);
 
   UpdateStatsFromExistingLocalAudioTracks();
 }
@@ -892,8 +903,8 @@
                   << session_->video_channel()->content_name();
     return;
   }
-  ExtractStatsFromList(video_info.receivers, transport_id, this);
-  ExtractStatsFromList(video_info.senders, transport_id, this);
+  ExtractStatsFromList(video_info.receivers, transport_id, this, kReceiving);
+  ExtractStatsFromList(video_info.senders, transport_id, this, kSending);
   if (video_info.bw_estimations.size() != 1) {
     LOG(LS_ERROR) << "BWEs count: " << video_info.bw_estimations.size();
   } else {
@@ -926,8 +937,11 @@
 }
 
 StatsReport* StatsCollector::GetReport(const std::string& type,
-                                       const std::string& id) {
-  std::string statsid = StatsId(type, id);
+                                       const std::string& id,
+                                       TrackDirection direction) {
+  ASSERT(type == StatsReport::kStatsReportTypeSsrc ||
+         type == StatsReport::kStatsReportTypeRemoteSsrc);
+  std::string statsid = StatsId(type, id, direction);
   StatsReport* report = NULL;
   std::map<std::string, StatsReport>::iterator it = reports_.find(statsid);
   if (it != reports_.end())
@@ -937,10 +951,13 @@
 }
 
 StatsReport* StatsCollector::GetOrCreateReport(const std::string& type,
-                                               const std::string& id) {
-  StatsReport* report = GetReport(type, id);
+                                               const std::string& id,
+                                               TrackDirection direction) {
+  ASSERT(type == StatsReport::kStatsReportTypeSsrc ||
+         type == StatsReport::kStatsReportTypeRemoteSsrc);
+  StatsReport* report = GetReport(type, id, direction);
   if (report == NULL) {
-    std::string statsid = StatsId(type, id);
+    std::string statsid = StatsId(type, id, direction);
     report = &reports_[statsid];  // Create new element.
     report->id = statsid;
     report->type = type;
@@ -957,7 +974,8 @@
     uint32 ssrc = it->second;
     std::string ssrc_id = talk_base::ToString<uint32>(ssrc);
     StatsReport* report = GetReport(StatsReport::kStatsReportTypeSsrc,
-                                    ssrc_id);
+                                    ssrc_id,
+                                    kSending);
     if (report == NULL) {
       // This can happen if a local audio track is added to a stream on the
       // fly and the report has not been set up yet. Do nothing in this case.
@@ -1012,4 +1030,24 @@
                        talk_base::ToString<int>(stats.echo_delay_std_ms));
 }
 
+bool StatsCollector::GetTrackIdBySsrc(uint32 ssrc, std::string* track_id,
+                                      TrackDirection direction) {
+  if (direction == kSending) {
+    if (!session()->GetLocalTrackIdBySsrc(ssrc, track_id)) {
+      LOG(LS_WARNING) << "The SSRC " << ssrc
+                      << " is not associated with a sending track";
+      return false;
+    }
+  } else {
+    ASSERT(direction == kReceiving);
+    if (!session()->GetRemoteTrackIdBySsrc(ssrc, track_id)) {
+      LOG(LS_WARNING) << "The SSRC " << ssrc
+                      << " is not associated with a receiving track";
+      return false;
+    }
+  }
+
+  return true;
+}
+
 }  // namespace webrtc
diff --git a/talk/app/webrtc/statscollector.h b/talk/app/webrtc/statscollector.h
index fdb2961..77a1ba0 100644
--- a/talk/app/webrtc/statscollector.h
+++ b/talk/app/webrtc/statscollector.h
@@ -46,6 +46,11 @@
 
 class StatsCollector {
  public:
+  enum TrackDirection {
+    kSending = 0,
+    kReceiving,
+  };
+
   StatsCollector();
 
   // Register the session Stats should operate on.
@@ -77,9 +82,11 @@
 
   // Prepare an SSRC report for the given ssrc. Used internally
   // in the ExtractStatsFromList template.
-  StatsReport* PrepareLocalReport(uint32 ssrc, const std::string& transport);
+  StatsReport* PrepareLocalReport(uint32 ssrc, const std::string& transport,
+                                  TrackDirection direction);
   // Prepare an SSRC report for the given remote ssrc. Used internally.
-  StatsReport* PrepareRemoteReport(uint32 ssrc, const std::string& transport);
+  StatsReport* PrepareRemoteReport(uint32 ssrc, const std::string& transport,
+                                   TrackDirection direction);
   // Extracts the ID of a Transport belonging to an SSRC. Used internally.
   bool GetTransportIdFromProxy(const std::string& proxy,
                                std::string* transport_id);
@@ -102,15 +109,22 @@
   void BuildSsrcToTransportId();
   WebRtcSession* session() { return session_; }
   webrtc::StatsReport* GetOrCreateReport(const std::string& type,
-                                         const std::string& id);
+                                         const std::string& id,
+                                         TrackDirection direction);
   webrtc::StatsReport* GetReport(const std::string& type,
-                                 const std::string& id);
+                                 const std::string& id,
+                                 TrackDirection direction);
 
   // Helper method to get stats from the local audio tracks.
   void UpdateStatsFromExistingLocalAudioTracks();
   void UpdateReportFromAudioTrack(AudioTrackInterface* track,
                                   StatsReport* report);
 
+  // Helper method to get the id for the track identified by ssrc.
+  // |direction| tells if the track is for sending or receiving.
+  bool GetTrackIdBySsrc(uint32 ssrc, std::string* track_id,
+                        TrackDirection direction);
+
   // A map from the report id to the report.
   std::map<std::string, StatsReport> reports_;
   // Raw pointer to the session the statistics are gathered from.
diff --git a/talk/app/webrtc/statscollector_unittest.cc b/talk/app/webrtc/statscollector_unittest.cc
index 3003d1b..5cdc81e 100644
--- a/talk/app/webrtc/statscollector_unittest.cc
+++ b/talk/app/webrtc/statscollector_unittest.cc
@@ -1,5 +1,6 @@
 /*
  * libjingle
+ * Copyright 2014, Google Inc.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
@@ -39,7 +40,8 @@
 #include "talk/media/devices/fakedevicemanager.h"
 #include "talk/p2p/base/fakesession.h"
 #include "talk/session/media/channelmanager.h"
-#include "testing/base/public/gmock.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
 
 using cricket::StatsOptions;
 using testing::_;
@@ -66,8 +68,8 @@
 const char kNoReports[] = "NO REPORTS";
 
 // Constant names for track identification.
-const char kTrackId[] = "somename";
-const char kAudioTrackId[] = "audio_track_id";
+const char kLocalTrackId[] = "local_track_id";
+const char kRemoteTrackId[] = "remote_track_id";
 const uint32 kSsrcOfTrack = 1234;
 
 class MockWebRtcSession : public webrtc::WebRtcSession {
@@ -78,7 +80,10 @@
   }
   MOCK_METHOD0(voice_channel, cricket::VoiceChannel*());
   MOCK_METHOD0(video_channel, cricket::VideoChannel*());
-  MOCK_METHOD2(GetTrackIdBySsrc, bool(uint32, std::string*));
+  // Libjingle uses "local" for a outgoing track, and "remote" for a incoming
+  // track.
+  MOCK_METHOD2(GetLocalTrackIdBySsrc, bool(uint32, std::string*));
+  MOCK_METHOD2(GetRemoteTrackIdBySsrc, bool(uint32, std::string*));
   MOCK_METHOD1(GetStats, bool(cricket::SessionStats*));
   MOCK_METHOD1(GetTransport, cricket::Transport*(const std::string&));
 };
@@ -116,10 +121,10 @@
   }
 };
 
-class FakeLocalAudioTrack
+class FakeAudioTrack
     : public webrtc::MediaStreamTrack<webrtc::AudioTrackInterface> {
  public:
-  explicit FakeLocalAudioTrack(const std::string& id)
+  explicit FakeAudioTrack(const std::string& id)
       : webrtc::MediaStreamTrack<webrtc::AudioTrackInterface>(id),
         processor_(new talk_base::RefCountedObject<FakeAudioProcessor>()) {}
   std::string kind() const OVERRIDE {
@@ -263,55 +268,56 @@
   EXPECT_EQ(ders.size(), i);
 }
 
-void VerifyVoiceReceiverInfoReport(const StatsReport* report,
-                                   const cricket::VoiceReceiverInfo& sinfo) {
+void VerifyVoiceReceiverInfoReport(
+    const StatsReport* report,
+    const cricket::VoiceReceiverInfo& info) {
   std::string value_in_report;
   EXPECT_TRUE(GetValue(
       report, StatsReport::kStatsValueNameAudioOutputLevel, &value_in_report));
-  EXPECT_EQ(talk_base::ToString<int>(sinfo.audio_level), value_in_report);
+  EXPECT_EQ(talk_base::ToString<int>(info.audio_level), value_in_report);
   EXPECT_TRUE(GetValue(
       report, StatsReport::kStatsValueNameBytesReceived, &value_in_report));
-  EXPECT_EQ(talk_base::ToString<int64>(sinfo.bytes_rcvd), value_in_report);
+  EXPECT_EQ(talk_base::ToString<int64>(info.bytes_rcvd), value_in_report);
   EXPECT_TRUE(GetValue(
       report, StatsReport::kStatsValueNameJitterReceived, &value_in_report));
-  EXPECT_EQ(talk_base::ToString<int>(sinfo.jitter_ms), value_in_report);
+  EXPECT_EQ(talk_base::ToString<int>(info.jitter_ms), value_in_report);
   EXPECT_TRUE(GetValue(
       report, StatsReport::kStatsValueNameJitterBufferMs, &value_in_report));
-  EXPECT_EQ(talk_base::ToString<int>(sinfo.jitter_buffer_ms), value_in_report);
+  EXPECT_EQ(talk_base::ToString<int>(info.jitter_buffer_ms), value_in_report);
   EXPECT_TRUE(GetValue(
       report, StatsReport::kStatsValueNamePreferredJitterBufferMs,
       &value_in_report));
-  EXPECT_EQ(talk_base::ToString<int>(sinfo.jitter_buffer_preferred_ms),
+  EXPECT_EQ(talk_base::ToString<int>(info.jitter_buffer_preferred_ms),
       value_in_report);
   EXPECT_TRUE(GetValue(
       report, StatsReport::kStatsValueNameCurrentDelayMs, &value_in_report));
-  EXPECT_EQ(talk_base::ToString<int>(sinfo.delay_estimate_ms), value_in_report);
+  EXPECT_EQ(talk_base::ToString<int>(info.delay_estimate_ms), value_in_report);
   EXPECT_TRUE(GetValue(
       report, StatsReport::kStatsValueNameExpandRate, &value_in_report));
-  EXPECT_EQ(talk_base::ToString<float>(sinfo.expand_rate), value_in_report);
+  EXPECT_EQ(talk_base::ToString<float>(info.expand_rate), value_in_report);
   EXPECT_TRUE(GetValue(
       report, StatsReport::kStatsValueNamePacketsReceived, &value_in_report));
-  EXPECT_EQ(talk_base::ToString<int>(sinfo.packets_rcvd), value_in_report);
+  EXPECT_EQ(talk_base::ToString<int>(info.packets_rcvd), value_in_report);
   EXPECT_TRUE(GetValue(
       report, StatsReport::kStatsValueNameDecodingCTSG, &value_in_report));
-  EXPECT_EQ(talk_base::ToString<int>(sinfo.decoding_calls_to_silence_generator),
+  EXPECT_EQ(talk_base::ToString<int>(info.decoding_calls_to_silence_generator),
       value_in_report);
   EXPECT_TRUE(GetValue(
       report, StatsReport::kStatsValueNameDecodingCTN, &value_in_report));
-  EXPECT_EQ(talk_base::ToString<int>(sinfo.decoding_calls_to_neteq),
+  EXPECT_EQ(talk_base::ToString<int>(info.decoding_calls_to_neteq),
       value_in_report);
   EXPECT_TRUE(GetValue(
       report, StatsReport::kStatsValueNameDecodingNormal, &value_in_report));
-  EXPECT_EQ(talk_base::ToString<int>(sinfo.decoding_normal), value_in_report);
+  EXPECT_EQ(talk_base::ToString<int>(info.decoding_normal), value_in_report);
   EXPECT_TRUE(GetValue(
       report, StatsReport::kStatsValueNameDecodingPLC, &value_in_report));
-  EXPECT_EQ(talk_base::ToString<int>(sinfo.decoding_plc), value_in_report);
+  EXPECT_EQ(talk_base::ToString<int>(info.decoding_plc), value_in_report);
   EXPECT_TRUE(GetValue(
       report, StatsReport::kStatsValueNameDecodingCNG, &value_in_report));
-  EXPECT_EQ(talk_base::ToString<int>(sinfo.decoding_cng), value_in_report);
+  EXPECT_EQ(talk_base::ToString<int>(info.decoding_cng), value_in_report);
   EXPECT_TRUE(GetValue(
       report, StatsReport::kStatsValueNameDecodingPLCCNG, &value_in_report));
-  EXPECT_EQ(talk_base::ToString<int>(sinfo.decoding_plc_cng), value_in_report);
+  EXPECT_EQ(talk_base::ToString<int>(info.decoding_plc_cng), value_in_report);
 }
 
 
@@ -369,6 +375,59 @@
   EXPECT_EQ(typing_detected, value_in_report);
 }
 
+// Helper methods to avoid duplication of code.
+void InitVoiceSenderInfo(cricket::VoiceSenderInfo* voice_sender_info) {
+  voice_sender_info->add_ssrc(kSsrcOfTrack);
+  voice_sender_info->codec_name = "fake_codec";
+  voice_sender_info->bytes_sent = 100;
+  voice_sender_info->packets_sent = 101;
+  voice_sender_info->rtt_ms = 102;
+  voice_sender_info->fraction_lost = 103;
+  voice_sender_info->jitter_ms = 104;
+  voice_sender_info->packets_lost = 105;
+  voice_sender_info->ext_seqnum = 106;
+  voice_sender_info->audio_level = 107;
+  voice_sender_info->echo_return_loss = 108;
+  voice_sender_info->echo_return_loss_enhancement = 109;
+  voice_sender_info->echo_delay_median_ms = 110;
+  voice_sender_info->echo_delay_std_ms = 111;
+  voice_sender_info->aec_quality_min = 112.0f;
+  voice_sender_info->typing_noise_detected = false;
+}
+
+void UpdateVoiceSenderInfoFromAudioTrack(
+    FakeAudioTrack* audio_track, cricket::VoiceSenderInfo* voice_sender_info) {
+  audio_track->GetSignalLevel(&voice_sender_info->audio_level);
+  webrtc::AudioProcessorInterface::AudioProcessorStats audio_processor_stats;
+  audio_track->GetAudioProcessor()->GetStats(&audio_processor_stats);
+  voice_sender_info->typing_noise_detected =
+      audio_processor_stats.typing_noise_detected;
+  voice_sender_info->echo_return_loss = audio_processor_stats.echo_return_loss;
+  voice_sender_info->echo_return_loss_enhancement =
+      audio_processor_stats.echo_return_loss_enhancement;
+  voice_sender_info->echo_delay_median_ms =
+      audio_processor_stats.echo_delay_median_ms;
+  voice_sender_info->aec_quality_min = audio_processor_stats.aec_quality_min;
+  voice_sender_info->echo_delay_std_ms =
+      audio_processor_stats.echo_delay_std_ms;
+}
+
+void InitVoiceReceiverInfo(cricket::VoiceReceiverInfo* voice_receiver_info) {
+  voice_receiver_info->add_ssrc(kSsrcOfTrack);
+  voice_receiver_info->bytes_rcvd = 110;
+  voice_receiver_info->packets_rcvd = 111;
+  voice_receiver_info->packets_lost = 112;
+  voice_receiver_info->fraction_lost = 113;
+  voice_receiver_info->packets_lost = 114;
+  voice_receiver_info->ext_seqnum = 115;
+  voice_receiver_info->jitter_ms = 116;
+  voice_receiver_info->jitter_buffer_ms = 117;
+  voice_receiver_info->jitter_buffer_preferred_ms = 118;
+  voice_receiver_info->delay_estimate_ms = 119;
+  voice_receiver_info->audio_level = 120;
+  voice_receiver_info->expand_rate = 121;
+}
+
 class StatsCollectorTest : public testing::Test {
  protected:
   StatsCollectorTest()
@@ -377,8 +436,7 @@
           new cricket::ChannelManager(media_engine_,
                                       new cricket::FakeDeviceManager(),
                                       talk_base::Thread::Current())),
-      session_(channel_manager_.get()),
-      track_id_(kTrackId) {
+      session_(channel_manager_.get()) {
     // By default, we ignore session GetStats calls.
     EXPECT_CALL(session_, GetStats(_)).WillRepeatedly(Return(false));
   }
@@ -398,27 +456,56 @@
     session_stats_.proxy_to_transport[vc_name] = kTransportName;
   }
 
-  // Adds a track with a given SSRC into the stats.
-  void AddVideoTrackStats() {
+  // Adds a outgoing video track with a given SSRC into the stats.
+  void AddOutgoingVideoTrackStats() {
     stream_ = webrtc::MediaStream::Create("streamlabel");
-    track_= webrtc::VideoTrack::Create(kTrackId, NULL);
+    track_= webrtc::VideoTrack::Create(kLocalTrackId, NULL);
     stream_->AddTrack(track_);
-    EXPECT_CALL(session_, GetTrackIdBySsrc(kSsrcOfTrack, _))
-      .WillRepeatedly(DoAll(SetArgPointee<1>(track_id_),
-                            Return(true)));
+    EXPECT_CALL(session_, GetLocalTrackIdBySsrc(kSsrcOfTrack, _))
+        .WillRepeatedly(DoAll(SetArgPointee<1>(kLocalTrackId), Return(true)));
+    EXPECT_CALL(session_, GetRemoteTrackIdBySsrc(kSsrcOfTrack, _))
+        .WillRepeatedly(Return(false));
   }
 
-  // Adds a local audio track with a given SSRC into the stats.
-  void AddLocalAudioTrackStats() {
+  // Adds a incoming video track with a given SSRC into the stats.
+  void AddIncomingVideoTrackStats() {
+    stream_ = webrtc::MediaStream::Create("streamlabel");
+    track_= webrtc::VideoTrack::Create(kRemoteTrackId, NULL);
+    stream_->AddTrack(track_);
+    EXPECT_CALL(session_, GetLocalTrackIdBySsrc(kSsrcOfTrack, _))
+        .WillRepeatedly(Return(false));
+    EXPECT_CALL(session_, GetRemoteTrackIdBySsrc(kSsrcOfTrack, _))
+        .WillRepeatedly(DoAll(SetArgPointee<1>(kRemoteTrackId),
+                              Return(true)));
+    }
+
+  // Adds a outgoing audio track with a given SSRC into the stats.
+  void AddOutgoingAudioTrackStats() {
     if (stream_ == NULL)
       stream_ = webrtc::MediaStream::Create("streamlabel");
 
-    audio_track_ =
-        new talk_base::RefCountedObject<FakeLocalAudioTrack>(kAudioTrackId);
+    audio_track_ = new talk_base::RefCountedObject<FakeAudioTrack>(
+        kLocalTrackId);
     stream_->AddTrack(audio_track_);
-    EXPECT_CALL(session_, GetTrackIdBySsrc(kSsrcOfTrack, _))
-        .WillRepeatedly(DoAll(SetArgPointee<1>(kAudioTrackId),
+    EXPECT_CALL(session_, GetLocalTrackIdBySsrc(kSsrcOfTrack, _))
+        .WillRepeatedly(DoAll(SetArgPointee<1>(kLocalTrackId),
                               Return(true)));
+    EXPECT_CALL(session_, GetRemoteTrackIdBySsrc(kSsrcOfTrack, _))
+        .WillRepeatedly(Return(false));
+  }
+
+  // Adds a incoming audio track with a given SSRC into the stats.
+  void AddIncomingAudioTrackStats() {
+    if (stream_ == NULL)
+      stream_ = webrtc::MediaStream::Create("streamlabel");
+
+    audio_track_ = new talk_base::RefCountedObject<FakeAudioTrack>(
+        kRemoteTrackId);
+    stream_->AddTrack(audio_track_);
+    EXPECT_CALL(session_, GetLocalTrackIdBySsrc(kSsrcOfTrack, _))
+        .WillRepeatedly(Return(false));
+    EXPECT_CALL(session_, GetRemoteTrackIdBySsrc(kSsrcOfTrack, _))
+        .WillRepeatedly(DoAll(SetArgPointee<1>(kRemoteTrackId), Return(true)));
   }
 
   void TestCertificateReports(const talk_base::FakeSSLCertificate& local_cert,
@@ -507,14 +594,13 @@
   cricket::SessionStats session_stats_;
   talk_base::scoped_refptr<webrtc::MediaStream> stream_;
   talk_base::scoped_refptr<webrtc::VideoTrack> track_;
-  talk_base::scoped_refptr<FakeLocalAudioTrack> audio_track_;
-  std::string track_id_;
+  talk_base::scoped_refptr<FakeAudioTrack> audio_track_;
 };
 
 // This test verifies that 64-bit counters are passed successfully.
 TEST_F(StatsCollectorTest, BytesCounterHandles64Bits) {
   webrtc::StatsCollector stats;  // Implementation under test.
-  MockVideoMediaChannel* media_channel = new MockVideoMediaChannel;
+  MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
   cricket::VideoChannel video_channel(talk_base::Thread::Current(),
       media_engine_, media_channel, &session_, "", false, NULL);
   StatsReports reports;  // returned values.
@@ -525,7 +611,7 @@
   const std::string kBytesSentString("12345678901234");
 
   stats.set_session(&session_);
-  AddVideoTrackStats();
+  AddOutgoingVideoTrackStats();
   stats.AddStream(stream_);
 
   // Construct a stats value to read.
@@ -547,7 +633,7 @@
 // Test that BWE information is reported via stats.
 TEST_F(StatsCollectorTest, BandwidthEstimationInfoIsReported) {
   webrtc::StatsCollector stats;  // Implementation under test.
-  MockVideoMediaChannel* media_channel = new MockVideoMediaChannel;
+  MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
   cricket::VideoChannel video_channel(talk_base::Thread::Current(),
       media_engine_, media_channel, &session_, "", false, NULL);
   StatsReports reports;  // returned values.
@@ -559,7 +645,7 @@
   const std::string kBytesSentString("12345678901234");
 
   stats.set_session(&session_);
-  AddVideoTrackStats();
+  AddOutgoingVideoTrackStats();
   stats.AddStream(stream_);
 
   // Construct a stats value to read.
@@ -624,10 +710,10 @@
 // without calling StatsCollector::UpdateStats.
 TEST_F(StatsCollectorTest, TrackObjectExistsWithoutUpdateStats) {
   webrtc::StatsCollector stats;  // Implementation under test.
-  MockVideoMediaChannel* media_channel = new MockVideoMediaChannel;
+  MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
   cricket::VideoChannel video_channel(talk_base::Thread::Current(),
       media_engine_, media_channel, &session_, "", false, NULL);
-  AddVideoTrackStats();
+  AddOutgoingVideoTrackStats();
   stats.AddStream(stream_);
 
   stats.set_session(&session_);
@@ -644,17 +730,17 @@
       ExtractStatsValue(StatsReport::kStatsReportTypeTrack,
                         reports,
                         StatsReport::kStatsValueNameTrackId);
-  EXPECT_EQ(kTrackId, trackValue);
+  EXPECT_EQ(kLocalTrackId, trackValue);
 }
 
 // This test verifies that the empty track report exists in the returned stats
 // when StatsCollector::UpdateStats is called with ssrc stats.
 TEST_F(StatsCollectorTest, TrackAndSsrcObjectExistAfterUpdateSsrcStats) {
   webrtc::StatsCollector stats;  // Implementation under test.
-  MockVideoMediaChannel* media_channel = new MockVideoMediaChannel;
+  MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
   cricket::VideoChannel video_channel(talk_base::Thread::Current(),
       media_engine_, media_channel, &session_, "", false, NULL);
-  AddVideoTrackStats();
+  AddOutgoingVideoTrackStats();
   stats.AddStream(stream_);
 
   stats.set_session(&session_);
@@ -684,15 +770,16 @@
   EXPECT_LE((size_t)3, reports.size());
   const StatsReport* track_report = FindNthReportByType(
       reports, StatsReport::kStatsReportTypeTrack, 1);
-  EXPECT_FALSE(track_report == NULL);
+  EXPECT_TRUE(track_report);
 
+  // Get report for the specific |track|.
   stats.GetStats(track_, &reports);
   // |reports| should contain at least one session report, one track report,
   // and one ssrc report.
   EXPECT_LE((size_t)3, reports.size());
   track_report = FindNthReportByType(
       reports, StatsReport::kStatsReportTypeTrack, 1);
-  EXPECT_FALSE(track_report == NULL);
+  EXPECT_TRUE(track_report);
 
   std::string ssrc_id = ExtractSsrcStatsValue(
       reports, StatsReport::kStatsValueNameSsrc);
@@ -700,19 +787,19 @@
 
   std::string track_id = ExtractSsrcStatsValue(
       reports, StatsReport::kStatsValueNameTrackId);
-  EXPECT_EQ(kTrackId, track_id);
+  EXPECT_EQ(kLocalTrackId, track_id);
 }
 
 // This test verifies that an SSRC object has the identifier of a Transport
 // stats object, and that this transport stats object exists in stats.
 TEST_F(StatsCollectorTest, TransportObjectLinkedFromSsrcObject) {
   webrtc::StatsCollector stats;  // Implementation under test.
-  MockVideoMediaChannel* media_channel = new MockVideoMediaChannel;
+  MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
   // The content_name known by the video channel.
   const std::string kVcName("vcname");
   cricket::VideoChannel video_channel(talk_base::Thread::Current(),
       media_engine_, media_channel, &session_, kVcName, false, NULL);
-  AddVideoTrackStats();
+  AddOutgoingVideoTrackStats();
   stats.AddStream(stream_);
 
   stats.set_session(&session_);
@@ -756,12 +843,12 @@
 // an outgoing SSRC where remote stats are not returned.
 TEST_F(StatsCollectorTest, RemoteSsrcInfoIsAbsent) {
   webrtc::StatsCollector stats;  // Implementation under test.
-  MockVideoMediaChannel* media_channel = new MockVideoMediaChannel;
+  MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
   // The content_name known by the video channel.
   const std::string kVcName("vcname");
   cricket::VideoChannel video_channel(talk_base::Thread::Current(),
       media_engine_, media_channel, &session_, kVcName, false, NULL);
-  AddVideoTrackStats();
+  AddOutgoingVideoTrackStats();
   stats.AddStream(stream_);
 
   stats.set_session(&session_);
@@ -781,12 +868,12 @@
 // an outgoing SSRC where stats are returned.
 TEST_F(StatsCollectorTest, RemoteSsrcInfoIsPresent) {
   webrtc::StatsCollector stats;  // Implementation under test.
-  MockVideoMediaChannel* media_channel = new MockVideoMediaChannel;
+  MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
   // The content_name known by the video channel.
   const std::string kVcName("vcname");
   cricket::VideoChannel video_channel(talk_base::Thread::Current(),
       media_engine_, media_channel, &session_, kVcName, false, NULL);
-  AddVideoTrackStats();
+  AddOutgoingVideoTrackStats();
   stats.AddStream(stream_);
 
   stats.set_session(&session_);
@@ -818,12 +905,61 @@
 
   stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard);
   stats.GetStats(NULL, &reports);
+
   const StatsReport* remote_report = FindNthReportByType(reports,
       StatsReport::kStatsReportTypeRemoteSsrc, 1);
   EXPECT_FALSE(remote_report == NULL);
   EXPECT_NE(0, remote_report->timestamp);
 }
 
+// This test verifies that the empty track report exists in the returned stats
+// when StatsCollector::UpdateStats is called with ssrc stats.
+TEST_F(StatsCollectorTest, ReportsFromRemoteTrack) {
+  webrtc::StatsCollector stats;  // Implementation under test.
+  MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
+  cricket::VideoChannel video_channel(talk_base::Thread::Current(),
+      media_engine_, media_channel, &session_, "", false, NULL);
+  AddIncomingVideoTrackStats();
+  stats.AddStream(stream_);
+
+  stats.set_session(&session_);
+
+  StatsReports reports;
+
+  // Constructs an ssrc stats update.
+  cricket::VideoReceiverInfo video_receiver_info;
+  cricket::VideoMediaInfo stats_read;
+  const int64 kNumOfPacketsConcealed = 54321;
+
+  // Construct a stats value to read.
+  video_receiver_info.add_ssrc(1234);
+  video_receiver_info.packets_concealed = kNumOfPacketsConcealed;
+  stats_read.receivers.push_back(video_receiver_info);
+
+  EXPECT_CALL(session_, video_channel()).WillRepeatedly(Return(&video_channel));
+  EXPECT_CALL(session_, voice_channel()).WillRepeatedly(ReturnNull());
+  EXPECT_CALL(*media_channel, GetStats(_, _))
+      .WillOnce(DoAll(SetArgPointee<1>(stats_read),
+                      Return(true)));
+
+  stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard);
+  stats.GetStats(NULL, &reports);
+  // |reports| should contain at least one session report, one track report,
+  // and one ssrc report.
+  EXPECT_LE(static_cast<size_t>(3), reports.size());
+  const StatsReport* track_report = FindNthReportByType(
+      reports, StatsReport::kStatsReportTypeTrack, 1);
+  EXPECT_TRUE(track_report);
+
+  std::string ssrc_id = ExtractSsrcStatsValue(
+      reports, StatsReport::kStatsValueNameSsrc);
+  EXPECT_EQ(talk_base::ToString<uint32>(kSsrcOfTrack), ssrc_id);
+
+  std::string track_id = ExtractSsrcStatsValue(
+      reports, StatsReport::kStatsValueNameTrackId);
+  EXPECT_EQ(kRemoteTrackId, track_id);
+}
+
 // This test verifies that all chained certificates are correctly
 // reported
 TEST_F(StatsCollectorTest, ChainedCertificateReportsCreated) {
@@ -982,7 +1118,7 @@
 // verbose output level.
 TEST_F(StatsCollectorTest, StatsOutputLevelVerbose) {
   webrtc::StatsCollector stats;  // Implementation under test.
-  MockVideoMediaChannel* media_channel = new MockVideoMediaChannel;
+  MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
   cricket::VideoChannel video_channel(talk_base::Thread::Current(),
       media_engine_, media_channel, &session_, "", false, NULL);
   stats.set_session(&session_);
@@ -1031,7 +1167,7 @@
   const std::string kVcName("vcname");
   cricket::VoiceChannel voice_channel(talk_base::Thread::Current(),
       media_engine_, media_channel, &session_, kVcName, false);
-  AddLocalAudioTrackStats();
+  AddOutgoingAudioTrackStats();
   stats.AddStream(stream_);
   stats.AddLocalAudioTrack(audio_track_.get(), kSsrcOfTrack);
 
@@ -1044,25 +1180,7 @@
                             Return(true)));
 
   cricket::VoiceSenderInfo voice_sender_info;
-  // Contents won't be modified by the AudioTrackInterface::GetStats().
-  voice_sender_info.add_ssrc(kSsrcOfTrack);
-  voice_sender_info.codec_name = "fake_codec";
-  voice_sender_info.bytes_sent = 100;
-  voice_sender_info.packets_sent = 101;
-  voice_sender_info.rtt_ms = 102;
-  voice_sender_info.fraction_lost = 103;
-  voice_sender_info.jitter_ms = 104;
-  voice_sender_info.packets_lost = 105;
-  voice_sender_info.ext_seqnum = 106;
-
-  // Contents will be modified by the AudioTrackInterface::GetStats().
-  voice_sender_info.audio_level = 107;
-  voice_sender_info.echo_return_loss = 108;;
-  voice_sender_info.echo_return_loss_enhancement = 109;
-  voice_sender_info.echo_delay_median_ms = 110;
-  voice_sender_info.echo_delay_std_ms = 111;
-  voice_sender_info.aec_quality_min = 112.0f;
-  voice_sender_info.typing_noise_detected = false;
+  InitVoiceSenderInfo(&voice_sender_info);
 
   // Constructs an ssrc stats update.
   cricket::VoiceMediaInfo stats_read;
@@ -1084,24 +1202,13 @@
   EXPECT_FALSE(report == NULL);
   std::string track_id = ExtractSsrcStatsValue(
       reports, StatsReport::kStatsValueNameTrackId);
-  EXPECT_EQ(kAudioTrackId, track_id);
+  EXPECT_EQ(kLocalTrackId, track_id);
   std::string ssrc_id = ExtractSsrcStatsValue(
       reports, StatsReport::kStatsValueNameSsrc);
   EXPECT_EQ(talk_base::ToString<uint32>(kSsrcOfTrack), ssrc_id);
 
   // Verifies the values in the track report.
-  audio_track_->GetSignalLevel(&voice_sender_info.audio_level);
-  webrtc::AudioProcessorInterface::AudioProcessorStats audio_processor_stats;
-  audio_track_->GetAudioProcessor()->GetStats(&audio_processor_stats);
-  voice_sender_info.typing_noise_detected =
-      audio_processor_stats.typing_noise_detected;
-  voice_sender_info.echo_return_loss = audio_processor_stats.echo_return_loss;
-  voice_sender_info.echo_return_loss_enhancement =
-      audio_processor_stats.echo_return_loss_enhancement;
-  voice_sender_info.echo_delay_median_ms =
-      audio_processor_stats.echo_delay_median_ms;
-  voice_sender_info.aec_quality_min = audio_processor_stats.aec_quality_min;
-  voice_sender_info.echo_delay_std_ms = audio_processor_stats.echo_delay_std_ms;
+  UpdateVoiceSenderInfoFromAudioTrack(audio_track_.get(), &voice_sender_info);
   VerifyVoiceSenderInfoReport(report, voice_sender_info);
 
   // Verify we get the same result by passing a track to GetStats().
@@ -1109,16 +1216,21 @@
   stats.GetStats(audio_track_.get(), &track_reports);
   const StatsReport* track_report = FindNthReportByType(
       track_reports, StatsReport::kStatsReportTypeSsrc, 1);
-  EXPECT_FALSE(track_report == NULL);
+  EXPECT_TRUE(track_report);
   track_id = ExtractSsrcStatsValue(track_reports,
                                    StatsReport::kStatsValueNameTrackId);
-  EXPECT_EQ(kAudioTrackId, track_id);
+  EXPECT_EQ(kLocalTrackId, track_id);
   ssrc_id = ExtractSsrcStatsValue(track_reports,
                                   StatsReport::kStatsValueNameSsrc);
   EXPECT_EQ(talk_base::ToString<uint32>(kSsrcOfTrack), ssrc_id);
   VerifyVoiceSenderInfoReport(track_report, voice_sender_info);
-}
 
+  // Verify that there is no remote report for the local audio track because
+  // we did not set it up.
+  const StatsReport* remote_report = FindNthReportByType(reports,
+      StatsReport::kStatsReportTypeRemoteSsrc, 1);
+  EXPECT_TRUE(remote_report == NULL);
+}
 
 // This test verifies that audio receive streams populate stats reports
 // correctly.
@@ -1129,7 +1241,7 @@
   const std::string kVcName("vcname");
   cricket::VoiceChannel voice_channel(talk_base::Thread::Current(),
       media_engine_, media_channel, &session_, kVcName, false);
-  stream_ = webrtc::MediaStream::Create("remoteStreamLabel");
+  AddIncomingAudioTrackStats();
   stats.AddStream(stream_);
 
   stats.set_session(&session_);
@@ -1141,19 +1253,7 @@
                             Return(true)));
 
   cricket::VoiceReceiverInfo voice_receiver_info;
-  voice_receiver_info.add_ssrc(kSsrcOfTrack);
-  voice_receiver_info.bytes_rcvd = 100;
-  voice_receiver_info.packets_rcvd = 101;
-  voice_receiver_info.packets_lost = 102;
-  voice_receiver_info.fraction_lost = 103;
-  voice_receiver_info.packets_lost = 104;
-  voice_receiver_info.ext_seqnum = 105;
-  voice_receiver_info.jitter_ms = 106;
-  voice_receiver_info.jitter_buffer_ms = 107;
-  voice_receiver_info.jitter_buffer_preferred_ms = 108;
-  voice_receiver_info.delay_estimate_ms = 109;
-  voice_receiver_info.audio_level = 110;
-  voice_receiver_info.expand_rate = 111;
+  InitVoiceReceiverInfo(&voice_receiver_info);
 
   // Constructs an ssrc stats update.
   cricket::VoiceMediaInfo stats_read;
@@ -1164,21 +1264,23 @@
   EXPECT_CALL(*media_channel, GetStats(_))
       .WillRepeatedly(DoAll(SetArgPointee<0>(stats_read),
                             Return(true)));
-  EXPECT_CALL(session_, GetTrackIdBySsrc(kSsrcOfTrack, _))
-      .WillRepeatedly(Return(true));
 
   StatsReports reports;  // returned values.
   stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard);
   stats.GetStats(NULL, &reports);
 
-  // Verify the remote report.
+  // Verify the track id is |kRemoteTrackId|.
+  const std::string track_id = ExtractSsrcStatsValue(
+      reports, StatsReport::kStatsValueNameTrackId);
+  EXPECT_EQ(kRemoteTrackId, track_id);
+
+  // Verify the report for this remote track.
   const StatsReport* report = FindNthReportByType(
         reports, StatsReport::kStatsReportTypeSsrc, 1);
   EXPECT_FALSE(report == NULL);
   VerifyVoiceReceiverInfoReport(report, voice_receiver_info);
 }
 
-
 // This test verifies that a local stats object won't update its statistics
 // after a RemoveLocalAudioTrack() call.
 TEST_F(StatsCollectorTest, GetStatsAfterRemoveAudioStream) {
@@ -1188,7 +1290,7 @@
   const std::string kVcName("vcname");
   cricket::VoiceChannel voice_channel(talk_base::Thread::Current(),
       media_engine_, media_channel, &session_, kVcName, false);
-  AddLocalAudioTrackStats();
+  AddOutgoingAudioTrackStats();
   stats.AddStream(stream_);
   stats.AddLocalAudioTrack(audio_track_.get(), kSsrcOfTrack);
 
@@ -1202,25 +1304,7 @@
 
   stats.RemoveLocalAudioTrack(audio_track_.get(), kSsrcOfTrack);
   cricket::VoiceSenderInfo voice_sender_info;
-  // Contents won't be modified by the AudioTrackInterface::GetStats().
-  voice_sender_info.add_ssrc(kSsrcOfTrack);
-  voice_sender_info.codec_name = "fake_codec";
-  voice_sender_info.bytes_sent = 100;
-  voice_sender_info.packets_sent = 101;
-  voice_sender_info.rtt_ms = 102;
-  voice_sender_info.fraction_lost = 103;
-  voice_sender_info.jitter_ms = 104;
-  voice_sender_info.packets_lost = 105;
-  voice_sender_info.ext_seqnum = 106;
-
-  // Contents will be modified by the AudioTrackInterface::GetStats().
-  voice_sender_info.audio_level = 107;
-  voice_sender_info.echo_return_loss = 108;;
-  voice_sender_info.echo_return_loss_enhancement = 109;
-  voice_sender_info.echo_delay_median_ms = 110;
-  voice_sender_info.echo_delay_std_ms = 111;
-  voice_sender_info.aec_quality_min = 112;
-  voice_sender_info.typing_noise_detected = false;
+  InitVoiceSenderInfo(&voice_sender_info);
 
   // Constructs an ssrc stats update.
   cricket::VoiceMediaInfo stats_read;
@@ -1242,7 +1326,7 @@
   EXPECT_FALSE(report == NULL);
   std::string track_id = ExtractSsrcStatsValue(
       reports, StatsReport::kStatsValueNameTrackId);
-  EXPECT_EQ(kAudioTrackId, track_id);
+  EXPECT_EQ(kLocalTrackId, track_id);
   std::string ssrc_id = ExtractSsrcStatsValue(
       reports, StatsReport::kStatsValueNameSsrc);
   EXPECT_EQ(talk_base::ToString<uint32>(kSsrcOfTrack), ssrc_id);
@@ -1253,4 +1337,82 @@
   VerifyVoiceSenderInfoReport(report, voice_sender_info);
 }
 
+// This test verifies that when ongoing and incoming audio tracks are using
+// the same ssrc, they populate stats reports correctly.
+TEST_F(StatsCollectorTest, LocalAndRemoteTracksWithSameSsrc) {
+  webrtc::StatsCollector stats;  // Implementation under test.
+  MockVoiceMediaChannel* media_channel = new MockVoiceMediaChannel();
+  // The content_name known by the voice channel.
+  const std::string kVcName("vcname");
+  cricket::VoiceChannel voice_channel(talk_base::Thread::Current(),
+      media_engine_, media_channel, &session_, kVcName, false);
+
+  // Create a local stream with a local audio track and adds it to the stats.
+  AddOutgoingAudioTrackStats();
+  stats.AddStream(stream_);
+  stats.AddLocalAudioTrack(audio_track_.get(), kSsrcOfTrack);
+
+  // Create a remote stream with a remote audio track and adds it to the stats.
+  talk_base::scoped_refptr<webrtc::MediaStream> remote_stream(
+      webrtc::MediaStream::Create("remotestreamlabel"));
+  talk_base::scoped_refptr<FakeAudioTrack> remote_track(
+      new talk_base::RefCountedObject<FakeAudioTrack>(kRemoteTrackId));
+  EXPECT_CALL(session_, GetRemoteTrackIdBySsrc(kSsrcOfTrack, _))
+      .WillRepeatedly(DoAll(SetArgPointee<1>(kRemoteTrackId), Return(true)));
+  remote_stream->AddTrack(remote_track);
+  stats.AddStream(remote_stream);
+
+  stats.set_session(&session_);
+
+  // Instruct the session to return stats containing the transport channel.
+  InitSessionStats(kVcName);
+  EXPECT_CALL(session_, GetStats(_))
+      .WillRepeatedly(DoAll(SetArgPointee<0>(session_stats_),
+                            Return(true)));
+
+  cricket::VoiceSenderInfo voice_sender_info;
+  InitVoiceSenderInfo(&voice_sender_info);
+
+  // Some of the contents in |voice_sender_info| needs to be updated from the
+  // |audio_track_|.
+  UpdateVoiceSenderInfoFromAudioTrack(audio_track_.get(), &voice_sender_info);
+
+  cricket::VoiceReceiverInfo voice_receiver_info;
+  InitVoiceReceiverInfo(&voice_receiver_info);
+
+  // Constructs an ssrc stats update.
+  cricket::VoiceMediaInfo stats_read;
+  stats_read.senders.push_back(voice_sender_info);
+  stats_read.receivers.push_back(voice_receiver_info);
+
+  EXPECT_CALL(session_, voice_channel()).WillRepeatedly(Return(&voice_channel));
+  EXPECT_CALL(session_, video_channel()).WillRepeatedly(ReturnNull());
+  EXPECT_CALL(*media_channel, GetStats(_))
+      .WillRepeatedly(DoAll(SetArgPointee<0>(stats_read),
+                            Return(true)));
+
+  StatsReports reports;  // returned values.
+  stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard);
+
+  // Get stats for the local track.
+  stats.GetStats(audio_track_.get(), &reports);
+  const StatsReport* track_report = FindNthReportByType(
+      reports, StatsReport::kStatsReportTypeSsrc, 1);
+  EXPECT_TRUE(track_report);
+  std::string track_id = ExtractSsrcStatsValue(
+      reports, StatsReport::kStatsValueNameTrackId);
+  EXPECT_EQ(kLocalTrackId, track_id);
+  VerifyVoiceSenderInfoReport(track_report, voice_sender_info);
+
+  // Get stats for the remote track.
+  stats.GetStats(remote_track.get(), &reports);
+  track_report = FindNthReportByType(reports,
+                                     StatsReport::kStatsReportTypeSsrc, 1);
+  EXPECT_TRUE(track_report);
+  track_id = ExtractSsrcStatsValue(reports,
+                                   StatsReport::kStatsValueNameTrackId);
+  EXPECT_EQ(kRemoteTrackId, track_id);
+  VerifyVoiceReceiverInfoReport(track_report, voice_receiver_info);
+}
+
 }  // namespace
diff --git a/talk/app/webrtc/webrtcsession.cc b/talk/app/webrtc/webrtcsession.cc
index d3bae91..887aa4f 100644
--- a/talk/app/webrtc/webrtcsession.cc
+++ b/talk/app/webrtc/webrtcsession.cc
@@ -932,31 +932,18 @@
   return false;
 }
 
-bool WebRtcSession::GetTrackIdBySsrc(uint32 ssrc, std::string* id) {
-  if (GetLocalTrackId(ssrc, id)) {
-    if (GetRemoteTrackId(ssrc, id)) {
-      LOG(LS_WARNING) << "SSRC " << ssrc
-                      << " exists in both local and remote descriptions";
-      return true;  // We return the remote track id.
-    }
-    return true;
-  } else {
-    return GetRemoteTrackId(ssrc, id);
-  }
-}
-
-bool WebRtcSession::GetLocalTrackId(uint32 ssrc, std::string* track_id) {
+bool WebRtcSession::GetLocalTrackIdBySsrc(uint32 ssrc, std::string* track_id) {
   if (!BaseSession::local_description())
     return false;
   return webrtc::GetTrackIdBySsrc(
-    BaseSession::local_description(), ssrc, track_id);
+      BaseSession::local_description(), ssrc, track_id);
 }
 
-bool WebRtcSession::GetRemoteTrackId(uint32 ssrc, std::string* track_id) {
+bool WebRtcSession::GetRemoteTrackIdBySsrc(uint32 ssrc, std::string* track_id) {
   if (!BaseSession::remote_description())
-      return false;
+    return false;
   return webrtc::GetTrackIdBySsrc(
-    BaseSession::remote_description(), ssrc, track_id);
+      BaseSession::remote_description(), ssrc, track_id);
 }
 
 std::string WebRtcSession::BadStateErrMsg(State state) {
diff --git a/talk/app/webrtc/webrtcsession.h b/talk/app/webrtc/webrtcsession.h
index 791e954..3ffea8f 100644
--- a/talk/app/webrtc/webrtcsession.h
+++ b/talk/app/webrtc/webrtcsession.h
@@ -166,7 +166,9 @@
   }
 
   // Get the id used as a media stream track's "id" field from ssrc.
-  virtual bool GetTrackIdBySsrc(uint32 ssrc, std::string* id);
+  virtual bool GetLocalTrackIdBySsrc(uint32 ssrc, std::string* track_id);
+  virtual bool GetRemoteTrackIdBySsrc(uint32 ssrc, std::string* track_id);
+
 
   // AudioMediaProviderInterface implementation.
   virtual void SetAudioPlayout(uint32 ssrc, bool enable,
@@ -289,9 +291,6 @@
                                     const cricket::ReceiveDataParams& params,
                                     const talk_base::Buffer& payload);
 
-  bool GetLocalTrackId(uint32 ssrc, std::string* track_id);
-  bool GetRemoteTrackId(uint32 ssrc, std::string* track_id);
-
   std::string BadStateErrMsg(State state);
   void SetIceConnectionState(PeerConnectionInterface::IceConnectionState state);
 
diff --git a/talk/libjingle_tests.gyp b/talk/libjingle_tests.gyp
index 9fefa91..00e2230 100755
--- a/talk/libjingle_tests.gyp
+++ b/talk/libjingle_tests.gyp
@@ -386,20 +386,25 @@
       'target_name': 'libjingle_peerconnection_unittest',
       'type': 'executable',
       'dependencies': [
+        '<(DEPTH)/testing/gmock.gyp:gmock',
         'gunit',
         'libjingle.gyp:libjingle',
         'libjingle.gyp:libjingle_p2p',
         'libjingle.gyp:libjingle_peerconnection',
         'libjingle_unittest_main',
       ],
-      # TODO(ronghuawu): Reenable below unit tests that require gmock.
+      'direct_dependent_settings': {
+        'include_dirs': [
+          '<(DEPTH)/testing/gmock/include',
+        ],
+      },
       'sources': [
         'app/webrtc/datachannel_unittest.cc',
         'app/webrtc/dtmfsender_unittest.cc',
         'app/webrtc/jsepsessiondescription_unittest.cc',
         'app/webrtc/localaudiosource_unittest.cc',
-        # 'app/webrtc/mediastream_unittest.cc',
-        # 'app/webrtc/mediastreamhandler_unittest.cc',
+        'app/webrtc/mediastream_unittest.cc',
+        'app/webrtc/mediastreamhandler_unittest.cc',
         'app/webrtc/mediastreamsignaling_unittest.cc',
         'app/webrtc/peerconnection_unittest.cc',
         'app/webrtc/peerconnectionendtoend_unittest.cc',
@@ -408,6 +413,7 @@
         # 'app/webrtc/peerconnectionproxy_unittest.cc',
         'app/webrtc/remotevideocapturer_unittest.cc',
         'app/webrtc/sctputils.cc',
+        'app/webrtc/statscollector_unittest.cc',
         'app/webrtc/test/fakeaudiocapturemodule.cc',
         'app/webrtc/test/fakeaudiocapturemodule.h',
         'app/webrtc/test/fakeaudiocapturemodule_unittest.cc',
@@ -426,6 +432,21 @@
         'app/webrtc/webrtcsdp_unittest.cc',
         'app/webrtc/webrtcsession_unittest.cc',
       ],
+      'conditions': [
+        ['OS=="android"', {
+          # We want gmock features that use tr1::tuple, but we currently
+          # don't support the variadic templates used by libstdc++'s
+          # implementation. gmock supports this scenario by providing its
+          # own implementation but we must opt in to it.
+          'defines': [
+            'GTEST_USE_OWN_TR1_TUPLE=1',
+            # GTEST_USE_OWN_TR1_TUPLE only works if GTEST_HAS_TR1_TUPLE is set.
+            # gmock r625 made it so that GTEST_HAS_TR1_TUPLE is set to 0
+            # automatically on android, so it has to be set explicitly here.
+            'GTEST_HAS_TR1_TUPLE=1',
+           ],
+        }],
+      ],
     },  # target libjingle_peerconnection_unittest
   ],
   'conditions': [
diff --git a/tools/valgrind-webrtc/memcheck/suppressions.txt b/tools/valgrind-webrtc/memcheck/suppressions.txt
index 509535d..2a3640e 100644
--- a/tools/valgrind-webrtc/memcheck/suppressions.txt
+++ b/tools/valgrind-webrtc/memcheck/suppressions.txt
@@ -698,3 +698,17 @@
    fun:_ZN21VideoMediaChannelTestIN7cricket17WebRtcVideoEngineENS0_23WebRtcVideoMediaChannelEE36TwoStreamsSendAndFailUnsignalledRecvERKNS0_10VideoCodecE
    fun:_ZN69WebRtcVideoMediaChannelTest_TwoStreamsSendAndFailUnsignalledRecv_Test8TestBodyEv
 }
+{
+  bug_3478
+  Memcheck:Leak
+  fun:_Znw*
+  fun:_ZNK9talk_base18FakeSSLCertificate12GetReferenceEv
+  fun:_ZN9talk_base18FakeSSLCertificate7DupCertES0_
+  fun:_ZSt9transformIN9__gnu_cxx17__normal_iteratorIPKN9talk_base18FakeSSLCertificateESt6vectorIS3_SaIS3_EEEENS1_IPPNS2_14SSLCertificateES6_ISB_SaISB_EEEEPFPS3_S3_EET0_T_SK_SJ_T1_
+  fun:_ZNK9talk_base18FakeSSLCertificate8GetChainEPPNS_12SSLCertChainE
+  fun:_ZN6webrtc14StatsCollector21AddCertificateReportsEPKN9talk_base14SSLCertificateE
+  fun:_ZN6webrtc14StatsCollector18ExtractSessionInfoEv
+  fun:_ZN6webrtc14StatsCollector11UpdateStatsENS_23PeerConnectionInterface16StatsOutputLevelE
+  fun:_ZN12_GLOBAL__N_118StatsCollectorTest22TestCertificateReportsERKN9talk_base18FakeSSLCertificateERKSt6vectorISsSaISsEES4_S9_
+  fun:_ZN12_GLOBAL__N_156StatsCollectorTest_ChainedCertificateReportsCreated_Test8TestBodyEv
+}