diff --git a/api/stats/rtcstats_objects.h b/api/stats/rtcstats_objects.h index f103b1d882..b62b0aa5f7 100644 --- a/api/stats/rtcstats_objects.h +++ b/api/stats/rtcstats_objects.h @@ -222,6 +222,8 @@ class RTC_EXPORT RTCIceCandidatePairStats final : public RTCStats { RTCStatsMember consent_requests_sent; RTCStatsMember packets_discarded_on_send; RTCStatsMember bytes_discarded_on_send; + RTCStatsMember last_packet_received_timestamp; + RTCStatsMember last_packet_sent_timestamp; }; // https://w3c.github.io/webrtc-stats/#icecandidate-dict* diff --git a/p2p/BUILD.gn b/p2p/BUILD.gn index 45bdf62082..d46955f659 100644 --- a/p2p/BUILD.gn +++ b/p2p/BUILD.gn @@ -111,6 +111,7 @@ rtc_library("rtc_p2p") { "../api/transport:field_trial_based_config", "../api/transport:stun_types", "../api/units:time_delta", + "../api/units:timestamp", "../logging:ice_log", "../rtc_base", "../rtc_base:async_resolver_interface", diff --git a/p2p/base/connection.cc b/p2p/base/connection.cc index 76652dbda5..f49c02234f 100644 --- a/p2p/base/connection.cc +++ b/p2p/base/connection.cc @@ -235,6 +235,7 @@ Connection::Connection(rtc::WeakPtr port, last_ping_response_received_(0), state_(IceCandidatePairState::WAITING), time_created_ms_(rtc::TimeMillis()), + delta_internal_unix_epoch_ms_(rtc::TimeUTCMillis() - rtc::TimeMillis()), field_trials_(&kDefaultFieldTrials), rtt_estimate_(DEFAULT_RTT_ESTIMATE_HALF_TIME_MS) { RTC_DCHECK_RUN_ON(network_thread_); @@ -1526,6 +1527,14 @@ ConnectionInfo Connection::stats() { stats_.total_round_trip_time_ms = total_round_trip_time_ms_; stats_.current_round_trip_time_ms = current_round_trip_time_ms_; stats_.remote_candidate = remote_candidate(); + if (last_data_received_ > 0) { + stats_.last_data_received = webrtc::Timestamp::Millis( + last_data_received_ + delta_internal_unix_epoch_ms_); + } + if (last_send_data_ > 0) { + stats_.last_data_sent = webrtc::Timestamp::Millis( + last_send_data_ + delta_internal_unix_epoch_ms_); + } return stats_; } diff --git a/p2p/base/connection.h b/p2p/base/connection.h index 7baff0287c..4be8483f9a 100644 --- a/p2p/base/connection.h +++ b/p2p/base/connection.h @@ -442,7 +442,8 @@ class Connection : public CandidatePairInterface { IceCandidatePairState state_ RTC_GUARDED_BY(network_thread_); // Time duration to switch from receiving to not receiving. absl::optional receiving_timeout_ RTC_GUARDED_BY(network_thread_); - int64_t time_created_ms_ RTC_GUARDED_BY(network_thread_); + const int64_t time_created_ms_ RTC_GUARDED_BY(network_thread_); + const int64_t delta_internal_unix_epoch_ms_ RTC_GUARDED_BY(network_thread_); int num_pings_sent_ RTC_GUARDED_BY(network_thread_) = 0; absl::optional log_description_ diff --git a/p2p/base/connection_info.h b/p2p/base/connection_info.h index a30b636d86..cd2a913451 100644 --- a/p2p/base/connection_info.h +++ b/p2p/base/connection_info.h @@ -15,6 +15,7 @@ #include "absl/types/optional.h" #include "api/candidate.h" +#include "api/units/timestamp.h" namespace cricket { @@ -72,6 +73,10 @@ struct ConnectionInfo { uint64_t total_round_trip_time_ms; // https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-currentroundtriptime absl::optional current_round_trip_time_ms; + + // https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-lastpacketreceivedtimestamp + absl::optional last_data_received; + absl::optional last_data_sent; }; // Information about all the candidate pairs of a channel. diff --git a/pc/rtc_stats_collector.cc b/pc/rtc_stats_collector.cc index da089d4588..5e57c71ca7 100644 --- a/pc/rtc_stats_collector.cc +++ b/pc/rtc_stats_collector.cc @@ -1727,6 +1727,15 @@ void RTCStatsCollector::ProduceIceCandidateAndPairStats_n( info.sent_ping_requests_total - info.sent_ping_requests_before_first_response); + if (info.last_data_received) { + candidate_pair_stats->last_packet_received_timestamp = + static_cast(info.last_data_received->ms()); + } + if (info.last_data_sent) { + candidate_pair_stats->last_packet_sent_timestamp = + static_cast(info.last_data_sent->ms()); + } + report->AddStats(std::move(candidate_pair_stats)); } diff --git a/pc/rtc_stats_collector_unittest.cc b/pc/rtc_stats_collector_unittest.cc index 7deb31aea6..742f87bdba 100644 --- a/pc/rtc_stats_collector_unittest.cc +++ b/pc/rtc_stats_collector_unittest.cc @@ -1940,6 +1940,8 @@ TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidatePairStats) { connection_info.state = cricket::IceCandidatePairState::IN_PROGRESS; connection_info.priority = 5555; connection_info.nominated = false; + connection_info.last_data_received = Timestamp::Millis(2500); + connection_info.last_data_sent = Timestamp::Millis(5200); cricket::TransportChannelStats transport_channel_stats; transport_channel_stats.component = cricket::ICE_CANDIDATE_COMPONENT_RTP; @@ -1974,6 +1976,9 @@ TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidatePairStats) { expected_pair.responses_received = 4321; expected_pair.responses_sent = 1000; expected_pair.consent_requests_sent = (2222 - 2000); + expected_pair.last_packet_received_timestamp = 2500; + expected_pair.last_packet_sent_timestamp = 5200; + // `expected_pair.current_round_trip_time` should be undefined because the // current RTT is not set. // `expected_pair.available_[outgoing/incoming]_bitrate` should be undefined diff --git a/pc/rtc_stats_integrationtest.cc b/pc/rtc_stats_integrationtest.cc index 1a97a1e3d8..c7d31529f5 100644 --- a/pc/rtc_stats_integrationtest.cc +++ b/pc/rtc_stats_integrationtest.cc @@ -516,6 +516,8 @@ class RTCStatsReportVerifier { verifier.TestMemberIsNonNegative(candidate_pair.responses_sent); verifier.TestMemberIsNonNegative( candidate_pair.consent_requests_sent); + verifier.TestMemberIsDefined(candidate_pair.last_packet_received_timestamp); + verifier.TestMemberIsDefined(candidate_pair.last_packet_sent_timestamp); return verifier.ExpectAllMembersSuccessfullyTested(); } diff --git a/stats/rtcstats_objects.cc b/stats/rtcstats_objects.cc index e5c6bfa1e1..8a6b6dfaa0 100644 --- a/stats/rtcstats_objects.cc +++ b/stats/rtcstats_objects.cc @@ -185,7 +185,9 @@ WEBRTC_RTCSTATS_IMPL(RTCIceCandidatePairStats, RTCStats, "candidate-pair", &responses_sent, &consent_requests_sent, &packets_discarded_on_send, - &bytes_discarded_on_send) + &bytes_discarded_on_send, + &last_packet_received_timestamp, + &last_packet_sent_timestamp) // clang-format on RTCIceCandidatePairStats::RTCIceCandidatePairStats(const std::string& id, @@ -216,7 +218,9 @@ RTCIceCandidatePairStats::RTCIceCandidatePairStats(std::string&& id, responses_sent("responsesSent"), consent_requests_sent("consentRequestsSent"), packets_discarded_on_send("packetsDiscardedOnSend"), - bytes_discarded_on_send("bytesDiscardedOnSend") {} + bytes_discarded_on_send("bytesDiscardedOnSend"), + last_packet_received_timestamp("lastPacketReceivedTimestamp"), + last_packet_sent_timestamp("lastPacketSentTimestamp") {} RTCIceCandidatePairStats::RTCIceCandidatePairStats( const RTCIceCandidatePairStats& other) = default;