Filter out some variables with initial -1 in the stats report.

If we call GetStats in PeerConnection before receiving the remote answer, we will get some variables in the StatsReports which are initially set to be -1.

Several conditions are added when extracting the info for the report in StatsCollector.

Those variables include:
gooRtt,
dataChannelId,
googEchoCancellationEchoDelayMedian,
googEchoCancellationEchoQualityMin,
googEchoCancellationEchoDelayStdDev,
googJitterReceived,
audioInputLevel,
googCaptureStartNtpTimeMs
packetsLost.

BUG=webrtc:3377

Review-Url: https://codereview.webrtc.org/1875873002
Cr-Commit-Position: refs/heads/master@{#12735}
diff --git a/webrtc/api/statscollector.cc b/webrtc/api/statscollector.cc
index 82c97cc..df1d0aa 100644
--- a/webrtc/api/statscollector.cc
+++ b/webrtc/api/statscollector.cc
@@ -88,7 +88,9 @@
                                  StatsReport* report) {
   report->AddString(StatsReport::kStatsValueNameCodecName, info.codec_name);
   report->AddInt64(StatsReport::kStatsValueNameBytesSent, info.bytes_sent);
-  report->AddInt64(StatsReport::kStatsValueNameRtt, info.rtt_ms);
+  if (info.rtt_ms >= 0) {
+    report->AddInt64(StatsReport::kStatsValueNameRtt, info.rtt_ms);
+  }
 }
 
 void ExtractCommonReceiveProperties(const cricket::MediaReceiverInfo& info,
@@ -105,17 +107,23 @@
                              int echo_delay_std_ms) {
   report->AddBoolean(StatsReport::kStatsValueNameTypingNoiseState,
                      typing_noise_detected);
-  report->AddFloat(StatsReport::kStatsValueNameEchoCancellationQualityMin,
-                   aec_quality_min);
+  if (aec_quality_min >= 0.0f) {
+    report->AddFloat(StatsReport::kStatsValueNameEchoCancellationQualityMin,
+                     aec_quality_min);
+  }
   const IntForAdd ints[] = {
-    { StatsReport::kStatsValueNameEchoReturnLoss, echo_return_loss },
-    { StatsReport::kStatsValueNameEchoReturnLossEnhancement,
-      echo_return_loss_enhancement },
     { StatsReport::kStatsValueNameEchoDelayMedian, echo_delay_median_ms },
     { StatsReport::kStatsValueNameEchoDelayStdDev, echo_delay_std_ms },
   };
-  for (const auto& i : ints)
-    report->AddInt(i.name, i.value);
+  for (const auto& i : ints) {
+    if (i.value >= 0) {
+      report->AddInt(i.name, i.value);
+    }
+  }
+  // These can take on valid negative values.
+  report->AddInt(StatsReport::kStatsValueNameEchoReturnLoss, echo_return_loss);
+  report->AddInt(StatsReport::kStatsValueNameEchoReturnLossEnhancement,
+                 echo_return_loss_enhancement);
 }
 
 void ExtractStats(const cricket::VoiceReceiverInfo& info, StatsReport* report) {
@@ -131,7 +139,6 @@
   };
 
   const IntForAdd ints[] = {
-    { StatsReport::kStatsValueNameAudioOutputLevel, info.audio_level },
     { StatsReport::kStatsValueNameCurrentDelayMs, info.delay_estimate_ms },
     { StatsReport::kStatsValueNameDecodingCNG, info.decoding_cng },
     { StatsReport::kStatsValueNameDecodingCTN, info.decoding_calls_to_neteq },
@@ -153,11 +160,17 @@
 
   for (const auto& i : ints)
     report->AddInt(i.name, i.value);
+  if (info.audio_level >= 0) {
+    report->AddInt(StatsReport::kStatsValueNameAudioOutputLevel,
+                   info.audio_level);
+  }
 
   report->AddInt64(StatsReport::kStatsValueNameBytesReceived,
                    info.bytes_rcvd);
-  report->AddInt64(StatsReport::kStatsValueNameCaptureStartNtpTimeMs,
-                   info.capture_start_ntp_time_ms);
+  if (info.capture_start_ntp_time_ms >= 0) {
+    report->AddInt64(StatsReport::kStatsValueNameCaptureStartNtpTimeMs,
+                     info.capture_start_ntp_time_ms);
+  }
   report->AddString(StatsReport::kStatsValueNameMediaType, "audio");
 }
 
