diff --git a/webrtc/api/rtcstatscollector.cc b/webrtc/api/rtcstatscollector.cc index 2d858c00e0..5f60ea13ce 100644 --- a/webrtc/api/rtcstatscollector.cc +++ b/webrtc/api/rtcstatscollector.cc @@ -180,16 +180,16 @@ void RTCStatsCollector::ProduceCertificateStats_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& transport_stats : session_stats.transport_stats) { rtc::scoped_refptr local_certificate; if (pc_->session()->GetLocalCertificate( - transport.second.transport_name, &local_certificate)) { + transport_stats.second.transport_name, &local_certificate)) { ProduceCertificateStatsFromSSLCertificateAndChain_s( timestamp_us, local_certificate->ssl_certificate(), report); } std::unique_ptr remote_certificate = pc_->session()->GetRemoteSSLCertificate( - transport.second.transport_name); + transport_stats.second.transport_name); if (remote_certificate) { ProduceCertificateStatsFromSSLCertificateAndChain_s( timestamp_us, *remote_certificate.get(), report); @@ -222,19 +222,65 @@ 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 + for (const auto& transport_stats : session_stats.transport_stats) { + for (const auto& channel_stats : transport_stats.second.channel_stats) { + for (const cricket::ConnectionInfo& info : + channel_stats.connection_infos) { + const std::string& id = "RTCIceCandidatePair_" + + info.local_candidate.id() + "_" + info.remote_candidate.id(); + std::unique_ptr candidate_pair_stats( + new RTCIceCandidatePairStats(id, timestamp_us)); + + // TODO(hbos): Set all of the |RTCIceCandidatePairStats|'s members, + // crbug.com/633550. + + // TODO(hbos): Set candidate_pair_stats->transport_id. Should be ID to + // RTCTransportStats which does not exist yet: crbug.com/653873. + // 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( + candidate_pair_stats->local_candidate_id = ProduceIceCandidateStats_s( timestamp_us, info.local_candidate, true, report); - ProduceIceCandidateStats_s( + candidate_pair_stats->remote_candidate_id = ProduceIceCandidateStats_s( timestamp_us, info.remote_candidate, false, report); + + // TODO(hbos): Set candidate_pair_stats->state. + // TODO(hbos): Set candidate_pair_stats->priority. + // TODO(hbos): Set candidate_pair_stats->nominated. + // TODO(hbos): This writable is different than the spec. It goes to + // false after a certain amount of time without a response passes. + // crbug.com/633550 + candidate_pair_stats->writable = info.writable; + // TODO(hbos): Set candidate_pair_stats->readable. + candidate_pair_stats->bytes_sent = + static_cast(info.sent_total_bytes); + candidate_pair_stats->bytes_received = + static_cast(info.recv_total_bytes); + // TODO(hbos): Set candidate_pair_stats->total_rtt. + // TODO(hbos): The |info.rtt| measurement is smoothed. It shouldn't be + // smoothed according to the spec. crbug.com/633550. See + // https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-currentrtt + candidate_pair_stats->current_rtt = + static_cast(info.rtt) / 1000.0; + // TODO(hbos): Set candidate_pair_stats->available_outgoing_bitrate. + // TODO(hbos): Set candidate_pair_stats->available_incoming_bitrate. + // TODO(hbos): Set candidate_pair_stats->requests_received. + candidate_pair_stats->requests_sent = + static_cast(info.sent_ping_requests_total); + candidate_pair_stats->responses_received = + static_cast(info.recv_ping_responses); + candidate_pair_stats->responses_sent = + static_cast(info.sent_ping_responses); + // TODO(hbos): Set candidate_pair_stats->retransmissions_received. + // TODO(hbos): Set candidate_pair_stats->retransmissions_sent. + // TODO(hbos): Set candidate_pair_stats->consent_requests_received. + // TODO(hbos): Set candidate_pair_stats->consent_requests_sent. + // TODO(hbos): Set candidate_pair_stats->consent_responses_received. + // TODO(hbos): Set candidate_pair_stats->consent_responses_sent. + + report->AddStats(std::move(candidate_pair_stats)); } } } @@ -248,13 +294,10 @@ const std::string& RTCStatsCollector::ProduceIceCandidateStats_s( 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)); - } + 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(); diff --git a/webrtc/api/rtcstatscollector.h b/webrtc/api/rtcstatscollector.h index 6443d1323b..712f9e2949 100644 --- a/webrtc/api/rtcstatscollector.h +++ b/webrtc/api/rtcstatscollector.h @@ -85,8 +85,7 @@ class RTCStatsCollector : public virtual rtc::RefCountInterface { 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 + // Produces |RTCIceCandidatePairStats| and |RTCIceCandidateStats|. void ProduceIceCandidateAndPairStats_s( int64_t timestamp_us, const SessionStats& session_stats, RTCStatsReport* report) const; diff --git a/webrtc/api/rtcstatscollector_unittest.cc b/webrtc/api/rtcstatscollector_unittest.cc index 0da772fb97..c8eed9febb 100644 --- a/webrtc/api/rtcstatscollector_unittest.cc +++ b/webrtc/api/rtcstatscollector_unittest.cc @@ -331,12 +331,11 @@ class RTCStatsCollectorTest : public testing::Test { return callback->report(); } - void ExpectReportContainsCandidate( + const RTCIceCandidateStats* ExpectReportContainsCandidate( const rtc::scoped_refptr& report, const cricket::Candidate& candidate, bool is_local) { - const RTCStats* stats = - report->Get("RTCIceCandidate_" + candidate.id()); + const RTCStats* stats = report->Get("RTCIceCandidate_" + candidate.id()); EXPECT_TRUE(stats); const RTCIceCandidateStats* candidate_stats; if (is_local) @@ -353,6 +352,69 @@ class RTCStatsCollectorTest : public testing::Test { static_cast(candidate.priority())); // TODO(hbos): Define candidate_stats->url. crbug.com/632723 EXPECT_FALSE(candidate_stats->url.is_defined()); + return candidate_stats; + } + + void ExpectReportContainsCandidatePair( + const rtc::scoped_refptr& report, + const cricket::TransportStats& transport_stats) { + for (const auto& channel_stats : transport_stats.channel_stats) { + for (const cricket::ConnectionInfo& info : + channel_stats.connection_infos) { + const std::string& id = "RTCIceCandidatePair_" + + info.local_candidate.id() + "_" + info.remote_candidate.id(); + const RTCStats* stats = report->Get(id); + EXPECT_TRUE(stats); + const RTCIceCandidatePairStats& candidate_pair_stats = + stats->cast_to(); + + // TODO(hbos): Define all the undefined |candidate_pair_stats| stats. + // The EXPECT_FALSE are for the undefined stats, see also todos listed + // in rtcstatscollector.cc. crbug.com/633550 + EXPECT_FALSE(candidate_pair_stats.transport_id.is_defined()); + const RTCIceCandidateStats* local_candidate = + ExpectReportContainsCandidate(report, info.local_candidate, true); + EXPECT_EQ(*candidate_pair_stats.local_candidate_id, + local_candidate->id()); + const RTCIceCandidateStats* remote_candidate = + ExpectReportContainsCandidate(report, info.remote_candidate, false); + EXPECT_EQ(*candidate_pair_stats.remote_candidate_id, + remote_candidate->id()); + + EXPECT_FALSE(candidate_pair_stats.state.is_defined()); + EXPECT_FALSE(candidate_pair_stats.priority.is_defined()); + EXPECT_FALSE(candidate_pair_stats.nominated.is_defined()); + EXPECT_EQ(*candidate_pair_stats.writable, info.writable); + EXPECT_FALSE(candidate_pair_stats.readable.is_defined()); + EXPECT_EQ(*candidate_pair_stats.bytes_sent, + static_cast(info.sent_total_bytes)); + EXPECT_EQ(*candidate_pair_stats.bytes_received, + static_cast(info.recv_total_bytes)); + EXPECT_FALSE(candidate_pair_stats.total_rtt.is_defined()); + EXPECT_EQ(*candidate_pair_stats.current_rtt, + static_cast(info.rtt) / 1000.0); + EXPECT_FALSE( + candidate_pair_stats.available_outgoing_bitrate.is_defined()); + EXPECT_FALSE( + candidate_pair_stats.available_incoming_bitrate.is_defined()); + EXPECT_FALSE(candidate_pair_stats.requests_received.is_defined()); + EXPECT_EQ(*candidate_pair_stats.requests_sent, + static_cast(info.sent_ping_requests_total)); + EXPECT_EQ(*candidate_pair_stats.responses_received, + static_cast(info.recv_ping_responses)); + EXPECT_EQ(*candidate_pair_stats.responses_sent, + static_cast(info.sent_ping_responses)); + EXPECT_FALSE( + candidate_pair_stats.retransmissions_received.is_defined()); + EXPECT_FALSE(candidate_pair_stats.retransmissions_sent.is_defined()); + EXPECT_FALSE( + candidate_pair_stats.consent_requests_received.is_defined()); + EXPECT_FALSE(candidate_pair_stats.consent_requests_sent.is_defined()); + EXPECT_FALSE( + candidate_pair_stats.consent_responses_received.is_defined()); + EXPECT_FALSE(candidate_pair_stats.consent_responses_sent.is_defined()); + } + } } void ExpectReportContainsCertificateInfo( @@ -659,6 +721,43 @@ TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidateStats) { ExpectReportContainsCandidate(report, *b_remote.get(), false); } +TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidatePairStats) { + std::unique_ptr local_candidate = CreateFakeCandidate( + "42.42.42.42", 42, "protocol", cricket::LOCAL_PORT_TYPE, 42); + std::unique_ptr remote_candidate = CreateFakeCandidate( + "42.42.42.42", 42, "protocol", cricket::LOCAL_PORT_TYPE, 42); + + SessionStats session_stats; + + cricket::ConnectionInfo connection_info; + connection_info.local_candidate = *local_candidate.get(); + connection_info.remote_candidate = *remote_candidate.get(); + connection_info.writable = true; + connection_info.sent_total_bytes = 42; + connection_info.recv_total_bytes = 1234; + connection_info.rtt = 1337; + connection_info.sent_ping_requests_total = 1010; + connection_info.recv_ping_responses = 4321; + connection_info.sent_ping_responses = 1000; + + cricket::TransportChannelStats transport_channel_stats; + transport_channel_stats.connection_infos.push_back(connection_info); + session_stats.transport_stats["transport"].transport_name = "transport"; + session_stats.transport_stats["transport"].channel_stats.push_back( + 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(); + ExpectReportContainsCandidatePair( + report, session_stats.transport_stats["transport"]); +} + 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 e827d011ea..4738fe89a8 100644 --- a/webrtc/api/stats/rtcstats_objects.h +++ b/webrtc/api/stats/rtcstats_objects.h @@ -17,6 +17,16 @@ namespace webrtc { +// https://w3c.github.io/webrtc-stats/#dom-rtcstatsicecandidatepairstate +struct RTCStatsIceCandidatePairState { + static const char* kFrozen; + static const char* kWaiting; + static const char* kInProgress; + static const char* kFailed; + static const char* kSucceeded; + static const char* kCancelled; +}; + // https://www.w3.org/TR/webrtc/#rtcicecandidatetype-enum struct RTCIceCandidateType { static const char* kHost; @@ -25,6 +35,43 @@ struct RTCIceCandidateType { static const char* kRelay; }; +class RTCIceCandidatePairStats : public RTCStats { + public: + WEBRTC_RTCSTATS_DECL(); + + RTCIceCandidatePairStats(const std::string& id, int64_t timestamp_us); + RTCIceCandidatePairStats(std::string&& id, int64_t timestamp_us); + RTCIceCandidatePairStats(const RTCIceCandidatePairStats& other); + ~RTCIceCandidatePairStats() override; + + RTCStatsMember transport_id; + RTCStatsMember local_candidate_id; + RTCStatsMember remote_candidate_id; + // TODO(hbos): Support enum types? + // "RTCStatsMember"? + RTCStatsMember state; + RTCStatsMember priority; + RTCStatsMember nominated; + RTCStatsMember writable; + RTCStatsMember readable; + RTCStatsMember bytes_sent; + RTCStatsMember bytes_received; + RTCStatsMember total_rtt; + RTCStatsMember current_rtt; + RTCStatsMember available_outgoing_bitrate; + RTCStatsMember available_incoming_bitrate; + RTCStatsMember requests_received; + RTCStatsMember requests_sent; + RTCStatsMember responses_received; + RTCStatsMember responses_sent; + RTCStatsMember retransmissions_received; + RTCStatsMember retransmissions_sent; + RTCStatsMember consent_requests_received; + RTCStatsMember consent_requests_sent; + RTCStatsMember consent_responses_received; + RTCStatsMember consent_responses_sent; +}; + // https://w3c.github.io/webrtc-stats/#icecandidate-dict* class RTCIceCandidateStats : public RTCStats { public: diff --git a/webrtc/stats/rtcstats_objects.cc b/webrtc/stats/rtcstats_objects.cc index 3c80404f9c..947b5647d7 100644 --- a/webrtc/stats/rtcstats_objects.cc +++ b/webrtc/stats/rtcstats_objects.cc @@ -12,11 +12,111 @@ namespace webrtc { +const char* RTCStatsIceCandidatePairState::kFrozen = "frozen"; +const char* RTCStatsIceCandidatePairState::kWaiting = "waiting"; +const char* RTCStatsIceCandidatePairState::kInProgress = "inprogress"; +const char* RTCStatsIceCandidatePairState::kFailed = "failed"; +const char* RTCStatsIceCandidatePairState::kSucceeded = "succeeded"; +const char* RTCStatsIceCandidatePairState::kCancelled = "cancelled"; + +// Strings defined in https://tools.ietf.org/html/rfc5245. const char* RTCIceCandidateType::kHost = "host"; const char* RTCIceCandidateType::kSrflx = "srflx"; const char* RTCIceCandidateType::kPrflx = "prflx"; const char* RTCIceCandidateType::kRelay = "relay"; +WEBRTC_RTCSTATS_IMPL(RTCIceCandidatePairStats, RTCStats, "candidate-pair", + &transport_id, + &local_candidate_id, + &remote_candidate_id, + &state, + &priority, + &nominated, + &writable, + &readable, + &bytes_sent, + &bytes_received, + &total_rtt, + ¤t_rtt, + &available_outgoing_bitrate, + &available_incoming_bitrate, + &requests_received, + &requests_sent, + &responses_received, + &responses_sent, + &retransmissions_received, + &retransmissions_sent, + &consent_requests_received, + &consent_requests_sent, + &consent_responses_received, + &consent_responses_sent); + +RTCIceCandidatePairStats::RTCIceCandidatePairStats( + const std::string& id, int64_t timestamp_us) + : RTCIceCandidatePairStats(std::string(id), timestamp_us) { +} + +RTCIceCandidatePairStats::RTCIceCandidatePairStats( + std::string&& id, int64_t timestamp_us) + : RTCStats(std::move(id), timestamp_us), + transport_id("transportId"), + local_candidate_id("localCandidateId"), + remote_candidate_id("remoteCandidateId"), + state("state"), + priority("priority"), + nominated("nominated"), + writable("writable"), + readable("readable"), + bytes_sent("bytesSent"), + bytes_received("bytesReceived"), + total_rtt("totalRtt"), + current_rtt("currentRtt"), + available_outgoing_bitrate("availableOutgoingBitrate"), + available_incoming_bitrate("availableIncomingBitrate"), + requests_received("requestsReceived"), + requests_sent("requestsSent"), + responses_received("responsesReceived"), + responses_sent("responsesSent"), + retransmissions_received("retransmissionsReceived"), + retransmissions_sent("retransmissionsSent"), + consent_requests_received("consentRequestsReceived"), + consent_requests_sent("consentRequestsSent"), + consent_responses_received("consentResponsesReceived"), + consent_responses_sent("consentResponsesSent") { +} + +RTCIceCandidatePairStats::RTCIceCandidatePairStats( + const RTCIceCandidatePairStats& other) + : RTCStats(other.id(), other.timestamp_us()), + transport_id(other.transport_id), + local_candidate_id(other.local_candidate_id), + remote_candidate_id(other.remote_candidate_id), + state(other.state), + priority(other.priority), + nominated(other.nominated), + writable(other.writable), + readable(other.readable), + bytes_sent(other.bytes_sent), + bytes_received(other.bytes_received), + total_rtt(other.total_rtt), + current_rtt(other.current_rtt), + available_outgoing_bitrate(other.available_outgoing_bitrate), + available_incoming_bitrate(other.available_incoming_bitrate), + requests_received(other.requests_received), + requests_sent(other.requests_sent), + responses_received(other.responses_received), + responses_sent(other.responses_sent), + retransmissions_received(other.retransmissions_received), + retransmissions_sent(other.retransmissions_sent), + consent_requests_received(other.consent_requests_received), + consent_requests_sent(other.consent_requests_sent), + consent_responses_received(other.consent_responses_received), + consent_responses_sent(other.consent_responses_sent) { +} + +RTCIceCandidatePairStats::~RTCIceCandidatePairStats() { +} + WEBRTC_RTCSTATS_IMPL(RTCIceCandidateStats, RTCStats, "ice-candidate", &ip, &port,