Index: webrtc/p2p/base/p2ptransportchannel.cc |
diff --git a/webrtc/p2p/base/p2ptransportchannel.cc b/webrtc/p2p/base/p2ptransportchannel.cc |
index fc1f0a4e6769ee96ce28f851d0f7cbc368fbf69c..7b5e1e70e9b6af1c51c35f7d02b79ec5354e13d9 100644 |
--- a/webrtc/p2p/base/p2ptransportchannel.cc |
+++ b/webrtc/p2p/base/p2ptransportchannel.cc |
@@ -200,6 +200,39 @@ bool ShouldSwitch(cricket::Connection* a_conn, |
return b_conn->rtt() <= a_conn->rtt() + kMinImprovement; |
} |
+// Determines whether we should switch the best connection to |new_connection| |
+// based the writable state, the last nominated connection, and the last |
+// connection receiving data. This is to prevent that the controlled side may |
+// switch the best connection too frequently when the controlling side is doing |
+// aggressive nominations. With this implementation, the controlled side will |
+// switch the best connection if the new nominated connection is writable, and |
+// i) the best connection is nullptr, or |
+// ii) the best connection was not nominated, or |
+// iii) the new connection has been nominated twice consecutively, or |
+// iv) the new connection is nominated and is receiving data, or |
+// v) the new connection has lower cost or high priority. |
+// TODO(honghaiz): Stop the aggressive nomination on the controlling side and |
+// implement the ice-renomination option. |
+bool ShouldSwitchOnNominatedOrDataReceived( |
+ cricket::Connection* best_connection, |
+ cricket::Connection* new_connection, |
+ cricket::Connection* last_nominated, |
+ cricket::Connection* last_data_received) { |
+ RTC_CHECK(new_connection != nullptr); |
+ if (!new_connection->writable()) { |
+ return false; |
+ } |
+ if (best_connection == nullptr || !best_connection->nominated()) { |
+ return true; |
+ } |
+ if (new_connection == last_nominated || |
+ new_connection == last_data_received) { |
+ return true; |
+ } |
+ // Compare the network cost and priority. |
+ return CompareConnectionCandidates(best_connection, new_connection) < 0; |
+} |
+ |
} // unnamed namespace |
namespace cricket { |
@@ -238,8 +271,6 @@ P2PTransportChannel::P2PTransportChannel(const std::string& transport_name, |
worker_thread_(rtc::Thread::Current()), |
incoming_only_(false), |
error_(0), |
- best_connection_(NULL), |
- pending_best_connection_(NULL), |
sort_dirty_(false), |
remote_ice_mode_(ICEMODE_FULL), |
ice_role_(ICEROLE_UNKNOWN), |
@@ -729,20 +760,25 @@ void P2PTransportChannel::OnNominated(Connection* conn) { |
ASSERT(worker_thread_ == rtc::Thread::Current()); |
ASSERT(ice_role_ == ICEROLE_CONTROLLED); |
- if (conn->write_state() == Connection::STATE_WRITABLE) { |
- if (best_connection_ != conn) { |
- pending_best_connection_ = NULL; |
- LOG(LS_INFO) << "Switching best connection on controlled side: " |
- << conn->ToString(); |
- SwitchBestConnectionTo(conn); |
- // Now we have selected the best connection, time to prune other existing |
- // connections and update the read/write state of the channel. |
- RequestSort(); |
- } |
+ if (best_connection_ == conn) { |
+ return; |
+ } |
+ |
+ if (ShouldSwitchOnNominatedOrDataReceived(best_connection_, conn, |
+ last_nominated_conn_, |
+ last_data_received_conn_)) { |
+ last_nominated_conn_ = nullptr; |
+ last_data_received_conn_ = nullptr; |
+ LOG(LS_INFO) << "Switching best connection on controlled side: " |
+ << conn->ToString(); |
+ SwitchBestConnectionTo(conn); |
+ // Now we have selected the best connection, time to prune other existing |
+ // connections and update the read/write state of the channel. |
+ RequestSort(); |
} else { |
- LOG(LS_INFO) << "Not switching the best connection on controlled side yet," |
- << " because it's not writable: " << conn->ToString(); |
- pending_best_connection_ = conn; |
+ LOG(LS_INFO) << "Not switching the best connection on controlled side yet: " |
+ << conn->ToString(); |
+ last_nominated_conn_ = conn; |
} |
} |
@@ -1444,9 +1480,18 @@ void P2PTransportChannel::OnConnectionStateChange(Connection* connection) { |
// Update the best connection if the state change is from pending best |
// connection and role is controlled. |
- if (ice_role_ == ICEROLE_CONTROLLED) { |
- if (connection == pending_best_connection_ && connection->writable()) { |
- pending_best_connection_ = NULL; |
+ if (ice_role_ == ICEROLE_CONTROLLED && connection == last_nominated_conn_) { |
+ // This essentially requires that the connection is both last nominated |
+ // and receiving data, or the best connection is nullptr, in order |
+ // to switch to |connection|. |
+ if (connection == best_connection_) { |
+ // If it is already the best connection, reset the last_nominated_conn_ |
+ last_nominated_conn_ = nullptr; |
+ } else if (ShouldSwitchOnNominatedOrDataReceived( |
+ best_connection_, connection, nullptr, |
+ last_data_received_conn_)) { |
+ last_nominated_conn_ = nullptr; |
+ last_data_received_conn_ = nullptr; |
LOG(LS_INFO) << "Switching best connection on controlled side" |
<< " because it's now writable: " << connection->ToString(); |
SwitchBestConnectionTo(connection); |
@@ -1489,8 +1534,8 @@ void P2PTransportChannel::OnConnectionDestroyed(Connection* connection) { |
LOG_J(LS_INFO, this) << "Removed connection (" |
<< static_cast<int>(connections_.size()) << " remaining)"; |
- if (pending_best_connection_ == connection) { |
- pending_best_connection_ = NULL; |
+ if (last_nominated_conn_ == connection) { |
+ last_nominated_conn_ = nullptr; |
} |
// If this is currently the best connection, then we need to pick a new one. |
@@ -1559,9 +1604,17 @@ 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 && !best_nominated_connection() && |
- connection->writable() && best_connection_ != connection) { |
+ if (ice_role_ != ICEROLE_CONTROLLED || best_connection_ == connection) { |
+ return; |
+ } |
+ |
+ if (ShouldSwitchOnNominatedOrDataReceived(best_connection_, connection, |
+ last_nominated_conn_, nullptr)) { |
SwitchBestConnectionTo(connection); |
+ last_data_received_conn_ = nullptr; |
+ last_nominated_conn_ = nullptr; |
+ } else { |
+ last_data_received_conn_ = connection; |
} |
} |