From 72a43a1d2cf3a667bbd218383aa975de72ed6804 Mon Sep 17 00:00:00 2001 From: Qingsi Wang Date: Tue, 20 Feb 2018 16:03:18 -0800 Subject: [PATCH] Collect packet loss and RTT stats of STUN binding requests. STUN candidates use STUN binding requests to keep NAT bindings open. Related stats including packet loss and RTT can be now collected via the legacy GetStats in PeerConnection. Bug: None Change-Id: I7b0eee1ccb07eb670a32ee303c9590047b25f31c Reviewed-on: https://webrtc-review.googlesource.com/54100 Commit-Queue: Qingsi Wang Reviewed-by: Taylor Brandstetter Cr-Commit-Position: refs/heads/master@{#22113} --- api/stats/rtcstats_objects.h | 2 ++ api/statstypes.cc | 11 +++++++ api/statstypes.h | 4 +++ p2p/base/fakeicetransport.h | 12 +++++--- p2p/base/icetransportinternal.h | 3 +- p2p/base/mockicetransport.h | 4 ++- p2p/base/p2ptransportchannel.cc | 19 ++++++++---- p2p/base/p2ptransportchannel.h | 3 +- p2p/base/p2ptransportchannel_unittest.cc | 4 ++- p2p/base/port.cc | 12 +++++++- p2p/base/port.h | 34 ++++++++++++++++++++- p2p/base/portallocator.cc | 20 +++++++++++++ p2p/base/portallocator.h | 12 ++++++++ p2p/base/portinterface.h | 4 +++ p2p/base/stunport.cc | 16 ++++++++-- p2p/base/stunport.h | 5 ++++ pc/jseptransport.cc | 2 +- pc/jseptransport.h | 1 + pc/peerconnection.cc | 6 ++++ pc/peerconnection.h | 1 + pc/peerconnectioninternal.h | 2 ++ pc/statscollector.cc | 38 ++++++++++++++++++++++-- pc/statscollector.h | 5 ++-- pc/test/fakepeerconnectionforstats.h | 4 +++ rtc_base/helpers.cc | 4 +++ rtc_base/helpers.h | 4 +++ 26 files changed, 208 insertions(+), 24 deletions(-) diff --git a/api/stats/rtcstats_objects.h b/api/stats/rtcstats_objects.h index a052c17383..8371543fad 100644 --- a/api/stats/rtcstats_objects.h +++ b/api/stats/rtcstats_objects.h @@ -183,6 +183,8 @@ class RTCIceCandidatePairStats final : public RTCStats { // TODO(hbos): |RTCStatsCollector| only collects candidates that are part of // ice candidate pairs, but there could be candidates not paired with anything. // crbug.com/632723 +// TODO(qingsi): Add the stats of STUN binding requests (keepalives) and collect +// them in the new PeerConnection::GetStats. class RTCIceCandidateStats : public RTCStats { public: WEBRTC_RTCSTATS_DECL(); diff --git a/api/statstypes.cc b/api/statstypes.cc index d1637e3ffd..8a8726530c 100644 --- a/api/statstypes.cc +++ b/api/statstypes.cc @@ -430,7 +430,9 @@ const char* StatsReport::Value::display_name() const { case kStatsValueNameBandwidthLimitedResolution: return "googBandwidthLimitedResolution"; // STUN ping related attributes. + // // TODO(zhihuang) Rename these stats to follow the standards. + // Connectivity checks. case kStatsValueNameSentPingRequestsTotal: return "requestsSent"; case kStatsValueNameSentPingRequestsBeforeFirstResponse: @@ -441,6 +443,15 @@ const char* StatsReport::Value::display_name() const { return "requestsReceived"; case kStatsValueNameRecvPingResponses: return "responsesReceived"; + // STUN Keepalive pings. + case kStatsValueNameSentStunKeepaliveRequests: + return "stunKeepaliveRequestsSent"; + case kStatsValueNameRecvStunKeepaliveResponses: + return "stunKeepaliveResponsesReceived"; + case kStatsValueNameStunKeepaliveRttTotal: + return "stunKeepaliveRttTotal"; + case kStatsValueNameStunKeepaliveRttSquaredTotal: + return "stunKeepaliveRttSquaredTotal"; // Candidate related attributes. Values are taken from // http://w3c.github.io/webrtc-stats/#rtcstatstype-enum*. diff --git a/api/statstypes.h b/api/statstypes.h index 2ba8ba5fd5..9dc6154997 100644 --- a/api/statstypes.h +++ b/api/statstypes.h @@ -129,6 +129,10 @@ class StatsReport { kStatsValueNameSentPingResponses, kStatsValueNameRecvPingRequests, kStatsValueNameRecvPingResponses, + kStatsValueNameSentStunKeepaliveRequests, + kStatsValueNameRecvStunKeepaliveResponses, + kStatsValueNameStunKeepaliveRttTotal, + kStatsValueNameStunKeepaliveRttSquaredTotal, // Internal StatsValue names. kStatsValueNameAccelerateRate, diff --git a/p2p/base/fakeicetransport.h b/p2p/base/fakeicetransport.h index 556a1cda03..427481e0e2 100644 --- a/p2p/base/fakeicetransport.h +++ b/p2p/base/fakeicetransport.h @@ -146,10 +146,14 @@ class FakeIceTransport : public IceTransportInternal { } void RemoveRemoteCandidate(const Candidate& candidate) override {} - bool GetStats(ConnectionInfos* infos) override { - ConnectionInfo info; - infos->clear(); - infos->push_back(info); + bool GetStats(ConnectionInfos* candidate_pair_stats_list, + CandidateStatsList* candidate_stats_list) override { + CandidateStats candidate_stats; + ConnectionInfo candidate_pair_stats; + candidate_stats_list->clear(); + candidate_stats_list->push_back(candidate_stats); + candidate_pair_stats_list->clear(); + candidate_pair_stats_list->push_back(candidate_pair_stats); return true; } diff --git a/p2p/base/icetransportinternal.h b/p2p/base/icetransportinternal.h index 13ba3316d9..192a9d06e0 100644 --- a/p2p/base/icetransportinternal.h +++ b/p2p/base/icetransportinternal.h @@ -201,7 +201,8 @@ class IceTransportInternal : public rtc::PacketTransportInternal { virtual IceGatheringState gathering_state() const = 0; // Returns the current stats for this connection. - virtual bool GetStats(ConnectionInfos* infos) = 0; + virtual bool GetStats(ConnectionInfos* candidate_pair_stats_list, + CandidateStatsList* candidate_stats_list) = 0; // Returns RTT estimate over the currently active connection, or an empty // rtc::Optional if there is none. diff --git a/p2p/base/mockicetransport.h b/p2p/base/mockicetransport.h index 3e7aed7297..c30d797fdf 100644 --- a/p2p/base/mockicetransport.h +++ b/p2p/base/mockicetransport.h @@ -40,7 +40,9 @@ class MockIceTransport : public IceTransportInternal { MOCK_METHOD2(SetOption, int(rtc::Socket::Option opt, int value)); MOCK_METHOD0(GetError, int()); MOCK_CONST_METHOD0(GetIceRole, cricket::IceRole()); - MOCK_METHOD1(GetStats, bool(cricket::ConnectionInfos* infos)); + MOCK_METHOD2(GetStats, + bool(cricket::ConnectionInfos* candidate_pair_stats_list, + cricket::CandidateStatsList* candidate_stats_list)); IceTransportState GetState() const override { return IceTransportState::STATE_INIT; diff --git a/p2p/base/p2ptransportchannel.cc b/p2p/base/p2ptransportchannel.cc index 2ad4114c27..414ff2b764 100644 --- a/p2p/base/p2ptransportchannel.cc +++ b/p2p/base/p2ptransportchannel.cc @@ -1136,15 +1136,22 @@ int P2PTransportChannel::SendPacket(const char *data, size_t len, return sent; } -bool P2PTransportChannel::GetStats(ConnectionInfos *infos) { +bool P2PTransportChannel::GetStats(ConnectionInfos* candidate_pair_stats_list, + CandidateStatsList* candidate_stats_list) { RTC_DCHECK(network_thread_ == rtc::Thread::Current()); - // Gather connection infos. - infos->clear(); + // Gather candidate and candidate pair stats. + candidate_stats_list->clear(); + candidate_pair_stats_list->clear(); + if (!allocator_sessions_.empty()) { + allocator_session()->GetCandidateStatsFromReadyPorts(candidate_stats_list); + } + + // TODO(qingsi): Remove naming inconsistency for candidate pair/connection. for (Connection* connection : connections_) { - ConnectionInfo info = connection->stats(); - info.best_connection = (selected_connection_ == connection); - infos->push_back(std::move(info)); + ConnectionInfo candidate_pair_stats = connection->stats(); + candidate_pair_stats.best_connection = (selected_connection_ == connection); + candidate_pair_stats_list->push_back(std::move(candidate_pair_stats)); connection->set_reported(true); } diff --git a/p2p/base/p2ptransportchannel.h b/p2p/base/p2ptransportchannel.h index af5625a4b4..e4db38cb42 100644 --- a/p2p/base/p2ptransportchannel.h +++ b/p2p/base/p2ptransportchannel.h @@ -118,7 +118,8 @@ class P2PTransportChannel : public IceTransportInternal, int SetOption(rtc::Socket::Option opt, int value) override; bool GetOption(rtc::Socket::Option opt, int* value) override; int GetError() override; - bool GetStats(std::vector* stats) override; + bool GetStats(std::vector* candidate_pair_stats_list, + std::vector* candidate_stats_list) override; rtc::Optional GetRttEstimate() override; // TODO(honghaiz): Remove this method once the reference of it in diff --git a/p2p/base/p2ptransportchannel_unittest.cc b/p2p/base/p2ptransportchannel_unittest.cc index fc2b65c8b3..71c648afa9 100644 --- a/p2p/base/p2ptransportchannel_unittest.cc +++ b/p2p/base/p2ptransportchannel_unittest.cc @@ -1176,8 +1176,10 @@ TEST_F(P2PTransportChannelTest, GetStats) { kMediumTimeout, clock); TestSendRecv(&clock); ConnectionInfos infos; - ASSERT_TRUE(ep1_ch1()->GetStats(&infos)); + CandidateStatsList candidate_stats_list; + ASSERT_TRUE(ep1_ch1()->GetStats(&infos, &candidate_stats_list)); ASSERT_GE(infos.size(), 1u); + ASSERT_GE(candidate_stats_list.size(), 1u); ConnectionInfo* best_conn_info = nullptr; for (ConnectionInfo& info : infos) { if (info.best_connection) { diff --git a/p2p/base/port.cc b/p2p/base/port.cc index 5f5153facb..b142865b75 100644 --- a/p2p/base/port.cc +++ b/p2p/base/port.cc @@ -193,6 +193,16 @@ static std::string ComputeFoundation(const std::string& type, return rtc::ToString(rtc::ComputeCrc32(ost.str())); } +CandidateStats::CandidateStats() = default; + +CandidateStats::CandidateStats(const CandidateStats&) = default; + +CandidateStats::CandidateStats(Candidate candidate) { + this->candidate = candidate; +} + +CandidateStats::~CandidateStats() = default; + ConnectionInfo::ConnectionInfo() : best_connection(false), writable(false), @@ -1428,7 +1438,7 @@ void Connection::ReceivedPingResponse(int rtt, const std::string& request_id) { set_write_state(STATE_WRITABLE); set_state(IceCandidatePairState::SUCCEEDED); if (rtt_samples_ > 0) { - rtt_ = (RTT_RATIO * rtt_ + rtt) / (RTT_RATIO + 1); + rtt_ = rtc::GetNextMovingAverage(rtt_, rtt, RTT_RATIO); } else { rtt_ = rtt; } diff --git a/p2p/base/port.h b/p2p/base/port.h index 8dbeccec47..16fedb8148 100644 --- a/p2p/base/port.h +++ b/p2p/base/port.h @@ -106,6 +106,36 @@ enum class IceCandidatePairState { // frozen because we have not implemented ICE freezing logic. }; +// Stats that we can return about the port of a connection. +class StunStats { + public: + StunStats() = default; + StunStats(const StunStats&) = default; + ~StunStats() = default; + + StunStats& operator=(const StunStats& other) = default; + + int stun_binding_requests_sent = 0; + int stun_binding_responses_received = 0; + double stun_binding_rtt_ms_total = 0; + double stun_binding_rtt_ms_squared_total = 0; +}; + +// Stats that we can return about a candidate. +class CandidateStats { + public: + CandidateStats(); + explicit CandidateStats(Candidate candidate); + CandidateStats(const CandidateStats&); + ~CandidateStats(); + + Candidate candidate; + // STUN port stats if this candidate is a STUN candidate. + rtc::Optional stun_stats; +}; + +typedef std::vector CandidateStatsList; + // Stats that we can return about the connections for a transport channel. // TODO(hta): Rename to ConnectionStats struct ConnectionInfo { @@ -149,7 +179,7 @@ struct ConnectionInfo { rtc::Optional current_round_trip_time_ms; }; -// Information about all the connections of a channel. +// Information about all the candidate pairs of a channel. typedef std::vector ConnectionInfos; const char* ProtoToString(ProtocolType proto); @@ -368,6 +398,8 @@ class Port : public PortInterface, public rtc::MessageHandler, int16_t network_cost() const { return network_cost_; } + void GetStunStats(rtc::Optional* stats) override{}; + protected: enum { MSG_DESTROY_IF_DEAD = 0, MSG_FIRST_AVAILABLE }; diff --git a/p2p/base/portallocator.cc b/p2p/base/portallocator.cc index 6ef084ca63..2b9b183f01 100644 --- a/p2p/base/portallocator.cc +++ b/p2p/base/portallocator.cc @@ -79,6 +79,19 @@ bool PortAllocatorSession::IsStopped() const { return false; } +void PortAllocatorSession::GetCandidateStatsFromReadyPorts( + CandidateStatsList* candidate_stats_list) const { + auto ports = ReadyPorts(); + for (auto* port : ports) { + auto candidates = port->Candidates(); + for (const auto& candidate : candidates) { + CandidateStats candidate_stats(candidate); + port->GetStunStats(&candidate_stats.stun_stats); + candidate_stats_list->push_back(std::move(candidate_stats)); + } + } +} + uint32_t PortAllocatorSession::generation() { return generation_; } @@ -210,4 +223,11 @@ void PortAllocator::DiscardCandidatePool() { pooled_sessions_.clear(); } +void PortAllocator::GetCandidateStatsFromPooledSessions( + CandidateStatsList* candidate_stats_list) { + for (const auto& session : pooled_sessions()) { + session->GetCandidateStatsFromReadyPorts(candidate_stats_list); + } +} + } // namespace cricket diff --git a/p2p/base/portallocator.h b/p2p/base/portallocator.h index f32fa07476..95052c2db1 100644 --- a/p2p/base/portallocator.h +++ b/p2p/base/portallocator.h @@ -244,6 +244,10 @@ class PortAllocatorSession : public sigslot::has_slots<> { virtual void RegatherOnFailedNetworks() {} // Re-gathers candidates on all networks. virtual void RegatherOnAllNetworks() {} + // Get candidate-level stats from all candidates on the ready ports and return + // the stats to the given list. + virtual void GetCandidateStatsFromReadyPorts( + CandidateStatsList* candidate_stats_list) const; // Set the interval at which STUN candidates will resend STUN binding requests // on the underlying ports to keep NAT bindings open. // The default value of the interval in implementation is restored if a null @@ -476,6 +480,14 @@ class PortAllocator : public sigslot::has_slots<> { return turn_customizer_; } + // Collect candidate stats from pooled allocator sessions. This can be used to + // collect candidate stats without creating an offer/answer or setting local + // description. After the local description is set, the ownership of the + // pooled session is taken by P2PTransportChannel, and the + // candidate stats can be collected from P2PTransportChannel::GetStats. + virtual void GetCandidateStatsFromPooledSessions( + CandidateStatsList* candidate_stats_list); + protected: virtual PortAllocatorSession* CreateSessionInternal( const std::string& content_name, diff --git a/p2p/base/portinterface.h b/p2p/base/portinterface.h index e34e39abc3..49a41647cd 100644 --- a/p2p/base/portinterface.h +++ b/p2p/base/portinterface.h @@ -15,6 +15,7 @@ #include #include "api/candidate.h" +#include "api/optional.h" #include "p2p/base/transportdescription.h" #include "rtc_base/asyncpacketsocket.h" #include "rtc_base/socketaddress.h" @@ -28,6 +29,7 @@ namespace cricket { class Connection; class IceMessage; class StunMessage; +class StunStats; enum ProtocolType { PROTO_UDP, @@ -125,6 +127,8 @@ class PortInterface { virtual std::string ToString() const = 0; + virtual void GetStunStats(rtc::Optional* stats) = 0; + protected: PortInterface(); }; diff --git a/p2p/base/stunport.cc b/p2p/base/stunport.cc index 2df14b7c62..3ea1f96a7f 100644 --- a/p2p/base/stunport.cc +++ b/p2p/base/stunport.cc @@ -52,7 +52,7 @@ class StunBindingRequest : public StunRequest { RTC_LOG(LS_ERROR) << "Binding address has bad family"; } else { rtc::SocketAddress addr(addr_attr->ipaddr(), addr_attr->port()); - port_->OnStunBindingRequestSucceeded(server_addr_, addr); + port_->OnStunBindingRequestSucceeded(this->Elapsed(), server_addr_, addr); } // The keep-alive requests will be stopped after its lifetime has passed. @@ -317,6 +317,10 @@ ProtocolType UDPPort::GetProtocol() const { return PROTO_UDP; } +void UDPPort::GetStunStats(rtc::Optional* stats) { + *stats = stats_; +} + void UDPPort::set_stun_keepalive_delay(const rtc::Optional& delay) { stun_keepalive_delay_ = (delay.has_value() ? delay.value() : KEEPALIVE_DELAY); } @@ -450,6 +454,7 @@ bool UDPPort::MaybeSetDefaultLocalAddress(rtc::SocketAddress* addr) const { } void UDPPort::OnStunBindingRequestSucceeded( + int rtt_ms, const rtc::SocketAddress& stun_server_addr, const rtc::SocketAddress& stun_reflected_addr) { if (bind_request_succeeded_servers_.find(stun_server_addr) != @@ -458,6 +463,11 @@ void UDPPort::OnStunBindingRequestSucceeded( } bind_request_succeeded_servers_.insert(stun_server_addr); + RTC_DCHECK(stats_.stun_binding_responses_received < + stats_.stun_binding_requests_sent); + stats_.stun_binding_responses_received++; + stats_.stun_binding_rtt_ms_total += rtt_ms; + stats_.stun_binding_rtt_ms_squared_total += rtt_ms * rtt_ms; // If socket is shared and |stun_reflected_addr| is equal to local socket // address, or if the same address has been added by another STUN server, // then discarding the stun address. @@ -520,8 +530,10 @@ void UDPPort::MaybeSetPortCompleteOrError() { void UDPPort::OnSendPacket(const void* data, size_t size, StunRequest* req) { StunBindingRequest* sreq = static_cast(req); rtc::PacketOptions options(DefaultDscpValue()); - if (socket_->SendTo(data, size, sreq->server_addr(), options) < 0) + if (socket_->SendTo(data, size, sreq->server_addr(), options) < 0) { RTC_LOG_ERR_EX(LERROR, socket_->GetError()) << "sendto"; + } + stats_.stun_binding_requests_sent++; } bool UDPPort::HasCandidateWithAddress(const rtc::SocketAddress& addr) const { diff --git a/p2p/base/stunport.h b/p2p/base/stunport.h index eaeea3ae91..72afbd3735 100644 --- a/p2p/base/stunport.h +++ b/p2p/base/stunport.h @@ -101,6 +101,8 @@ class UDPPort : public Port { bool SupportsProtocol(const std::string& protocol) const override; ProtocolType GetProtocol() const override; + void GetStunStats(rtc::Optional* stats) override; + void set_stun_keepalive_delay(const rtc::Optional& delay); int stun_keepalive_delay() const { return stun_keepalive_delay_; @@ -206,6 +208,7 @@ class UDPPort : public Port { // Below methods handles binding request responses. void OnStunBindingRequestSucceeded( + int rtt_ms, const rtc::SocketAddress& stun_server_addr, const rtc::SocketAddress& stun_reflected_addr); void OnStunBindingOrResolveRequestFailed( @@ -240,6 +243,8 @@ class UDPPort : public Port { int stun_keepalive_delay_; int stun_keepalive_lifetime_ = INFINITE_LIFETIME; + StunStats stats_; + // This is true by default and false when // PORTALLOCATOR_DISABLE_DEFAULT_LOCAL_CANDIDATE is specified. bool emit_local_for_anyaddress_; diff --git a/pc/jseptransport.cc b/pc/jseptransport.cc index 12aad7a3f0..a6a6135b4d 100644 --- a/pc/jseptransport.cc +++ b/pc/jseptransport.cc @@ -234,7 +234,7 @@ bool JsepTransport::GetStats(TransportStats* stats) { dtls_transport->GetSslCipherSuite(&substats.ssl_cipher_suite); substats.dtls_state = dtls_transport->dtls_state(); if (!dtls_transport->ice_transport()->GetStats( - &substats.connection_infos)) { + &substats.connection_infos, &substats.candidate_stats_list)) { return false; } stats->channel_stats.push_back(substats); diff --git a/pc/jseptransport.h b/pc/jseptransport.h index 972f91bf5b..719ff7a031 100644 --- a/pc/jseptransport.h +++ b/pc/jseptransport.h @@ -39,6 +39,7 @@ struct TransportChannelStats { ~TransportChannelStats(); int component = 0; + CandidateStatsList candidate_stats_list; ConnectionInfos connection_infos; int srtp_crypto_suite = rtc::SRTP_INVALID_CRYPTO_SUITE; int ssl_cipher_suite = rtc::TLS_NULL_WITH_NULL_NULL; diff --git a/pc/peerconnection.cc b/pc/peerconnection.cc index 533e64845e..aa7146b9b7 100644 --- a/pc/peerconnection.cc +++ b/pc/peerconnection.cc @@ -4937,6 +4937,12 @@ bool PeerConnection::ReadyToSendData() const { sctp_ready_to_send_data_; } +cricket::CandidateStatsList PeerConnection::GetPooledCandidateStats() const { + cricket::CandidateStatsList candidate_states_list; + port_allocator_->GetCandidateStatsFromPooledSessions(&candidate_states_list); + return candidate_states_list; +} + std::map PeerConnection::GetTransportNamesByMid() const { std::map transport_names_by_mid; diff --git a/pc/peerconnection.h b/pc/peerconnection.h index f1dc2996a8..e6ced417c2 100644 --- a/pc/peerconnection.h +++ b/pc/peerconnection.h @@ -233,6 +233,7 @@ class PeerConnection : public PeerConnectionInternal, return sctp_transport_name_; } + cricket::CandidateStatsList GetPooledCandidateStats() const override; std::map GetTransportNamesByMid() const override; std::map GetTransportStatsByNames( const std::set& transport_names) override; diff --git a/pc/peerconnectioninternal.h b/pc/peerconnectioninternal.h index 4bca65ee24..8cf4989976 100644 --- a/pc/peerconnectioninternal.h +++ b/pc/peerconnectioninternal.h @@ -56,6 +56,8 @@ class PeerConnectionInternal : public PeerConnectionInterface { virtual rtc::Optional sctp_content_name() const = 0; virtual rtc::Optional sctp_transport_name() const = 0; + virtual cricket::CandidateStatsList GetPooledCandidateStats() const = 0; + // Returns a map from MID to transport name for all active media sections. virtual std::map GetTransportNamesByMid() const = 0; diff --git a/pc/statscollector.cc b/pc/statscollector.cc index 9a636b47b1..e7fb95b76a 100644 --- a/pc/statscollector.cc +++ b/pc/statscollector.cc @@ -670,10 +670,12 @@ StatsReport* StatsCollector::AddConnectionInfoReport( report->AddBoolean(b.name, b.value); report->AddId(StatsReport::kStatsValueNameChannelId, channel_report_id); + cricket::CandidateStats local_candidate_stats(info.local_candidate); + cricket::CandidateStats remote_candidate_stats(info.remote_candidate); report->AddId(StatsReport::kStatsValueNameLocalCandidateId, - AddCandidateReport(info.local_candidate, true)->id()); + AddCandidateReport(local_candidate_stats, true)->id()); report->AddId(StatsReport::kStatsValueNameRemoteCandidateId, - AddCandidateReport(info.remote_candidate, false)->id()); + AddCandidateReport(remote_candidate_stats, false)->id()); const Int64ForAdd int64s[] = { {StatsReport::kStatsValueNameBytesReceived, info.recv_total_bytes}, @@ -708,8 +710,9 @@ StatsReport* StatsCollector::AddConnectionInfoReport( } StatsReport* StatsCollector::AddCandidateReport( - const cricket::Candidate& candidate, + const cricket::CandidateStats& candidate_stats, bool local) { + const auto& candidate = candidate_stats.candidate; StatsReport::Id id(StatsReport::NewCandidateId(local, candidate.id())); StatsReport* report = reports_.Find(id); if (!report) { @@ -718,6 +721,18 @@ StatsReport* StatsCollector::AddCandidateReport( if (local) { report->AddString(StatsReport::kStatsValueNameCandidateNetworkType, AdapterTypeToStatsType(candidate.network_type())); + if (candidate_stats.stun_stats.has_value()) { + const auto& stun_stats = candidate_stats.stun_stats.value(); + report->AddInt64(StatsReport::kStatsValueNameSentStunKeepaliveRequests, + stun_stats.stun_binding_requests_sent); + report->AddInt64(StatsReport::kStatsValueNameRecvStunKeepaliveResponses, + stun_stats.stun_binding_responses_received); + report->AddFloat(StatsReport::kStatsValueNameStunKeepaliveRttTotal, + stun_stats.stun_binding_rtt_ms_total); + report->AddFloat( + StatsReport::kStatsValueNameStunKeepaliveRttSquaredTotal, + stun_stats.stun_binding_rtt_ms_squared_total); + } } report->AddString(StatsReport::kStatsValueNameCandidateIPAddress, candidate.address().ipaddr().ToString()); @@ -745,6 +760,13 @@ void StatsCollector::ExtractSessionInfo() { report->AddBoolean(StatsReport::kStatsValueNameInitiator, pc_->initial_offerer()); + cricket::CandidateStatsList pooled_candidate_stats_list = + pc_->GetPooledCandidateStats(); + + for (const cricket::CandidateStats& stats : pooled_candidate_stats_list) { + AddCandidateReport(stats, true); + } + std::set transport_names; for (const auto& entry : pc_->GetTransportNamesByMid()) { transport_names.insert(entry.second); @@ -808,6 +830,16 @@ void StatsCollector::ExtractSessionInfo() { rtc::SSLStreamAdapter::SslCipherSuiteToName(ssl_cipher_suite)); } + // Collect stats for non-pooled candidates. Note that the reports + // generated here supersedes the candidate reports generated in + // AddConnectionInfoReport below, and they may report candidates that are + // not paired. Also, the candidate report generated in + // AddConnectionInfoReport do not report port stats like StunStats. + for (const cricket::CandidateStats& stats : + channel_iter.candidate_stats_list) { + AddCandidateReport(stats, true); + } + int connection_id = 0; for (const cricket::ConnectionInfo& info : channel_iter.connection_infos) { diff --git a/pc/statscollector.h b/pc/statscollector.h index 681a8f0098..abd65a97f9 100644 --- a/pc/statscollector.h +++ b/pc/statscollector.h @@ -96,8 +96,9 @@ class StatsCollector { // Helper method for creating IceCandidate report. |is_local| indicates // whether this candidate is local or remote. - StatsReport* AddCandidateReport(const cricket::Candidate& candidate, - bool local); + StatsReport* AddCandidateReport( + const cricket::CandidateStats& candidate_stats, + bool local); // Adds a report for this certificate and every certificate in its chain, and // returns the leaf certificate's report (|cert|'s report). diff --git a/pc/test/fakepeerconnectionforstats.h b/pc/test/fakepeerconnectionforstats.h index c198d1de5a..c26d144ca4 100644 --- a/pc/test/fakepeerconnectionforstats.h +++ b/pc/test/fakepeerconnectionforstats.h @@ -272,6 +272,10 @@ class FakePeerConnectionForStats : public FakePeerConnectionBase { return sctp_data_channels_; } + cricket::CandidateStatsList GetPooledCandidateStats() const override { + return {}; + } + std::map GetTransportNamesByMid() const override { std::map transport_names_by_mid; if (voice_channel_) { diff --git a/rtc_base/helpers.cc b/rtc_base/helpers.cc index b0c8856615..9cb9268792 100644 --- a/rtc_base/helpers.cc +++ b/rtc_base/helpers.cc @@ -215,4 +215,8 @@ double CreateRandomDouble() { std::numeric_limits::epsilon()); } +double GetNextMovingAverage(double prev_average, double cur, double ratio) { + return (ratio * prev_average + cur) / (ratio + 1); +} + } // namespace rtc diff --git a/rtc_base/helpers.h b/rtc_base/helpers.h index e1d2ebb2aa..0100794154 100644 --- a/rtc_base/helpers.h +++ b/rtc_base/helpers.h @@ -59,6 +59,10 @@ uint32_t CreateRandomNonZeroId(); // Generates a random double between 0.0 (inclusive) and 1.0 (exclusive). double CreateRandomDouble(); +// Compute moving average with the given ratio between the previous average +// value and the current value. +double GetNextMovingAverage(double prev_average, double cur, double ratio); + } // namespace rtc #endif // RTC_BASE_HELPERS_H_