| Index: webrtc/p2p/base/p2ptransportchannel.cc
|
| diff --git a/webrtc/p2p/base/p2ptransportchannel.cc b/webrtc/p2p/base/p2ptransportchannel.cc
|
| index e7f5c941c403825362799f9fe82bcc44ecf1a196..cd1af82c8d59365a47af2647869680ae6c98c20c 100644
|
| --- a/webrtc/p2p/base/p2ptransportchannel.cc
|
| +++ b/webrtc/p2p/base/p2ptransportchannel.cc
|
| @@ -10,8 +10,8 @@
|
|
|
| #include "webrtc/p2p/base/p2ptransportchannel.h"
|
|
|
| -#include <set>
|
| #include <algorithm>
|
| +#include <set>
|
| #include "webrtc/p2p/base/common.h"
|
| #include "webrtc/p2p/base/relayport.h" // For RELAY_PORT_TYPE.
|
| #include "webrtc/p2p/base/stunport.h" // For STUN_PORT_TYPE.
|
| @@ -23,11 +23,7 @@
|
| namespace {
|
|
|
| // messages for queuing up work for ourselves
|
| -enum {
|
| - MSG_SORT = 1,
|
| - MSG_PING,
|
| - MSG_CHECK_RECEIVING
|
| -};
|
| +enum { MSG_SORT = 1, MSG_CHECK_AND_PING };
|
|
|
| // When the socket is unwritable, we will use 10 Kbps (ignoring IP+UDP headers)
|
| // for pinging. When the socket is writable, we will use only 1 Kbps because
|
| @@ -35,12 +31,17 @@ enum {
|
| // well on a 28.8K modem, which is the slowest connection on which the voice
|
| // quality is reasonable at all.
|
| static const uint32 PING_PACKET_SIZE = 60 * 8;
|
| -static const uint32 WRITABLE_DELAY = 1000 * PING_PACKET_SIZE / 1000; // 480ms
|
| -static const uint32 UNWRITABLE_DELAY = 1000 * PING_PACKET_SIZE / 10000; // 50ms
|
| -
|
| -// If there is a current writable connection, then we will also try hard to
|
| -// make sure it is pinged at this rate.
|
| -static const uint32 MAX_CURRENT_WRITABLE_DELAY = 900; // 2*WRITABLE_DELAY - bit
|
| +// STRONG_PING_DELAY (480ms) is applied when the best connection is both
|
| +// writable and receiving.
|
| +static const uint32 STRONG_PING_DELAY = 1000 * PING_PACKET_SIZE / 1000;
|
| +// WEAK_PING_DELAY (48ms) is applied when the best connection is either not
|
| +// writable or not receiving.
|
| +static const uint32 WEAK_PING_DELAY = 1000 * PING_PACKET_SIZE / 10000;
|
| +
|
| +// If the current best connection is both writable and receiving, then we will
|
| +// also try hard to make sure it is pinged at this rate (a little less than
|
| +// 2 * STRONG_PING_DELAY).
|
| +static const uint32 MAX_CURRENT_STRONG_DELAY = 900;
|
|
|
| static const int MIN_CHECK_RECEIVING_DELAY = 50; // ms
|
|
|
| @@ -71,15 +72,23 @@ int CompareConnectionCandidates(cricket::Connection* a,
|
| (b->remote_candidate().generation() + b->port()->generation());
|
| }
|
|
|
| -// Compare two connections based on their connected state, writability and
|
| -// static preferences.
|
| -int CompareConnections(cricket::Connection *a, cricket::Connection *b) {
|
| +// Compare two connections based on their writing, receiving, and connected
|
| +// states.
|
| +int CompareConnectionStates(cricket::Connection* a, cricket::Connection* b) {
|
| // Sort based on write-state. Better states have lower values.
|
| if (a->write_state() < b->write_state())
|
| return 1;
|
| if (a->write_state() > b->write_state())
|
| return -1;
|
|
|
| + // We prefer a receiving connection to a non-receiving, higher-priority
|
| + // connection when sorting connections and choosing which connection to
|
| + // switch to.
|
| + if (a->receiving() && !b->receiving())
|
| + return 1;
|
| + if (!a->receiving() && b->receiving())
|
| + return -1;
|
| +
|
| // WARNING: Some complexity here about TCP reconnecting.
|
| // When a TCP connection fails because of a TCP socket disconnecting, the
|
| // active side of the connection will attempt to reconnect for 5 seconds while
|
| @@ -111,7 +120,14 @@ int CompareConnections(cricket::Connection *a, cricket::Connection *b) {
|
| return -1;
|
| }
|
| }
|
| + return 0;
|
| +}
|
|
|
| +int CompareConnections(cricket::Connection* a, cricket::Connection* b) {
|
| + int state_cmp = CompareConnectionStates(a, b);
|
| + if (state_cmp != 0) {
|
| + return state_cmp;
|
| + }
|
| // Compare the candidate information.
|
| return CompareConnectionCandidates(a, b);
|
| }
|
| @@ -149,19 +165,33 @@ class ConnectionCompare {
|
| };
|
|
|
| // Determines whether we should switch between two connections, based first on
|
| -// static preferences and then (if those are equal) on latency estimates.
|
| -bool ShouldSwitch(cricket::Connection* a_conn, cricket::Connection* b_conn) {
|
| +// connection states, static preferences, and then (if those are equal) on
|
| +// latency estimates.
|
| +bool ShouldSwitch(cricket::Connection* a_conn,
|
| + cricket::Connection* b_conn,
|
| + cricket::IceRole ice_role) {
|
| if (a_conn == b_conn)
|
| return false;
|
|
|
| if (!a_conn || !b_conn) // don't think the latter should happen
|
| return true;
|
|
|
| - int prefs_cmp = CompareConnections(a_conn, b_conn);
|
| - if (prefs_cmp < 0)
|
| - return true;
|
| - if (prefs_cmp > 0)
|
| + // We prefer to switch to a writable and receiving connection over a
|
| + // non-writable or non-receiving connection, even if the latter has
|
| + // been nominated by the controlling side.
|
| + int state_cmp = CompareConnectionStates(a_conn, b_conn);
|
| + if (state_cmp != 0) {
|
| + return state_cmp < 0;
|
| + }
|
| + if (ice_role == cricket::ICEROLE_CONTROLLED && a_conn->nominated()) {
|
| + LOG(LS_VERBOSE) << "Controlled side did not switch due to nominated status";
|
| return false;
|
| + }
|
| +
|
| + int prefs_cmp = CompareConnectionCandidates(a_conn, b_conn);
|
| + if (prefs_cmp != 0) {
|
| + return prefs_cmp < 0;
|
| + }
|
|
|
| return b_conn->rtt() <= a_conn->rtt() + kMinImprovement;
|
| }
|
| @@ -350,11 +380,8 @@ void P2PTransportChannel::Connect() {
|
| return;
|
| }
|
|
|
| - // Start pinging as the ports come in.
|
| - thread()->Post(this, MSG_PING);
|
| -
|
| - thread()->PostDelayed(
|
| - check_receiving_delay_, this, MSG_CHECK_RECEIVING);
|
| + // Start checking and pinging as the ports come in.
|
| + thread()->Post(this, MSG_CHECK_AND_PING);
|
| }
|
|
|
| void P2PTransportChannel::MaybeStartGathering() {
|
| @@ -913,7 +940,8 @@ void P2PTransportChannel::SortConnections() {
|
| // need to consider switching to.
|
| ConnectionCompare cmp;
|
| std::stable_sort(connections_.begin(), connections_.end(), cmp);
|
| - LOG(LS_VERBOSE) << "Sorting available connections:";
|
| + LOG(LS_VERBOSE) << "Sorting " << connections_.size()
|
| + << " available connections:";
|
| for (uint32 i = 0; i < connections_.size(); ++i) {
|
| LOG(LS_VERBOSE) << connections_[i]->ToString();
|
| }
|
| @@ -924,10 +952,7 @@ void P2PTransportChannel::SortConnections() {
|
| // If necessary, switch to the new choice.
|
| // 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)) {
|
| + if (ShouldSwitch(best_connection_, top_connection, ice_role_)) {
|
| LOG(LS_INFO) << "Switching best connection: " << top_connection->ToString();
|
| SwitchBestConnectionTo(top_connection);
|
| }
|
| @@ -1013,7 +1038,6 @@ void P2PTransportChannel::SwitchBestConnectionTo(Connection* conn) {
|
| LOG_J(LS_INFO, this) << "New best connection: "
|
| << best_connection_->ToString();
|
| SignalRouteChange(this, best_connection_->remote_candidate());
|
| - set_receiving(best_connection_->receiving());
|
| } else {
|
| LOG_J(LS_INFO, this) << "No best connection";
|
| }
|
| @@ -1022,15 +1046,19 @@ void P2PTransportChannel::SwitchBestConnectionTo(Connection* conn) {
|
| void P2PTransportChannel::UpdateChannelState() {
|
| // The Handle* functions already set the writable state. We'll just double-
|
| // check it here.
|
| - bool writable = ((best_connection_ != NULL) &&
|
| - (best_connection_->write_state() ==
|
| - Connection::STATE_WRITABLE));
|
| + bool writable = best_connection_ && best_connection_->writable();
|
| ASSERT(writable == this->writable());
|
| if (writable != this->writable())
|
| LOG(LS_ERROR) << "UpdateChannelState: writable state mismatch";
|
|
|
| - // TODO(honghaiz): The channel receiving state is set in OnCheckReceiving.
|
| - // Will revisit in a subsequent code change.
|
| + bool receiving = false;
|
| + for (const Connection* connection : connections_) {
|
| + if (connection->receiving()) {
|
| + receiving = true;
|
| + break;
|
| + }
|
| + }
|
| + set_receiving(receiving);
|
| }
|
|
|
| // We checked the status of our connections and we had at least one that
|
| @@ -1063,6 +1091,11 @@ void P2PTransportChannel::HandleAllTimedOut() {
|
| HandleNotWritable();
|
| }
|
|
|
| +bool P2PTransportChannel::Weak() const {
|
| + return !(best_connection_ && best_connection_->receiving() &&
|
| + best_connection_->writable());
|
| +}
|
| +
|
| // If we have a best connection, return it, otherwise return top one in the
|
| // list (later we will mark it best).
|
| Connection* P2PTransportChannel::GetBestConnectionOnNetwork(
|
| @@ -1086,11 +1119,8 @@ void P2PTransportChannel::OnMessage(rtc::Message *pmsg) {
|
| case MSG_SORT:
|
| OnSort();
|
| break;
|
| - case MSG_PING:
|
| - OnPing();
|
| - break;
|
| - case MSG_CHECK_RECEIVING:
|
| - OnCheckReceiving();
|
| + case MSG_CHECK_AND_PING:
|
| + OnCheckAndPing();
|
| break;
|
| default:
|
| ASSERT(false);
|
| @@ -1104,30 +1134,22 @@ void P2PTransportChannel::OnSort() {
|
| SortConnections();
|
| }
|
|
|
| -// Handle queued up ping request
|
| -void P2PTransportChannel::OnPing() {
|
| +// Handle queued up check-and-ping request
|
| +void P2PTransportChannel::OnCheckAndPing() {
|
| // Make sure the states of the connections are up-to-date (since this affects
|
| // which ones are pingable).
|
| UpdateConnectionStates();
|
| -
|
| - // Find the oldest pingable connection and have it do a ping.
|
| - Connection* conn = FindNextPingableConnection();
|
| - if (conn)
|
| - PingConnection(conn);
|
| -
|
| - // Post ourselves a message to perform the next ping.
|
| - uint32 delay = writable() ? WRITABLE_DELAY : UNWRITABLE_DELAY;
|
| - thread()->PostDelayed(delay, this, MSG_PING);
|
| -}
|
| -
|
| -void P2PTransportChannel::OnCheckReceiving() {
|
| - if (best_connection_) {
|
| - bool receiving = rtc::Time() <=
|
| - best_connection_->last_received() + receiving_timeout_;
|
| - set_receiving(receiving);
|
| + // When the best connection is either not receiving or not writable,
|
| + // switch to weak ping delay.
|
| + int ping_delay = Weak() ? WEAK_PING_DELAY : STRONG_PING_DELAY;
|
| + if (rtc::Time() >= last_ping_sent_ms_ + ping_delay) {
|
| + Connection* conn = FindNextPingableConnection();
|
| + if (conn) {
|
| + PingConnection(conn);
|
| + }
|
| }
|
| -
|
| - thread()->PostDelayed(check_receiving_delay_, this, MSG_CHECK_RECEIVING);
|
| + int check_delay = std::min(ping_delay, check_receiving_delay_);
|
| + thread()->PostDelayed(check_delay, this, MSG_CHECK_AND_PING);
|
| }
|
|
|
| // Is the connection in a state for us to even consider pinging the other side?
|
| @@ -1149,9 +1171,9 @@ bool P2PTransportChannel::IsPingable(Connection* conn) {
|
| return false;
|
| }
|
|
|
| - // If the channel is not writable, ping all candidates. Otherwise, we only
|
| + // If the channel is weak, ping all candidates. Otherwise, we only
|
| // want to ping connections that have not timed out on writing.
|
| - return !writable() || conn->write_state() != Connection::STATE_WRITE_TIMEOUT;
|
| + return Weak() || conn->write_state() != Connection::STATE_WRITE_TIMEOUT;
|
| }
|
|
|
| // Returns the next pingable connection to ping. This will be the oldest
|
| @@ -1163,9 +1185,8 @@ bool P2PTransportChannel::IsPingable(Connection* conn) {
|
| Connection* P2PTransportChannel::FindNextPingableConnection() {
|
| uint32 now = rtc::Time();
|
| if (best_connection_ && best_connection_->connected() &&
|
| - (best_connection_->write_state() == Connection::STATE_WRITABLE) &&
|
| - (best_connection_->last_ping_sent() + MAX_CURRENT_WRITABLE_DELAY <=
|
| - now)) {
|
| + best_connection_->writable() &&
|
| + (best_connection_->last_ping_sent() + MAX_CURRENT_STRONG_DELAY <= now)) {
|
| return best_connection_;
|
| }
|
|
|
| @@ -1223,7 +1244,8 @@ void P2PTransportChannel::PingConnection(Connection* conn) {
|
| use_candidate = best_connection_->writable();
|
| }
|
| conn->set_use_candidate_attr(use_candidate);
|
| - conn->Ping(rtc::Time());
|
| + last_ping_sent_ms_ = rtc::Time();
|
| + conn->Ping(last_ping_sent_ms_);
|
| }
|
|
|
| // When a connection's state changes, we need to figure out who to use as
|
|
|