| Index: webrtc/p2p/base/p2ptransportchannel.cc | 
| diff --git a/webrtc/p2p/base/p2ptransportchannel.cc b/webrtc/p2p/base/p2ptransportchannel.cc | 
| index 6bcf0d7d2bfb09e9548501dccfde4e2ba249a5bd..370b03fb8ae193f6aa987b8cfeecb3ae1ba4f28a 100644 | 
| --- a/webrtc/p2p/base/p2ptransportchannel.cc | 
| +++ b/webrtc/p2p/base/p2ptransportchannel.cc | 
| @@ -66,6 +66,8 @@ namespace cricket { | 
| // well on a 28.8K modem, which is the slowest connection on which the voice | 
| // quality is reasonable at all. | 
| static const int PING_PACKET_SIZE = 60 * 8; | 
| + | 
| +// The next two ping intervals are at the channel level. | 
| // STRONG_PING_INTERVAL (480ms) is applied when the selected connection is both | 
| // writable and receiving. | 
| static const int STRONG_PING_INTERVAL = 1000 * PING_PACKET_SIZE / 1000; | 
| @@ -73,11 +75,13 @@ static const int STRONG_PING_INTERVAL = 1000 * PING_PACKET_SIZE / 1000; | 
| // not writable or not receiving. | 
| const int WEAK_PING_INTERVAL = 1000 * PING_PACKET_SIZE / 10000; | 
|  | 
| -// Writable connections are pinged at a faster rate while stabilizing. | 
| -const int STABILIZING_WRITABLE_CONNECTION_PING_INTERVAL = 900;  // ms | 
| - | 
| -// Writable connections are pinged at a slower rate once stabilized. | 
| -const int STABLE_WRITABLE_CONNECTION_PING_INTERVAL = 2500;  // ms | 
| +// The next two ping intervals are at the connection level. | 
| +// Writable connections are pinged at a faster rate while the connections are | 
| +// stabilizing or the channel is weak. | 
| +const int WEAK_OR_STABILIZING_WRITABLE_CONNECTION_PING_INTERVAL = 900;  // ms | 
| +// Writable connections are pinged at a slower rate once they are stabilized and | 
| +// the channel is strongly connected. | 
| +const int STRONG_AND_STABLE_WRITABLE_CONNECTION_PING_INTERVAL = 2500;  // ms | 
|  | 
| static const int MIN_CHECK_RECEIVING_INTERVAL = 50;  // ms | 
|  | 
| @@ -116,7 +120,7 @@ P2PTransportChannel::P2PTransportChannel(const std::string& transport_name, | 
| DEFAULT_BACKUP_CONNECTION_PING_INTERVAL, | 
| GATHER_ONCE /* continual_gathering_policy */, | 
| false /* prioritize_most_likely_candidate_pairs */, | 
| -              STABLE_WRITABLE_CONNECTION_PING_INTERVAL, | 
| +              STRONG_AND_STABLE_WRITABLE_CONNECTION_PING_INTERVAL, | 
| true /* presume_writable_when_fully_relayed */, | 
| DEFAULT_REGATHER_ON_FAILED_NETWORKS_INTERVAL, | 
| RECEIVING_SWITCHING_DELAY) { | 
| @@ -1305,6 +1309,24 @@ void P2PTransportChannel::SortConnectionsAndUpdateState() { | 
| MaybeStartPinging(); | 
| } | 
|  | 
| +std::map<rtc::Network*, Connection*> | 
| +P2PTransportChannel::GetPremierConnectionsByNetwork() const { | 
| +  // |connections_| has been sorted, so the first one in the list on a given | 
| +  // network is the premier connection, except that the selected connection | 
| +  // is always the premier connection on the network. | 
| +  std::map<rtc::Network*, Connection*> premier_connections_by_network; | 
| +  if (selected_connection_) { | 
| +    premier_connections_by_network[selected_connection_->port()->Network()] = | 
| +        selected_connection_; | 
| +  } | 
| +  for (Connection* conn : connections_) { | 
| +    rtc::Network* network = conn->port()->Network(); | 
| +    // This only inserts when the network does not exist in the map. | 
| +    premier_connections_by_network.insert(std::make_pair(network, conn)); | 
| +  } | 
| +  return premier_connections_by_network; | 
| +} | 
| + | 
| 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 | 
| @@ -1315,25 +1337,15 @@ void P2PTransportChannel::PruneConnections() { | 
| // switch. If the |premier| 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 CompareConnectionStates. | 
| - | 
| -  // 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* premier = GetBestConnectionOnNetwork(network); | 
| -    // Do not prune connections if the current selected connection is weak on | 
| +  auto premier_connections_by_network = GetPremierConnectionsByNetwork(); | 
| +  for (Connection* conn : connections_) { | 
| +    // Do not prune connections if the current premier connection is weak on | 
| // this network. Otherwise, it may delete connections prematurely. | 
| -    if (!premier || premier->weak()) { | 
| -      continue; | 
| -    } | 
| - | 
| -    for (Connection* conn : connections_) { | 
| -      if ((conn != premier) && (conn->port()->Network() == network) && | 
| -          (CompareConnectionCandidates(premier, conn) >= 0)) { | 
| -        conn->Prune(); | 
| -      } | 
| +    Connection* premier = | 
| +        premier_connections_by_network[conn->port()->Network()]; | 
| +    if (premier && conn != premier && !premier->weak() && | 
| +        CompareConnectionCandidates(premier, conn) >= 0) { | 
| +      conn->Prune(); | 
| } | 
| } | 
| } | 
| @@ -1471,26 +1483,6 @@ bool P2PTransportChannel::ReadyToSend(Connection* connection) const { | 
| PresumedWritable(connection)); | 
| } | 
|  | 
| -// If we have a selected connection, return it, otherwise return top one in the | 
| -// list (later we will mark it best). | 
| -Connection* P2PTransportChannel::GetBestConnectionOnNetwork( | 
| -    rtc::Network* network) const { | 
| -  // If the selected connection is on this network, then it wins. | 
| -  if (selected_connection_ && | 
| -      (selected_connection_->port()->Network() == network)) { | 
| -    return selected_connection_; | 
| -  } | 
| - | 
| -  // Otherwise, we return the top-most in sorted order. | 
| -  for (size_t i = 0; i < connections_.size(); ++i) { | 
| -    if (connections_[i]->port()->Network() == network) { | 
| -      return connections_[i]; | 
| -    } | 
| -  } | 
| - | 
| -  return NULL; | 
| -} | 
| - | 
| // Handle any queued up requests | 
| void P2PTransportChannel::OnMessage(rtc::Message *pmsg) { | 
| switch (pmsg->message_id) { | 
| @@ -1596,14 +1588,48 @@ bool P2PTransportChannel::IsPingable(const Connection* conn, | 
| return (now >= conn->last_ping_sent() + ping_interval); | 
| } | 
|  | 
| -bool P2PTransportChannel::IsSelectedConnectionPingable(int64_t now) { | 
| -  if (!selected_connection_ || !selected_connection_->connected() || | 
| -      !selected_connection_->writable()) { | 
| +Connection* P2PTransportChannel::FindBestPremierConnectionToPing(int64_t now) { | 
| +  // If the selected connection is pingable, select it to ping. | 
| +  if (IsPremierConnectionPingable(selected_connection_, now)) { | 
| +    return selected_connection_; | 
| +  } | 
| +  // If the selected connection is strongly connected, don't bother to ping | 
| +  // premier connections on other networks at a higher priority. This prevents | 
| +  // a few premier connections from saturating all transmission slots for ping | 
| +  // and also prevents the backup connections from being pinged frequently. | 
| +  if (!weak()) { | 
| +    return nullptr; | 
| +  } | 
| + | 
| +  // Otherwise, find the premier connection that has not been pinged for the | 
| +  // longest time. | 
| +  auto premier_connections_by_network = GetPremierConnectionsByNetwork(); | 
| +  Connection* oldest_pingable_premier_connection = nullptr; | 
| +  for (auto kv : premier_connections_by_network) { | 
| +    Connection* conn = kv.second; | 
| +    if (conn == nullptr || conn == selected_connection_) { | 
| +      continue; | 
| +    } | 
| +    if (IsPremierConnectionPingable(conn, now) && | 
| +        (!oldest_pingable_premier_connection || | 
| +         conn->last_ping_sent() < | 
| +             oldest_pingable_premier_connection->last_ping_sent())) { | 
| +      oldest_pingable_premier_connection = conn; | 
| +    } | 
| +  } | 
| +  return oldest_pingable_premier_connection; | 
| +} | 
| + | 
| +bool P2PTransportChannel::IsPremierConnectionPingable( | 
| +    Connection* premier_connection, | 
| +    int64_t now) { | 
| +  if (!premier_connection || !premier_connection->connected() || | 
| +      !premier_connection->writable()) { | 
| return false; | 
| } | 
|  | 
| -  int interval = CalculateActiveWritablePingInterval(selected_connection_, now); | 
| -  return selected_connection_->last_ping_sent() + interval <= now; | 
| +  int interval = CalculateActiveWritablePingInterval(premier_connection, now); | 
| +  return premier_connection->last_ping_sent() + interval <= now; | 
| } | 
|  | 
| int P2PTransportChannel::CalculateActiveWritablePingInterval( | 
| @@ -1616,10 +1642,12 @@ int P2PTransportChannel::CalculateActiveWritablePingInterval( | 
| } | 
|  | 
| int stable_interval = config_.stable_writable_connection_ping_interval; | 
| -  int stablizing_interval = | 
| -      std::min(stable_interval, STABILIZING_WRITABLE_CONNECTION_PING_INTERVAL); | 
| - | 
| -  return conn->stable(now) ? stable_interval : stablizing_interval; | 
| +  int weak_or_stablizing_interval = std::min( | 
| +      stable_interval, WEAK_OR_STABILIZING_WRITABLE_CONNECTION_PING_INTERVAL); | 
| +  // If the channel is weak or the connection is not stable yet, use the | 
| +  // weak_or_stablizing_interval. | 
| +  return (!weak() && conn->stable(now)) ? stable_interval | 
| +                                        : weak_or_stablizing_interval; | 
| } | 
|  | 
| // Returns the next pingable connection to ping.  This will be the oldest | 
| @@ -1631,10 +1659,8 @@ int P2PTransportChannel::CalculateActiveWritablePingInterval( | 
| // CompareConnectionStates. | 
| Connection* P2PTransportChannel::FindNextPingableConnection() { | 
| int64_t now = rtc::TimeMillis(); | 
| -  Connection* conn_to_ping = nullptr; | 
| -  if (IsSelectedConnectionPingable(now)) { | 
| -    conn_to_ping = selected_connection_; | 
| -  } else { | 
| +  Connection* conn_to_ping = FindBestPremierConnectionToPing(now); | 
| +  if (!conn_to_ping) { | 
| conn_to_ping = FindConnectionToPing(now); | 
| } | 
| return conn_to_ping; | 
| @@ -1749,8 +1775,9 @@ void P2PTransportChannel::OnConnectionDestroyed(Connection* connection) { | 
| unpinged_connections_.erase(*iter); | 
| connections_.erase(iter); | 
|  | 
| -  LOG_J(LS_INFO, this) << "Removed connection (" | 
| -    << static_cast<int>(connections_.size()) << " remaining)"; | 
| +  LOG_J(LS_INFO, this) << "Removed connection " << std::hex << connection | 
| +                       << std::dec << " (" << connections_.size() | 
| +                       << " remaining)"; | 
|  | 
| // If this is currently the selected connection, then we need to pick a new | 
| // one. The call to SortConnectionsAndUpdateState will pick a new one. It | 
| @@ -1878,7 +1905,7 @@ void P2PTransportChannel::OnReadyToSend(Connection* connection) { | 
|  | 
| // Find "triggered checks".  We ping first those connections that have | 
| // received a ping but have not sent a ping since receiving it | 
| -// (last_received_ping > last_sent_ping).  But we shouldn't do | 
| +// (last_ping_received > last_ping_sent).  But we shouldn't do | 
| // triggered checks if the connection is already writable. | 
| Connection* P2PTransportChannel::FindOldestConnectionNeedingTriggeredCheck( | 
| int64_t now) { | 
|  |