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:

committed by
Commit Bot

parent
957318ceaf
commit
93a9d19d4e
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user