p2p_transport_channel: Add estimated disconnected time to CandidatePairChangeEvent

This patch adds a computed estimate on how long the ice stack
was disconnected before switching to a new connection.

The metric is currently computed as now - max(connection->last_data_recevied())
and has resonably good precision.

Bug: webrtc:11862
Change-Id: I8950d55f0eadcf164de089cdb715b4f7eed0a4c3
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/182002
Reviewed-by: Sami Kalliomäki <sakal@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Jonas Oreland <jonaso@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#31969}
This commit is contained in:
Jonas Oreland
2020-08-20 12:25:04 +02:00
committed by Commit Bot
parent 957318ceaf
commit 93a9d19d4e
6 changed files with 113 additions and 3 deletions

View File

@ -1784,6 +1784,15 @@ void P2PTransportChannel::SwitchSelectedConnection(Connection* conn,
pair_change.selected_candidate_pair = *GetSelectedCandidatePair();
pair_change.last_data_received_ms =
selected_connection_->last_data_received();
if (old_selected_connection) {
pair_change.estimated_disconnected_time_ms =
ComputeEstimatedDisconnectedTimeMs(rtc::TimeMillis(),
old_selected_connection);
} else {
pair_change.estimated_disconnected_time_ms = 0;
}
SignalCandidatePairChanged(pair_change);
}
@ -1792,6 +1801,16 @@ void P2PTransportChannel::SwitchSelectedConnection(Connection* conn,
ice_controller_->SetSelectedConnection(selected_connection_);
}
int64_t P2PTransportChannel::ComputeEstimatedDisconnectedTimeMs(
int64_t now_ms,
Connection* old_connection) {
// TODO(jonaso): nicer keeps estimate of how frequently data _should_ be
// received, this could be used to give better estimate (if needed).
int64_t last_data_or_old_ping =
std::max(old_connection->last_received(), last_data_received_ms_);
return (now_ms - last_data_or_old_ping);
}
// Warning: UpdateState should eventually be called whenever a connection
// is added, deleted, or the write state of any connection changes so that the
// transport controller will get the up-to-date channel state. However it
@ -2110,6 +2129,9 @@ void P2PTransportChannel::OnReadPacket(Connection* connection,
if (connection == selected_connection_) {
// Let the client know of an incoming packet
RTC_DCHECK(connection->last_data_received() >= last_data_received_ms_);
last_data_received_ms_ =
std::max(last_data_received_ms_, connection->last_data_received());
SignalReadPacket(this, data, len, packet_time_us, 0);
return;
}
@ -2118,6 +2140,10 @@ void P2PTransportChannel::OnReadPacket(Connection* connection,
if (!FindConnection(connection))
return;
RTC_DCHECK(connection->last_data_received() >= last_data_received_ms_);
last_data_received_ms_ =
std::max(last_data_received_ms_, connection->last_data_received());
// Let the client know of an incoming packet
SignalReadPacket(this, data, len, packet_time_us, 0);

View File

@ -358,6 +358,9 @@ class RTC_EXPORT P2PTransportChannel : public IceTransportInternal {
return const_cast<Connection*>(conn);
}
int64_t ComputeEstimatedDisconnectedTimeMs(int64_t now,
Connection* old_connection);
std::string transport_name_ RTC_GUARDED_BY(network_thread_);
int component_ RTC_GUARDED_BY(network_thread_);
PortAllocator* allocator_ RTC_GUARDED_BY(network_thread_);
@ -440,6 +443,10 @@ class RTC_EXPORT P2PTransportChannel : public IceTransportInternal {
// Number of times the selected_connection_ has been modified.
uint32_t selected_candidate_pair_changes_ = 0;
// When was last data received on a existing connection,
// from connection->last_data_received() that uses rtc::TimeMillis().
int64_t last_data_received_ms_ = 0;
IceFieldTrials field_trials_;
RTC_DISALLOW_COPY_AND_ASSIGN(P2PTransportChannel);

View File

@ -3268,6 +3268,14 @@ class P2PTransportChannelPingTest : public ::testing::Test,
}
}
int64_t LastEstimatedDisconnectedTimeMs() const {
if (!last_candidate_change_event_.has_value()) {
return 0;
} else {
return last_candidate_change_event_->estimated_disconnected_time_ms;
}
}
private:
std::unique_ptr<rtc::VirtualSocketServer> vss_;
rtc::AutoSocketServerThread thread_;
@ -4088,6 +4096,64 @@ TEST_F(P2PTransportChannelPingTest,
EXPECT_EQ(0, reset_selected_candidate_pair_switches());
}
TEST_F(P2PTransportChannelPingTest, TestEstimatedDisconnectedTime) {
rtc::ScopedFakeClock clock;
clock.AdvanceTime(webrtc::TimeDelta::Seconds(1));
FakePortAllocator pa(rtc::Thread::Current(), nullptr);
P2PTransportChannel ch("test", 1, &pa);
PrepareChannel(&ch);
ch.SetIceRole(ICEROLE_CONTROLLED);
ch.MaybeStartGathering();
// The connections have decreasing priority.
Connection* conn1 =
CreateConnectionWithCandidate(&ch, &clock, "1.1.1.1", /* port= */ 1,
/* priority= */ 10, /* writable= */ true);
ASSERT_TRUE(conn1 != nullptr);
Connection* conn2 =
CreateConnectionWithCandidate(&ch, &clock, "2.2.2.2", /* port= */ 2,
/* priority= */ 9, /* writable= */ true);
ASSERT_TRUE(conn2 != nullptr);
// conn1 is the selected connection because it has a higher priority,
EXPECT_EQ_SIMULATED_WAIT(conn1, ch.selected_connection(), kDefaultTimeout,
clock);
EXPECT_TRUE(CandidatePairMatchesNetworkRoute(conn1));
// No estimateded disconnect time at first connect <=> value is 0.
EXPECT_EQ(LastEstimatedDisconnectedTimeMs(), 0);
// Use nomination to force switching of selected connection.
int nomination = 1;
{
clock.AdvanceTime(webrtc::TimeDelta::Seconds(1));
// This will not parse as STUN, and is considered data
conn1->OnReadPacket("XYZ", 3, rtc::TimeMicros());
clock.AdvanceTime(webrtc::TimeDelta::Seconds(2));
// conn2 is nominated; it becomes selected.
NominateConnection(conn2, nomination++);
EXPECT_EQ(conn2, ch.selected_connection());
// We got data 2s ago...guess that we lost 2s of connectivity.
EXPECT_EQ(LastEstimatedDisconnectedTimeMs(), 2000);
}
{
clock.AdvanceTime(webrtc::TimeDelta::Seconds(1));
conn2->OnReadPacket("XYZ", 3, rtc::TimeMicros());
clock.AdvanceTime(webrtc::TimeDelta::Seconds(2));
ReceivePingOnConnection(conn2, kIceUfrag[1], 1, nomination++);
clock.AdvanceTime(webrtc::TimeDelta::Millis(500));
ReceivePingOnConnection(conn1, kIceUfrag[1], 1, nomination++);
EXPECT_EQ(conn1, ch.selected_connection());
// We got ping 500ms ago...guess that we lost 500ms of connectivity.
EXPECT_EQ(LastEstimatedDisconnectedTimeMs(), 500);
}
}
TEST_F(P2PTransportChannelPingTest,
TestControlledAgentIgnoresSmallerNomination) {
rtc::ScopedFakeClock clock;

View File

@ -150,6 +150,8 @@ struct CandidatePairChangeEvent {
CandidatePair selected_candidate_pair;
int64_t last_data_received_ms;
std::string reason;
// How long do we estimate that we've been disconnected.
int64_t estimated_disconnected_time_ms;
};
typedef std::set<rtc::SocketAddress> ServerAddresses;

View File

@ -20,12 +20,20 @@ public final class CandidatePairChangeEvent {
public final int lastDataReceivedMs;
public final String reason;
/**
* An estimate from the ICE stack on how long it was disconnected before
* changing to the new candidate pair in this event.
* The first time an candidate pair is signaled the value will be 0.
*/
public final int estimatedDisconnectedTimeMs;
@CalledByNative
CandidatePairChangeEvent(
IceCandidate local, IceCandidate remote, int lastDataReceivedMs, String reason) {
CandidatePairChangeEvent(IceCandidate local, IceCandidate remote, int lastDataReceivedMs,
String reason, int estimatedDisconnectedTimeMs) {
this.local = local;
this.remote = remote;
this.lastDataReceivedMs = lastDataReceivedMs;
this.reason = reason;
this.estimatedDisconnectedTimeMs = estimatedDisconnectedTimeMs;
}
}

View File

@ -128,7 +128,8 @@ ScopedJavaLocalRef<jobject> NativeToJavaCandidatePairChange(
env, NativeToJavaCandidate(env, selected_pair.local_candidate()),
NativeToJavaCandidate(env, selected_pair.remote_candidate()),
static_cast<int>(event.last_data_received_ms),
NativeToJavaString(env, event.reason));
NativeToJavaString(env, event.reason),
static_cast<int>(event.estimated_disconnected_time_ms));
}
} // namespace