@@ -177,8 +190,11 @@
     { StatsReport::kStatsValueNamePacketsSent, info.packets_sent },
   };
 
-  for (const auto& i : ints)
-    report->AddInt(i.name, i.value);
+  for (const auto& i : ints) {
+    if (i.value >= 0) {
+      report->AddInt(i.name, i.value);
+    }
+  }
   report->AddString(StatsReport::kStatsValueNameMediaType, "audio");
 }
 
@@ -188,8 +204,10 @@
                     info.decoder_implementation_name);
   report->AddInt64(StatsReport::kStatsValueNameBytesReceived,
                    info.bytes_rcvd);
-  report->AddInt64(StatsReport::kStatsValueNameCaptureStartNtpTimeMs,
-                   info.capture_start_ntp_time_ms);
+  if (info.capture_start_ntp_time_ms >= 0) {
+    report->AddInt64(StatsReport::kStatsValueNameCaptureStartNtpTimeMs,
+                     info.capture_start_ntp_time_ms);
+  }
   const IntForAdd ints[] = {
     { StatsReport::kStatsValueNameCurrentDelayMs, info.current_delay_ms },
     { StatsReport::kStatsValueNameDecodeMs, info.decode_ms },
@@ -870,7 +888,10 @@
     StatsReport* report = reports_.ReplaceOrAddNew(id);
     report->set_timestamp(stats_gathering_started_);
     report->AddString(StatsReport::kStatsValueNameLabel, dc->label());
-    report->AddInt(StatsReport::kStatsValueNameDataChannelId, dc->id());
+    // Filter out the initial id (-1).
+    if (dc->id() >= 0) {
+      report->AddInt(StatsReport::kStatsValueNameDataChannelId, dc->id());
+    }
     report->AddString(StatsReport::kStatsValueNameProtocol, dc->protocol());
     report->AddString(StatsReport::kStatsValueNameState,
                       DataChannelInterface::DataStateString(dc->state()));
diff --git a/webrtc/api/statscollector_unittest.cc b/webrtc/api/statscollector_unittest.cc
index 00b50b6..9243aed 100644
--- a/webrtc/api/statscollector_unittest.cc
+++ b/webrtc/api/statscollector_unittest.cc
@@ -165,6 +165,48 @@
   rtc::scoped_refptr<FakeAudioProcessor> processor_;
 };
 
