Commit f46bf54d authored by Lambros Lambrou's avatar Lambros Lambrou Committed by Commit Bot

[remoting WebRTC] Use maxRateKbps from ICE config

This replaces the hard-coded relay bandwidth cap of 8000Kbps with
the value of maxRateKbps (if present) in the ICE config response from
the TURN server. If the value is not present, no cap is applied.

Bug: 880462
Change-Id: I0d8d2a48f78559e93cff94055a65da7ec32de494
Reviewed-on: https://chromium-review.googlesource.com/1211668Reviewed-by: default avatarJamie Walch <jamiewalch@chromium.org>
Commit-Queue: Lambros Lambrou <lambroslambrou@chromium.org>
Cr-Commit-Position: refs/heads/master@{#589376}
parent e3a08235
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
#include "remoting/protocol/ice_config.h" #include "remoting/protocol/ice_config.h"
#include <algorithm>
#include "base/json/json_reader.h" #include "base/json/json_reader.h"
#include "base/json/json_writer.h" #include "base/json/json_writer.h"
#include "base/strings/string_number_conversions.h" #include "base/strings/string_number_conversions.h"
...@@ -86,6 +88,21 @@ bool AddServerToConfig(std::string url, ...@@ -86,6 +88,21 @@ bool AddServerToConfig(std::string url,
return true; return true;
} }
// Returns the smallest specified value, or 0 if neither is specified.
// A value is "specified" if it is greater than 0.
int MinimumSpecified(int value1, int value2) {
if (value1 <= 0) {
// value1 is not specified, so return value2 (or 0).
return std::max(0, value2);
}
if (value2 <= 0) {
// value1 is specified, so return it directly.
return value1;
}
// Both values are specified, so return the minimum.
return std::min(value1, value2);
}
} // namespace } // namespace
IceConfig::IceConfig() = default; IceConfig::IceConfig() = default;
...@@ -117,6 +134,7 @@ IceConfig IceConfig::Parse(const base::DictionaryValue& dictionary) { ...@@ -117,6 +134,7 @@ IceConfig IceConfig::Parse(const base::DictionaryValue& dictionary) {
// Parse iceServers list and store them in |ice_config|. // Parse iceServers list and store them in |ice_config|.
bool errors_found = false; bool errors_found = false;
ice_config.max_bitrate_kbps = 0;
for (const auto& server : *ice_servers_list) { for (const auto& server : *ice_servers_list) {
const base::DictionaryValue* server_dict; const base::DictionaryValue* server_dict;
if (!server.GetAsDictionary(&server_dict)) { if (!server.GetAsDictionary(&server_dict)) {
...@@ -136,6 +154,16 @@ IceConfig IceConfig::Parse(const base::DictionaryValue& dictionary) { ...@@ -136,6 +154,16 @@ IceConfig IceConfig::Parse(const base::DictionaryValue& dictionary) {
std::string password; std::string password;
server_dict->GetString("credential", &password); server_dict->GetString("credential", &password);
// Compute the lowest specified bitrate of all the ICE servers.
// Ideally the bitrate would be stored per ICE server, but it is not
// possible (at the application level) to look up which particular
// ICE server was used for the P2P connection.
double new_bitrate_double;
if (server_dict->GetDouble("maxRateKbps", &new_bitrate_double)) {
ice_config.max_bitrate_kbps = MinimumSpecified(
ice_config.max_bitrate_kbps, static_cast<int>(new_bitrate_double));
}
for (const auto& url : *urls_list) { for (const auto& url : *urls_list) {
std::string url_str; std::string url_str;
if (!url.GetAsString(&url_str)) { if (!url.GetAsString(&url_str)) {
......
...@@ -42,6 +42,10 @@ struct IceConfig { ...@@ -42,6 +42,10 @@ struct IceConfig {
// Standard TURN servers // Standard TURN servers
std::vector<cricket::RelayServerConfig> turn_servers; std::vector<cricket::RelayServerConfig> turn_servers;
// If greater than 0, the max bandwidth used for relayed connections should
// be set to this value.
int max_bitrate_kbps = 0;
}; };
} // namespace protocol } // namespace protocol
......
...@@ -26,7 +26,8 @@ TEST(IceConfigTest, ParseValid) { ...@@ -26,7 +26,8 @@ TEST(IceConfigTest, ParseValid) {
" \"turns:the_server.com?transport=udp\"" " \"turns:the_server.com?transport=udp\""
" ]," " ],"
" \"username\": \"123\"," " \"username\": \"123\","
" \"credential\": \"abc\"" " \"credential\": \"abc\","
" \"maxRateKbps\": 8000.0"
" }," " },"
" {" " {"
" \"urls\": [" " \"urls\": ["
...@@ -71,6 +72,7 @@ TEST(IceConfigTest, ParseValid) { ...@@ -71,6 +72,7 @@ TEST(IceConfigTest, ParseValid) {
EXPECT_EQ(rtc::SocketAddress("stun_server.com", 18344), EXPECT_EQ(rtc::SocketAddress("stun_server.com", 18344),
config.stun_servers[0]); config.stun_servers[0]);
EXPECT_EQ(rtc::SocketAddress("1.2.3.4", 3478), config.stun_servers[1]); EXPECT_EQ(rtc::SocketAddress("1.2.3.4", 3478), config.stun_servers[1]);
EXPECT_EQ(8000.0, config.max_bitrate_kbps);
} }
TEST(IceConfigTest, ParseDataEnvelope) { TEST(IceConfigTest, ParseDataEnvelope) {
...@@ -126,5 +128,63 @@ TEST(IceConfigTest, NotParseable) { ...@@ -126,5 +128,63 @@ TEST(IceConfigTest, NotParseable) {
EXPECT_TRUE(config.is_null()); EXPECT_TRUE(config.is_null());
} }
TEST(IceConfigTest, UnspecifiedMaxRate_IsZero) {
const char kTestConfigJson[] =
"{"
" \"iceServers\": ["
" {"
" \"urls\": ["
" \"stun:1.2.3.4\""
" ]"
" }"
" ]"
"}";
IceConfig config = IceConfig::Parse(kTestConfigJson);
EXPECT_EQ(0, config.max_bitrate_kbps);
}
TEST(IceConfigTest, OneSpecifiedMaxRate_IsUsed) {
const char kTestConfigJson1[] =
"{"
" \"iceServers\": ["
" {"
" \"urls\": ["
" \"stun:1.2.3.4\""
" ],"
" \"maxRateKbps\": 1000.0"
" },"
" {"
" \"urls\": ["
" \"stun:1.2.3.4\""
" ]"
" }"
" ]"
"}";
IceConfig config1 = IceConfig::Parse(kTestConfigJson1);
EXPECT_EQ(1000, config1.max_bitrate_kbps);
const char kTestConfigJson2[] =
"{"
" \"iceServers\": ["
" {"
" \"urls\": ["
" \"stun:1.2.3.4\""
" ]"
" },"
" {"
" \"urls\": ["
" \"stun:1.2.3.4\""
" ],"
" \"maxRateKbps\": 2000.0"
" }"
" ]"
"}";
IceConfig config2 = IceConfig::Parse(kTestConfigJson2);
EXPECT_EQ(2000, config2.max_bitrate_kbps);
}
} // namespace protocol } // namespace protocol
} // namespace remoting } // namespace remoting
...@@ -122,5 +122,10 @@ void TransportContext::OnIceConfig(RelayMode relay_mode, ...@@ -122,5 +122,10 @@ void TransportContext::OnIceConfig(RelayMode relay_mode,
} }
} }
int TransportContext::GetTurnMaxRateKbps() const {
DCHECK_EQ(relay_mode_, RelayMode::TURN);
return ice_config_[RelayMode::TURN].max_bitrate_kbps;
}
} // namespace protocol } // namespace protocol
} // namespace remoting } // namespace remoting
...@@ -101,6 +101,11 @@ class TransportContext : public base::RefCountedThreadSafe<TransportContext> { ...@@ -101,6 +101,11 @@ class TransportContext : public base::RefCountedThreadSafe<TransportContext> {
TransportRole role() const { return role_; } TransportRole role() const { return role_; }
rtc::NetworkManager* network_manager() const { return network_manager_; } rtc::NetworkManager* network_manager() const { return network_manager_; }
// Returns the suggested bandwidth cap for TURN relay connections, or 0 if
// no rate-limit is set in the IceConfig. Currently this is not used for
// legacy GTURN connections.
int GetTurnMaxRateKbps() const;
private: private:
friend class base::RefCountedThreadSafe<TransportContext>; friend class base::RefCountedThreadSafe<TransportContext>;
......
...@@ -53,13 +53,6 @@ const int kTransportInfoSendDelayMs = 20; ...@@ -53,13 +53,6 @@ const int kTransportInfoSendDelayMs = 20;
// XML namespace for the transport elements. // XML namespace for the transport elements.
const char kTransportNamespace[] = "google:remoting:webrtc"; const char kTransportNamespace[] = "google:remoting:webrtc";
// Bitrate cap applied to relay connections. This is done to prevent
// large amounts of packet loss, since the Google TURN/relay server drops
// packets to limit the connection to ~10Mbps. The rate-limiting behavior works
// badly with WebRTC's bandwidth-estimation, which results in the host process
// trying to send frames too rapidly over the connection.
constexpr int kMaxBitrateOnRelayKbps = 8000;
#if !defined(NDEBUG) #if !defined(NDEBUG)
// Command line switch used to disable signature verification. // Command line switch used to disable signature verification.
// TODO(sergeyu): Remove this flag. // TODO(sergeyu): Remove this flag.
...@@ -757,6 +750,16 @@ void WebrtcTransport::OnStatsDelivered( ...@@ -757,6 +750,16 @@ void WebrtcTransport::OnStatsDelivered(
VLOG(0) << "Relay connection: " << (is_relay ? "true" : "false"); VLOG(0) << "Relay connection: " << (is_relay ? "true" : "false");
if (is_relay) { if (is_relay) {
int max_bitrate_kbps = transport_context_->GetTurnMaxRateKbps();
if (max_bitrate_kbps <= 0) {
VLOG(0) << "No bitrate cap set.";
return;
}
VLOG(0) << "Capping bitrate to " << max_bitrate_kbps << "kbps.";
// Apply the suggested bitrate cap to prevent large amounts of packet loss.
// The Google TURN/relay server limits the connection speed by dropping
// packets, which may interact badly with WebRTC's bandwidth-estimation.
auto senders = peer_connection()->GetSenders(); auto senders = peer_connection()->GetSenders();
for (rtc::scoped_refptr<webrtc::RtpSenderInterface> sender : senders) { for (rtc::scoped_refptr<webrtc::RtpSenderInterface> sender : senders) {
// x-google-max-bitrate is only set for video codecs in the SDP exchange. // x-google-max-bitrate is only set for video codecs in the SDP exchange.
...@@ -777,7 +780,7 @@ void WebrtcTransport::OnStatsDelivered( ...@@ -777,7 +780,7 @@ void WebrtcTransport::OnStatsDelivered(
<< sender->id(); << sender->id();
} }
parameters.encodings[0].max_bitrate_bps = kMaxBitrateOnRelayKbps * 1000; parameters.encodings[0].max_bitrate_bps = max_bitrate_kbps * 1000;
sender->SetParameters(parameters); sender->SetParameters(parameters);
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment