Add support for RTCTransportStats.selectedCandidatePairChanges

This patch adds accounting and reporting needed for
newly added RTCTransportStats.selectedCandidatePairChanges,
https://w3c.github.io/webrtc-stats/#dom-rtctransportstats-selectedcandidatepairchanges

a) P2PTransportChannel counts everytime selected_connection_
is modified and reports this counter in the GetStats()-call.
b) RTCStatsCollector puts the counter into the standardized
stats object.

Bug: webrtc:10900
Change-Id: Ibaeca18706b8edcbcb44b0c6f2754854bcb545ba
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/149830
Reviewed-by: Qingsi Wang <qingsi@webrtc.org>
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Commit-Queue: Jonas Oreland <jonaso@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#28987}
This commit is contained in:
Jonas Oreland 2019-08-28 08:10:27 +02:00 committed by Commit Bot
parent 3b69817e62
commit 149dc72dfa
16 changed files with 211 additions and 88 deletions

View File

@ -594,6 +594,7 @@ class RTC_EXPORT RTCTransportStats final : public RTCStats {
RTCStatsMember<std::string> selected_candidate_pair_id; RTCStatsMember<std::string> selected_candidate_pair_id;
RTCStatsMember<std::string> local_certificate_id; RTCStatsMember<std::string> local_certificate_id;
RTCStatsMember<std::string> remote_certificate_id; RTCStatsMember<std::string> remote_certificate_id;
RTCStatsMember<uint32_t> selected_candidate_pair_changes;
}; };
} // namespace webrtc } // namespace webrtc

View File