+// This fake audio processor is used to verify that the undesired initial values
+// (-1) will be filtered out.
+class FakeAudioProcessorWithInitValue : public webrtc::AudioProcessorInterface {
+ public:
+  FakeAudioProcessorWithInitValue() {}
+  ~FakeAudioProcessorWithInitValue() {}
+
+ private:
+  void GetStats(AudioProcessorInterface::AudioProcessorStats* stats) override {
+    stats->typing_noise_detected = false;
+    stats->echo_return_loss = -100;
+    stats->echo_return_loss_enhancement = -100;
+    stats->echo_delay_median_ms = -1;
+    stats->aec_quality_min = -1.0f;
+    stats->echo_delay_std_ms = -1;
+  }
+};
+
+class FakeAudioTrackWithInitValue
+    : public webrtc::MediaStreamTrack<webrtc::AudioTrackInterface> {
+ public:
+  explicit FakeAudioTrackWithInitValue(const std::string& id)
+      : webrtc::MediaStreamTrack<webrtc::AudioTrackInterface>(id),
+        processor_(
+            new rtc::RefCountedObject<FakeAudioProcessorWithInitValue>()) {}
+  std::string kind() const override { return "audio"; }
+  webrtc::AudioSourceInterface* GetSource() const override { return NULL; }
+  void AddSink(webrtc::AudioTrackSinkInterface* sink) override {}
+  void RemoveSink(webrtc::AudioTrackSinkInterface* sink) override {}
+  bool GetSignalLevel(int* level) override {
+    *level = 1;
+    return true;
+  }
+  rtc::scoped_refptr<webrtc::AudioProcessorInterface> GetAudioProcessor()
+      override {
+    return processor_;
+  }
+
+ private:
+  rtc::scoped_refptr<FakeAudioProcessorWithInitValue> processor_;
+};
+
 bool GetValue(const StatsReport* report,
               StatsReport::StatsValueName name,
               std::string* value) {
@@ -444,7 +486,8 @@
 }
 
 void UpdateVoiceSenderInfoFromAudioTrack(
-    FakeAudioTrack* audio_track, cricket::VoiceSenderInfo* voice_sender_info) {
+    AudioTrackInterface* 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);
@@ -778,6 +821,29 @@
   std::vector<rtc::scoped_refptr<DataChannel>> data_channels_;
 };
 
+TEST_F(StatsCollectorTest, FilterOutNegativeDataChannelId) {
+  const std::string label = "hacks";
+  // The data channel id is from the Config which is -1 initially.
+  const int id = -1;
+  const std::string state = DataChannelInterface::DataStateString(
+      DataChannelInterface::DataState::kConnecting);
+
+  AddDataChannel(cricket::DCT_SCTP, label, id);
+  StatsCollectorForTest stats(&pc_);
+
+  stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard);
+
+  StatsReports reports;
+  stats.GetStats(NULL, &reports);
+
+  const StatsReport* report =
+      FindNthReportByType(reports, StatsReport::kStatsReportTypeDataChannel, 1);
+
+  std::string value_in_report;
+  EXPECT_FALSE(GetValue(report, StatsReport::kStatsValueNameDataChannelId,
+                        &value_in_report));
+}
+
 // Verify that ExtractDataInfo populates reports.
 TEST_F(StatsCollectorTest, ExtractDataInfo) {
   const std::string label = "hacks";
@@ -1500,6 +1566,113 @@
                          std::move(remote_cert), std::vector<std::string>());
 }
 
