| Index: webrtc/p2p/base/p2ptransportchannel.cc
|
| diff --git a/webrtc/p2p/base/p2ptransportchannel.cc b/webrtc/p2p/base/p2ptransportchannel.cc
|
| index 4ebd3a41c44612585ba4340a34d816e5e77fe167..e9055f688f229ae1c9017fdb5deca12284d127d8 100644
|
| --- a/webrtc/p2p/base/p2ptransportchannel.cc
|
| +++ b/webrtc/p2p/base/p2ptransportchannel.cc
|
| @@ -80,6 +80,8 @@ const int STABLE_WRITABLE_CONNECTION_PING_INTERVAL = 2500; // ms
|
|
|
| static const int MIN_CHECK_RECEIVING_INTERVAL = 50; // ms
|
|
|
| +static const int RECEIVING_SWITCHING_DELAY = 1000; // ms
|
| +
|
| // We periodically check if any existing networks do not have any connection
|
| // and regather on those networks.
|
| static const int DEFAULT_REGATHER_ON_FAILED_NETWORKS_INTERVAL = 5 * 60 * 1000;
|
| @@ -112,7 +114,8 @@ P2PTransportChannel::P2PTransportChannel(const std::string& transport_name,
|
| false /* prioritize_most_likely_candidate_pairs */,
|
| STABLE_WRITABLE_CONNECTION_PING_INTERVAL,
|
| true /* presume_writable_when_fully_relayed */,
|
| - DEFAULT_REGATHER_ON_FAILED_NETWORKS_INTERVAL) {
|
| + DEFAULT_REGATHER_ON_FAILED_NETWORKS_INTERVAL,
|
| + RECEIVING_SWITCHING_DELAY) {
|
| uint32_t weak_ping_interval = ::strtoul(
|
| webrtc::field_trial::FindFullName("WebRTC-StunInterPacketDelay").c_str(),
|
| nullptr, 10);
|
| @@ -184,7 +187,8 @@ void P2PTransportChannel::AddConnection(Connection* connection) {
|
| // TODO(honghaiz): Stop the aggressive nomination on the controlling side and
|
| // implement the ice-renomination option.
|
| bool P2PTransportChannel::ShouldSwitchSelectedConnection(
|
| - Connection* new_connection) const {
|
| + Connection* new_connection,
|
| + bool* missed_receiving_unchanged_threshold) const {
|
| if (!new_connection || selected_connection_ == new_connection) {
|
| return false;
|
| }
|
| @@ -193,7 +197,11 @@ bool P2PTransportChannel::ShouldSwitchSelectedConnection(
|
| return true;
|
| }
|
|
|
| - int cmp = CompareConnections(selected_connection_, new_connection);
|
| + rtc::Optional<int64_t> receiving_unchanged_threshold(
|
| + rtc::TimeMillis() - config_.receiving_switching_delay.value_or(0));
|
| + int cmp = CompareConnections(selected_connection_, new_connection,
|
| + receiving_unchanged_threshold,
|
| + missed_receiving_unchanged_threshold);
|
| if (cmp != 0) {
|
| return cmp < 0;
|
| }
|
| @@ -203,6 +211,28 @@ bool P2PTransportChannel::ShouldSwitchSelectedConnection(
|
| return new_connection->rtt() <= selected_connection_->rtt() - kMinImprovement;
|
| }
|
|
|
| +bool P2PTransportChannel::MaybeSwitchSelectedConnection(
|
| + Connection* new_connection,
|
| + const std::string& reason) {
|
| + bool missed_receiving_unchanged_threshold = false;
|
| + if (ShouldSwitchSelectedConnection(new_connection,
|
| + &missed_receiving_unchanged_threshold)) {
|
| + LOG(LS_INFO) << "Switching selected connection due to " << reason;
|
| + SwitchSelectedConnection(new_connection);
|
| + return true;
|
| + }
|
| + if (missed_receiving_unchanged_threshold &&
|
| + config_.receiving_switching_delay) {
|
| + // If we do not switch to the connection because it missed the receiving
|
| + // threshold, the new connection is in a better receiving state than the
|
| + // currently selected connection. So we need to re-check whether it needs
|
| + // to be switched at a later time.
|
| + thread()->PostDelayed(RTC_FROM_HERE, *config_.receiving_switching_delay,
|
| + this, MSG_SORT_AND_UPDATE_STATE);
|
| + }
|
| + return false;
|
| +}
|
| +
|
| void P2PTransportChannel::SetIceRole(IceRole ice_role) {
|
| ASSERT(worker_thread_ == rtc::Thread::Current());
|
| if (ice_role_ != ice_role) {
|
| @@ -369,6 +399,11 @@ void P2PTransportChannel::SetIceConfig(const IceConfig& config) {
|
| LOG(LS_INFO) << "Set regather_on_failed_networks_interval to "
|
| << *config_.regather_on_failed_networks_interval;
|
| }
|
| + if (config.receiving_switching_delay) {
|
| + config_.receiving_switching_delay = config.receiving_switching_delay;
|
| + LOG(LS_INFO) << "Set receiving_switching_delay to"
|
| + << *config_.receiving_switching_delay;
|
| + }
|
| }
|
|
|
| const IceConfig& P2PTransportChannel::config() const {
|
| @@ -627,20 +662,16 @@ void P2PTransportChannel::OnNominated(Connection* conn) {
|
| return;
|
| }
|
|
|
| - if (!ShouldSwitchSelectedConnection(conn)) {
|
| + if (MaybeSwitchSelectedConnection(conn,
|
| + "nomination on the controlled side")) {
|
| + // Now that we have selected a connection, it is time to prune other
|
| + // connections and update the read/write state of the channel.
|
| + RequestSortAndStateUpdate();
|
| + } else {
|
| LOG(LS_INFO)
|
| << "Not switching the selected connection on controlled side yet: "
|
| << conn->ToString();
|
| - return;
|
| }
|
| -
|
| - LOG(LS_INFO)
|
| - << "Switching selected connection on controlled side due to nomination: "
|
| - << conn->ToString();
|
| - SwitchSelectedConnection(conn);
|
| - // Now that we have selected a connection, it is time to prune other
|
| - // connections and update the read/write state of the channel.
|
| - RequestSortAndStateUpdate();
|
| }
|
|
|
| void P2PTransportChannel::AddRemoteCandidate(const Candidate& candidate) {
|
| @@ -987,8 +1018,11 @@ void P2PTransportChannel::MaybeStartPinging() {
|
|
|
| // Compare two connections based on their writing, receiving, and connected
|
| // states.
|
| -int P2PTransportChannel::CompareConnectionStates(const Connection* a,
|
| - const Connection* b) const {
|
| +int P2PTransportChannel::CompareConnectionStates(
|
| + const Connection* a,
|
| + const Connection* b,
|
| + rtc::Optional<int64_t> receiving_unchanged_threshold,
|
| + bool* missed_receiving_unchanged_threshold) const {
|
| // First, prefer a connection that's writable or presumed writable over
|
| // one that's not writable.
|
| bool a_writable = a->writable() || PresumedWritable(a);
|
| @@ -1015,7 +1049,12 @@ int P2PTransportChannel::CompareConnectionStates(const Connection* a,
|
| return a_is_better;
|
| }
|
| if (!a->receiving() && b->receiving()) {
|
| - return b_is_better;
|
| + if (!receiving_unchanged_threshold ||
|
| + (a->receiving_unchanged_since() <= *receiving_unchanged_threshold &&
|
| + b->receiving_unchanged_since() <= *receiving_unchanged_threshold)) {
|
| + return b_is_better;
|
| + }
|
| + *missed_receiving_unchanged_threshold = true;
|
| }
|
|
|
| // WARNING: Some complexity here about TCP reconnecting.
|
| @@ -1082,15 +1121,19 @@ int P2PTransportChannel::CompareConnectionCandidates(
|
| (b->remote_candidate().generation() + b->port()->generation());
|
| }
|
|
|
| -int P2PTransportChannel::CompareConnections(const Connection* a,
|
| - const Connection* b) const {
|
| +int P2PTransportChannel::CompareConnections(
|
| + const Connection* a,
|
| + const Connection* b,
|
| + rtc::Optional<int64_t> receiving_unchanged_threshold,
|
| + bool* missed_receiving_unchanged_threshold) const {
|
| RTC_CHECK(a != nullptr);
|
| RTC_CHECK(b != nullptr);
|
|
|
| // We prefer to switch to a writable and receiving connection over a
|
| // non-writable or non-receiving connection, even if the latter has
|
| // been nominated by the controlling side.
|
| - int state_cmp = CompareConnectionStates(a, b);
|
| + int state_cmp = CompareConnectionStates(a, b, receiving_unchanged_threshold,
|
| + missed_receiving_unchanged_threshold);
|
| if (state_cmp != 0) {
|
| return state_cmp;
|
| }
|
| @@ -1143,11 +1186,11 @@ void P2PTransportChannel::SortConnectionsAndUpdateState() {
|
| // need to consider switching to.
|
| std::stable_sort(connections_.begin(), connections_.end(),
|
| [this](const Connection* a, const Connection* b) {
|
| - int cmp = CompareConnections(a, b);
|
| + int cmp = CompareConnections(
|
| + a, b, rtc::Optional<int64_t>(), nullptr);
|
| if (cmp != 0) {
|
| return cmp > 0;
|
| }
|
| -
|
| // Otherwise, sort based on latency estimate.
|
| return a->rtt() < b->rtt();
|
| });
|
| @@ -1164,11 +1207,7 @@ void P2PTransportChannel::SortConnectionsAndUpdateState() {
|
| // If necessary, switch to the new choice. Note that |top_connection| doesn't
|
| // have to be writable to become the selected connection although it will
|
| // have higher priority if it is writable.
|
| - if (ShouldSwitchSelectedConnection(top_connection)) {
|
| - LOG(LS_INFO) << "Switching selected connection after sorting: "
|
| - << top_connection->ToString();
|
| - SwitchSelectedConnection(top_connection);
|
| - }
|
| + MaybeSwitchSelectedConnection(top_connection, "sorting");
|
|
|
| // The controlled side can prune only if the selected connection has been
|
| // nominated because otherwise it may prune the connection that will be
|
| @@ -1622,7 +1661,7 @@ void P2PTransportChannel::OnConnectionDestroyed(Connection* connection) {
|
| // we can just set selected to nullptr and re-choose a best assuming that
|
| // there was no selected connection.
|
| if (selected_connection_ == connection) {
|
| - LOG(LS_INFO) << "selected connection destroyed. Will choose a new one.";
|
| + LOG(LS_INFO) << "Selected connection destroyed. Will choose a new one.";
|
| SwitchSelectedConnection(nullptr);
|
| RequestSortAndStateUpdate();
|
| } else {
|
| @@ -1726,11 +1765,8 @@ void P2PTransportChannel::OnReadPacket(Connection* connection,
|
|
|
| // May need to switch the sending connection based on the receiving media path
|
| // if this is the controlled side.
|
| - if (ice_role_ == ICEROLE_CONTROLLED &&
|
| - ShouldSwitchSelectedConnection(connection)) {
|
| - LOG(LS_INFO) << "Switching selected connection on controlled side due to "
|
| - << "data received: " << connection->ToString();
|
| - SwitchSelectedConnection(connection);
|
| + if (ice_role_ == ICEROLE_CONTROLLED) {
|
| + MaybeSwitchSelectedConnection(connection, "data received");
|
| }
|
| }
|
|
|
|
|