RTCIceCandidateStats[1] added.
The RTCStatsCollector collects candidates from candidate pairs. Note
that there may be other candidates that are not paired with anything,
stats for these should also be produced before closing crbug.com/632723.
[1] https://w3c.github.io/webrtc-stats/#icecandidate-dict*
BUG=chromium:627816, chromium:632723
Review-Url: https://codereview.webrtc.org/2384143002
Cr-Commit-Position: refs/heads/master@{#14565}
diff --git a/webrtc/api/rtcstatscollector.cc b/webrtc/api/rtcstatscollector.cc
index 084eab1..2d858c0 100644
--- a/webrtc/api/rtcstatscollector.cc
+++ b/webrtc/api/rtcstatscollector.cc
@@ -18,9 +18,24 @@
#include "webrtc/api/webrtcsession.h"
#include "webrtc/base/checks.h"
#include "webrtc/base/sslidentity.h"
+#include "webrtc/p2p/base/candidate.h"
+#include "webrtc/p2p/base/port.h"
namespace webrtc {
+const char* CandidateTypeToRTCIceCandidateType(const std::string& type) {
+ if (type == cricket::LOCAL_PORT_TYPE)
+ return RTCIceCandidateType::kHost;
+ if (type == cricket::STUN_PORT_TYPE)
+ return RTCIceCandidateType::kSrflx;
+ if (type == cricket::PRFLX_PORT_TYPE)
+ return RTCIceCandidateType::kPrflx;
+ if (type == cricket::RELAY_PORT_TYPE)
+ return RTCIceCandidateType::kRelay;
+ RTC_NOTREACHED();
+ return nullptr;
+}
+
rtc::scoped_refptr<RTCStatsCollector> RTCStatsCollector::Create(
PeerConnection* pc, int64_t cache_lifetime_us) {
return rtc::scoped_refptr<RTCStatsCollector>(
@@ -93,6 +108,8 @@
SessionStats session_stats;
if (pc_->session()->GetTransportStats(&session_stats)) {
ProduceCertificateStats_s(timestamp_us, session_stats, report.get());
+ ProduceIceCandidateAndPairStats_s(timestamp_us, session_stats,
+ report.get());
}
ProducePeerConnectionStats_s(timestamp_us, report.get());
@@ -201,6 +218,59 @@
}
}
+void RTCStatsCollector::ProduceIceCandidateAndPairStats_s(
+ int64_t timestamp_us, const SessionStats& session_stats,
+ RTCStatsReport* report) const {
+ RTC_DCHECK(signaling_thread_->IsCurrent());
+ for (const auto& transport : session_stats.transport_stats) {
+ for (const auto& channel : transport.second.channel_stats) {
+ for (const cricket::ConnectionInfo& info : channel.connection_infos) {
+ // TODO(hbos): Produce |RTCIceCandidatePairStats| referencing the
+ // resulting |RTCIceCandidateStats| pair. crbug.com/633550
+ // TODO(hbos): There could be other candidates that are not paired with
+ // anything. We don't have a complete list. Local candidates come from
+ // Port objects, and prflx candidates (both local and remote) are only
+ // stored in candidate pairs. crbug.com/632723
+ ProduceIceCandidateStats_s(
+ timestamp_us, info.local_candidate, true, report);
+ ProduceIceCandidateStats_s(
+ timestamp_us, info.remote_candidate, false, report);
+ }
+ }
+ }
+}
+
+const std::string& RTCStatsCollector::ProduceIceCandidateStats_s(
+ int64_t timestamp_us, const cricket::Candidate& candidate, bool is_local,
+ RTCStatsReport* report) const {
+ RTC_DCHECK(signaling_thread_->IsCurrent());
+ const std::string& id = "RTCIceCandidate_" + candidate.id();
+ const RTCStats* stats = report->Get(id);
+ if (!stats) {
+ std::unique_ptr<RTCIceCandidateStats> candidate_stats;
+ if (is_local) {
+ candidate_stats.reset(
+ new RTCLocalIceCandidateStats(id, timestamp_us));
+ } else {
+ candidate_stats.reset(
+ new RTCRemoteIceCandidateStats(id, timestamp_us));
+ }
+ candidate_stats->ip = candidate.address().ipaddr().ToString();
+ candidate_stats->port = static_cast<int32_t>(candidate.address().port());
+ candidate_stats->protocol = candidate.protocol();
+ candidate_stats->candidate_type = CandidateTypeToRTCIceCandidateType(
+ candidate.type());
+ candidate_stats->priority = static_cast<int32_t>(candidate.priority());
+ // TODO(hbos): Define candidate_stats->url. crbug.com/632723
+
+ stats = candidate_stats.get();
+ report->AddStats(std::move(candidate_stats));
+ }
+ RTC_DCHECK_EQ(stats->type(), is_local ? RTCLocalIceCandidateStats::kType
+ : RTCRemoteIceCandidateStats::kType);
+ return stats->id();
+}
+
void RTCStatsCollector::ProducePeerConnectionStats_s(
int64_t timestamp_us, RTCStatsReport* report) const {
RTC_DCHECK(signaling_thread_->IsCurrent());
diff --git a/webrtc/api/rtcstatscollector.h b/webrtc/api/rtcstatscollector.h
index 2f5ed86..6443d13 100644
--- a/webrtc/api/rtcstatscollector.h
+++ b/webrtc/api/rtcstatscollector.h
@@ -21,10 +21,12 @@
#include "webrtc/base/scoped_ref_ptr.h"
#include "webrtc/base/timeutils.h"
+namespace cricket {
+class Candidate;
+} // namespace cricket
+
namespace rtc {
-
class SSLCertificate;
-
} // namespace rtc
namespace webrtc {
@@ -76,12 +78,22 @@
void AddPartialResults_s(rtc::scoped_refptr<RTCStatsReport> partial_report);
void DeliverCachedReport();
+ // Produces |RTCCertificateStats|.
void ProduceCertificateStats_s(
int64_t timestamp_us, const SessionStats& session_stats,
RTCStatsReport* report) const;
void ProduceCertificateStatsFromSSLCertificateAndChain_s(
int64_t timestamp_us, const rtc::SSLCertificate& certificate,
RTCStatsReport* report) const;
+ // Produces |RTCIceCandidateStats|. TODO(hbos): Should also produce
+ // |RTCIceCandidatePairStats|. crbug.com/633550
+ void ProduceIceCandidateAndPairStats_s(
+ int64_t timestamp_us, const SessionStats& session_stats,
+ RTCStatsReport* report) const;
+ const std::string& ProduceIceCandidateStats_s(
+ int64_t timestamp_us, const cricket::Candidate& candidate, bool is_local,
+ RTCStatsReport* report) const;
+ // Produces |RTCPeerConnectionStats|.
void ProducePeerConnectionStats_s(
int64_t timestamp_us, RTCStatsReport* report) const;
@@ -105,6 +117,9 @@
rtc::scoped_refptr<const RTCStatsReport> cached_report_;
};
+// Helper function, exposed for unittests.
+const char* CandidateTypeToRTCIceCandidateType(const std::string& type);
+
} // namespace webrtc
#endif // WEBRTC_API_RTCSTATSCOLLECTOR_H_
diff --git a/webrtc/api/rtcstatscollector_unittest.cc b/webrtc/api/rtcstatscollector_unittest.cc
index 14abbd2..7103864 100644
--- a/webrtc/api/rtcstatscollector_unittest.cc
+++ b/webrtc/api/rtcstatscollector_unittest.cc
@@ -25,10 +25,12 @@
#include "webrtc/base/fakesslidentity.h"
#include "webrtc/base/gunit.h"
#include "webrtc/base/logging.h"
+#include "webrtc/base/socketaddress.h"
#include "webrtc/base/thread_checker.h"
#include "webrtc/base/timedelta.h"
#include "webrtc/base/timeutils.h"
#include "webrtc/media/base/fakemediaengine.h"
+#include "webrtc/p2p/base/port.h"
using testing::_;
using testing::Invoke;
@@ -91,6 +93,20 @@
return info;
}
+std::unique_ptr<cricket::Candidate> CreateFakeCandidate(
+ const std::string& hostname,
+ int port,
+ const std::string& protocol,
+ const std::string& candidate_type,
+ uint32_t priority) {
+ std::unique_ptr<cricket::Candidate> candidate(new cricket::Candidate());
+ candidate->set_address(rtc::SocketAddress(hostname, port));
+ candidate->set_protocol(protocol);
+ candidate->set_type(candidate_type);
+ candidate->set_priority(priority);
+ return candidate;
+}
+
class RTCStatsCollectorTestHelper : public SetSessionDescriptionObserver {
public:
RTCStatsCollectorTestHelper()
@@ -111,6 +127,10 @@
EXPECT_CALL(pc_, sctp_data_channels()).WillRepeatedly(
ReturnRef(data_channels_));
EXPECT_CALL(session_, GetTransportStats(_)).WillRepeatedly(Return(false));
+ EXPECT_CALL(session_, GetLocalCertificate(_, _)).WillRepeatedly(
+ Return(false));
+ EXPECT_CALL(session_, GetRemoteSSLCertificate_ReturnsRawPointer(_))
+ .WillRepeatedly(Return(nullptr));
}
rtc::ScopedFakeClock& fake_clock() { return fake_clock_; }
@@ -308,6 +328,30 @@
return callback->report();
}
+ void ExpectReportContainsCandidate(
+ const rtc::scoped_refptr<const RTCStatsReport>& report,
+ const cricket::Candidate& candidate,
+ bool is_local) {
+ const RTCStats* stats =
+ report->Get("RTCIceCandidate_" + candidate.id());
+ EXPECT_TRUE(stats);
+ const RTCIceCandidateStats* candidate_stats;
+ if (is_local)
+ candidate_stats = &stats->cast_to<RTCLocalIceCandidateStats>();
+ else
+ candidate_stats = &stats->cast_to<RTCRemoteIceCandidateStats>();
+ EXPECT_EQ(*candidate_stats->ip, candidate.address().ipaddr().ToString());
+ EXPECT_EQ(*candidate_stats->port,
+ static_cast<int32_t>(candidate.address().port()));
+ EXPECT_EQ(*candidate_stats->protocol, candidate.protocol());
+ EXPECT_EQ(*candidate_stats->candidate_type,
+ CandidateTypeToRTCIceCandidateType(candidate.type()));
+ EXPECT_EQ(*candidate_stats->priority,
+ static_cast<int32_t>(candidate.priority()));
+ // TODO(hbos): Define candidate_stats->url. crbug.com/632723
+ EXPECT_FALSE(candidate_stats->url.is_defined());
+ }
+
void ExpectReportContainsCertificateInfo(
const rtc::scoped_refptr<const RTCStatsReport>& report,
const CertificateInfo& cert_info) {
@@ -534,6 +578,84 @@
ExpectReportContainsCertificateInfo(report, *remote_certinfo.get());
}
+TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidateStats) {
+ // Candidates in the first transport stats.
+ std::unique_ptr<cricket::Candidate> a_local_host = CreateFakeCandidate(
+ "1.2.3.4", 5,
+ "a_local_host's protocol",
+ cricket::LOCAL_PORT_TYPE,
+ 0);
+ std::unique_ptr<cricket::Candidate> a_remote_srflx = CreateFakeCandidate(
+ "6.7.8.9", 10,
+ "remote_srflx's protocol",
+ cricket::STUN_PORT_TYPE,
+ 1);
+ std::unique_ptr<cricket::Candidate> a_local_prflx = CreateFakeCandidate(
+ "11.12.13.14", 15,
+ "a_local_prflx's protocol",
+ cricket::PRFLX_PORT_TYPE,
+ 2);
+ std::unique_ptr<cricket::Candidate> a_remote_relay = CreateFakeCandidate(
+ "16.17.18.19", 20,
+ "a_remote_relay's protocol",
+ cricket::RELAY_PORT_TYPE,
+ 3);
+ // Candidates in the second transport stats.
+ std::unique_ptr<cricket::Candidate> b_local = CreateFakeCandidate(
+ "42.42.42.42", 42,
+ "b_local's protocol",
+ cricket::LOCAL_PORT_TYPE,
+ 42);
+ std::unique_ptr<cricket::Candidate> b_remote = CreateFakeCandidate(
+ "42.42.42.42", 42,
+ "b_remote's protocol",
+ cricket::LOCAL_PORT_TYPE,
+ 42);
+
+ SessionStats session_stats;
+
+ cricket::TransportChannelStats a_transport_channel_stats;
+ a_transport_channel_stats.connection_infos.push_back(
+ cricket::ConnectionInfo());
+ a_transport_channel_stats.connection_infos[0].local_candidate =
+ *a_local_host.get();
+ a_transport_channel_stats.connection_infos[0].remote_candidate =
+ *a_remote_srflx.get();
+ a_transport_channel_stats.connection_infos.push_back(
+ cricket::ConnectionInfo());
+ a_transport_channel_stats.connection_infos[1].local_candidate =
+ *a_local_prflx.get();
+ a_transport_channel_stats.connection_infos[1].remote_candidate =
+ *a_remote_relay.get();
+ session_stats.transport_stats["a"].channel_stats.push_back(
+ a_transport_channel_stats);
+
+ cricket::TransportChannelStats b_transport_channel_stats;
+ b_transport_channel_stats.connection_infos.push_back(
+ cricket::ConnectionInfo());
+ b_transport_channel_stats.connection_infos[0].local_candidate =
+ *b_local.get();
+ b_transport_channel_stats.connection_infos[0].remote_candidate =
+ *b_remote.get();
+ session_stats.transport_stats["b"].channel_stats.push_back(
+ b_transport_channel_stats);
+
+ // Mock the session to return the desired candidates.
+ EXPECT_CALL(test_->session(), GetTransportStats(_)).WillRepeatedly(Invoke(
+ [this, &session_stats](SessionStats* stats) {
+ *stats = session_stats;
+ return true;
+ }));
+
+ rtc::scoped_refptr<const RTCStatsReport> report = GetStatsReport();
+ ExpectReportContainsCandidate(report, *a_local_host.get(), true);
+ ExpectReportContainsCandidate(report, *a_remote_srflx.get(), false);
+ ExpectReportContainsCandidate(report, *a_local_prflx.get(), true);
+ ExpectReportContainsCandidate(report, *a_remote_relay.get(), false);
+ ExpectReportContainsCandidate(report, *b_local.get(), true);
+ ExpectReportContainsCandidate(report, *b_remote.get(), false);
+}
+
TEST_F(RTCStatsCollectorTest, CollectRTCPeerConnectionStats) {
int64_t before = rtc::TimeUTCMicros();
rtc::scoped_refptr<const RTCStatsReport> report = GetStatsReport();
diff --git a/webrtc/api/stats/rtcstats_objects.h b/webrtc/api/stats/rtcstats_objects.h
index f816d48..e827d01 100644
--- a/webrtc/api/stats/rtcstats_objects.h
+++ b/webrtc/api/stats/rtcstats_objects.h
@@ -17,8 +17,57 @@
namespace webrtc {
+// https://www.w3.org/TR/webrtc/#rtcicecandidatetype-enum
+struct RTCIceCandidateType {
+ static const char* kHost;
+ static const char* kSrflx;
+ static const char* kPrflx;
+ static const char* kRelay;
+};
+
+// https://w3c.github.io/webrtc-stats/#icecandidate-dict*
+class RTCIceCandidateStats : public RTCStats {
+ public:
+ WEBRTC_RTCSTATS_DECL();
+
+ RTCIceCandidateStats(const RTCIceCandidateStats& other);
+ ~RTCIceCandidateStats() override;
+
+ RTCStatsMember<std::string> ip;
+ RTCStatsMember<int32_t> port;
+ RTCStatsMember<std::string> protocol;
+ // TODO(hbos): Support enum types? "RTCStatsMember<RTCIceCandidateType>"?
+ RTCStatsMember<std::string> candidate_type;
+ RTCStatsMember<int32_t> priority;
+ RTCStatsMember<std::string> url;
+
+ protected:
+ RTCIceCandidateStats(const std::string& id, int64_t timestamp_us);
+ RTCIceCandidateStats(std::string&& id, int64_t timestamp_us);
+};
+
+// In the spec both local and remote varieties are of type RTCIceCandidateStats.
+// But here we define them as subclasses of |RTCIceCandidateStats| because the
+// |kType| need to be different ("RTCStatsType type") in the local/remote case.
+// https://w3c.github.io/webrtc-stats/#rtcstatstype-str*
+class RTCLocalIceCandidateStats final : public RTCIceCandidateStats {
+ public:
+ static const char kType[];
+ RTCLocalIceCandidateStats(const std::string& id, int64_t timestamp_us);
+ RTCLocalIceCandidateStats(std::string&& id, int64_t timestamp_us);
+ const char* type() const override;
+};
+
+class RTCRemoteIceCandidateStats final : public RTCIceCandidateStats {
+ public:
+ static const char kType[];
+ RTCRemoteIceCandidateStats(const std::string& id, int64_t timestamp_us);
+ RTCRemoteIceCandidateStats(std::string&& id, int64_t timestamp_us);
+ const char* type() const override;
+};
+
// https://w3c.github.io/webrtc-stats/#certificatestats-dict*
-class RTCCertificateStats : public RTCStats {
+class RTCCertificateStats final : public RTCStats {
public:
WEBRTC_RTCSTATS_DECL();
@@ -35,7 +84,7 @@
// https://w3c.github.io/webrtc-stats/#pcstats-dict*
// TODO(hbos): Tracking bug crbug.com/636818
-class RTCPeerConnectionStats : public RTCStats {
+class RTCPeerConnectionStats final : public RTCStats {
public:
WEBRTC_RTCSTATS_DECL();