Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(443)

Unified Diff: webrtc/p2p/base/p2ptransportchannel.cc

Issue 2063823008: Adding IceConfig option to assume TURN/TURN candidate pairs will work. (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Formatting. Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: webrtc/p2p/base/p2ptransportchannel.cc
diff --git a/webrtc/p2p/base/p2ptransportchannel.cc b/webrtc/p2p/base/p2ptransportchannel.cc
index d83726eab5a5b53c1af9edbda62f93907ac5e22c..88506a41aad7d84617ebcbafaf150fc9cc827438 100644
--- a/webrtc/p2p/base/p2ptransportchannel.cc
+++ b/webrtc/p2p/base/p2ptransportchannel.cc
@@ -29,10 +29,19 @@ namespace {
// messages for queuing up work for ourselves
enum { MSG_SORT = 1, MSG_CHECK_AND_PING };
+// Mapped write states used by P2PTransportChannel::MappedWriteState.
+enum {
+ MAPPED_WRITE_STATE_WRITABLE = 1,
+ MAPPED_WRITE_STATE_PROBABLY_WRITABLE,
+ MAPPED_WRITE_STATE_UNRELIABLE,
+ MAPPED_WRITE_STATE_INIT,
+ MAPPED_WRITE_STATE_TIMEOUT
+};
pthatcher1 2016/06/15 19:12:46 Can we just add Connection::STATE_WRITE_PRESUMED t
+
// The minimum improvement in RTT that justifies a switch.
static const double kMinImprovement = 10;
-bool IsRelayRelay(cricket::Connection* conn) {
+bool IsRelayRelay(const cricket::Connection* conn) {
return conn->local_candidate().type() == cricket::RELAY_PORT_TYPE &&
conn->remote_candidate().type() == cricket::RELAY_PORT_TYPE;
}
@@ -51,155 +60,6 @@ cricket::PortInterface::CandidateOrigin GetOrigin(cricket::PortInterface* port,
return cricket::PortInterface::ORIGIN_OTHER_PORT;
}
-// Compares two connections based only on the candidate and network information.
-// Returns positive if |a| is better than |b|.
-int CompareConnectionCandidates(cricket::Connection* a,
- cricket::Connection* b) {
- uint32_t a_cost = a->ComputeNetworkCost();
- uint32_t b_cost = b->ComputeNetworkCost();
- // Smaller cost is better.
- if (a_cost < b_cost) {
- return 1;
- }
- if (a_cost > b_cost) {
- return -1;
- }
-
- // Compare connection priority. Lower values get sorted last.
- if (a->priority() > b->priority())
- return 1;
- if (a->priority() < b->priority())
- return -1;
-
- // If we're still tied at this point, prefer a younger generation.
- return (a->remote_candidate().generation() + a->port()->generation()) -
- (b->remote_candidate().generation() + b->port()->generation());
-}
-
-// 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
- // pretending to be writable (the connection is not set to the unwritable
- // state). On the passive side, the connection also remains writable even
- // though it is disconnected, and a new connection is created when the active
- // side connects. At that point, there are two TCP connections on the passive
- // side: 1. the old, disconnected one that is pretending to be writable, and
- // 2. the new, connected one that is maybe not yet writable. For purposes of
- // pruning, pinging, and selecting the best connection, we want to treat the
- // new connection as "better" than the old one. We could add a method called
- // something like Connection::ImReallyBadEvenThoughImWritable, but that is
- // equivalent to the existing Connection::connected(), which we already have.
- // So, in code throughout this file, we'll check whether the connection is
- // connected() or not, and if it is not, treat it as "worse" than a connected
- // one, even though it's writable. In the code below, we're doing so to make
- // sure we treat a new writable connection as better than an old disconnected
- // connection.
-
- // In the case where we reconnect TCP connections, the original best
- // connection is disconnected without changing to WRITE_TIMEOUT. In this case,
- // the new connection, when it becomes writable, should have higher priority.
- if (a->write_state() == cricket::Connection::STATE_WRITABLE &&
- b->write_state() == cricket::Connection::STATE_WRITABLE) {
- if (a->connected() && !b->connected()) {
- return 1;
- }
- if (!a->connected() && b->connected()) {
- 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);
-}
-
-// Wraps the comparison connection into a less than operator that puts higher
-// priority writable connections first.
-class ConnectionCompare {
- public:
- bool operator()(const cricket::Connection *ca,
- const cricket::Connection *cb) {
- cricket::Connection* a = const_cast<cricket::Connection*>(ca);
- cricket::Connection* b = const_cast<cricket::Connection*>(cb);
-
- // Compare first on writability and static preferences.
- int cmp = CompareConnections(a, b);
- if (cmp > 0)
- return true;
- if (cmp < 0)
- return false;
-
- // Otherwise, sort based on latency estimate.
- return a->rtt() < b->rtt();
-
- // Should we bother checking for the last connection that last received
- // data? It would help rendezvous on the connection that is also receiving
- // packets.
- //
- // TODO: Yes we should definitely do this. The TCP protocol gains
- // efficiency by being used bidirectionally, as opposed to two separate
- // unidirectional streams. This test should probably occur before
- // comparison of local prefs (assuming combined prefs are the same). We
- // need to be careful though, not to bounce back and forth with both sides
- // trying to rendevous with the other.
- }
-};
-
-// Determines whether we should switch between two connections, based first on
-// 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;
-
- // 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;
-}
-
} // unnamed namespace
namespace cricket {
@@ -250,7 +110,8 @@ P2PTransportChannel::P2PTransportChannel(const std::string& transport_name,
0 /* backup_connection_ping_interval */,
false /* gather_continually */,
false /* prioritize_most_likely_candidate_pairs */,
- MAX_CURRENT_STRONG_INTERVAL /* max_strong_interval */) {
+ MAX_CURRENT_STRONG_INTERVAL /* max_strong_interval */,
+ true /* turn_to_turn_createpermission_needed */) {
uint32_t weak_ping_interval = ::strtoul(
webrtc::field_trial::FindFullName("WebRTC-StunInterPacketDelay").c_str(),
nullptr, 10);
@@ -438,6 +299,19 @@ void P2PTransportChannel::SetIceConfig(const IceConfig& config) {
LOG(LS_INFO) << "Set max strong interval to "
<< config_.max_strong_interval;
}
+
+ if (config.turn_to_turn_createpermission_needed !=
+ config_.turn_to_turn_createpermission_needed) {
+ if (!connections_.empty()) {
+ LOG(LS_ERROR) << "Trying to change TURN-TURN CreatePermission needed "
+ << "while connections already exist!";
+ } else {
+ config_.turn_to_turn_createpermission_needed =
+ config.turn_to_turn_createpermission_needed;
+ LOG(LS_INFO) << "Set TURN-TURN CreatePermission needed to "
+ << config_.turn_to_turn_createpermission_needed;
+ }
+ }
pthatcher1 2016/06/15 19:12:46 Why not just allow it and document that it doesn't
Taylor Brandstetter 2016/06/16 00:13:40 It's easier to just disallow it then to allow it i
}
const IceConfig& P2PTransportChannel::config() const {
@@ -1063,6 +937,172 @@ void P2PTransportChannel::RequestSort() {
}
}
+int P2PTransportChannel::MappedWriteState(const cricket::Connection* a) const {
+ switch (a->write_state()) {
+ case Connection::STATE_WRITABLE:
+ return MAPPED_WRITE_STATE_WRITABLE;
+ case Connection::STATE_WRITE_UNRELIABLE:
+ return MAPPED_WRITE_STATE_UNRELIABLE;
+ case Connection::STATE_WRITE_INIT:
+ // If the connection hasn't received a binding response, but it's a TURN-
+ // TURN connection and CreatePermission isn't needed, the connection is
+ // "probably writable" which is just a step below writable.
+ if (!config_.turn_to_turn_createpermission_needed && IsRelayRelay(a)) {
+ return MAPPED_WRITE_STATE_PROBABLY_WRITABLE;
+ }
+ return MAPPED_WRITE_STATE_INIT;
+ case Connection::STATE_WRITE_TIMEOUT:
+ return MAPPED_WRITE_STATE_TIMEOUT;
+ default:
+ RTC_DCHECK(false);
+ return MAPPED_WRITE_STATE_TIMEOUT;
honghaiz3 2016/06/15 16:37:18 Should we consider putting the write state mapping
Taylor Brandstetter 2016/06/15 17:08:56 I considered that. But that would mean we'd be let
pthatcher1 2016/06/15 19:12:46 What if we passed the initial Connection state int
honghaiz3 2016/06/15 19:26:36 There is one case in which the write_state reset t
Taylor Brandstetter 2016/06/16 00:13:40 I *really* don't want to pass a state in, or make
+ }
+}
+
+// Compare two connections based on their writing, receiving, and connected
+// states.
+int P2PTransportChannel::CompareConnectionStates(const Connection* a,
+ const Connection* b) const {
+ // Sort based on write-state. Better states have lower values.
+ if (MappedWriteState(a) < MappedWriteState(b)) {
+ return 1;
+ }
+ if (MappedWriteState(a) > MappedWriteState(b)) {
+ return -1;
+ }
pthatcher1 2016/06/15 19:12:46 This could be like the other comps: // Prefer bett
Taylor Brandstetter 2016/06/16 00:13:41 Done.
+
+ // 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
+ // pretending to be writable (the connection is not set to the unwritable
+ // state). On the passive side, the connection also remains writable even
+ // though it is disconnected, and a new connection is created when the active
+ // side connects. At that point, there are two TCP connections on the passive
+ // side: 1. the old, disconnected one that is pretending to be writable, and
+ // 2. the new, connected one that is maybe not yet writable. For purposes of
+ // pruning, pinging, and selecting the best connection, we want to treat the
+ // new connection as "better" than the old one. We could add a method called
+ // something like Connection::ImReallyBadEvenThoughImWritable, but that is
+ // equivalent to the existing Connection::connected(), which we already have.
+ // So, in code throughout this file, we'll check whether the connection is
+ // connected() or not, and if it is not, treat it as "worse" than a connected
+ // one, even though it's writable. In the code below, we're doing so to make
+ // sure we treat a new writable connection as better than an old disconnected
+ // connection.
+
+ // In the case where we reconnect TCP connections, the original best
+ // connection is disconnected without changing to WRITE_TIMEOUT. In this case,
+ // the new connection, when it becomes writable, should have higher priority.
+ if (a->write_state() == Connection::STATE_WRITABLE &&
+ b->write_state() == Connection::STATE_WRITABLE) {
+ if (a->connected() && !b->connected()) {
+ return 1;
+ }
+ if (!a->connected() && b->connected()) {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+// Compares two connections based only on the candidate and network information.
+// Returns positive if |a| is better than |b|.
+int P2PTransportChannel::CompareConnectionCandidates(
+ const Connection* a,
+ const Connection* b) const {
+ uint32_t a_cost = a->ComputeNetworkCost();
+ uint32_t b_cost = b->ComputeNetworkCost();
+ // Smaller cost is better.
+ if (a_cost < b_cost) {
+ return 1;
+ }
+ if (a_cost > b_cost) {
+ return -1;
+ }
pthatcher1 2016/06/15 19:12:46 To make it a lot like the other comp code, should
Taylor Brandstetter 2016/06/16 00:13:40 Hmm. Now we're talking about subtracting unsigned
pthatcher1 2016/06/16 23:31:36 True.
+
+ // Compare connection priority. Lower values get sorted last.
+ if (a->priority() > b->priority()) {
+ return 1;
+ }
+ if (a->priority() < b->priority()) {
+ return -1;
+ }
pthatcher1 2016/06/15 19:12:46 Similarly here: // Prefer higher priority int pri
Taylor Brandstetter 2016/06/16 00:13:40 See above. Plus, these are uint64_t's. Even if we
+
+ // If we're still tied at this point, prefer a younger generation.
pthatcher1 2016/06/15 19:12:46 Could you add "(larger generation number)" to make
Taylor Brandstetter 2016/06/16 00:13:40 Done.
+ return (a->remote_candidate().generation() + a->port()->generation()) -
+ (b->remote_candidate().generation() + b->port()->generation());
+}
+
+int P2PTransportChannel::CompareConnections(const Connection* a,
+ const Connection* b) const {
+ // Compare first on writability and static preferences.
+ int state_cmp = CompareConnectionStates(a, b);
+ if (state_cmp != 0) {
+ return state_cmp;
+ }
+ // Then compare the candidate information.
+ int candidates_cmp = CompareConnectionCandidates(a, b);
+ if (candidates_cmp != 0) {
+ return candidates_cmp;
+ }
+ // Otherwise, compare based on latency estimate.
+ return b->rtt() - a->rtt();
+
+ // Should we bother checking for the last connection that last received
+ // data? It would help rendezvous on the connection that is also receiving
+ // packets.
+ //
+ // TODO(deadbeef): Yes we should definitely do this. The TCP protocol gains
+ // efficiency by being used bidirectionally, as opposed to two separate
+ // unidirectional streams. This test should probably occur before
+ // comparison of local prefs (assuming combined prefs are the same). We
+ // need to be careful though, not to bounce back and forth with both sides
+ // trying to rendevous with the other.
pthatcher1 2016/06/15 19:12:46 That's a good point. It would make sense for the
Taylor Brandstetter 2016/06/16 00:13:40 FYI: I didn't write this comment, I just moved it
+}
+
+// Determines whether we should switch between two connections, based first on
+// connection states, static preferences, and then (if those are equal) on
+// latency estimates.
+bool P2PTransportChannel::ShouldSwitch(const Connection* a,
+ const Connection* b) const {
pthatcher1 2016/06/15 19:12:46 If you're going to move this, we might as well nam
Taylor Brandstetter 2016/06/16 00:13:41 Done.
+ if (a == b) {
+ return false;
+ }
+
+ if (!a || !b) { // don't think the latter should happen
+ return true;
+ }
pthatcher1 2016/06/15 19:12:46 This might be more readable as: if (!conn) { re
Taylor Brandstetter 2016/06/16 00:13:40 Done.
+
+ // 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, b);
+ if (state_cmp != 0) {
+ return state_cmp < 0;
+ }
+ if (ice_role_ == ICEROLE_CONTROLLED && a->nominated()) {
pthatcher1 2016/06/15 19:12:46 Would it make sense to have a few helper methods?
Taylor Brandstetter 2016/06/16 00:13:40 We don't need the former function. If the other si
+ LOG(LS_VERBOSE) << "Controlled side did not switch due to nominated status";
+ return false;
+ }
+
+ int prefs_cmp = CompareConnectionCandidates(a, b);
+ if (prefs_cmp != 0) {
+ return prefs_cmp < 0;
+ }
+
+ return b->rtt() <= a->rtt() + kMinImprovement;
pthatcher1 2016/06/15 19:12:46 This might be more readable as: return (selected-
Taylor Brandstetter 2016/06/16 00:13:40 Done.
+}
+
// Sort the available connections to find the best one. We also monitor
// the number of available connections and the current state.
void P2PTransportChannel::SortConnections() {
@@ -1079,8 +1119,10 @@ void P2PTransportChannel::SortConnections() {
// 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);
+ std::stable_sort(connections_.begin(), connections_.end(),
+ [this](const Connection* a, const Connection* b) {
+ return CompareConnections(a, b) > 0;
+ });
LOG(LS_VERBOSE) << "Sorting " << connections_.size()
<< " available connections:";
for (size_t i = 0; i < connections_.size(); ++i) {
@@ -1093,7 +1135,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.
- if (ShouldSwitch(best_connection_, top_connection, ice_role_)) {
+ if (ShouldSwitch(best_connection_, top_connection)) {
LOG(LS_INFO) << "Switching best connection: " << top_connection->ToString();
SwitchBestConnectionTo(top_connection);
}
@@ -1237,7 +1279,14 @@ void P2PTransportChannel::UpdateState() {
SignalStateChanged(this);
}
- bool writable = best_connection_ && best_connection_->writable();
+ // If our best connection is "probably writable" (TURN-TURN with no
+ // CreatePermission required), act like we're already writable to the upper
pthatcher1 2016/06/15 19:12:46 I think the phrased "presumed writable" might be s
Taylor Brandstetter 2016/06/16 00:13:41 Done.
+ // layers, so they can start media quicker.
+ bool writable =
pthatcher1 2016/06/15 19:12:46 Can we make a method called PresumedWritable()? T
Taylor Brandstetter 2016/06/16 00:13:41 Done.
+ best_connection_ &&
+ (MappedWriteState(best_connection_) == MAPPED_WRITE_STATE_WRITABLE ||
+ MappedWriteState(best_connection_) ==
+ MAPPED_WRITE_STATE_PROBABLY_WRITABLE);
set_writable(writable);
bool receiving = false;

Powered by Google App Engine
This is Rietveld 408576698