+// This test verifies that the audio/video related stats which are -1 initially
+// will be filtered out.
+TEST_F(StatsCollectorTest, FilterOutNegativeInitialValues) {
+  StatsCollectorForTest stats(&pc_);
+
+  EXPECT_CALL(session_, GetLocalCertificate(_, _))
+      .WillRepeatedly(Return(false));
+  EXPECT_CALL(session_, GetRemoteSSLCertificate_ReturnsRawPointer(_))
+      .WillRepeatedly(Return(nullptr));
+
+  MockVoiceMediaChannel* media_channel = new MockVoiceMediaChannel();
+  // The transport_name known by the voice channel.
+  const std::string kVcName("vcname");
+  cricket::VoiceChannel voice_channel(worker_thread_, network_thread_,
+                                        media_engine_, media_channel, nullptr,
+                                        kVcName, false);
+
+  // Create a local stream with a local audio track and adds it to the stats.
+  if (stream_ == NULL)
+    stream_ = webrtc::MediaStream::Create("streamlabel");
+
+  rtc::scoped_refptr<FakeAudioTrackWithInitValue> local_track(
+      new rtc::RefCountedObject<FakeAudioTrackWithInitValue>(kLocalTrackId));
+  stream_->AddTrack(local_track);
+  EXPECT_CALL(session_, GetLocalTrackIdBySsrc(kSsrcOfTrack, _))
+      .WillOnce(DoAll(SetArgPointee<1>(kLocalTrackId), Return(true)));
+  stats.AddStream(stream_);
+  stats.AddLocalAudioTrack(local_track.get(), kSsrcOfTrack);
+
+  // Create a remote stream with a remote audio track and adds it to the stats.
+  rtc::scoped_refptr<webrtc::MediaStream> remote_stream(
+      webrtc::MediaStream::Create("remotestreamlabel"));
+  rtc::scoped_refptr<FakeAudioTrackWithInitValue> remote_track(
+      new rtc::RefCountedObject<FakeAudioTrackWithInitValue>(kRemoteTrackId));
+  EXPECT_CALL(session_, GetRemoteTrackIdBySsrc(kSsrcOfTrack, _))
+      .WillOnce(DoAll(SetArgPointee<1>(kRemoteTrackId), Return(true)));
+  remote_stream->AddTrack(remote_track);
+  stats.AddStream(remote_stream);
+
+  // Instruct the session to return stats containing the transport channel.
+  InitSessionStats(kVcName);
+  EXPECT_CALL(session_, GetTransportStats(_))
+      .WillRepeatedly(DoAll(SetArgPointee<0>(session_stats_), Return(true)));
+
+  cricket::VoiceSenderInfo voice_sender_info;
+  voice_sender_info.add_ssrc(kSsrcOfTrack);
+  // These values are set to -1 initially in audio_send_stream.
+  // The voice_sender_info will read the values from audio_send_stream.
+  voice_sender_info.rtt_ms = -1;
+  voice_sender_info.packets_lost = -1;
+  voice_sender_info.jitter_ms = -1;
+
+  // Some of the contents in |voice_sender_info| needs to be updated from the
+  // |audio_track_|.
+  UpdateVoiceSenderInfoFromAudioTrack(local_track.get(), &voice_sender_info);
+
+  cricket::VoiceReceiverInfo voice_receiver_info;
+  voice_receiver_info.add_ssrc(kSsrcOfTrack);
+  voice_receiver_info.capture_start_ntp_time_ms = -1;
+  voice_receiver_info.audio_level = -1;
+
+  // 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;
+  stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard);
+
+  // Get stats for the local track.
+  stats.GetStats(local_track.get(), &reports);
+  const StatsReport* report =
+      FindNthReportByType(reports, StatsReport::kStatsReportTypeSsrc, 1);
+  EXPECT_TRUE(report);
+  // The -1 will not be added to the stats report.
+  std::string value_in_report;
+  EXPECT_FALSE(
+      GetValue(report, StatsReport::kStatsValueNameRtt, &value_in_report));
+  EXPECT_FALSE(GetValue(report, StatsReport::kStatsValueNamePacketsLost,
+                        &value_in_report));
+  EXPECT_FALSE(GetValue(report, StatsReport::kStatsValueNameJitterReceived,
+                        &value_in_report));
+  EXPECT_FALSE(GetValue(report,
+                        StatsReport::kStatsValueNameEchoCancellationQualityMin,
+                        &value_in_report));
+  EXPECT_FALSE(GetValue(report, StatsReport::kStatsValueNameEchoDelayMedian,
+                        &value_in_report));
+  EXPECT_FALSE(GetValue(report, StatsReport::kStatsValueNameEchoDelayStdDev,
+                        &value_in_report));
+
+  // Get stats for the remote track.
+  reports.clear();
+  stats.GetStats(remote_track.get(), &reports);
+  report = FindNthReportByType(reports, StatsReport::kStatsReportTypeSsrc, 1);
+  EXPECT_TRUE(report);
+  EXPECT_FALSE(GetValue(report,
+                        StatsReport::kStatsValueNameCaptureStartNtpTimeMs,
+                        &value_in_report));
+  EXPECT_FALSE(GetValue(report, StatsReport::kStatsValueNameAudioInputLevel,
+                        &value_in_report));
+}
+
 // This test verifies that a local stats object can get statistics via
 // AudioTrackInterface::GetStats() method.
 TEST_F(StatsCollectorTest, GetStatsFromLocalAudioTrack) {