@ -193,14 +193,13 @@ class FakeIceTransport : public IceTransportInternal {
void RemoveAllRemoteCandidates() override { remote_candidates_.clear(); } void RemoveAllRemoteCandidates() override { remote_candidates_.clear(); }
bool GetStats(ConnectionInfos* candidate_pair_stats_list, bool GetStats(IceTransportStats* ice_transport_stats) override {
CandidateStatsList* candidate_stats_list) override {
CandidateStats candidate_stats; CandidateStats candidate_stats;
ConnectionInfo candidate_pair_stats; ConnectionInfo candidate_pair_stats;
candidate_stats_list->clear(); ice_transport_stats->candidate_stats_list.clear();
candidate_stats_list->push_back(candidate_stats); ice_transport_stats->candidate_stats_list.push_back(candidate_stats);
candidate_pair_stats_list->clear(); ice_transport_stats->connection_infos.clear();
candidate_pair_stats_list->push_back(candidate_pair_stats); ice_transport_stats->connection_infos.push_back(candidate_pair_stats);
return true; return true;
} }

View File

@ -30,6 +30,15 @@
namespace cricket { namespace cricket {
struct IceTransportStats {
CandidateStatsList candidate_stats_list;
ConnectionInfos connection_infos;
// Number of times the selected candidate pair has changed
// Initially 0 and 1 once the first candidate pair has been selected.
// The counter is increase also when "unselecting" a connection.
uint32_t selected_candidate_pair_changes = 0;
};
typedef std::vector<Candidate> Candidates; typedef std::vector<Candidate> Candidates;
enum IceConnectionState { enum IceConnectionState {
@ -256,8 +265,7 @@ class RTC_EXPORT IceTransportInternal : public rtc::PacketTransportInternal {
virtual IceGatheringState gathering_state() const = 0; virtual IceGatheringState gathering_state() const = 0;
// Returns the current stats for this connection. // Returns the current stats for this connection.
virtual bool GetStats(ConnectionInfos* candidate_pair_stats_list, virtual bool GetStats(IceTransportStats* ice_transport_stats) = 0;
CandidateStatsList* candidate_stats_list) = 0;
// Returns RTT estimate over the currently active connection, or an empty // Returns RTT estimate over the currently active connection, or an empty
// absl::optional if there is none. // absl::optional if there is none.

View File

@ -40,9 +40,7 @@ class MockIceTransport : public IceTransportInternal {
MOCK_METHOD2(SetOption, int(rtc::Socket::Option opt, int value)); MOCK_METHOD2(SetOption, int(rtc::Socket::Option opt, int value));
MOCK_METHOD0(GetError, int()); MOCK_METHOD0(GetError, int());
MOCK_CONST_METHOD0(GetIceRole, cricket::IceRole()); MOCK_CONST_METHOD0(GetIceRole, cricket::IceRole());
MOCK_METHOD2(GetStats, MOCK_METHOD1(GetStats, bool(cricket::IceTransportStats* ice_transport_stats));
bool(cricket::ConnectionInfos* candidate_pair_stats_list,
cricket::CandidateStatsList* candidate_stats_list));
IceTransportState GetState() const override { IceTransportState GetState() const override {
return IceTransportState::STATE_INIT; return IceTransportState::STATE_INIT;

View File

@ -1493,15 +1493,15 @@ int P2PTransportChannel::SendPacket(const char* data,
return sent; return sent;
} }
bool P2PTransportChannel::GetStats(ConnectionInfos* candidate_pair_stats_list, bool P2PTransportChannel::GetStats(IceTransportStats* ice_transport_stats) {
CandidateStatsList* candidate_stats_list) {
RTC_DCHECK_RUN_ON(network_thread_); RTC_DCHECK_RUN_ON(network_thread_);
// Gather candidate and candidate pair stats. // Gather candidate and candidate pair stats.
candidate_stats_list->clear(); ice_transport_stats->candidate_stats_list.clear();
candidate_pair_stats_list->clear(); ice_transport_stats->connection_infos.clear();
if (!allocator_sessions_.empty()) { if (!allocator_sessions_.empty()) {
allocator_session()->GetCandidateStatsFromReadyPorts(candidate_stats_list); allocator_session()->GetCandidateStatsFromReadyPorts(
&ice_transport_stats->candidate_stats_list);
} }
// TODO(qingsi): Remove naming inconsistency for candidate pair/connection. // TODO(qingsi): Remove naming inconsistency for candidate pair/connection.
@ -1510,10 +1510,12 @@ bool P2PTransportChannel::GetStats(ConnectionInfos* candidate_pair_stats_list,
stats.local_candidate = SanitizeLocalCandidate(stats.local_candidate); stats.local_candidate = SanitizeLocalCandidate(stats.local_candidate);
stats.remote_candidate = SanitizeRemoteCandidate(stats.remote_candidate); stats.remote_candidate = SanitizeRemoteCandidate(stats.remote_candidate);
stats.best_connection = (selected_connection_ == connection); stats.best_connection = (selected_connection_ == connection);
candidate_pair_stats_list->push_back(std::move(stats)); ice_transport_stats->connection_infos.push_back(std::move(stats));
connection->set_reported(true); connection->set_reported(true);
} }
ice_transport_stats->selected_candidate_pair_changes =
selected_candidate_pair_changes_;
return true; return true;
} }
@ -1991,6 +1993,8 @@ void P2PTransportChannel::SwitchSelectedConnection(Connection* conn,
SignalCandidatePairChanged(pair_change); SignalCandidatePairChanged(pair_change);
} }
SignalNetworkRouteChanged(network_route_); SignalNetworkRouteChanged(network_route_);
++selected_candidate_pair_changes_;
} }
// Warning: UpdateState should eventually be called whenever a connection // Warning: UpdateState should eventually be called whenever a connection

View File

@ -131,8 +131,7 @@ class RTC_EXPORT P2PTransportChannel : public IceTransportInternal {
int SetOption(rtc::Socket::Option opt, int value) override; int SetOption(rtc::Socket::Option opt, int value) override;
bool GetOption(rtc::Socket::Option opt, int* value) override; bool GetOption(rtc::Socket::Option opt, int* value) override;
int GetError() override; int GetError() override;
bool GetStats(std::vector<ConnectionInfo>* candidate_pair_stats_list, bool GetStats(IceTransportStats* ice_transport_stats) override;
std::vector<CandidateStats>* candidate_stats_list) override;
absl::optional<int> GetRttEstimate() override; absl::optional<int> GetRttEstimate() override;
const Connection* selected_connection() const override; const Connection* selected_connection() const override;
absl::optional<const CandidatePair> GetSelectedCandidatePair() const override; absl::optional<const CandidatePair> GetSelectedCandidatePair() const override;
@ -499,6 +498,9 @@ class RTC_EXPORT P2PTransportChannel : public IceTransportInternal {
void AddRemoteCandidateWithResolver(Candidate candidate, void AddRemoteCandidateWithResolver(Candidate candidate,
rtc::AsyncResolverInterface* resolver); rtc::AsyncResolverInterface* resolver);
// Number of times the selected_connection_ has been modified.
uint32_t selected_candidate_pair_changes_ = 0;
RTC_DISALLOW_COPY_AND_ASSIGN(P2PTransportChannel); RTC_DISALLOW_COPY_AND_ASSIGN(P2PTransportChannel);
}; };

View File

@ -1231,13 +1231,13 @@ TEST_F(P2PTransportChannelTest, GetStats) {
ep2_ch1()->writable(), ep2_ch1()->writable(),
kMediumTimeout, clock); kMediumTimeout, clock);
TestSendRecv(&clock); TestSendRecv(&clock);
ConnectionInfos infos; IceTransportStats ice_transport_stats;
CandidateStatsList candidate_stats_list; ASSERT_TRUE(ep1_ch1()->GetStats(&ice_transport_stats));
ASSERT_TRUE(ep1_ch1()->GetStats(&infos, &candidate_stats_list)); ASSERT_GE(ice_transport_stats.connection_infos.size(), 1u);
ASSERT_GE(infos.size(), 1u); ASSERT_GE(ice_transport_stats.candidate_stats_list.size(), 1u);
ASSERT_GE(candidate_stats_list.size(), 1u); EXPECT_EQ(ice_transport_stats.selected_candidate_pair_changes, 1u);
ConnectionInfo* best_conn_info = nullptr; ConnectionInfo* best_conn_info = nullptr;
for (ConnectionInfo& info : infos) { for (ConnectionInfo& info : ice_transport_stats.connection_infos) {
if (info.best_connection) { if (info.best_connection) {
best_conn_info = &info; best_conn_info = &info;
break; break;
@ -1582,13 +1582,16 @@ TEST_F(P2PTransportChannelTest, PeerReflexiveRemoteCandidateIsSanitized) {
EXPECT_EQ(PRFLX_PORT_TYPE, pair_ep1->remote_candidate().type()); EXPECT_EQ(PRFLX_PORT_TYPE, pair_ep1->remote_candidate().type());
EXPECT_TRUE(pair_ep1->remote_candidate().address().ipaddr().IsNil()); EXPECT_TRUE(pair_ep1->remote_candidate().address().ipaddr().IsNil());
ConnectionInfos pair_stats; IceTransportStats ice_transport_stats;
CandidateStatsList candidate_stats; ep1_ch1()->GetStats(&ice_transport_stats);
ep1_ch1()->GetStats(&pair_stats, &candidate_stats);
// Check the candidate pair stats. // Check the candidate pair stats.
ASSERT_EQ(1u, pair_stats.size()); ASSERT_EQ(1u, ice_transport_stats.connection_infos.size());
EXPECT_EQ(PRFLX_PORT_TYPE, pair_stats[0].remote_candidate.type()); EXPECT_EQ(PRFLX_PORT_TYPE,
EXPECT_TRUE(pair_stats[0].remote_candidate.address().ipaddr().IsNil()); ice_transport_stats.connection_infos[0].remote_candidate.type());
EXPECT_TRUE(ice_transport_stats.connection_infos[0]
.remote_candidate.address()
.ipaddr()
.IsNil());
// Let ep1 receive the remote candidate to update its type from prflx to host. // Let ep1 receive the remote candidate to update its type from prflx to host.
ResumeCandidates(1); ResumeCandidates(1);
@ -1608,12 +1611,14 @@ TEST_F(P2PTransportChannelTest, PeerReflexiveRemoteCandidateIsSanitized) {
EXPECT_TRUE( EXPECT_TRUE(
updated_pair_ep1->remote_candidate().address().EqualIPs(kPublicAddrs[1])); updated_pair_ep1->remote_candidate().address().EqualIPs(kPublicAddrs[1]));
ep1_ch1()->GetStats(&pair_stats, &candidate_stats); ep1_ch1()->GetStats(&ice_transport_stats);
// Check the candidate pair stats. // Check the candidate pair stats.
ASSERT_EQ(1u, pair_stats.size()); ASSERT_EQ(1u, ice_transport_stats.connection_infos.size());
EXPECT_EQ(LOCAL_PORT_TYPE, pair_stats[0].remote_candidate.type()); EXPECT_EQ(LOCAL_PORT_TYPE,
EXPECT_TRUE( ice_transport_stats.connection_infos[0].remote_candidate.type());
pair_stats[0].remote_candidate.address().EqualIPs(kPublicAddrs[1])); EXPECT_TRUE(ice_transport_stats.connection_infos[0]
.remote_candidate.address()
.EqualIPs(kPublicAddrs[1]));
DestroyChannels(); DestroyChannels();
} }
@ -5010,17 +5015,15 @@ TEST_F(P2PTransportChannelTest,
ASSERT_EQ_WAIT(3u, ep1_ch1()->connections().size(), kMediumTimeout); ASSERT_EQ_WAIT(3u, ep1_ch1()->connections().size(), kMediumTimeout);
ASSERT_EQ_WAIT(3u, ep2_ch1()->connections().size(), kMediumTimeout); ASSERT_EQ_WAIT(3u, ep2_ch1()->connections().size(), kMediumTimeout);
ConnectionInfos connection_infos_ep1; IceTransportStats ice_transport_stats1;
CandidateStatsList candidate_stats_list_ep1; IceTransportStats ice_transport_stats2;
ConnectionInfos connection_infos_ep2; ep1_ch1()->GetStats(&ice_transport_stats1);
CandidateStatsList candidate_stats_list_ep2; ep2_ch1()->GetStats(&ice_transport_stats2);
ep1_ch1()->GetStats(&connection_infos_ep1, &candidate_stats_list_ep1); EXPECT_EQ(3u, ice_transport_stats1.connection_infos.size());
ep2_ch1()->GetStats(&connection_infos_ep2, &candidate_stats_list_ep2); EXPECT_EQ(3u, ice_transport_stats1.candidate_stats_list.size());
EXPECT_EQ(3u, connection_infos_ep1.size()); EXPECT_EQ(3u, ice_transport_stats2.connection_infos.size());
EXPECT_EQ(3u, candidate_stats_list_ep1.size());
EXPECT_EQ(3u, connection_infos_ep2.size());
// Check the stats of ep1 seen by ep1. // Check the stats of ep1 seen by ep1.
for (const auto& connection_info : connection_infos_ep1) { for (const auto& connection_info : ice_transport_stats1.connection_infos) {
const auto& local_candidate = connection_info.local_candidate; const auto& local_candidate = connection_info.local_candidate;
if (local_candidate.type() == LOCAL_PORT_TYPE) { if (local_candidate.type() == LOCAL_PORT_TYPE) {
EXPECT_TRUE(local_candidate.address().IsUnresolvedIP()); EXPECT_TRUE(local_candidate.address().IsUnresolvedIP());
@ -5037,7 +5040,7 @@ TEST_F(P2PTransportChannelTest,
} }
} }
// Check the stats of ep1 seen by ep2. // Check the stats of ep1 seen by ep2.
for (const auto& connection_info : connection_infos_ep2) { for (const auto& connection_info : ice_transport_stats2.connection_infos) {
const auto& remote_candidate = connection_info.remote_candidate; const auto& remote_candidate = connection_info.remote_candidate;
if (remote_candidate.type() == LOCAL_PORT_TYPE) { if (remote_candidate.type() == LOCAL_PORT_TYPE) {
EXPECT_TRUE(remote_candidate.address().IsUnresolvedIP()); EXPECT_TRUE(remote_candidate.address().IsUnresolvedIP());
@ -5053,6 +5056,96 @@ TEST_F(P2PTransportChannelTest,
DestroyChannels(); DestroyChannels();
} }
TEST_F(P2PTransportChannelTest,
ConnectingIncreasesSelectedCandidatePairChanges) {
rtc::ScopedFakeClock clock;
ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags,
kDefaultPortAllocatorFlags);
CreateChannels();
IceTransportStats ice_transport_stats;
ASSERT_TRUE(ep1_ch1()->GetStats(&ice_transport_stats));
EXPECT_EQ(0u, ice_transport_stats.selected_candidate_pair_changes);
// Let the channels connect.
EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->selected_connection() != nullptr,
kMediumTimeout, clock);
ASSERT_TRUE(ep1_ch1()->GetStats(&ice_transport_stats));
EXPECT_EQ(1u, ice_transport_stats.selected_candidate_pair_changes);
DestroyChannels();
}
TEST_F(P2PTransportChannelTest,
DisconnectedIncreasesSelectedCandidatePairChanges) {
rtc::ScopedFakeClock clock;
ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags,
kDefaultPortAllocatorFlags);
CreateChannels();
IceTransportStats ice_transport_stats;
ASSERT_TRUE(ep1_ch1()->GetStats(&ice_transport_stats));
EXPECT_EQ(0u, ice_transport_stats.selected_candidate_pair_changes);
// Let the channels connect.
EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->selected_connection() != nullptr,
kMediumTimeout, clock);
ASSERT_TRUE(ep1_ch1()->GetStats(&ice_transport_stats));
EXPECT_EQ(1u, ice_transport_stats.selected_candidate_pair_changes);
// Prune connections and wait for disconnect.
for (Connection* con : ep1_ch1()->connections()) {
con->Prune();
}
EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->selected_connection() == nullptr,
kMediumTimeout, clock);
ASSERT_TRUE(ep1_ch1()->GetStats(&ice_transport_stats));
EXPECT_EQ(2u, ice_transport_stats.selected_candidate_pair_changes);
DestroyChannels();
}
TEST_F(P2PTransportChannelTest,
NewSelectionIncreasesSelectedCandidatePairChanges) {
rtc::ScopedFakeClock clock;
ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags,
kDefaultPortAllocatorFlags);
CreateChannels();
IceTransportStats ice_transport_stats;
ASSERT_TRUE(ep1_ch1()->GetStats(&ice_transport_stats));
EXPECT_EQ(0u, ice_transport_stats.selected_candidate_pair_changes);
// Let the channels connect.
EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->selected_connection() != nullptr,
kMediumTimeout, clock);
ASSERT_TRUE(ep1_ch1()->GetStats(&ice_transport_stats));
EXPECT_EQ(1u, ice_transport_stats.selected_candidate_pair_changes);
// Prune the currently selected connection and wait for selection
// of a new one.
const Connection* selected_connection = ep1_ch1()->selected_connection();
for (Connection* con : ep1_ch1()->connections()) {
if (con == selected_connection) {
con->Prune();
}
}
EXPECT_TRUE_SIMULATED_WAIT(
ep1_ch1()->selected_connection() != nullptr &&
(ep1_ch1()->GetStats(&ice_transport_stats),
ice_transport_stats.selected_candidate_pair_changes >= 2u),
kMediumTimeout, clock);
ASSERT_TRUE(ep1_ch1()->GetStats(&ice_transport_stats));
EXPECT_GE(ice_transport_stats.selected_candidate_pair_changes, 2u);
DestroyChannels();
}
// A similar test as above to check the selected candidate pair is sanitized // A similar test as above to check the selected candidate pair is sanitized
// when it is queried via GetSelectedCandidatePair. // when it is queried via GetSelectedCandidatePair.
TEST_F(P2PTransportChannelTest, TEST_F(P2PTransportChannelTest,

View File

@ -747,7 +747,7 @@ bool JsepTransport::GetTransportStats(DtlsTransportInternal* dtls_transport,
dtls_transport->GetSslCipherSuite(&substats.ssl_cipher_suite); dtls_transport->GetSslCipherSuite(&substats.ssl_cipher_suite);
substats.dtls_state = dtls_transport->dtls_state(); substats.dtls_state = dtls_transport->dtls_state();
if (!dtls_transport->ice_transport()->GetStats( if (!dtls_transport->ice_transport()->GetStats(
&substats.connection_infos, &substats.candidate_stats_list)) { &substats.ice_transport_stats)) {
return false; return false;
} }
stats->channel_stats.push_back(substats); stats->channel_stats.push_back(substats);

View File

@ -7251,7 +7251,7 @@ void PeerConnection::ReportBestConnectionState(
for (const cricket::TransportChannelStats& channel_stats : for (const cricket::TransportChannelStats& channel_stats :
stats.channel_stats) { stats.channel_stats) {
for (const cricket::ConnectionInfo& connection_info : for (const cricket::ConnectionInfo& connection_info :
channel_stats.connection_infos) { channel_stats.ice_transport_stats.connection_infos) {
if (!connection_info.best_connection) { if (!connection_info.best_connection) {
continue; continue;
} }

View File

@ -1258,7 +1258,7 @@ void RTCStatsCollector::ProduceIceCandidateAndPairStats_n(
std::string transport_id = RTCTransportStatsIDFromTransportChannel( std::string transport_id = RTCTransportStatsIDFromTransportChannel(
transport_name, channel_stats.component); transport_name, channel_stats.component);
for (const cricket::ConnectionInfo& info : for (const cricket::ConnectionInfo& info :
channel_stats.connection_infos) { channel_stats.ice_transport_stats.connection_infos) {
std::unique_ptr<RTCIceCandidatePairStats> candidate_pair_stats( std::unique_ptr<RTCIceCandidatePairStats> candidate_pair_stats(
new RTCIceCandidatePairStats( new RTCIceCandidatePairStats(
RTCIceCandidatePairStatsIDFromConnectionInfo(info), RTCIceCandidatePairStatsIDFromConnectionInfo(info),
@ -1689,8 +1689,10 @@ void RTCStatsCollector::ProduceTransportStats_n(
transport_stats->bytes_received = 0; transport_stats->bytes_received = 0;
transport_stats->dtls_state = transport_stats->dtls_state =
DtlsTransportStateToRTCDtlsTransportState(channel_stats.dtls_state); DtlsTransportStateToRTCDtlsTransportState(channel_stats.dtls_state);
transport_stats->selected_candidate_pair_changes =
channel_stats.ice_transport_stats.selected_candidate_pair_changes;
for (const cricket::ConnectionInfo& info : for (const cricket::ConnectionInfo& info :
channel_stats.connection_infos) { channel_stats.ice_transport_stats.connection_infos) {
*transport_stats->bytes_sent += info.sent_total_bytes; *transport_stats->bytes_sent += info.sent_total_bytes;
*transport_stats->bytes_received += info.recv_total_bytes; *transport_stats->bytes_received += info.recv_total_bytes;
if (info.best_connection) { if (info.best_connection) {

View File

@ -1135,35 +1135,35 @@ TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidateStats) {
// Add candidate pairs to connection. // Add candidate pairs to connection.
cricket::TransportChannelStats a_transport_channel_stats; cricket::TransportChannelStats a_transport_channel_stats;
a_transport_channel_stats.connection_infos.push_back( a_transport_channel_stats.ice_transport_stats.connection_infos.push_back(
cricket::ConnectionInfo()); cricket::ConnectionInfo());
a_transport_channel_stats.connection_infos[0].local_candidate = a_transport_channel_stats.ice_transport_stats.connection_infos[0]
*a_local_host.get(); .local_candidate = *a_local_host.get();
a_transport_channel_stats.connection_infos[0].remote_candidate = a_transport_channel_stats.ice_transport_stats.connection_infos[0]
*a_remote_srflx.get(); .remote_candidate = *a_remote_srflx.get();
a_transport_channel_stats.connection_infos.push_back( a_transport_channel_stats.ice_transport_stats.connection_infos.push_back(
cricket::ConnectionInfo()); cricket::ConnectionInfo());
a_transport_channel_stats.connection_infos[1].local_candidate = a_transport_channel_stats.ice_transport_stats.connection_infos[1]
*a_local_prflx.get(); .local_candidate = *a_local_prflx.get();
a_transport_channel_stats.connection_infos[1].remote_candidate = a_transport_channel_stats.ice_transport_stats.connection_infos[1]
*a_remote_relay.get(); .remote_candidate = *a_remote_relay.get();
a_transport_channel_stats.connection_infos.push_back( a_transport_channel_stats.ice_transport_stats.connection_infos.push_back(
cricket::ConnectionInfo()); cricket::ConnectionInfo());
a_transport_channel_stats.connection_infos[2].local_candidate = a_transport_channel_stats.ice_transport_stats.connection_infos[2]
*a_local_relay.get(); .local_candidate = *a_local_relay.get();
a_transport_channel_stats.connection_infos[2].remote_candidate = a_transport_channel_stats.ice_transport_stats.connection_infos[2]
*a_remote_relay.get(); .remote_candidate = *a_remote_relay.get();
pc_->AddVoiceChannel("audio", "a"); pc_->AddVoiceChannel("audio", "a");
pc_->SetTransportStats("a", a_transport_channel_stats); pc_->SetTransportStats("a", a_transport_channel_stats);
cricket::TransportChannelStats b_transport_channel_stats; cricket::TransportChannelStats b_transport_channel_stats;
b_transport_channel_stats.connection_infos.push_back( b_transport_channel_stats.ice_transport_stats.connection_infos.push_back(
cricket::ConnectionInfo()); cricket::ConnectionInfo());
b_transport_channel_stats.connection_infos[0].local_candidate = b_transport_channel_stats.ice_transport_stats.connection_infos[0]
*b_local.get(); .local_candidate = *b_local.get();
b_transport_channel_stats.connection_infos[0].remote_candidate = b_transport_channel_stats.ice_transport_stats.connection_infos[0]
*b_remote.get(); .remote_candidate = *b_remote.get();
pc_->AddVideoChannel("video", "b"); pc_->AddVideoChannel("video", "b");
pc_->SetTransportStats("b", b_transport_channel_stats); pc_->SetTransportStats("b", b_transport_channel_stats);
@ -1225,7 +1225,8 @@ TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidatePairStats) {
cricket::TransportChannelStats transport_channel_stats; cricket::TransportChannelStats transport_channel_stats;
transport_channel_stats.component = cricket::ICE_CANDIDATE_COMPONENT_RTP; transport_channel_stats.component = cricket::ICE_CANDIDATE_COMPONENT_RTP;
transport_channel_stats.connection_infos.push_back(connection_info); transport_channel_stats.ice_transport_stats.connection_infos.push_back(
connection_info);
pc_->AddVideoChannel("video", kTransportName); pc_->AddVideoChannel("video", kTransportName);
pc_->SetTransportStats(kTransportName, transport_channel_stats); pc_->SetTransportStats(kTransportName, transport_channel_stats);
@ -1266,7 +1267,8 @@ TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidatePairStats) {
EXPECT_TRUE(report->Get(*expected_pair.transport_id)); EXPECT_TRUE(report->Get(*expected_pair.transport_id));
// Set nominated and "GetStats" again. // Set nominated and "GetStats" again.
transport_channel_stats.connection_infos[0].nominated = true; transport_channel_stats.ice_transport_stats.connection_infos[0].nominated =
true;
pc_->SetTransportStats(kTransportName, transport_channel_stats); pc_->SetTransportStats(kTransportName, transport_channel_stats);
report = stats_->GetFreshStatsReport(); report = stats_->GetFreshStatsReport();
expected_pair.nominated = true; expected_pair.nominated = true;
@ -1277,8 +1279,10 @@ TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidatePairStats) {
EXPECT_TRUE(report->Get(*expected_pair.transport_id)); EXPECT_TRUE(report->Get(*expected_pair.transport_id));
// Set round trip times and "GetStats" again. // Set round trip times and "GetStats" again.
transport_channel_stats.connection_infos[0].total_round_trip_time_ms = 7331; transport_channel_stats.ice_transport_stats.connection_infos[0]
transport_channel_stats.connection_infos[0].current_round_trip_time_ms = 1337; .total_round_trip_time_ms = 7331;
transport_channel_stats.ice_transport_stats.connection_infos[0]
.current_round_trip_time_ms = 1337;
pc_->SetTransportStats(kTransportName, transport_channel_stats); pc_->SetTransportStats(kTransportName, transport_channel_stats);
report = stats_->GetFreshStatsReport(); report = stats_->GetFreshStatsReport();
expected_pair.total_round_trip_time = 7.331; expected_pair.total_round_trip_time = 7.331;
@ -1290,7 +1294,8 @@ TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidatePairStats) {
EXPECT_TRUE(report->Get(*expected_pair.transport_id)); EXPECT_TRUE(report->Get(*expected_pair.transport_id));
// Make pair the current pair, clear bandwidth and "GetStats" again. // Make pair the current pair, clear bandwidth and "GetStats" again.
transport_channel_stats.connection_infos[0].best_connection = true; transport_channel_stats.ice_transport_stats.connection_infos[0]
.best_connection = true;
pc_->SetTransportStats(kTransportName, transport_channel_stats); pc_->SetTransportStats(kTransportName, transport_channel_stats);
report = stats_->GetFreshStatsReport(); report = stats_->GetFreshStatsReport();
// |expected_pair.available_[outgoing/incoming]_bitrate| should still be // |expected_pair.available_[outgoing/incoming]_bitrate| should still be
@ -2066,8 +2071,11 @@ TEST_F(RTCStatsCollectorTest, CollectRTCTransportStats) {
rtp_connection_info.recv_total_bytes = 1337; rtp_connection_info.recv_total_bytes = 1337;
cricket::TransportChannelStats rtp_transport_channel_stats; cricket::TransportChannelStats rtp_transport_channel_stats;
rtp_transport_channel_stats.component = cricket::ICE_CANDIDATE_COMPONENT_RTP; rtp_transport_channel_stats.component = cricket::ICE_CANDIDATE_COMPONENT_RTP;
rtp_transport_channel_stats.connection_infos.push_back(rtp_connection_info); rtp_transport_channel_stats.ice_transport_stats.connection_infos.push_back(
rtp_connection_info);
rtp_transport_channel_stats.dtls_state = cricket::DTLS_TRANSPORT_NEW; rtp_transport_channel_stats.dtls_state = cricket::DTLS_TRANSPORT_NEW;
rtp_transport_channel_stats.ice_transport_stats
.selected_candidate_pair_changes = 1;
pc_->SetTransportStats(kTransportName, {rtp_transport_channel_stats}); pc_->SetTransportStats(kTransportName, {rtp_transport_channel_stats});
// Get stats without RTCP, an active connection or certificates. // Get stats without RTCP, an active connection or certificates.
@ -2080,6 +2088,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCTransportStats) {
expected_rtp_transport.bytes_sent = 42; expected_rtp_transport.bytes_sent = 42;
expected_rtp_transport.bytes_received = 1337; expected_rtp_transport.bytes_received = 1337;
expected_rtp_transport.dtls_state = RTCDtlsTransportState::kNew; expected_rtp_transport.dtls_state = RTCDtlsTransportState::kNew;
expected_rtp_transport.selected_candidate_pair_changes = 1;
ASSERT_TRUE(report->Get(expected_rtp_transport.id())); ASSERT_TRUE(report->Get(expected_rtp_transport.id()));
EXPECT_EQ( EXPECT_EQ(
@ -2095,7 +2104,8 @@ TEST_F(RTCStatsCollectorTest, CollectRTCTransportStats) {
cricket::TransportChannelStats rtcp_transport_channel_stats; cricket::TransportChannelStats rtcp_transport_channel_stats;
rtcp_transport_channel_stats.component = rtcp_transport_channel_stats.component =
cricket::ICE_CANDIDATE_COMPONENT_RTCP; cricket::ICE_CANDIDATE_COMPONENT_RTCP;
rtcp_transport_channel_stats.connection_infos.push_back(rtcp_connection_info); rtcp_transport_channel_stats.ice_transport_stats.connection_infos.push_back(
rtcp_connection_info);
rtcp_transport_channel_stats.dtls_state = cricket::DTLS_TRANSPORT_CONNECTING; rtcp_transport_channel_stats.dtls_state = cricket::DTLS_TRANSPORT_CONNECTING;
pc_->SetTransportStats(kTransportName, {rtp_transport_channel_stats, pc_->SetTransportStats(kTransportName, {rtp_transport_channel_stats,
rtcp_transport_channel_stats}); rtcp_transport_channel_stats});
@ -2110,9 +2120,9 @@ TEST_F(RTCStatsCollectorTest, CollectRTCTransportStats) {
expected_rtcp_transport.bytes_sent = 1337; expected_rtcp_transport.bytes_sent = 1337;
expected_rtcp_transport.bytes_received = 42; expected_rtcp_transport.bytes_received = 42;
expected_rtcp_transport.dtls_state = RTCDtlsTransportState::kConnecting; expected_rtcp_transport.dtls_state = RTCDtlsTransportState::kConnecting;
expected_rtcp_transport.selected_candidate_pair_changes = 0;
expected_rtp_transport.rtcp_transport_stats_id = expected_rtcp_transport.id(); expected_rtp_transport.rtcp_transport_stats_id = expected_rtcp_transport.id();
ASSERT_TRUE(report->Get(expected_rtp_transport.id())); ASSERT_TRUE(report->Get(expected_rtp_transport.id()));
EXPECT_EQ( EXPECT_EQ(
expected_rtp_transport, expected_rtp_transport,
@ -2123,7 +2133,8 @@ TEST_F(RTCStatsCollectorTest, CollectRTCTransportStats) {
report->Get(expected_rtcp_transport.id())->cast_to<RTCTransportStats>()); report->Get(expected_rtcp_transport.id())->cast_to<RTCTransportStats>());
// Get stats with an active connection (selected candidate pair). // Get stats with an active connection (selected candidate pair).
rtcp_transport_channel_stats.connection_infos[0].best_connection = true; rtcp_transport_channel_stats.ice_transport_stats.connection_infos[0]
.best_connection = true;
pc_->SetTransportStats(kTransportName, {rtp_transport_channel_stats, pc_->SetTransportStats(kTransportName, {rtp_transport_channel_stats,
rtcp_transport_channel_stats}); rtcp_transport_channel_stats});

View File

@ -959,6 +959,8 @@ class RTCStatsReportVerifier {
RTCCertificateStats::kType); RTCCertificateStats::kType);
verifier.TestMemberIsIDReference(transport.remote_certificate_id, verifier.TestMemberIsIDReference(transport.remote_certificate_id,
RTCCertificateStats::kType); RTCCertificateStats::kType);
verifier.TestMemberIsPositive<uint32_t>(
transport.selected_candidate_pair_changes);
return verifier.ExpectAllMembersSuccessfullyTested(); return verifier.ExpectAllMembersSuccessfullyTested();
} }

View File

@ -865,13 +865,13 @@ void StatsCollector::ExtractSessionInfo() {
// not paired. Also, the candidate report generated in // not paired. Also, the candidate report generated in
// AddConnectionInfoReport do not report port stats like StunStats. // AddConnectionInfoReport do not report port stats like StunStats.
for (const cricket::CandidateStats& stats : for (const cricket::CandidateStats& stats :
channel_iter.candidate_stats_list) { channel_iter.ice_transport_stats.candidate_stats_list) {
AddCandidateReport(stats, true); AddCandidateReport(stats, true);
} }
int connection_id = 0; int connection_id = 0;
for (const cricket::ConnectionInfo& info : for (const cricket::ConnectionInfo& info :
channel_iter.connection_infos) { channel_iter.ice_transport_stats.connection_infos) {
StatsReport* connection_report = AddConnectionInfoReport( StatsReport* connection_report = AddConnectionInfoReport(
transport_name, channel_iter.component, connection_id++, transport_name, channel_iter.component, connection_id++,
channel_report->id(), info); channel_report->id(), info);

View File

@ -1296,7 +1296,7 @@ TEST_F(StatsCollectorTest, IceCandidateReport) {
connection_info.local_candidate = local; connection_info.local_candidate = local;
connection_info.remote_candidate = remote; connection_info.remote_candidate = remote;
TransportChannelStats channel_stats; TransportChannelStats channel_stats;
channel_stats.connection_infos.push_back(connection_info); channel_stats.ice_transport_stats.connection_infos.push_back(connection_info);
pc->AddVoiceChannel("audio", kTransportName); pc->AddVoiceChannel("audio", kTransportName);
pc->SetTransportStats(kTransportName, channel_stats); pc->SetTransportStats(kTransportName, channel_stats);

View File

@ -15,6 +15,7 @@
#include <vector> #include <vector>
#include "p2p/base/dtls_transport_internal.h" #include "p2p/base/dtls_transport_internal.h"
#include "p2p/base/ice_transport_internal.h"
#include "p2p/base/port.h" #include "p2p/base/port.h"
#include "rtc_base/ssl_stream_adapter.h" #include "rtc_base/ssl_stream_adapter.h"
@ -26,11 +27,10 @@ struct TransportChannelStats {
~TransportChannelStats(); ~TransportChannelStats();
int component = 0; int component = 0;
CandidateStatsList candidate_stats_list;
ConnectionInfos connection_infos;
int srtp_crypto_suite = rtc::SRTP_INVALID_CRYPTO_SUITE; int srtp_crypto_suite = rtc::SRTP_INVALID_CRYPTO_SUITE;
int ssl_cipher_suite = rtc::TLS_NULL_WITH_NULL_NULL; int ssl_cipher_suite = rtc::TLS_NULL_WITH_NULL_NULL;
DtlsTransportState dtls_state = DTLS_TRANSPORT_NEW; DtlsTransportState dtls_state = DTLS_TRANSPORT_NEW;
IceTransportStats ice_transport_stats;
}; };
// Information about all the channels of a transport. // Information about all the channels of a transport.

View File

@ -868,7 +868,8 @@ WEBRTC_RTCSTATS_IMPL(RTCTransportStats, RTCStats, "transport",
&dtls_state, &dtls_state,
&selected_candidate_pair_id, &selected_candidate_pair_id,
&local_certificate_id, &local_certificate_id,
&remote_certificate_id) &remote_certificate_id,
&selected_candidate_pair_changes)
// clang-format on // clang-format on
RTCTransportStats::RTCTransportStats(const std::string& id, RTCTransportStats::RTCTransportStats(const std::string& id,
@ -883,7 +884,8 @@ RTCTransportStats::RTCTransportStats(std::string&& id, int64_t timestamp_us)
dtls_state("dtlsState"), dtls_state("dtlsState"),
selected_candidate_pair_id("selectedCandidatePairId"), selected_candidate_pair_id("selectedCandidatePairId"),
local_certificate_id("localCertificateId"), local_certificate_id("localCertificateId"),
remote_certificate_id("remoteCertificateId") {} remote_certificate_id("remoteCertificateId"),
selected_candidate_pair_changes("selectedCandidatePairChanges") {}
RTCTransportStats::RTCTransportStats(const RTCTransportStats& other) RTCTransportStats::RTCTransportStats(const RTCTransportStats& other)
: RTCStats(other.id(), other.timestamp_us()), : RTCStats(other.id(), other.timestamp_us()),
@ -893,7 +895,8 @@ RTCTransportStats::RTCTransportStats(const RTCTransportStats& other)
dtls_state(other.dtls_state), dtls_state(other.dtls_state),
selected_candidate_pair_id(other.selected_candidate_pair_id), selected_candidate_pair_id(other.selected_candidate_pair_id),
local_certificate_id(other.local_certificate_id), local_certificate_id(other.local_certificate_id),
remote_certificate_id(other.remote_certificate_id) {} remote_certificate_id(other.remote_certificate_id),
selected_candidate_pair_changes(other.selected_candidate_pair_changes) {}
RTCTransportStats::~RTCTransportStats() {} RTCTransportStats::~RTCTransportStats() {}