From ab9f6e4dea4fa48f415e7c589209718918f8e3dd Mon Sep 17 00:00:00 2001 From: hbos Date: Fri, 7 Oct 2016 02:18:47 -0700 Subject: [PATCH] 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} --- webrtc/api/rtcstatscollector.cc | 70 +++++++++++++ webrtc/api/rtcstatscollector.h | 19 +++- webrtc/api/rtcstatscollector_unittest.cc | 122 +++++++++++++++++++++++ webrtc/api/stats/rtcstats_objects.h | 53 +++++++++- webrtc/stats/rtcstats_objects.cc | 74 ++++++++++++++ 5 files changed, 334 insertions(+), 4 deletions(-) diff --git a/webrtc/api/rtcstatscollector.cc b/webrtc/api/rtcstatscollector.cc index 084eab177c..2d858c00e0 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::Create( PeerConnection* pc, int64_t cache_lifetime_us) { return rtc::scoped_refptr( @@ -93,6 +108,8 @@ void RTCStatsCollector::ProducePartialResultsOnSignalingThread( 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::ProduceCertificateStatsFromSSLCertificateAndChain_s( } } +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 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(candidate.address().port()); + candidate_stats->protocol = candidate.protocol(); + candidate_stats->candidate_type = CandidateTypeToRTCIceCandidateType( + candidate.type()); + candidate_stats->priority = static_cast(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 2f5ed8610a..6443d1323b 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 @@ class RTCStatsCollector : public virtual rtc::RefCountInterface { void AddPartialResults_s(rtc::scoped_refptr 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 @@ class RTCStatsCollector : public virtual rtc::RefCountInterface { rtc::scoped_refptr 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 14abbd251a..7103864af0 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 @@ std::unique_ptr CreateFakeCertificateAndInfoFromDers( return info; } +std::unique_ptr CreateFakeCandidate( + const std::string& hostname, + int port, + const std::string& protocol, + const std::string& candidate_type, + uint32_t priority) { + std::unique_ptr 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 @@ class RTCStatsCollectorTestHelper : public SetSessionDescriptionObserver { 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 @@ class RTCStatsCollectorTest : public testing::Test { return callback->report(); } + void ExpectReportContainsCandidate( + const rtc::scoped_refptr& 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(); + else + candidate_stats = &stats->cast_to(); + EXPECT_EQ(*candidate_stats->ip, candidate.address().ipaddr().ToString()); + EXPECT_EQ(*candidate_stats->port, + static_cast(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(candidate.priority())); + // TODO(hbos): Define candidate_stats->url. crbug.com/632723 + EXPECT_FALSE(candidate_stats->url.is_defined()); + } + void ExpectReportContainsCertificateInfo( const rtc::scoped_refptr& report, const CertificateInfo& cert_info) { @@ -534,6 +578,84 @@ TEST_F(RTCStatsCollectorTest, CollectRTCCertificateStatsChain) { ExpectReportContainsCertificateInfo(report, *remote_certinfo.get()); } +TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidateStats) { + // Candidates in the first transport stats. + std::unique_ptr a_local_host = CreateFakeCandidate( + "1.2.3.4", 5, + "a_local_host's protocol", + cricket::LOCAL_PORT_TYPE, + 0); + std::unique_ptr a_remote_srflx = CreateFakeCandidate( + "6.7.8.9", 10, + "remote_srflx's protocol", + cricket::STUN_PORT_TYPE, + 1); + std::unique_ptr a_local_prflx = CreateFakeCandidate( + "11.12.13.14", 15, + "a_local_prflx's protocol", + cricket::PRFLX_PORT_TYPE, + 2); + std::unique_ptr 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 b_local = CreateFakeCandidate( + "42.42.42.42", 42, + "b_local's protocol", + cricket::LOCAL_PORT_TYPE, + 42); + std::unique_ptr 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 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 report = GetStatsReport(); diff --git a/webrtc/api/stats/rtcstats_objects.h b/webrtc/api/stats/rtcstats_objects.h index f816d48b55..e827d011ea 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 ip; + RTCStatsMember port; + RTCStatsMember protocol; + // TODO(hbos): Support enum types? "RTCStatsMember"? + RTCStatsMember candidate_type; + RTCStatsMember priority; + RTCStatsMember 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 @@ class RTCCertificateStats : public RTCStats { // 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(); diff --git a/webrtc/stats/rtcstats_objects.cc b/webrtc/stats/rtcstats_objects.cc index b90941a5c0..3c80404f9c 100644 --- a/webrtc/stats/rtcstats_objects.cc +++ b/webrtc/stats/rtcstats_objects.cc @@ -12,6 +12,80 @@ namespace webrtc { +const char* RTCIceCandidateType::kHost = "host"; +const char* RTCIceCandidateType::kSrflx = "srflx"; +const char* RTCIceCandidateType::kPrflx = "prflx"; +const char* RTCIceCandidateType::kRelay = "relay"; + +WEBRTC_RTCSTATS_IMPL(RTCIceCandidateStats, RTCStats, "ice-candidate", + &ip, + &port, + &protocol, + &candidate_type, + &priority, + &url); + +RTCIceCandidateStats::RTCIceCandidateStats( + const std::string& id, int64_t timestamp_us) + : RTCIceCandidateStats(std::string(id), timestamp_us) { +} + +RTCIceCandidateStats::RTCIceCandidateStats( + std::string&& id, int64_t timestamp_us) + : RTCStats(std::move(id), timestamp_us), + ip("ip"), + port("port"), + protocol("protocol"), + candidate_type("candidateType"), + priority("priority"), + url("url") { +} + +RTCIceCandidateStats::RTCIceCandidateStats(const RTCIceCandidateStats& other) + : RTCStats(other.id(), other.timestamp_us()), + ip(other.ip), + port(other.port), + protocol(other.protocol), + candidate_type(other.candidate_type), + priority(other.priority), + url(other.url) { +} + +RTCIceCandidateStats::~RTCIceCandidateStats() { +} + +const char RTCLocalIceCandidateStats::kType[] = "local-candidate"; + +RTCLocalIceCandidateStats::RTCLocalIceCandidateStats( + const std::string& id, int64_t timestamp_us) + : RTCIceCandidateStats(id, timestamp_us) { +} + +RTCLocalIceCandidateStats::RTCLocalIceCandidateStats( + std::string&& id, int64_t timestamp_us) + : RTCIceCandidateStats(std::move(id), timestamp_us) { +} + +const char* RTCLocalIceCandidateStats::type() const { + return kType; +} + +const char RTCRemoteIceCandidateStats::kType[] = "remote-candidate"; + +RTCRemoteIceCandidateStats::RTCRemoteIceCandidateStats( + const std::string& id, int64_t timestamp_us) + : RTCIceCandidateStats(id, timestamp_us) { +} + +RTCRemoteIceCandidateStats::RTCRemoteIceCandidateStats( + std::string&& id, int64_t timestamp_us) + : RTCIceCandidateStats(std::move(id), timestamp_us) { +} + +const char* RTCRemoteIceCandidateStats::type() const { + return kType; +} + WEBRTC_RTCSTATS_IMPL(RTCCertificateStats, RTCStats, "certificate", &fingerprint, &fingerprint_algorithm,