| Index: webrtc/p2p/base/p2ptransportchannel.cc
|
| diff --git a/webrtc/p2p/base/p2ptransportchannel.cc b/webrtc/p2p/base/p2ptransportchannel.cc
|
| index e7788e733ad4866e1f35a8949603673e70c9a06f..d6f3cbc3d20d84b85335ec541d58aad909e96db9 100644
|
| --- a/webrtc/p2p/base/p2ptransportchannel.cc
|
| +++ b/webrtc/p2p/base/p2ptransportchannel.cc
|
| @@ -31,6 +31,15 @@ enum { MSG_SORT = 1, MSG_CHECK_AND_PING };
|
| // The minimum improvement in RTT that justifies a switch.
|
| static const double kMinImprovement = 10;
|
|
|
| +bool IsRelayRelay(cricket::Connection* conn) {
|
| + return conn->local_candidate().type() == cricket::RELAY_PORT_TYPE &&
|
| + conn->remote_candidate().type() == cricket::RELAY_PORT_TYPE;
|
| +}
|
| +
|
| +bool IsUdp(cricket::Connection* conn) {
|
| + return conn->local_candidate().relay_protocol() == cricket::UDP_PROTOCOL_NAME;
|
| +}
|
| +
|
| cricket::PortInterface::CandidateOrigin GetOrigin(cricket::PortInterface* port,
|
| cricket::PortInterface* origin_port) {
|
| if (!origin_port)
|
| @@ -237,8 +246,11 @@ P2PTransportChannel::P2PTransportChannel(const std::string& transport_name,
|
| tiebreaker_(0),
|
| gathering_state_(kIceGatheringNew),
|
| check_receiving_delay_(MIN_CHECK_RECEIVING_DELAY * 5),
|
| - receiving_timeout_(MIN_CHECK_RECEIVING_DELAY * 50),
|
| - backup_connection_ping_interval_(0) {
|
| + config_(MIN_CHECK_RECEIVING_DELAY * 50 /* receiving_timeout */,
|
| + 0 /* backup_connection_ping_interval */,
|
| + false /* gather_continually */,
|
| + false /* prioritize_most_likely_candidate_pairs */,
|
| + MAX_CURRENT_STRONG_DELAY /* most_strong_delay */) {
|
| uint32_t weak_ping_delay = ::strtoul(
|
| webrtc::field_trial::FindFullName("WebRTC-StunInterPacketDelay").c_str(),
|
| nullptr, 10);
|
| @@ -277,8 +289,9 @@ void P2PTransportChannel::AddAllocatorSession(PortAllocatorSession* session) {
|
|
|
| void P2PTransportChannel::AddConnection(Connection* connection) {
|
| connections_.push_back(connection);
|
| + unpinged_connections_.insert(connection);
|
| connection->set_remote_ice_mode(remote_ice_mode_);
|
| - connection->set_receiving_timeout(receiving_timeout_);
|
| + connection->set_receiving_timeout(config_.receiving_timeout_ms);
|
| connection->SignalReadPacket.connect(
|
| this, &P2PTransportChannel::OnReadPacket);
|
| connection->SignalReadyToSend.connect(
|
| @@ -388,31 +401,47 @@ void P2PTransportChannel::SetRemoteIceMode(IceMode mode) {
|
| }
|
|
|
| void P2PTransportChannel::SetIceConfig(const IceConfig& config) {
|
| - gather_continually_ = config.gather_continually;
|
| - LOG(LS_INFO) << "Set gather_continually to " << gather_continually_;
|
| + config_.gather_continually = config.gather_continually;
|
| + LOG(LS_INFO) << "Set gather_continually to " << config_.gather_continually;
|
|
|
| if (config.backup_connection_ping_interval >= 0 &&
|
| - backup_connection_ping_interval_ !=
|
| + config_.backup_connection_ping_interval !=
|
| config.backup_connection_ping_interval) {
|
| - backup_connection_ping_interval_ = config.backup_connection_ping_interval;
|
| + config_.backup_connection_ping_interval =
|
| + config.backup_connection_ping_interval;
|
| LOG(LS_INFO) << "Set backup connection ping interval to "
|
| - << backup_connection_ping_interval_ << " milliseconds.";
|
| + << config_.backup_connection_ping_interval << " milliseconds.";
|
| }
|
|
|
| if (config.receiving_timeout_ms >= 0 &&
|
| - receiving_timeout_ != config.receiving_timeout_ms) {
|
| - receiving_timeout_ = config.receiving_timeout_ms;
|
| + config_.receiving_timeout_ms != config.receiving_timeout_ms) {
|
| + config_.receiving_timeout_ms = config.receiving_timeout_ms;
|
| check_receiving_delay_ =
|
| - std::max(MIN_CHECK_RECEIVING_DELAY, receiving_timeout_ / 10);
|
| + std::max(MIN_CHECK_RECEIVING_DELAY, config_.receiving_timeout_ms / 10);
|
|
|
| for (Connection* connection : connections_) {
|
| - connection->set_receiving_timeout(receiving_timeout_);
|
| + connection->set_receiving_timeout(config_.receiving_timeout_ms);
|
| }
|
| - LOG(LS_INFO) << "Set ICE receiving timeout to " << receiving_timeout_
|
| - << " milliseconds";
|
| + LOG(LS_INFO) << "Set ICE receiving timeout to "
|
| + << config_.receiving_timeout_ms << " milliseconds";
|
| + }
|
| +
|
| + config_.prioritize_most_likely_candidate_pairs =
|
| + config.prioritize_most_likely_candidate_pairs;
|
| + LOG(LS_INFO) << "Set ping most likely connection to "
|
| + << config_.prioritize_most_likely_candidate_pairs;
|
| +
|
| + if (config.max_strong_delay >= 0 &&
|
| + config_.max_strong_delay != config.max_strong_delay) {
|
| + config_.max_strong_delay = config.max_strong_delay;
|
| + LOG(LS_INFO) << "Set max strong delay to " << config_.max_strong_delay;
|
| }
|
| }
|
|
|
| +const IceConfig& P2PTransportChannel::config() const {
|
| + return config_;
|
| +}
|
| +
|
| // Go into the state of processing candidates, and running in general
|
| void P2PTransportChannel::Connect() {
|
| ASSERT(worker_thread_ == rtc::Thread::Current());
|
| @@ -629,8 +658,8 @@ void P2PTransportChannel::OnUnknownAddress(
|
| }
|
| }
|
|
|
| - Connection* connection = port->CreateConnection(
|
| - remote_candidate, cricket::PortInterface::ORIGIN_THIS_PORT);
|
| + Connection* connection =
|
| + port->CreateConnection(remote_candidate, PortInterface::ORIGIN_THIS_PORT);
|
| if (!connection) {
|
| ASSERT(false);
|
| port->SendBindingErrorResponse(stun_msg, address, STUN_ERROR_SERVER_ERROR,
|
| @@ -807,7 +836,7 @@ bool P2PTransportChannel::CreateConnection(PortInterface* port,
|
|
|
| // Don't create connection if this is a candidate we received in a
|
| // message and we are not allowed to make outgoing connections.
|
| - if (origin == cricket::PortInterface::ORIGIN_MESSAGE && incoming_only_)
|
| + if (origin == PortInterface::ORIGIN_MESSAGE && incoming_only_)
|
| return false;
|
|
|
| connection = port->CreateConnection(remote_candidate, origin);
|
| @@ -823,8 +852,7 @@ bool P2PTransportChannel::CreateConnection(PortInterface* port,
|
| return true;
|
| }
|
|
|
| -bool P2PTransportChannel::FindConnection(
|
| - cricket::Connection* connection) const {
|
| +bool P2PTransportChannel::FindConnection(Connection* connection) const {
|
| std::vector<Connection*>::const_iterator citer =
|
| std::find(connections_.begin(), connections_.end(), connection);
|
| return citer != connections_.end();
|
| @@ -1154,7 +1182,7 @@ void P2PTransportChannel::MaybeStopPortAllocatorSessions() {
|
| }
|
| // If gathering continually, keep the last session running so that it
|
| // will gather candidates if the networks change.
|
| - if (gather_continually_ && session == allocator_sessions_.back()) {
|
| + if (config_.gather_continually && session == allocator_sessions_.back()) {
|
| session->ClearGettingPorts();
|
| break;
|
| }
|
| @@ -1223,6 +1251,7 @@ void P2PTransportChannel::OnCheckAndPing() {
|
| Connection* conn = FindNextPingableConnection();
|
| if (conn) {
|
| PingConnection(conn);
|
| + MarkConnectionPinged(conn);
|
| }
|
| }
|
| int check_delay = std::min(ping_delay, check_receiving_delay_);
|
| @@ -1264,7 +1293,7 @@ bool P2PTransportChannel::IsPingable(Connection* conn, uint32_t now) {
|
| // or not, but backup connections are pinged at a slower rate.
|
| if (IsBackupConnection(conn)) {
|
| return (now >= conn->last_ping_response_received() +
|
| - backup_connection_ping_interval_);
|
| + config_.backup_connection_ping_interval);
|
| }
|
| return conn->active();
|
| }
|
| @@ -1277,42 +1306,21 @@ bool P2PTransportChannel::IsPingable(Connection* conn, uint32_t now) {
|
| // target to become writable instead. See the big comment in CompareConnections.
|
| Connection* P2PTransportChannel::FindNextPingableConnection() {
|
| uint32_t now = rtc::Time();
|
| + Connection* conn_to_ping = nullptr;
|
| if (best_connection_ && best_connection_->connected() &&
|
| best_connection_->writable() &&
|
| - (best_connection_->last_ping_sent() + MAX_CURRENT_STRONG_DELAY <= now)) {
|
| - return best_connection_;
|
| - }
|
| -
|
| - // First, 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
|
| - // triggered checks if the connection is already writable.
|
| - Connection* oldest_needing_triggered_check = nullptr;
|
| - Connection* oldest = nullptr;
|
| - for (Connection* conn : connections_) {
|
| - if (!IsPingable(conn, now)) {
|
| - continue;
|
| - }
|
| - bool needs_triggered_check =
|
| - (!conn->writable() &&
|
| - conn->last_ping_received() > conn->last_ping_sent());
|
| - if (needs_triggered_check &&
|
| - (!oldest_needing_triggered_check ||
|
| - (conn->last_ping_received() <
|
| - oldest_needing_triggered_check->last_ping_received()))) {
|
| - oldest_needing_triggered_check = conn;
|
| - }
|
| - if (!oldest || (conn->last_ping_sent() < oldest->last_ping_sent())) {
|
| - oldest = conn;
|
| - }
|
| + (best_connection_->last_ping_sent() + config_.max_strong_delay <= now)) {
|
| + conn_to_ping = best_connection_;
|
| + } else {
|
| + conn_to_ping = FindConnectionToPing(now);
|
| }
|
| + return conn_to_ping;
|
| +}
|
|
|
| - if (oldest_needing_triggered_check) {
|
| - LOG(LS_INFO) << "Selecting connection for triggered check: " <<
|
| - oldest_needing_triggered_check->ToString();
|
| - return oldest_needing_triggered_check;
|
| +void P2PTransportChannel::MarkConnectionPinged(Connection* conn) {
|
| + if (conn && pinged_connections_.insert(conn).second) {
|
| + unpinged_connections_.erase(conn);
|
| }
|
| - return oldest;
|
| }
|
|
|
| // Apart from sending ping from |conn| this method also updates
|
| @@ -1382,6 +1390,8 @@ void P2PTransportChannel::OnConnectionDestroyed(Connection* connection) {
|
| std::vector<Connection*>::iterator iter =
|
| std::find(connections_.begin(), connections_.end(), connection);
|
| ASSERT(iter != connections_.end());
|
| + pinged_connections_.erase(*iter);
|
| + unpinged_connections_.erase(*iter);
|
| connections_.erase(iter);
|
|
|
| LOG_J(LS_INFO, this) << "Removed connection ("
|
| @@ -1426,7 +1436,7 @@ void P2PTransportChannel::OnPortDestroyed(PortInterface* port) {
|
| void P2PTransportChannel::OnPortNetworkInactive(PortInterface* port) {
|
| // If it does not gather continually, the port will be removed from the list
|
| // when ICE restarts.
|
| - if (!gather_continually_) {
|
| + if (!config_.gather_continually) {
|
| return;
|
| }
|
| auto it = std::find(ports_.begin(), ports_.end(), port);
|
| @@ -1474,4 +1484,121 @@ 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
|
| +// triggered checks if the connection is already writable.
|
| +Connection* P2PTransportChannel::FindOldestConnectionNeedingTriggeredCheck(
|
| + uint32_t now) {
|
| + Connection* oldest_needing_triggered_check = nullptr;
|
| + for (auto conn : connections_) {
|
| + if (!IsPingable(conn, now)) {
|
| + continue;
|
| + }
|
| + bool needs_triggered_check =
|
| + (!conn->writable() &&
|
| + conn->last_ping_received() > conn->last_ping_sent());
|
| + if (needs_triggered_check &&
|
| + (!oldest_needing_triggered_check ||
|
| + (conn->last_ping_received() <
|
| + oldest_needing_triggered_check->last_ping_received()))) {
|
| + oldest_needing_triggered_check = conn;
|
| + }
|
| + }
|
| +
|
| + if (oldest_needing_triggered_check) {
|
| + LOG(LS_INFO) << "Selecting connection for triggered check: "
|
| + << oldest_needing_triggered_check->ToString();
|
| + }
|
| + return oldest_needing_triggered_check;
|
| +}
|
| +
|
| +Connection* P2PTransportChannel::FindConnectionToPing(uint32_t now) {
|
| + RTC_CHECK(connections_.size() ==
|
| + pinged_connections_.size() + unpinged_connections_.size());
|
| +
|
| + // If there is nothing pingable in the |unpinged_connections_|, copy
|
| + // over from |pinged_connections_|. We do this here such that the
|
| + // new connection will take precedence.
|
| + if (std::find_if(unpinged_connections_.begin(), unpinged_connections_.end(),
|
| + [this, now](Connection* conn) {
|
| + return this->IsPingable(conn, now);
|
| + }) == unpinged_connections_.end()) {
|
| + unpinged_connections_.insert(pinged_connections_.begin(),
|
| + pinged_connections_.end());
|
| + pinged_connections_.clear();
|
| + }
|
| +
|
| + Connection* conn_to_ping = FindOldestConnectionNeedingTriggeredCheck(now);
|
| + if (conn_to_ping) {
|
| + return conn_to_ping;
|
| + }
|
| +
|
| + for (Connection* conn : unpinged_connections_) {
|
| + if (!IsPingable(conn, now)) {
|
| + continue;
|
| + }
|
| + if (!conn_to_ping ||
|
| + SelectMostPingableConnection(conn_to_ping, conn) == conn) {
|
| + conn_to_ping = conn;
|
| + }
|
| + }
|
| + return conn_to_ping;
|
| +}
|
| +
|
| +Connection* P2PTransportChannel::MostLikelyToWork(Connection* conn1,
|
| + Connection* conn2) {
|
| + bool rr1 = IsRelayRelay(conn1);
|
| + bool rr2 = IsRelayRelay(conn2);
|
| + if (rr1 && !rr2) {
|
| + return conn1;
|
| + } else if (rr2 && !rr1) {
|
| + return conn2;
|
| + } else if (rr1 && rr2) {
|
| + bool udp1 = IsUdp(conn1);
|
| + bool udp2 = IsUdp(conn2);
|
| + if (udp1 && !udp2) {
|
| + return conn1;
|
| + } else if (udp2 && udp1) {
|
| + return conn2;
|
| + }
|
| + }
|
| + return nullptr;
|
| +}
|
| +
|
| +Connection* P2PTransportChannel::LeastRecentlyPinged(Connection* conn1,
|
| + Connection* conn2) {
|
| + if (conn1->last_ping_sent() < conn2->last_ping_sent()) {
|
| + return conn1;
|
| + }
|
| + if (conn1->last_ping_sent() > conn2->last_ping_sent()) {
|
| + return conn2;
|
| + }
|
| + return nullptr;
|
| +}
|
| +
|
| +Connection* P2PTransportChannel::SelectMostPingableConnection(
|
| + Connection* conn1,
|
| + Connection* conn2) {
|
| + RTC_DCHECK(conn1 != conn2);
|
| + if (config_.prioritize_most_likely_candidate_pairs) {
|
| + Connection* most_likely_to_work_conn = MostLikelyToWork(conn1, conn2);
|
| + if (most_likely_to_work_conn) {
|
| + return most_likely_to_work_conn;
|
| + }
|
| + }
|
| +
|
| + Connection* least_recently_pinged_conn = LeastRecentlyPinged(conn1, conn2);
|
| + if (least_recently_pinged_conn) {
|
| + return least_recently_pinged_conn;
|
| + }
|
| +
|
| + // During the initial state when nothing has been pinged yet, return the first
|
| + // one in the ordered |connections_|.
|
| + return *(std::find_if(connections_.begin(), connections_.end(),
|
| + [conn1, conn2](Connection* conn) {
|
| + return conn == conn1 || conn == conn2;
|
| + }));
|
| +}
|
| +
|
| } // namespace cricket
|
|
|