API for periodically regathering ICE candidates
Adds to the RTCConfiguration `ice_regather_interval_range` which, when
set, specifies the randomized delay between automatic runs of ICE
regathering. The regathering will occur on all networks and re-use the
existing ICE ufrag/password. New connections are established once the
candidates come back and WebRTC will automatically switch to the new
connection that corresponds to the currently selected connection.
Bug: webrtc:7969
Change-Id: I6bbf5439a48e285f704aed9f408631cba038c82b
Reviewed-on: https://chromium-review.googlesource.com/562505
Reviewed-by: Peter Thatcher <pthatcher@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#18978}
diff --git a/webrtc/api/peerconnectioninterface.h b/webrtc/api/peerconnectioninterface.h
index d6cabf4..6649f2a 100644
--- a/webrtc/api/peerconnectioninterface.h
+++ b/webrtc/api/peerconnectioninterface.h
@@ -453,6 +453,13 @@
// (STUN pings), in milliseconds.
rtc::Optional<int> ice_check_min_interval;
+
+ // ICE Periodic Regathering
+ // If set, WebRTC will periodically create and propose candidates without
+ // starting a new ICE generation. The regathering happens continuously with
+ // interval specified in milliseconds by the uniform distribution [a, b].
+ rtc::Optional<rtc::IntervalRange> ice_regather_interval_range;
+
//
// Don't forget to update operator== if adding something.
//
diff --git a/webrtc/p2p/base/jseptransport.h b/webrtc/p2p/base/jseptransport.h
index 9353cd7..aee0718 100644
--- a/webrtc/p2p/base/jseptransport.h
+++ b/webrtc/p2p/base/jseptransport.h
@@ -184,6 +184,10 @@
// active network having no connection on it.
rtc::Optional<int> regather_on_failed_networks_interval;
+ // Interval to perform ICE regathering on all networks
+ // The delay in milliseconds is sampled from the uniform distribution [a, b]
+ rtc::Optional<rtc::IntervalRange> regather_all_networks_interval_range;
+
// The time period in which we will not switch the selected connection
// when a new connection becomes receiving but the selected connection is not
// in case that the selected connection may become receiving soon.
diff --git a/webrtc/p2p/base/p2ptransportchannel.cc b/webrtc/p2p/base/p2ptransportchannel.cc
index 78b4c93..2c9ee13 100644
--- a/webrtc/p2p/base/p2ptransportchannel.cc
+++ b/webrtc/p2p/base/p2ptransportchannel.cc
@@ -12,6 +12,7 @@
#include <algorithm>
#include <iterator>
+#include <random>
#include <set>
#include "webrtc/api/umametrics.h"
@@ -32,7 +33,8 @@
enum {
MSG_SORT_AND_UPDATE_STATE = 1,
MSG_CHECK_AND_PING,
- MSG_REGATHER_ON_FAILED_NETWORKS
+ MSG_REGATHER_ON_FAILED_NETWORKS,
+ MSG_REGATHER_ON_ALL_NETWORKS
};
// The minimum improvement in RTT that justifies a switch.
@@ -111,6 +113,7 @@
ice_role_(ICEROLE_UNKNOWN),
tiebreaker_(0),
gathering_state_(kIceGatheringNew),
+ rand_(std::random_device()()),
check_receiving_interval_(MIN_CHECK_RECEIVING_INTERVAL * 5),
config_(MIN_CHECK_RECEIVING_INTERVAL * 50 /* receiving_timeout */,
DEFAULT_BACKUP_CONNECTION_PING_INTERVAL,
@@ -428,6 +431,14 @@
LOG(LS_INFO) << "Set regather_on_failed_networks_interval to "
<< *config_.regather_on_failed_networks_interval;
}
+
+ if (config.regather_all_networks_interval_range) {
+ config_.regather_all_networks_interval_range =
+ config.regather_all_networks_interval_range;
+ LOG(LS_INFO) << "Set regather_all_networks_interval_range to "
+ << config.regather_all_networks_interval_range->ToString();
+ }
+
if (config.receiving_switching_delay) {
config_.receiving_switching_delay = config.receiving_switching_delay;
LOG(LS_INFO) << "Set receiving_switching_delay to"
@@ -1077,6 +1088,11 @@
thread()->PostDelayed(RTC_FROM_HERE,
*config_.regather_on_failed_networks_interval, this,
MSG_REGATHER_ON_FAILED_NETWORKS);
+ if (config_.regather_all_networks_interval_range) {
+ thread()->PostDelayed(RTC_FROM_HERE,
+ SampleRegatherAllNetworksInterval(), this,
+ MSG_REGATHER_ON_ALL_NETWORKS);
+ }
started_pinging_ = true;
}
}
@@ -1182,8 +1198,39 @@
// If we're still tied at this point, prefer a younger generation.
// (Younger generation means a larger generation number).
- return (a->remote_candidate().generation() + a->port()->generation()) -
- (b->remote_candidate().generation() + b->port()->generation());
+ int cmp = (a->remote_candidate().generation() + a->port()->generation()) -
+ (b->remote_candidate().generation() + b->port()->generation());
+ if (cmp != 0) {
+ return cmp;
+ }
+
+ // A periodic regather (triggered by the regather_all_networks_interval_range)
+ // will produce candidates that appear the same but would use a new port. We
+ // want to use the new candidates and purge the old candidates as they come
+ // in, so use the fact that the old ports get pruned immediately to rank the
+ // candidates with an active port/remote candidate higher.
+ bool a_pruned = IsPortPruned(a->port()) ||
+ IsRemoteCandidatePruned(a->remote_candidate());
+ bool b_pruned = IsPortPruned(b->port()) ||
+ IsRemoteCandidatePruned(b->remote_candidate());
+ if (!a_pruned && b_pruned) {
+ return a_is_better;
+ }
+ if (a_pruned && !b_pruned) {
+ return b_is_better;
+ }
+
+ // Otherwise, must be equal
+ return 0;
+}
+
+bool P2PTransportChannel::IsPortPruned(const Port* port) const {
+ return std::find(ports_.begin(), ports_.end(), port) == ports_.end();
+}
+
+bool P2PTransportChannel::IsRemoteCandidatePruned(const Candidate& cand) const {
+ return std::find(remote_candidates_.begin(), remote_candidates_.end(), cand)
+ == remote_candidates_.end();
}
int P2PTransportChannel::CompareConnections(
@@ -1528,6 +1575,9 @@
case MSG_REGATHER_ON_FAILED_NETWORKS:
OnRegatherOnFailedNetworks();
break;
+ case MSG_REGATHER_ON_ALL_NETWORKS:
+ OnRegatherOnAllNetworks();
+ break;
default:
RTC_NOTREACHED();
break;
@@ -1913,6 +1963,16 @@
MSG_REGATHER_ON_FAILED_NETWORKS);
}
+void P2PTransportChannel::OnRegatherOnAllNetworks() {
+ if (!allocator_sessions_.empty() && allocator_session()->IsCleared()) {
+ allocator_session()->RegatherOnAllNetworks();
+ }
+
+ thread()->PostDelayed(RTC_FROM_HERE,
+ SampleRegatherAllNetworksInterval(), this,
+ MSG_REGATHER_ON_ALL_NETWORKS);
+}
+
void P2PTransportChannel::PruneAllPorts() {
pruned_ports_.insert(pruned_ports_.end(), ports_.begin(), ports_.end());
ports_.clear();
@@ -2066,4 +2126,10 @@
SignalReceivingState(this);
}
+int P2PTransportChannel::SampleRegatherAllNetworksInterval() {
+ auto interval = config_.regather_all_networks_interval_range;
+ RTC_DCHECK(interval);
+ return rand_.Rand(interval->min(), interval->max());
+}
+
} // namespace cricket
diff --git a/webrtc/p2p/base/p2ptransportchannel.h b/webrtc/p2p/base/p2ptransportchannel.h
index beca558..d2461de 100644
--- a/webrtc/p2p/base/p2ptransportchannel.h
+++ b/webrtc/p2p/base/p2ptransportchannel.h
@@ -33,6 +33,7 @@
#include "webrtc/p2p/base/portinterface.h"
#include "webrtc/rtc_base/asyncpacketsocket.h"
#include "webrtc/rtc_base/constructormagic.h"
+#include "webrtc/rtc_base/random.h"
#include "webrtc/rtc_base/sigslot.h"
namespace cricket {
@@ -280,6 +281,7 @@
void OnMessage(rtc::Message* pmsg) override;
void OnCheckAndPing();
void OnRegatherOnFailedNetworks();
+ void OnRegatherOnAllNetworks();
uint32_t GetNominationAttr(Connection* conn) const;
bool GetUseCandidateAttr(Connection* conn, NominationMode mode) const;
@@ -328,6 +330,16 @@
: static_cast<uint32_t>(remote_ice_parameters_.size() - 1);
}
+ // Samples a delay from the uniform distribution defined by the
+ // regather_on_all_networks_interval ICE configuration pair.
+ int SampleRegatherAllNetworksInterval();
+
+ // Indicates if the given local port has been pruned.
+ bool IsPortPruned(const Port* port) const;
+
+ // Indicates if the given remote candidate has been pruned.
+ bool IsRemoteCandidatePruned(const Candidate& cand) const;
+
// Sets the writable state, signaling if necessary.
void set_writable(bool writable);
// Sets the receiving state, signaling if necessary.
@@ -372,6 +384,9 @@
uint64_t tiebreaker_;
IceGatheringState gathering_state_;
+ // Used to generate random intervals for regather_all_networks_interval_range.
+ webrtc::Random rand_;
+
int check_receiving_interval_;
int64_t last_ping_sent_ms_ = 0;
int weak_ping_interval_ = WEAK_PING_INTERVAL;
diff --git a/webrtc/p2p/base/p2ptransportchannel_unittest.cc b/webrtc/p2p/base/p2ptransportchannel_unittest.cc
index 7b3fa6a..c37b818 100644
--- a/webrtc/p2p/base/p2ptransportchannel_unittest.cc
+++ b/webrtc/p2p/base/p2ptransportchannel_unittest.cc
@@ -1362,6 +1362,106 @@
DestroyChannels();
}
+// Tests that ICE regathering occurs regularly when
+// regather_all_networks_interval_range configuration value is set.
+TEST_F(P2PTransportChannelTest, TestIceRegatherOnAllNetworksContinual) {
+ rtc::ScopedFakeClock clock;
+ ConfigureEndpoints(OPEN, OPEN, kOnlyLocalPorts, kOnlyLocalPorts);
+
+ // ep1 gathers continually but ep2 does not.
+ const int kRegatherInterval = 2000;
+ IceConfig config1 = CreateIceConfig(1000, GATHER_CONTINUALLY);
+ config1.regather_all_networks_interval_range.emplace(
+ kRegatherInterval, kRegatherInterval);
+ IceConfig config2;
+ config2.regather_all_networks_interval_range.emplace(
+ kRegatherInterval, kRegatherInterval);
+ CreateChannels(config1, config2);
+
+ EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() &&
+ ep2_ch1()->receiving() &&
+ ep2_ch1()->writable(),
+ kDefaultTimeout, clock);
+
+ fw()->AddRule(false, rtc::FP_ANY, rtc::FD_ANY, kPublicAddrs[0]);
+ // Timeout value such that all connections are deleted.
+ const int kNetworkGatherDuration = 11000;
+ SIMULATED_WAIT(false, kNetworkGatherDuration, clock);
+ // Expect regathering to happen 5 times in 11s with 2s interval.
+ EXPECT_LE(5, GetMetricsObserver(0)->GetEnumCounter(
+ webrtc::kEnumCounterIceRegathering,
+ static_cast<int>(IceRegatheringReason::OCCASIONAL_REFRESH)));
+ // Expect no regathering if continual gathering not configured.
+ EXPECT_EQ(0, GetMetricsObserver(1)->GetEnumCounter(
+ webrtc::kEnumCounterIceRegathering,
+ static_cast<int>(IceRegatheringReason::OCCASIONAL_REFRESH)));
+
+ DestroyChannels();
+}
+
+// Test that ICE periodic regathering can change the selected connection on the
+// specified interval and that the peers can communicate over the new
+// connection. The test is parameterized to test that it works when regathering
+// is done by the ICE controlling peer and when done by the controlled peer.
+class P2PTransportRegatherAllNetworksTest : public P2PTransportChannelTest {
+ protected:
+ void TestWithRoles(IceRole p1_role, IceRole p2_role) {
+ rtc::ScopedFakeClock clock;
+ ConfigureEndpoints(NAT_SYMMETRIC, NAT_SYMMETRIC, kDefaultPortAllocatorFlags,
+ kDefaultPortAllocatorFlags);
+ set_force_relay(true);
+
+ const int kRegatherInterval = 2000;
+ const int kNumRegathers = 2;
+
+ // Set up peer 1 to auto regather every 2s.
+ IceConfig config1 = CreateIceConfig(1000, GATHER_CONTINUALLY);
+ config1.regather_all_networks_interval_range.emplace(
+ kRegatherInterval, kRegatherInterval);
+ IceConfig config2 = CreateIceConfig(1000, GATHER_CONTINUALLY);
+
+ // Set peer roles.
+ SetIceRole(0, p1_role);
+ SetIceRole(1, p2_role);
+
+ CreateChannels(config1, config2);
+
+ // Wait for initial connection to be made.
+ EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() &&
+ ep1_ch1()->writable() &&
+ ep2_ch1()->receiving() &&
+ ep2_ch1()->writable(),
+ kMediumTimeout, clock);
+
+ const Connection* initial_selected = ep1_ch1()->selected_connection();
+
+ // Wait long enough for 2 regathering cycles to happen plus some extra so
+ // the new connection has time to settle.
+ const int kWaitRegather =
+ kRegatherInterval * kNumRegathers + kRegatherInterval / 2;
+ SIMULATED_WAIT(false, kWaitRegather, clock);
+ EXPECT_EQ(kNumRegathers, GetMetricsObserver(0)->GetEnumCounter(
+ webrtc::kEnumCounterIceRegathering,
+ static_cast<int>(IceRegatheringReason::OCCASIONAL_REFRESH)));
+
+ const Connection* new_selected = ep1_ch1()->selected_connection();
+
+ // Want the new selected connection to be different.
+ ASSERT_NE(initial_selected, new_selected);
+
+ // Make sure we can communicate over the new connection too.
+ TestSendRecv(clock);
+ }
+};
+
+TEST_F(P2PTransportRegatherAllNetworksTest, TestControlling) {
+ TestWithRoles(ICEROLE_CONTROLLING, ICEROLE_CONTROLLED);
+}
+
+TEST_F(P2PTransportRegatherAllNetworksTest, TestControlled) {
+ TestWithRoles(ICEROLE_CONTROLLED, ICEROLE_CONTROLLING);
+}
+
// Test that we properly create a connection on a STUN ping from unknown address
// when the signaling is slow.
TEST_F(P2PTransportChannelTest, PeerReflexiveCandidateBeforeSignaling) {
diff --git a/webrtc/p2p/base/portallocator.h b/webrtc/p2p/base/portallocator.h
index 74b67af..9540e40 100644
--- a/webrtc/p2p/base/portallocator.h
+++ b/webrtc/p2p/base/portallocator.h
@@ -89,7 +89,12 @@
};
// Defines various reasons that have caused ICE regathering.
-enum class IceRegatheringReason { NETWORK_CHANGE, NETWORK_FAILURE, MAX_VALUE };
+enum class IceRegatheringReason {
+ NETWORK_CHANGE, // Network interfaces on the device changed
+ NETWORK_FAILURE, // Regather only on networks that have failed
+ OCCASIONAL_REFRESH, // Periodic regather on all networks
+ MAX_VALUE
+};
const uint32_t kDefaultPortAllocatorFlags = 0;
@@ -238,7 +243,6 @@
// implementation should start re-gathering on all networks of that interface.
virtual void RegatherOnFailedNetworks() {}
// Re-gathers candidates on all networks.
- // TODO(honghaiz): Implement this in BasicPortAllocator.
virtual void RegatherOnAllNetworks() {}
// Another way of getting the information provided by the signals below.
diff --git a/webrtc/p2p/client/basicportallocator.cc b/webrtc/p2p/client/basicportallocator.cc
index 5ddc475..2970987 100644
--- a/webrtc/p2p/client/basicportallocator.cc
+++ b/webrtc/p2p/client/basicportallocator.cc
@@ -339,19 +339,43 @@
sequence->set_network_failed();
}
}
+
+ bool disable_equivalent_phases = true;
+ Regather(failed_networks, disable_equivalent_phases,
+ IceRegatheringReason::NETWORK_FAILURE);
+}
+
+void BasicPortAllocatorSession::RegatherOnAllNetworks() {
+ std::vector<rtc::Network*> networks = GetNetworks();
+ if (networks.empty()) {
+ return;
+ }
+
+ LOG(LS_INFO) << "Regather candidates on all networks";
+
+ // We expect to generate candidates that are equivalent to what we have now.
+ // Force DoAllocate to generate them instead of skipping.
+ bool disable_equivalent_phases = false;
+ Regather(networks, disable_equivalent_phases,
+ IceRegatheringReason::OCCASIONAL_REFRESH);
+}
+
+void BasicPortAllocatorSession::Regather(
+ const std::vector<rtc::Network*>& networks,
+ bool disable_equivalent_phases,
+ IceRegatheringReason reason) {
// Remove ports from being used locally and send signaling to remove
// the candidates on the remote side.
- std::vector<PortData*> ports_to_prune = GetUnprunedPorts(failed_networks);
+ std::vector<PortData*> ports_to_prune = GetUnprunedPorts(networks);
if (!ports_to_prune.empty()) {
- LOG(LS_INFO) << "Prune " << ports_to_prune.size()
- << " ports because their networks failed";
+ LOG(LS_INFO) << "Prune " << ports_to_prune.size() << " ports";
PrunePortsAndRemoveCandidates(ports_to_prune);
}
if (allocation_started_ && network_manager_started_ && !IsStopped()) {
- SignalIceRegathering(this, IceRegatheringReason::NETWORK_FAILURE);
+ SignalIceRegathering(this, reason);
- DoAllocate();
+ DoAllocate(disable_equivalent_phases);
}
}
@@ -530,8 +554,10 @@
}
void BasicPortAllocatorSession::OnAllocate() {
- if (network_manager_started_ && !IsStopped())
- DoAllocate();
+ if (network_manager_started_ && !IsStopped()) {
+ bool disable_equivalent_phases = true;
+ DoAllocate(disable_equivalent_phases);
+ }
allocation_started_ = true;
}
@@ -586,7 +612,7 @@
// For each network, see if we have a sequence that covers it already. If not,
// create a new sequence to create the appropriate ports.
-void BasicPortAllocatorSession::DoAllocate() {
+void BasicPortAllocatorSession::DoAllocate(bool disable_equivalent) {
bool done_signal_needed = false;
std::vector<rtc::Network*> networks = GetNetworks();
if (networks.empty()) {
@@ -622,13 +648,15 @@
continue;
}
- // Disable phases that would only create ports equivalent to
- // ones that we have already made.
- DisableEquivalentPhases(networks[i], config, &sequence_flags);
+ if (disable_equivalent) {
+ // Disable phases that would only create ports equivalent to
+ // ones that we have already made.
+ DisableEquivalentPhases(networks[i], config, &sequence_flags);
- if ((sequence_flags & DISABLE_ALL_PHASES) == DISABLE_ALL_PHASES) {
- // New AllocationSequence would have nothing to do, so don't make it.
- continue;
+ if ((sequence_flags & DISABLE_ALL_PHASES) == DISABLE_ALL_PHASES) {
+ // New AllocationSequence would have nothing to do, so don't make it.
+ continue;
+ }
}
AllocationSequence* sequence =
@@ -671,7 +699,8 @@
// If the network manager has started, it must be regathering.
SignalIceRegathering(this, IceRegatheringReason::NETWORK_CHANGE);
}
- DoAllocate();
+ bool disable_equivalent_phases = true;
+ DoAllocate(disable_equivalent_phases);
}
if (!network_manager_started_) {
diff --git a/webrtc/p2p/client/basicportallocator.h b/webrtc/p2p/client/basicportallocator.h
index 7cdf0ba..a991417 100644
--- a/webrtc/p2p/client/basicportallocator.h
+++ b/webrtc/p2p/client/basicportallocator.h
@@ -112,6 +112,7 @@
std::vector<Candidate> ReadyCandidates() const override;
bool CandidatesAllocationDone() const override;
void RegatherOnFailedNetworks() override;
+ void RegatherOnAllNetworks() override;
void PruneAllPorts() override;
protected:
@@ -185,7 +186,7 @@
void OnConfigStop();
void AllocatePorts();
void OnAllocate();
- void DoAllocate();
+ void DoAllocate(bool disable_equivalent_phases);
void OnNetworksChanged();
void OnAllocationSequenceObjectsCreated();
void DisableEquivalentPhases(rtc::Network* network,
@@ -203,6 +204,9 @@
PortData* FindPort(Port* port);
std::vector<rtc::Network*> GetNetworks();
std::vector<rtc::Network*> GetFailedNetworks();
+ void Regather(const std::vector<rtc::Network*>& networks,
+ bool disable_equivalent_phases,
+ IceRegatheringReason reason);
bool CheckCandidateFilter(const Candidate& c) const;
bool CandidatePairable(const Candidate& c, const Port* port) const;
diff --git a/webrtc/pc/peerconnection.cc b/webrtc/pc/peerconnection.cc
index a947c30..42664b0 100644
--- a/webrtc/pc/peerconnection.cc
+++ b/webrtc/pc/peerconnection.cc
@@ -223,11 +223,20 @@
bool PeerConnectionInterface::RTCConfiguration::operator==(
const PeerConnectionInterface::RTCConfiguration& o) const {
// This static_assert prevents us from accidentally breaking operator==.
+ // Note: Order matters! Fields must be ordered the same as RTCConfiguration.
struct stuff_being_tested_for_equality {
- IceTransportsType type;
IceServers servers;
+ IceTransportsType type;
BundlePolicy bundle_policy;
RtcpMuxPolicy rtcp_mux_policy;
+ std::vector<rtc::scoped_refptr<rtc::RTCCertificate>> certificates;
+ int ice_candidate_pool_size;
+ bool disable_ipv6;
+ bool disable_ipv6_on_wifi;
+ bool enable_rtp_data_channel;
+ rtc::Optional<int> screencast_min_bitrate;
+ rtc::Optional<bool> combined_audio_video_bwe;
+ rtc::Optional<bool> enable_dtls_srtp;
TcpCandidatePolicy tcp_candidate_policy;
CandidateNetworkPolicy candidate_network_policy;
int audio_jitter_buffer_max_packets;
@@ -235,22 +244,15 @@
int ice_connection_receiving_timeout;
int ice_backup_candidate_pair_ping_interval;
ContinualGatheringPolicy continual_gathering_policy;
- std::vector<rtc::scoped_refptr<rtc::RTCCertificate>> certificates;
bool prioritize_most_likely_ice_candidate_pairs;
struct cricket::MediaConfig media_config;
- bool disable_ipv6;
- bool disable_ipv6_on_wifi;
- bool enable_rtp_data_channel;
bool enable_quic;
- rtc::Optional<int> screencast_min_bitrate;
- rtc::Optional<bool> combined_audio_video_bwe;
- rtc::Optional<bool> enable_dtls_srtp;
- int ice_candidate_pool_size;
bool prune_turn_ports;
bool presume_writable_when_fully_relayed;
bool enable_ice_renomination;
bool redetermine_role_on_ice_restart;
rtc::Optional<int> ice_check_min_interval;
+ rtc::Optional<rtc::IntervalRange> ice_regather_interval_range;
};
static_assert(sizeof(stuff_being_tested_for_equality) == sizeof(*this),
"Did you add something to RTCConfiguration and forget to "
@@ -284,7 +286,8 @@
o.presume_writable_when_fully_relayed &&
enable_ice_renomination == o.enable_ice_renomination &&
redetermine_role_on_ice_restart == o.redetermine_role_on_ice_restart &&
- ice_check_min_interval == o.ice_check_min_interval;
+ ice_check_min_interval == o.ice_check_min_interval &&
+ ice_regather_interval_range == o.ice_regather_interval_range;
}
bool PeerConnectionInterface::RTCConfiguration::operator!=(
diff --git a/webrtc/pc/webrtcsession.cc b/webrtc/pc/webrtcsession.cc
index 4ca4528..b4b30a0 100644
--- a/webrtc/pc/webrtcsession.cc
+++ b/webrtc/pc/webrtcsession.cc
@@ -1222,6 +1222,8 @@
ice_config.presume_writable_when_fully_relayed =
config.presume_writable_when_fully_relayed;
ice_config.ice_check_min_interval = config.ice_check_min_interval;
+ ice_config.regather_all_networks_interval_range =
+ config.ice_regather_interval_range;
return ice_config;
}
diff --git a/webrtc/rtc_base/timeutils.h b/webrtc/rtc_base/timeutils.h
index ea7b17d..c7f035c 100644
--- a/webrtc/rtc_base/timeutils.h
+++ b/webrtc/rtc_base/timeutils.h
@@ -15,6 +15,9 @@
#include <time.h>
#include <ctime>
+#include <string>
+
+#include "webrtc/rtc_base/checks.h"
namespace rtc {
@@ -124,6 +127,32 @@
// measuring time intervals and timeouts.
int64_t TimeUTCMicros();
+// Interval of time from the range [min, max] inclusive.
+class IntervalRange {
+ public:
+ IntervalRange() : min_(0), max_(0) {}
+ IntervalRange(int min, int max) : min_(min), max_(max) {
+ RTC_DCHECK_LE(min, max);
+ }
+
+ int min() const { return min_; }
+ int max() const { return max_; }
+
+ std::string ToString() const {
+ std::stringstream ss;
+ ss << "[" << min_ << "," << max_ << "]";
+ return ss.str();
+ }
+
+ bool operator==(const IntervalRange& o) const {
+ return min_ == o.min_ && max_ == o.max_;
+ }
+
+ private:
+ int min_;
+ int max_;
+};
+
} // namespace rtc
#endif // WEBRTC_RTC_BASE_TIMEUTILS_H_