| 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;
|
| }
|
| }
|
|
|
|
|