Index: webrtc/p2p/base/p2ptransportchannel.cc |
diff --git a/webrtc/p2p/base/p2ptransportchannel.cc b/webrtc/p2p/base/p2ptransportchannel.cc |
index 732e527b789e9639a9525af5b92a35046c3a9e29..094a8dcc8f1ffc937a436dc85890633c62d60760 100644 |
--- a/webrtc/p2p/base/p2ptransportchannel.cc |
+++ b/webrtc/p2p/base/p2ptransportchannel.cc |
@@ -229,8 +229,7 @@ void P2PTransportChannel::AddConnection(Connection* connection) { |
this, &P2PTransportChannel::OnConnectionStateChange); |
connection->SignalDestroyed.connect( |
this, &P2PTransportChannel::OnConnectionDestroyed); |
- connection->SignalUseCandidate.connect( |
- this, &P2PTransportChannel::OnUseCandidate); |
+ connection->SignalNominated.connect(this, &P2PTransportChannel::OnNominated); |
} |
void P2PTransportChannel::SetIceRole(IceRole ice_role) { |
@@ -522,7 +521,7 @@ void P2PTransportChannel::OnUnknownAddress( |
// There shouldn't be an existing connection with this remote address. |
// When ports are muxed, this channel might get multiple unknown address |
// signals. In that case if the connection is already exists, we should |
- // simply ignore the signal othewise send server error. |
+ // simply ignore the signal otherwise send server error. |
if (port->GetConnection(remote_candidate.address())) { |
if (port_muxed) { |
LOG(LS_INFO) << "Connection already exists for peer reflexive " |
@@ -553,6 +552,13 @@ void P2PTransportChannel::OnUnknownAddress( |
AddConnection(connection); |
connection->ReceivedPing(); |
+ bool received_use_candidate = |
+ stun_msg->GetByteString(STUN_ATTR_USE_CANDIDATE) != nullptr; |
+ if (received_use_candidate && ice_role_ == ICEROLE_CONTROLLED) { |
+ connection->set_nominated(true); |
+ OnNominated(connection); |
+ } |
+ |
// Update the list of connections since we just added another. We do this |
// after sending the response since it could (in principle) delete the |
// connection in question. |
@@ -574,7 +580,7 @@ void P2PTransportChannel::OnSignalingReady() { |
} |
} |
-void P2PTransportChannel::OnUseCandidate(Connection* conn) { |
+void P2PTransportChannel::OnNominated(Connection* conn) { |
ASSERT(worker_thread_ == rtc::Thread::Current()); |
ASSERT(ice_role_ == ICEROLE_CONTROLLED); |
@@ -917,16 +923,10 @@ void P2PTransportChannel::SortConnections() { |
// Any changes after this point will require a re-sort. |
sort_dirty_ = false; |
- // Get a list of the networks that we are using. |
- std::set<rtc::Network*> networks; |
- for (uint32 i = 0; i < connections_.size(); ++i) |
- networks.insert(connections_[i]->port()->Network()); |
- |
// Find the best alternative connection by sorting. It is important to note |
// that amongst equal preference, writable connections, this will choose the |
// one whose estimated latency is lowest. So it is the only one that we |
// need to consider switching to. |
- |
ConnectionCompare cmp; |
std::stable_sort(connections_.begin(), connections_.end(), cmp); |
LOG(LS_VERBOSE) << "Sorting available connections:"; |
@@ -934,46 +934,25 @@ void P2PTransportChannel::SortConnections() { |
LOG(LS_VERBOSE) << connections_[i]->ToString(); |
} |
- Connection* top_connection = NULL; |
- if (connections_.size() > 0) |
- top_connection = connections_[0]; |
- |
- // We don't want to pick the best connections if channel is |
- // CONTROLLED, as connections will be selected by the CONTROLLING |
- // agent. |
+ Connection* top_connection = |
+ (connections_.size() > 0) ? connections_[0] : nullptr; |
// If necessary, switch to the new choice. |
- if (ice_role_ == ICEROLE_CONTROLLING) { |
- if (ShouldSwitch(best_connection_, top_connection)) { |
- LOG(LS_INFO) << "Switching best connection on controlling side: " |
- << top_connection->ToString(); |
- SwitchBestConnectionTo(top_connection); |
- } |
+ // Note that |top_connection| doesn't have to be writable to become the best |
+ // connection although it will have higher priority if it is writable. |
+ // The controlled side can switch the best connection only if the current |
+ // |best connection_| has not been nominated by the controlling side yet. |
+ if ((ice_role_ == ICEROLE_CONTROLLING || !best_nominated_connection()) && |
+ ShouldSwitch(best_connection_, top_connection)) { |
+ LOG(LS_INFO) << "Switching best connection: " << top_connection->ToString(); |
+ SwitchBestConnectionTo(top_connection); |
} |
- // We can prune any connection for which there is a connected, writable |
- // connection on the same network with better or equal priority. We leave |
- // those with better priority just in case they become writable later (at |
- // which point, we would prune out the current best connection). We leave |
- // connections on other networks because they may not be using the same |
- // resources and they may represent very distinct paths over which we can |
- // switch. If the |primier| connection is not connected, we may be |
- // reconnecting a TCP connection and temporarily do not prune connections in |
- // this network. See the big comment in CompareConnections. |
- std::set<rtc::Network*>::iterator network; |
- for (network = networks.begin(); network != networks.end(); ++network) { |
- Connection* primier = GetBestConnectionOnNetwork(*network); |
- if (!primier || (primier->write_state() != Connection::STATE_WRITABLE) || |
- !primier->connected()) |
- continue; |
- |
- for (uint32 i = 0; i < connections_.size(); ++i) { |
- if ((connections_[i] != primier) && |
- (connections_[i]->port()->Network() == *network) && |
- (CompareConnectionCandidates(primier, connections_[i]) >= 0)) { |
- connections_[i]->Prune(); |
- } |
- } |
+ // Controlled side can prune only if the best connection has been nominated. |
+ // because otherwise it may delete the connection that will be selected by |
+ // the controlling side. |
+ if (ice_role_ == ICEROLE_CONTROLLING || best_nominated_connection()) { |
+ PruneConnections(); |
} |
// Check if all connections are timedout. |
@@ -1000,6 +979,41 @@ void P2PTransportChannel::SortConnections() { |
UpdateChannelState(); |
} |
+Connection* P2PTransportChannel::best_nominated_connection() const { |
+ return (best_connection_ && best_connection_->nominated()) ? best_connection_ |
+ : nullptr; |
+} |
+ |
+void P2PTransportChannel::PruneConnections() { |
+ // We can prune any connection for which there is a connected, writable |
+ // connection on the same network with better or equal priority. We leave |
+ // those with better priority just in case they become writable later (at |
+ // which point, we would prune out the current best connection). We leave |
+ // connections on other networks because they may not be using the same |
+ // resources and they may represent very distinct paths over which we can |
+ // switch. If the |primier| connection is not connected, we may be |
+ // reconnecting a TCP connection and temporarily do not prune connections in |
+ // this network. See the big comment in CompareConnections. |
+ |
+ // Get a list of the networks that we are using. |
+ std::set<rtc::Network*> networks; |
+ for (const Connection* conn : connections_) { |
+ networks.insert(conn->port()->Network()); |
+ } |
+ for (rtc::Network* network : networks) { |
+ Connection* primier = GetBestConnectionOnNetwork(network); |
+ if (!(primier && primier->writable() && primier->connected())) { |
+ continue; |
+ } |
+ |
+ for (Connection* conn : connections_) { |
+ if ((conn != primier) && (conn->port()->Network() == network) && |
+ (CompareConnectionCandidates(primier, conn) >= 0)) { |
+ conn->Prune(); |
+ } |
+ } |
+ } |
+} |
// Track the best connection, and let listeners know |
void P2PTransportChannel::SwitchBestConnectionTo(Connection* conn) { |
@@ -1332,6 +1346,13 @@ void P2PTransportChannel::OnReadPacket( |
// Let the client know of an incoming packet |
SignalReadPacket(this, data, len, packet_time, 0); |
+ |
+ // 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) { |
+ SwitchBestConnectionTo(connection); |
+ } |
} |
void P2PTransportChannel::OnReadyToSend(Connection* connection) { |